重构组件

This commit is contained in:
polarboy 2024-07-14 15:08:26 +08:00
parent b6f9bbf6d7
commit a0a778e5cb
31 changed files with 3445 additions and 3344 deletions

View File

@ -1,7 +1,12 @@
using AtomUI.Data;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
namespace AtomUI.Controls;
@ -14,7 +19,7 @@ public enum AlertType
Error
}
public partial class Alert : BorderedStyleControl
public class Alert : BorderedStyleControl, IControlCustomStyle
{
public static readonly StyledProperty<AlertType> TypeProperty =
AvaloniaProperty.Register<Alert, AlertType>(nameof(AlertType));
@ -90,6 +95,18 @@ public partial class Alert : BorderedStyleControl
set => SetValue(ExtraActionProperty, value);
}
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private bool _initialized = false;
private Grid? _mainLayout;
private Label? _messageLabel;
private MarqueeLabel? _messageMarqueeLabel;
private Label? _descriptionLabel;
private PathIcon? _icon;
private IconButton? _closeButton;
private StackPanel? _infoStack;
private bool _scalingAwareConfigApplied = false;
static Alert()
{
AffectsMeasure<Segmented>(IsClosableProperty,
@ -130,4 +147,278 @@ public partial class Alert : BorderedStyleControl
base.OnAttachedToVisualTree(e);
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
#region IControlCustomStyle
void IControlCustomStyle.InitOnConstruct()
{
_mainLayout = new Grid()
{
RowDefinitions =
{
new RowDefinition(GridLength.Auto)
},
ColumnDefinitions =
{
new ColumnDefinition(GridLength.Auto),
new ColumnDefinition(GridLength.Star),
new ColumnDefinition(GridLength.Auto),
new ColumnDefinition(GridLength.Auto)
}
};
}
void IControlCustomStyle.SetupUi()
{
HorizontalAlignment = HorizontalAlignment.Stretch;
_customStyle.ApplyFixedStyleConfig();
CreateStructure();
SetupPaddingStyleConfig();
HandleDescriptionEnabled(!string.IsNullOrEmpty(Description));
Child = _mainLayout;
}
private void CreateStructure()
{
_descriptionLabel = new Label
{
Content = Description,
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Left,
VerticalContentAlignment = VerticalAlignment.Center,
Padding = new Thickness(0)
};
TextBlock.SetTextWrapping(_descriptionLabel, TextWrapping.Wrap);
_controlTokenBinder.AddControlBinding(_descriptionLabel!, Label.MarginProperty, GlobalResourceKey.MarginXS);
_descriptionLabel.IsVisible = !string.IsNullOrEmpty(Description);
_infoStack = new StackPanel()
{
Orientation = Orientation.Vertical,
VerticalAlignment = VerticalAlignment.Center
};
HandleMessageMarqueEnabled(IsMessageMarqueEnabled);
SetupCloseButton();
SetupTypeIcon();
_infoStack.Children.Add(_descriptionLabel);
Grid.SetColumn(_infoStack, 1);
Grid.SetRow(_infoStack, 0);
_mainLayout!.Children.Add(_infoStack);
if (ExtraAction is not null) {
ExtraAction.VerticalAlignment = VerticalAlignment.Top;
Grid.SetColumn(ExtraAction, 2);
Grid.SetRow(ExtraAction, 0);
_controlTokenBinder.AddControlBinding(ExtraAction, MarginProperty, AlertResourceKey.ExtraElementMargin);
_mainLayout!.Children.Add(ExtraAction);
}
}
private Control GetMessageControl()
{
if (IsMessageMarqueEnabled) {
return _messageMarqueeLabel!;
}
return _messageLabel!;
}
public void SetupCloseButton()
{
if (IsClosable && _closeButton is null) {
if (CloseIcon is null) {
CloseIcon = new PathIcon
{
Kind = "CloseOutlined",
};
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorIcon);
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorIconHover);
}
_closeButton = new IconButton
{
VerticalAlignment = VerticalAlignment.Top,
};
_controlTokenBinder.AddControlBinding(_closeButton, WidthProperty, GlobalResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(_closeButton, HeightProperty, GlobalResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(_closeButton, MarginProperty, AlertResourceKey.ExtraElementMargin);
Grid.SetRow(_closeButton, 0);
Grid.SetColumn(_closeButton, 3);
_mainLayout!.Children.Add(_closeButton);
BindUtils.RelayBind(this, CloseIconProperty, _closeButton, IconButton.IconProperty);
} else if (_closeButton is not null) {
_closeButton.IsVisible = IsClosable;
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
ApplyAlertTypeStyleConfig();
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
}
private void SetupPaddingStyleConfig()
{
if (_descriptionLabel!.IsVisible) {
_controlTokenBinder.AddControlBinding(PaddingProperty, AlertResourceKey.WithDescriptionPadding);
} else {
_controlTokenBinder.AddControlBinding(PaddingProperty, AlertResourceKey.DefaultPadding);
}
}
private void ApplyAlertTypeStyleConfig()
{
if (Type == AlertType.Success) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorSuccessBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorSuccessBorder);
} else if (Type == AlertType.Info) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorInfoBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorInfoBorder);
} else if (Type == AlertType.Warning) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorWarningBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorWarningBorder);
} else if (Type == AlertType.Error) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorder);
}
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
if (!_scalingAwareConfigApplied) {
_scalingAwareConfigApplied = true;
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this));
}
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (_initialized) {
if (e.Property == DescriptionProperty) {
var desc = e.GetNewValue<string?>();
var enabled = !string.IsNullOrEmpty(desc);
HandleDescriptionEnabled(enabled);
HandleIconForDescriptionEnabled(enabled);
_descriptionLabel!.IsVisible = enabled;
} else if (e.Property == IsMessageMarqueEnabledProperty) {
var enabled = e.GetNewValue<bool>();
HandleMessageMarqueEnabled(enabled);
}
}
}
private void HandleDescriptionEnabled(bool enabled)
{
var messageControl = GetMessageControl();
if (enabled) {
_controlTokenBinder.AddControlBinding(messageControl, FontSizeProperty, GlobalResourceKey.FontSizeLG);
_controlTokenBinder.AddControlBinding(messageControl, MarginProperty, AlertResourceKey.MessageWithDescriptionMargin);
if (_closeButton is not null) {
_closeButton.VerticalAlignment = VerticalAlignment.Top;
}
messageControl.VerticalAlignment = VerticalAlignment.Top;
} else {
_controlTokenBinder.AddControlBinding(messageControl, FontSizeProperty, GlobalResourceKey.FontSize);
messageControl.Margin = new Thickness();
if (_closeButton is not null) {
_closeButton.VerticalAlignment = VerticalAlignment.Center;
}
messageControl.VerticalAlignment = VerticalAlignment.Stretch;
}
}
private void HandleIconForDescriptionEnabled(bool enabled)
{
if (_icon is not null && IsShowIcon) {
if (enabled) {
_controlTokenBinder.AddControlBinding(_icon, WidthProperty, AlertResourceKey.WithDescriptionIconSize);
_controlTokenBinder.AddControlBinding(_icon, HeightProperty, AlertResourceKey.WithDescriptionIconSize);
_controlTokenBinder.AddControlBinding(_icon, MarginProperty, AlertResourceKey.IconWithDescriptionMargin);
_icon.VerticalAlignment = VerticalAlignment.Top;
} else {
_controlTokenBinder.AddControlBinding(_icon, WidthProperty, AlertResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(_icon, HeightProperty, AlertResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(_icon, MarginProperty, AlertResourceKey.IconDefaultMargin);
_icon.VerticalAlignment = VerticalAlignment.Center;
}
}
}
private void SetupTypeIcon()
{
if (IsShowIcon) {
if (_icon is null) {
var kind = string.Empty;
var resourceKey = string.Empty;
if (Type == AlertType.Success) {
kind = "CheckCircleFilled";
resourceKey = GlobalResourceKey.ColorSuccess;
} else if (Type == AlertType.Info) {
kind = "InfoCircleFilled";
resourceKey = GlobalResourceKey.ColorPrimary;
} else if (Type == AlertType.Warning) {
kind = "ExclamationCircleFilled";
resourceKey = GlobalResourceKey.ColorWarning;
} else if (Type == AlertType.Error) {
kind = "CloseCircleFilled";
resourceKey = GlobalResourceKey.ColorError;
}
_icon = new PathIcon
{
Kind = kind,
};
_controlTokenBinder.AddControlBinding(_icon, PathIcon.NormalFillBrushProperty, resourceKey);
HandleIconForDescriptionEnabled(_descriptionLabel!.IsVisible);
Grid.SetRow(_icon, 0);
Grid.SetColumn(_icon, 0);
_mainLayout!.Children.Add(_icon);
}
} else if (_icon is not null) {
_icon.IsVisible = false;
}
}
private void HandleMessageMarqueEnabled(bool enabled)
{
if (!enabled) {
if (_messageLabel is null) {
_messageLabel = new Label
{
Content = Message,
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Left,
VerticalContentAlignment = VerticalAlignment.Center,
Padding = new Thickness(0)
};
TextBlock.SetTextWrapping(_messageLabel, TextWrapping.Wrap);
}
if (_messageMarqueeLabel is not null) {
_infoStack!.Children.Remove(_messageMarqueeLabel);
}
_infoStack!.Children.Insert(0, _messageLabel);
} else {
if (_messageMarqueeLabel is null) {
_messageMarqueeLabel = new MarqueeLabel
{
Text = Message,
HorizontalAlignment = HorizontalAlignment.Stretch,
Padding = new Thickness(0)
};
}
if (_messageLabel is not null) {
_infoStack!.Children.Remove(_messageLabel);
}
_infoStack!.Children.Insert(0, _messageMarqueeLabel);
}
}
#endregion
}

View File

@ -1,297 +0,0 @@
using AtomUI.Data;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.Media;
namespace AtomUI.Controls;
public partial class Alert : IControlCustomStyle
{
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private bool _initialized = false;
private Grid? _mainLayout;
private Label? _messageLabel;
private MarqueeLabel? _messageMarqueeLabel;
private Label? _descriptionLabel;
private PathIcon? _icon;
private IconButton? _closeButton;
private StackPanel? _infoStack;
private bool _scalingAwareConfigApplied = false;
void IControlCustomStyle.InitOnConstruct()
{
_mainLayout = new Grid()
{
RowDefinitions =
{
new RowDefinition(GridLength.Auto)
},
ColumnDefinitions =
{
new ColumnDefinition(GridLength.Auto),
new ColumnDefinition(GridLength.Star),
new ColumnDefinition(GridLength.Auto),
new ColumnDefinition(GridLength.Auto)
}
};
}
void IControlCustomStyle.SetupUi()
{
HorizontalAlignment = HorizontalAlignment.Stretch;
_customStyle.ApplyFixedStyleConfig();
CreateStructure();
SetupPaddingStyleConfig();
HandleDescriptionEnabled(!string.IsNullOrEmpty(Description));
Child = _mainLayout;
}
private void CreateStructure()
{
_descriptionLabel = new Label
{
Content = Description,
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Left,
VerticalContentAlignment = VerticalAlignment.Center,
Padding = new Thickness(0)
};
TextBlock.SetTextWrapping(_descriptionLabel, TextWrapping.Wrap);
_controlTokenBinder.AddControlBinding(_descriptionLabel!, Label.MarginProperty, GlobalResourceKey.MarginXS);
_descriptionLabel.IsVisible = !string.IsNullOrEmpty(Description);
_infoStack = new StackPanel()
{
Orientation = Orientation.Vertical,
VerticalAlignment = VerticalAlignment.Center
};
HandleMessageMarqueEnabled(IsMessageMarqueEnabled);
SetupCloseButton();
SetupTypeIcon();
_infoStack.Children.Add(_descriptionLabel);
Grid.SetColumn(_infoStack, 1);
Grid.SetRow(_infoStack, 0);
_mainLayout!.Children.Add(_infoStack);
if (ExtraAction is not null) {
ExtraAction.VerticalAlignment = VerticalAlignment.Top;
Grid.SetColumn(ExtraAction, 2);
Grid.SetRow(ExtraAction, 0);
_controlTokenBinder.AddControlBinding(ExtraAction, MarginProperty, AlertResourceKey.ExtraElementMargin);
_mainLayout!.Children.Add(ExtraAction);
}
}
private Control GetMessageControl()
{
if (IsMessageMarqueEnabled) {
return _messageMarqueeLabel!;
}
return _messageLabel!;
}
public void SetupCloseButton()
{
if (IsClosable && _closeButton is null) {
if (CloseIcon is null) {
CloseIcon = new PathIcon
{
Kind = "CloseOutlined",
};
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorIcon);
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorIconHover);
}
_closeButton = new IconButton
{
VerticalAlignment = VerticalAlignment.Top,
};
_controlTokenBinder.AddControlBinding(_closeButton, WidthProperty, GlobalResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(_closeButton, HeightProperty, GlobalResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(_closeButton, MarginProperty, AlertResourceKey.ExtraElementMargin);
Grid.SetRow(_closeButton, 0);
Grid.SetColumn(_closeButton, 3);
_mainLayout!.Children.Add(_closeButton);
BindUtils.RelayBind(this, CloseIconProperty, _closeButton, IconButton.IconProperty);
} else if (_closeButton is not null) {
_closeButton.IsVisible = IsClosable;
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
ApplyAlertTypeStyleConfig();
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
}
private void SetupPaddingStyleConfig()
{
if (_descriptionLabel!.IsVisible) {
_controlTokenBinder.AddControlBinding(PaddingProperty, AlertResourceKey.WithDescriptionPadding);
} else {
_controlTokenBinder.AddControlBinding(PaddingProperty, AlertResourceKey.DefaultPadding);
}
}
private void ApplyAlertTypeStyleConfig()
{
if (Type == AlertType.Success) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorSuccessBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorSuccessBorder);
} else if (Type == AlertType.Info) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorInfoBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorInfoBorder);
} else if (Type == AlertType.Warning) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorWarningBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorWarningBorder);
} else if (Type == AlertType.Error) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorder);
}
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
if (!_scalingAwareConfigApplied) {
_scalingAwareConfigApplied = true;
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this));
}
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (_initialized) {
if (e.Property == DescriptionProperty) {
var desc = e.GetNewValue<string?>();
var enabled = !string.IsNullOrEmpty(desc);
HandleDescriptionEnabled(enabled);
HandleIconForDescriptionEnabled(enabled);
_descriptionLabel!.IsVisible = enabled;
} else if (e.Property == IsMessageMarqueEnabledProperty) {
var enabled = e.GetNewValue<bool>();
HandleMessageMarqueEnabled(enabled);
}
}
}
private void HandleDescriptionEnabled(bool enabled)
{
var messageControl = GetMessageControl();
if (enabled) {
_controlTokenBinder.AddControlBinding(messageControl, FontSizeProperty, GlobalResourceKey.FontSizeLG);
_controlTokenBinder.AddControlBinding(messageControl, MarginProperty, AlertResourceKey.MessageWithDescriptionMargin);
if (_closeButton is not null) {
_closeButton.VerticalAlignment = VerticalAlignment.Top;
}
messageControl.VerticalAlignment = VerticalAlignment.Top;
} else {
_controlTokenBinder.AddControlBinding(messageControl, FontSizeProperty, GlobalResourceKey.FontSize);
messageControl.Margin = new Thickness();
if (_closeButton is not null) {
_closeButton.VerticalAlignment = VerticalAlignment.Center;
}
messageControl.VerticalAlignment = VerticalAlignment.Stretch;
}
}
private void HandleIconForDescriptionEnabled(bool enabled)
{
if (_icon is not null && IsShowIcon) {
if (enabled) {
_controlTokenBinder.AddControlBinding(_icon, WidthProperty, AlertResourceKey.WithDescriptionIconSize);
_controlTokenBinder.AddControlBinding(_icon, HeightProperty, AlertResourceKey.WithDescriptionIconSize);
_controlTokenBinder.AddControlBinding(_icon, MarginProperty, AlertResourceKey.IconWithDescriptionMargin);
_icon.VerticalAlignment = VerticalAlignment.Top;
} else {
_controlTokenBinder.AddControlBinding(_icon, WidthProperty, AlertResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(_icon, HeightProperty, AlertResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(_icon, MarginProperty, AlertResourceKey.IconDefaultMargin);
_icon.VerticalAlignment = VerticalAlignment.Center;
}
}
}
private void SetupTypeIcon()
{
if (IsShowIcon) {
if (_icon is null) {
var kind = string.Empty;
var resourceKey = string.Empty;
if (Type == AlertType.Success) {
kind = "CheckCircleFilled";
resourceKey = GlobalResourceKey.ColorSuccess;
} else if (Type == AlertType.Info) {
kind = "InfoCircleFilled";
resourceKey = GlobalResourceKey.ColorPrimary;
} else if (Type == AlertType.Warning) {
kind = "ExclamationCircleFilled";
resourceKey = GlobalResourceKey.ColorWarning;
} else if (Type == AlertType.Error) {
kind = "CloseCircleFilled";
resourceKey = GlobalResourceKey.ColorError;
}
_icon = new PathIcon
{
Kind = kind,
};
_controlTokenBinder.AddControlBinding(_icon, PathIcon.NormalFillBrushProperty, resourceKey);
HandleIconForDescriptionEnabled(_descriptionLabel!.IsVisible);
Grid.SetRow(_icon, 0);
Grid.SetColumn(_icon, 0);
_mainLayout!.Children.Add(_icon);
}
} else if (_icon is not null) {
_icon.IsVisible = false;
}
}
private void HandleMessageMarqueEnabled(bool enabled)
{
if (!enabled) {
if (_messageLabel is null) {
_messageLabel = new Label
{
Content = Message,
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Left,
VerticalContentAlignment = VerticalAlignment.Center,
Padding = new Thickness(0)
};
TextBlock.SetTextWrapping(_messageLabel, TextWrapping.Wrap);
}
if (_messageMarqueeLabel is not null) {
_infoStack!.Children.Remove(_messageMarqueeLabel);
}
_infoStack!.Children.Insert(0, _messageLabel);
} else {
if (_messageMarqueeLabel is null) {
_messageMarqueeLabel = new MarqueeLabel
{
Text = Message,
HorizontalAlignment = HorizontalAlignment.Stretch,
Padding = new Thickness(0)
};
}
if (_messageLabel is not null) {
_infoStack!.Children.Remove(_messageLabel);
}
_infoStack!.Children.Insert(0, _messageMarqueeLabel);
}
}
}

View File

@ -1,7 +1,14 @@
using AtomUI.Data;
using System.Reactive.Disposables;
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
namespace AtomUI.Controls;
@ -70,7 +77,9 @@ public enum ArrowPosition
RightEdgeAlignedBottom
}
public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
public partial class ArrowDecoratedBox : StyledControl,
IShadowMaskInfoProvider,
IControlCustomStyle
{
public static readonly StyledProperty<bool> IsShowArrowProperty =
AvaloniaProperty.Register<ArrowDecoratedBox, bool>(nameof(IsShowArrow), true);
@ -133,6 +142,16 @@ public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
set => SetValue(ChildProperty, value);
}
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private Geometry? _arrowGeometry;
private Rect _contentRect;
private Rect _arrowRect;
private Border? _container;
private CompositeDisposable? _compositeDisposable;
private bool _needGenerateArrowVertexPoint = true;
static ArrowDecoratedBox()
{
AffectsMeasure<ArrowDecoratedBox>(ArrowPositionProperty, IsShowArrowProperty);
@ -206,4 +225,282 @@ public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
{
return GetContentRect(DesiredSize).Deflate(0.5);
}
#region IControlCustomStyle
// 组件的 Token 绑定属性
private double _arrowSize;
private static readonly DirectProperty<ArrowDecoratedBox, double> ArrowSizeTokenProperty
= AvaloniaProperty.RegisterDirect<ArrowDecoratedBox, double>(nameof(_arrowSize),
(o) => o._arrowSize,
(o, v) => o._arrowSize = v);
// 组件的 Token 绑定属性
void IControlCustomStyle.SetupUi()
{
_container = new Border();
NotifyCreateUi();
_customStyle.ApplyFixedStyleConfig();
if (IsShowArrow) {
BuildGeometry(true);
}
LogicalChildren.Add(_container);
VisualChildren.Add(_container);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainer);
_initialized = true;
}
private void SetupRelayProperties()
{
_compositeDisposable = new CompositeDisposable();
// 生命周期一样,可以不用管理
if (_container is not null) {
if (Child?.Parent is not null) {
UiStructureUtils.ClearLogicalParentRecursive(Child, null);
UiStructureUtils.ClearVisualParentRecursive(Child, null);
}
_compositeDisposable.Add(BindUtils.RelayBind(this, BackgroundSizingProperty, _container));
_compositeDisposable.Add(BindUtils.RelayBind(this, BackgroundProperty, _container));
_compositeDisposable.Add(BindUtils.RelayBind(this, CornerRadiusProperty, _container));
_compositeDisposable.Add(BindUtils.RelayBind(this, ChildProperty, _container));
_compositeDisposable.Add(BindUtils.RelayBind(this, PaddingProperty, _container));
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
NotifyApplyFixedStyleConfig();
}
private (double, double) GetArrowVertexPoint()
{
if (_needGenerateArrowVertexPoint) {
BuildGeometry(true);
_arrowRect = GetArrowRect(DesiredSize);
_needGenerateArrowVertexPoint = false;
}
return _arrowVertexPoint;
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsShowArrowProperty ||
e.Property == ArrowPositionProperty ||
e.Property == ArrowSizeTokenProperty ||
e.Property == VisualParentProperty) {
if (e.Property == IsShowArrowProperty && VisualRoot is null) {
// 当开启的时候,但是还没有加入的渲染树,这个时候我们取不到 Token 需要在取值的时候重新生成一下
_needGenerateArrowVertexPoint = true;
}
if (_initialized && VisualRoot is not null) {
BuildGeometry(true);
_arrowRect = GetArrowRect(DesiredSize);
}
}
}
private void BuildGeometry(bool force = false)
{
if (_arrowGeometry is null || force) {
_arrowGeometry = CommonShapeBuilder.BuildArrow(_arrowSize, 1.5);
}
}
protected virtual void NotifyApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(MinHeightProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(PaddingProperty, GlobalResourceKey.PaddingXS);
_controlTokenBinder.AddControlBinding(ArrowSizeTokenProperty, ArrowDecoratedBoxResourceKey.ArrowSize);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
}
public sealed override void Render(DrawingContext context)
{
if (IsShowArrow) {
var direction = GetDirection(ArrowPosition);
var matrix = Matrix.CreateTranslation(-_arrowSize / 2, -_arrowSize / 2);
if (direction == Direction.Right) {
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(90));
matrix *= Matrix.CreateTranslation(_arrowSize / 2, _arrowSize / 2);
} else if (direction == Direction.Top) {
matrix *= Matrix.CreateTranslation(_arrowSize / 2, 0);
} else if (direction == Direction.Left) {
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(-90));
matrix *= Matrix.CreateTranslation(0, _arrowSize / 2);
} else {
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(180));
matrix *= Matrix.CreateTranslation(_arrowSize / 2, _arrowSize / 2);
}
matrix *= Matrix.CreateTranslation(_arrowRect.X, _arrowRect.Y);
_arrowGeometry!.Transform = new MatrixTransform(matrix);
context.DrawGeometry(_container?.Background, null, _arrowGeometry);
}
}
protected override Size MeasureOverride(Size availableSize)
{
var size = base.MeasureOverride(availableSize);
var targetWidth = size.Width;
var targetHeight = size.Height;
targetHeight = Math.Max(MinHeight, targetHeight);
if (IsShowArrow) {
BuildGeometry();
var realArrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width);
var direction = GetDirection(ArrowPosition);
if (direction == Direction.Left || direction == Direction.Right) {
targetWidth += realArrowSize;
} else {
targetHeight += realArrowSize;
}
}
var targetSize = new Size(targetWidth, targetHeight);
_arrowRect = GetArrowRect(targetSize);
return targetSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
var visualChildren = VisualChildren;
var visualCount = visualChildren.Count;
_contentRect = GetContentRect(finalSize);
for (int i = 0; i < visualCount; ++i) {
var child = visualChildren[i];
if (child is Layoutable layoutable) {
layoutable.Arrange(_contentRect);
}
}
return finalSize;
}
private Rect GetContentRect(Size finalSize)
{
var offsetX = 0d;
var offsetY = 0d;
var targetWidth = finalSize.Width;
var targetHeight = finalSize.Height;
if (IsShowArrow) {
var arrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width) + 0.5;
var direction = GetDirection(ArrowPosition);
if (direction == Direction.Left || direction == Direction.Right) {
targetWidth -= arrowSize;
} else {
targetHeight -= arrowSize;
}
if (direction == Direction.Right) {
offsetX = 0.5;
} else if (direction == Direction.Bottom) {
offsetY = 0.5;
} else if (direction == Direction.Top) {
offsetY = arrowSize - 0.5;
} else {
offsetX = arrowSize - 0.5;
}
}
return new Rect(offsetX, offsetY, targetWidth, targetHeight);
}
private Rect GetArrowRect(Size finalSize)
{
var offsetX = 0d;
var offsetY = 0d;
var targetWidth = 0d;
var targetHeight = 0d;
var position = ArrowPosition;
if (IsShowArrow) {
var size = _arrowGeometry!.Bounds.Size;
var minValue = Math.Min(size.Width, size.Height);
var maxValue = Math.Max(size.Width, size.Height);
if (position == ArrowPosition.Left ||
position == ArrowPosition.LeftEdgeAlignedTop ||
position == ArrowPosition.LeftEdgeAlignedBottom) {
targetWidth = minValue;
targetHeight = maxValue;
if (position == ArrowPosition.Left) {
offsetY = (finalSize.Height - maxValue) / 2;
} else if (position == ArrowPosition.LeftEdgeAlignedTop) {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = minValue;
} else {
offsetY = maxValue;
}
} else {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = finalSize.Height - minValue - maxValue;
} else {
offsetY = finalSize.Height - maxValue * 2;
}
}
} else if (position == ArrowPosition.Top ||
position == ArrowPosition.TopEdgeAlignedLeft ||
position == ArrowPosition.TopEdgeAlignedRight) {
if (position == ArrowPosition.TopEdgeAlignedLeft) {
offsetX = maxValue;
} else if (position == ArrowPosition.Top) {
offsetX = (finalSize.Width - maxValue) / 2;
} else {
offsetX = finalSize.Width - maxValue * 2;
}
targetWidth = maxValue;
targetHeight = minValue;
} else if (position == ArrowPosition.Right ||
position == ArrowPosition.RightEdgeAlignedTop ||
position == ArrowPosition.RightEdgeAlignedBottom) {
offsetX = finalSize.Width - minValue;
if (position == ArrowPosition.Right) {
offsetY = (finalSize.Height - maxValue) / 2;
} else if (position == ArrowPosition.RightEdgeAlignedTop) {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = minValue;
} else {
offsetY = maxValue;
}
} else {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = finalSize.Height - minValue - maxValue;
} else {
offsetY = finalSize.Height - maxValue * 2;
}
}
targetWidth = minValue;
targetHeight = maxValue;
} else {
offsetY = finalSize.Height - minValue;
targetWidth = maxValue;
targetHeight = minValue;
if (position == ArrowPosition.BottomEdgeAlignedLeft) {
offsetX = maxValue;
} else if (position == ArrowPosition.Bottom) {
offsetX = (finalSize.Width - maxValue) / 2;
} else {
offsetX = finalSize.Width - maxValue * 2;
}
}
}
var targetRect = new Rect(offsetX, offsetY, targetWidth, targetHeight);
var center = targetRect.Center;
// 计算中点
var direction = GetDirection(position);
if (direction == Direction.Left || direction == Direction.Right) {
_arrowVertexPoint = (center.Y, finalSize.Height - center.Y);
} else if (direction == Direction.Top || direction == Direction.Bottom) {
_arrowVertexPoint = (center.X, finalSize.Width - center.X);
}
return targetRect;
}
#endregion
}

