mirror of
https://gitee.com/chinware/atomui.git
synced 2024-11-30 02:47:45 +08:00
重构组件
This commit is contained in:
parent
b6f9bbf6d7
commit
a0a778e5cb
@ -1,7 +1,12 @@
|
|||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
|
using AtomUI.Styling;
|
||||||
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.Media;
|
||||||
using Avalonia.Metadata;
|
using Avalonia.Metadata;
|
||||||
|
|
||||||
namespace AtomUI.Controls;
|
namespace AtomUI.Controls;
|
||||||
@ -14,7 +19,7 @@ public enum AlertType
|
|||||||
Error
|
Error
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Alert : BorderedStyleControl
|
public class Alert : BorderedStyleControl, IControlCustomStyle
|
||||||
{
|
{
|
||||||
public static readonly StyledProperty<AlertType> TypeProperty =
|
public static readonly StyledProperty<AlertType> TypeProperty =
|
||||||
AvaloniaProperty.Register<Alert, AlertType>(nameof(AlertType));
|
AvaloniaProperty.Register<Alert, AlertType>(nameof(AlertType));
|
||||||
@ -90,6 +95,18 @@ public partial class Alert : BorderedStyleControl
|
|||||||
set => SetValue(ExtraActionProperty, value);
|
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()
|
static Alert()
|
||||||
{
|
{
|
||||||
AffectsMeasure<Segmented>(IsClosableProperty,
|
AffectsMeasure<Segmented>(IsClosableProperty,
|
||||||
@ -130,4 +147,278 @@ public partial class Alert : BorderedStyleControl
|
|||||||
base.OnAttachedToVisualTree(e);
|
base.OnAttachedToVisualTree(e);
|
||||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
_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
|
||||||
}
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.Media;
|
||||||
using Avalonia.Metadata;
|
using Avalonia.Metadata;
|
||||||
|
|
||||||
namespace AtomUI.Controls;
|
namespace AtomUI.Controls;
|
||||||
@ -70,7 +77,9 @@ public enum ArrowPosition
|
|||||||
RightEdgeAlignedBottom
|
RightEdgeAlignedBottom
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
|
public partial class ArrowDecoratedBox : StyledControl,
|
||||||
|
IShadowMaskInfoProvider,
|
||||||
|
IControlCustomStyle
|
||||||
{
|
{
|
||||||
public static readonly StyledProperty<bool> IsShowArrowProperty =
|
public static readonly StyledProperty<bool> IsShowArrowProperty =
|
||||||
AvaloniaProperty.Register<ArrowDecoratedBox, bool>(nameof(IsShowArrow), true);
|
AvaloniaProperty.Register<ArrowDecoratedBox, bool>(nameof(IsShowArrow), true);
|
||||||
@ -133,6 +142,16 @@ public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
|
|||||||
set => SetValue(ChildProperty, value);
|
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()
|
static ArrowDecoratedBox()
|
||||||
{
|
{
|
||||||
AffectsMeasure<ArrowDecoratedBox>(ArrowPositionProperty, IsShowArrowProperty);
|
AffectsMeasure<ArrowDecoratedBox>(ArrowPositionProperty, IsShowArrowProperty);
|
||||||
@ -206,4 +225,282 @@ public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
|
|||||||
{
|
{
|
||||||
return GetContentRect(DesiredSize).Deflate(0.5);
|
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
|
||||||
}
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,17 @@
|
|||||||
|
using AtomUI.Controls.Utils;
|
||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
using AtomUI.TokenSystem;
|
using AtomUI.Icon;
|
||||||
|
using AtomUI.Media;
|
||||||
|
using AtomUI.Styling;
|
||||||
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
namespace AtomUI.Controls;
|
namespace AtomUI.Controls;
|
||||||
|
|
||||||
@ -23,7 +33,10 @@ public enum ButtonShape
|
|||||||
Round,
|
Round,
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Button : AvaloniaButton, ISizeTypeAware
|
public partial class Button : AvaloniaButton,
|
||||||
|
ISizeTypeAware,
|
||||||
|
IControlCustomStyle,
|
||||||
|
IWaveAdornerInfoProvider
|
||||||
{
|
{
|
||||||
#region 公共属性定义
|
#region 公共属性定义
|
||||||
public static readonly StyledProperty<ButtonType> ButtonTypeProperty =
|
public static readonly StyledProperty<ButtonType> ButtonTypeProperty =
|
||||||
@ -90,6 +103,13 @@ public partial class Button : AvaloniaButton, ISizeTypeAware
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private ControlStyleState _styleState;
|
||||||
|
private ControlTokenBinder _controlTokenBinder;
|
||||||
|
private IControlCustomStyle _customStyle;
|
||||||
|
private StackPanel? _stackPanel;
|
||||||
|
private Label? _label;
|
||||||
|
private bool _initialized = false;
|
||||||
|
|
||||||
static Button()
|
static Button()
|
||||||
{
|
{
|
||||||
@ -144,4 +164,566 @@ public partial class Button : AvaloniaButton, ISizeTypeAware
|
|||||||
base.OnAttachedToVisualTree(e);
|
base.OnAttachedToVisualTree(e);
|
||||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
_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
|
||||||
}
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,13 @@
|
|||||||
|
using AtomUI.Controls.Utils;
|
||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
using AtomUI.Media;
|
using AtomUI.Media;
|
||||||
using AtomUI.Styling;
|
using AtomUI.Styling;
|
||||||
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
|
using Avalonia.Animation.Easings;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
@ -11,14 +17,44 @@ namespace AtomUI.Controls;
|
|||||||
|
|
||||||
using AvaloniaCheckBox = Avalonia.Controls.CheckBox;
|
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()
|
public CheckBox()
|
||||||
{
|
{
|
||||||
_controlTokenBinder = new ControlTokenBinder(this, CheckBoxToken.ID);
|
_controlTokenBinder = new ControlTokenBinder(this, CheckBoxToken.ID);
|
||||||
_customStyle = this;
|
_customStyle = this;
|
||||||
|
_borderRenderHelper = new BorderRenderHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnAttachedToVisualTree(e);
|
||||||
|
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnAttachedToLogicalTree(e);
|
base.OnAttachedToLogicalTree(e);
|
||||||
@ -55,19 +91,29 @@ public partial class CheckBox : AvaloniaCheckBox, ICustomHitTest
|
|||||||
layoutable.Arrange(arrangeRect);
|
layoutable.Arrange(arrangeRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalSize;
|
return finalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed override void Render(DrawingContext context)
|
public sealed override void Render(DrawingContext context)
|
||||||
{
|
{
|
||||||
var indicatorRect = IndicatorRect();
|
var indicatorRect = IndicatorRect();
|
||||||
var penWidth = IndicatorBorderThickness.Top;
|
var penWidth = IndicatorBorderThickness.Top;
|
||||||
PenUtils.TryModifyOrCreate(ref _cachedPen, IndicatorBorderBrush, penWidth);
|
|
||||||
var borderRadius = GeometryUtils.CornerRadiusScalarValue(IndicatorBorderRadius);
|
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)) {
|
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 checkMarkPenWidth = 2;
|
||||||
var checkMarkPen = new Pen(IndicatorCheckedMarkBrush, 2);
|
var checkMarkPen = new Pen(IndicatorCheckedMarkBrush, 2);
|
||||||
var checkMarkBounds = checkMarkGeometry.GetRenderBounds(checkMarkPen);
|
var checkMarkBounds = checkMarkGeometry.GetRenderBounds(checkMarkPen);
|
||||||
@ -84,9 +130,161 @@ public partial class CheckBox : AvaloniaCheckBox, ICustomHitTest
|
|||||||
context.FillRectangle(IndicatorTristateMarkBrush!, indicatorTristateRect);
|
context.FillRectangle(IndicatorTristateMarkBrush!, indicatorTristateRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HitTest(Point point)
|
public bool HitTest(Point point)
|
||||||
{
|
{
|
||||||
return true;
|
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
|
||||||
}
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
81
src/AtomUI.Controls/EmptyIndicator/BuiltInImageBuilder.cs
Normal file
81
src/AtomUI.Controls/EmptyIndicator/BuiltInImageBuilder.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
8
src/AtomUI.Controls/EmptyIndicator/EmptyIndicator.cs
Normal file
8
src/AtomUI.Controls/EmptyIndicator/EmptyIndicator.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace AtomUI.Controls;
|
||||||
|
|
||||||
|
public class EmptyIndicator : Control
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace AtomUI.Controls;
|
||||||
|
|
||||||
|
|
||||||
|
public class EmptyIndicatorToken
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -1,13 +1,34 @@
|
|||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
|
using AtomUI.Styling;
|
||||||
using AtomUI.TokenSystem;
|
using AtomUI.TokenSystem;
|
||||||
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
|
||||||
namespace AtomUI.Controls;
|
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 =
|
public static readonly DirectProperty<MarqueeLabel, double> CycleSpaceProperty =
|
||||||
AvaloniaProperty.RegisterDirect<MarqueeLabel, double>(nameof(CycleSpace),
|
AvaloniaProperty.RegisterDirect<MarqueeLabel, double>(nameof(CycleSpace),
|
||||||
o => o.CycleSpace,
|
o => o.CycleSpace,
|
||||||
@ -76,4 +97,139 @@ public partial class MarqueeLabel : TextBlock
|
|||||||
return size;
|
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
|
||||||
}
|
}
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,14 @@
|
|||||||
|
using AtomUI.Controls.Utils;
|
||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
|
using AtomUI.Media;
|
||||||
|
using AtomUI.Styling;
|
||||||
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
|
||||||
namespace AtomUI.Controls;
|
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 =
|
public static readonly StyledProperty<ButtonSizeType> SizeTypeProperty =
|
||||||
AvaloniaProperty.Register<OptionButton, ButtonSizeType>(nameof(SizeType), ButtonSizeType.Middle);
|
AvaloniaProperty.Register<OptionButton, ButtonSizeType>(nameof(SizeType), ButtonSizeType.Middle);
|
||||||
|
|
||||||
@ -135,5 +155,355 @@ public partial class OptionButton : AvaloniaRadioButton, ISizeTypeAware
|
|||||||
base.OnAttachedToVisualTree(e);
|
base.OnAttachedToVisualTree(e);
|
||||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
_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
|
||||||
|
|
||||||
}
|
}
|
@ -1,10 +1,15 @@
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using AtomUI.Controls.Utils;
|
||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
using AtomUI.TokenSystem;
|
using AtomUI.Styling;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.Media;
|
||||||
using Avalonia.Metadata;
|
using Avalonia.Metadata;
|
||||||
|
|
||||||
namespace AtomUI.Controls;
|
namespace AtomUI.Controls;
|
||||||
@ -12,7 +17,9 @@ namespace AtomUI.Controls;
|
|||||||
using ButtonSizeType = SizeType;
|
using ButtonSizeType = SizeType;
|
||||||
using OptionButtons = AvaloniaList<OptionButton>;
|
using OptionButtons = AvaloniaList<OptionButton>;
|
||||||
|
|
||||||
public partial class OptionButtonGroup : StyledControl, ISizeTypeAware
|
public partial class OptionButtonGroup : StyledControl,
|
||||||
|
ISizeTypeAware,
|
||||||
|
IControlCustomStyle
|
||||||
{
|
{
|
||||||
public static readonly StyledProperty<ButtonSizeType> SizeTypeProperty =
|
public static readonly StyledProperty<ButtonSizeType> SizeTypeProperty =
|
||||||
AvaloniaProperty.Register<OptionButtonGroup, ButtonSizeType>(nameof(SizeType), ButtonSizeType.Middle);
|
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();
|
[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()
|
static OptionButtonGroup()
|
||||||
{
|
{
|
||||||
@ -204,4 +218,164 @@ public partial class OptionButtonGroup : StyledControl, ISizeTypeAware
|
|||||||
base.OnAttachedToVisualTree(e);
|
base.OnAttachedToVisualTree(e);
|
||||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
_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
|
||||||
}
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,11 @@
|
|||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
|
using AtomUI.Icon;
|
||||||
|
using AtomUI.Media;
|
||||||
|
using AtomUI.Styling;
|
||||||
using AtomUI.Utils;
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
|
using Avalonia.Animation.Easings;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
@ -17,7 +22,9 @@ public enum ProgressStatus
|
|||||||
Active,
|
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 LARGE_STROKE_THICKNESS = 8;
|
||||||
protected const double MIDDLE_STROKE_THICKNESS = 6;
|
protected const double MIDDLE_STROKE_THICKNESS = 6;
|
||||||
@ -190,6 +197,14 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
|
|||||||
get => _strokeThickness;
|
get => _strokeThickness;
|
||||||
set => SetAndRaise(StrokeThicknessProperty, ref _strokeThickness, value);
|
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()
|
static AbstractProgressBar()
|
||||||
{
|
{
|
||||||
@ -297,4 +312,153 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void NotifyHandleExtraInfoVisibility() { }
|
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
|
||||||
}
|
}
|
@ -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) {}
|
|
||||||
}
|
|
@ -1,8 +1,14 @@
|
|||||||
|
using AtomUI.Controls.Utils;
|
||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
using AtomUI.Media;
|
using AtomUI.Media;
|
||||||
|
using AtomUI.Styling;
|
||||||
using AtomUI.TokenSystem;
|
using AtomUI.TokenSystem;
|
||||||
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
@ -12,8 +18,17 @@ namespace AtomUI.Controls;
|
|||||||
|
|
||||||
using AvaloniaRadioButton = Avalonia.Controls.RadioButton;
|
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()
|
static RadioButton()
|
||||||
{
|
{
|
||||||
AffectsRender<RadioButton>(
|
AffectsRender<RadioButton>(
|
||||||
@ -38,6 +53,12 @@ public partial class RadioButton : AvaloniaRadioButton, ICustomHitTest
|
|||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnAttachedToVisualTree(e);
|
||||||
|
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
@ -87,4 +108,136 @@ public partial class RadioButton : AvaloniaRadioButton, ICustomHitTest
|
|||||||
{
|
{
|
||||||
return true;
|
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
|
||||||
}
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,20 @@
|
|||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
|
using AtomUI.Icon;
|
||||||
|
using AtomUI.Media;
|
||||||
|
using AtomUI.Styling;
|
||||||
using AtomUI.TokenSystem;
|
using AtomUI.TokenSystem;
|
||||||
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Metadata;
|
using Avalonia.Metadata;
|
||||||
|
|
||||||
namespace AtomUI.Controls;
|
namespace AtomUI.Controls;
|
||||||
|
|
||||||
public partial class SegmentedItem : StyledControl
|
public partial class SegmentedItem : StyledControl, IControlCustomStyle
|
||||||
{
|
{
|
||||||
public static readonly StyledProperty<SizeType> SizeTypeProperty =
|
public static readonly StyledProperty<SizeType> SizeTypeProperty =
|
||||||
AvaloniaProperty.Register<SegmentedItem, SizeType>(nameof(SizeType), SizeType.Middle);
|
AvaloniaProperty.Register<SegmentedItem, SizeType>(nameof(SizeType), SizeType.Middle);
|
||||||
@ -17,14 +24,14 @@ public partial class SegmentedItem : StyledControl
|
|||||||
|
|
||||||
public static readonly StyledProperty<PathIcon?> IconProperty
|
public static readonly StyledProperty<PathIcon?> IconProperty
|
||||||
= AvaloniaProperty.Register<SegmentedItem, PathIcon?>(nameof(Icon));
|
= AvaloniaProperty.Register<SegmentedItem, PathIcon?>(nameof(Icon));
|
||||||
|
|
||||||
public static readonly DirectProperty<SegmentedItem, bool> IsPressedProperty =
|
public static readonly DirectProperty<SegmentedItem, bool> IsPressedProperty =
|
||||||
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsPressed), o => o.IsPressed);
|
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsPressed), o => o.IsPressed);
|
||||||
|
|
||||||
public static readonly DirectProperty<SegmentedItem, bool> IsCurrentItemProperty =
|
public static readonly DirectProperty<SegmentedItem, bool> IsCurrentItemProperty =
|
||||||
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsCurrentItem),
|
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsCurrentItem),
|
||||||
o => o.IsCurrentItem,
|
o => o.IsCurrentItem,
|
||||||
(o, v) => o.IsCurrentItem = v);
|
(o, v) => o.IsCurrentItem = v);
|
||||||
|
|
||||||
internal SizeType SizeType
|
internal SizeType SizeType
|
||||||
{
|
{
|
||||||
@ -53,10 +60,22 @@ public partial class SegmentedItem : StyledControl
|
|||||||
get => _isPressed;
|
get => _isPressed;
|
||||||
private set => SetAndRaise(IsPressedProperty, ref _isPressed, value);
|
private set => SetAndRaise(IsPressedProperty, ref _isPressed, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 内部属性
|
// 内部属性
|
||||||
private bool _isCurrentItem = false;
|
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()
|
static SegmentedItem()
|
||||||
{
|
{
|
||||||
@ -82,6 +101,7 @@ public partial class SegmentedItem : StyledControl
|
|||||||
targetWidth += _paddingXXS;
|
targetWidth += _paddingXXS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Size(targetWidth, targetHeight);
|
return new Size(targetWidth, targetHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,10 +113,11 @@ public partial class SegmentedItem : StyledControl
|
|||||||
if (Text.Length == 0) {
|
if (Text.Length == 0) {
|
||||||
offsetX += (DesiredSize.Width - Icon.Width) / 2;
|
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;
|
offsetX += Icon.DesiredSize.Width + _paddingXXS;
|
||||||
}
|
}
|
||||||
|
|
||||||
_label!.Arrange(new Rect(new Point(offsetX, -1), _label.DesiredSize));
|
_label!.Arrange(new Rect(new Point(offsetX, -1), _label.DesiredSize));
|
||||||
return finalSize;
|
return finalSize;
|
||||||
}
|
}
|
||||||
@ -132,4 +153,123 @@ public partial class SegmentedItem : StyledControl
|
|||||||
IsPressed = false;
|
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
|
||||||
}
|
}
|
@ -1,8 +1,12 @@
|
|||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
|
using AtomUI.Media;
|
||||||
|
using AtomUI.Styling;
|
||||||
using AtomUI.TokenSystem;
|
using AtomUI.TokenSystem;
|
||||||
using AtomUI.Utils;
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Rendering;
|
using Avalonia.Rendering;
|
||||||
@ -12,7 +16,9 @@ namespace AtomUI.Controls;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在内部维护一些额外信息的控件,用户无感知
|
/// 在内部维护一些额外信息的控件,用户无感知
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal partial class SegmentedItemBox : BorderedStyleControl, ICustomHitTest
|
internal partial class SegmentedItemBox : BorderedStyleControl,
|
||||||
|
ICustomHitTest,
|
||||||
|
IControlCustomStyle
|
||||||
{
|
{
|
||||||
internal Control Item { get; }
|
internal Control Item { get; }
|
||||||
|
|
||||||
@ -44,7 +50,12 @@ internal partial class SegmentedItemBox : BorderedStyleControl, ICustomHitTest
|
|||||||
|
|
||||||
private bool _isCurrentItem;
|
private bool _isCurrentItem;
|
||||||
internal bool IsCurrentItem { get => _isCurrentItem; set => SetCurrentItem(value); }
|
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;
|
public int LastItem { get; set; } = -1;
|
||||||
|
|
||||||
static SegmentedItemBox()
|
static SegmentedItemBox()
|
||||||
@ -153,4 +164,100 @@ internal partial class SegmentedItemBox : BorderedStyleControl, ICustomHitTest
|
|||||||
{
|
{
|
||||||
return true;
|
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
|
||||||
}
|
}
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,14 @@
|
|||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
|
using AtomUI.Media;
|
||||||
|
using AtomUI.Styling;
|
||||||
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Metadata;
|
using Avalonia.Metadata;
|
||||||
|
|
||||||
namespace AtomUI.Controls;
|
namespace AtomUI.Controls;
|
||||||
@ -14,7 +20,7 @@ public enum SeparatorTitlePosition
|
|||||||
Center
|
Center
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Separator : StyledControl
|
public partial class Separator : StyledControl, IControlCustomStyle
|
||||||
{
|
{
|
||||||
public static readonly StyledProperty<string?> TitleProperty =
|
public static readonly StyledProperty<string?> TitleProperty =
|
||||||
AvaloniaProperty.Register<Separator, string?>(nameof(Title));
|
AvaloniaProperty.Register<Separator, string?>(nameof(Title));
|
||||||
@ -25,19 +31,19 @@ public partial class Separator : StyledControl
|
|||||||
|
|
||||||
public static readonly StyledProperty<IBrush?> TitleColorProperty =
|
public static readonly StyledProperty<IBrush?> TitleColorProperty =
|
||||||
AvaloniaProperty.Register<Separator, IBrush?>(nameof(TitleColor));
|
AvaloniaProperty.Register<Separator, IBrush?>(nameof(TitleColor));
|
||||||
|
|
||||||
public static readonly StyledProperty<IBrush?> LineColorProperty =
|
public static readonly StyledProperty<IBrush?> LineColorProperty =
|
||||||
AvaloniaProperty.Register<Separator, IBrush?>(nameof(LineColor));
|
AvaloniaProperty.Register<Separator, IBrush?>(nameof(LineColor));
|
||||||
|
|
||||||
public static readonly StyledProperty<Orientation> OrientationProperty =
|
public static readonly StyledProperty<Orientation> OrientationProperty =
|
||||||
AvaloniaProperty.Register<Separator, Orientation>(nameof(Orientation), Orientation.Horizontal);
|
AvaloniaProperty.Register<Separator, Orientation>(nameof(Orientation), Orientation.Horizontal);
|
||||||
|
|
||||||
public static readonly StyledProperty<double> OrientationMarginProperty =
|
public static readonly StyledProperty<double> OrientationMarginProperty =
|
||||||
AvaloniaProperty.Register<Separator, double>(nameof(OrientationMargin), double.NaN);
|
AvaloniaProperty.Register<Separator, double>(nameof(OrientationMargin), double.NaN);
|
||||||
|
|
||||||
public static readonly StyledProperty<bool> IsDashedLineProperty =
|
public static readonly StyledProperty<bool> IsDashedLineProperty =
|
||||||
AvaloniaProperty.Register<Separator, bool>(nameof(Orientation), false);
|
AvaloniaProperty.Register<Separator, bool>(nameof(Orientation), false);
|
||||||
|
|
||||||
public static readonly StyledProperty<double> LineWidthProperty =
|
public static readonly StyledProperty<double> LineWidthProperty =
|
||||||
AvaloniaProperty.Register<Separator, double>(nameof(LineWidth), 1);
|
AvaloniaProperty.Register<Separator, double>(nameof(LineWidth), 1);
|
||||||
|
|
||||||
@ -68,7 +74,7 @@ public partial class Separator : StyledControl
|
|||||||
get => GetValue(TitleColorProperty);
|
get => GetValue(TitleColorProperty);
|
||||||
set => SetValue(TitleColorProperty, value);
|
set => SetValue(TitleColorProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分割线标题的颜色
|
/// 分割线标题的颜色
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -77,7 +83,7 @@ public partial class Separator : StyledControl
|
|||||||
get => GetValue(LineColorProperty);
|
get => GetValue(LineColorProperty);
|
||||||
set => SetValue(LineColorProperty, value);
|
set => SetValue(LineColorProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分割线的方向,垂直和水平分割线
|
/// 分割线的方向,垂直和水平分割线
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -97,7 +103,7 @@ public partial class Separator : StyledControl
|
|||||||
get => GetValue(OrientationMarginProperty);
|
get => GetValue(OrientationMarginProperty);
|
||||||
set => SetValue(OrientationMarginProperty, value);
|
set => SetValue(OrientationMarginProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否为虚线
|
/// 是否为虚线
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -115,25 +121,33 @@ public partial class Separator : StyledControl
|
|||||||
get => GetValue(LineWidthProperty);
|
get => GetValue(LineWidthProperty);
|
||||||
set => SetValue(LineWidthProperty, value);
|
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()
|
static Separator()
|
||||||
{
|
{
|
||||||
AffectsMeasure<Separator>(OrientationProperty,
|
AffectsMeasure<Separator>(OrientationProperty,
|
||||||
LineWidthProperty,
|
LineWidthProperty,
|
||||||
TitleProperty);
|
TitleProperty);
|
||||||
AffectsArrange<Separator>(TitlePositionProperty);
|
AffectsArrange<Separator>(TitlePositionProperty);
|
||||||
AffectsRender<Separator>(TitleColorProperty,
|
AffectsRender<Separator>(TitleColorProperty,
|
||||||
LineColorProperty,
|
LineColorProperty,
|
||||||
IsDashedLineProperty);
|
IsDashedLineProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Separator()
|
public Separator()
|
||||||
{
|
{
|
||||||
_controlTokenBinder = new ControlTokenBinder(this, SeparatorToken.ID);
|
_controlTokenBinder = new ControlTokenBinder(this, SeparatorToken.ID);
|
||||||
_customStyle = this;
|
_customStyle = this;
|
||||||
_customStyle.InitOnConstruct();
|
_customStyle.InitOnConstruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnAttachedToLogicalTree(e);
|
base.OnAttachedToLogicalTree(e);
|
||||||
@ -142,18 +156,234 @@ public partial class Separator : StyledControl
|
|||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnPropertyChanged(e);
|
base.OnPropertyChanged(e);
|
||||||
_customStyle.HandlePropertyChangedForStyle(e);
|
_customStyle.HandlePropertyChangedForStyle(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnAttachedToVisualTree(e);
|
base.OnAttachedToVisualTree(e);
|
||||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
_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
|
public class VerticalSeparator : Separator
|
||||||
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,19 @@
|
|||||||
using AtomUI.ColorSystem;
|
using AtomUI.ColorSystem;
|
||||||
using AtomUI.Data;
|
using AtomUI.Data;
|
||||||
using AtomUI.Icon;
|
using AtomUI.Icon;
|
||||||
|
using AtomUI.Styling;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
namespace AtomUI.Controls;
|
namespace AtomUI.Controls;
|
||||||
|
|
||||||
public enum TagStatus {
|
public enum TagStatus
|
||||||
|
{
|
||||||
// 状态
|
// 状态
|
||||||
Success,
|
Success,
|
||||||
Info,
|
Info,
|
||||||
@ -16,25 +21,51 @@ public enum TagStatus {
|
|||||||
Warning
|
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()
|
static Tag()
|
||||||
{
|
{
|
||||||
_presetColorMap = new Dictionary<PresetColorType, TagCalcColor>();
|
_presetColorMap = new Dictionary<PresetColorType, TagCalcColor>();
|
||||||
_statusColorMap = new Dictionary<TagStatus, TagStatusCalcColor>();
|
_statusColorMap = new Dictionary<TagStatus, TagStatusCalcColor>();
|
||||||
AffectsMeasure<Tag>(BorderedProperty,
|
AffectsMeasure<Tag>(BorderedProperty,
|
||||||
IconProperty,
|
IconProperty,
|
||||||
ClosableProperty,
|
ClosableProperty,
|
||||||
PaddingXXSTokenProperty,
|
PaddingXXSTokenProperty,
|
||||||
TagCloseIconSizeTokenProperty,
|
TagCloseIconSizeTokenProperty,
|
||||||
TagIconSizeTokenProperty);
|
TagIconSizeTokenProperty);
|
||||||
AffectsRender<Tag>(ClosableProperty,
|
AffectsRender<Tag>(ClosableProperty,
|
||||||
DefaultBgTokenProperty,
|
DefaultBgTokenProperty,
|
||||||
DefaultForegroundTokenProperty,
|
DefaultForegroundTokenProperty,
|
||||||
TagBorderlessBgTokenProperty,
|
TagBorderlessBgTokenProperty,
|
||||||
ColorTextLightSolidTokenProperty);
|
ColorTextLightSolidTokenProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tag()
|
public Tag()
|
||||||
{
|
{
|
||||||
_customStyle = this;
|
_customStyle = this;
|
||||||
@ -43,7 +74,7 @@ public partial class Tag : Label
|
|||||||
SetupStatusColorMap();
|
SetupStatusColorMap();
|
||||||
_customStyle.InitOnConstruct();
|
_customStyle.InitOnConstruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnAttachedToLogicalTree(e);
|
base.OnAttachedToLogicalTree(e);
|
||||||
@ -52,13 +83,13 @@ public partial class Tag : Label
|
|||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnAttachedToVisualTree(e);
|
base.OnAttachedToVisualTree(e);
|
||||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Size MeasureOverride(Size availableSize)
|
protected override Size MeasureOverride(Size availableSize)
|
||||||
{
|
{
|
||||||
var textBlock = _textBlock!;
|
var textBlock = _textBlock!;
|
||||||
@ -95,18 +126,20 @@ public partial class Tag : Label
|
|||||||
if (Icon is not null) {
|
if (Icon is not null) {
|
||||||
Icon.Arrange(IconRect(finalSize));
|
Icon.Arrange(IconRect(finalSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CloseIcon is not null) {
|
if (CloseIcon is not null) {
|
||||||
CloseIcon.Arrange(CloseIconRect(finalSize));
|
CloseIcon.Arrange(CloseIconRect(finalSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalSize;
|
return finalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnPropertyChanged(e);
|
base.OnPropertyChanged(e);
|
||||||
_customStyle.HandlePropertyChangedForStyle(e);
|
_customStyle.HandlePropertyChangedForStyle(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPointerMoved(PointerEventArgs e)
|
protected override void OnPointerMoved(PointerEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnPointerMoved(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
|
||||||
}
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,10 +6,12 @@ using AtomUI.Data;
|
|||||||
using AtomUI.MotionScene;
|
using AtomUI.MotionScene;
|
||||||
using AtomUI.Reflection;
|
using AtomUI.Reflection;
|
||||||
using AtomUI.Styling;
|
using AtomUI.Styling;
|
||||||
|
using AtomUI.Utils;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Metadata;
|
using Avalonia.Controls.Metadata;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Controls.Primitives.PopupPositioning;
|
||||||
using Avalonia.Data;
|
using Avalonia.Data;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
@ -22,12 +24,18 @@ namespace AtomUI.Controls;
|
|||||||
using AvaloniaWin = Avalonia.Controls.Window;
|
using AvaloniaWin = Avalonia.Controls.Window;
|
||||||
|
|
||||||
[PseudoClasses(":open")]
|
[PseudoClasses(":open")]
|
||||||
public partial class ToolTip : StyledControl, IShadowMaskInfoProvider
|
public partial class ToolTip : StyledControl,
|
||||||
|
IShadowMaskInfoProvider,
|
||||||
|
IControlCustomStyle
|
||||||
{
|
{
|
||||||
private Popup? _popup;
|
private Popup? _popup;
|
||||||
private Action<IPopupHost?>? _popupHostChangedHandler;
|
private Action<IPopupHost?>? _popupHostChangedHandler;
|
||||||
private AvaloniaWin? _currentAnchorWindow;
|
private AvaloniaWin? _currentAnchorWindow;
|
||||||
private PopupPositionInfo? _popupPositionInfo; // 这个信息在隐藏动画的时候会用到
|
private PopupPositionInfo? _popupPositionInfo; // 这个信息在隐藏动画的时候会用到
|
||||||
|
private bool _initialized = false;
|
||||||
|
private IControlCustomStyle _customStyle;
|
||||||
|
private ControlTokenBinder _controlTokenBinder;
|
||||||
|
private ArrowDecoratedBox? _arrowDecoratedBox;
|
||||||
|
|
||||||
// 当鼠标移走了,但是打开动画还没完成,我们需要记录下来这个信号
|
// 当鼠标移走了,但是打开动画还没完成,我们需要记录下来这个信号
|
||||||
internal bool RequestCloseWhereAnimationCompleted { get; set; } = false;
|
internal bool RequestCloseWhereAnimationCompleted { get; set; } = false;
|
||||||
@ -660,4 +668,101 @@ public partial class ToolTip : StyledControl, IShadowMaskInfoProvider
|
|||||||
_initialized = true;
|
_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
|
||||||
}
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user