Refactor ComboBox dark theme

This commit is contained in:
qinware
2025-08-08 16:23:16 +08:00
parent 80d3800b8b
commit 004dd98cbb
13 changed files with 80 additions and 39 deletions
@@ -40,7 +40,7 @@
<Style Selector="^:is(atom|ArrowDecoratedBox)">
<Setter Property="IsMotionEnabled" Value="{DynamicResource {x:Static atom:SharedTokenKey.EnableMotion}}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorText}}" />
<Setter Property="Background" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBgContainer}}" />
<Setter Property="Background" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBgElevated}}" />
<Setter Property="MinHeight" Value="{DynamicResource {x:Static atom:SharedTokenKey.ControlHeight}}" />
<Setter Property="Padding" Value="{DynamicResource {x:Static atom:ArrowDecoratedBoxTokenKey.Padding}}" />
<Setter Property="ArrowSize" Value="{DynamicResource {x:Static atom:ArrowDecoratedBoxTokenKey.ArrowSize}}" />
+24 -17
View File
@@ -7,6 +7,7 @@ using Avalonia.Controls;
using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Layout;
namespace AtomUI.Controls;
@@ -161,7 +162,6 @@ public class ComboBox : AvaloniaComboBox,
#endregion
private Popup? _popup;
private IconButton? _openIndicatorButton;
static ComboBox()
{
@@ -177,25 +177,23 @@ public class ComboBox : AvaloniaComboBox,
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
this.SetPopup(null); // 情况父类,方式鼠标点击的错误处理
_popup = e.NameScope.Find<Popup>(ComboBoxThemeConstants.PopupPart);
_openIndicatorButton = e.NameScope.Find<IconButton>(ComboBoxThemeConstants.OpenIndicatorButtonPart);
if (_openIndicatorButton is not null)
{
_openIndicatorButton.Click += (sender, args) => { SetCurrentValue(IsDropDownOpenProperty, true); };
}
var spinnerHandle = e.NameScope.Find<ComboBoxSpinnerInnerBox>(ComboBoxThemeConstants.SpinnerInnerBoxPart);
if (spinnerHandle?.SpinnerContent is ComboBoxHandle handle)
{
handle.HandleClick += (sender, args) =>
{
SetCurrentValue(IsDropDownOpenProperty, !IsDropDownOpen);
};
handle.HandleClick += HandleOpenPopupClicked;
}
if (_popup is IPopupHostProvider popupHostProvider)
{
popupHostProvider.PopupHostChanged += HandlePopupHostChanged;
}
UpdatePseudoClasses();
}
private void HandleOpenPopupClicked(object? sender, EventArgs e)
{
SetCurrentValue(IsDropDownOpenProperty, !IsDropDownOpen);
}
private void HandlePopupHostChanged(IPopupHost? host)
@@ -212,12 +210,6 @@ public class ComboBox : AvaloniaComboBox,
}
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
UpdatePseudoClasses();
}
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
{
base.PrepareContainerForItemOverride(container, item, index);
@@ -237,6 +229,21 @@ public class ComboBox : AvaloniaComboBox,
UpdatePseudoClasses();
}
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
if (!e.Handled && e.Source is Visual source)
{
if (_popup?.IsInsidePopup(source) == true)
{
if (UpdateSelectionFromEventSource(e.Source))
{
e.Handled = true;
}
}
}
base.OnPointerReleased(e);
}
private void UpdatePseudoClasses()
{
@@ -0,0 +1,23 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using AtomUI.Reflection;
using AvaloniaComboBox = Avalonia.Controls.ComboBox;
namespace AtomUI.Controls;
internal static class ComboBoxReflectionExtensions
{
#region 反射信息定义
[DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicFields, typeof(AvaloniaComboBox))]
private static readonly Lazy<FieldInfo> PopupFieldInfo = new Lazy<FieldInfo>(() =>
typeof(AvaloniaComboBox).GetFieldInfoOrThrow("_popup",
BindingFlags.Instance | BindingFlags.NonPublic));
#endregion
public static void SetPopup(this AvaloniaComboBox comboBox, Popup? popup)
{
PopupFieldInfo.Value.SetValue(comboBox, popup);
}
}
@@ -95,7 +95,7 @@ internal class ComboBoxToken : ButtonSpinnerToken
var colorTextDisabled = SharedToken.ColorTextDisabled;
var colorTextSecondary = SharedToken.ColorTextSecondary;
var colorBgContainer = SharedToken.ColorBgContainer;
var colorBgContainer = SharedToken.ColorBgElevated;
var colorBgTextHover = SharedToken.ColorBgTextHover;
ItemColor = colorTextSecondary;
@@ -105,7 +105,6 @@ internal class ComboBoxToken : ButtonSpinnerToken
ItemBgColor = colorBgContainer;
ItemHoverBgColor = colorBgTextHover;
ItemSelectedBgColor = SharedToken.ControlItemBgActive;
ItemDisabledColor = colorTextDisabled;
ItemPadding = new Thickness(SharedToken.UniformlyPaddingSM, 0);
@@ -100,7 +100,7 @@
<Setter Property="MaskShadows" Value="{DynamicResource {x:Static atom:ComboBoxTokenKey.PopupBoxShadows}}"/>
</Style>
<Style Selector="^ /template/ Border#PART_PopupFrame">
<Setter Property="Background" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBgContainer}}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBgElevated}}"/>
<Setter Property="CornerRadius" Value="{DynamicResource {x:Static atom:ComboBoxTokenKey.PopupBorderRadius}}"/>
<Setter Property="Padding" Value="{DynamicResource {x:Static atom:ComboBoxTokenKey.PopupContentPadding}}"/>
</Style>
@@ -20,7 +20,7 @@
</Setter>
<Style Selector="^:is(atom|MenuFlyoutPresenter)">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Background" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBgElevated}}" />
<Setter Property="CornerRadius" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuPopupBorderRadius}}" />
<Setter Property="IsMotionEnabled" Value="{DynamicResource {x:Static atom:SharedTokenKey.EnableMotion}}" />
+1 -2
View File
@@ -250,7 +250,6 @@ internal class MenuToken : AbstractControlDesignToken
var colorTextSecondary = SharedToken.ColorTextQuaternary;
var colorBgContainer = SharedToken.ColorBgContainer;
var colorBgTextHover = SharedToken.ColorBgTextHover;
var colorBgElevated = SharedToken.ColorBgElevated;
var padding = SharedToken.UniformlyPadding;
var controlHeight = SharedToken.ControlHeight;
var controlHeightSM = SharedToken.ControlHeightSM;
@@ -275,7 +274,7 @@ internal class MenuToken : AbstractControlDesignToken
DangerItemHoverColor = colorError;
ItemHeight = controlHeightSM;
MenuBgColor = colorBgElevated;
MenuBgColor = SharedToken.ColorBgContainer;
ItemPaddingInline = new Thickness(padding, SharedToken.UniformlyPaddingXXS);
ItemIconSize = SharedToken.IconSize;
@@ -26,7 +26,7 @@
</ControlTemplate>
</Setter>
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="IsMotionEnabled" Value="{DynamicResource {x:Static atom:SharedTokenKey.EnableMotion}}" />
<Setter Property="Background" Value="{DynamicResource {x:Static atom:MenuTokenKey.MenuBgColor}}" />
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static atom:SharedTokenKey.ColorBorder}}" />
+7 -1
View File
@@ -653,13 +653,19 @@ public class Popup : AvaloniaPopup,
return;
}
// 防止干扰
if (!popupRoot.IsVisible)
{
return;
}
using (BeginIgnoringIsOpen())
{
SetCurrentValue(IsMotionAwareOpenProperty, false);
}
_closeAnimating = true;
var shadowAwareLayer = _buddyLayer as IShadowAwareLayer;
Debug.Assert(shadowAwareLayer != null);
+2 -1
View File
@@ -144,8 +144,9 @@ public class Segmented : SelectingItemsControl,
{
SelectedIndex = 0;
}
SetupSelectedThumbRect();
ConfigureTransitions();
}
private void HandleSelectionChanged(object? sender, SelectionChangedEventArgs args)
+12 -12
View File
@@ -47,24 +47,24 @@ public class Tag : TemplatedControl,
{
#region 公共属性定义
public static readonly StyledProperty<string?> TagColorProperty
= AvaloniaProperty.Register<Tag, string?>(
public static readonly StyledProperty<string?> TagColorProperty =
AvaloniaProperty.Register<Tag, string?>(
nameof(Color));
public static readonly StyledProperty<bool> IsClosableProperty
= AvaloniaProperty.Register<Tag, bool>(nameof(IsClosable));
public static readonly StyledProperty<bool> IsClosableProperty =
AvaloniaProperty.Register<Tag, bool>(nameof(IsClosable));
public static readonly StyledProperty<bool> BorderedProperty
= AvaloniaProperty.Register<Tag, bool>(nameof(Bordered), true);
public static readonly StyledProperty<bool> BorderedProperty =
AvaloniaProperty.Register<Tag, bool>(nameof(Bordered), true);
public static readonly StyledProperty<Icon?> IconProperty
= AvaloniaProperty.Register<Tag, Icon?>(nameof(Icon));
public static readonly StyledProperty<Icon?> IconProperty =
AvaloniaProperty.Register<Tag, Icon?>(nameof(Icon));
public static readonly StyledProperty<Icon?> CloseIconProperty
= AvaloniaProperty.Register<Tag, Icon?>(nameof(CloseIcon));
public static readonly StyledProperty<Icon?> CloseIconProperty =
AvaloniaProperty.Register<Tag, Icon?>(nameof(CloseIcon));
public static readonly StyledProperty<string?> TagTextProperty
= AvaloniaProperty.Register<Tag, string?>(
public static readonly StyledProperty<string?> TagTextProperty =
AvaloniaProperty.Register<Tag, string?>(
nameof(TagText));
public string? TagColor
+2
View File
@@ -1,5 +1,6 @@
using AtomUI.Animations;
using AtomUI.Controls;
using AtomUI.Theme.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
@@ -191,6 +192,7 @@ public class Icon : Control, ICustomHitTest, IMotionAwareControl
RenderTransform = new RotateTransform();
_sourceGeometriesData = new List<Geometry>();
_transforms = new List<Matrix>();
this.ConfigureMotionBindingStyle();
}
private void ConfigureTransitions()
@@ -34,6 +34,10 @@ public class DarkThemeVariantCalculator : AbstractThemeVariantCalculator
// Dark tokens
SetupColorPalettes(designToken);
CalculateColorMapTokenValues(designToken);
// Customize selected item background color
// https://github.com/ant-design/ant-design/issues/30524#issuecomment-871961867
designToken.ColorPrimaryBg = designToken.ColorPrimaryBorder;
designToken.ColorPrimaryBgHover = designToken.ColorPrimaryBorderHover;
}
protected override ColorMap GenerateColorPalettes(Color baseColor)