View File

@ -1,301 +0,0 @@
using System.Reactive.Disposables;
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
namespace AtomUI.Controls;
public partial class ArrowDecoratedBox : IControlCustomStyle
{
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private Geometry? _arrowGeometry;
private Rect _contentRect;
private Rect _arrowRect;
private Border? _container;
private CompositeDisposable? _compositeDisposable;
private bool _needGenerateArrowVertexPoint = true;
// 组件的 Token 绑定属性
private double _arrowSize;
private static readonly DirectProperty<ArrowDecoratedBox, double> ArrowSizeTokenProperty
= AvaloniaProperty.RegisterDirect<ArrowDecoratedBox, double>(nameof(_arrowSize),
(o) => o._arrowSize,
(o, v) => o._arrowSize = v);
// 组件的 Token 绑定属性
void IControlCustomStyle.SetupUi()
{
_container = new Border();
NotifyCreateUi();
_customStyle.ApplyFixedStyleConfig();
if (IsShowArrow) {
BuildGeometry(true);
}
LogicalChildren.Add(_container);
VisualChildren.Add(_container);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainer);
_initialized = true;
}
private void SetupRelayProperties()
{
_compositeDisposable = new CompositeDisposable();
// 生命周期一样,可以不用管理
if (_container is not null) {
if (Child?.Parent is not null) {
UiStructureUtils.ClearLogicalParentRecursive(Child, null);
UiStructureUtils.ClearVisualParentRecursive(Child, null);
}
_compositeDisposable.Add(BindUtils.RelayBind(this, BackgroundSizingProperty, _container));
_compositeDisposable.Add(BindUtils.RelayBind(this, BackgroundProperty, _container));
_compositeDisposable.Add(BindUtils.RelayBind(this, CornerRadiusProperty, _container));
_compositeDisposable.Add(BindUtils.RelayBind(this, ChildProperty, _container));
_compositeDisposable.Add(BindUtils.RelayBind(this, PaddingProperty, _container));
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
NotifyApplyFixedStyleConfig();
}
private (double, double) GetArrowVertexPoint()
{
if (_needGenerateArrowVertexPoint) {
BuildGeometry(true);
_arrowRect = GetArrowRect(DesiredSize);
_needGenerateArrowVertexPoint = false;
}
return _arrowVertexPoint;
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsShowArrowProperty ||
e.Property == ArrowPositionProperty ||
e.Property == ArrowSizeTokenProperty ||
e.Property == VisualParentProperty) {
if (e.Property == IsShowArrowProperty && VisualRoot is null) {
// 当开启的时候,但是还没有加入的渲染树,这个时候我们取不到 Token 需要在取值的时候重新生成一下
_needGenerateArrowVertexPoint = true;
}
if (_initialized && VisualRoot is not null) {
BuildGeometry(true);
_arrowRect = GetArrowRect(DesiredSize);
}
}
}
private void BuildGeometry(bool force = false)
{
if (_arrowGeometry is null || force) {
_arrowGeometry = CommonShapeBuilder.BuildArrow(_arrowSize, 1.5);
}
}
protected virtual void NotifyApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(MinHeightProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(PaddingProperty, GlobalResourceKey.PaddingXS);
_controlTokenBinder.AddControlBinding(ArrowSizeTokenProperty, ArrowDecoratedBoxResourceKey.ArrowSize);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
}
public sealed override void Render(DrawingContext context)
{
if (IsShowArrow) {
var direction = GetDirection(ArrowPosition);
var matrix = Matrix.CreateTranslation(-_arrowSize / 2, -_arrowSize / 2);
if (direction == Direction.Right) {
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(90));
matrix *= Matrix.CreateTranslation(_arrowSize / 2, _arrowSize / 2);
} else if (direction == Direction.Top) {
matrix *= Matrix.CreateTranslation(_arrowSize / 2, 0);
} else if (direction == Direction.Left) {
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(-90));
matrix *= Matrix.CreateTranslation(0, _arrowSize / 2);
} else {
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(180));
matrix *= Matrix.CreateTranslation(_arrowSize / 2, _arrowSize / 2);
}
matrix *= Matrix.CreateTranslation(_arrowRect.X, _arrowRect.Y);
_arrowGeometry!.Transform = new MatrixTransform(matrix);
context.DrawGeometry(_container?.Background, null, _arrowGeometry);
}
}
protected override Size MeasureOverride(Size availableSize)
{
var size = base.MeasureOverride(availableSize);
var targetWidth = size.Width;
var targetHeight = size.Height;
targetHeight = Math.Max(MinHeight, targetHeight);
if (IsShowArrow) {
BuildGeometry();
var realArrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width);
var direction = GetDirection(ArrowPosition);
if (direction == Direction.Left || direction == Direction.Right) {
targetWidth += realArrowSize;
} else {
targetHeight += realArrowSize;
}
}
var targetSize = new Size(targetWidth, targetHeight);
_arrowRect = GetArrowRect(targetSize);
return targetSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
var visualChildren = VisualChildren;
var visualCount = visualChildren.Count;
_contentRect = GetContentRect(finalSize);
for (int i = 0; i < visualCount; ++i) {
var child = visualChildren[i];
if (child is Layoutable layoutable) {
layoutable.Arrange(_contentRect);
}
}
return finalSize;
}
private Rect GetContentRect(Size finalSize)
{
var offsetX = 0d;
var offsetY = 0d;
var targetWidth = finalSize.Width;
var targetHeight = finalSize.Height;
if (IsShowArrow) {
var arrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width) + 0.5;
var direction = GetDirection(ArrowPosition);
if (direction == Direction.Left || direction == Direction.Right) {
targetWidth -= arrowSize;
} else {
targetHeight -= arrowSize;
}
if (direction == Direction.Right) {
offsetX = 0.5;
} else if (direction == Direction.Bottom) {
offsetY = 0.5;
} else if (direction == Direction.Top) {
offsetY = arrowSize - 0.5;
} else {
offsetX = arrowSize - 0.5;
}
}
return new Rect(offsetX, offsetY, targetWidth, targetHeight);
}
private Rect GetArrowRect(Size finalSize)
{
var offsetX = 0d;
var offsetY = 0d;
var targetWidth = 0d;
var targetHeight = 0d;
var position = ArrowPosition;
if (IsShowArrow) {
var size = _arrowGeometry!.Bounds.Size;
var minValue = Math.Min(size.Width, size.Height);
var maxValue = Math.Max(size.Width, size.Height);
if (position == ArrowPosition.Left ||
position == ArrowPosition.LeftEdgeAlignedTop ||
position == ArrowPosition.LeftEdgeAlignedBottom) {
targetWidth = minValue;
targetHeight = maxValue;
if (position == ArrowPosition.Left) {
offsetY = (finalSize.Height - maxValue) / 2;
} else if (position == ArrowPosition.LeftEdgeAlignedTop) {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = minValue;
} else {
offsetY = maxValue;
}
} else {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = finalSize.Height - minValue - maxValue;
} else {
offsetY = finalSize.Height - maxValue * 2;
}
}
} else if (position == ArrowPosition.Top ||
position == ArrowPosition.TopEdgeAlignedLeft ||
position == ArrowPosition.TopEdgeAlignedRight) {
if (position == ArrowPosition.TopEdgeAlignedLeft) {
offsetX = maxValue;
} else if (position == ArrowPosition.Top) {
offsetX = (finalSize.Width - maxValue) / 2;
} else {
offsetX = finalSize.Width - maxValue * 2;
}
targetWidth = maxValue;
targetHeight = minValue;
} else if (position == ArrowPosition.Right ||
position == ArrowPosition.RightEdgeAlignedTop ||
position == ArrowPosition.RightEdgeAlignedBottom) {
offsetX = finalSize.Width - minValue;
if (position == ArrowPosition.Right) {
offsetY = (finalSize.Height - maxValue) / 2;
} else if (position == ArrowPosition.RightEdgeAlignedTop) {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = minValue;
} else {
offsetY = maxValue;
}
} else {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = finalSize.Height - minValue - maxValue;
} else {
offsetY = finalSize.Height - maxValue * 2;
}
}
targetWidth = minValue;
targetHeight = maxValue;
} else {
offsetY = finalSize.Height - minValue;
targetWidth = maxValue;
targetHeight = minValue;
if (position == ArrowPosition.BottomEdgeAlignedLeft) {
offsetX = maxValue;
} else if (position == ArrowPosition.Bottom) {
offsetX = (finalSize.Width - maxValue) / 2;
} else {
offsetX = finalSize.Width - maxValue * 2;
}
}
}
var targetRect = new Rect(offsetX, offsetY, targetWidth, targetHeight);
var center = targetRect.Center;
// 计算中点
var direction = GetDirection(position);
if (direction == Direction.Left || direction == Direction.Right) {
_arrowVertexPoint = (center.Y, finalSize.Height - center.Y);
} else if (direction == Direction.Top || direction == Direction.Bottom) {
_arrowVertexPoint = (center.X, finalSize.Width - center.X);
}
return targetRect;
}
}

View File

@ -1,7 +1,17 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.TokenSystem;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
namespace AtomUI.Controls;
@ -23,7 +33,10 @@ public enum ButtonShape
Round,
}
public partial class Button : AvaloniaButton, ISizeTypeAware
public partial class Button : AvaloniaButton,
ISizeTypeAware,
IControlCustomStyle,
IWaveAdornerInfoProvider
{
#region
public static readonly StyledProperty<ButtonType> ButtonTypeProperty =
@ -90,6 +103,13 @@ public partial class Button : AvaloniaButton, ISizeTypeAware
}
#endregion
private ControlStyleState _styleState;
private ControlTokenBinder _controlTokenBinder;
private IControlCustomStyle _customStyle;
private StackPanel? _stackPanel;
private Label? _label;
private bool _initialized = false;
static Button()
{
@ -144,4 +164,566 @@ public partial class Button : AvaloniaButton, ISizeTypeAware
base.OnAttachedToVisualTree(e);
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
#region IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
HorizontalAlignment = HorizontalAlignment.Left;
VerticalAlignment = VerticalAlignment.Bottom;
Cursor = new Cursor(StandardCursorType.Hand);
_customStyle.CollectStyleState();
CreateMainLayout();
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
ApplyShapeStyleConfig();
SetupIcon();
ApplyIconModeStyleConfig();
_customStyle.SetupTransitions();
if (ButtonType == ButtonType.Default) {
if (IsDanger) {
Effect = new DropShadowEffect
{
OffsetX = _dangerShadow.OffsetX,
OffsetY = _dangerShadow.OffsetY,
Color = _dangerShadow.Color,
BlurRadius = _dangerShadow.Blur,
};
} else {
Effect = new DropShadowEffect
{
OffsetX = _defaultShadow.OffsetX,
OffsetY = _defaultShadow.OffsetY,
Color = _defaultShadow.Color,
BlurRadius = _defaultShadow.Blur,
};
}
} else if (ButtonType == ButtonType.Primary) {
if (IsDanger) {
Effect = new DropShadowEffect
{
OffsetX = _dangerShadow.OffsetX,
OffsetY = _dangerShadow.OffsetY,
Color = _dangerShadow.Color,
BlurRadius = _dangerShadow.Blur,
};
} else {
Effect = new DropShadowEffect
{
OffsetX = _primaryShadow.OffsetX,
OffsetY = _primaryShadow.OffsetY,
Color = _primaryShadow.Color,
BlurRadius = _primaryShadow.Blur,
};
}
}
}
private void CreateMainLayout()
{
if (Text is null && Content is string content) {
Text = content;
}
_label = new Label()
{
Content = Text,
Padding = new Thickness(0),
VerticalContentAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
_stackPanel = new StackPanel()
{
UseLayoutRounding = false,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
Orientation = Orientation.Horizontal,
ClipToBounds = true
};
_stackPanel.Children.Add(_label);
Content = _stackPanel;
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
if (ButtonType == ButtonType.Primary) {
ApplyPrimaryStyle();
} else if (ButtonType == ButtonType.Default) {
ApplyDefaultStyle();
} else if (ButtonType == ButtonType.Text) {
ApplyTextStyle();
} else if (ButtonType == ButtonType.Link) {
ApplyLinkStyle();
}
}
void ApplyShapeStyleConfig()
{
if (Shape == ButtonShape.Circle) {
_controlTokenBinder.AddControlBinding(PaddingProperty, "CirclePadding");
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(PaddingXXSTokenProperty, "PaddingXXS");
_controlTokenBinder.AddControlBinding(DefaultShadowTokenProperty, "DefaultShadow");
_controlTokenBinder.AddControlBinding(PrimaryShadowTokenProperty, "PrimaryShadow");
_controlTokenBinder.AddControlBinding(DangerShadowTokenProperty, "DangerShadow");
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
if (ButtonType == ButtonType.Default ||
(ButtonType == ButtonType.Primary && (IsGhost || !IsEnabled))) {
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this));
}
}
void IControlCustomStyle.SetupTransitions()
{
var transitions = new Transitions();
if (ButtonType == ButtonType.Primary) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty));
if (IsGhost) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BorderBrushProperty));
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty));
}
} else if (ButtonType == ButtonType.Default) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BorderBrushProperty));
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty));
} else if (ButtonType == ButtonType.Text) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty));
} else if (ButtonType == ButtonType.Link) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty));
}
Transitions = transitions;
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
_controlTokenBinder.AddControlBinding(FontSizeProperty, ButtonResourceKey.ContentFontSizeSM);
_controlTokenBinder.AddControlBinding(PaddingProperty, ButtonResourceKey.PaddingSM);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(FontSizeProperty, ButtonResourceKey.ContentFontSize);
_controlTokenBinder.AddControlBinding(PaddingProperty, GlobalResourceKey.Padding);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
_controlTokenBinder.AddControlBinding(FontSizeProperty, ButtonResourceKey.ContentFontSizeLG);
_controlTokenBinder.AddControlBinding(PaddingProperty, ButtonResourceKey.PaddingLG);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
}
if (Icon is not null) {
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.IconSizeSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.IconSize);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.IconSizeLG);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.IconSizeLG);
}
}
}
private void ApplyPrimaryStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
// IsGhost 优先级最高
if (IsGhost) {
Background = new SolidColorBrush(Colors.Transparent);
if (IsDanger) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorError);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorderHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorBorderHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimary);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.PrimaryColor);
if (IsDanger) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorError);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorPrimary);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.BorderColorDisabled);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
}
}
private void ApplyDefaultStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, ButtonResourceKey.DefaultBg);
if (IsDanger) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorError);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
if (IsGhost) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
}
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorderHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorBorderHover,
BindingPriority.StyleTrigger);
}
} else {
if (IsGhost) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextLightSolid);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorTextLightSolid);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.DefaultBorderColor);
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultColor);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.DefaultActiveBorderColor,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultActiveColor,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.DefaultHoverBorderColor,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultHoverColor,
BindingPriority.StyleTrigger);
}
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.BorderColorDisabled);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
}
}
private void ApplyTextStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
if (IsDanger) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBgActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBgHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultColor);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgTextActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, ButtonResourceKey.TextHoverBg,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
}
}
private void ApplyLinkStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (IsGhost) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
} else {
_controlTokenBinder.AddControlBinding(BackgroundProperty, ButtonResourceKey.DefaultBg);
}
if (IsDanger) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorLink);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorLinkActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorLinkHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
}
}
// TODO 针对 primary 的是否是 ghost 没有完成
private void SetupIcon()
{
if (Icon is not null) {
_stackPanel!.Children.Insert(0, Icon);
if (Text is not null) {
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSize);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeLG);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeLG);
}
Icon.Margin = new Thickness(0, 0, _paddingXXS, 0);
} else {
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.OnlyIconSizeSM);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.OnlyIconSizeSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.OnlyIconSize);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.OnlyIconSize);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.OnlyIconSizeLG);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.OnlyIconSizeLG);
}
}
if (ButtonType == ButtonType.Primary) {
SetupPrimaryIconStyle();
} else if (ButtonType == ButtonType.Default || ButtonType == ButtonType.Link) {
SetupDefaultOrLinkIconStyle();
}
}
}
private void SetupPrimaryIconStyle()
{
if (Icon is null || Icon.ThemeType == IconThemeType.TwoTone) {
return;
}
// IsGhost 优先级最高
if (IsGhost) {
if (IsDanger) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorError);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorErrorActive);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorErrorBorderHover);
} else {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorPrimaryActive);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorPrimaryHover);
}
} else {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, ButtonResourceKey.PrimaryColor);
}
_controlTokenBinder.AddControlBinding(Icon, PathIcon.DisabledFilledBrushProperty, GlobalResourceKey.ColorTextDisabled);
}
private void SetupDefaultOrLinkIconStyle()
{
if (Icon is null || Icon.ThemeType == IconThemeType.TwoTone) {
return;
}
if (IsDanger) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorError);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorErrorActive);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorErrorBorderHover);
} else {
if (IsGhost) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorTextLightSolid);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorPrimaryActive);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorPrimaryHover);
} else {
if (ButtonType == ButtonType.Link) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorLink);
} else {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, ButtonResourceKey.DefaultColor);
}
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, ButtonResourceKey.DefaultActiveColor);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, ButtonResourceKey.DefaultHoverColor);
}
}
_controlTokenBinder.AddControlBinding(Icon, PathIcon.DisabledFilledBrushProperty, GlobalResourceKey.ColorTextDisabled);
}
private void ApplyIconModeStyleConfig()
{
if (Icon is null) {
return;
}
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
Icon.IconMode = IconMode.Selected;
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
Icon.IconMode = IconMode.Active;
} else {
Icon.IconMode = IconMode.Normal;
}
} else {
Icon.IconMode = IconMode.Disabled;
}
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPointerOverProperty ||
e.Property == IsPressedProperty ||
e.Property == IsEnabledProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
ApplyIconModeStyleConfig();
if (e.Property == IsPressedProperty) {
if (_styleState.HasFlag(ControlStyleState.Raised) && (ButtonType == ButtonType.Primary ||
ButtonType == ButtonType.Default)) {
WaveType waveType = default;
if (Shape == ButtonShape.Default) {
waveType = WaveType.RoundRectWave;
} else if (Shape == ButtonShape.Round) {
waveType = WaveType.PillWave;
} else if (Shape == ButtonShape.Circle) {
waveType = WaveType.CircleWave;
}
Color? waveColor = null;
if (IsDanger) {
if (ButtonType == ButtonType.Primary && !IsGhost) {
waveColor = Color.Parse(Background?.ToString()!);
} else {
waveColor = Color.Parse(Foreground?.ToString()!);
}
}
WaveSpiritAdorner.ShowWaveAdorner(this, waveType, waveColor);
}
}
}
if (e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
}
if (_initialized && e.Property == IconProperty) {
var oldValue = e.GetOldValue<PathIcon?>();
var newValue = e.GetNewValue<PathIcon?>();
if (oldValue is not null) {
_stackPanel!.Children.Remove(oldValue);
}
if (newValue is not null) {
SetupIcon();
}
}
if (_initialized && e.Property == ContentProperty) {
// 不推荐,尽最大能力还原
var oldText = (_label!.Content as string)!;
var newContent = e.GetNewValue<object?>();
if (newContent is string newText) {
if (oldText != newText) {
_label!.Content = newText;
}
}
Content = _stackPanel;
}
if (_initialized && e.Property == TextProperty) {
_label!.Content = Text;
}
}
public Rect WaveGeometry()
{
return new Rect(0, 0, Bounds.Width, Bounds.Height);
}
public CornerRadius WaveBorderRadius()
{
return CornerRadius;
}
#endregion
}

