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.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Metadata;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
@ -14,7 +19,7 @@ public enum AlertType
|
||||
Error
|
||||
}
|
||||
|
||||
public partial class Alert : BorderedStyleControl
|
||||
public class Alert : BorderedStyleControl, IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<AlertType> TypeProperty =
|
||||
AvaloniaProperty.Register<Alert, AlertType>(nameof(AlertType));
|
||||
@ -90,6 +95,18 @@ public partial class Alert : BorderedStyleControl
|
||||
set => SetValue(ExtraActionProperty, value);
|
||||
}
|
||||
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private bool _initialized = false;
|
||||
private Grid? _mainLayout;
|
||||
private Label? _messageLabel;
|
||||
private MarqueeLabel? _messageMarqueeLabel;
|
||||
private Label? _descriptionLabel;
|
||||
private PathIcon? _icon;
|
||||
private IconButton? _closeButton;
|
||||
private StackPanel? _infoStack;
|
||||
private bool _scalingAwareConfigApplied = false;
|
||||
|
||||
static Alert()
|
||||
{
|
||||
AffectsMeasure<Segmented>(IsClosableProperty,
|
||||
@ -130,4 +147,278 @@ public partial class Alert : BorderedStyleControl
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
void IControlCustomStyle.InitOnConstruct()
|
||||
{
|
||||
_mainLayout = new Grid()
|
||||
{
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition(GridLength.Auto)
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition(GridLength.Auto),
|
||||
new ColumnDefinition(GridLength.Star),
|
||||
new ColumnDefinition(GridLength.Auto),
|
||||
new ColumnDefinition(GridLength.Auto)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
CreateStructure();
|
||||
SetupPaddingStyleConfig();
|
||||
HandleDescriptionEnabled(!string.IsNullOrEmpty(Description));
|
||||
Child = _mainLayout;
|
||||
}
|
||||
|
||||
private void CreateStructure()
|
||||
{
|
||||
_descriptionLabel = new Label
|
||||
{
|
||||
Content = Description,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Left,
|
||||
VerticalContentAlignment = VerticalAlignment.Center,
|
||||
Padding = new Thickness(0)
|
||||
};
|
||||
TextBlock.SetTextWrapping(_descriptionLabel, TextWrapping.Wrap);
|
||||
_controlTokenBinder.AddControlBinding(_descriptionLabel!, Label.MarginProperty, GlobalResourceKey.MarginXS);
|
||||
_descriptionLabel.IsVisible = !string.IsNullOrEmpty(Description);
|
||||
_infoStack = new StackPanel()
|
||||
{
|
||||
Orientation = Orientation.Vertical,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
HandleMessageMarqueEnabled(IsMessageMarqueEnabled);
|
||||
SetupCloseButton();
|
||||
SetupTypeIcon();
|
||||
|
||||
_infoStack.Children.Add(_descriptionLabel);
|
||||
|
||||
Grid.SetColumn(_infoStack, 1);
|
||||
Grid.SetRow(_infoStack, 0);
|
||||
_mainLayout!.Children.Add(_infoStack);
|
||||
|
||||
|
||||
if (ExtraAction is not null) {
|
||||
ExtraAction.VerticalAlignment = VerticalAlignment.Top;
|
||||
Grid.SetColumn(ExtraAction, 2);
|
||||
Grid.SetRow(ExtraAction, 0);
|
||||
_controlTokenBinder.AddControlBinding(ExtraAction, MarginProperty, AlertResourceKey.ExtraElementMargin);
|
||||
_mainLayout!.Children.Add(ExtraAction);
|
||||
}
|
||||
}
|
||||
|
||||
private Control GetMessageControl()
|
||||
{
|
||||
if (IsMessageMarqueEnabled) {
|
||||
return _messageMarqueeLabel!;
|
||||
}
|
||||
|
||||
return _messageLabel!;
|
||||
}
|
||||
|
||||
public void SetupCloseButton()
|
||||
{
|
||||
if (IsClosable && _closeButton is null) {
|
||||
if (CloseIcon is null) {
|
||||
CloseIcon = new PathIcon
|
||||
{
|
||||
Kind = "CloseOutlined",
|
||||
};
|
||||
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorIcon);
|
||||
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorIconHover);
|
||||
}
|
||||
|
||||
_closeButton = new IconButton
|
||||
{
|
||||
VerticalAlignment = VerticalAlignment.Top,
|
||||
};
|
||||
_controlTokenBinder.AddControlBinding(_closeButton, WidthProperty, GlobalResourceKey.IconSizeSM);
|
||||
_controlTokenBinder.AddControlBinding(_closeButton, HeightProperty, GlobalResourceKey.IconSizeSM);
|
||||
_controlTokenBinder.AddControlBinding(_closeButton, MarginProperty, AlertResourceKey.ExtraElementMargin);
|
||||
Grid.SetRow(_closeButton, 0);
|
||||
Grid.SetColumn(_closeButton, 3);
|
||||
_mainLayout!.Children.Add(_closeButton);
|
||||
BindUtils.RelayBind(this, CloseIconProperty, _closeButton, IconButton.IconProperty);
|
||||
} else if (_closeButton is not null) {
|
||||
_closeButton.IsVisible = IsClosable;
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
ApplyAlertTypeStyleConfig();
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
|
||||
}
|
||||
|
||||
private void SetupPaddingStyleConfig()
|
||||
{
|
||||
if (_descriptionLabel!.IsVisible) {
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, AlertResourceKey.WithDescriptionPadding);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, AlertResourceKey.DefaultPadding);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyAlertTypeStyleConfig()
|
||||
{
|
||||
if (Type == AlertType.Success) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorSuccessBg);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorSuccessBorder);
|
||||
} else if (Type == AlertType.Info) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorInfoBg);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorInfoBorder);
|
||||
} else if (Type == AlertType.Warning) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorWarningBg);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorWarningBorder);
|
||||
} else if (Type == AlertType.Error) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBg);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorder);
|
||||
}
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
|
||||
{
|
||||
if (!_scalingAwareConfigApplied) {
|
||||
_scalingAwareConfigApplied = true;
|
||||
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
|
||||
new RenderScaleAwareThicknessConfigure(this));
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (_initialized) {
|
||||
if (e.Property == DescriptionProperty) {
|
||||
var desc = e.GetNewValue<string?>();
|
||||
var enabled = !string.IsNullOrEmpty(desc);
|
||||
HandleDescriptionEnabled(enabled);
|
||||
HandleIconForDescriptionEnabled(enabled);
|
||||
_descriptionLabel!.IsVisible = enabled;
|
||||
} else if (e.Property == IsMessageMarqueEnabledProperty) {
|
||||
var enabled = e.GetNewValue<bool>();
|
||||
HandleMessageMarqueEnabled(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDescriptionEnabled(bool enabled)
|
||||
{
|
||||
var messageControl = GetMessageControl();
|
||||
if (enabled) {
|
||||
_controlTokenBinder.AddControlBinding(messageControl, FontSizeProperty, GlobalResourceKey.FontSizeLG);
|
||||
_controlTokenBinder.AddControlBinding(messageControl, MarginProperty, AlertResourceKey.MessageWithDescriptionMargin);
|
||||
if (_closeButton is not null) {
|
||||
_closeButton.VerticalAlignment = VerticalAlignment.Top;
|
||||
}
|
||||
messageControl.VerticalAlignment = VerticalAlignment.Top;
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(messageControl, FontSizeProperty, GlobalResourceKey.FontSize);
|
||||
messageControl.Margin = new Thickness();
|
||||
if (_closeButton is not null) {
|
||||
_closeButton.VerticalAlignment = VerticalAlignment.Center;
|
||||
}
|
||||
messageControl.VerticalAlignment = VerticalAlignment.Stretch;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleIconForDescriptionEnabled(bool enabled)
|
||||
{
|
||||
if (_icon is not null && IsShowIcon) {
|
||||
if (enabled) {
|
||||
_controlTokenBinder.AddControlBinding(_icon, WidthProperty, AlertResourceKey.WithDescriptionIconSize);
|
||||
_controlTokenBinder.AddControlBinding(_icon, HeightProperty, AlertResourceKey.WithDescriptionIconSize);
|
||||
_controlTokenBinder.AddControlBinding(_icon, MarginProperty, AlertResourceKey.IconWithDescriptionMargin);
|
||||
_icon.VerticalAlignment = VerticalAlignment.Top;
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(_icon, WidthProperty, AlertResourceKey.IconSize);
|
||||
_controlTokenBinder.AddControlBinding(_icon, HeightProperty, AlertResourceKey.IconSize);
|
||||
_controlTokenBinder.AddControlBinding(_icon, MarginProperty, AlertResourceKey.IconDefaultMargin);
|
||||
_icon.VerticalAlignment = VerticalAlignment.Center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTypeIcon()
|
||||
{
|
||||
if (IsShowIcon) {
|
||||
if (_icon is null) {
|
||||
var kind = string.Empty;
|
||||
var resourceKey = string.Empty;
|
||||
if (Type == AlertType.Success) {
|
||||
kind = "CheckCircleFilled";
|
||||
resourceKey = GlobalResourceKey.ColorSuccess;
|
||||
} else if (Type == AlertType.Info) {
|
||||
kind = "InfoCircleFilled";
|
||||
resourceKey = GlobalResourceKey.ColorPrimary;
|
||||
} else if (Type == AlertType.Warning) {
|
||||
kind = "ExclamationCircleFilled";
|
||||
resourceKey = GlobalResourceKey.ColorWarning;
|
||||
} else if (Type == AlertType.Error) {
|
||||
kind = "CloseCircleFilled";
|
||||
resourceKey = GlobalResourceKey.ColorError;
|
||||
}
|
||||
|
||||
_icon = new PathIcon
|
||||
{
|
||||
Kind = kind,
|
||||
};
|
||||
|
||||
_controlTokenBinder.AddControlBinding(_icon, PathIcon.NormalFillBrushProperty, resourceKey);
|
||||
|
||||
HandleIconForDescriptionEnabled(_descriptionLabel!.IsVisible);
|
||||
|
||||
Grid.SetRow(_icon, 0);
|
||||
Grid.SetColumn(_icon, 0);
|
||||
_mainLayout!.Children.Add(_icon);
|
||||
}
|
||||
} else if (_icon is not null) {
|
||||
_icon.IsVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMessageMarqueEnabled(bool enabled)
|
||||
{
|
||||
if (!enabled) {
|
||||
if (_messageLabel is null) {
|
||||
_messageLabel = new Label
|
||||
{
|
||||
Content = Message,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Left,
|
||||
VerticalContentAlignment = VerticalAlignment.Center,
|
||||
Padding = new Thickness(0)
|
||||
};
|
||||
TextBlock.SetTextWrapping(_messageLabel, TextWrapping.Wrap);
|
||||
}
|
||||
|
||||
if (_messageMarqueeLabel is not null) {
|
||||
_infoStack!.Children.Remove(_messageMarqueeLabel);
|
||||
}
|
||||
_infoStack!.Children.Insert(0, _messageLabel);
|
||||
} else {
|
||||
if (_messageMarqueeLabel is null) {
|
||||
_messageMarqueeLabel = new MarqueeLabel
|
||||
{
|
||||
Text = Message,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
Padding = new Thickness(0)
|
||||
};
|
||||
}
|
||||
if (_messageLabel is not null) {
|
||||
_infoStack!.Children.Remove(_messageLabel);
|
||||
}
|
||||
_infoStack!.Children.Insert(0, _messageMarqueeLabel);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -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.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Metadata;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
@ -70,7 +77,9 @@ public enum ArrowPosition
|
||||
RightEdgeAlignedBottom
|
||||
}
|
||||
|
||||
public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
|
||||
public partial class ArrowDecoratedBox : StyledControl,
|
||||
IShadowMaskInfoProvider,
|
||||
IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<bool> IsShowArrowProperty =
|
||||
AvaloniaProperty.Register<ArrowDecoratedBox, bool>(nameof(IsShowArrow), true);
|
||||
@ -133,6 +142,16 @@ public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
|
||||
set => SetValue(ChildProperty, value);
|
||||
}
|
||||
|
||||
private bool _initialized = false;
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private Geometry? _arrowGeometry;
|
||||
private Rect _contentRect;
|
||||
private Rect _arrowRect;
|
||||
private Border? _container;
|
||||
private CompositeDisposable? _compositeDisposable;
|
||||
private bool _needGenerateArrowVertexPoint = true;
|
||||
|
||||
static ArrowDecoratedBox()
|
||||
{
|
||||
AffectsMeasure<ArrowDecoratedBox>(ArrowPositionProperty, IsShowArrowProperty);
|
||||
@ -206,4 +225,282 @@ public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
|
||||
{
|
||||
return GetContentRect(DesiredSize).Deflate(0.5);
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
// 组件的 Token 绑定属性
|
||||
private double _arrowSize;
|
||||
|
||||
private static readonly DirectProperty<ArrowDecoratedBox, double> ArrowSizeTokenProperty
|
||||
= AvaloniaProperty.RegisterDirect<ArrowDecoratedBox, double>(nameof(_arrowSize),
|
||||
(o) => o._arrowSize,
|
||||
(o, v) => o._arrowSize = v);
|
||||
// 组件的 Token 绑定属性
|
||||
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
_container = new Border();
|
||||
NotifyCreateUi();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
if (IsShowArrow) {
|
||||
BuildGeometry(true);
|
||||
}
|
||||
|
||||
LogicalChildren.Add(_container);
|
||||
VisualChildren.Add(_container);
|
||||
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainer);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void SetupRelayProperties()
|
||||
{
|
||||
_compositeDisposable = new CompositeDisposable();
|
||||
// 生命周期一样,可以不用管理
|
||||
if (_container is not null) {
|
||||
if (Child?.Parent is not null) {
|
||||
UiStructureUtils.ClearLogicalParentRecursive(Child, null);
|
||||
UiStructureUtils.ClearVisualParentRecursive(Child, null);
|
||||
}
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, BackgroundSizingProperty, _container));
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, BackgroundProperty, _container));
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, CornerRadiusProperty, _container));
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, ChildProperty, _container));
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, PaddingProperty, _container));
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
NotifyApplyFixedStyleConfig();
|
||||
}
|
||||
|
||||
private (double, double) GetArrowVertexPoint()
|
||||
{
|
||||
if (_needGenerateArrowVertexPoint) {
|
||||
BuildGeometry(true);
|
||||
_arrowRect = GetArrowRect(DesiredSize);
|
||||
_needGenerateArrowVertexPoint = false;
|
||||
}
|
||||
|
||||
return _arrowVertexPoint;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsShowArrowProperty ||
|
||||
e.Property == ArrowPositionProperty ||
|
||||
e.Property == ArrowSizeTokenProperty ||
|
||||
e.Property == VisualParentProperty) {
|
||||
if (e.Property == IsShowArrowProperty && VisualRoot is null) {
|
||||
// 当开启的时候,但是还没有加入的渲染树,这个时候我们取不到 Token 需要在取值的时候重新生成一下
|
||||
_needGenerateArrowVertexPoint = true;
|
||||
}
|
||||
if (_initialized && VisualRoot is not null) {
|
||||
BuildGeometry(true);
|
||||
_arrowRect = GetArrowRect(DesiredSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildGeometry(bool force = false)
|
||||
{
|
||||
if (_arrowGeometry is null || force) {
|
||||
_arrowGeometry = CommonShapeBuilder.BuildArrow(_arrowSize, 1.5);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void NotifyApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(MinHeightProperty, GlobalResourceKey.ControlHeight);
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, GlobalResourceKey.PaddingXS);
|
||||
_controlTokenBinder.AddControlBinding(ArrowSizeTokenProperty, ArrowDecoratedBoxResourceKey.ArrowSize);
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
|
||||
}
|
||||
|
||||
public sealed override void Render(DrawingContext context)
|
||||
{
|
||||
if (IsShowArrow) {
|
||||
var direction = GetDirection(ArrowPosition);
|
||||
var matrix = Matrix.CreateTranslation(-_arrowSize / 2, -_arrowSize / 2);
|
||||
|
||||
if (direction == Direction.Right) {
|
||||
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(90));
|
||||
matrix *= Matrix.CreateTranslation(_arrowSize / 2, _arrowSize / 2);
|
||||
} else if (direction == Direction.Top) {
|
||||
matrix *= Matrix.CreateTranslation(_arrowSize / 2, 0);
|
||||
} else if (direction == Direction.Left) {
|
||||
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(-90));
|
||||
matrix *= Matrix.CreateTranslation(0, _arrowSize / 2);
|
||||
} else {
|
||||
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(180));
|
||||
matrix *= Matrix.CreateTranslation(_arrowSize / 2, _arrowSize / 2);
|
||||
}
|
||||
|
||||
matrix *= Matrix.CreateTranslation(_arrowRect.X, _arrowRect.Y);
|
||||
_arrowGeometry!.Transform = new MatrixTransform(matrix);
|
||||
context.DrawGeometry(_container?.Background, null, _arrowGeometry);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
var size = base.MeasureOverride(availableSize);
|
||||
var targetWidth = size.Width;
|
||||
var targetHeight = size.Height;
|
||||
targetHeight = Math.Max(MinHeight, targetHeight);
|
||||
|
||||
if (IsShowArrow) {
|
||||
BuildGeometry();
|
||||
var realArrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width);
|
||||
var direction = GetDirection(ArrowPosition);
|
||||
if (direction == Direction.Left || direction == Direction.Right) {
|
||||
targetWidth += realArrowSize;
|
||||
} else {
|
||||
targetHeight += realArrowSize;
|
||||
}
|
||||
}
|
||||
|
||||
var targetSize = new Size(targetWidth, targetHeight);
|
||||
_arrowRect = GetArrowRect(targetSize);
|
||||
return targetSize;
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
var visualChildren = VisualChildren;
|
||||
var visualCount = visualChildren.Count;
|
||||
_contentRect = GetContentRect(finalSize);
|
||||
for (int i = 0; i < visualCount; ++i) {
|
||||
var child = visualChildren[i];
|
||||
if (child is Layoutable layoutable) {
|
||||
layoutable.Arrange(_contentRect);
|
||||
}
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
private Rect GetContentRect(Size finalSize)
|
||||
{
|
||||
var offsetX = 0d;
|
||||
var offsetY = 0d;
|
||||
var targetWidth = finalSize.Width;
|
||||
var targetHeight = finalSize.Height;
|
||||
if (IsShowArrow) {
|
||||
var arrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width) + 0.5;
|
||||
var direction = GetDirection(ArrowPosition);
|
||||
if (direction == Direction.Left || direction == Direction.Right) {
|
||||
targetWidth -= arrowSize;
|
||||
} else {
|
||||
targetHeight -= arrowSize;
|
||||
}
|
||||
|
||||
if (direction == Direction.Right) {
|
||||
offsetX = 0.5;
|
||||
} else if (direction == Direction.Bottom) {
|
||||
offsetY = 0.5;
|
||||
} else if (direction == Direction.Top) {
|
||||
offsetY = arrowSize - 0.5;
|
||||
} else {
|
||||
offsetX = arrowSize - 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
return new Rect(offsetX, offsetY, targetWidth, targetHeight);
|
||||
}
|
||||
|
||||
private Rect GetArrowRect(Size finalSize)
|
||||
{
|
||||
var offsetX = 0d;
|
||||
var offsetY = 0d;
|
||||
var targetWidth = 0d;
|
||||
var targetHeight = 0d;
|
||||
var position = ArrowPosition;
|
||||
if (IsShowArrow) {
|
||||
var size = _arrowGeometry!.Bounds.Size;
|
||||
|
||||
var minValue = Math.Min(size.Width, size.Height);
|
||||
var maxValue = Math.Max(size.Width, size.Height);
|
||||
if (position == ArrowPosition.Left ||
|
||||
position == ArrowPosition.LeftEdgeAlignedTop ||
|
||||
position == ArrowPosition.LeftEdgeAlignedBottom) {
|
||||
targetWidth = minValue;
|
||||
targetHeight = maxValue;
|
||||
if (position == ArrowPosition.Left) {
|
||||
offsetY = (finalSize.Height - maxValue) / 2;
|
||||
} else if (position == ArrowPosition.LeftEdgeAlignedTop) {
|
||||
if (maxValue * 2 > finalSize.Height / 2) {
|
||||
offsetY = minValue;
|
||||
} else {
|
||||
offsetY = maxValue;
|
||||
}
|
||||
} else {
|
||||
if (maxValue * 2 > finalSize.Height / 2) {
|
||||
offsetY = finalSize.Height - minValue - maxValue;
|
||||
} else {
|
||||
offsetY = finalSize.Height - maxValue * 2;
|
||||
}
|
||||
}
|
||||
} else if (position == ArrowPosition.Top ||
|
||||
position == ArrowPosition.TopEdgeAlignedLeft ||
|
||||
position == ArrowPosition.TopEdgeAlignedRight) {
|
||||
if (position == ArrowPosition.TopEdgeAlignedLeft) {
|
||||
offsetX = maxValue;
|
||||
} else if (position == ArrowPosition.Top) {
|
||||
offsetX = (finalSize.Width - maxValue) / 2;
|
||||
} else {
|
||||
offsetX = finalSize.Width - maxValue * 2;
|
||||
}
|
||||
|
||||
targetWidth = maxValue;
|
||||
targetHeight = minValue;
|
||||
} else if (position == ArrowPosition.Right ||
|
||||
position == ArrowPosition.RightEdgeAlignedTop ||
|
||||
position == ArrowPosition.RightEdgeAlignedBottom) {
|
||||
offsetX = finalSize.Width - minValue;
|
||||
if (position == ArrowPosition.Right) {
|
||||
offsetY = (finalSize.Height - maxValue) / 2;
|
||||
} else if (position == ArrowPosition.RightEdgeAlignedTop) {
|
||||
if (maxValue * 2 > finalSize.Height / 2) {
|
||||
offsetY = minValue;
|
||||
} else {
|
||||
offsetY = maxValue;
|
||||
}
|
||||
} else {
|
||||
if (maxValue * 2 > finalSize.Height / 2) {
|
||||
offsetY = finalSize.Height - minValue - maxValue;
|
||||
} else {
|
||||
offsetY = finalSize.Height - maxValue * 2;
|
||||
}
|
||||
}
|
||||
|
||||
targetWidth = minValue;
|
||||
targetHeight = maxValue;
|
||||
} else {
|
||||
offsetY = finalSize.Height - minValue;
|
||||
targetWidth = maxValue;
|
||||
targetHeight = minValue;
|
||||
if (position == ArrowPosition.BottomEdgeAlignedLeft) {
|
||||
offsetX = maxValue;
|
||||
} else if (position == ArrowPosition.Bottom) {
|
||||
offsetX = (finalSize.Width - maxValue) / 2;
|
||||
} else {
|
||||
offsetX = finalSize.Width - maxValue * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var targetRect = new Rect(offsetX, offsetY, targetWidth, targetHeight);
|
||||
var center = targetRect.Center;
|
||||
|
||||
// 计算中点
|
||||
var direction = GetDirection(position);
|
||||
if (direction == Direction.Left || direction == Direction.Right) {
|
||||
_arrowVertexPoint = (center.Y, finalSize.Height - center.Y);
|
||||
} else if (direction == Direction.Top || direction == Direction.Bottom) {
|
||||
_arrowVertexPoint = (center.X, finalSize.Width - center.X);
|
||||
}
|
||||
return targetRect;
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -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.TokenSystem;
|
||||
using AtomUI.Icon;
|
||||
using AtomUI.Media;
|
||||
using AtomUI.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
@ -23,7 +33,10 @@ public enum ButtonShape
|
||||
Round,
|
||||
}
|
||||
|
||||
public partial class Button : AvaloniaButton, ISizeTypeAware
|
||||
public partial class Button : AvaloniaButton,
|
||||
ISizeTypeAware,
|
||||
IControlCustomStyle,
|
||||
IWaveAdornerInfoProvider
|
||||
{
|
||||
#region 公共属性定义
|
||||
public static readonly StyledProperty<ButtonType> ButtonTypeProperty =
|
||||
@ -90,6 +103,13 @@ public partial class Button : AvaloniaButton, ISizeTypeAware
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private ControlStyleState _styleState;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private IControlCustomStyle _customStyle;
|
||||
private StackPanel? _stackPanel;
|
||||
private Label? _label;
|
||||
private bool _initialized = false;
|
||||
|
||||
static Button()
|
||||
{
|
||||
@ -144,4 +164,566 @@ public partial class Button : AvaloniaButton, ISizeTypeAware
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Left;
|
||||
VerticalAlignment = VerticalAlignment.Bottom;
|
||||
Cursor = new Cursor(StandardCursorType.Hand);
|
||||
_customStyle.CollectStyleState();
|
||||
CreateMainLayout();
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
ApplyShapeStyleConfig();
|
||||
SetupIcon();
|
||||
ApplyIconModeStyleConfig();
|
||||
_customStyle.SetupTransitions();
|
||||
|
||||
if (ButtonType == ButtonType.Default) {
|
||||
if (IsDanger) {
|
||||
Effect = new DropShadowEffect
|
||||
{
|
||||
OffsetX = _dangerShadow.OffsetX,
|
||||
OffsetY = _dangerShadow.OffsetY,
|
||||
Color = _dangerShadow.Color,
|
||||
BlurRadius = _dangerShadow.Blur,
|
||||
};
|
||||
} else {
|
||||
Effect = new DropShadowEffect
|
||||
{
|
||||
OffsetX = _defaultShadow.OffsetX,
|
||||
OffsetY = _defaultShadow.OffsetY,
|
||||
Color = _defaultShadow.Color,
|
||||
BlurRadius = _defaultShadow.Blur,
|
||||
};
|
||||
}
|
||||
} else if (ButtonType == ButtonType.Primary) {
|
||||
if (IsDanger) {
|
||||
Effect = new DropShadowEffect
|
||||
{
|
||||
OffsetX = _dangerShadow.OffsetX,
|
||||
OffsetY = _dangerShadow.OffsetY,
|
||||
Color = _dangerShadow.Color,
|
||||
BlurRadius = _dangerShadow.Blur,
|
||||
};
|
||||
} else {
|
||||
Effect = new DropShadowEffect
|
||||
{
|
||||
OffsetX = _primaryShadow.OffsetX,
|
||||
OffsetY = _primaryShadow.OffsetY,
|
||||
Color = _primaryShadow.Color,
|
||||
BlurRadius = _primaryShadow.Blur,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMainLayout()
|
||||
{
|
||||
if (Text is null && Content is string content) {
|
||||
Text = content;
|
||||
}
|
||||
_label = new Label()
|
||||
{
|
||||
Content = Text,
|
||||
Padding = new Thickness(0),
|
||||
VerticalContentAlignment = VerticalAlignment.Center,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
_stackPanel = new StackPanel()
|
||||
{
|
||||
UseLayoutRounding = false,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
Orientation = Orientation.Horizontal,
|
||||
ClipToBounds = true
|
||||
};
|
||||
_stackPanel.Children.Add(_label);
|
||||
Content = _stackPanel;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyVariableStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.ReleaseTriggerBindings(this);
|
||||
if (ButtonType == ButtonType.Primary) {
|
||||
ApplyPrimaryStyle();
|
||||
} else if (ButtonType == ButtonType.Default) {
|
||||
ApplyDefaultStyle();
|
||||
} else if (ButtonType == ButtonType.Text) {
|
||||
ApplyTextStyle();
|
||||
} else if (ButtonType == ButtonType.Link) {
|
||||
ApplyLinkStyle();
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyShapeStyleConfig()
|
||||
{
|
||||
if (Shape == ButtonShape.Circle) {
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, "CirclePadding");
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(PaddingXXSTokenProperty, "PaddingXXS");
|
||||
_controlTokenBinder.AddControlBinding(DefaultShadowTokenProperty, "DefaultShadow");
|
||||
_controlTokenBinder.AddControlBinding(PrimaryShadowTokenProperty, "PrimaryShadow");
|
||||
_controlTokenBinder.AddControlBinding(DangerShadowTokenProperty, "DangerShadow");
|
||||
}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
if (IsPressed) {
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
} else {
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
|
||||
{
|
||||
if (ButtonType == ButtonType.Default ||
|
||||
(ButtonType == ButtonType.Primary && (IsGhost || !IsEnabled))) {
|
||||
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
|
||||
new RenderScaleAwareThicknessConfigure(this));
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupTransitions()
|
||||
{
|
||||
var transitions = new Transitions();
|
||||
if (ButtonType == ButtonType.Primary) {
|
||||
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty));
|
||||
if (IsGhost) {
|
||||
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BorderBrushProperty));
|
||||
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty));
|
||||
}
|
||||
} else if (ButtonType == ButtonType.Default) {
|
||||
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BorderBrushProperty));
|
||||
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty));
|
||||
} else if (ButtonType == ButtonType.Text) {
|
||||
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty));
|
||||
} else if (ButtonType == ButtonType.Link) {
|
||||
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty));
|
||||
}
|
||||
|
||||
Transitions = transitions;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplySizeTypeStyleConfig()
|
||||
{
|
||||
if (SizeType == SizeType.Small) {
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, ButtonResourceKey.ContentFontSizeSM);
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, ButtonResourceKey.PaddingSM);
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
|
||||
} else if (SizeType == SizeType.Middle) {
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, ButtonResourceKey.ContentFontSize);
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, GlobalResourceKey.Padding);
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
|
||||
} else if (SizeType == SizeType.Large) {
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, ButtonResourceKey.ContentFontSizeLG);
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, ButtonResourceKey.PaddingLG);
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
|
||||
}
|
||||
|
||||
if (Icon is not null) {
|
||||
if (SizeType == SizeType.Small) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.IconSizeSM);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.IconSizeSM);
|
||||
} else if (SizeType == SizeType.Middle) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.IconSize);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.IconSize);
|
||||
} else if (SizeType == SizeType.Large) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.IconSizeLG);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.IconSizeLG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyPrimaryStyle()
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
// IsGhost 优先级最高
|
||||
if (IsGhost) {
|
||||
Background = new SolidColorBrush(Colors.Transparent);
|
||||
if (IsDanger) {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorError);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
|
||||
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorderHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorBorderHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimary);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimary);
|
||||
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.PrimaryColor);
|
||||
if (IsDanger) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorError);
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorPrimary);
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.BorderColorDisabled);
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyDefaultStyle()
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, ButtonResourceKey.DefaultBg);
|
||||
if (IsDanger) {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorError);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
|
||||
|
||||
if (IsGhost) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
|
||||
}
|
||||
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorErrorBorderHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorBorderHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else {
|
||||
if (IsGhost) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextLightSolid);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorTextLightSolid);
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
|
||||
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.DefaultBorderColor);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultColor);
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.DefaultActiveBorderColor,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultActiveColor,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.DefaultHoverBorderColor,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultHoverColor,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, ButtonResourceKey.BorderColorDisabled);
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyTextStyle()
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
|
||||
|
||||
if (IsDanger) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBgActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorErrorBgHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, ButtonResourceKey.DefaultColor);
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgTextActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, ButtonResourceKey.TextHoverBg,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyLinkStyle()
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
if (IsGhost) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, ButtonResourceKey.DefaultBg);
|
||||
}
|
||||
|
||||
if (IsDanger) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorError);
|
||||
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorErrorHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorLink);
|
||||
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorLinkActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorLinkHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 针对 primary 的是否是 ghost 没有完成
|
||||
private void SetupIcon()
|
||||
{
|
||||
if (Icon is not null) {
|
||||
_stackPanel!.Children.Insert(0, Icon);
|
||||
if (Text is not null) {
|
||||
if (SizeType == SizeType.Small) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeSM);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeSM);
|
||||
} else if (SizeType == SizeType.Middle) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSize);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSize);
|
||||
} else if (SizeType == SizeType.Large) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeLG);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeLG);
|
||||
}
|
||||
|
||||
Icon.Margin = new Thickness(0, 0, _paddingXXS, 0);
|
||||
} else {
|
||||
if (SizeType == SizeType.Small) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.OnlyIconSizeSM);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.OnlyIconSizeSM);
|
||||
} else if (SizeType == SizeType.Middle) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.OnlyIconSize);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.OnlyIconSize);
|
||||
} else if (SizeType == SizeType.Large) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, ButtonResourceKey.OnlyIconSizeLG);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, ButtonResourceKey.OnlyIconSizeLG);
|
||||
}
|
||||
}
|
||||
|
||||
if (ButtonType == ButtonType.Primary) {
|
||||
SetupPrimaryIconStyle();
|
||||
} else if (ButtonType == ButtonType.Default || ButtonType == ButtonType.Link) {
|
||||
SetupDefaultOrLinkIconStyle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupPrimaryIconStyle()
|
||||
{
|
||||
if (Icon is null || Icon.ThemeType == IconThemeType.TwoTone) {
|
||||
return;
|
||||
}
|
||||
// IsGhost 优先级最高
|
||||
if (IsGhost) {
|
||||
if (IsDanger) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorError);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorErrorActive);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorErrorBorderHover);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorPrimary);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorPrimaryActive);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorPrimaryHover);
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, ButtonResourceKey.PrimaryColor);
|
||||
}
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.DisabledFilledBrushProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
}
|
||||
|
||||
private void SetupDefaultOrLinkIconStyle()
|
||||
{
|
||||
if (Icon is null || Icon.ThemeType == IconThemeType.TwoTone) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDanger) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorError);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorErrorActive);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorErrorBorderHover);
|
||||
} else {
|
||||
if (IsGhost) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorTextLightSolid);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, GlobalResourceKey.ColorPrimaryActive);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorPrimaryHover);
|
||||
} else {
|
||||
if (ButtonType == ButtonType.Link) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, GlobalResourceKey.ColorLink);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty, ButtonResourceKey.DefaultColor);
|
||||
}
|
||||
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty, ButtonResourceKey.DefaultActiveColor);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty, ButtonResourceKey.DefaultHoverColor);
|
||||
}
|
||||
}
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.DisabledFilledBrushProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
}
|
||||
|
||||
private void ApplyIconModeStyleConfig()
|
||||
{
|
||||
if (Icon is null) {
|
||||
return;
|
||||
}
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
Icon.IconMode = IconMode.Selected;
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
Icon.IconMode = IconMode.Active;
|
||||
} else {
|
||||
Icon.IconMode = IconMode.Normal;
|
||||
}
|
||||
} else {
|
||||
Icon.IconMode = IconMode.Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsPointerOverProperty ||
|
||||
e.Property == IsPressedProperty ||
|
||||
e.Property == IsEnabledProperty) {
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
ApplyIconModeStyleConfig();
|
||||
if (e.Property == IsPressedProperty) {
|
||||
if (_styleState.HasFlag(ControlStyleState.Raised) && (ButtonType == ButtonType.Primary ||
|
||||
ButtonType == ButtonType.Default)) {
|
||||
WaveType waveType = default;
|
||||
if (Shape == ButtonShape.Default) {
|
||||
waveType = WaveType.RoundRectWave;
|
||||
} else if (Shape == ButtonShape.Round) {
|
||||
waveType = WaveType.PillWave;
|
||||
} else if (Shape == ButtonShape.Circle) {
|
||||
waveType = WaveType.CircleWave;
|
||||
}
|
||||
|
||||
Color? waveColor = null;
|
||||
if (IsDanger) {
|
||||
if (ButtonType == ButtonType.Primary && !IsGhost) {
|
||||
waveColor = Color.Parse(Background?.ToString()!);
|
||||
} else {
|
||||
waveColor = Color.Parse(Foreground?.ToString()!);
|
||||
}
|
||||
}
|
||||
|
||||
WaveSpiritAdorner.ShowWaveAdorner(this, waveType, waveColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.Property == SizeTypeProperty) {
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
}
|
||||
|
||||
if (_initialized && e.Property == IconProperty) {
|
||||
var oldValue = e.GetOldValue<PathIcon?>();
|
||||
var newValue = e.GetNewValue<PathIcon?>();
|
||||
if (oldValue is not null) {
|
||||
_stackPanel!.Children.Remove(oldValue);
|
||||
}
|
||||
|
||||
if (newValue is not null) {
|
||||
SetupIcon();
|
||||
}
|
||||
}
|
||||
|
||||
if (_initialized && e.Property == ContentProperty) {
|
||||
// 不推荐,尽最大能力还原
|
||||
var oldText = (_label!.Content as string)!;
|
||||
var newContent = e.GetNewValue<object?>();
|
||||
if (newContent is string newText) {
|
||||
if (oldText != newText) {
|
||||
_label!.Content = newText;
|
||||
}
|
||||
}
|
||||
|
||||
Content = _stackPanel;
|
||||
}
|
||||
|
||||
if (_initialized && e.Property == TextProperty) {
|
||||
_label!.Content = Text;
|
||||
}
|
||||
}
|
||||
|
||||
public Rect WaveGeometry()
|
||||
{
|
||||
return new Rect(0, 0, Bounds.Width, Bounds.Height);
|
||||
}
|
||||
|
||||
public CornerRadius WaveBorderRadius()
|
||||
{
|
||||
return CornerRadius;
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -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.Media;
|
||||
using AtomUI.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Animation.Easings;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
@ -11,14 +17,44 @@ namespace AtomUI.Controls;
|
||||
|
||||
using AvaloniaCheckBox = Avalonia.Controls.CheckBox;
|
||||
|
||||
public partial class CheckBox : AvaloniaCheckBox, ICustomHitTest
|
||||
public partial class CheckBox : AvaloniaCheckBox,
|
||||
ICustomHitTest,
|
||||
IWaveAdornerInfoProvider,
|
||||
IControlCustomStyle
|
||||
{
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private ControlStyleState _styleState;
|
||||
private BorderRenderHelper _borderRenderHelper;
|
||||
private bool _initialized = false;
|
||||
|
||||
static CheckBox()
|
||||
{
|
||||
AffectsRender<CheckBox>(
|
||||
IsCheckedProperty,
|
||||
IndicatorCheckedMarkEffectSizeProperty,
|
||||
PaddingInlineProperty,
|
||||
IndicatorBorderBrushProperty,
|
||||
IndicatorCheckedMarkBrushProperty,
|
||||
IndicatorTristateMarkBrushProperty,
|
||||
IndicatorBackgroundProperty,
|
||||
IndicatorBorderThicknessProperty,
|
||||
IndicatorBorderRadiusProperty);
|
||||
}
|
||||
|
||||
public CheckBox()
|
||||
{
|
||||
_controlTokenBinder = new ControlTokenBinder(this, CheckBoxToken.ID);
|
||||
_customStyle = this;
|
||||
_borderRenderHelper = new BorderRenderHelper();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
@ -55,19 +91,29 @@ public partial class CheckBox : AvaloniaCheckBox, ICustomHitTest
|
||||
layoutable.Arrange(arrangeRect);
|
||||
}
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
|
||||
public sealed override void Render(DrawingContext context)
|
||||
{
|
||||
var indicatorRect = IndicatorRect();
|
||||
var penWidth = IndicatorBorderThickness.Top;
|
||||
PenUtils.TryModifyOrCreate(ref _cachedPen, IndicatorBorderBrush, penWidth);
|
||||
var borderRadius = GeometryUtils.CornerRadiusScalarValue(IndicatorBorderRadius);
|
||||
context.DrawRectangle(IndicatorBackground, _cachedPen, indicatorRect.Deflate(penWidth / 2),
|
||||
borderRadius, borderRadius);
|
||||
{
|
||||
var originTransform = Matrix.CreateTranslation(indicatorRect.X, indicatorRect.Y);
|
||||
using var transformState = context.PushTransform(originTransform);
|
||||
_borderRenderHelper.Render(context, indicatorRect.Size,
|
||||
borderThickness:new Thickness(penWidth),
|
||||
new CornerRadius(borderRadius),
|
||||
BackgroundSizing.OuterBorderEdge,
|
||||
IndicatorBackground,
|
||||
IndicatorBorderBrush,
|
||||
new BoxShadows());
|
||||
}
|
||||
if (_styleState.HasFlag(ControlStyleState.On)) {
|
||||
var checkMarkGeometry = CommonShapeBuilder.BuildCheckMark(new Size(IndicatorCheckedMarkEffectSize, IndicatorCheckedMarkEffectSize));
|
||||
var checkMarkGeometry =
|
||||
CommonShapeBuilder.BuildCheckMark(new Size(IndicatorCheckedMarkEffectSize, IndicatorCheckedMarkEffectSize));
|
||||
var checkMarkPenWidth = 2;
|
||||
var checkMarkPen = new Pen(IndicatorCheckedMarkBrush, 2);
|
||||
var checkMarkBounds = checkMarkGeometry.GetRenderBounds(checkMarkPen);
|
||||
@ -84,9 +130,161 @@ public partial class CheckBox : AvaloniaCheckBox, ICustomHitTest
|
||||
context.FillRectangle(IndicatorTristateMarkBrush!, indicatorTristateRect);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool HitTest(Point point)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
Cursor = new Cursor(StandardCursorType.Hand);
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
_customStyle.SetupTransitions();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
switch (IsChecked) {
|
||||
case true:
|
||||
_styleState |= ControlStyleState.On;
|
||||
break;
|
||||
case false:
|
||||
_styleState |= ControlStyleState.Off;
|
||||
break;
|
||||
default:
|
||||
_styleState |= ControlStyleState.Indeterminate;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsPressed) {
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
} else {
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
}
|
||||
|
||||
// Measure 之后才有值
|
||||
private Rect IndicatorRect()
|
||||
{
|
||||
var offsetY = (DesiredSize.Height - Margin.Top - Margin.Bottom - CheckIndicatorSize) / 2;
|
||||
return new Rect(0d, offsetY, CheckIndicatorSize, CheckIndicatorSize);
|
||||
}
|
||||
|
||||
private Rect TextRect()
|
||||
{
|
||||
var offsetX = CheckIndicatorSize + PaddingInline;
|
||||
return new Rect(offsetX, 0d, DesiredSize.Width - offsetX, DesiredSize.Height);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(CheckIndicatorSizeProperty, CheckBoxResourceKey.CheckIndicatorSize);
|
||||
_controlTokenBinder.AddControlBinding(PaddingInlineProperty, GlobalResourceKey.PaddingXS);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBorderRadiusProperty, GlobalResourceKey.BorderRadiusSM);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorTristateMarkSizeProperty,
|
||||
CheckBoxResourceKey.IndicatorTristateMarkSize);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorTristateMarkBrushProperty, GlobalResourceKey.ColorPrimary);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
|
||||
new RenderScaleAwareThicknessConfigure(this));
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyVariableStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.ReleaseTriggerBindings(this);
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorText);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorBgContainer);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorCheckedMarkBrushProperty, GlobalResourceKey.ColorBgContainer);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorBorder);
|
||||
if (_styleState.HasFlag(ControlStyleState.On)) {
|
||||
IndicatorCheckedMarkEffectSize = CheckIndicatorSize;
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorPrimary,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimary,
|
||||
BindingPriority.StyleTrigger);
|
||||
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else if (_styleState.HasFlag(ControlStyleState.Off)) {
|
||||
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
|
||||
IndicatorCheckedMarkEffectSize = CheckIndicatorSize * 0.7;
|
||||
} else if (_styleState.HasFlag(ControlStyleState.Indeterminate)) {
|
||||
IndicatorCheckedMarkEffectSize = CheckIndicatorSize * 0.7;
|
||||
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBorderBrushProperty, GlobalResourceKey.ColorBorder);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
if (_styleState.HasFlag(ControlStyleState.On)) {
|
||||
IndicatorCheckedMarkEffectSize = CheckIndicatorSize;
|
||||
_controlTokenBinder.AddControlBinding(IndicatorCheckedMarkBrushProperty,
|
||||
GlobalResourceKey.ColorTextDisabled,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.Indeterminate)) {
|
||||
_controlTokenBinder.AddControlBinding(IndicatorTristateMarkBrushProperty,
|
||||
GlobalResourceKey.ColorTextDisabled,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupTransitions()
|
||||
{
|
||||
Transitions = new Transitions()
|
||||
{
|
||||
AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBackgroundProperty),
|
||||
AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBorderBrushProperty),
|
||||
AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorTristateMarkBrushProperty),
|
||||
AnimationUtils.CreateTransition<DoubleTransition>(IndicatorCheckedMarkEffectSizeProperty,
|
||||
GlobalResourceKey.MotionDurationMid, new BackEaseOut())
|
||||
};
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsPointerOverProperty ||
|
||||
e.Property == IsCheckedProperty ||
|
||||
e.Property == IsEnabledProperty) {
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
if (e.Property == IsCheckedProperty &&
|
||||
_styleState.HasFlag(ControlStyleState.Enabled) &&
|
||||
_styleState.HasFlag(ControlStyleState.On)) {
|
||||
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.RoundRectWave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Rect WaveGeometry()
|
||||
{
|
||||
return IndicatorRect();
|
||||
}
|
||||
|
||||
public CornerRadius WaveBorderRadius()
|
||||
{
|
||||
return IndicatorBorderRadius;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -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.Styling;
|
||||
using AtomUI.TokenSystem;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
public partial class MarqueeLabel : TextBlock
|
||||
public partial class MarqueeLabel : TextBlock,
|
||||
IControlCustomStyle
|
||||
{
|
||||
private IControlCustomStyle? _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private ControlStyleState _styleState;
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
private bool _initialized = false;
|
||||
private double _cycleSpace; // 默认的间隔
|
||||
private Animation? _animation;
|
||||
private bool _animationRunning = false;
|
||||
private double _lastDesiredWidth;
|
||||
private double _lastTextWidth;
|
||||
private double _moveSpeed = 150; // 像素每秒
|
||||
private double _pivotOffsetStartValue = 0;
|
||||
|
||||
public static readonly DirectProperty<MarqueeLabel, double> CycleSpaceProperty =
|
||||
AvaloniaProperty.RegisterDirect<MarqueeLabel, double>(nameof(CycleSpace),
|
||||
o => o.CycleSpace,
|
||||
@ -76,4 +97,139 @@ public partial class MarqueeLabel : TextBlock
|
||||
return size;
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
TextWrapping = TextWrapping.NoWrap;
|
||||
_customStyle?.CollectStyleState();
|
||||
_customStyle?.ApplyFixedStyleConfig();
|
||||
}
|
||||
|
||||
private double CalculateDuration(double distance)
|
||||
{
|
||||
// 计算持续时间,确保至少有一毫秒的持续时间以避免除以零的错误
|
||||
return 4 * Math.Max(1, distance / _moveSpeed * 1000);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(CycleSpaceProperty, MarqueeLabelResourceKey.CycleSpace);
|
||||
_controlTokenBinder.AddControlBinding(MoveSpeedProperty, MarqueeLabelResourceKey.DefaultSpeed);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
|
||||
if (_initialized) {
|
||||
if (e.Property == IsPointerOverProperty) {
|
||||
_customStyle?.CollectStyleState();
|
||||
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_pivotOffsetStartValue = PivotOffset;
|
||||
HandleCleanupMarqueeAnimation();
|
||||
} else {
|
||||
if (DesiredSize.Width < _lastDesiredWidth || MathUtils.AreClose(DesiredSize.Width, _lastDesiredWidth)) {
|
||||
ReConfigureAnimation();
|
||||
HandleStartupMarqueeAnimation();
|
||||
}
|
||||
}
|
||||
// 这里处理暂停事件
|
||||
} else if (e.Property == CycleSpaceProperty || e.Property == MoveSpeedProperty) {
|
||||
var originRunning = _animationRunning;
|
||||
ReConfigureAnimation();
|
||||
if (originRunning) {
|
||||
HandleStartupMarqueeAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStartupMarqueeAnimation()
|
||||
{
|
||||
if (_animation is not null) {
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
var animationTask = _animation!.RunAsync(this, _cancellationTokenSource.Token);
|
||||
_animationRunning = true;
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await animationTask;
|
||||
_animationRunning = false;
|
||||
_animation = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCleanupMarqueeAnimation()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private void HandleLayoutUpdated(Size size, Size availableSize)
|
||||
{
|
||||
if (availableSize.Width > size.Width || MathUtils.AreClose(availableSize.Width, size.Width)) {
|
||||
HandleCleanupMarqueeAnimation();
|
||||
PivotOffset = 0;
|
||||
_lastDesiredWidth = 0;
|
||||
_pivotOffsetStartValue = 0;
|
||||
} else {
|
||||
if (!MathUtils.AreClose(_lastDesiredWidth, size.Width)) {
|
||||
_lastDesiredWidth = size.Width;
|
||||
if (!MathUtils.AreClose(_lastTextWidth, TextLayout.Width)) {
|
||||
_lastTextWidth = TextLayout.Width;
|
||||
}
|
||||
}
|
||||
if (!_animationRunning) {
|
||||
ReConfigureAnimation();
|
||||
HandleStartupMarqueeAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReConfigureAnimation()
|
||||
{
|
||||
if (_animation is not null && _animationRunning) {
|
||||
_cancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
var cycleWidth = _lastTextWidth + _cycleSpace;
|
||||
if (_pivotOffsetStartValue < -cycleWidth) {
|
||||
_pivotOffsetStartValue %= cycleWidth;
|
||||
}
|
||||
|
||||
var delta = _pivotOffsetStartValue - cycleWidth - _pivotOffsetStartValue;
|
||||
_animation = new Animation()
|
||||
{
|
||||
IterationCount = new IterationCount(long.MaxValue),
|
||||
Children =
|
||||
{
|
||||
new KeyFrame()
|
||||
{
|
||||
Cue = new Cue(0.0d),
|
||||
Setters = { new Setter(PivotOffsetProperty, _pivotOffsetStartValue) }
|
||||
},
|
||||
new KeyFrame()
|
||||
{
|
||||
Cue = new Cue(1.0d),
|
||||
Setters = { new Setter(PivotOffsetProperty, _pivotOffsetStartValue - cycleWidth) }
|
||||
}
|
||||
},
|
||||
FillMode = FillMode.Both,
|
||||
Duration = TimeSpan.FromMilliseconds(CalculateDuration(_lastTextWidth + _cycleSpace))
|
||||
};
|
||||
}
|
||||
|
||||
protected override void RenderTextLayout(DrawingContext context, Point origin)
|
||||
{
|
||||
var offset = TextLayout.OverhangLeading + PivotOffset;
|
||||
TextLayout.Draw(context, origin + new Point(offset, 0));
|
||||
if (PivotOffset < 0) {
|
||||
TextLayout.Draw(context, origin + new Point(offset + _lastTextWidth + _cycleSpace, 0));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -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.Media;
|
||||
using AtomUI.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
@ -33,8 +42,19 @@ public class OptionButtonPointerEventArgs : EventArgs
|
||||
}
|
||||
}
|
||||
|
||||
public partial class OptionButton : AvaloniaRadioButton, ISizeTypeAware
|
||||
public partial class OptionButton : AvaloniaRadioButton,
|
||||
ISizeTypeAware,
|
||||
IWaveAdornerInfoProvider,
|
||||
IControlCustomStyle
|
||||
{
|
||||
private bool _initialized = false;
|
||||
private ControlStyleState _styleState;
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private StackPanel? _stackPanel;
|
||||
private Label? _label;
|
||||
private CornerRadius? _originCornerRadius;
|
||||
|
||||
public static readonly StyledProperty<ButtonSizeType> SizeTypeProperty =
|
||||
AvaloniaProperty.Register<OptionButton, ButtonSizeType>(nameof(SizeType), ButtonSizeType.Middle);
|
||||
|
||||
@ -135,5 +155,355 @@ public partial class OptionButton : AvaloniaRadioButton, ISizeTypeAware
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
public Rect WaveGeometry()
|
||||
{
|
||||
return new Rect(0, 0, Bounds.Width, Bounds.Height);
|
||||
}
|
||||
|
||||
public CornerRadius WaveBorderRadius()
|
||||
{
|
||||
return CornerRadius;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
Cursor = new Cursor(StandardCursorType.Hand);
|
||||
VerticalAlignment = VerticalAlignment.Center;
|
||||
_customStyle.CollectStyleState();
|
||||
CreateMainLayout();
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
_customStyle.SetupTransitions();
|
||||
}
|
||||
|
||||
private void CreateMainLayout()
|
||||
{
|
||||
if (Text.Length == 0 && Content is string content) {
|
||||
Text = content;
|
||||
}
|
||||
|
||||
_label = new Label()
|
||||
{
|
||||
Content = Text,
|
||||
Padding = new Thickness(0),
|
||||
VerticalContentAlignment = VerticalAlignment.Center,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
_stackPanel = new StackPanel()
|
||||
{
|
||||
UseLayoutRounding = false,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
Orientation = Orientation.Horizontal,
|
||||
ClipToBounds = true
|
||||
};
|
||||
_stackPanel.Children.Add(_label);
|
||||
Content = _stackPanel;
|
||||
BindUtils.RelayBind(this, WidthProperty, _stackPanel);
|
||||
BindUtils.RelayBind(this, HeightProperty, _stackPanel);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupTransitions()
|
||||
{
|
||||
var transitions = new Transitions();
|
||||
if (ButtonStyle == OptionButtonStyle.Solid) {
|
||||
transitions.Add(new SolidColorBrushTransition()
|
||||
{
|
||||
Property = BackgroundProperty,
|
||||
Duration = _motionDuration
|
||||
});
|
||||
} else if (ButtonStyle == OptionButtonStyle.Outline) {
|
||||
transitions.Add(new SolidColorBrushTransition()
|
||||
{
|
||||
Property = BorderBrushProperty,
|
||||
Duration = _motionDuration
|
||||
});
|
||||
}
|
||||
|
||||
transitions.Add(new SolidColorBrushTransition()
|
||||
{
|
||||
Property = ForegroundProperty,
|
||||
Duration = _motionDuration
|
||||
});
|
||||
Transitions = transitions;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
if (IsPressed) {
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
} else {
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
|
||||
if (IsChecked.HasValue && IsChecked.Value) {
|
||||
_styleState |= ControlStyleState.Selected;
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(MotionDurationTokenProperty, GlobalResourceKey.MotionDurationMid);
|
||||
_controlTokenBinder.AddControlBinding(ColorPrimaryHoverTokenProperty, GlobalResourceKey.ColorPrimaryHover);
|
||||
_controlTokenBinder.AddControlBinding(ColorPrimaryActiveTokenProperty, GlobalResourceKey.ColorPrimaryActive);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
|
||||
new RenderScaleAwareThicknessConfigure(this, thickness =>
|
||||
{
|
||||
if (InOptionGroup) {
|
||||
return new Thickness(0);
|
||||
}
|
||||
|
||||
return thickness;
|
||||
}));
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsPointerOverProperty ||
|
||||
e.Property == IsPressedProperty ||
|
||||
e.Property == IsCheckedProperty) {
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
if (e.Property == IsPressedProperty) {
|
||||
if (_styleState.HasFlag(ControlStyleState.Raised)) {
|
||||
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.RoundRectWave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.Property == GroupPositionTraitProperty) {
|
||||
if (_originCornerRadius.HasValue) {
|
||||
CornerRadius = BuildCornerRadius(GroupPositionTrait, _originCornerRadius!.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (_initialized && e.Property == SizeTypeProperty) {
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
}
|
||||
|
||||
if (_initialized && e.Property == ContentProperty) {
|
||||
// 不推荐,尽最大能力还原
|
||||
var oldText = (_label!.Content as string)!;
|
||||
var newContent = e.GetNewValue<object?>();
|
||||
if (newContent is string newText) {
|
||||
if (oldText != newText) {
|
||||
_label!.Content = newText;
|
||||
}
|
||||
}
|
||||
|
||||
Content = _stackPanel;
|
||||
}
|
||||
|
||||
if (_initialized && e.Property == TextProperty) {
|
||||
_label!.Content = Text;
|
||||
}
|
||||
|
||||
if (e.Property == InOptionGroupProperty) {
|
||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private CornerRadius BuildCornerRadius(OptionButtonPositionTrait positionTrait, CornerRadius cornerRadius)
|
||||
{
|
||||
if (positionTrait == OptionButtonPositionTrait.First) {
|
||||
return new CornerRadius(cornerRadius.TopLeft,
|
||||
0,
|
||||
0,
|
||||
cornerRadius.BottomLeft);
|
||||
} else if (positionTrait == OptionButtonPositionTrait.Last) {
|
||||
return new CornerRadius(0,
|
||||
cornerRadius.TopRight,
|
||||
cornerRadius.BottomRight,
|
||||
0);
|
||||
} else if (positionTrait == OptionButtonPositionTrait.Middle) {
|
||||
return new CornerRadius(0);
|
||||
}
|
||||
|
||||
return cornerRadius;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyVariableStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.ReleaseTriggerBindings(this);
|
||||
if (ButtonStyle == OptionButtonStyle.Outline) {
|
||||
ApplyOutlineStyle();
|
||||
} else if (ButtonStyle == OptionButtonStyle.Solid) {
|
||||
ApplySolidStyle();
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplySizeTypeStyleConfig()
|
||||
{
|
||||
if (SizeType == ButtonSizeType.Small) {
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, OptionButtonResourceKey.ContentFontSizeSM);
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, OptionButtonResourceKey.PaddingSM);
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
|
||||
} else if (SizeType == ButtonSizeType.Middle) {
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, OptionButtonResourceKey.ContentFontSize);
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, OptionButtonResourceKey.Padding);
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
|
||||
} else if (SizeType == ButtonSizeType.Large) {
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, OptionButtonResourceKey.ContentFontSizeLG);
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, OptionButtonResourceKey.PaddingLG);
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
|
||||
}
|
||||
|
||||
_originCornerRadius = CornerRadius;
|
||||
CornerRadius = BuildCornerRadius(GroupPositionTrait, _originCornerRadius!.Value);
|
||||
}
|
||||
|
||||
private void ApplySolidStyle()
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
if (_styleState.HasFlag(ControlStyleState.Selected)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, OptionButtonResourceKey.ButtonSolidCheckedColor);
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, OptionButtonResourceKey.ButtonSolidCheckedBackground);
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty,
|
||||
OptionButtonResourceKey.ButtonSolidCheckedActiveBackground,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty,
|
||||
OptionButtonResourceKey.ButtonSolidCheckedHoverBackground,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorText);
|
||||
if (InOptionGroup) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainer);
|
||||
}
|
||||
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorBorder);
|
||||
if (_styleState.HasFlag(ControlStyleState.Selected)) {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, OptionButtonResourceKey.ButtonCheckedColorDisabled);
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, OptionButtonResourceKey.ButtonCheckedBgDisabled);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyOutlineStyle()
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
if (_styleState.HasFlag(ControlStyleState.Selected)) {
|
||||
if (InOptionGroup) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, OptionButtonResourceKey.ButtonBackground);
|
||||
}
|
||||
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimary);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimary);
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else {
|
||||
if (InOptionGroup) {
|
||||
_controlTokenBinder.AddControlBinding(this, BackgroundProperty, GlobalResourceKey.ColorTransparent);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(this, BackgroundProperty,
|
||||
OptionButtonResourceKey.ButtonCheckedBackground);
|
||||
}
|
||||
|
||||
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorBorder);
|
||||
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, OptionButtonResourceKey.ButtonColor);
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorPrimaryActive,
|
||||
BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorPrimaryHover,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(this, BorderBrushProperty, GlobalResourceKey.ColorBorder);
|
||||
if (_styleState.HasFlag(ControlStyleState.Selected)) {
|
||||
_controlTokenBinder.AddControlBinding(this, ForegroundProperty,
|
||||
OptionButtonResourceKey.ButtonCheckedColorDisabled);
|
||||
_controlTokenBinder.AddControlBinding(this, BackgroundProperty, OptionButtonResourceKey.ButtonCheckedBgDisabled);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(this, ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
_controlTokenBinder.AddControlBinding(this, BackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||
{
|
||||
base.OnPointerPressed(e);
|
||||
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
|
||||
{
|
||||
IsHovering = true,
|
||||
IsPressed = true
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
||||
{
|
||||
base.OnPointerReleased(e);
|
||||
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
|
||||
{
|
||||
IsHovering = true,
|
||||
IsPressed = false
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnPointerEntered(PointerEventArgs e)
|
||||
{
|
||||
base.OnPointerEntered(e);
|
||||
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
|
||||
{
|
||||
IsHovering = true,
|
||||
IsPressed = false
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnPointerExited(PointerEventArgs e)
|
||||
{
|
||||
base.OnPointerExited(e);
|
||||
OptionButtonPointerEvent?.Invoke(this, new OptionButtonPointerEventArgs(this)
|
||||
{
|
||||
IsHovering = false,
|
||||
IsPressed = false
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
@ -1,10 +1,15 @@
|
||||
using System.Collections.Specialized;
|
||||
using AtomUI.Controls.Utils;
|
||||
using AtomUI.Data;
|
||||
using AtomUI.TokenSystem;
|
||||
using AtomUI.Styling;
|
||||
using Avalonia;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Metadata;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
@ -12,7 +17,9 @@ namespace AtomUI.Controls;
|
||||
using ButtonSizeType = SizeType;
|
||||
using OptionButtons = AvaloniaList<OptionButton>;
|
||||
|
||||
public partial class OptionButtonGroup : StyledControl, ISizeTypeAware
|
||||
public partial class OptionButtonGroup : StyledControl,
|
||||
ISizeTypeAware,
|
||||
IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<ButtonSizeType> SizeTypeProperty =
|
||||
AvaloniaProperty.Register<OptionButtonGroup, ButtonSizeType>(nameof(SizeType), ButtonSizeType.Middle);
|
||||
@ -76,6 +83,13 @@ public partial class OptionButtonGroup : StyledControl, ISizeTypeAware
|
||||
}
|
||||
|
||||
[Content] public OptionButtons Options { get; } = new OptionButtons();
|
||||
|
||||
private bool _initialized = false;
|
||||
private ControlStyleState _styleState;
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private StackPanel? _layout;
|
||||
private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
|
||||
|
||||
static OptionButtonGroup()
|
||||
{
|
||||
@ -204,4 +218,164 @@ public partial class OptionButtonGroup : StyledControl, ISizeTypeAware
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
void IControlCustomStyle.InitOnConstruct()
|
||||
{
|
||||
_layout = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
ClipToBounds = true,
|
||||
};
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Left;
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
ApplyButtonSizeConfig();
|
||||
ApplyButtonStyleConfig();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
|
||||
LogicalChildren.Add(_layout!);
|
||||
VisualChildren.Add(_layout!);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupTransitions() {}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(MotionDurationTokenProperty, GlobalResourceKey.MotionDurationMid);
|
||||
_controlTokenBinder.AddControlBinding(ColorBorderTokenProperty, GlobalResourceKey.ColorBorder);
|
||||
_controlTokenBinder.AddControlBinding(ColorPrimaryTokenProperty, GlobalResourceKey.ColorPrimary);
|
||||
_controlTokenBinder.AddControlBinding(ColorPrimaryHoverTokenProperty, GlobalResourceKey.ColorPrimaryHover);
|
||||
_controlTokenBinder.AddControlBinding(ColorPrimaryActiveTokenProperty, GlobalResourceKey.ColorPrimaryActive);
|
||||
_controlTokenBinder.AddControlBinding(SelectedOptionBorderColorProperty, GlobalResourceKey.ColorPrimary);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
|
||||
new RenderScaleAwareThicknessConfigure(this));
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == SizeTypeProperty) {
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
} else if (e.Property == ButtonStyleProperty) {
|
||||
ApplyButtonStyleConfig();
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplySizeTypeStyleConfig()
|
||||
{
|
||||
if (SizeType == SizeType.Small) {
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
|
||||
} else if (SizeType == SizeType.Middle) {
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
|
||||
} else if (SizeType == SizeType.Large) {
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusLG);
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
|
||||
}
|
||||
|
||||
ApplyButtonSizeConfig();
|
||||
}
|
||||
|
||||
private void ApplyButtonSizeConfig()
|
||||
{
|
||||
foreach (var optionButton in Options) {
|
||||
optionButton.SizeType = SizeType;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyButtonStyleConfig()
|
||||
{
|
||||
foreach (var optionButton in Options) {
|
||||
optionButton.ButtonStyle = ButtonStyle;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
_borderRenderHelper.Render(context,
|
||||
new Size(DesiredSize.Width, DesiredSize.Height),
|
||||
BorderThickness,
|
||||
CornerRadius,
|
||||
BackgroundSizing.InnerBorderEdge,
|
||||
null,
|
||||
_colorBorder,
|
||||
new BoxShadows());
|
||||
for (int i = 0; i < Options.Count; ++i) {
|
||||
var optionButton = Options[i];
|
||||
if (ButtonStyle == OptionButtonStyle.Solid) {
|
||||
if (i <= Options.Count - 2) {
|
||||
var nextOption = Options[i + 1];
|
||||
if (nextOption == SelectedOption || optionButton == SelectedOption) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i != Options.Count - 1) {
|
||||
var offsetX = optionButton.Bounds.Right;
|
||||
var startPoint = new Point(offsetX, 0);
|
||||
var endPoint = new Point(offsetX, Bounds.Height);
|
||||
using var optionState = context.PushRenderOptions(new RenderOptions()
|
||||
{
|
||||
EdgeMode = EdgeMode.Aliased
|
||||
});
|
||||
context.DrawLine(new Pen(_colorBorder, BorderThickness.Left), startPoint, endPoint);
|
||||
}
|
||||
|
||||
if (ButtonStyle == OptionButtonStyle.Outline) {
|
||||
if (optionButton.IsEnabled && optionButton.IsChecked.HasValue && optionButton.IsChecked.Value) {
|
||||
// 绘制选中边框
|
||||
var offsetX = optionButton.Bounds.X;
|
||||
var width = optionButton.DesiredSize.Width;
|
||||
if (i != 0) {
|
||||
offsetX -= BorderThickness.Left;
|
||||
width += BorderThickness.Left;
|
||||
}
|
||||
var translationMatrix = Matrix.CreateTranslation(offsetX, 0);
|
||||
using var state = context.PushTransform(translationMatrix);
|
||||
var cornerRadius = new CornerRadius(0);
|
||||
if (i == 0) {
|
||||
cornerRadius = new CornerRadius(CornerRadius.TopLeft, 0, 0, CornerRadius.BottomLeft);
|
||||
} else if (i == Options.Count - 1) {
|
||||
cornerRadius = new CornerRadius(0, CornerRadius.TopRight, CornerRadius.BottomRight, 0);
|
||||
}
|
||||
_borderRenderHelper.Render(context,
|
||||
new Size(width, DesiredSize.Height),
|
||||
BorderThickness,
|
||||
cornerRadius,
|
||||
BackgroundSizing.InnerBorderEdge,
|
||||
null,
|
||||
SelectedOptionBorderColor,
|
||||
new BoxShadows());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleOptionPointerEvent(object? sender, OptionButtonPointerEventArgs args)
|
||||
{
|
||||
if (args.Button == SelectedOption) {
|
||||
_controlTokenBinder.ReleaseTriggerBindings(this);
|
||||
if (args.IsPressed) {
|
||||
_controlTokenBinder.AddControlBinding(SelectedOptionBorderColorProperty, GlobalResourceKey.ColorPrimaryActive, BindingPriority.StyleTrigger);
|
||||
} else if (args.IsHovering) {
|
||||
_controlTokenBinder.AddControlBinding(SelectedOptionBorderColorProperty, GlobalResourceKey.ColorPrimaryHover, BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -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.Icon;
|
||||
using AtomUI.Media;
|
||||
using AtomUI.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Animation.Easings;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Layout;
|
||||
@ -17,7 +22,9 @@ public enum ProgressStatus
|
||||
Active,
|
||||
}
|
||||
|
||||
public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeAware
|
||||
public abstract partial class AbstractProgressBar : RangeBaseControl,
|
||||
ISizeTypeAware,
|
||||
IControlCustomStyle
|
||||
{
|
||||
protected const double LARGE_STROKE_THICKNESS = 8;
|
||||
protected const double MIDDLE_STROKE_THICKNESS = 6;
|
||||
@ -190,6 +197,14 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
|
||||
get => _strokeThickness;
|
||||
set => SetAndRaise(StrokeThicknessProperty, ref _strokeThickness, value);
|
||||
}
|
||||
|
||||
protected bool _initialized = false;
|
||||
protected ControlStyleState _styleState;
|
||||
protected ControlTokenBinder _controlTokenBinder;
|
||||
internal IControlCustomStyle _customStyle;
|
||||
protected Label? _percentageLabel;
|
||||
protected PathIcon? _successCompletedIcon;
|
||||
protected PathIcon? _exceptionCompletedIcon;
|
||||
|
||||
static AbstractProgressBar()
|
||||
{
|
||||
@ -297,4 +312,153 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
|
||||
}
|
||||
|
||||
protected virtual void NotifyHandleExtraInfoVisibility() { }
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
_customStyle.SetupTransitions();
|
||||
|
||||
NotifySetupUi();
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.AfterUiStructureReady()
|
||||
{
|
||||
NotifyUiStructureReady();
|
||||
}
|
||||
|
||||
protected virtual void NotifyUiStructureReady()
|
||||
{
|
||||
// 创建完更新调用一次
|
||||
NotifyEffectSizeTypeChanged();
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupTransitions()
|
||||
{
|
||||
var transitions = new Transitions();
|
||||
|
||||
transitions.Add(AnimationUtils.CreateTransition<DoubleTransition>(ValueProperty, GlobalResourceKey.MotionDurationVerySlow, new ExponentialEaseOut()));
|
||||
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBarBrushProperty, GlobalResourceKey.MotionDurationFast));
|
||||
|
||||
NotifySetupTransitions(ref transitions);
|
||||
Transitions = transitions;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
ApplyIndicatorBarBackgroundStyleConfig();
|
||||
NotifyApplyFixedStyleConfig();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplySizeTypeStyleConfig()
|
||||
{
|
||||
ApplyEffectiveSizeTypeStyleConfig();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == SizeTypeProperty) {
|
||||
EffectiveSizeType = e.GetNewValue<SizeType>();
|
||||
} else if (e.Property == EffectiveSizeTypeProperty) {
|
||||
if (_initialized) {
|
||||
NotifyEffectSizeTypeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
if (e.Property == IsEnabledProperty ||
|
||||
e.Property == PercentageProperty) {
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
}
|
||||
|
||||
NotifyPropertyChanged(e);
|
||||
}
|
||||
|
||||
protected virtual void NotifySetupTransitions(ref Transitions transitions) {}
|
||||
protected virtual void ApplyIndicatorBarBackgroundStyleConfig() {}
|
||||
protected virtual void ApplyEffectiveSizeTypeStyleConfig() {}
|
||||
|
||||
protected virtual void NotifySetupUi()
|
||||
{
|
||||
var label = GetOrCreatePercentInfoLabel();
|
||||
AddChildControl(label);
|
||||
CreateCompletedIcons();
|
||||
}
|
||||
|
||||
protected abstract void CreateCompletedIcons();
|
||||
|
||||
protected virtual void NotifyApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(SuccessThresholdBrushProperty, GlobalResourceKey.ColorSuccess);
|
||||
}
|
||||
|
||||
protected virtual void NotifyPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyVariableStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.ReleaseTriggerBindings(this);
|
||||
NotifyApplyVariableStyleConfig();
|
||||
}
|
||||
|
||||
protected virtual void NotifyApplyVariableStyleConfig()
|
||||
{
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
if (TrailColor.HasValue) {
|
||||
GrooveBrush = new SolidColorBrush(TrailColor.Value);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(GrooveBrushProperty, ProgressBarResourceKey.RemainingColor);
|
||||
}
|
||||
|
||||
if (Status == ProgressStatus.Success || MathUtils.AreClose(Value, Maximum)) {
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, GlobalResourceKey.ColorSuccess);
|
||||
} else if (Status == ProgressStatus.Exception) {
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, GlobalResourceKey.ColorError);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, ProgressBarResourceKey.DefaultColor);
|
||||
}
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextLabel);
|
||||
if (_initialized) {
|
||||
_exceptionCompletedIcon!.IconMode = IconMode.Normal;
|
||||
_successCompletedIcon!.IconMode = IconMode.Normal;
|
||||
}
|
||||
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(GrooveBrushProperty, GlobalResourceKey.ColorBgContainerDisabled);
|
||||
_controlTokenBinder.AddControlBinding(IndicatorBarBrushProperty, GlobalResourceKey.ControlItemBgActiveDisabled);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
if (_initialized) {
|
||||
_exceptionCompletedIcon!.IconMode = IconMode.Disabled;
|
||||
_successCompletedIcon!.IconMode = IconMode.Disabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddChildControl(Control child)
|
||||
{
|
||||
VisualChildren.Add(child);
|
||||
(child as ISetLogicalParent).SetParent(this);
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
NotifyPrepareDrawingContext(context);
|
||||
RenderGroove(context);
|
||||
RenderIndicatorBar(context);
|
||||
}
|
||||
|
||||
protected virtual void NotifyPrepareDrawingContext(DrawingContext context) {}
|
||||
#endregion
|
||||
}
|
@ -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.Media;
|
||||
using AtomUI.Styling;
|
||||
using AtomUI.TokenSystem;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
@ -12,8 +18,17 @@ namespace AtomUI.Controls;
|
||||
|
||||
using AvaloniaRadioButton = Avalonia.Controls.RadioButton;
|
||||
|
||||
public partial class RadioButton : AvaloniaRadioButton, ICustomHitTest
|
||||
public partial class RadioButton : AvaloniaRadioButton,
|
||||
ICustomHitTest,
|
||||
IWaveAdornerInfoProvider,
|
||||
IControlCustomStyle
|
||||
{
|
||||
private bool _initialized = false;
|
||||
private IPen? _cachedPen;
|
||||
private ControlStyleState _styleState;
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
|
||||
static RadioButton()
|
||||
{
|
||||
AffectsRender<RadioButton>(
|
||||
@ -38,6 +53,12 @@ public partial class RadioButton : AvaloniaRadioButton, ICustomHitTest
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
@ -87,4 +108,136 @@ public partial class RadioButton : AvaloniaRadioButton, ICustomHitTest
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
Cursor = new Cursor(StandardCursorType.Hand);
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
_customStyle.SetupTransitions();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyVariableStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.ReleaseTriggerBindings(this);
|
||||
_controlTokenBinder.AddControlBinding(RadioBorderBrushProperty, GlobalResourceKey.ColorBorder);
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
// 暂时启用和禁用状态不归为 style trigger
|
||||
_controlTokenBinder.AddControlBinding(RadioInnerBackgroundProperty, RadioButtonResourceKey.RadioColor);
|
||||
if (_styleState.HasFlag(ControlStyleState.On)) {
|
||||
_controlTokenBinder.AddControlBinding(RadioBorderBrushProperty, GlobalResourceKey.ColorPrimary);
|
||||
_controlTokenBinder.AddControlBinding(RadioBackgroundProperty, GlobalResourceKey.ColorPrimary);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(RadioBackgroundProperty, GlobalResourceKey.ColorBgContainer);
|
||||
if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(RadioBorderBrushProperty, GlobalResourceKey.ColorPrimary,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
_controlTokenBinder.AddControlBinding(RadioBackgroundProperty, GlobalResourceKey.ColorBgContainerDisabled,
|
||||
BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(RadioInnerBackgroundProperty, RadioButtonResourceKey.DotColorDisabled,
|
||||
BindingPriority.StyleTrigger);
|
||||
}
|
||||
RadioDotEffectSize = CalculateDotSize(IsEnabled, IsChecked.HasValue && IsChecked.Value);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(RadioBorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Style,
|
||||
new RenderScaleAwareThicknessConfigure(this));
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(RadioSizeProperty, RadioButtonResourceKey.RadioSize);
|
||||
_controlTokenBinder.AddControlBinding(DotSizeValueProperty, RadioButtonResourceKey.DotSize);
|
||||
_controlTokenBinder.AddControlBinding(DotPaddingValueProperty, RadioButtonResourceKey.DotPadding);
|
||||
_controlTokenBinder.AddControlBinding(PaddingInlineProperty, GlobalResourceKey.PaddingXS);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
if (IsPressed) {
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
} else {
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
|
||||
if (IsChecked.HasValue && IsChecked.Value) {
|
||||
_styleState |= ControlStyleState.On;
|
||||
} else {
|
||||
_styleState |= ControlStyleState.Off;
|
||||
}
|
||||
}
|
||||
|
||||
private double CalculateDotSize(bool isEnabled, bool isChecked)
|
||||
{
|
||||
double targetValue;
|
||||
if (isChecked) {
|
||||
if (isEnabled) {
|
||||
targetValue = _dotSizeValue;
|
||||
} else {
|
||||
targetValue = RadioSize - _dotPaddingValue * 2;
|
||||
}
|
||||
} else {
|
||||
targetValue = _dotSizeValue * 0.6;
|
||||
}
|
||||
|
||||
return targetValue;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupTransitions()
|
||||
{
|
||||
Transitions = new Transitions()
|
||||
{
|
||||
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBorderBrushProperty),
|
||||
AnimationUtils.CreateTransition<DoubleTransition>(RadioDotEffectSizeProperty),
|
||||
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBackgroundProperty, GlobalResourceKey.MotionDurationFast)
|
||||
};
|
||||
}
|
||||
|
||||
// Measure 之后才有值
|
||||
private Rect RadioRect()
|
||||
{
|
||||
var offsetY = (DesiredSize.Height - Margin.Top - Margin.Bottom - RadioSize) / 2;
|
||||
return new Rect(0d, offsetY, RadioSize, RadioSize);
|
||||
}
|
||||
|
||||
private Rect RadioTextRect()
|
||||
{
|
||||
var offsetX = RadioSize + PaddingInline;
|
||||
return new Rect(offsetX, 0d, DesiredSize.Width - offsetX, DesiredSize.Height);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsPointerOverProperty ||
|
||||
e.Property == IsCheckedProperty ||
|
||||
e.Property == IsEnabledProperty) {
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
if (e.Property == IsCheckedProperty &&
|
||||
_styleState.HasFlag(ControlStyleState.Enabled) &&
|
||||
_styleState.HasFlag(ControlStyleState.On)) {
|
||||
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.CircleWave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Rect WaveGeometry()
|
||||
{
|
||||
return RadioRect();
|
||||
}
|
||||
|
||||
public CornerRadius WaveBorderRadius()
|
||||
{
|
||||
return new CornerRadius(RadioSize / 2);
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -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.Icon;
|
||||
using AtomUI.Media;
|
||||
using AtomUI.Styling;
|
||||
using AtomUI.TokenSystem;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Metadata;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
public partial class SegmentedItem : StyledControl
|
||||
public partial class SegmentedItem : StyledControl, IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<SizeType> SizeTypeProperty =
|
||||
AvaloniaProperty.Register<SegmentedItem, SizeType>(nameof(SizeType), SizeType.Middle);
|
||||
@ -17,14 +24,14 @@ public partial class SegmentedItem : StyledControl
|
||||
|
||||
public static readonly StyledProperty<PathIcon?> IconProperty
|
||||
= AvaloniaProperty.Register<SegmentedItem, PathIcon?>(nameof(Icon));
|
||||
|
||||
|
||||
public static readonly DirectProperty<SegmentedItem, bool> IsPressedProperty =
|
||||
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsPressed), o => o.IsPressed);
|
||||
|
||||
|
||||
public static readonly DirectProperty<SegmentedItem, bool> IsCurrentItemProperty =
|
||||
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsCurrentItem),
|
||||
o => o.IsCurrentItem,
|
||||
(o, v) => o.IsCurrentItem = v);
|
||||
AvaloniaProperty.RegisterDirect<SegmentedItem, bool>(nameof(IsCurrentItem),
|
||||
o => o.IsCurrentItem,
|
||||
(o, v) => o.IsCurrentItem = v);
|
||||
|
||||
internal SizeType SizeType
|
||||
{
|
||||
@ -53,10 +60,22 @@ public partial class SegmentedItem : StyledControl
|
||||
get => _isPressed;
|
||||
private set => SetAndRaise(IsPressedProperty, ref _isPressed, value);
|
||||
}
|
||||
|
||||
|
||||
// 内部属性
|
||||
private bool _isCurrentItem = false;
|
||||
internal bool IsCurrentItem { get => _isCurrentItem; set => SetAndRaise(IsCurrentItemProperty, ref _isCurrentItem, value); }
|
||||
|
||||
internal bool IsCurrentItem
|
||||
{
|
||||
get => _isCurrentItem;
|
||||
set => SetAndRaise(IsCurrentItemProperty, ref _isCurrentItem, value);
|
||||
}
|
||||
|
||||
private bool _initialized = false;
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private Label? _label;
|
||||
private bool _isPressed = false;
|
||||
private ControlStyleState _styleState;
|
||||
|
||||
static SegmentedItem()
|
||||
{
|
||||
@ -82,6 +101,7 @@ public partial class SegmentedItem : StyledControl
|
||||
targetWidth += _paddingXXS;
|
||||
}
|
||||
}
|
||||
|
||||
return new Size(targetWidth, targetHeight);
|
||||
}
|
||||
|
||||
@ -93,10 +113,11 @@ public partial class SegmentedItem : StyledControl
|
||||
if (Text.Length == 0) {
|
||||
offsetX += (DesiredSize.Width - Icon.Width) / 2;
|
||||
}
|
||||
Icon.Arrange(new Rect(new (offsetX, offsetY), new Size(Icon.Width, Icon.Height)));
|
||||
|
||||
Icon.Arrange(new Rect(new(offsetX, offsetY), new Size(Icon.Width, Icon.Height)));
|
||||
offsetX += Icon.DesiredSize.Width + _paddingXXS;
|
||||
}
|
||||
|
||||
|
||||
_label!.Arrange(new Rect(new Point(offsetX, -1), _label.DesiredSize));
|
||||
return finalSize;
|
||||
}
|
||||
@ -132,4 +153,123 @@ public partial class SegmentedItem : StyledControl
|
||||
IsPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
_label = new Label()
|
||||
{
|
||||
Content = Text,
|
||||
Padding = new Thickness(0),
|
||||
VerticalContentAlignment = VerticalAlignment.Center,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
_customStyle.SetupTransitions();
|
||||
|
||||
LogicalChildren.Add(_label);
|
||||
VisualChildren.Add(_label);
|
||||
|
||||
ApplyIconStyleConfig();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupTransitions()
|
||||
{
|
||||
Transitions = new Transitions()
|
||||
{
|
||||
AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty)
|
||||
};
|
||||
}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
if (IsPressed) {
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
} else {
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
|
||||
if (IsCurrentItem) {
|
||||
_styleState |= ControlStyleState.Selected;
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(PaddingXXSTokenProperty, GlobalResourceKey.PaddingXXS);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplySizeTypeStyleConfig()
|
||||
{
|
||||
if (SizeType == SizeType.Small) {
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightSM);
|
||||
} else if (SizeType == SizeType.Middle) {
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
|
||||
} else if (SizeType == SizeType.Large) {
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeightLG);
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (_initialized) {
|
||||
if (e.Property == IsPointerOverProperty ||
|
||||
e.Property == IsPressedProperty ||
|
||||
e.Property == IsCurrentItemProperty) {
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
} else if (e.Property == SizeTypeProperty) {
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
} else if (e.Property == TextProperty) {
|
||||
_label!.Content = Text;
|
||||
} else if (e.Property == IconProperty) {
|
||||
var oldIcon = e.GetOldValue<PathIcon?>();
|
||||
if (oldIcon is not null) {
|
||||
_controlTokenBinder.ReleaseBindings(oldIcon);
|
||||
LogicalChildren.Remove(oldIcon);
|
||||
VisualChildren.Remove(oldIcon);
|
||||
}
|
||||
|
||||
ApplyIconStyleConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置大小和颜色
|
||||
private void ApplyIconStyleConfig()
|
||||
{
|
||||
if (Icon is not null) {
|
||||
if (Icon.ThemeType != IconThemeType.TwoTone) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty,
|
||||
SegmentedResourceKey.ItemColor);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.ActiveFilledBrushProperty,
|
||||
SegmentedResourceKey.ItemHoverColor);
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.SelectedFilledBrushProperty,
|
||||
SegmentedResourceKey.ItemSelectedColor);
|
||||
}
|
||||
|
||||
if (SizeType == SizeType.Small) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeSM);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeSM);
|
||||
} else if (SizeType == SizeType.Middle) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSize);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSize);
|
||||
} else if (SizeType == SizeType.Large) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, WidthProperty, GlobalResourceKey.IconSizeLG);
|
||||
_controlTokenBinder.AddControlBinding(Icon, HeightProperty, GlobalResourceKey.IconSizeLG);
|
||||
}
|
||||
|
||||
LogicalChildren.Add(Icon);
|
||||
VisualChildren.Add(Icon);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -1,8 +1,12 @@
|
||||
using AtomUI.Data;
|
||||
using AtomUI.Media;
|
||||
using AtomUI.Styling;
|
||||
using AtomUI.TokenSystem;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Rendering;
|
||||
@ -12,7 +16,9 @@ namespace AtomUI.Controls;
|
||||
/// <summary>
|
||||
/// 在内部维护一些额外信息的控件,用户无感知
|
||||
/// </summary>
|
||||
internal partial class SegmentedItemBox : BorderedStyleControl, ICustomHitTest
|
||||
internal partial class SegmentedItemBox : BorderedStyleControl,
|
||||
ICustomHitTest,
|
||||
IControlCustomStyle
|
||||
{
|
||||
internal Control Item { get; }
|
||||
|
||||
@ -44,7 +50,12 @@ internal partial class SegmentedItemBox : BorderedStyleControl, ICustomHitTest
|
||||
|
||||
private bool _isCurrentItem;
|
||||
internal bool IsCurrentItem { get => _isCurrentItem; set => SetCurrentItem(value); }
|
||||
|
||||
private bool _initialized = false;
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private bool _isPressed = false;
|
||||
private ControlStyleState _styleState;
|
||||
|
||||
public int LastItem { get; set; } = -1;
|
||||
|
||||
static SegmentedItemBox()
|
||||
@ -153,4 +164,100 @@ internal partial class SegmentedItemBox : BorderedStyleControl, ICustomHitTest
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
Cursor = new Cursor(StandardCursorType.Hand);
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
_customStyle.SetupTransitions();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupTransitions()
|
||||
{
|
||||
Transitions = new Transitions()
|
||||
{
|
||||
AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty),
|
||||
AnimationUtils.CreateTransition<SolidColorBrushTransition>(ForegroundProperty),
|
||||
};
|
||||
}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
if (IsPressed) {
|
||||
_styleState |= ControlStyleState.Sunken;
|
||||
} else {
|
||||
_styleState |= ControlStyleState.Raised;
|
||||
}
|
||||
|
||||
if (IsCurrentItem) {
|
||||
_styleState |= ControlStyleState.Selected;
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplySizeTypeStyleConfig()
|
||||
{
|
||||
if (SizeType == SizeType.Large) {
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadius);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSizeLG);
|
||||
} else if (SizeType == SizeType.Middle) {
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
|
||||
} else if (SizeType == SizeType.Small) {
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusXS);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyVariableStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.ReleaseTriggerBindings(this);
|
||||
if (_styleState.HasFlag(ControlStyleState.Enabled)) {
|
||||
if (!_styleState.HasFlag(ControlStyleState.Selected)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorTransparent);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, SegmentedResourceKey.ItemColor);
|
||||
if (_styleState.HasFlag(ControlStyleState.Sunken)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, SegmentedResourceKey.ItemActiveBg, BindingPriority.StyleTrigger);
|
||||
} else if (_styleState.HasFlag(ControlStyleState.MouseOver)) {
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, SegmentedResourceKey.ItemHoverBg, BindingPriority.StyleTrigger);
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, SegmentedResourceKey.ItemHoverColor, BindingPriority.StyleTrigger);
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, SegmentedResourceKey.ItemSelectedColor);
|
||||
}
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightSMTokenProperty, GlobalResourceKey.ControlHeightSM);
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightTokenProperty, GlobalResourceKey.ControlHeight);
|
||||
_controlTokenBinder.AddControlBinding(ControlHeightLGTokenProperty, GlobalResourceKey.ControlHeightLG);
|
||||
_controlTokenBinder.AddControlBinding(TrackPaddingTokenProperty, SegmentedResourceKey.TrackPadding);
|
||||
_controlTokenBinder.AddControlBinding(SegmentedItemPaddingSMTokenProperty, SegmentedResourceKey.SegmentedItemPaddingSM);
|
||||
_controlTokenBinder.AddControlBinding(SegmentedItemPaddingTokenProperty, SegmentedResourceKey.SegmentedItemPadding);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsPressedProperty ||
|
||||
e.Property == IsPointerOverProperty ||
|
||||
e.Property == IsCurrentItemProperty) {
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
}
|
||||
|
||||
if (_initialized) {
|
||||
if (e.Property == SizeTypeProperty) {
|
||||
_customStyle.ApplySizeTypeStyleConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -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.Media;
|
||||
using AtomUI.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Metadata;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
@ -14,7 +20,7 @@ public enum SeparatorTitlePosition
|
||||
Center
|
||||
}
|
||||
|
||||
public partial class Separator : StyledControl
|
||||
public partial class Separator : StyledControl, IControlCustomStyle
|
||||
{
|
||||
public static readonly StyledProperty<string?> TitleProperty =
|
||||
AvaloniaProperty.Register<Separator, string?>(nameof(Title));
|
||||
@ -25,19 +31,19 @@ public partial class Separator : StyledControl
|
||||
|
||||
public static readonly StyledProperty<IBrush?> TitleColorProperty =
|
||||
AvaloniaProperty.Register<Separator, IBrush?>(nameof(TitleColor));
|
||||
|
||||
|
||||
public static readonly StyledProperty<IBrush?> LineColorProperty =
|
||||
AvaloniaProperty.Register<Separator, IBrush?>(nameof(LineColor));
|
||||
|
||||
|
||||
public static readonly StyledProperty<Orientation> OrientationProperty =
|
||||
AvaloniaProperty.Register<Separator, Orientation>(nameof(Orientation), Orientation.Horizontal);
|
||||
|
||||
|
||||
public static readonly StyledProperty<double> OrientationMarginProperty =
|
||||
AvaloniaProperty.Register<Separator, double>(nameof(OrientationMargin), double.NaN);
|
||||
|
||||
|
||||
public static readonly StyledProperty<bool> IsDashedLineProperty =
|
||||
AvaloniaProperty.Register<Separator, bool>(nameof(Orientation), false);
|
||||
|
||||
|
||||
public static readonly StyledProperty<double> LineWidthProperty =
|
||||
AvaloniaProperty.Register<Separator, double>(nameof(LineWidth), 1);
|
||||
|
||||
@ -68,7 +74,7 @@ public partial class Separator : StyledControl
|
||||
get => GetValue(TitleColorProperty);
|
||||
set => SetValue(TitleColorProperty, value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 分割线标题的颜色
|
||||
/// </summary>
|
||||
@ -77,7 +83,7 @@ public partial class Separator : StyledControl
|
||||
get => GetValue(LineColorProperty);
|
||||
set => SetValue(LineColorProperty, value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 分割线的方向,垂直和水平分割线
|
||||
/// </summary>
|
||||
@ -97,7 +103,7 @@ public partial class Separator : StyledControl
|
||||
get => GetValue(OrientationMarginProperty);
|
||||
set => SetValue(OrientationMarginProperty, value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否为虚线
|
||||
/// </summary>
|
||||
@ -115,25 +121,33 @@ public partial class Separator : StyledControl
|
||||
get => GetValue(LineWidthProperty);
|
||||
set => SetValue(LineWidthProperty, value);
|
||||
}
|
||||
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private bool _initialized = false;
|
||||
private bool _scalingAwareConfigApplied = false;
|
||||
private Label? _titleLabel;
|
||||
private const double SEPARATOR_LINE_MIN_PROPORTION = 0.25;
|
||||
private double _currentEdgeDistance;
|
||||
|
||||
static Separator()
|
||||
{
|
||||
AffectsMeasure<Separator>(OrientationProperty,
|
||||
AffectsMeasure<Separator>(OrientationProperty,
|
||||
LineWidthProperty,
|
||||
TitleProperty);
|
||||
AffectsArrange<Separator>(TitlePositionProperty);
|
||||
AffectsRender<Separator>(TitleColorProperty,
|
||||
AffectsRender<Separator>(TitleColorProperty,
|
||||
LineColorProperty,
|
||||
IsDashedLineProperty);
|
||||
}
|
||||
|
||||
|
||||
public Separator()
|
||||
{
|
||||
_controlTokenBinder = new ControlTokenBinder(this, SeparatorToken.ID);
|
||||
_customStyle = this;
|
||||
_customStyle.InitOnConstruct();
|
||||
}
|
||||
|
||||
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
@ -142,18 +156,234 @@ public partial class Separator : StyledControl
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
_customStyle.HandlePropertyChangedForStyle(e);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
_titleLabel = new Label
|
||||
{
|
||||
Content = Title,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Center,
|
||||
VerticalContentAlignment = VerticalAlignment.Center,
|
||||
Padding = new Thickness(0)
|
||||
};
|
||||
|
||||
LogicalChildren.Add(_titleLabel);
|
||||
VisualChildren.Add(_titleLabel);
|
||||
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
HandleForOrientation();
|
||||
_controlTokenBinder.AddControlBinding(TitleColorProperty, GlobalResourceKey.ColorText);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, GlobalResourceKey.FontSize);
|
||||
_controlTokenBinder.AddControlBinding(LineColorProperty, GlobalResourceKey.ColorSplit);
|
||||
|
||||
_controlTokenBinder.AddControlBinding(TextPaddingInlineTokenProperty, SeparatorResourceKey.TextPaddingInline);
|
||||
_controlTokenBinder.AddControlBinding(OrientationMarginPercentTokenProperty,
|
||||
SeparatorResourceKey.OrientationMarginPercent);
|
||||
_controlTokenBinder.AddControlBinding(VerticalMarginInlineTokenProperty,
|
||||
SeparatorResourceKey.VerticalMarginInline);
|
||||
|
||||
if (_titleLabel is not null) {
|
||||
BindUtils.RelayBind(this, FontSizeProperty, _titleLabel);
|
||||
BindUtils.RelayBind(this, TitleColorProperty, _titleLabel, ForegroundProperty);
|
||||
BindUtils.RelayBind(this, FontStyleProperty, _titleLabel, FontStyleProperty);
|
||||
BindUtils.RelayBind(this, FontWeightProperty, _titleLabel, FontWeightProperty);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleForOrientation()
|
||||
{
|
||||
if (Orientation == Orientation.Horizontal) {
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
VerticalAlignment = VerticalAlignment.Center;
|
||||
_titleLabel!.IsVisible = true;
|
||||
} else {
|
||||
HorizontalAlignment = HorizontalAlignment.Center;
|
||||
VerticalAlignment = VerticalAlignment.Center;
|
||||
_titleLabel!.IsVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
|
||||
{
|
||||
if (!_scalingAwareConfigApplied) {
|
||||
_scalingAwareConfigApplied = true;
|
||||
_controlTokenBinder.AddControlBinding(LineWidthProperty, GlobalResourceKey.LineWidth, BindingPriority.Style,
|
||||
new RenderScaleAwareDoubleConfigure(this));
|
||||
}
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == OrientationProperty) {
|
||||
if (_initialized) {
|
||||
HandleForOrientation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 当为水平分隔线的时候,我们设置最小的高度,当为垂直分割线的时候我们设置一个合适宽度
|
||||
// 然后保持尽可能保持文字尽可能的显示,如果小于最小分隔部分的两倍的时候,文字隐藏。
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
var size = base.MeasureOverride(availableSize);
|
||||
var targetHeight = size.Height;
|
||||
var targetWidth = size.Width;
|
||||
if (Orientation == Orientation.Horizontal) {
|
||||
if (Title is null || Title?.Length == 0) {
|
||||
targetHeight = LineWidth * 3;
|
||||
}
|
||||
|
||||
if (!double.IsInfinity(availableSize.Width)) {
|
||||
targetWidth = Math.Max(availableSize.Width, targetWidth);
|
||||
}
|
||||
} else {
|
||||
targetWidth = Math.Max(1, LineWidth) + _verticalMarginInline;
|
||||
targetHeight = FontUtils.ConvertEmToPixel(1, FontSize, TopLevel.GetTopLevel(this)?.RenderScaling ?? 1.0);
|
||||
if (!double.IsInfinity(availableSize.Height)) {
|
||||
targetHeight = Math.Max(availableSize.Height, targetHeight);
|
||||
}
|
||||
}
|
||||
|
||||
return new Size(targetWidth, targetHeight);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (Orientation == Orientation.Horizontal && _titleLabel!.IsVisible) {
|
||||
var titleRect = GetTitleRect(finalSize);
|
||||
_titleLabel.Arrange(titleRect);
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
private double GetTextPaddingInline()
|
||||
{
|
||||
return FontSize * _textPaddingInline;
|
||||
}
|
||||
|
||||
private Rect GetTitleRect(Size finalSize)
|
||||
{
|
||||
Rect titleRect = default;
|
||||
if (Orientation == Orientation.Horizontal && Title?.Length > 0) {
|
||||
// 线最小得占到 25 %,拍脑袋
|
||||
var lineMinWidth = finalSize.Width * SEPARATOR_LINE_MIN_PROPORTION;
|
||||
var titleWidth = _titleLabel!.DesiredSize.Width + 2;
|
||||
var remainWidth = finalSize.Width - titleWidth - GetTextPaddingInline() * 2;
|
||||
if (lineMinWidth > remainWidth) {
|
||||
// 字过多
|
||||
titleWidth = finalSize.Width - lineMinWidth;
|
||||
}
|
||||
|
||||
// 处理完成之后,字的宽度一定在 width 范围内
|
||||
// 计算位置
|
||||
if (TitlePosition == SeparatorTitlePosition.Left) {
|
||||
if (!double.IsNaN(OrientationMargin)) {
|
||||
_currentEdgeDistance = Math.Min((finalSize.Width - titleWidth) / 2, OrientationMargin);
|
||||
} else {
|
||||
_currentEdgeDistance = finalSize.Width * _orientationMarginPercent;
|
||||
}
|
||||
|
||||
titleRect = new Rect(new Point(_currentEdgeDistance + GetTextPaddingInline(), 0),
|
||||
new Size(titleWidth, finalSize.Height));
|
||||
var rightDelta = titleRect.Right - finalSize.Width;
|
||||
if (MathUtils.GreaterThan(rightDelta, 0)) {
|
||||
titleRect = titleRect.WithWidth(finalSize.Width - titleRect.Left);
|
||||
}
|
||||
} else if (TitlePosition == SeparatorTitlePosition.Right) {
|
||||
if (!double.IsNaN(OrientationMargin)) {
|
||||
_currentEdgeDistance = Math.Min((finalSize.Width - titleWidth) / 2, OrientationMargin);
|
||||
} else {
|
||||
_currentEdgeDistance = finalSize.Width * _orientationMarginPercent;
|
||||
}
|
||||
|
||||
titleRect = new Rect(
|
||||
new Point(finalSize.Width - _currentEdgeDistance - titleWidth - GetTextPaddingInline() * 2, 0),
|
||||
new Size(titleWidth, finalSize.Height));
|
||||
double leftDelta = titleRect.Left - 0;
|
||||
if (leftDelta < 0) {
|
||||
titleRect = titleRect.WithX(0);
|
||||
}
|
||||
} else {
|
||||
// 居中
|
||||
titleRect = new Rect(new Point((finalSize.Width - titleWidth) / 2, 0),
|
||||
new Size(titleWidth, finalSize.Height));
|
||||
}
|
||||
}
|
||||
|
||||
return titleRect;
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
using var state = context.PushRenderOptions(new RenderOptions
|
||||
{
|
||||
BitmapInterpolationMode = BitmapInterpolationMode.LowQuality,
|
||||
TextRenderingMode = TextRenderingMode.Alias
|
||||
});
|
||||
|
||||
var linePen = new Pen(LineColor, LineWidth);
|
||||
var controlRect = new Rect(new Point(0, 0), DesiredSize);
|
||||
if (IsDashedLine) {
|
||||
linePen.DashStyle = DashStyle.Dash;
|
||||
}
|
||||
|
||||
if (Orientation == Orientation.Horizontal) {
|
||||
var offsetY = controlRect.Height / 2.0;
|
||||
if (Title?.Length > 0) {
|
||||
// 画两个线段
|
||||
var titleRect = GetTitleRect(DesiredSize);
|
||||
if (TitlePosition == SeparatorTitlePosition.Left) {
|
||||
if (double.IsNaN(OrientationMargin)) {
|
||||
context.DrawLine(linePen, new Point(0, offsetY),
|
||||
new Point(titleRect.Left - GetTextPaddingInline(), offsetY));
|
||||
}
|
||||
|
||||
context.DrawLine(linePen, new Point(titleRect.Right + GetTextPaddingInline(), offsetY),
|
||||
new Point(controlRect.Right, offsetY));
|
||||
} else if (TitlePosition == SeparatorTitlePosition.Right) {
|
||||
context.DrawLine(linePen, new Point(0, offsetY),
|
||||
new Point(titleRect.Left - GetTextPaddingInline(), offsetY));
|
||||
if (double.IsNaN(OrientationMargin)) {
|
||||
context.DrawLine(linePen, new Point(titleRect.Right + GetTextPaddingInline(), offsetY),
|
||||
new Point(controlRect.Right, offsetY));
|
||||
}
|
||||
} else {
|
||||
context.DrawLine(linePen, new Point(0, offsetY),
|
||||
new Point(titleRect.Left - GetTextPaddingInline(), offsetY));
|
||||
context.DrawLine(linePen, new Point(titleRect.Right + GetTextPaddingInline(), offsetY),
|
||||
new Point(controlRect.Right, offsetY));
|
||||
}
|
||||
} else {
|
||||
context.DrawLine(linePen, new Point(0, offsetY), new Point(controlRect.Right, offsetY));
|
||||
}
|
||||
} else {
|
||||
var offsetX = controlRect.Width / 2.0;
|
||||
context.DrawLine(linePen, new Point(offsetX, 0), new Point(offsetX, controlRect.Bottom));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class VerticalSeparator : Separator
|
||||
|
@ -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.Data;
|
||||
using AtomUI.Icon;
|
||||
using AtomUI.Styling;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
public enum TagStatus {
|
||||
public enum TagStatus
|
||||
{
|
||||
// 状态
|
||||
Success,
|
||||
Info,
|
||||
@ -16,25 +21,51 @@ public enum TagStatus {
|
||||
Warning
|
||||
}
|
||||
|
||||
public partial class Tag : Label
|
||||
internal struct TagCalcColor
|
||||
{
|
||||
public Color LightColor { get; set; } // 1 号色
|
||||
public Color LightBorderColor { get; set; } // 3 号色
|
||||
public Color DarkColor { get; set; } // 6 号色
|
||||
public Color TextColor { get; set; } // 7 号色
|
||||
}
|
||||
|
||||
internal struct TagStatusCalcColor
|
||||
{
|
||||
public Color Color { get; set; }
|
||||
public Color Background { get; set; }
|
||||
public Color BorderColor { get; set; }
|
||||
}
|
||||
|
||||
public partial class Tag : Label, IControlCustomStyle
|
||||
{
|
||||
private bool _initialized = false;
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private ControlStyleState _styleState;
|
||||
private bool _isPresetColorTag = false;
|
||||
private bool _hasColorSet = false;
|
||||
private static Dictionary<PresetColorType, TagCalcColor> _presetColorMap;
|
||||
private static Dictionary<TagStatus, TagStatusCalcColor> _statusColorMap;
|
||||
private Panel? _layoutPanel;
|
||||
private TextBlock? _textBlock;
|
||||
|
||||
static Tag()
|
||||
{
|
||||
_presetColorMap = new Dictionary<PresetColorType, TagCalcColor>();
|
||||
_statusColorMap = new Dictionary<TagStatus, TagStatusCalcColor>();
|
||||
AffectsMeasure<Tag>(BorderedProperty,
|
||||
IconProperty,
|
||||
ClosableProperty,
|
||||
PaddingXXSTokenProperty,
|
||||
TagCloseIconSizeTokenProperty,
|
||||
TagIconSizeTokenProperty);
|
||||
IconProperty,
|
||||
ClosableProperty,
|
||||
PaddingXXSTokenProperty,
|
||||
TagCloseIconSizeTokenProperty,
|
||||
TagIconSizeTokenProperty);
|
||||
AffectsRender<Tag>(ClosableProperty,
|
||||
DefaultBgTokenProperty,
|
||||
DefaultForegroundTokenProperty,
|
||||
TagBorderlessBgTokenProperty,
|
||||
ColorTextLightSolidTokenProperty);
|
||||
DefaultBgTokenProperty,
|
||||
DefaultForegroundTokenProperty,
|
||||
TagBorderlessBgTokenProperty,
|
||||
ColorTextLightSolidTokenProperty);
|
||||
}
|
||||
|
||||
|
||||
public Tag()
|
||||
{
|
||||
_customStyle = this;
|
||||
@ -43,7 +74,7 @@ public partial class Tag : Label
|
||||
SetupStatusColorMap();
|
||||
_customStyle.InitOnConstruct();
|
||||
}
|
||||
|
||||
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
@ -52,13 +83,13 @@ public partial class Tag : Label
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_customStyle.ApplyRenderScalingAwareStyleConfig();
|
||||
}
|
||||
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
var textBlock = _textBlock!;
|
||||
@ -95,18 +126,20 @@ public partial class Tag : Label
|
||||
if (Icon is not null) {
|
||||
Icon.Arrange(IconRect(finalSize));
|
||||
}
|
||||
|
||||
if (CloseIcon is not null) {
|
||||
CloseIcon.Arrange(CloseIconRect(finalSize));
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
_customStyle.HandlePropertyChangedForStyle(e);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnPointerMoved(PointerEventArgs e)
|
||||
{
|
||||
base.OnPointerMoved(e);
|
||||
@ -119,4 +152,290 @@ public partial class Tag : Label
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
|
||||
void IControlCustomStyle.InitOnConstruct()
|
||||
{
|
||||
_layoutPanel = new Panel();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
_customStyle.CollectStyleState();
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
_customStyle.ApplyVariableStyleConfig();
|
||||
if (TagColor is not null) {
|
||||
SetupTagColorInfo(TagColor);
|
||||
}
|
||||
|
||||
var tagContent = string.Empty;
|
||||
if (Content is string) {
|
||||
// 只接受字符串
|
||||
tagContent = Content as string;
|
||||
}
|
||||
|
||||
_textBlock = new TextBlock
|
||||
{
|
||||
Text = tagContent,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
_layoutPanel?.Children.Add(_textBlock);
|
||||
Content = _layoutPanel;
|
||||
SetupTagIcon();
|
||||
SetupTagClosable();
|
||||
_customStyle.SetupTransitions();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(DefaultBgTokenProperty, TagResourceKey.DefaultBg);
|
||||
_controlTokenBinder.AddControlBinding(DefaultForegroundTokenProperty, TagResourceKey.DefaultColor);
|
||||
_controlTokenBinder.AddControlBinding(FontSizeProperty, TagResourceKey.TagFontSize);
|
||||
_controlTokenBinder.AddControlBinding(TagLineHeightTokenProperty, TagResourceKey.TagLineHeight);
|
||||
_controlTokenBinder.AddControlBinding(TagIconSizeTokenProperty, TagResourceKey.TagIconSize);
|
||||
_controlTokenBinder.AddControlBinding(TagCloseIconSizeTokenProperty, TagResourceKey.TagCloseIconSize);
|
||||
_controlTokenBinder.AddControlBinding(PaddingXXSTokenProperty, GlobalResourceKey.PaddingXXS);
|
||||
_controlTokenBinder.AddControlBinding(PaddingProperty, TagResourceKey.TagPadding);
|
||||
_controlTokenBinder.AddControlBinding(TagBorderlessBgTokenProperty, TagResourceKey.TagBorderlessBg);
|
||||
_controlTokenBinder.AddControlBinding(BorderBrushProperty, GlobalResourceKey.ColorBorder);
|
||||
_controlTokenBinder.AddControlBinding(CornerRadiusProperty, GlobalResourceKey.BorderRadiusSM);
|
||||
_controlTokenBinder.AddControlBinding(ColorTextLightSolidTokenProperty, GlobalResourceKey.ColorTextLightSolid);
|
||||
|
||||
Background = _defaultBackground;
|
||||
Foreground = _defaultForeground;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig()
|
||||
{
|
||||
_controlTokenBinder.AddControlBinding(BorderThicknessProperty, "BorderThickness", BindingPriority.Style,
|
||||
new RenderScaleAwareThicknessConfigure(this, thickness =>
|
||||
{
|
||||
if (!Bordered) {
|
||||
return new Thickness(0);
|
||||
}
|
||||
|
||||
return thickness;
|
||||
}));
|
||||
}
|
||||
|
||||
void IControlCustomStyle.CollectStyleState()
|
||||
{
|
||||
StyleUtils.InitCommonState(this, ref _styleState);
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == ClosableProperty && _initialized) {
|
||||
SetupTagClosable();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupPresetColorMap()
|
||||
{
|
||||
if (_presetColorMap.Count == 0) {
|
||||
// TODO 根据当前的主题风格设置,是否需要根据风格不一样进行动态调整呢?
|
||||
var activatedTheme = ThemeManager.Current.ActivatedTheme;
|
||||
var globalToken = activatedTheme?.GlobalToken;
|
||||
if (globalToken == null) {
|
||||
// 是否需要输出日志
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var entry in PresetPrimaryColor.AllColorTypes()) {
|
||||
var colorMap = globalToken.GetColorPalette(entry)!;
|
||||
var calcColor = new TagCalcColor()
|
||||
{
|
||||
LightColor = colorMap.Color1,
|
||||
LightBorderColor = colorMap.Color3,
|
||||
DarkColor = colorMap.Color6,
|
||||
TextColor = colorMap.Color7
|
||||
};
|
||||
_presetColorMap.Add(entry.Type, calcColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupStatusColorMap()
|
||||
{
|
||||
if (_statusColorMap.Count == 0) {
|
||||
var activatedTheme = ThemeManager.Current.ActivatedTheme;
|
||||
var globalToken = activatedTheme?.GlobalToken;
|
||||
if (globalToken == null) {
|
||||
// 是否需要输出日志
|
||||
return;
|
||||
}
|
||||
|
||||
var colorToken = globalToken.ColorToken;
|
||||
var colorSuccessToken = colorToken.ColorSuccessToken;
|
||||
var colorInfoToken = colorToken.ColorInfoToken;
|
||||
var colorWarningToken = colorToken.ColorWarningToken;
|
||||
var colorErrorToken = colorToken.ColorErrorToken;
|
||||
|
||||
_statusColorMap.Add(TagStatus.Success, new TagStatusCalcColor
|
||||
{
|
||||
Color = colorSuccessToken.ColorSuccess,
|
||||
Background = colorSuccessToken.ColorSuccessBg,
|
||||
BorderColor = colorSuccessToken.ColorSuccessBorder
|
||||
});
|
||||
|
||||
_statusColorMap.Add(TagStatus.Info, new TagStatusCalcColor
|
||||
{
|
||||
Color = colorInfoToken.ColorInfo,
|
||||
Background = colorInfoToken.ColorInfoBg,
|
||||
BorderColor = colorInfoToken.ColorInfoBorder
|
||||
});
|
||||
|
||||
_statusColorMap.Add(TagStatus.Warning, new TagStatusCalcColor
|
||||
{
|
||||
Color = colorWarningToken.ColorWarning,
|
||||
Background = colorWarningToken.ColorWarningBg,
|
||||
BorderColor = colorWarningToken.ColorWarningBorder
|
||||
});
|
||||
|
||||
_statusColorMap.Add(TagStatus.Error, new TagStatusCalcColor
|
||||
{
|
||||
Color = colorErrorToken.ColorError,
|
||||
Background = colorErrorToken.ColorErrorBg,
|
||||
BorderColor = colorErrorToken.ColorErrorBorder
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private Rect IconRect(Size controlSize)
|
||||
{
|
||||
var bevelRect = BevelRect(controlSize);
|
||||
var offsetX = bevelRect.Left + Padding.Left;
|
||||
var offsetY = bevelRect.Y + (bevelRect.Height - _tagIconSize) / 2;
|
||||
return new Rect(offsetX, offsetY, _tagIconSize, _tagIconSize);
|
||||
}
|
||||
|
||||
private Rect CloseIconRect(Size controlSize)
|
||||
{
|
||||
var bevelRect = BevelRect(controlSize);
|
||||
var offsetX = bevelRect.Right - Padding.Right - _tagCloseIconSize;
|
||||
var offsetY = bevelRect.Y + (bevelRect.Height - _tagCloseIconSize) / 2;
|
||||
return new Rect(offsetX, offsetY, _tagCloseIconSize, _tagCloseIconSize);
|
||||
}
|
||||
|
||||
private Rect BevelRect(Size controlSize)
|
||||
{
|
||||
var targetRect = new Rect(0, 0, controlSize.Width, controlSize.Height);
|
||||
return targetRect.Deflate(BorderThickness);
|
||||
}
|
||||
|
||||
private Rect TextRect(Size controlSize)
|
||||
{
|
||||
var bevelRect = BevelRect(controlSize);
|
||||
var offsetX = bevelRect.Left + Padding.Left;
|
||||
if (Icon is not null) {
|
||||
offsetX += _tagIconSize + _paddingXXS;
|
||||
}
|
||||
|
||||
// 这个时候已经算好了
|
||||
var textSize = _textBlock!.DesiredSize;
|
||||
var offsetY = bevelRect.Y + (bevelRect.Height - textSize.Height) / 2;
|
||||
return new Rect(offsetX, offsetY, textSize.Width, textSize.Height);
|
||||
}
|
||||
|
||||
private void SetupTagColorInfo(string colorStr)
|
||||
{
|
||||
_isPresetColorTag = false;
|
||||
_hasColorSet = false;
|
||||
colorStr = colorStr.Trim().ToLower();
|
||||
|
||||
Background = _defaultBackground;
|
||||
|
||||
foreach (var entry in _presetColorMap) {
|
||||
if (entry.Key.ToString().ToLower() == colorStr) {
|
||||
var colorInfo = _presetColorMap[entry.Key];
|
||||
Foreground = new SolidColorBrush(colorInfo.TextColor);
|
||||
BorderBrush = new SolidColorBrush(colorInfo.LightBorderColor);
|
||||
Background = new SolidColorBrush(colorInfo.LightColor);
|
||||
_isPresetColorTag = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entry in _statusColorMap) {
|
||||
if (entry.Key.ToString().ToLower() == colorStr) {
|
||||
var colorInfo = _statusColorMap[entry.Key];
|
||||
Foreground = new SolidColorBrush(colorInfo.Color);
|
||||
BorderBrush = new SolidColorBrush(colorInfo.BorderColor);
|
||||
Background = new SolidColorBrush(colorInfo.Background);
|
||||
_isPresetColorTag = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Color.TryParse(colorStr, out Color color)) {
|
||||
Bordered = false;
|
||||
Foreground = _colorTextLightSolid;
|
||||
_hasColorSet = true;
|
||||
Background = new SolidColorBrush(color);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTagClosable()
|
||||
{
|
||||
if (Closable) {
|
||||
if (CloseIcon is null) {
|
||||
CloseIcon = new PathIcon()
|
||||
{
|
||||
Kind = "CloseOutlined",
|
||||
Width = _tagCloseIconSize,
|
||||
Height = _tagCloseIconSize,
|
||||
};
|
||||
|
||||
if (_hasColorSet && !_isPresetColorTag) {
|
||||
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty,
|
||||
GlobalResourceKey.ColorTextLightSolid);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty,
|
||||
GlobalResourceKey.ColorIcon);
|
||||
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.ActiveFilledBrushProperty,
|
||||
GlobalResourceKey.ColorIconHover);
|
||||
}
|
||||
} else {
|
||||
CloseIcon.Width = _tagCloseIconSize;
|
||||
CloseIcon.Height = _tagCloseIconSize;
|
||||
if (CloseIcon.ThemeType != IconThemeType.TwoTone) {
|
||||
if (_hasColorSet && !_isPresetColorTag) {
|
||||
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty,
|
||||
GlobalResourceKey.ColorTextLightSolid);
|
||||
} else {
|
||||
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.NormalFillBrushProperty,
|
||||
GlobalResourceKey.ColorIcon);
|
||||
_controlTokenBinder.AddControlBinding(CloseIcon, PathIcon.ActiveFilledBrushProperty,
|
||||
GlobalResourceKey.ColorIconHover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseIcon.Cursor = new Cursor(StandardCursorType.Hand);
|
||||
_layoutPanel?.Children.Add(CloseIcon);
|
||||
} else {
|
||||
if (CloseIcon != null) {
|
||||
_layoutPanel?.Children.Remove(CloseIcon);
|
||||
CloseIcon = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTagIcon()
|
||||
{
|
||||
if (Icon is not null) {
|
||||
Icon.Width = _tagIconSize;
|
||||
Icon.Height = _tagIconSize;
|
||||
_layoutPanel?.Children.Insert(0, Icon);
|
||||
if (_hasColorSet) {
|
||||
_controlTokenBinder.AddControlBinding(Icon, PathIcon.NormalFillBrushProperty,
|
||||
GlobalResourceKey.ColorTextLightSolid);
|
||||
} else if (_isPresetColorTag) {
|
||||
Icon.NormalFilledBrush = Foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -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.Reflection;
|
||||
using AtomUI.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Primitives.PopupPositioning;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
@ -22,12 +24,18 @@ namespace AtomUI.Controls;
|
||||
using AvaloniaWin = Avalonia.Controls.Window;
|
||||
|
||||
[PseudoClasses(":open")]
|
||||
public partial class ToolTip : StyledControl, IShadowMaskInfoProvider
|
||||
public partial class ToolTip : StyledControl,
|
||||
IShadowMaskInfoProvider,
|
||||
IControlCustomStyle
|
||||
{
|
||||
private Popup? _popup;
|
||||
private Action<IPopupHost?>? _popupHostChangedHandler;
|
||||
private AvaloniaWin? _currentAnchorWindow;
|
||||
private PopupPositionInfo? _popupPositionInfo; // 这个信息在隐藏动画的时候会用到
|
||||
private bool _initialized = false;
|
||||
private IControlCustomStyle _customStyle;
|
||||
private ControlTokenBinder _controlTokenBinder;
|
||||
private ArrowDecoratedBox? _arrowDecoratedBox;
|
||||
|
||||
// 当鼠标移走了,但是打开动画还没完成,我们需要记录下来这个信号
|
||||
internal bool RequestCloseWhereAnimationCompleted { get; set; } = false;
|
||||
@ -660,4 +668,101 @@ public partial class ToolTip : StyledControl, IShadowMaskInfoProvider
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
#region IControlCustomStyle 实现
|
||||
void IControlCustomStyle.SetupUi()
|
||||
{
|
||||
Background = new SolidColorBrush(Colors.Transparent);
|
||||
_arrowDecoratedBox = new ArrowDecoratedBox();
|
||||
if (Content is string text) {
|
||||
_arrowDecoratedBox.Child = new TextBlock
|
||||
{
|
||||
Text = text,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
};
|
||||
} else if (Content is Control control) {
|
||||
_arrowDecoratedBox.Child = control;
|
||||
}
|
||||
|
||||
((ISetLogicalParent)_arrowDecoratedBox).SetParent(this);
|
||||
VisualChildren.Add(_arrowDecoratedBox);
|
||||
_customStyle.ApplyFixedStyleConfig();
|
||||
}
|
||||
|
||||
void IControlCustomStyle.ApplyFixedStyleConfig()
|
||||
{
|
||||
if (_arrowDecoratedBox is not null) {
|
||||
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, FontSizeProperty, GlobalResourceKey.FontSize);
|
||||
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, MaxWidthProperty, ToolTipResourceKey.ToolTipMaxWidth);
|
||||
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, BackgroundProperty, ToolTipResourceKey.ToolTipBackground);
|
||||
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, ForegroundProperty, ToolTipResourceKey.ToolTipColor);
|
||||
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, MinHeightProperty, GlobalResourceKey.ControlHeight);
|
||||
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, PaddingProperty, ToolTipResourceKey.ToolTipPadding);
|
||||
_controlTokenBinder.AddControlBinding(MarginXXSTokenProperty, GlobalResourceKey.MarginXXS);
|
||||
_controlTokenBinder.AddControlBinding(MotionDurationTokenProperty, GlobalResourceKey.MotionDurationMid);
|
||||
_controlTokenBinder.AddControlBinding(ShadowsTokenProperty, GlobalResourceKey.BoxShadowsSecondary);
|
||||
_controlTokenBinder.AddControlBinding(_arrowDecoratedBox, ArrowDecoratedBox.CornerRadiusProperty, ToolTipResourceKey.BorderRadiusOuter);
|
||||
// TODO 生命周期一样还需要管理起来吗?
|
||||
BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, _arrowDecoratedBox, ArrowDecoratedBox.IsShowArrowProperty);
|
||||
}
|
||||
}
|
||||
|
||||
public CornerRadius GetMaskCornerRadius()
|
||||
{
|
||||
return _arrowDecoratedBox!.GetMaskCornerRadius();
|
||||
}
|
||||
|
||||
public Rect GetMaskBounds()
|
||||
{
|
||||
return _arrowDecoratedBox!.GetMaskBounds();
|
||||
}
|
||||
|
||||
private void SetupArrowPosition(PlacementMode placement, PopupAnchor? anchor = null, PopupGravity? gravity = null)
|
||||
{
|
||||
var arrowPosition = PopupUtils.CalculateArrowPosition(placement, anchor, gravity);
|
||||
if (_arrowDecoratedBox is not null && arrowPosition is not null) {
|
||||
_arrowDecoratedBox.ArrowPosition = arrowPosition.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupPointCenterOffset(Control placementTarget, PlacementMode placement, PopupAnchor? anchor = null, PopupGravity? gravity = null)
|
||||
{
|
||||
var offset =
|
||||
CalculatePopupPositionDelta(placementTarget, placement, anchor, gravity);
|
||||
_popup!.HorizontalOffset += offset.X;
|
||||
_popup.VerticalOffset += offset.Y;
|
||||
}
|
||||
|
||||
private Point CalculatePopupPositionDelta(Control anchorTarget, PlacementMode placement, PopupAnchor? anchor = null,
|
||||
PopupGravity? gravity = null)
|
||||
{
|
||||
var offsetX = 0d;
|
||||
var offsetY = 0d;
|
||||
if (GetIsShowArrow(anchorTarget) && GetIsPointAtCenter(anchorTarget)) {
|
||||
if (PopupUtils.CanEnabledArrow(placement, anchor, gravity)) {
|
||||
var arrowVertexPoint = _arrowDecoratedBox!.ArrowVertexPoint;
|
||||
var anchorSize = anchorTarget.Bounds.Size;
|
||||
var centerX = anchorSize.Width / 2;
|
||||
var centerY = anchorSize.Height / 2;
|
||||
// 这里计算不需要全局坐标
|
||||
if (placement == PlacementMode.TopEdgeAlignedLeft ||
|
||||
placement == PlacementMode.BottomEdgeAlignedLeft) {
|
||||
offsetX += centerX - arrowVertexPoint.Item1;
|
||||
} else if (placement == PlacementMode.TopEdgeAlignedRight ||
|
||||
placement == PlacementMode.BottomEdgeAlignedRight) {
|
||||
offsetX -= centerX - arrowVertexPoint.Item2;
|
||||
} else if (placement == PlacementMode.RightEdgeAlignedTop ||
|
||||
placement == PlacementMode.LeftEdgeAlignedTop) {
|
||||
offsetY += centerY - arrowVertexPoint.Item1;
|
||||
} else if (placement == PlacementMode.RightEdgeAlignedBottom ||
|
||||
placement == PlacementMode.LeftEdgeAlignedBottom) {
|
||||
offsetY -= centerY - arrowVertexPoint.Item2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Point(offsetX, offsetY);
|
||||
}
|
||||
#endregion
|
||||
}
|
@ -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