View File

@ -1,585 +0,0 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
namespace AtomUI.Controls;
public partial class Button : IWaveAdornerInfoProvider, IControlCustomStyle
{
private ControlStyleState _styleState;
private ControlTokenBinder _controlTokenBinder;
private IControlCustomStyle _customStyle;
private StackPanel? _stackPanel;
private Label? _label;
private bool _initialized = false;
void IControlCustomStyle.SetupUi()
{
HorizontalAlignment = HorizontalAlignment.Left;
VerticalAlignment = VerticalAlignment.Bottom;
Cursor = new Cursor(StandardCursorType.Hand);
_customStyle.CollectStyleState();
CreateMainLayout();
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
ApplyShapeStyleConfig();
SetupIcon();
ApplyIconModeStyleConfig();
_customStyle.SetupTransitions();
if (ButtonType == ButtonType.Default) {
if (IsDanger) {
Effect = new DropShadowEffect
{
OffsetX = _dangerShadow.OffsetX,
OffsetY = _dangerShadow.OffsetY,
Color = _dangerShadow.Color,
BlurRadius = _dangerShadow.Blur,
};
} else {
Effect = new DropShadowEffect
{
OffsetX = _defaultShadow.OffsetX,
OffsetY = _defaultShadow.OffsetY,
Color = _defaultShadow.Color,
BlurRadius = _defaultShadow.Blur,
};
}
} else if (ButtonType == ButtonType.Primary) {
if (IsDanger) {
Effect = new DropShadowEffect
{
OffsetX = _dangerShadow.OffsetX,
OffsetY = _dangerShadow.OffsetY,
Color = _dangerShadow.Color,
BlurRadius = _dangerShadow.Blur,
};
} else {
Effect = new DropShadowEffect
{
OffsetX = _primaryShadow.OffsetX,
OffsetY = _primaryShadow.OffsetY,
Color = _primaryShadow.Color,
BlurRadius = _primaryShadow.Blur,
};
}
}
}
private void CreateMainLayout()
{
if (Text is null && Content is string content) {
Text = content;
}
_label = new Label()
{
Content = Text,
Padding = new Thickness(0),
VerticalContentAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
_stackPanel = new StackPanel()
{
UseLayoutRounding = false,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
Orientation = Orientation.Horizontal,
ClipToBounds = true
};
_stackPanel.Children.Add(_label);
Content = _stackPanel;
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
if (ButtonType == ButtonType.Primary) {
ApplyPrimaryStyle();
} else if (ButtonType == ButtonType.Default) {
ApplyDefaultStyle();
} else if (ButtonType == ButtonType.Text) {
ApplyTextStyle();
} else if (ButtonType == ButtonType.Link) {
ApplyLinkStyle();
}
}
void ApplyShapeStyleConfig()
{
if (Shape == ButtonShape.Circle) {
_controlTokenBinder.AddControlBinding(PaddingProperty, "CirclePadding");
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(PaddingXXSTokenProperty, "PaddingXXS");
_controlTokenBinder.AddControlBinding(DefaultShadowTokenProperty, "DefaultShadow");
_controlTokenBinder.AddControlBinding(PrimaryShadowTokenProperty, "PrimaryShadow");
_controlTokenBinder.AddControlBinding(DangerShadowTokenProperty, "DangerShadow");
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
if (ButtonType == ButtonType.Default ||
(ButtonType == ButtonType.Primary && (IsGhost || !IsEnabled))) {
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this));
}
}
void IControlCustomStyle.SetupTransitions()
{
var transitions = new Transitions();
if (ButtonType == ButtonType.Primary) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty));
if (IsGhost) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BorderBrushProperty));
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty));
}
} else if (ButtonType == ButtonType.Default) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BorderBrushProperty));
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty));
} else if (ButtonType == ButtonType.Text) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty));
} else if (ButtonType == ButtonType.Link) {
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty));
}
Transitions = transitions;
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
_controlTokenBinder.AddControlBinding(FontSizeProperty, ButtonResourceKey.ContentFontSizeSM);
_controlTokenBinder.AddControlBinding(PaddingProperty, ButtonResourceKey.PaddingSM);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(FontSizeProperty, ButtonResourceKey.ContentFontSize);
_controlTokenBinder.AddControlBinding(PaddingProperty, GlobalResourceKey.Padding);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
_controlTokenBinder.AddControlBinding(FontSizeProperty, ButtonResourceKey.ContentFontSizeLG);
_controlTokenBinder.AddControlBinding(PaddingProperty, ButtonResourceKey.PaddingLG);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
}
if (Icon is not null) {
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.IconSizeSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.IconSize);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.IconSizeLG);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.IconSizeLG);
}
}
}
private void ApplyPrimaryStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
// IsGhost 优先级最高
if (IsGhost) {
Background = new SolidColorBrush(Colors.Transparent);
if (IsDanger) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorError);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorderHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorBorderHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimary);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.PrimaryColor);
if (IsDanger) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorError);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorPrimary);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.BorderColorDisabled);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
}
}
private void ApplyDefaultStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, ButtonResourceKey.DefaultBg);
if (IsDanger) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorError);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
if (IsGhost) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
}
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorderHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorBorderHover,
BindingPriority.StyleTrigger);
}
} else {
if (IsGhost) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextLightSolid);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorTextLightSolid);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.DefaultBorderColor);
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultColor);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.DefaultActiveBorderColor,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultActiveColor,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.DefaultHoverBorderColor,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultHoverColor,
BindingPriority.StyleTrigger);
}
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.BorderColorDisabled);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
}
}
private void ApplyTextStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
if (IsDanger) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBgActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBgHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultColor);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgTextActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, ButtonResourceKey.TextHoverBg,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
}
}
private void ApplyLinkStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (IsGhost) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
} else {
_controlTokenBinder.AddControlBinding(BackgroundProperty, ButtonResourceKey.DefaultBg);
}
if (IsDanger) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorHover,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorLink);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorLinkActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorLinkHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
}
}
// TODO 针对 primary 的是否是 ghost 没有完成
private void SetupIcon()
{
if (Icon is not null) {
_stackPanel!.Children.Insert(0, Icon);
if (Text is not null) {
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSize);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeLG);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeLG);
}
Icon.Margin = new Thickness(0, 0, _paddingXXS, 0);
} else {
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.OnlyIconSizeSM);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.OnlyIconSizeSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.OnlyIconSize);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.OnlyIconSize);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.OnlyIconSizeLG);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.OnlyIconSizeLG);
}
}
if (ButtonType == ButtonType.Primary) {
SetupPrimaryIconStyle();
} else if (ButtonType == ButtonType.Default || ButtonType == ButtonType.Link) {
SetupDefaultOrLinkIconStyle();
}
}
}
private void SetupPrimaryIconStyle()
{
if (Icon is null || Icon.ThemeType == IconThemeType.TwoTone) {
return;
}
// IsGhost 优先级最高
if (IsGhost) {
if (IsDanger) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorError);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorErrorActive);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorErrorBorderHover);
} else {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorPrimaryActive);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorPrimaryHover);
}
} else {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, ButtonResourceKey.PrimaryColor);
}
_controlTokenBinder.AddControlBinding(Icon, PathIcon.DisabledFilledBrushProperty, GlobalResourceKey.ColorTextDisabled);
}
private void SetupDefaultOrLinkIconStyle()
{
if (Icon is null || Icon.ThemeType == IconThemeType.TwoTone) {
return;
}
if (IsDanger) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorError);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorErrorActive);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorErrorBorderHover);
} else {
if (IsGhost) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorTextLightSolid);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorPrimaryActive);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorPrimaryHover);
} else {
if (ButtonType == ButtonType.Link) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorLink);
} else {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, ButtonResourceKey.DefaultColor);
}
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, ButtonResourceKey.DefaultActiveColor);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, ButtonResourceKey.DefaultHoverColor);
}
}
_controlTokenBinder.AddControlBinding(Icon, PathIcon.DisabledFilledBrushProperty, GlobalResourceKey.ColorTextDisabled);
}
private void ApplyIconModeStyleConfig()
{
if (Icon is null) {
return;
}
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
Icon.IconMode = IconMode.Selected;
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
Icon.IconMode = IconMode.Active;
} else {
Icon.IconMode = IconMode.Normal;
}
} else {
Icon.IconMode = IconMode.Disabled;
}
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPointerOverProperty ||
e.Property == IsPressedProperty ||
e.Property == IsEnabledProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
ApplyIconModeStyleConfig();
if (e.Property == IsPressedProperty) {
if (_styleState.HasFlag(ControlStyleState.Raised) && (ButtonType == ButtonType.Primary ||
ButtonType == ButtonType.Default)) {
WaveType waveType = default;
if (Shape == ButtonShape.Default) {
waveType = WaveType.RoundRectWave;
} else if (Shape == ButtonShape.Round) {
waveType = WaveType.PillWave;
} else if (Shape == ButtonShape.Circle) {
waveType = WaveType.CircleWave;
}
Color? waveColor = null;
if (IsDanger) {
if (ButtonType == ButtonType.Primary && !IsGhost) {
waveColor = Color.Parse(Background?.ToString()!);
} else {
waveColor = Color.Parse(Foreground?.ToString()!);
}
}
WaveSpiritAdorner.ShowWaveAdorner(this, waveType, waveColor);
}
}
}
if (e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
}
if (_initialized && e.Property == IconProperty) {
var oldValue = e.GetOldValue<PathIcon?>();
var newValue = e.GetNewValue<PathIcon?>();
if (oldValue is not null) {
_stackPanel!.Children.Remove(oldValue);
}
if (newValue is not null) {
SetupIcon();
}
}
if (_initialized && e.Property == ContentProperty) {
// 不推荐,尽最大能力还原
var oldText = (_label!.Content as string)!;
var newContent = e.GetNewValue<object?>();
if (newContent is string newText) {
if (oldText != newText) {
_label!.Content = newText;
}
}
Content = _stackPanel;
}
if (_initialized && e.Property == TextProperty) {
_label!.Content = Text;
}
}
public Rect WaveGeometry()
{
return new Rect(0, 0, Bounds.Width, Bounds.Height);
}
public CornerRadius WaveBorderRadius()
{
return CornerRadius;
}
}

View File

@ -1,7 +1,13 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
@ -11,14 +17,44 @@ namespace AtomUI.Controls;
using AvaloniaCheckBox = Avalonia.Controls.CheckBox;
public partial class CheckBox : AvaloniaCheckBox, ICustomHitTest
public partial class CheckBox : AvaloniaCheckBox,
ICustomHitTest,
IWaveAdornerInfoProvider,
IControlCustomStyle
{
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private ControlStyleState _styleState;
private BorderRenderHelper _borderRenderHelper;
private bool _initialized = false;
static CheckBox()
{
AffectsRender<CheckBox>(
IsCheckedProperty,
IndicatorCheckedMarkEffectSizeProperty,
PaddingInlineProperty,
IndicatorBorderBrushProperty,
IndicatorCheckedMarkBrushProperty,
IndicatorTristateMarkBrushProperty,
IndicatorBackgroundProperty,
IndicatorBorderThicknessProperty,
IndicatorBorderRadiusProperty);
}
public CheckBox()
{
_controlTokenBinder = new ControlTokenBinder(this, CheckBoxToken.ID);
_customStyle = this;
_borderRenderHelper = new BorderRenderHelper();
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
@ -55,19 +91,29 @@ public partial class CheckBox : AvaloniaCheckBox, ICustomHitTest
layoutable.Arrange(arrangeRect);
}
}
return finalSize;
}
public sealed override void Render(DrawingContext context)
{
var indicatorRect = IndicatorRect();
var penWidth = IndicatorBorderThickness.Top;
PenUtils.TryModifyOrCreate(ref _cachedPen, IndicatorBorderBrush, penWidth);
var borderRadius = GeometryUtils.CornerRadiusScalarValue(IndicatorBorderRadius);
context.DrawRectangle(IndicatorBackground, _cachedPen, indicatorRect.Deflate(penWidth / 2),
borderRadius, borderRadius);
{
var originTransform = Matrix.CreateTranslation(indicatorRect.X, indicatorRect.Y);
using var transformState = context.PushTransform(originTransform);
_borderRenderHelper.Render(context, indicatorRect.Size,
borderThickness:new Thickness(penWidth),
new CornerRadius(borderRadius),
BackgroundSizing.OuterBorderEdge,
IndicatorBackground,
IndicatorBorderBrush,
new BoxShadows());
}
if (_styleState.HasFlag(ControlStyleState.On)) {
var checkMarkGeometry = CommonShapeBuilder.BuildCheckMark(new Size(IndicatorCheckedMarkEffectSize, IndicatorCheckedMarkEffectSize));
var checkMarkGeometry =
CommonShapeBuilder.BuildCheckMark(new Size(IndicatorCheckedMarkEffectSize, IndicatorCheckedMarkEffectSize));
var checkMarkPenWidth = 2;
var checkMarkPen = new Pen(IndicatorCheckedMarkBrush, 2);
var checkMarkBounds = checkMarkGeometry.GetRenderBounds(checkMarkPen);
@ -84,9 +130,161 @@ public partial class CheckBox : AvaloniaCheckBox, ICustomHitTest
context.FillRectangle(IndicatorTristateMarkBrush!, indicatorTristateRect);
}
}
public bool HitTest(Point point)
{
return true;
}
#region IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
Cursor = new Cursor(StandardCursorType.Hand);
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.SetupTransitions();
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
switch (IsChecked) {
case true:
_styleState |= ControlStyleState.On;
break;
case false:
_styleState |= ControlStyleState.Off;
break;
default:
_styleState |= ControlStyleState.Indeterminate;
break;
}
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
}
// Measure 之后才有值
private Rect IndicatorRect()
{
var offsetY = (DesiredSize.Height - Margin.Top - Margin.Bottom - CheckIndicatorSize) / 2;
return new Rect(0d, offsetY, CheckIndicatorSize, CheckIndicatorSize);
}
private Rect TextRect()
{
var offsetX = CheckIndicatorSize + PaddingInline;
return new Rect(offsetX, 0d, DesiredSize.Width - offsetX, DesiredSize.Height);
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(CheckIndicatorSizeProperty, CheckBoxResourceKey.CheckIndicatorSize);
_controlTokenBinder.AddControlBinding(PaddingInlineProperty, GlobalResourceKey.PaddingXS);
_controlTokenBinder.AddControlBinding(IndicatorBorderRadiusProperty, GlobalResourceKey.BorderRadiusSM);
_controlTokenBinder.AddControlBinding(IndicatorTristateMarkSizeProperty,
CheckBoxResourceKey.IndicatorTristateMarkSize);
_controlTokenBinder.AddControlBinding(IndicatorTristateMarkBrushProperty, GlobalResourceKey.ColorPrimary);
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
_controlTokenBinder.AddControlBinding(IndicatorBorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this));
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorText);
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorBgContainer);
_controlTokenBinder.AddControlBinding(IndicatorCheckedMarkBrushProperty, GlobalResourceKey.ColorBgContainer);
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorBorder);
if (_styleState.HasFlag(ControlStyleState.On)) {
IndicatorCheckedMarkEffectSize = CheckIndicatorSize;
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorPrimary,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimary,
BindingPriority.StyleTrigger);
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
} else if (_styleState.HasFlag(ControlStyleState.Off)) {
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
IndicatorCheckedMarkEffectSize = CheckIndicatorSize * 0.7;
} else if (_styleState.HasFlag(ControlStyleState.Indeterminate)) {
IndicatorCheckedMarkEffectSize = CheckIndicatorSize * 0.7;
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorBorder);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
if (_styleState.HasFlag(ControlStyleState.On)) {
IndicatorCheckedMarkEffectSize = CheckIndicatorSize;
_controlTokenBinder.AddControlBinding(IndicatorCheckedMarkBrushProperty,
GlobalResourceKey.ColorTextDisabled,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.Indeterminate)) {
_controlTokenBinder.AddControlBinding(IndicatorTristateMarkBrushProperty,
GlobalResourceKey.ColorTextDisabled,
BindingPriority.StyleTrigger);
}
}
}
void IControlCustomStyle.SetupTransitions()
{
Transitions = new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBackgroundProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBorderBrushProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorTristateMarkBrushProperty),
AnimationUtils.CreateTransition<DoubleTransition>(IndicatorCheckedMarkEffectSizeProperty,
GlobalResourceKey.MotionDurationMid, new BackEaseOut())
};
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPointerOverProperty ||
e.Property == IsCheckedProperty ||
e.Property == IsEnabledProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
if (e.Property == IsCheckedProperty &&
_styleState.HasFlag(ControlStyleState.Enabled) &&
_styleState.HasFlag(ControlStyleState.On)) {
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.RoundRectWave);
}
}
}
public Rect WaveGeometry()
{
return IndicatorRect();
}
public CornerRadius WaveBorderRadius()
{
return IndicatorBorderRadius;
}
#endregion
}

View File

@ -1,174 +0,0 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Media;
namespace AtomUI.Controls;
public partial class CheckBox : IWaveAdornerInfoProvider,
IControlCustomStyle
{
private IPen? _cachedPen;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private ControlStyleState _styleState;
private bool _initialized = false;
static CheckBox()
{
AffectsRender<CheckBox>(
IsCheckedProperty,
IndicatorCheckedMarkEffectSizeProperty,
PaddingInlineProperty,
IndicatorBorderBrushProperty,
IndicatorCheckedMarkBrushProperty,
IndicatorTristateMarkBrushProperty,
IndicatorBackgroundProperty,
IndicatorBorderThicknessProperty,
IndicatorBorderRadiusProperty);
}
void IControlCustomStyle.SetupUi()
{
Cursor = new Cursor(StandardCursorType.Hand);
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.SetupTransitions();
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
switch (IsChecked) {
case true:
_styleState |= ControlStyleState.On;
break;
case false:
_styleState |= ControlStyleState.Off;
break;
default:
_styleState |= ControlStyleState.Indeterminate;
break;
}
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
}
// Measure 之后才有值
private Rect IndicatorRect()
{
var offsetY = (DesiredSize.Height - Margin.Top - Margin.Bottom - CheckIndicatorSize) / 2;
return new Rect(0d, offsetY, CheckIndicatorSize, CheckIndicatorSize);
}
private Rect TextRect()
{
var offsetX = CheckIndicatorSize + PaddingInline;
return new Rect(offsetX, 0d, DesiredSize.Width - offsetX, DesiredSize.Height);
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(CheckIndicatorSizeProperty, CheckBoxResourceKey.CheckIndicatorSize);
_controlTokenBinder.AddControlBinding(PaddingInlineProperty, GlobalResourceKey.PaddingXS);
_controlTokenBinder.AddControlBinding(IndicatorBorderRadiusProperty, GlobalResourceKey.BorderRadiusSM);
_controlTokenBinder.AddControlBinding(IndicatorTristateMarkSizeProperty, CheckBoxResourceKey.IndicatorTristateMarkSize);
_controlTokenBinder.AddControlBinding(IndicatorTristateMarkBrushProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(IndicatorBorderThicknessProperty, GlobalResourceKey.BorderThickness);
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorText);
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorBgContainer);
_controlTokenBinder.AddControlBinding(IndicatorCheckedMarkBrushProperty, GlobalResourceKey.ColorBgContainer);
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorBorder);
if (_styleState.HasFlag(ControlStyleState.On)) {
IndicatorCheckedMarkEffectSize = CheckIndicatorSize;
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorPrimary,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimary,
BindingPriority.StyleTrigger);
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
} else if (_styleState.HasFlag(ControlStyleState.Off)) {
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
IndicatorCheckedMarkEffectSize = CheckIndicatorSize * 0.7;
} else if (_styleState.HasFlag(ControlStyleState.Indeterminate)) {
IndicatorCheckedMarkEffectSize = CheckIndicatorSize * 0.7;
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorBorder);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
if (_styleState.HasFlag(ControlStyleState.On)) {
IndicatorCheckedMarkEffectSize = CheckIndicatorSize;
_controlTokenBinder.AddControlBinding(IndicatorCheckedMarkBrushProperty, GlobalResourceKey.ColorTextDisabled,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.Indeterminate)) {
_controlTokenBinder.AddControlBinding(IndicatorTristateMarkBrushProperty, GlobalResourceKey.ColorTextDisabled,
BindingPriority.StyleTrigger);
}
}
}
void IControlCustomStyle.SetupTransitions()
{
Transitions = new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBackgroundProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBorderBrushProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorTristateMarkBrushProperty),
AnimationUtils.CreateTransition<DoubleTransition>(IndicatorCheckedMarkEffectSizeProperty, GlobalResourceKey.MotionDurationMid, new BackEaseOut())
};
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPointerOverProperty ||
e.Property == IsCheckedProperty ||
e.Property == IsEnabledProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
if (e.Property == IsCheckedProperty &&
_styleState.HasFlag(ControlStyleState.Enabled) &&
_styleState.HasFlag(ControlStyleState.On)) {
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.RoundRectWave);
}
}
}
public Rect WaveGeometry()
{
return IndicatorRect();
}
public CornerRadius WaveBorderRadius()
{
return IndicatorBorderRadius;
}
}

View File

@ -0,0 +1,81 @@
using AtomUI.ColorSystem;
using Avalonia.Media;
namespace AtomUI.Controls;
internal static class BuiltInImageBuilder
{
public static string BuildSimpleImage(Color contentColor, Color borderColor, Color shadowColor)
{
var source =
$"""
<svg width="64" height="41" viewBox="0 0 64 41" xmlns="http://www.w3.org/2000/svg">
<title>Simple Empty</title>
<g transform="translate(0 1)" fill="none" fillRule="evenodd">
<ellipse fill={shadowColor.HexName()} cx="32" cy="33" rx="32" ry="7" />
<g fillRule="nonzero" stroke={borderColor.HexName()}>
<path d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z" />
<path
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
fill={contentColor.HexName()}
/>
</g>
</g>
</svg>
""";
return source;
}
public static string BuildDefaultImage()
{
var source =
"""
<svg
style={themeStyle}
width="184"
height="152"
viewBox="0 0 184 152"
xmlns="http://www.w3.org/2000/svg">
<title>empty image</title>
<g fill="none" fillRule="evenodd">
<g transform="translate(24 31.67)">
<ellipse
fillOpacity=".8"
fill="#F5F5F7"
cx="67.797"
cy="106.89"
rx="67.797"
ry="12.668"
/>
<path
d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
fill="#AEB8C2"
/>
<path
d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z"
fill="url(#linearGradient-1)"
transform="translate(13.56)"
/>
<path
d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
fill="#F5F5F7"
/>
<path
d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
fill="#DCE0E6"
/>
</g>
<path
d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
fill="#DCE0E6"
/>
<g transform="translate(149.65 15.383)" fill="#FFF">
<ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815" />
<path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z" />
</g>
</g>
</svg>
""";
return source;
}
}

View File

@ -0,0 +1,8 @@
using Avalonia.Controls;
namespace AtomUI.Controls;
public class EmptyIndicator : Control
{
}

View File

@ -0,0 +1,7 @@
namespace AtomUI.Controls;
public class EmptyIndicatorToken
{
}

View File

@ -1,13 +1,34 @@
using AtomUI.Data;
using AtomUI.Styling;
using AtomUI.TokenSystem;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading;
namespace AtomUI.Controls;
public partial class MarqueeLabel : TextBlock
public partial class MarqueeLabel : TextBlock,
IControlCustomStyle
{
private IControlCustomStyle? _customStyle;
private ControlTokenBinder _controlTokenBinder;
private ControlStyleState _styleState;
private CancellationTokenSource? _cancellationTokenSource;
private bool _initialized = false;
private double _cycleSpace; // 默认的间隔
private Animation? _animation;
private bool _animationRunning = false;
private double _lastDesiredWidth;
private double _lastTextWidth;
private double _moveSpeed = 150; // 像素每秒
private double _pivotOffsetStartValue = 0;
public static readonly DirectProperty<MarqueeLabel, double> CycleSpaceProperty =
AvaloniaProperty.RegisterDirect<MarqueeLabel, double>(nameof(CycleSpace),
o => o.CycleSpace,
@ -76,4 +97,139 @@ public partial class MarqueeLabel : TextBlock
return size;
}
#region IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
HorizontalAlignment = HorizontalAlignment.Stretch;
TextWrapping = TextWrapping.NoWrap;
_customStyle?.CollectStyleState();
_customStyle?.ApplyFixedStyleConfig();
}
private double CalculateDuration(double distance)
{
// 计算持续时间,确保至少有一毫秒的持续时间以避免除以零的错误
return 4 * Math.Max(1, distance / _moveSpeed * 1000);
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(CycleSpaceProperty, MarqueeLabelResourceKey.CycleSpace);
_controlTokenBinder.AddControlBinding(MoveSpeedProperty, MarqueeLabelResourceKey.DefaultSpeed);
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (_initialized) {
if (e.Property == IsPointerOverProperty) {
_customStyle?.CollectStyleState();
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_pivotOffsetStartValue = PivotOffset;
HandleCleanupMarqueeAnimation();
} else {
if (DesiredSize.Width < _lastDesiredWidth || MathUtils.AreClose(DesiredSize.Width, _lastDesiredWidth)) {
ReConfigureAnimation();
HandleStartupMarqueeAnimation();
}
}
// 这里处理暂停事件
} else if (e.Property == CycleSpaceProperty || e.Property == MoveSpeedProperty) {
var originRunning = _animationRunning;
ReConfigureAnimation();
if (originRunning) {
HandleStartupMarqueeAnimation();
}
}
}
}
private void HandleStartupMarqueeAnimation()
{
if (_animation is not null) {
_cancellationTokenSource = new CancellationTokenSource();
var animationTask = _animation!.RunAsync(this, _cancellationTokenSource.Token);
_animationRunning = true;
Dispatcher.UIThread.InvokeAsync(async () =>
{
await animationTask;
_animationRunning = false;
_animation = null;
});
}
}
private void HandleCleanupMarqueeAnimation()
{
_cancellationTokenSource?.Cancel();
}
private void HandleLayoutUpdated(Size size, Size availableSize)
{
if (availableSize.Width > size.Width || MathUtils.AreClose(availableSize.Width, size.Width)) {
HandleCleanupMarqueeAnimation();
PivotOffset = 0;
_lastDesiredWidth = 0;
_pivotOffsetStartValue = 0;
} else {
if (!MathUtils.AreClose(_lastDesiredWidth, size.Width)) {
_lastDesiredWidth = size.Width;
if (!MathUtils.AreClose(_lastTextWidth, TextLayout.Width)) {
_lastTextWidth = TextLayout.Width;
}
}
if (!_animationRunning) {
ReConfigureAnimation();
HandleStartupMarqueeAnimation();
}
}
}
private void ReConfigureAnimation()
{
if (_animation is not null && _animationRunning) {
_cancellationTokenSource?.Cancel();
}
var cycleWidth = _lastTextWidth + _cycleSpace;
if (_pivotOffsetStartValue < -cycleWidth) {
_pivotOffsetStartValue %= cycleWidth;
}
var delta = _pivotOffsetStartValue - cycleWidth - _pivotOffsetStartValue;
_animation = new Animation()
{
IterationCount = new IterationCount(long.MaxValue),
Children =
{
new KeyFrame()
{
Cue = new Cue(0.0d),
Setters = { new Setter(PivotOffsetProperty, _pivotOffsetStartValue) }
},
new KeyFrame()
{
Cue = new Cue(1.0d),
Setters = { new Setter(PivotOffsetProperty, _pivotOffsetStartValue - cycleWidth) }
}
},
FillMode = FillMode.Both,
Duration = TimeSpan.FromMilliseconds(CalculateDuration(_lastTextWidth + _cycleSpace))
};
}
protected override void RenderTextLayout(DrawingContext context, Point origin)
{
var offset = TextLayout.OverhangLeading + PivotOffset;
TextLayout.Draw(context, origin + new Point(offset, 0));
if (PivotOffset < 0) {
TextLayout.Draw(context, origin + new Point(offset + _lastTextWidth + _cycleSpace, 0));
}
}
#endregion
}

View File

@ -1,161 +0,0 @@
using AtomUI.Data;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading;
namespace AtomUI.Controls;
public partial class MarqueeLabel : IControlCustomStyle
{
private IControlCustomStyle? _customStyle;
private ControlTokenBinder _controlTokenBinder;
private ControlStyleState _styleState;
private CancellationTokenSource? _cancellationTokenSource;
private bool _initialized = false;
private double _cycleSpace; // 默认的间隔
private Animation? _animation;
private bool _animationRunning = false;
private double _lastDesiredWidth;
private double _lastTextWidth;
private double _moveSpeed = 150; // 像素每秒
private double _pivotOffsetStartValue = 0;
void IControlCustomStyle.SetupUi()
{
HorizontalAlignment = HorizontalAlignment.Stretch;
TextWrapping = TextWrapping.NoWrap;
_customStyle?.CollectStyleState();
_customStyle?.ApplyFixedStyleConfig();
}
private double CalculateDuration(double distance)
{
// 计算持续时间,确保至少有一毫秒的持续时间以避免除以零的错误
return 4 * Math.Max(1, distance / _moveSpeed * 1000);
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(CycleSpaceProperty, MarqueeLabelResourceKey.CycleSpace);
_controlTokenBinder.AddControlBinding(MoveSpeedProperty, MarqueeLabelResourceKey.DefaultSpeed);
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (_initialized) {
if (e.Property == IsPointerOverProperty) {
_customStyle?.CollectStyleState();
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_pivotOffsetStartValue = PivotOffset;
HandleCleanupMarqueeAnimation();
} else {
if (DesiredSize.Width < _lastDesiredWidth || MathUtils.AreClose(DesiredSize.Width, _lastDesiredWidth)) {
ReConfigureAnimation();
HandleStartupMarqueeAnimation();
}
}
// 这里处理暂停事件
} else if (e.Property == CycleSpaceProperty || e.Property == MoveSpeedProperty) {
var originRunning = _animationRunning;
ReConfigureAnimation();
if (originRunning) {
HandleStartupMarqueeAnimation();
}
}
}
}
private void HandleStartupMarqueeAnimation()
{
if (_animation is not null) {
_cancellationTokenSource = new CancellationTokenSource();
var animationTask = _animation!.RunAsync(this, _cancellationTokenSource.Token);
_animationRunning = true;
Dispatcher.UIThread.InvokeAsync(async () =>
{
await animationTask;
_animationRunning = false;
_animation = null;
});
}
}
private void HandleCleanupMarqueeAnimation()
{
_cancellationTokenSource?.Cancel();
}
private void HandleLayoutUpdated(Size size, Size availableSize)
{
if (availableSize.Width > size.Width || MathUtils.AreClose(availableSize.Width, size.Width)) {
HandleCleanupMarqueeAnimation();
PivotOffset = 0;
_lastDesiredWidth = 0;
_pivotOffsetStartValue = 0;
} else {
if (!MathUtils.AreClose(_lastDesiredWidth, size.Width)) {
_lastDesiredWidth = size.Width;
if (!MathUtils.AreClose(_lastTextWidth, TextLayout.Width)) {
_lastTextWidth = TextLayout.Width;
}
}
if (!_animationRunning) {
ReConfigureAnimation();
HandleStartupMarqueeAnimation();
}
}
}
private void ReConfigureAnimation()
{
if (_animation is not null && _animationRunning) {
_cancellationTokenSource?.Cancel();
}
var cycleWidth = _lastTextWidth + _cycleSpace;
if (_pivotOffsetStartValue < -cycleWidth) {
_pivotOffsetStartValue %= cycleWidth;
}
var delta = _pivotOffsetStartValue - cycleWidth - _pivotOffsetStartValue;
_animation = new Animation()
{
IterationCount = new IterationCount(long.MaxValue),
Children =
{
new KeyFrame()
{
Cue = new Cue(0.0d),
Setters = { new Setter(PivotOffsetProperty, _pivotOffsetStartValue) }
},
new KeyFrame()
{
Cue = new Cue(1.0d),
Setters = { new Setter(PivotOffsetProperty, _pivotOffsetStartValue - cycleWidth) }
}
},
FillMode = FillMode.Both,
Duration = TimeSpan.FromMilliseconds(CalculateDuration(_lastTextWidth + _cycleSpace))
};
}
protected override void RenderTextLayout(DrawingContext context, Point origin)
{
var offset = TextLayout.OverhangLeading + PivotOffset;
TextLayout.Draw(context, origin + new Point(offset, 0));
if (PivotOffset < 0) {
TextLayout.Draw(context, origin + new Point(offset + _lastTextWidth + _cycleSpace, 0));
}
}
}

View File

@ -1,5 +1,14 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.LogicalTree;
namespace AtomUI.Controls;
@ -33,8 +42,19 @@ public class OptionButtonPointerEventArgs : EventArgs
}
}
public partial class OptionButton : AvaloniaRadioButton, ISizeTypeAware
public partial class OptionButton : AvaloniaRadioButton,
ISizeTypeAware,
IWaveAdornerInfoProvider,
IControlCustomStyle
{
private bool _initialized = false;
private ControlStyleState _styleState;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private StackPanel? _stackPanel;
private Label? _label;
private CornerRadius? _originCornerRadius;
public static readonly StyledProperty<ButtonSizeType> SizeTypeProperty =
AvaloniaProperty.Register<OptionButton, ButtonSizeType>(nameof(SizeType), ButtonSizeType.Middle);
@ -135,5 +155,355 @@ public partial class OptionButton : AvaloniaRadioButton, ISizeTypeAware
base.OnAttachedToVisualTree(e);
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
#region IControlCustomStyle
public Rect WaveGeometry()
{
return new Rect(0, 0, Bounds.Width, Bounds.Height);
}
public CornerRadius WaveBorderRadius()
{
return CornerRadius;
}
void IControlCustomStyle.SetupUi()
{
Cursor = new Cursor(StandardCursorType.Hand);
VerticalAlignment = VerticalAlignment.Center;
_customStyle.CollectStyleState();
CreateMainLayout();
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.ApplyFixedStyleConfig();
_customStyle.SetupTransitions();
}
private void CreateMainLayout()
{
if (Text.Length == 0 && Content is string content) {
Text = content;
}
_label = new Label()
{
Content = Text,
Padding = new Thickness(0),
VerticalContentAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
_stackPanel = new StackPanel()
{
UseLayoutRounding = false,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
Orientation = Orientation.Horizontal,
ClipToBounds = true
};
_stackPanel.Children.Add(_label);
Content = _stackPanel;
BindUtils.RelayBind(this, WidthProperty, _stackPanel);
BindUtils.RelayBind(this, HeightProperty, _stackPanel);
}
void IControlCustomStyle.SetupTransitions()
{
var transitions = new Transitions();
if (ButtonStyle == OptionButtonStyle.Solid) {
transitions.Add(new SolidColorBrushTransition()
{
Property = BackgroundProperty,
Duration = _motionDuration
});
} else if (ButtonStyle == OptionButtonStyle.Outline) {
transitions.Add(new SolidColorBrushTransition()
{
Property = BorderBrushProperty,
Duration = _motionDuration
});
}
transitions.Add(new SolidColorBrushTransition()
{
Property = ForegroundProperty,
Duration = _motionDuration
});
Transitions = transitions;
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
if (IsChecked.HasValue && IsChecked.Value) {
_styleState |= ControlStyleState.Selected;
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(MotionDurationTokenProperty, GlobalResourceKey.MotionDurationMid);
_controlTokenBinder.AddControlBinding(ColorPrimaryHoverTokenProperty, GlobalResourceKey.ColorPrimaryHover);
_controlTokenBinder.AddControlBinding(ColorPrimaryActiveTokenProperty, GlobalResourceKey.ColorPrimaryActive);
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this, thickness =>
{
if (InOptionGroup) {
return new Thickness(0);
}
return thickness;
}));
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPointerOverProperty ||
e.Property == IsPressedProperty ||
e.Property == IsCheckedProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
if (e.Property == IsPressedProperty) {
if (_styleState.HasFlag(ControlStyleState.Raised)) {
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.RoundRectWave);
}
}
}
if (e.Property == GroupPositionTraitProperty) {
if (_originCornerRadius.HasValue) {
CornerRadius = BuildCornerRadius(GroupPositionTrait, _originCornerRadius!.Value);
}
}
if (_initialized && e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
}
if (_initialized && e.Property == ContentProperty) {
// 不推荐,尽最大能力还原
var oldText = (_label!.Content as string)!;
var newContent = e.GetNewValue<object?>();
if (newContent is string newText) {
if (oldText != newText) {
_label!.Content = newText;
}
}
Content = _stackPanel;
}
if (_initialized && e.Property == TextProperty) {
_label!.Content = Text;
}
if (e.Property == InOptionGroupProperty) {
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
}
private CornerRadius BuildCornerRadius(OptionButtonPositionTrait positionTrait, CornerRadius cornerRadius)
{
if (positionTrait == OptionButtonPositionTrait.First) {
return new CornerRadius(cornerRadius.TopLeft,
0,
0,
cornerRadius.BottomLeft);
} else if (positionTrait == OptionButtonPositionTrait.Last) {
return new CornerRadius(0,
cornerRadius.TopRight,
cornerRadius.BottomRight,
0);
} else if (positionTrait == OptionButtonPositionTrait.Middle) {
return new CornerRadius(0);
}
return cornerRadius;
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
if (ButtonStyle == OptionButtonStyle.Outline) {
ApplyOutlineStyle();
} else if (ButtonStyle == OptionButtonStyle.Solid) {
ApplySolidStyle();
}
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == ButtonSizeType.Small) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
_controlTokenBinder.AddControlBinding(FontSizeProperty, OptionButtonResourceKey.ContentFontSizeSM);
_controlTokenBinder.AddControlBinding(PaddingProperty, OptionButtonResourceKey.PaddingSM);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
} else if (SizeType == ButtonSizeType.Middle) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(FontSizeProperty, OptionButtonResourceKey.ContentFontSize);
_controlTokenBinder.AddControlBinding(PaddingProperty, OptionButtonResourceKey.Padding);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
} else if (SizeType == ButtonSizeType.Large) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
_controlTokenBinder.AddControlBinding(FontSizeProperty, OptionButtonResourceKey.ContentFontSizeLG);
_controlTokenBinder.AddControlBinding(PaddingProperty, OptionButtonResourceKey.PaddingLG);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
}
_originCornerRadius = CornerRadius;
CornerRadius = BuildCornerRadius(GroupPositionTrait, _originCornerRadius!.Value);
}
private void ApplySolidStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (_styleState.HasFlag(ControlStyleState.Selected)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, OptionButtonResourceKey.ButtonSolidCheckedColor);
_controlTokenBinder.AddControlBinding(BackgroundProperty, OptionButtonResourceKey.ButtonSolidCheckedBackground);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty,
OptionButtonResourceKey.ButtonSolidCheckedActiveBackground,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty,
OptionButtonResourceKey.ButtonSolidCheckedHoverBackground,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorText);
if (InOptionGroup) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
} else {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainer);
}
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorBorder);
if (_styleState.HasFlag(ControlStyleState.Selected)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, OptionButtonResourceKey.ButtonCheckedColorDisabled);
_controlTokenBinder.AddControlBinding(BackgroundProperty, OptionButtonResourceKey.ButtonCheckedBgDisabled);
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
}
}
}
private void ApplyOutlineStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (_styleState.HasFlag(ControlStyleState.Selected)) {
if (InOptionGroup) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
} else {
_controlTokenBinder.AddControlBinding(BackgroundProperty, OptionButtonResourceKey.ButtonBackground);
}
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimary);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
} else {
if (InOptionGroup) {
_controlTokenBinder.AddControlBinding(this, BackgroundProperty, GlobalResourceKey.ColorTransparent);
} else {
_controlTokenBinder.AddControlBinding(this, BackgroundProperty,
OptionButtonResourceKey.ButtonCheckedBackground);
}
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorBorder);
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, OptionButtonResourceKey.ButtonColor);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorBorder);
if (_styleState.HasFlag(ControlStyleState.Selected)) {
_controlTokenBinder.AddControlBinding(this, ForegroundProperty,
OptionButtonResourceKey.ButtonCheckedColorDisabled);
_controlTokenBinder.AddControlBinding(this, BackgroundProperty, OptionButtonResourceKey.ButtonCheckedBgDisabled);
} else {
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(this, BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
}
}
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
{
IsHovering = true,
IsPressed = true
});
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
{
IsHovering = true,
IsPressed = false
});
}
protected override void OnPointerEntered(PointerEventArgs e)
{
base.OnPointerEntered(e);
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
{
IsHovering = true,
IsPressed = false
});
}
protected override void OnPointerExited(PointerEventArgs e)
{
base.OnPointerExited(e);
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
{
IsHovering = false,
IsPressed = false
});
}
#endregion
}

View File

@ -1,10 +1,15 @@
using System.Collections.Specialized;
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.TokenSystem;
using AtomUI.Styling;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
namespace AtomUI.Controls;
@ -12,7 +17,9 @@ namespace AtomUI.Controls;
using ButtonSizeType = SizeType;
using OptionButtons = AvaloniaList<OptionButton>;
public partial class OptionButtonGroup : StyledControl, ISizeTypeAware
public partial class OptionButtonGroup : StyledControl,
ISizeTypeAware,
IControlCustomStyle
{
public static readonly StyledProperty<ButtonSizeType> SizeTypeProperty =
AvaloniaProperty.Register<OptionButtonGroup, ButtonSizeType>(nameof(SizeType), ButtonSizeType.Middle);
@ -76,6 +83,13 @@ public partial class OptionButtonGroup : StyledControl, ISizeTypeAware
}
[Content] public OptionButtons Options { get; } = new OptionButtons();
private bool _initialized = false;
private ControlStyleState _styleState;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private StackPanel? _layout;
private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
static OptionButtonGroup()
{
@ -204,4 +218,164 @@ public partial class OptionButtonGroup : StyledControl, ISizeTypeAware
base.OnAttachedToVisualTree(e);
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
#region IControlCustomStyle
void IControlCustomStyle.InitOnConstruct()
{
_layout = new StackPanel
{
Orientation = Orientation.Horizontal,
ClipToBounds = true,
};
}
void IControlCustomStyle.SetupUi()
{
HorizontalAlignment = HorizontalAlignment.Left;
_customStyle.CollectStyleState();
_customStyle.ApplySizeTypeStyleConfig();
ApplyButtonSizeConfig();
ApplyButtonStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.ApplyFixedStyleConfig();
LogicalChildren.Add(_layout!);
VisualChildren.Add(_layout!);
}
void IControlCustomStyle.SetupTransitions() {}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(MotionDurationTokenProperty, GlobalResourceKey.MotionDurationMid);
_controlTokenBinder.AddControlBinding(ColorBorderTokenProperty, GlobalResourceKey.ColorBorder);
_controlTokenBinder.AddControlBinding(ColorPrimaryTokenProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(ColorPrimaryHoverTokenProperty, GlobalResourceKey.ColorPrimaryHover);
_controlTokenBinder.AddControlBinding(ColorPrimaryActiveTokenProperty, GlobalResourceKey.ColorPrimaryActive);
_controlTokenBinder.AddControlBinding(SelectedOptionBorderColorProperty, GlobalResourceKey.ColorPrimary);
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this));
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
} else if (e.Property == ButtonStyleProperty) {
ApplyButtonStyleConfig();
}
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
}
ApplyButtonSizeConfig();
}
private void ApplyButtonSizeConfig()
{
foreach (var optionButton in Options) {
optionButton.SizeType = SizeType;
}
}
private void ApplyButtonStyleConfig()
{
foreach (var optionButton in Options) {
optionButton.ButtonStyle = ButtonStyle;
}
}
public override void Render(DrawingContext context)
{
_borderRenderHelper.Render(context,
new Size(DesiredSize.Width, DesiredSize.Height),
BorderThickness,
CornerRadius,
BackgroundSizing.InnerBorderEdge,
null,
_colorBorder,
new BoxShadows());
for (int i = 0; i < Options.Count; ++i) {
var optionButton = Options[i];
if (ButtonStyle == OptionButtonStyle.Solid) {
if (i <= Options.Count - 2) {
var nextOption = Options[i + 1];
if (nextOption == SelectedOption || optionButton == SelectedOption) {
continue;
}
}
}
if (i != Options.Count - 1) {
var offsetX = optionButton.Bounds.Right;
var startPoint = new Point(offsetX, 0);
var endPoint = new Point(offsetX, Bounds.Height);
using var optionState = context.PushRenderOptions(new RenderOptions()
{
EdgeMode = EdgeMode.Aliased
});
context.DrawLine(new Pen(_colorBorder, BorderThickness.Left), startPoint, endPoint);
}
if (ButtonStyle == OptionButtonStyle.Outline) {
if (optionButton.IsEnabled && optionButton.IsChecked.HasValue && optionButton.IsChecked.Value) {
// 绘制选中边框
var offsetX = optionButton.Bounds.X;
var width = optionButton.DesiredSize.Width;
if (i != 0) {
offsetX -= BorderThickness.Left;
width += BorderThickness.Left;
}
var translationMatrix = Matrix.CreateTranslation(offsetX, 0);
using var state = context.PushTransform(translationMatrix);
var cornerRadius = new CornerRadius(0);
if (i == 0) {
cornerRadius = new CornerRadius(CornerRadius.TopLeft, 0, 0, CornerRadius.BottomLeft);
} else if (i == Options.Count - 1) {
cornerRadius = new CornerRadius(0, CornerRadius.TopRight, CornerRadius.BottomRight, 0);
}
_borderRenderHelper.Render(context,
new Size(width, DesiredSize.Height),
BorderThickness,
cornerRadius,
BackgroundSizing.InnerBorderEdge,
null,
SelectedOptionBorderColor,
new BoxShadows());
}
}
}
}
private void HandleOptionPointerEvent(object? sender, OptionButtonPointerEventArgs args)
{
if (args.Button == SelectedOption) {
_controlTokenBinder.ReleaseTriggerBindings(this);
if (args.IsPressed) {
_controlTokenBinder.AddControlBinding(SelectedOptionBorderColorProperty, GlobalResourceKey.ColorPrimaryActive, BindingPriority.StyleTrigger);
} else if (args.IsHovering) {
_controlTokenBinder.AddControlBinding(SelectedOptionBorderColorProperty, GlobalResourceKey.ColorPrimaryHover, BindingPriority.StyleTrigger);
}
}
}
#endregion
}

View File

@ -1,178 +0,0 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Styling;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.Media;
namespace AtomUI.Controls;
public partial class OptionButtonGroup : IControlCustomStyle
{
private bool _initialized = false;
private ControlStyleState _styleState;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private StackPanel? _layout;
private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
void IControlCustomStyle.InitOnConstruct()
{
_layout = new StackPanel
{
Orientation = Orientation.Horizontal,
ClipToBounds = true,
};
}
void IControlCustomStyle.SetupUi()
{
HorizontalAlignment = HorizontalAlignment.Left;
_customStyle.CollectStyleState();
_customStyle.ApplySizeTypeStyleConfig();
ApplyButtonSizeConfig();
ApplyButtonStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.ApplyFixedStyleConfig();
LogicalChildren.Add(_layout!);
VisualChildren.Add(_layout!);
}
void IControlCustomStyle.SetupTransitions() {}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(MotionDurationTokenProperty, GlobalResourceKey.MotionDurationMid);
_controlTokenBinder.AddControlBinding(ColorBorderTokenProperty, GlobalResourceKey.ColorBorder);
_controlTokenBinder.AddControlBinding(ColorPrimaryTokenProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(ColorPrimaryHoverTokenProperty, GlobalResourceKey.ColorPrimaryHover);
_controlTokenBinder.AddControlBinding(ColorPrimaryActiveTokenProperty, GlobalResourceKey.ColorPrimaryActive);
_controlTokenBinder.AddControlBinding(SelectedOptionBorderColorProperty, GlobalResourceKey.ColorPrimary);
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this));
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
} else if (e.Property == ButtonStyleProperty) {
ApplyButtonStyleConfig();
}
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
}
ApplyButtonSizeConfig();
}
private void ApplyButtonSizeConfig()
{
foreach (var optionButton in Options) {
optionButton.SizeType = SizeType;
}
}
private void ApplyButtonStyleConfig()
{
foreach (var optionButton in Options) {
optionButton.ButtonStyle = ButtonStyle;
}
}
public override void Render(DrawingContext context)
{
_borderRenderHelper.Render(context,
new Size(DesiredSize.Width, DesiredSize.Height),
BorderThickness,
CornerRadius,
BackgroundSizing.InnerBorderEdge,
null,
_colorBorder,
new BoxShadows());
for (int i = 0; i < Options.Count; ++i) {
var optionButton = Options[i];
if (ButtonStyle == OptionButtonStyle.Solid) {
if (i <= Options.Count - 2) {
var nextOption = Options[i + 1];
if (nextOption == SelectedOption || optionButton == SelectedOption) {
continue;
}
}
}
if (i != Options.Count - 1) {
var offsetX = optionButton.Bounds.Right;
var startPoint = new Point(offsetX, 0);
var endPoint = new Point(offsetX, Bounds.Height);
using var optionState = context.PushRenderOptions(new RenderOptions()
{
EdgeMode = EdgeMode.Aliased
});
context.DrawLine(new Pen(_colorBorder, BorderThickness.Left), startPoint, endPoint);
}
if (ButtonStyle == OptionButtonStyle.Outline) {
if (optionButton.IsEnabled && optionButton.IsChecked.HasValue && optionButton.IsChecked.Value) {
// 绘制选中边框
var offsetX = optionButton.Bounds.X;
var width = optionButton.DesiredSize.Width;
if (i != 0) {
offsetX -= BorderThickness.Left;
width += BorderThickness.Left;
}
var translationMatrix = Matrix.CreateTranslation(offsetX, 0);
using var state = context.PushTransform(translationMatrix);
var cornerRadius = new CornerRadius(0);
if (i == 0) {
cornerRadius = new CornerRadius(CornerRadius.TopLeft, 0, 0, CornerRadius.BottomLeft);
} else if (i == Options.Count - 1) {
cornerRadius = new CornerRadius(0, CornerRadius.TopRight, CornerRadius.BottomRight, 0);
}
_borderRenderHelper.Render(context,
new Size(width, DesiredSize.Height),
BorderThickness,
cornerRadius,
BackgroundSizing.InnerBorderEdge,
null,
SelectedOptionBorderColor,
new BoxShadows());
}
}
}
}
private void HandleOptionPointerEvent(object? sender, OptionButtonPointerEventArgs args)
{
if (args.Button == SelectedOption) {
_controlTokenBinder.ReleaseTriggerBindings(this);
if (args.IsPressed) {
_controlTokenBinder.AddControlBinding(SelectedOptionBorderColorProperty, GlobalResourceKey.ColorPrimaryActive, BindingPriority.StyleTrigger);
} else if (args.IsHovering) {
_controlTokenBinder.AddControlBinding(SelectedOptionBorderColorProperty, GlobalResourceKey.ColorPrimaryHover, BindingPriority.StyleTrigger);
}
}
}
}

View File

@ -1,374 +0,0 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
namespace AtomUI.Controls;
using ButtonSizeType = SizeType;
public partial class OptionButton : IWaveAdornerInfoProvider, IControlCustomStyle
{
private bool _initialized = false;
private ControlStyleState _styleState;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private StackPanel? _stackPanel;
private Label? _label;
private CornerRadius? _originCornerRadius;
public Rect WaveGeometry()
{
return new Rect(0, 0, Bounds.Width, Bounds.Height);
}
public CornerRadius WaveBorderRadius()
{
return CornerRadius;
}
void IControlCustomStyle.SetupUi()
{
Cursor = new Cursor(StandardCursorType.Hand);
VerticalAlignment = VerticalAlignment.Center;
_customStyle.CollectStyleState();
CreateMainLayout();
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.ApplyFixedStyleConfig();
_customStyle.SetupTransitions();
}
private void CreateMainLayout()
{
if (Text.Length == 0 && Content is string content) {
Text = content;
}
_label = new Label()
{
Content = Text,
Padding = new Thickness(0),
VerticalContentAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
_stackPanel = new StackPanel()
{
UseLayoutRounding = false,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
Orientation = Orientation.Horizontal,
ClipToBounds = true
};
_stackPanel.Children.Add(_label);
Content = _stackPanel;
BindUtils.RelayBind(this, WidthProperty, _stackPanel);
BindUtils.RelayBind(this, HeightProperty, _stackPanel);
}
void IControlCustomStyle.SetupTransitions()
{
var transitions = new Transitions();
if (ButtonStyle == OptionButtonStyle.Solid) {
transitions.Add(new SolidColorBrushTransition()
{
Property = BackgroundProperty,
Duration = _motionDuration
});
} else if (ButtonStyle == OptionButtonStyle.Outline) {
transitions.Add(new SolidColorBrushTransition()
{
Property = BorderBrushProperty,
Duration = _motionDuration
});
}
transitions.Add(new SolidColorBrushTransition()
{
Property = ForegroundProperty,
Duration = _motionDuration
});
Transitions = transitions;
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
if (IsChecked.HasValue && IsChecked.Value) {
_styleState |= ControlStyleState.Selected;
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(MotionDurationTokenProperty, GlobalResourceKey.MotionDurationMid);
_controlTokenBinder.AddControlBinding(ColorPrimaryHoverTokenProperty, GlobalResourceKey.ColorPrimaryHover);
_controlTokenBinder.AddControlBinding(ColorPrimaryActiveTokenProperty, GlobalResourceKey.ColorPrimaryActive);
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this, thickness =>
{
if (InOptionGroup) {
return new Thickness(0);
}
return thickness;
}));
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPointerOverProperty ||
e.Property == IsPressedProperty ||
e.Property == IsCheckedProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
if (e.Property == IsPressedProperty) {
if (_styleState.HasFlag(ControlStyleState.Raised)) {
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.RoundRectWave);
}
}
}
if (e.Property == GroupPositionTraitProperty) {
if (_originCornerRadius.HasValue) {
CornerRadius = BuildCornerRadius(GroupPositionTrait, _originCornerRadius!.Value);
}
}
if (_initialized && e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
}
if (_initialized && e.Property == ContentProperty) {
// 不推荐,尽最大能力还原
var oldText = (_label!.Content as string)!;
var newContent = e.GetNewValue<object?>();
if (newContent is string newText) {
if (oldText != newText) {
_label!.Content = newText;
}
}
Content = _stackPanel;
}
if (_initialized && e.Property == TextProperty) {
_label!.Content = Text;
}
if (e.Property == InOptionGroupProperty) {
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
}
private CornerRadius BuildCornerRadius(OptionButtonPositionTrait positionTrait, CornerRadius cornerRadius)
{
if (positionTrait == OptionButtonPositionTrait.First) {
return new CornerRadius(cornerRadius.TopLeft,
0,
0,
cornerRadius.BottomLeft);
} else if (positionTrait == OptionButtonPositionTrait.Last) {
return new CornerRadius(0,
cornerRadius.TopRight,
cornerRadius.BottomRight,
0);
} else if (positionTrait == OptionButtonPositionTrait.Middle) {
return new CornerRadius(0);
}
return cornerRadius;
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
if (ButtonStyle == OptionButtonStyle.Outline) {
ApplyOutlineStyle();
} else if (ButtonStyle == OptionButtonStyle.Solid) {
ApplySolidStyle();
}
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == ButtonSizeType.Small) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
_controlTokenBinder.AddControlBinding(FontSizeProperty, OptionButtonResourceKey.ContentFontSizeSM);
_controlTokenBinder.AddControlBinding(PaddingProperty, OptionButtonResourceKey.PaddingSM);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
} else if (SizeType == ButtonSizeType.Middle) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(FontSizeProperty, OptionButtonResourceKey.ContentFontSize);
_controlTokenBinder.AddControlBinding(PaddingProperty, OptionButtonResourceKey.Padding);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
} else if (SizeType == ButtonSizeType.Large) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
_controlTokenBinder.AddControlBinding(FontSizeProperty, OptionButtonResourceKey.ContentFontSizeLG);
_controlTokenBinder.AddControlBinding(PaddingProperty, OptionButtonResourceKey.PaddingLG);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
}
_originCornerRadius = CornerRadius;
CornerRadius = BuildCornerRadius(GroupPositionTrait, _originCornerRadius!.Value);
}
private void ApplySolidStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (_styleState.HasFlag(ControlStyleState.Selected)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, OptionButtonResourceKey.ButtonSolidCheckedColor);
_controlTokenBinder.AddControlBinding(BackgroundProperty, OptionButtonResourceKey.ButtonSolidCheckedBackground);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty,
OptionButtonResourceKey.ButtonSolidCheckedActiveBackground,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty,
OptionButtonResourceKey.ButtonSolidCheckedHoverBackground,
BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorText);
if (InOptionGroup) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
} else {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainer);
}
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorBorder);
if (_styleState.HasFlag(ControlStyleState.Selected)) {
_controlTokenBinder.AddControlBinding(ForegroundProperty, OptionButtonResourceKey.ButtonCheckedColorDisabled);
_controlTokenBinder.AddControlBinding(BackgroundProperty, OptionButtonResourceKey.ButtonCheckedBgDisabled);
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
}
}
}
private void ApplyOutlineStyle()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (_styleState.HasFlag(ControlStyleState.Selected)) {
if (InOptionGroup) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
} else {
_controlTokenBinder.AddControlBinding(BackgroundProperty, OptionButtonResourceKey.ButtonBackground);
}
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimary);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
} else {
if (InOptionGroup) {
_controlTokenBinder.AddControlBinding(this, BackgroundProperty, GlobalResourceKey.ColorTransparent);
} else {
_controlTokenBinder.AddControlBinding(this, BackgroundProperty,
OptionButtonResourceKey.ButtonCheckedBackground);
}
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorBorder);
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, OptionButtonResourceKey.ButtonColor);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorBorder);
if (_styleState.HasFlag(ControlStyleState.Selected)) {
_controlTokenBinder.AddControlBinding(this, ForegroundProperty,
OptionButtonResourceKey.ButtonCheckedColorDisabled);
_controlTokenBinder.AddControlBinding(this, BackgroundProperty, OptionButtonResourceKey.ButtonCheckedBgDisabled);
} else {
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(this, BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
}
}
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
{
IsHovering = true,
IsPressed = true
});
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
{
IsHovering = true,
IsPressed = false
});
}
protected override void OnPointerEntered(PointerEventArgs e)
{
base.OnPointerEntered(e);
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
{
IsHovering = true,
IsPressed = false
});
}
protected override void OnPointerExited(PointerEventArgs e)
{
base.OnPointerExited(e);
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
{
IsHovering = false,
IsPressed = false
});
}
}

View File

@ -1,6 +1,11 @@
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Layout;
@ -17,7 +22,9 @@ public enum ProgressStatus
Active,
}
public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeAware
public abstract partial class AbstractProgressBar : RangeBaseControl,
ISizeTypeAware,
IControlCustomStyle
{
protected const double LARGE_STROKE_THICKNESS = 8;
protected const double MIDDLE_STROKE_THICKNESS = 6;
@ -190,6 +197,14 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
get => _strokeThickness;
set => SetAndRaise(StrokeThicknessProperty, ref _strokeThickness, value);
}
protected bool _initialized = false;
protected ControlStyleState _styleState;
protected ControlTokenBinder _controlTokenBinder;
internal IControlCustomStyle _customStyle;
protected Label? _percentageLabel;
protected PathIcon? _successCompletedIcon;
protected PathIcon? _exceptionCompletedIcon;
static AbstractProgressBar()
{
@ -297,4 +312,153 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
}
protected virtual void NotifyHandleExtraInfoVisibility() { }
#region IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.SetupTransitions();
NotifySetupUi();
_initialized = true;
}
void IControlCustomStyle.AfterUiStructureReady()
{
NotifyUiStructureReady();
}
protected virtual void NotifyUiStructureReady()
{
// 创建完更新调用一次
NotifyEffectSizeTypeChanged();
UpdateProgress();
}
void IControlCustomStyle.SetupTransitions()
{
var transitions = new Transitions();
transitions.Add(AnimationUtils.CreateTransition<DoubleTransition>(ValueProperty, GlobalResourceKey.MotionDurationVerySlow, new ExponentialEaseOut()));
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBarBrushProperty, GlobalResourceKey.MotionDurationFast));
NotifySetupTransitions(ref transitions);
Transitions = transitions;
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
ApplyIndicatorBarBackgroundStyleConfig();
NotifyApplyFixedStyleConfig();
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
ApplyEffectiveSizeTypeStyleConfig();
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == SizeTypeProperty) {
EffectiveSizeType = e.GetNewValue<SizeType>();
} else if (e.Property == EffectiveSizeTypeProperty) {
if (_initialized) {
NotifyEffectSizeTypeChanged();
}
}
if (e.Property == IsEnabledProperty ||
e.Property == PercentageProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
}
NotifyPropertyChanged(e);
}
protected virtual void NotifySetupTransitions(ref Transitions transitions) {}
protected virtual void ApplyIndicatorBarBackgroundStyleConfig() {}
protected virtual void ApplyEffectiveSizeTypeStyleConfig() {}
protected virtual void NotifySetupUi()
{
var label = GetOrCreatePercentInfoLabel();
AddChildControl(label);
CreateCompletedIcons();
}
protected abstract void CreateCompletedIcons();
protected virtual void NotifyApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(SuccessThresholdBrushProperty, GlobalResourceKey.ColorSuccess);
}
protected virtual void NotifyPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
NotifyApplyVariableStyleConfig();
}
protected virtual void NotifyApplyVariableStyleConfig()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (TrailColor.HasValue) {
GrooveBrush = new SolidColorBrush(TrailColor.Value);
} else {
_controlTokenBinder.AddControlBinding(GrooveBrushProperty, ProgressBarResourceKey.RemainingColor);
}
if (Status == ProgressStatus.Success || MathUtils.AreClose(Value, Maximum)) {
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, GlobalResourceKey.ColorSuccess);
} else if (Status == ProgressStatus.Exception) {
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, GlobalResourceKey.ColorError);
} else {
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, ProgressBarResourceKey.DefaultColor);
}
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextLabel);
if (_initialized) {
_exceptionCompletedIcon!.IconMode = IconMode.Normal;
_successCompletedIcon!.IconMode = IconMode.Normal;
}
} else {
_controlTokenBinder.AddControlBinding(GrooveBrushProperty, GlobalResourceKey.ColorBgContainerDisabled);
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, GlobalResourceKey.ControlItemBgActiveDisabled);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
if (_initialized) {
_exceptionCompletedIcon!.IconMode = IconMode.Disabled;
_successCompletedIcon!.IconMode = IconMode.Disabled;
}
}
}
protected void AddChildControl(Control child)
{
VisualChildren.Add(child);
(child as ISetLogicalParent).SetParent(this);
}
public override void Render(DrawingContext context)
{
NotifyPrepareDrawingContext(context);
RenderGroove(context);
RenderIndicatorBar(context);
}
protected virtual void NotifyPrepareDrawingContext(DrawingContext context) {}
#endregion
}

View File

@ -1,170 +0,0 @@
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Media;
namespace AtomUI.Controls;
public partial class AbstractProgressBar : IControlCustomStyle
{
protected bool _initialized = false;
protected ControlStyleState _styleState;
protected ControlTokenBinder _controlTokenBinder;
internal IControlCustomStyle _customStyle;
protected Label? _percentageLabel;
protected PathIcon? _successCompletedIcon;
protected PathIcon? _exceptionCompletedIcon;
void IControlCustomStyle.SetupUi()
{
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.SetupTransitions();
NotifySetupUi();
_initialized = true;
}
void IControlCustomStyle.AfterUiStructureReady()
{
NotifyUiStructureReady();
}
protected virtual void NotifyUiStructureReady()
{
// 创建完更新调用一次
NotifyEffectSizeTypeChanged();
UpdateProgress();
}
void IControlCustomStyle.SetupTransitions()
{
var transitions = new Transitions();
transitions.Add(AnimationUtils.CreateTransition<DoubleTransition>(ValueProperty, GlobalResourceKey.MotionDurationVerySlow, new ExponentialEaseOut()));
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBarBrushProperty, GlobalResourceKey.MotionDurationFast));
NotifySetupTransitions(ref transitions);
Transitions = transitions;
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
ApplyIndicatorBarBackgroundStyleConfig();
NotifyApplyFixedStyleConfig();
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
ApplyEffectiveSizeTypeStyleConfig();
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == SizeTypeProperty) {
EffectiveSizeType = e.GetNewValue<SizeType>();
} else if (e.Property == EffectiveSizeTypeProperty) {
if (_initialized) {
NotifyEffectSizeTypeChanged();
}
}
if (e.Property == IsEnabledProperty ||
e.Property == PercentageProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
}
NotifyPropertyChanged(e);
}
protected virtual void NotifySetupTransitions(ref Transitions transitions) {}
protected virtual void ApplyIndicatorBarBackgroundStyleConfig() {}
protected virtual void ApplyEffectiveSizeTypeStyleConfig() {}
protected virtual void NotifySetupUi()
{
var label = GetOrCreatePercentInfoLabel();
AddChildControl(label);
CreateCompletedIcons();
}
protected abstract void CreateCompletedIcons();
protected virtual void NotifyApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(SuccessThresholdBrushProperty, GlobalResourceKey.ColorSuccess);
}
protected virtual void NotifyPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
NotifyApplyVariableStyleConfig();
}
protected virtual void NotifyApplyVariableStyleConfig()
{
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (TrailColor.HasValue) {
GrooveBrush = new SolidColorBrush(TrailColor.Value);
} else {
_controlTokenBinder.AddControlBinding(GrooveBrushProperty, ProgressBarResourceKey.RemainingColor);
}
if (Status == ProgressStatus.Success || MathUtils.AreClose(Value, Maximum)) {
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, GlobalResourceKey.ColorSuccess);
} else if (Status == ProgressStatus.Exception) {
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, GlobalResourceKey.ColorError);
} else {
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, ProgressBarResourceKey.DefaultColor);
}
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextLabel);
if (_initialized) {
_exceptionCompletedIcon!.IconMode = IconMode.Normal;
_successCompletedIcon!.IconMode = IconMode.Normal;
}
} else {
_controlTokenBinder.AddControlBinding(GrooveBrushProperty, GlobalResourceKey.ColorBgContainerDisabled);
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, GlobalResourceKey.ControlItemBgActiveDisabled);
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
if (_initialized) {
_exceptionCompletedIcon!.IconMode = IconMode.Disabled;
_successCompletedIcon!.IconMode = IconMode.Disabled;
}
}
}
protected void AddChildControl(Control child)
{
VisualChildren.Add(child);
(child as ISetLogicalParent).SetParent(this);
}
public override void Render(DrawingContext context)
{
NotifyPrepareDrawingContext(context);
RenderGroove(context);
RenderIndicatorBar(context);
}
protected virtual void NotifyPrepareDrawingContext(DrawingContext context) {}
}

View File

@ -1,8 +1,14 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.TokenSystem;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
@ -12,8 +18,17 @@ namespace AtomUI.Controls;
using AvaloniaRadioButton = Avalonia.Controls.RadioButton;
public partial class RadioButton : AvaloniaRadioButton, ICustomHitTest
public partial class RadioButton : AvaloniaRadioButton,
ICustomHitTest,
IWaveAdornerInfoProvider,
IControlCustomStyle
{
private bool _initialized = false;
private IPen? _cachedPen;
private ControlStyleState _styleState;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
static RadioButton()
{
AffectsRender<RadioButton>(
@ -38,6 +53,12 @@ public partial class RadioButton : AvaloniaRadioButton, ICustomHitTest
_initialized = true;
}
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
@ -87,4 +108,136 @@ public partial class RadioButton : AvaloniaRadioButton, ICustomHitTest
{
return true;
}
#region IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
Cursor = new Cursor(StandardCursorType.Hand);
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.SetupTransitions();
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
_controlTokenBinder.AddControlBinding(RadioBorderBrushProperty, GlobalResourceKey.ColorBorder);
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
// 暂时启用和禁用状态不归为 style trigger
_controlTokenBinder.AddControlBinding(RadioInnerBackgroundProperty, RadioButtonResourceKey.RadioColor);
if (_styleState.HasFlag(ControlStyleState.On)) {
_controlTokenBinder.AddControlBinding(RadioBorderBrushProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(RadioBackgroundProperty, GlobalResourceKey.ColorPrimary);
} else {
_controlTokenBinder.AddControlBinding(RadioBackgroundProperty, GlobalResourceKey.ColorBgContainer);
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(RadioBorderBrushProperty, GlobalResourceKey.ColorPrimary,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(RadioBackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(RadioInnerBackgroundProperty, RadioButtonResourceKey.DotColorDisabled,
BindingPriority.StyleTrigger);
}
RadioDotEffectSize = CalculateDotSize(IsEnabled, IsChecked.HasValue && IsChecked.Value);
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
_controlTokenBinder.AddControlBinding(RadioBorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this));
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(RadioSizeProperty, RadioButtonResourceKey.RadioSize);
_controlTokenBinder.AddControlBinding(DotSizeValueProperty, RadioButtonResourceKey.DotSize);
_controlTokenBinder.AddControlBinding(DotPaddingValueProperty, RadioButtonResourceKey.DotPadding);
_controlTokenBinder.AddControlBinding(PaddingInlineProperty, GlobalResourceKey.PaddingXS);
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
if (IsChecked.HasValue && IsChecked.Value) {
_styleState |= ControlStyleState.On;
} else {
_styleState |= ControlStyleState.Off;
}
}
private double CalculateDotSize(bool isEnabled, bool isChecked)
{
double targetValue;
if (isChecked) {
if (isEnabled) {
targetValue = _dotSizeValue;
} else {
targetValue = RadioSize - _dotPaddingValue * 2;
}
} else {
targetValue = _dotSizeValue * 0.6;
}
return targetValue;
}
void IControlCustomStyle.SetupTransitions()
{
Transitions = new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBorderBrushProperty),
AnimationUtils.CreateTransition<DoubleTransition>(RadioDotEffectSizeProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBackgroundProperty, GlobalResourceKey.MotionDurationFast)
};
}
// Measure 之后才有值
private Rect RadioRect()
{
var offsetY = (DesiredSize.Height - Margin.Top - Margin.Bottom - RadioSize) / 2;
return new Rect(0d, offsetY, RadioSize, RadioSize);
}
private Rect RadioTextRect()
{
var offsetX = RadioSize + PaddingInline;
return new Rect(offsetX, 0d, DesiredSize.Width - offsetX, DesiredSize.Height);
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPointerOverProperty ||
e.Property == IsCheckedProperty ||
e.Property == IsEnabledProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
if (e.Property == IsCheckedProperty &&
_styleState.HasFlag(ControlStyleState.Enabled) &&
_styleState.HasFlag(ControlStyleState.On)) {
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.CircleWave);
}
}
}
public Rect WaveGeometry()
{
return RadioRect();
}
public CornerRadius WaveBorderRadius()
{
return new CornerRadius(RadioSize / 2);
}
#endregion
}

View File

@ -1,146 +0,0 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Media;
namespace AtomUI.Controls;
public partial class RadioButton : IWaveAdornerInfoProvider, IControlCustomStyle
{
private bool _initialized = false;
private IPen? _cachedPen;
private ControlStyleState _styleState;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
void IControlCustomStyle.SetupUi()
{
Cursor = new Cursor(StandardCursorType.Hand);
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.SetupTransitions();
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
_controlTokenBinder.AddControlBinding(RadioBorderBrushProperty, GlobalResourceKey.ColorBorder);
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
// 暂时启用和禁用状态不归为 style trigger
_controlTokenBinder.AddControlBinding(RadioInnerBackgroundProperty, RadioButtonResourceKey.RadioColor);
if (_styleState.HasFlag(ControlStyleState.On)) {
_controlTokenBinder.AddControlBinding(RadioBorderBrushProperty, GlobalResourceKey.ColorPrimary);
_controlTokenBinder.AddControlBinding(RadioBackgroundProperty, GlobalResourceKey.ColorPrimary);
} else {
_controlTokenBinder.AddControlBinding(RadioBackgroundProperty, GlobalResourceKey.ColorBgContainer);
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(RadioBorderBrushProperty, GlobalResourceKey.ColorPrimary,
BindingPriority.StyleTrigger);
}
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
_controlTokenBinder.AddControlBinding(RadioBackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled,
BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(RadioInnerBackgroundProperty, RadioButtonResourceKey.DotColorDisabled,
BindingPriority.StyleTrigger);
}
RadioDotEffectSize = CalculateDotSize(IsEnabled, IsChecked.HasValue && IsChecked.Value);
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(RadioSizeProperty, RadioButtonResourceKey.RadioSize);
_controlTokenBinder.AddControlBinding(DotSizeValueProperty, RadioButtonResourceKey.DotSize);
_controlTokenBinder.AddControlBinding(DotPaddingValueProperty, RadioButtonResourceKey.DotPadding);
_controlTokenBinder.AddControlBinding(PaddingInlineProperty, GlobalResourceKey.PaddingXS);
_controlTokenBinder.AddControlBinding(RadioBorderThicknessProperty, GlobalResourceKey.BorderThickness);
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
if (IsChecked.HasValue && IsChecked.Value) {
_styleState |= ControlStyleState.On;
} else {
_styleState |= ControlStyleState.Off;
}
}
private double CalculateDotSize(bool isEnabled, bool isChecked)
{
double targetValue;
if (isChecked) {
if (isEnabled) {
targetValue = _dotSizeValue;
} else {
targetValue = RadioSize - _dotPaddingValue * 2;
}
} else {
targetValue = _dotSizeValue * 0.6;
}
return targetValue;
}
void IControlCustomStyle.SetupTransitions()
{
Transitions = new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBorderBrushProperty),
AnimationUtils.CreateTransition<DoubleTransition>(RadioDotEffectSizeProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBackgroundProperty, GlobalResourceKey.MotionDurationFast)
};
}
// Measure 之后才有值
private Rect RadioRect()
{
var offsetY = (DesiredSize.Height - Margin.Top - Margin.Bottom - RadioSize) / 2;
return new Rect(0d, offsetY, RadioSize, RadioSize);
}
private Rect RadioTextRect()
{
var offsetX = RadioSize + PaddingInline;
return new Rect(offsetX, 0d, DesiredSize.Width - offsetX, DesiredSize.Height);
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPointerOverProperty ||
e.Property == IsCheckedProperty ||
e.Property == IsEnabledProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
if (e.Property == IsCheckedProperty &&
_styleState.HasFlag(ControlStyleState.Enabled) &&
_styleState.HasFlag(ControlStyleState.On)) {
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.CircleWave);
}
}
}
public Rect WaveGeometry()
{
return RadioRect();
}
public CornerRadius WaveBorderRadius()
{
return new CornerRadius(RadioSize / 2);
}
}

View File

@ -1,13 +1,20 @@
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.TokenSystem;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
namespace AtomUI.Controls;
public partial class SegmentedItem : StyledControl
public partial class SegmentedItem : StyledControl, IControlCustomStyle
{
public static readonly StyledProperty<SizeType> SizeTypeProperty =
AvaloniaProperty.Register<SegmentedItem, SizeType>(nameof(SizeType), SizeType.Middle);
@ -17,14 +24,14 @@ public partial class SegmentedItem : StyledControl
public static readonly StyledProperty<PathIcon?> IconProperty
= AvaloniaProperty.Register<SegmentedItem, PathIcon?>(nameof(Icon));
public static readonly DirectProperty<SegmentedItem, bool> IsPressedProperty =
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsPressed), o => o.IsPressed);
public static readonly DirectProperty<SegmentedItem, bool> IsCurrentItemProperty =
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsCurrentItem),
o => o.IsCurrentItem,
(o, v) => o.IsCurrentItem = v);
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsCurrentItem),
o => o.IsCurrentItem,
(o, v) => o.IsCurrentItem = v);
internal SizeType SizeType
{
@ -53,10 +60,22 @@ public partial class SegmentedItem : StyledControl
get => _isPressed;
private set => SetAndRaise(IsPressedProperty, ref _isPressed, value);
}
// 内部属性
private bool _isCurrentItem = false;
internal bool IsCurrentItem { get => _isCurrentItem; set => SetAndRaise(IsCurrentItemProperty, ref _isCurrentItem, value); }
internal bool IsCurrentItem
{
get => _isCurrentItem;
set => SetAndRaise(IsCurrentItemProperty, ref _isCurrentItem, value);
}
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private Label? _label;
private bool _isPressed = false;
private ControlStyleState _styleState;
static SegmentedItem()
{
@ -82,6 +101,7 @@ public partial class SegmentedItem : StyledControl
targetWidth += _paddingXXS;
}
}
return new Size(targetWidth, targetHeight);
}
@ -93,10 +113,11 @@ public partial class SegmentedItem : StyledControl
if (Text.Length == 0) {
offsetX += (DesiredSize.Width - Icon.Width) / 2;
}
Icon.Arrange(new Rect(new (offsetX, offsetY), new Size(Icon.Width, Icon.Height)));
Icon.Arrange(new Rect(new(offsetX, offsetY), new Size(Icon.Width, Icon.Height)));
offsetX += Icon.DesiredSize.Width + _paddingXXS;
}
_label!.Arrange(new Rect(new Point(offsetX, -1), _label.DesiredSize));
return finalSize;
}
@ -132,4 +153,123 @@ public partial class SegmentedItem : StyledControl
IsPressed = false;
}
}
#region IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
_label = new Label()
{
Content = Text,
Padding = new Thickness(0),
VerticalContentAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
};
_customStyle.CollectStyleState();
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.SetupTransitions();
LogicalChildren.Add(_label);
VisualChildren.Add(_label);
ApplyIconStyleConfig();
}
void IControlCustomStyle.SetupTransitions()
{
Transitions = new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty)
};
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
if (IsCurrentItem) {
_styleState |= ControlStyleState.Selected;
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(PaddingXXSTokenProperty, GlobalResourceKey.PaddingXXS);
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
}
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (_initialized) {
if (e.Property == IsPointerOverProperty ||
e.Property == IsPressedProperty ||
e.Property == IsCurrentItemProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
} else if (e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
} else if (e.Property == TextProperty) {
_label!.Content = Text;
} else if (e.Property == IconProperty) {
var oldIcon = e.GetOldValue<PathIcon?>();
if (oldIcon is not null) {
_controlTokenBinder.ReleaseBindings(oldIcon);
LogicalChildren.Remove(oldIcon);
VisualChildren.Remove(oldIcon);
}
ApplyIconStyleConfig();
}
}
}
// 设置大小和颜色
private void ApplyIconStyleConfig()
{
if (Icon is not null) {
if (Icon.ThemeType != IconThemeType.TwoTone) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty,
SegmentedResourceKey.ItemColor);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty,
SegmentedResourceKey.ItemHoverColor);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty,
SegmentedResourceKey.ItemSelectedColor);
}
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSize);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeLG);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeLG);
}
LogicalChildren.Add(Icon);
VisualChildren.Add(Icon);
}
}
#endregion
}

View File

@ -1,8 +1,12 @@
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.TokenSystem;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Rendering;
@ -12,7 +16,9 @@ namespace AtomUI.Controls;
/// <summary>
/// 在内部维护一些额外信息的控件,用户无感知
/// </summary>
internal partial class SegmentedItemBox : BorderedStyleControl, ICustomHitTest
internal partial class SegmentedItemBox : BorderedStyleControl,
ICustomHitTest,
IControlCustomStyle
{
internal Control Item { get; }
@ -44,7 +50,12 @@ internal partial class SegmentedItemBox : BorderedStyleControl, ICustomHitTest
private bool _isCurrentItem;
internal bool IsCurrentItem { get => _isCurrentItem; set => SetCurrentItem(value); }
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private bool _isPressed = false;
private ControlStyleState _styleState;
public int LastItem { get; set; } = -1;
static SegmentedItemBox()
@ -153,4 +164,100 @@ internal partial class SegmentedItemBox : BorderedStyleControl, ICustomHitTest
{
return true;
}
#region IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
Cursor = new Cursor(StandardCursorType.Hand);
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.SetupTransitions();
}
void IControlCustomStyle.SetupTransitions()
{
Transitions = new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty),
};
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
if (IsCurrentItem) {
_styleState |= ControlStyleState.Selected;
}
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSizeLG);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
} else if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusXS);
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
}
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (!_styleState.HasFlag(ControlStyleState.Selected)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
_controlTokenBinder.AddControlBinding(ForegroundProperty, SegmentedResourceKey.ItemColor);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, SegmentedResourceKey.ItemActiveBg, BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, SegmentedResourceKey.ItemHoverBg, BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, SegmentedResourceKey.ItemHoverColor, BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, SegmentedResourceKey.ItemSelectedColor);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(ControlHeightSMTokenProperty, GlobalResourceKey.ControlHeightSM);
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(ControlHeightLGTokenProperty, GlobalResourceKey.ControlHeightLG);
_controlTokenBinder.AddControlBinding(TrackPaddingTokenProperty, SegmentedResourceKey.TrackPadding);
_controlTokenBinder.AddControlBinding(SegmentedItemPaddingSMTokenProperty, SegmentedResourceKey.SegmentedItemPaddingSM);
_controlTokenBinder.AddControlBinding(SegmentedItemPaddingTokenProperty, SegmentedResourceKey.SegmentedItemPadding);
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPressedProperty ||
e.Property == IsPointerOverProperty ||
e.Property == IsCurrentItemProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
}
if (_initialized) {
if (e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
}
}
}
#endregion
}

View File

@ -1,114 +0,0 @@
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Data;
using Avalonia.Input;
namespace AtomUI.Controls;
internal partial class SegmentedItemBox : IControlCustomStyle
{
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private bool _isPressed = false;
private ControlStyleState _styleState;
void IControlCustomStyle.SetupUi()
{
Cursor = new Cursor(StandardCursorType.Hand);
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.SetupTransitions();
}
void IControlCustomStyle.SetupTransitions()
{
Transitions = new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty),
};
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
if (IsCurrentItem) {
_styleState |= ControlStyleState.Selected;
}
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSizeLG);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
} else if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusXS);
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
}
}
void IControlCustomStyle.ApplyVariableStyleConfig()
{
_controlTokenBinder.ReleaseTriggerBindings(this);
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
if (!_styleState.HasFlag(ControlStyleState.Selected)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
_controlTokenBinder.AddControlBinding(ForegroundProperty, SegmentedResourceKey.ItemColor);
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, SegmentedResourceKey.ItemActiveBg, BindingPriority.StyleTrigger);
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
_controlTokenBinder.AddControlBinding(BackgroundProperty, SegmentedResourceKey.ItemHoverBg, BindingPriority.StyleTrigger);
_controlTokenBinder.AddControlBinding(ForegroundProperty, SegmentedResourceKey.ItemHoverColor, BindingPriority.StyleTrigger);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, SegmentedResourceKey.ItemSelectedColor);
}
} else {
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(ControlHeightSMTokenProperty, GlobalResourceKey.ControlHeightSM);
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(ControlHeightLGTokenProperty, GlobalResourceKey.ControlHeightLG);
_controlTokenBinder.AddControlBinding(TrackPaddingTokenProperty, SegmentedResourceKey.TrackPadding);
_controlTokenBinder.AddControlBinding(SegmentedItemPaddingSMTokenProperty, SegmentedResourceKey.SegmentedItemPaddingSM);
_controlTokenBinder.AddControlBinding(SegmentedItemPaddingTokenProperty, SegmentedResourceKey.SegmentedItemPadding);
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPressedProperty ||
e.Property == IsPointerOverProperty ||
e.Property == IsCurrentItemProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
}
if (_initialized) {
if (e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
}
}
}
}

View File

@ -1,133 +0,0 @@
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Layout;
namespace AtomUI.Controls;
public partial class SegmentedItem : IControlCustomStyle
{
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private Label? _label;
private bool _isPressed = false;
private ControlStyleState _styleState;
void IControlCustomStyle.SetupUi()
{
_label = new Label()
{
Content = Text,
Padding = new Thickness(0),
VerticalContentAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
};
_customStyle.CollectStyleState();
_customStyle.ApplySizeTypeStyleConfig();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
_customStyle.SetupTransitions();
LogicalChildren.Add(_label);
VisualChildren.Add(_label);
ApplyIconStyleConfig();
}
void IControlCustomStyle.SetupTransitions()
{
Transitions = new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty)
};
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
if (IsPressed) {
_styleState |= ControlStyleState.Sunken;
} else {
_styleState |= ControlStyleState.Raised;
}
if (IsCurrentItem) {
_styleState |= ControlStyleState.Selected;
}
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(PaddingXXSTokenProperty, GlobalResourceKey.PaddingXXS);
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
}
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (_initialized) {
if (e.Property == IsPointerOverProperty ||
e.Property == IsPressedProperty ||
e.Property == IsCurrentItemProperty) {
_customStyle.CollectStyleState();
_customStyle.ApplyVariableStyleConfig();
} else if (e.Property == SizeTypeProperty) {
_customStyle.ApplySizeTypeStyleConfig();
} else if (e.Property == TextProperty) {
_label!.Content = Text;
} else if (e.Property == IconProperty) {
var oldIcon = e.GetOldValue<PathIcon?>();
if (oldIcon is not null) {
_controlTokenBinder.ReleaseBindings(oldIcon);
LogicalChildren.Remove(oldIcon);
VisualChildren.Remove(oldIcon);
}
ApplyIconStyleConfig();
}
}
}
// 设置大小和颜色
private void ApplyIconStyleConfig()
{
if (Icon is not null) {
if (Icon.ThemeType != IconThemeType.TwoTone) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, SegmentedResourceKey.ItemColor);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, SegmentedResourceKey.ItemHoverColor);
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, SegmentedResourceKey.ItemSelectedColor);
}
if (SizeType == SizeType.Small) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeSM);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeSM);
} else if (SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSize);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSize);
} else if (SizeType == SizeType.Large) {
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeLG);
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeLG);
}
LogicalChildren.Add(Icon);
VisualChildren.Add(Icon);
}
}
}

View File

@ -1,8 +1,14 @@
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Metadata;
namespace AtomUI.Controls;
@ -14,7 +20,7 @@ public enum SeparatorTitlePosition
Center
}
public partial class Separator : StyledControl
public partial class Separator : StyledControl, IControlCustomStyle
{
public static readonly StyledProperty<string?> TitleProperty =
AvaloniaProperty.Register<Separator, string?>(nameof(Title));
@ -25,19 +31,19 @@ public partial class Separator : StyledControl
public static readonly StyledProperty<IBrush?> TitleColorProperty =
AvaloniaProperty.Register<Separator, IBrush?>(nameof(TitleColor));
public static readonly StyledProperty<IBrush?> LineColorProperty =
AvaloniaProperty.Register<Separator, IBrush?>(nameof(LineColor));
public static readonly StyledProperty<Orientation> OrientationProperty =
AvaloniaProperty.Register<Separator, Orientation>(nameof(Orientation), Orientation.Horizontal);
public static readonly StyledProperty<double> OrientationMarginProperty =
AvaloniaProperty.Register<Separator, double>(nameof(OrientationMargin), double.NaN);
public static readonly StyledProperty<bool> IsDashedLineProperty =
AvaloniaProperty.Register<Separator, bool>(nameof(Orientation), false);
public static readonly StyledProperty<double> LineWidthProperty =
AvaloniaProperty.Register<Separator, double>(nameof(LineWidth), 1);
@ -68,7 +74,7 @@ public partial class Separator : StyledControl
get => GetValue(TitleColorProperty);
set => SetValue(TitleColorProperty, value);
}
/// <summary>
/// 分割线标题的颜色
/// </summary>
@ -77,7 +83,7 @@ public partial class Separator : StyledControl
get => GetValue(LineColorProperty);
set => SetValue(LineColorProperty, value);
}
/// <summary>
/// 分割线的方向,垂直和水平分割线
/// </summary>
@ -97,7 +103,7 @@ public partial class Separator : StyledControl
get => GetValue(OrientationMarginProperty);
set => SetValue(OrientationMarginProperty, value);
}
/// <summary>
/// 是否为虚线
/// </summary>
@ -115,25 +121,33 @@ public partial class Separator : StyledControl
get => GetValue(LineWidthProperty);
set => SetValue(LineWidthProperty, value);
}
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private bool _initialized = false;
private bool _scalingAwareConfigApplied = false;
private Label? _titleLabel;
private const double SEPARATOR_LINE_MIN_PROPORTION = 0.25;
private double _currentEdgeDistance;
static Separator()
{
AffectsMeasure<Separator>(OrientationProperty,
AffectsMeasure<Separator>(OrientationProperty,
LineWidthProperty,
TitleProperty);
AffectsArrange<Separator>(TitlePositionProperty);
AffectsRender<Separator>(TitleColorProperty,
AffectsRender<Separator>(TitleColorProperty,
LineColorProperty,
IsDashedLineProperty);
}
public Separator()
{
_controlTokenBinder = new ControlTokenBinder(this, SeparatorToken.ID);
_customStyle = this;
_customStyle.InitOnConstruct();
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
@ -142,18 +156,234 @@ public partial class Separator : StyledControl
_initialized = true;
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
_customStyle.HandlePropertyChangedForStyle(e);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
#region IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
_titleLabel = new Label
{
Content = Title,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
Padding = new Thickness(0)
};
LogicalChildren.Add(_titleLabel);
VisualChildren.Add(_titleLabel);
_customStyle.ApplyFixedStyleConfig();
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
HandleForOrientation();
_controlTokenBinder.AddControlBinding(TitleColorProperty, GlobalResourceKey.ColorText);
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
_controlTokenBinder.AddControlBinding(LineColorProperty, GlobalResourceKey.ColorSplit);
_controlTokenBinder.AddControlBinding(TextPaddingInlineTokenProperty, SeparatorResourceKey.TextPaddingInline);
_controlTokenBinder.AddControlBinding(OrientationMarginPercentTokenProperty,
SeparatorResourceKey.OrientationMarginPercent);
_controlTokenBinder.AddControlBinding(VerticalMarginInlineTokenProperty,
SeparatorResourceKey.VerticalMarginInline);
if (_titleLabel is not null) {
BindUtils.RelayBind(this, FontSizeProperty, _titleLabel);
BindUtils.RelayBind(this, TitleColorProperty, _titleLabel, ForegroundProperty);
BindUtils.RelayBind(this, FontStyleProperty, _titleLabel, FontStyleProperty);
BindUtils.RelayBind(this, FontWeightProperty, _titleLabel, FontWeightProperty);
}
}
private void HandleForOrientation()
{
if (Orientation == Orientation.Horizontal) {
HorizontalAlignment = HorizontalAlignment.Stretch;
VerticalAlignment = VerticalAlignment.Center;
_titleLabel!.IsVisible = true;
} else {
HorizontalAlignment = HorizontalAlignment.Center;
VerticalAlignment = VerticalAlignment.Center;
_titleLabel!.IsVisible = false;
}
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
if (!_scalingAwareConfigApplied) {
_scalingAwareConfigApplied = true;
_controlTokenBinder.AddControlBinding(LineWidthProperty, GlobalResourceKey.LineWidth, BindingPriority.Style,
new RenderScaleAwareDoubleConfigure(this));
}
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == OrientationProperty) {
if (_initialized) {
HandleForOrientation();
}
}
}
// 当为水平分隔线的时候,我们设置最小的高度,当为垂直分割线的时候我们设置一个合适宽度
// 然后保持尽可能保持文字尽可能的显示,如果小于最小分隔部分的两倍的时候,文字隐藏。
protected override Size MeasureOverride(Size availableSize)
{
var size = base.MeasureOverride(availableSize);
var targetHeight = size.Height;
var targetWidth = size.Width;
if (Orientation == Orientation.Horizontal) {
if (Title is null || Title?.Length == 0) {
targetHeight = LineWidth * 3;
}
if (!double.IsInfinity(availableSize.Width)) {
targetWidth = Math.Max(availableSize.Width, targetWidth);
}
} else {
targetWidth = Math.Max(1, LineWidth) + _verticalMarginInline;
targetHeight = FontUtils.ConvertEmToPixel(1, FontSize, TopLevel.GetTopLevel(this)?.RenderScaling ?? 1.0);
if (!double.IsInfinity(availableSize.Height)) {
targetHeight = Math.Max(availableSize.Height, targetHeight);
}
}
return new Size(targetWidth, targetHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
if (Orientation == Orientation.Horizontal && _titleLabel!.IsVisible) {
var titleRect = GetTitleRect(finalSize);
_titleLabel.Arrange(titleRect);
}
return finalSize;
}
private double GetTextPaddingInline()
{
return FontSize * _textPaddingInline;
}
private Rect GetTitleRect(Size finalSize)
{
Rect titleRect = default;
if (Orientation == Orientation.Horizontal && Title?.Length > 0) {
// 线最小得占到 25 %,拍脑袋
var lineMinWidth = finalSize.Width * SEPARATOR_LINE_MIN_PROPORTION;
var titleWidth = _titleLabel!.DesiredSize.Width + 2;
var remainWidth = finalSize.Width - titleWidth - GetTextPaddingInline() * 2;
if (lineMinWidth > remainWidth) {
// 字过多
titleWidth = finalSize.Width - lineMinWidth;
}
// 处理完成之后,字的宽度一定在 width 范围内
// 计算位置
if (TitlePosition == SeparatorTitlePosition.Left) {
if (!double.IsNaN(OrientationMargin)) {
_currentEdgeDistance = Math.Min((finalSize.Width - titleWidth) / 2, OrientationMargin);
} else {
_currentEdgeDistance = finalSize.Width * _orientationMarginPercent;
}
titleRect = new Rect(new Point(_currentEdgeDistance + GetTextPaddingInline(), 0),
new Size(titleWidth, finalSize.Height));
var rightDelta = titleRect.Right - finalSize.Width;
if (MathUtils.GreaterThan(rightDelta, 0)) {
titleRect = titleRect.WithWidth(finalSize.Width - titleRect.Left);
}
} else if (TitlePosition == SeparatorTitlePosition.Right) {
if (!double.IsNaN(OrientationMargin)) {
_currentEdgeDistance = Math.Min((finalSize.Width - titleWidth) / 2, OrientationMargin);
} else {
_currentEdgeDistance = finalSize.Width * _orientationMarginPercent;
}
titleRect = new Rect(
new Point(finalSize.Width - _currentEdgeDistance - titleWidth - GetTextPaddingInline() * 2, 0),
new Size(titleWidth, finalSize.Height));
double leftDelta = titleRect.Left - 0;
if (leftDelta < 0) {
titleRect = titleRect.WithX(0);
}
} else {
// 居中
titleRect = new Rect(new Point((finalSize.Width - titleWidth) / 2, 0),
new Size(titleWidth, finalSize.Height));
}
}
return titleRect;
}
public override void Render(DrawingContext context)
{
using var state = context.PushRenderOptions(new RenderOptions
{
BitmapInterpolationMode = BitmapInterpolationMode.LowQuality,
TextRenderingMode = TextRenderingMode.Alias
});
var linePen = new Pen(LineColor, LineWidth);
var controlRect = new Rect(new Point(0, 0), DesiredSize);
if (IsDashedLine) {
linePen.DashStyle = DashStyle.Dash;
}
if (Orientation == Orientation.Horizontal) {
var offsetY = controlRect.Height / 2.0;
if (Title?.Length > 0) {
// 画两个线段
var titleRect = GetTitleRect(DesiredSize);
if (TitlePosition == SeparatorTitlePosition.Left) {
if (double.IsNaN(OrientationMargin)) {
context.DrawLine(linePen, new Point(0, offsetY),
new Point(titleRect.Left - GetTextPaddingInline(), offsetY));
}
context.DrawLine(linePen, new Point(titleRect.Right + GetTextPaddingInline(), offsetY),
new Point(controlRect.Right, offsetY));
} else if (TitlePosition == SeparatorTitlePosition.Right) {
context.DrawLine(linePen, new Point(0, offsetY),
new Point(titleRect.Left - GetTextPaddingInline(), offsetY));
if (double.IsNaN(OrientationMargin)) {
context.DrawLine(linePen, new Point(titleRect.Right + GetTextPaddingInline(), offsetY),
new Point(controlRect.Right, offsetY));
}
} else {
context.DrawLine(linePen, new Point(0, offsetY),
new Point(titleRect.Left - GetTextPaddingInline(), offsetY));
context.DrawLine(linePen, new Point(titleRect.Right + GetTextPaddingInline(), offsetY),
new Point(controlRect.Right, offsetY));
}
} else {
context.DrawLine(linePen, new Point(0, offsetY), new Point(controlRect.Right, offsetY));
}
} else {
var offsetX = controlRect.Width / 2.0;
context.DrawLine(linePen, new Point(offsetX, 0), new Point(offsetX, controlRect.Bottom));
}
}
#endregion
}
public class VerticalSeparator : Separator

View File

@ -1,219 +0,0 @@
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Math = System.Math;
namespace AtomUI.Controls;
public partial class Separator : IControlCustomStyle
{
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private bool _initialized = false;
private bool _scalingAwareConfigApplied = false;
private Label? _titleLabel;
private const double SEPARATOR_LINE_MIN_PROPORTION = 0.25;
private double _currentEdgeDistance;
void IControlCustomStyle.SetupUi()
{
_titleLabel = new Label
{
Content = Title,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
Padding = new Thickness(0)
};
LogicalChildren.Add(_titleLabel);
VisualChildren.Add(_titleLabel);
_customStyle.ApplyFixedStyleConfig();
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
HandleForOrientation();
_controlTokenBinder.AddControlBinding(TitleColorProperty, GlobalResourceKey.ColorText);
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
_controlTokenBinder.AddControlBinding(LineColorProperty, GlobalResourceKey.ColorSplit);
_controlTokenBinder.AddControlBinding(TextPaddingInlineTokenProperty, SeparatorResourceKey.TextPaddingInline);
_controlTokenBinder.AddControlBinding(OrientationMarginPercentTokenProperty, SeparatorResourceKey.OrientationMarginPercent);
_controlTokenBinder.AddControlBinding(VerticalMarginInlineTokenProperty, SeparatorResourceKey.VerticalMarginInline);
if (_titleLabel is not null) {
BindUtils.RelayBind(this, FontSizeProperty, _titleLabel);
BindUtils.RelayBind(this, TitleColorProperty, _titleLabel, ForegroundProperty);
BindUtils.RelayBind(this, FontStyleProperty, _titleLabel, FontStyleProperty);
BindUtils.RelayBind(this, FontWeightProperty, _titleLabel, FontWeightProperty);
}
}
private void HandleForOrientation()
{
if (Orientation == Orientation.Horizontal) {
HorizontalAlignment = HorizontalAlignment.Stretch;
VerticalAlignment = VerticalAlignment.Center;
_titleLabel!.IsVisible = true;
} else {
HorizontalAlignment = HorizontalAlignment.Center;
VerticalAlignment = VerticalAlignment.Center;
_titleLabel!.IsVisible = false;
}
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
if (!_scalingAwareConfigApplied) {
_scalingAwareConfigApplied = true;
_controlTokenBinder.AddControlBinding(LineWidthProperty, GlobalResourceKey.LineWidth, BindingPriority.Style,
new RenderScaleAwareDoubleConfigure(this));
}
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == OrientationProperty) {
if (_initialized) {
HandleForOrientation();
}
}
}
// 当为水平分隔线的时候,我们设置最小的高度,当为垂直分割线的时候我们设置一个合适宽度
// 然后保持尽可能保持文字尽可能的显示,如果小于最小分隔部分的两倍的时候,文字隐藏。
protected override Size MeasureOverride(Size availableSize)
{
var size = base.MeasureOverride(availableSize);
var targetHeight = size.Height;
var targetWidth = size.Width;
if (Orientation == Orientation.Horizontal) {
if (Title is null || Title?.Length == 0) {
targetHeight = LineWidth * 3;
}
if (!double.IsInfinity(availableSize.Width)) {
targetWidth = Math.Max(availableSize.Width, targetWidth);
}
} else {
targetWidth = Math.Max(1, LineWidth) + _verticalMarginInline;
targetHeight = FontUtils.ConvertEmToPixel(1, FontSize, TopLevel.GetTopLevel(this)?.RenderScaling ?? 1.0);
if (!double.IsInfinity(availableSize.Height)) {
targetHeight = Math.Max(availableSize.Height, targetHeight);
}
}
return new Size(targetWidth, targetHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
if (Orientation == Orientation.Horizontal && _titleLabel!.IsVisible) {
var titleRect = GetTitleRect(finalSize);
_titleLabel.Arrange(titleRect);
}
return finalSize;
}
private double GetTextPaddingInline()
{
return FontSize * _textPaddingInline;
}
private Rect GetTitleRect(Size finalSize)
{
Rect titleRect = default;
if (Orientation == Orientation.Horizontal && Title?.Length > 0) {
// 线最小得占到 25 %,拍脑袋
var lineMinWidth = finalSize.Width * SEPARATOR_LINE_MIN_PROPORTION;
var titleWidth = _titleLabel!.DesiredSize.Width + 2;
var remainWidth = finalSize.Width - titleWidth - GetTextPaddingInline() * 2;
if (lineMinWidth > remainWidth) {
// 字过多
titleWidth = finalSize.Width - lineMinWidth;
}
// 处理完成之后,字的宽度一定在 width 范围内
// 计算位置
if (TitlePosition == SeparatorTitlePosition.Left) {
if (!double.IsNaN(OrientationMargin)) {
_currentEdgeDistance = Math.Min((finalSize.Width - titleWidth) / 2, OrientationMargin);
} else {
_currentEdgeDistance = finalSize.Width * _orientationMarginPercent;
}
titleRect = new Rect(new Point(_currentEdgeDistance + GetTextPaddingInline(), 0), new Size(titleWidth, finalSize.Height));
var rightDelta = titleRect.Right - finalSize.Width;
if (MathUtils.GreaterThan(rightDelta, 0)) {
titleRect = titleRect.WithWidth(finalSize.Width - titleRect.Left);
}
} else if (TitlePosition == SeparatorTitlePosition.Right) {
if (!double.IsNaN(OrientationMargin)) {
_currentEdgeDistance = Math.Min((finalSize.Width - titleWidth) / 2, OrientationMargin);
} else {
_currentEdgeDistance = finalSize.Width * _orientationMarginPercent;
}
titleRect = new Rect(new Point(finalSize.Width - _currentEdgeDistance - titleWidth - GetTextPaddingInline() * 2, 0),
new Size(titleWidth, finalSize.Height));
double leftDelta = titleRect.Left - 0;
if (leftDelta < 0) {
titleRect = titleRect.WithX(0);
}
} else {
// 居中
titleRect = new Rect(new Point((finalSize.Width - titleWidth) / 2, 0), new Size(titleWidth, finalSize.Height));
}
}
return titleRect;
}
public override void Render(DrawingContext context)
{
using var state = context.PushRenderOptions(new RenderOptions
{
BitmapInterpolationMode = BitmapInterpolationMode.LowQuality,
TextRenderingMode = TextRenderingMode.Alias
});
var linePen = new Pen(LineColor, LineWidth);
var controlRect = new Rect(new Point(0, 0), DesiredSize);
if (IsDashedLine) {
linePen.DashStyle = DashStyle.Dash;
}
if (Orientation == Orientation.Horizontal) {
var offsetY = controlRect.Height / 2.0;
if (Title?.Length > 0) {
// 画两个线段
var titleRect = GetTitleRect(DesiredSize);
if (TitlePosition == SeparatorTitlePosition.Left) {
if (double.IsNaN(OrientationMargin)) {
context.DrawLine(linePen, new Point(0, offsetY), new Point(titleRect.Left - GetTextPaddingInline(), offsetY));
}
context.DrawLine(linePen, new Point(titleRect.Right + GetTextPaddingInline(), offsetY),
new Point(controlRect.Right, offsetY));
} else if (TitlePosition == SeparatorTitlePosition.Right) {
context.DrawLine(linePen, new Point(0, offsetY), new Point(titleRect.Left - GetTextPaddingInline(), offsetY));
if (double.IsNaN(OrientationMargin)) {
context.DrawLine(linePen, new Point(titleRect.Right + GetTextPaddingInline(), offsetY), new Point(controlRect.Right, offsetY));
}
} else {
context.DrawLine(linePen, new Point(0, offsetY), new Point(titleRect.Left - GetTextPaddingInline(), offsetY));
context.DrawLine(linePen, new Point(titleRect.Right + GetTextPaddingInline(), offsetY), new Point(controlRect.Right, offsetY));
}
} else {
context.DrawLine(linePen, new Point(0, offsetY), new Point(controlRect.Right, offsetY));
}
} else {
var offsetX = controlRect.Width / 2.0;
context.DrawLine(linePen, new Point(offsetX, 0), new Point(offsetX, controlRect.Bottom));
}
}
}

View File

@ -1,14 +1,19 @@
using AtomUI.ColorSystem;
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Styling;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
namespace AtomUI.Controls;
public enum TagStatus {
public enum TagStatus
{
// 状态
Success,
Info,
@ -16,25 +21,51 @@ public enum TagStatus {
Warning
}
public partial class Tag : Label
internal struct TagCalcColor
{
public Color LightColor { get; set; } // 1 号色
public Color LightBorderColor { get; set; } // 3 号色
public Color DarkColor { get; set; } // 6 号色
public Color TextColor { get; set; } // 7 号色
}
internal struct TagStatusCalcColor
{
public Color Color { get; set; }
public Color Background { get; set; }
public Color BorderColor { get; set; }
}
public partial class Tag : Label, IControlCustomStyle
{
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private ControlStyleState _styleState;
private bool _isPresetColorTag = false;
private bool _hasColorSet = false;
private static Dictionary<PresetColorType, TagCalcColor> _presetColorMap;
private static Dictionary<TagStatus, TagStatusCalcColor> _statusColorMap;
private Panel? _layoutPanel;
private TextBlock? _textBlock;
static Tag()
{
_presetColorMap = new Dictionary<PresetColorType, TagCalcColor>();
_statusColorMap = new Dictionary<TagStatus, TagStatusCalcColor>();
AffectsMeasure<Tag>(BorderedProperty,
IconProperty,
ClosableProperty,
PaddingXXSTokenProperty,
TagCloseIconSizeTokenProperty,
TagIconSizeTokenProperty);
IconProperty,
ClosableProperty,
PaddingXXSTokenProperty,
TagCloseIconSizeTokenProperty,
TagIconSizeTokenProperty);
AffectsRender<Tag>(ClosableProperty,
DefaultBgTokenProperty,
DefaultForegroundTokenProperty,
TagBorderlessBgTokenProperty,
ColorTextLightSolidTokenProperty);
DefaultBgTokenProperty,
DefaultForegroundTokenProperty,
TagBorderlessBgTokenProperty,
ColorTextLightSolidTokenProperty);
}
public Tag()
{
_customStyle = this;
@ -43,7 +74,7 @@ public partial class Tag : Label
SetupStatusColorMap();
_customStyle.InitOnConstruct();
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
@ -52,13 +83,13 @@ public partial class Tag : Label
_initialized = true;
}
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_customStyle.ApplyRenderScalingAwareStyleConfig();
}
protected override Size MeasureOverride(Size availableSize)
{
var textBlock = _textBlock!;
@ -95,18 +126,20 @@ public partial class Tag : Label
if (Icon is not null) {
Icon.Arrange(IconRect(finalSize));
}
if (CloseIcon is not null) {
CloseIcon.Arrange(CloseIconRect(finalSize));
}
return finalSize;
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
_customStyle.HandlePropertyChangedForStyle(e);
}
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);
@ -119,4 +152,290 @@ public partial class Tag : Label
}
}
}
#region IControlCustomStyle
void IControlCustomStyle.InitOnConstruct()
{
_layoutPanel = new Panel();
}
void IControlCustomStyle.SetupUi()
{
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
if (TagColor is not null) {
SetupTagColorInfo(TagColor);
}
var tagContent = string.Empty;
if (Content is string) {
// 只接受字符串
tagContent = Content as string;
}
_textBlock = new TextBlock
{
Text = tagContent,
VerticalAlignment = VerticalAlignment.Center
};
_layoutPanel?.Children.Add(_textBlock);
Content = _layoutPanel;
SetupTagIcon();
SetupTagClosable();
_customStyle.SetupTransitions();
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(DefaultBgTokenProperty, TagResourceKey.DefaultBg);
_controlTokenBinder.AddControlBinding(DefaultForegroundTokenProperty, TagResourceKey.DefaultColor);
_controlTokenBinder.AddControlBinding(FontSizeProperty, TagResourceKey.TagFontSize);
_controlTokenBinder.AddControlBinding(TagLineHeightTokenProperty, TagResourceKey.TagLineHeight);
_controlTokenBinder.AddControlBinding(TagIconSizeTokenProperty, TagResourceKey.TagIconSize);
_controlTokenBinder.AddControlBinding(TagCloseIconSizeTokenProperty, TagResourceKey.TagCloseIconSize);
_controlTokenBinder.AddControlBinding(PaddingXXSTokenProperty, GlobalResourceKey.PaddingXXS);
_controlTokenBinder.AddControlBinding(PaddingProperty, TagResourceKey.TagPadding);
_controlTokenBinder.AddControlBinding(TagBorderlessBgTokenProperty, TagResourceKey.TagBorderlessBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorBorder);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
_controlTokenBinder.AddControlBinding(ColorTextLightSolidTokenProperty, GlobalResourceKey.ColorTextLightSolid);
Background = _defaultBackground;
Foreground = _defaultForeground;
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, "BorderThickness", BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this, thickness =>
{
if (!Bordered) {
return new Thickness(0);
}
return thickness;
}));
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == ClosableProperty && _initialized) {
SetupTagClosable();
}
}
private void SetupPresetColorMap()
{
if (_presetColorMap.Count == 0) {
// TODO 根据当前的主题风格设置,是否需要根据风格不一样进行动态调整呢?
var activatedTheme = ThemeManager.Current.ActivatedTheme;
var globalToken = activatedTheme?.GlobalToken;
if (globalToken == null) {
// 是否需要输出日志
return;
}
foreach (var entry in PresetPrimaryColor.AllColorTypes()) {
var colorMap = globalToken.GetColorPalette(entry)!;
var calcColor = new TagCalcColor()
{
LightColor = colorMap.Color1,
LightBorderColor = colorMap.Color3,
DarkColor = colorMap.Color6,
TextColor = colorMap.Color7
};
_presetColorMap.Add(entry.Type, calcColor);
}
}
}
private void SetupStatusColorMap()
{
if (_statusColorMap.Count == 0) {
var activatedTheme = ThemeManager.Current.ActivatedTheme;
var globalToken = activatedTheme?.GlobalToken;
if (globalToken == null) {
// 是否需要输出日志
return;
}
var colorToken = globalToken.ColorToken;
var colorSuccessToken = colorToken.ColorSuccessToken;
var colorInfoToken = colorToken.ColorInfoToken;
var colorWarningToken = colorToken.ColorWarningToken;
var colorErrorToken = colorToken.ColorErrorToken;
_statusColorMap.Add(TagStatus.Success, new TagStatusCalcColor
{
Color = colorSuccessToken.ColorSuccess,
Background = colorSuccessToken.ColorSuccessBg,
BorderColor = colorSuccessToken.ColorSuccessBorder
});
_statusColorMap.Add(TagStatus.Info, new TagStatusCalcColor
{
Color = colorInfoToken.ColorInfo,
Background = colorInfoToken.ColorInfoBg,
BorderColor = colorInfoToken.ColorInfoBorder
});
_statusColorMap.Add(TagStatus.Warning, new TagStatusCalcColor
{
Color = colorWarningToken.ColorWarning,
Background = colorWarningToken.ColorWarningBg,
BorderColor = colorWarningToken.ColorWarningBorder
});
_statusColorMap.Add(TagStatus.Error, new TagStatusCalcColor
{
Color = colorErrorToken.ColorError,
Background = colorErrorToken.ColorErrorBg,
BorderColor = colorErrorToken.ColorErrorBorder
});
}
}
private Rect IconRect(Size controlSize)
{
var bevelRect = BevelRect(controlSize);
var offsetX = bevelRect.Left + Padding.Left;
var offsetY = bevelRect.Y + (bevelRect.Height - _tagIconSize) / 2;
return new Rect(offsetX, offsetY, _tagIconSize, _tagIconSize);
}
private Rect CloseIconRect(Size controlSize)
{
var bevelRect = BevelRect(controlSize);
var offsetX = bevelRect.Right - Padding.Right - _tagCloseIconSize;
var offsetY = bevelRect.Y + (bevelRect.Height - _tagCloseIconSize) / 2;
return new Rect(offsetX, offsetY, _tagCloseIconSize, _tagCloseIconSize);
}
private Rect BevelRect(Size controlSize)
{
var targetRect = new Rect(0, 0, controlSize.Width, controlSize.Height);
return targetRect.Deflate(BorderThickness);
}
private Rect TextRect(Size controlSize)
{
var bevelRect = BevelRect(controlSize);
var offsetX = bevelRect.Left + Padding.Left;
if (Icon is not null) {
offsetX += _tagIconSize + _paddingXXS;
}
// 这个时候已经算好了
var textSize = _textBlock!.DesiredSize;
var offsetY = bevelRect.Y + (bevelRect.Height - textSize.Height) / 2;
return new Rect(offsetX, offsetY, textSize.Width, textSize.Height);
}
private void SetupTagColorInfo(string colorStr)
{
_isPresetColorTag = false;
_hasColorSet = false;
colorStr = colorStr.Trim().ToLower();
Background = _defaultBackground;
foreach (var entry in _presetColorMap) {
if (entry.Key.ToString().ToLower() == colorStr) {
var colorInfo = _presetColorMap[entry.Key];
Foreground = new SolidColorBrush(colorInfo.TextColor);
BorderBrush = new SolidColorBrush(colorInfo.LightBorderColor);
Background = new SolidColorBrush(colorInfo.LightColor);
_isPresetColorTag = true;
return;
}
}
foreach (var entry in _statusColorMap) {
if (entry.Key.ToString().ToLower() == colorStr) {
var colorInfo = _statusColorMap[entry.Key];
Foreground = new SolidColorBrush(colorInfo.Color);
BorderBrush = new SolidColorBrush(colorInfo.BorderColor);
Background = new SolidColorBrush(colorInfo.Background);
_isPresetColorTag = true;
return;
}
}
if (Color.TryParse(colorStr, out Color color)) {
Bordered = false;
Foreground = _colorTextLightSolid;
_hasColorSet = true;
Background = new SolidColorBrush(color);
}
}
private void SetupTagClosable()
{
if (Closable) {
if (CloseIcon is null) {
CloseIcon = new PathIcon()
{
Kind = "CloseOutlined",
Width = _tagCloseIconSize,
Height = _tagCloseIconSize,
};
if (_hasColorSet && !_isPresetColorTag) {
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty,
GlobalResourceKey.ColorTextLightSolid);
} else {
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty,
GlobalResourceKey.ColorIcon);
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.ActiveFilledBrushProperty,
GlobalResourceKey.ColorIconHover);
}
} else {
CloseIcon.Width = _tagCloseIconSize;
CloseIcon.Height = _tagCloseIconSize;
if (CloseIcon.ThemeType != IconThemeType.TwoTone) {
if (_hasColorSet && !_isPresetColorTag) {
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty,
GlobalResourceKey.ColorTextLightSolid);
} else {
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty,
GlobalResourceKey.ColorIcon);
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.ActiveFilledBrushProperty,
GlobalResourceKey.ColorIconHover);
}
}
}
CloseIcon.Cursor = new Cursor(StandardCursorType.Hand);
_layoutPanel?.Children.Add(CloseIcon);
} else {
if (CloseIcon != null) {
_layoutPanel?.Children.Remove(CloseIcon);
CloseIcon = null;
}
}
}
private void SetupTagIcon()
{
if (Icon is not null) {
Icon.Width = _tagIconSize;
Icon.Height = _tagIconSize;
_layoutPanel?.Children.Insert(0, Icon);
if (_hasColorSet) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty,
GlobalResourceKey.ColorTextLightSolid);
} else if (_isPresetColorTag) {
Icon.NormalFilledBrush = Foreground;
}
}
}
#endregion
}

View File

@ -1,316 +0,0 @@
using AtomUI.ColorSystem;
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Styling;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
namespace AtomUI.Controls;
internal struct TagCalcColor
{
public Color LightColor { get; set; } // 1 号色
public Color LightBorderColor { get; set; } // 3 号色
public Color DarkColor { get; set; } // 6 号色
public Color TextColor { get; set; } // 7 号色
}
internal struct TagStatusCalcColor
{
public Color Color { get; set; }
public Color Background { get; set; }
public Color BorderColor { get; set; }
}
public partial class Tag : IControlCustomStyle
{
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private ControlStyleState _styleState;
private bool _isPresetColorTag = false;
private bool _hasColorSet = false;
private static Dictionary<PresetColorType, TagCalcColor> _presetColorMap;
private static Dictionary<TagStatus, TagStatusCalcColor> _statusColorMap;
private Panel? _layoutPanel;
private TextBlock? _textBlock;
void IControlCustomStyle.InitOnConstruct()
{
_layoutPanel = new Panel();
}
void IControlCustomStyle.SetupUi()
{
_customStyle.CollectStyleState();
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplyVariableStyleConfig();
if (TagColor is not null) {
SetupTagColorInfo(TagColor);
}
var tagContent = string.Empty;
if (Content is string) {
// 只接受字符串
tagContent = Content as string;
}
_textBlock = new TextBlock
{
Text = tagContent,
VerticalAlignment = VerticalAlignment.Center
};
_layoutPanel?.Children.Add(_textBlock);
Content = _layoutPanel;
SetupTagIcon();
SetupTagClosable();
_customStyle.SetupTransitions();
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(DefaultBgTokenProperty, TagResourceKey.DefaultBg);
_controlTokenBinder.AddControlBinding(DefaultForegroundTokenProperty, TagResourceKey.DefaultColor);
_controlTokenBinder.AddControlBinding(FontSizeProperty, TagResourceKey.TagFontSize);
_controlTokenBinder.AddControlBinding(TagLineHeightTokenProperty, TagResourceKey.TagLineHeight);
_controlTokenBinder.AddControlBinding(TagIconSizeTokenProperty, TagResourceKey.TagIconSize);
_controlTokenBinder.AddControlBinding(TagCloseIconSizeTokenProperty, TagResourceKey.TagCloseIconSize);
_controlTokenBinder.AddControlBinding(PaddingXXSTokenProperty, GlobalResourceKey.PaddingXXS);
_controlTokenBinder.AddControlBinding(PaddingProperty, TagResourceKey.TagPadding);
_controlTokenBinder.AddControlBinding(TagBorderlessBgTokenProperty, TagResourceKey.TagBorderlessBg);
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorBorder);
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
_controlTokenBinder.AddControlBinding(ColorTextLightSolidTokenProperty, GlobalResourceKey.ColorTextLightSolid);
Background = _defaultBackground;
Foreground = _defaultForeground;
}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
{
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, "BorderThickness", BindingPriority.Style,
new RenderScaleAwareThicknessConfigure(this, thickness =>
{
if (!Bordered) {
return new Thickness(0);
}
return thickness;
}));
}
void IControlCustomStyle.CollectStyleState()
{
StyleUtils.InitCommonState(this, ref _styleState);
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == ClosableProperty && _initialized) {
SetupTagClosable();
}
}
private void SetupPresetColorMap()
{
if (_presetColorMap.Count == 0) {
// TODO 根据当前的主题风格设置,是否需要根据风格不一样进行动态调整呢?
var activatedTheme = ThemeManager.Current.ActivatedTheme;
var globalToken = activatedTheme?.GlobalToken;
if (globalToken == null) {
// 是否需要输出日志
return;
}
foreach (var entry in PresetPrimaryColor.AllColorTypes()) {
var colorMap = globalToken.GetColorPalette(entry)!;
var calcColor = new TagCalcColor()
{
LightColor = colorMap.Color1,
LightBorderColor = colorMap.Color3,
DarkColor = colorMap.Color6,
TextColor = colorMap.Color7
};
_presetColorMap.Add(entry.Type, calcColor);
}
}
}
private void SetupStatusColorMap()
{
if (_statusColorMap.Count == 0) {
var activatedTheme = ThemeManager.Current.ActivatedTheme;
var globalToken = activatedTheme?.GlobalToken;
if (globalToken == null) {
// 是否需要输出日志
return;
}
var colorToken = globalToken.ColorToken;
var colorSuccessToken = colorToken.ColorSuccessToken;
var colorInfoToken = colorToken.ColorInfoToken;
var colorWarningToken = colorToken.ColorWarningToken;
var colorErrorToken = colorToken.ColorErrorToken;
_statusColorMap.Add(TagStatus.Success, new TagStatusCalcColor
{
Color = colorSuccessToken.ColorSuccess,
Background = colorSuccessToken.ColorSuccessBg,
BorderColor = colorSuccessToken.ColorSuccessBorder
});
_statusColorMap.Add(TagStatus.Info, new TagStatusCalcColor
{
Color = colorInfoToken.ColorInfo,
Background = colorInfoToken.ColorInfoBg,
BorderColor = colorInfoToken.ColorInfoBorder
});
_statusColorMap.Add(TagStatus.Warning, new TagStatusCalcColor
{
Color = colorWarningToken.ColorWarning,
Background = colorWarningToken.ColorWarningBg,
BorderColor = colorWarningToken.ColorWarningBorder
});
_statusColorMap.Add(TagStatus.Error, new TagStatusCalcColor
{
Color = colorErrorToken.ColorError,
Background = colorErrorToken.ColorErrorBg,
BorderColor = colorErrorToken.ColorErrorBorder
});
}
}
private Rect IconRect(Size controlSize)
{
var bevelRect = BevelRect(controlSize);
var offsetX = bevelRect.Left + Padding.Left;
var offsetY = bevelRect.Y + (bevelRect.Height - _tagIconSize) / 2;
return new Rect(offsetX, offsetY, _tagIconSize, _tagIconSize);
}
private Rect CloseIconRect(Size controlSize)
{
var bevelRect = BevelRect(controlSize);
var offsetX = bevelRect.Right - Padding.Right - _tagCloseIconSize;
var offsetY = bevelRect.Y + (bevelRect.Height - _tagCloseIconSize) / 2;
return new Rect(offsetX, offsetY, _tagCloseIconSize, _tagCloseIconSize);
}
private Rect BevelRect(Size controlSize)
{
var targetRect = new Rect(0, 0, controlSize.Width, controlSize.Height);
return targetRect.Deflate(BorderThickness);
}
private Rect TextRect(Size controlSize)
{
var bevelRect = BevelRect(controlSize);
var offsetX = bevelRect.Left + Padding.Left;
if (Icon is not null) {
offsetX += _tagIconSize + _paddingXXS;
}
// 这个时候已经算好了
var textSize = _textBlock!.DesiredSize;
var offsetY = bevelRect.Y + (bevelRect.Height - textSize.Height) / 2;
return new Rect(offsetX, offsetY, textSize.Width, textSize.Height);
}
private void SetupTagColorInfo(string colorStr)
{
_isPresetColorTag = false;
_hasColorSet = false;
colorStr = colorStr.Trim().ToLower();
Background = _defaultBackground;
foreach (var entry in _presetColorMap) {
if (entry.Key.ToString().ToLower() == colorStr) {
var colorInfo = _presetColorMap[entry.Key];
Foreground = new SolidColorBrush(colorInfo.TextColor);
BorderBrush = new SolidColorBrush(colorInfo.LightBorderColor);
Background = new SolidColorBrush(colorInfo.LightColor);
_isPresetColorTag = true;
return;
}
}
foreach (var entry in _statusColorMap) {
if (entry.Key.ToString().ToLower() == colorStr) {
var colorInfo = _statusColorMap[entry.Key];
Foreground = new SolidColorBrush(colorInfo.Color);
BorderBrush = new SolidColorBrush(colorInfo.BorderColor);
Background = new SolidColorBrush(colorInfo.Background);
_isPresetColorTag = true;
return;
}
}
if (Color.TryParse(colorStr, out Color color)) {
Bordered = false;
Foreground = _colorTextLightSolid;
_hasColorSet = true;
Background = new SolidColorBrush(color);
}
}
private void SetupTagClosable()
{
if (Closable) {
if (CloseIcon is null) {
CloseIcon = new PathIcon()
{
Kind = "CloseOutlined",
Width = _tagCloseIconSize,
Height = _tagCloseIconSize,
};
if (_hasColorSet && !_isPresetColorTag) {
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorTextLightSolid);
} else {
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorIcon);
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorIconHover);
}
} else {
CloseIcon.Width = _tagCloseIconSize;
CloseIcon.Height = _tagCloseIconSize;
if (CloseIcon.ThemeType != IconThemeType.TwoTone) {
if (_hasColorSet && !_isPresetColorTag) {
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorTextLightSolid);
} else {
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorIcon);
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorIconHover);
}
}
}
CloseIcon.Cursor = new Cursor(StandardCursorType.Hand);
_layoutPanel?.Children.Add(CloseIcon);
} else {
if (CloseIcon != null) {
_layoutPanel?.Children.Remove(CloseIcon);
CloseIcon = null;
}
}
}
private void SetupTagIcon()
{
if (Icon is not null) {
Icon.Width = _tagIconSize;
Icon.Height = _tagIconSize;
_layoutPanel?.Children.Insert(0, Icon);
if (_hasColorSet) {
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorTextLightSolid);
} else if (_isPresetColorTag) {
Icon.NormalFilledBrush = Foreground;
}
}
}
}

View File

@ -6,10 +6,12 @@ using AtomUI.Data;
using AtomUI.MotionScene;
using AtomUI.Reflection;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.LogicalTree;
@ -22,12 +24,18 @@ namespace AtomUI.Controls;
using AvaloniaWin = Avalonia.Controls.Window;
[PseudoClasses(":open")]
public partial class ToolTip : StyledControl, IShadowMaskInfoProvider
public partial class ToolTip : StyledControl,
IShadowMaskInfoProvider,
IControlCustomStyle
{
private Popup? _popup;
private Action<IPopupHost?>? _popupHostChangedHandler;
private AvaloniaWin? _currentAnchorWindow;
private PopupPositionInfo? _popupPositionInfo; // 这个信息在隐藏动画的时候会用到
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private ArrowDecoratedBox? _arrowDecoratedBox;
// 当鼠标移走了,但是打开动画还没完成,我们需要记录下来这个信号
internal bool RequestCloseWhereAnimationCompleted { get; set; } = false;
@ -660,4 +668,101 @@ public partial class ToolTip : StyledControl, IShadowMaskInfoProvider
_initialized = true;
}
}
#region IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
Background = new SolidColorBrush(Colors.Transparent);
_arrowDecoratedBox = new ArrowDecoratedBox();
if (Content is string text) {
_arrowDecoratedBox.Child = new TextBlock
{
Text = text,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
TextWrapping = TextWrapping.Wrap,
};
} else if (Content is Control control) {
_arrowDecoratedBox.Child = control;
}
((ISetLogicalParent)_arrowDecoratedBox).SetParent(this);
VisualChildren.Add(_arrowDecoratedBox);
_customStyle.ApplyFixedStyleConfig();
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
if (_arrowDecoratedBox is not null) {
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, FontSizeProperty, GlobalResourceKey.FontSize);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, MaxWidthProperty, ToolTipResourceKey.ToolTipMaxWidth);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, BackgroundProperty, ToolTipResourceKey.ToolTipBackground);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, ForegroundProperty, ToolTipResourceKey.ToolTipColor);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, MinHeightProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, PaddingProperty, ToolTipResourceKey.ToolTipPadding);
_controlTokenBinder.AddControlBinding(MarginXXSTokenProperty, GlobalResourceKey.MarginXXS);
_controlTokenBinder.AddControlBinding(MotionDurationTokenProperty, GlobalResourceKey.MotionDurationMid);
_controlTokenBinder.AddControlBinding(ShadowsTokenProperty, GlobalResourceKey.BoxShadowsSecondary);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, ArrowDecoratedBox.CornerRadiusProperty, ToolTipResourceKey.BorderRadiusOuter);
// TODO 生命周期一样还需要管理起来吗?
BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, _arrowDecoratedBox, ArrowDecoratedBox.IsShowArrowProperty);
}
}
public CornerRadius GetMaskCornerRadius()
{
return _arrowDecoratedBox!.GetMaskCornerRadius();
}
public Rect GetMaskBounds()
{
return _arrowDecoratedBox!.GetMaskBounds();
}
private void SetupArrowPosition(PlacementMode placement, PopupAnchor? anchor = null, PopupGravity? gravity = null)
{
var arrowPosition = PopupUtils.CalculateArrowPosition(placement, anchor, gravity);
if (_arrowDecoratedBox is not null && arrowPosition is not null) {
_arrowDecoratedBox.ArrowPosition = arrowPosition.Value;
}
}
private void SetupPointCenterOffset(Control placementTarget, PlacementMode placement, PopupAnchor? anchor = null, PopupGravity? gravity = null)
{
var offset =
CalculatePopupPositionDelta(placementTarget, placement, anchor, gravity);
_popup!.HorizontalOffset += offset.X;
_popup.VerticalOffset += offset.Y;
}
private Point CalculatePopupPositionDelta(Control anchorTarget, PlacementMode placement, PopupAnchor? anchor = null,
PopupGravity? gravity = null)
{
var offsetX = 0d;
var offsetY = 0d;
if (GetIsShowArrow(anchorTarget) && GetIsPointAtCenter(anchorTarget)) {
if (PopupUtils.CanEnabledArrow(placement, anchor, gravity)) {
var arrowVertexPoint = _arrowDecoratedBox!.ArrowVertexPoint;
var anchorSize = anchorTarget.Bounds.Size;
var centerX = anchorSize.Width / 2;
var centerY = anchorSize.Height / 2;
// 这里计算不需要全局坐标
if (placement == PlacementMode.TopEdgeAlignedLeft ||
placement == PlacementMode.BottomEdgeAlignedLeft) {
offsetX += centerX - arrowVertexPoint.Item1;
} else if (placement == PlacementMode.TopEdgeAlignedRight ||
placement == PlacementMode.BottomEdgeAlignedRight) {
offsetX -= centerX - arrowVertexPoint.Item2;
} else if (placement == PlacementMode.RightEdgeAlignedTop ||
placement == PlacementMode.LeftEdgeAlignedTop) {
offsetY += centerY - arrowVertexPoint.Item1;
} else if (placement == PlacementMode.RightEdgeAlignedBottom ||
placement == PlacementMode.LeftEdgeAlignedBottom) {
offsetY -= centerY - arrowVertexPoint.Item2;
}
}
}
return new Point(offsetX, offsetY);
}
#endregion
}

View File

@ -1,113 +0,0 @@
using AtomUI.Data;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Layout;
using Avalonia.Media;
namespace AtomUI.Controls;
public partial class ToolTip : IControlCustomStyle
{
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private ArrowDecoratedBox? _arrowDecoratedBox;
void IControlCustomStyle.SetupUi()
{
Background = new SolidColorBrush(Colors.Transparent);
_arrowDecoratedBox = new ArrowDecoratedBox();
if (Content is string text) {
_arrowDecoratedBox.Child = new TextBlock
{
Text = text,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
TextWrapping = TextWrapping.Wrap,
};
} else if (Content is Control control) {
_arrowDecoratedBox.Child = control;
}
((ISetLogicalParent)_arrowDecoratedBox).SetParent(this);
VisualChildren.Add(_arrowDecoratedBox);
_customStyle.ApplyFixedStyleConfig();
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
if (_arrowDecoratedBox is not null) {
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, FontSizeProperty, GlobalResourceKey.FontSize);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, MaxWidthProperty, ToolTipResourceKey.ToolTipMaxWidth);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, BackgroundProperty, ToolTipResourceKey.ToolTipBackground);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, ForegroundProperty, ToolTipResourceKey.ToolTipColor);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, MinHeightProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, PaddingProperty, ToolTipResourceKey.ToolTipPadding);
_controlTokenBinder.AddControlBinding(MarginXXSTokenProperty, GlobalResourceKey.MarginXXS);
_controlTokenBinder.AddControlBinding(MotionDurationTokenProperty, GlobalResourceKey.MotionDurationMid);
_controlTokenBinder.AddControlBinding(ShadowsTokenProperty, GlobalResourceKey.BoxShadowsSecondary);
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, ArrowDecoratedBox.CornerRadiusProperty, ToolTipResourceKey.BorderRadiusOuter);
// TODO 生命周期一样还需要管理起来吗?
BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, _arrowDecoratedBox, ArrowDecoratedBox.IsShowArrowProperty);
}
}
public CornerRadius GetMaskCornerRadius()
{
return _arrowDecoratedBox!.GetMaskCornerRadius();
}
public Rect GetMaskBounds()
{
return _arrowDecoratedBox!.GetMaskBounds();
}
private void SetupArrowPosition(PlacementMode placement, PopupAnchor? anchor = null, PopupGravity? gravity = null)
{
var arrowPosition = PopupUtils.CalculateArrowPosition(placement, anchor, gravity);
if (_arrowDecoratedBox is not null && arrowPosition is not null) {
_arrowDecoratedBox.ArrowPosition = arrowPosition.Value;
}
}
private void SetupPointCenterOffset(Control placementTarget, PlacementMode placement, PopupAnchor? anchor = null, PopupGravity? gravity = null)
{
var offset =
CalculatePopupPositionDelta(placementTarget, placement, anchor, gravity);
_popup!.HorizontalOffset += offset.X;
_popup.VerticalOffset += offset.Y;
}
private Point CalculatePopupPositionDelta(Control anchorTarget, PlacementMode placement, PopupAnchor? anchor = null,
PopupGravity? gravity = null)
{
var offsetX = 0d;
var offsetY = 0d;
if (GetIsShowArrow(anchorTarget) && GetIsPointAtCenter(anchorTarget)) {
if (PopupUtils.CanEnabledArrow(placement, anchor, gravity)) {
var arrowVertexPoint = _arrowDecoratedBox!.ArrowVertexPoint;
var anchorSize = anchorTarget.Bounds.Size;
var centerX = anchorSize.Width / 2;
var centerY = anchorSize.Height / 2;
// 这里计算不需要全局坐标
if (placement == PlacementMode.TopEdgeAlignedLeft ||
placement == PlacementMode.BottomEdgeAlignedLeft) {
offsetX += centerX - arrowVertexPoint.Item1;
} else if (placement == PlacementMode.TopEdgeAlignedRight ||
placement == PlacementMode.BottomEdgeAlignedRight) {
offsetX -= centerX - arrowVertexPoint.Item2;
} else if (placement == PlacementMode.RightEdgeAlignedTop ||
placement == PlacementMode.LeftEdgeAlignedTop) {
offsetY += centerY - arrowVertexPoint.Item1;
} else if (placement == PlacementMode.RightEdgeAlignedBottom ||
placement == PlacementMode.LeftEdgeAlignedBottom) {
offsetY -= centerY - arrowVertexPoint.Item2;
}
}
}
return new Point(offsetX, offsetY);
}
}