Refactor RadioButton

This commit is contained in:
polarboy 2024-09-28 14:25:06 +08:00
parent c0db4ba822
commit d0f71b4cc5
7 changed files with 358 additions and 312 deletions

View File

@ -48,8 +48,8 @@ internal class CheckBoxIndicator : Control, IWaveAdornerInfoProvider
public bool? IsChecked
{
get => GetValue(ToggleButton.IsCheckedProperty);
set => SetValue(ToggleButton.IsCheckedProperty, value);
get => GetValue(IsCheckedProperty);
set => SetValue(IsCheckedProperty, value);
}
public double Size

View File

@ -72,11 +72,12 @@ internal class CheckBoxTheme : BaseControlTheme
var commonStyle = new Style(selector => selector.Nesting());
commonStyle.Add(CheckBox.CursorProperty, new Cursor(StandardCursorType.Hand));
commonStyle.Add(CheckBox.HorizontalAlignmentProperty, HorizontalAlignment.Left);
Add(commonStyle);
var contentPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ContentPresenterPart));
contentPresenterStyle.Add(ContentPresenter.MarginProperty, CheckBoxTokenResourceKey.TextMargin);
Add(contentPresenterStyle);
commonStyle.Add(contentPresenterStyle);
Add(commonStyle);
var disableStyle =
new Style(selector => selector.Nesting().PropertyEquals(InputElement.IsEnabledProperty, false));

View File

@ -517,6 +517,7 @@ namespace AtomUI.Theme.Styling
public static readonly TokenResourceKey RadioColor = new TokenResourceKey("RadioButton.RadioColor", "AtomUI.Token");
public static readonly TokenResourceKey RadioBgColor = new TokenResourceKey("RadioButton.RadioBgColor", "AtomUI.Token");
public static readonly TokenResourceKey DotPadding = new TokenResourceKey("RadioButton.DotPadding", "AtomUI.Token");
public static readonly TokenResourceKey TextMargin = new TokenResourceKey("RadioButton.TextMargin", "AtomUI.Token");
}
public static class SegmentedTokenResourceKey

View File

@ -1,17 +1,4 @@
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Rendering;
namespace AtomUI.Controls;
@ -19,273 +6,10 @@ namespace AtomUI.Controls;
using AvaloniaRadioButton = Avalonia.Controls.RadioButton;
public class RadioButton : AvaloniaRadioButton,
ICustomHitTest,
IWaveAdornerInfoProvider
ICustomHitTest
{
#region
internal static readonly StyledProperty<double> RadioSizeProperty =
AvaloniaProperty.Register<Button, double>(nameof(RadioSize));
internal static readonly StyledProperty<double> PaddingInlineProperty =
AvaloniaProperty.Register<Button, double>(nameof(PaddingInline));
internal static readonly StyledProperty<IBrush?> RadioBorderBrushProperty =
AvaloniaProperty.Register<Button, IBrush?>(nameof(RadioBorderBrush));
internal static readonly StyledProperty<IBrush?> RadioInnerBackgroundProperty =
AvaloniaProperty.Register<Button, IBrush?>(nameof(RadioInnerBackground));
internal static readonly StyledProperty<IBrush?> RadioBackgroundProperty =
AvaloniaProperty.Register<Button, IBrush?>(nameof(RadioBackground));
internal static readonly StyledProperty<Thickness> RadioBorderThicknessProperty =
AvaloniaProperty.Register<Button, Thickness>(nameof(RadioBorderThickness));
internal static readonly StyledProperty<double> RadioDotEffectSizeProperty =
AvaloniaProperty.Register<Button, double>(nameof(RadioDotEffectSize));
internal static readonly StyledProperty<double> DotSizeValueProperty =
AvaloniaProperty.Register<RadioButton, double>(
nameof(DotSizeValue));
internal static readonly StyledProperty<double> DotPaddingProperty =
AvaloniaProperty.Register<RadioButton, double>(
nameof(DotPadding));
internal double RadioSize
{
get => GetValue(RadioSizeProperty);
set => SetValue(RadioSizeProperty, value);
}
internal double PaddingInline
{
get => GetValue(PaddingInlineProperty);
set => SetValue(PaddingInlineProperty, value);
}
internal IBrush? RadioBorderBrush
{
get => GetValue(RadioBorderBrushProperty);
set => SetValue(RadioBorderBrushProperty, value);
}
internal IBrush? RadioInnerBackground
{
get => GetValue(RadioInnerBackgroundProperty);
set => SetValue(RadioInnerBackgroundProperty, value);
}
internal IBrush? RadioBackground
{
get => GetValue(RadioBackgroundProperty);
set => SetValue(RadioBackgroundProperty, value);
}
internal Thickness RadioBorderThickness
{
get => GetValue(RadioBorderThicknessProperty);
set => SetValue(RadioBorderThicknessProperty, value);
}
internal double RadioDotEffectSize
{
get => GetValue(RadioDotEffectSizeProperty);
set => SetValue(RadioDotEffectSizeProperty, value);
}
internal double DotSizeValue
{
get => GetValue(DotSizeValueProperty);
set => SetValue(DotSizeValueProperty, value);
}
internal double DotPadding
{
get => GetValue(DotPaddingProperty);
set => SetValue(DotPaddingProperty, value);
}
#endregion
private IPen? _cachedPen;
private ControlStyleState _styleState;
static RadioButton()
{
AffectsRender<RadioButton>(
RadioBorderBrushProperty,
RadioInnerBackgroundProperty,
RadioBackgroundProperty,
RadioBorderThicknessProperty,
RadioDotEffectSizeProperty);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
HandlePropertyChangedForStyle(e);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
HandleTemplateApplied(e.NameScope);
}
private void HandleTemplateApplied(INameScope scope)
{
TokenResourceBinder.CreateGlobalResourceBinding(this, RadioBorderThicknessProperty,
GlobalTokenResourceKey.BorderThickness, BindingPriority.Template,
new RenderScaleAwareThicknessConfigure(this));
Cursor = new Cursor(StandardCursorType.Hand);
CollectStyleState();
RadioDotEffectSize = CalculateDotSize(IsEnabled, IsChecked.HasValue && IsChecked.Value);
SetupTransitions();
}
protected override Size MeasureOverride(Size availableSize)
{
var size = base.MeasureOverride(availableSize);
var targetWidth = size.Width + RadioSize + PaddingInline;
var targetHeight = Math.Max(size.Height, RadioSize);
return new Size(targetWidth, targetHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
var arrangeRect = RadioTextRect();
var visualChildren = VisualChildren;
var visualCount = visualChildren.Count;
for (var i = 0; i < visualCount; i++)
{
var visual = visualChildren[i];
if (visual is Layoutable layoutable)
{
layoutable.Arrange(arrangeRect);
}
}
return finalSize;
}
public sealed override void Render(DrawingContext context)
{
var radioRect = RadioRect();
var penWidth = RadioBorderThickness.Top;
PenUtils.TryModifyOrCreate(ref _cachedPen, RadioBorderBrush, RadioBorderThickness.Top);
context.DrawEllipse(RadioBackground, _cachedPen, radioRect.Deflate(penWidth / 2));
if (IsChecked.HasValue && IsChecked.Value)
{
var dotDiameter = RadioDotEffectSize / 2;
context.DrawEllipse(RadioInnerBackground, null, radioRect.Center, dotDiameter, dotDiameter);
}
}
public bool HitTest(Point point)
{
return true;
}
private void CollectStyleState()
{
ControlStateUtils.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 - DotPadding * 2;
}
}
else
{
targetValue = DotSizeValue * 0.6;
}
return targetValue;
}
private void SetupTransitions()
{
Transitions = new Transitions
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBorderBrushProperty),
AnimationUtils.CreateTransition<DoubleTransition>(RadioDotEffectSizeProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBackgroundProperty,
GlobalTokenResourceKey.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);
}
private void HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == IsPointerOverProperty ||
e.Property == IsCheckedProperty ||
e.Property == IsEnabledProperty)
{
CollectStyleState();
if (VisualRoot is not null)
{
RadioDotEffectSize = CalculateDotSize(IsEnabled, IsChecked.HasValue && IsChecked.Value);
}
if (e.Property == IsCheckedProperty &&
_styleState.HasFlag(ControlStyleState.Enabled) &&
_styleState.HasFlag(ControlStyleState.On))
{
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.CircleWave);
}
}
}
public Rect WaveGeometry()
{
return RadioRect();
}
public CornerRadius WaveBorderRadius()
{
return new CornerRadius(RadioSize / 2);
}
}

View File

@ -1,7 +1,13 @@
using AtomUI.Theme;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Styling;
namespace AtomUI.Controls;
@ -9,55 +15,143 @@ namespace AtomUI.Controls;
[ControlThemeProvider]
internal class RadioButtonTheme : BaseControlTheme
{
internal const string FramePart = "PART_Frame";
internal const string IndicatorPart = "PART_Indicator";
internal const string ContentPresenterPart = "PART_ContentPresenter";
public RadioButtonTheme()
: base(typeof(RadioButton))
{
}
protected override void BuildStyles()
protected override IControlTemplate BuildControlTemplate()
{
this.Add(RadioButton.RadioSizeProperty, RadioButtonTokenResourceKey.RadioSize);
this.Add(RadioButton.PaddingInlineProperty, GlobalTokenResourceKey.PaddingXS);
this.Add(RadioButton.RadioBorderBrushProperty, GlobalTokenResourceKey.ColorBorder);
this.Add(RadioButton.DotSizeValueProperty, RadioButtonTokenResourceKey.DotSize);
this.Add(RadioButton.DotPaddingProperty, RadioButtonTokenResourceKey.DotPadding);
BuildEnabledStyle();
BuildDisabledStyle();
return new FuncControlTemplate<RadioButton>((radioButton, scope) =>
{
var frame = new Border
{
Name = FramePart
};
CreateTemplateParentBinding(frame, Border.BackgroundProperty, RadioButton.BackgroundProperty);
CreateTemplateParentBinding(frame, Border.BorderBrushProperty, RadioButton.BorderBrushProperty);
CreateTemplateParentBinding(frame, Border.BorderThicknessProperty, RadioButton.BorderThicknessProperty);
CreateTemplateParentBinding(frame, Border.CornerRadiusProperty, RadioButton.CornerRadiusProperty);
var layout = new DockPanel
{
LastChildFill = true
};
var indicator = new RadioIndicator()
{
Name = IndicatorPart
};
CreateTemplateParentBinding(indicator, CheckBoxIndicator.IsCheckedProperty, RadioButton.IsCheckedProperty);
layout.Children.Add(indicator);
var contentPresenter = new ContentPresenter()
{
Name = ContentPresenterPart,
RecognizesAccessKey = true
};
CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentTemplateProperty,
RadioButton.ContentTemplateProperty);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentProperty,
RadioButton.ContentProperty);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.FontSizeProperty,
RadioButton.FontSizeProperty);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.IsVisibleProperty,
RadioButton.ContentProperty,
BindingMode.Default, ObjectConverters.IsNotNull);
layout.Children.Add(contentPresenter);
frame.Child = layout;
return frame;
});
}
private void BuildDisabledStyle()
protected override void BuildStyles()
{
var commonStyle = new Style(selector => selector.Nesting());
commonStyle.Add(RadioButton.CursorProperty, new Cursor(StandardCursorType.Hand));
commonStyle.Add(RadioButton.HorizontalAlignmentProperty, HorizontalAlignment.Left);
var disableStyle =
new Style(selector => selector.Nesting().PropertyEquals(InputElement.IsEnabledProperty, false));
disableStyle.Add(TemplatedControl.ForegroundProperty, GlobalTokenResourceKey.ColorTextDisabled);
disableStyle.Add(RadioButton.RadioBackgroundProperty, GlobalTokenResourceKey.ColorBgContainerDisabled);
disableStyle.Add(RadioButton.RadioInnerBackgroundProperty, RadioButtonTokenResourceKey.DotColorDisabled);
Add(disableStyle);
commonStyle.Add(disableStyle);
var contentPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ContentPresenterPart));
contentPresenterStyle.Add(ContentPresenter.MarginProperty, CheckBoxTokenResourceKey.TextMargin);
commonStyle.Add(contentPresenterStyle);
Add(commonStyle);
BuildIndicatorStyle();
}
private void BuildEnabledStyle()
private void BuildIndicatorStyle()
{
{
var indicatorStyle = new Style(selector => selector.Nesting().Template().Name(IndicatorPart));
indicatorStyle.Add(RadioIndicator.RadioSizeProperty, RadioButtonTokenResourceKey.RadioSize);
indicatorStyle.Add(RadioIndicator.WidthProperty, RadioButtonTokenResourceKey.RadioSize);
indicatorStyle.Add(RadioIndicator.HeightProperty, RadioButtonTokenResourceKey.RadioSize);
indicatorStyle.Add(RadioIndicator.PaddingInlineProperty, GlobalTokenResourceKey.PaddingXS);
indicatorStyle.Add(RadioIndicator.DotSizeValueProperty, RadioButtonTokenResourceKey.DotSize);
indicatorStyle.Add(RadioIndicator.DotPaddingProperty, RadioButtonTokenResourceKey.DotPadding);
Add(indicatorStyle);
}
var disableStyle =
new Style(selector => selector.Nesting().PropertyEquals(InputElement.IsEnabledProperty, false));
{
var indicatorStyle = new Style(selector => selector.Nesting().Template().Name(IndicatorPart));
indicatorStyle.Add(RadioIndicator.RadioBackgroundProperty, GlobalTokenResourceKey.ColorBgContainerDisabled);
indicatorStyle.Add(RadioIndicator.RadioBorderBrushProperty, GlobalTokenResourceKey.ColorBorder);
indicatorStyle.Add(RadioIndicator.RadioInnerBackgroundProperty,
RadioButtonTokenResourceKey.DotColorDisabled);
disableStyle.Add(indicatorStyle);
}
Add(disableStyle);
var enabledStyle =
new Style(selector => selector.Nesting().PropertyEquals(InputElement.IsEnabledProperty, true));
enabledStyle.Add(RadioButton.RadioInnerBackgroundProperty, RadioButtonTokenResourceKey.RadioColor);
{
{
var indicatorStyle = new Style(selector => selector.Nesting().Template().Name(IndicatorPart));
indicatorStyle.Add(RadioIndicator.RadioInnerBackgroundProperty, RadioButtonTokenResourceKey.RadioColor);
indicatorStyle.Add(RadioIndicator.RadioBorderBrushProperty, GlobalTokenResourceKey.ColorBorder);
enabledStyle.Add(indicatorStyle);
}
// 选中
var checkedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Checked));
checkedStyle.Add(RadioButton.RadioBorderBrushProperty, GlobalTokenResourceKey.ColorPrimary);
checkedStyle.Add(RadioButton.RadioBackgroundProperty, GlobalTokenResourceKey.ColorPrimary);
enabledStyle.Add(checkedStyle);
// 没选中
var unCheckedStyle =
new Style(selector => selector.Nesting().Not(x => x.Nesting().Class(StdPseudoClass.Checked)));
unCheckedStyle.Add(RadioButton.RadioBackgroundProperty, GlobalTokenResourceKey.ColorBgContainer);
var unCheckedHoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.PointerOver));
unCheckedHoverStyle.Add(RadioButton.RadioBorderBrushProperty, GlobalTokenResourceKey.ColorPrimary);
unCheckedStyle.Add(unCheckedHoverStyle);
enabledStyle.Add(unCheckedStyle);
var checkedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Checked));
{
var indicatorStyle = new Style(selector => selector.Nesting().Template().Name(IndicatorPart));
indicatorStyle.Add(RadioIndicator.RadioBorderBrushProperty, GlobalTokenResourceKey.ColorPrimary);
indicatorStyle.Add(RadioIndicator.RadioBackgroundProperty, GlobalTokenResourceKey.ColorPrimary);
checkedStyle.Add(indicatorStyle);
}
enabledStyle.Add(checkedStyle);
var unCheckedStyle =
new Style(selector => selector.Nesting().Not(x => x.Nesting().Class(StdPseudoClass.Checked)));
{
var indicatorStyle = new Style(selector => selector.Nesting().Template().Name(IndicatorPart));
indicatorStyle.Add(RadioIndicator.RadioBackgroundProperty, GlobalTokenResourceKey.ColorBgContainer);
unCheckedStyle.Add(indicatorStyle);
}
var unCheckedHoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.PointerOver));
{
var indicatorStyle = new Style(selector => selector.Nesting().Template().Name(IndicatorPart));
indicatorStyle.Add(RadioIndicator.RadioBorderBrushProperty, GlobalTokenResourceKey.ColorPrimary);
unCheckedHoverStyle.Add(indicatorStyle);
}
unCheckedStyle.Add(unCheckedHoverStyle);
enabledStyle.Add(unCheckedStyle);
}
Add(enabledStyle);
}
}

View File

@ -1,4 +1,5 @@
using AtomUI.Theme.TokenSystem;
using Avalonia;
using Avalonia.Media;
namespace AtomUI.Controls;
@ -27,8 +28,11 @@ internal class RadioButtonToken : AbstractControlDesignToken
public Color RadioColor { get; set; }
public Color RadioBgColor { get; set; }
public double DotPadding { get; set; }
public Thickness TextMargin { get; set; }
public RadioButtonToken()
: base(ID)
{
@ -59,5 +63,6 @@ internal class RadioButtonToken : AbstractControlDesignToken
// internal
RadioColor = wireFrame ? colorPrimary : colorWhite;
RadioBgColor = wireFrame ? colorBgContainer : colorPrimary;
TextMargin = new Thickness(_globalToken.MarginXXS, 0, 0, 0);
}
}

View File

@ -0,0 +1,221 @@
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Media;
namespace AtomUI.Controls;
internal class RadioIndicator : Control, IWaveAdornerInfoProvider
{
public static readonly StyledProperty<bool?> IsCheckedProperty =
ToggleButton.IsCheckedProperty.AddOwner<CheckBoxIndicator>();
public static readonly StyledProperty<double> RadioSizeProperty =
AvaloniaProperty.Register<RadioIndicator, double>(nameof(RadioSize));
public static readonly StyledProperty<double> PaddingInlineProperty =
AvaloniaProperty.Register<RadioIndicator, double>(nameof(PaddingInline));
public static readonly StyledProperty<IBrush?> RadioBorderBrushProperty =
AvaloniaProperty.Register<RadioIndicator, IBrush?>(nameof(RadioBorderBrush));
public static readonly StyledProperty<IBrush?> RadioInnerBackgroundProperty =
AvaloniaProperty.Register<RadioIndicator, IBrush?>(nameof(RadioInnerBackground));
public static readonly StyledProperty<IBrush?> RadioBackgroundProperty =
AvaloniaProperty.Register<RadioIndicator, IBrush?>(nameof(RadioBackground));
public static readonly StyledProperty<Thickness> RadioBorderThicknessProperty =
AvaloniaProperty.Register<RadioIndicator, Thickness>(nameof(RadioBorderThickness));
public static readonly StyledProperty<double> RadioDotEffectSizeProperty =
AvaloniaProperty.Register<RadioIndicator, double>(nameof(RadioDotEffectSize));
public static readonly StyledProperty<double> DotSizeValueProperty =
AvaloniaProperty.Register<RadioButton, double>(
nameof(DotSizeValue));
public static readonly StyledProperty<double> DotPaddingProperty =
AvaloniaProperty.Register<RadioButton, double>(
nameof(DotPadding));
public bool? IsChecked
{
get => GetValue(IsCheckedProperty);
set => SetValue(IsCheckedProperty, value);
}
public double RadioSize
{
get => GetValue(RadioSizeProperty);
set => SetValue(RadioSizeProperty, value);
}
public double PaddingInline
{
get => GetValue(PaddingInlineProperty);
set => SetValue(PaddingInlineProperty, value);
}
public IBrush? RadioBorderBrush
{
get => GetValue(RadioBorderBrushProperty);
set => SetValue(RadioBorderBrushProperty, value);
}
public IBrush? RadioInnerBackground
{
get => GetValue(RadioInnerBackgroundProperty);
set => SetValue(RadioInnerBackgroundProperty, value);
}
public IBrush? RadioBackground
{
get => GetValue(RadioBackgroundProperty);
set => SetValue(RadioBackgroundProperty, value);
}
public Thickness RadioBorderThickness
{
get => GetValue(RadioBorderThicknessProperty);
set => SetValue(RadioBorderThicknessProperty, value);
}
public double RadioDotEffectSize
{
get => GetValue(RadioDotEffectSizeProperty);
set => SetValue(RadioDotEffectSizeProperty, value);
}
public double DotSizeValue
{
get => GetValue(DotSizeValueProperty);
set => SetValue(DotSizeValueProperty, value);
}
public double DotPadding
{
get => GetValue(DotPaddingProperty);
set => SetValue(DotPaddingProperty, value);
}
private IPen? _cachedPen;
private ControlStyleState _styleState;
static RadioIndicator()
{
AffectsRender<RadioIndicator>(
IsCheckedProperty,
RadioBorderBrushProperty,
RadioInnerBackgroundProperty,
RadioBackgroundProperty,
RadioBorderThicknessProperty,
RadioDotEffectSizeProperty);
}
public override void ApplyTemplate()
{
base.ApplyTemplate();
CollectStyleState();
RadioDotEffectSize = CalculateDotSize(IsEnabled, IsChecked.HasValue && IsChecked.Value);
TokenResourceBinder.CreateGlobalResourceBinding(this, RadioBorderThicknessProperty,
GlobalTokenResourceKey.BorderThickness, BindingPriority.Template,
new RenderScaleAwareThicknessConfigure(this));
Transitions ??= new Transitions
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBorderBrushProperty),
AnimationUtils.CreateTransition<DoubleTransition>(RadioDotEffectSizeProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(RadioBackgroundProperty,
GlobalTokenResourceKey.MotionDurationFast)
};
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == IsPointerOverProperty ||
e.Property == IsCheckedProperty ||
e.Property == IsEnabledProperty)
{
CollectStyleState();
if (VisualRoot is not null)
{
RadioDotEffectSize = CalculateDotSize(IsEnabled, IsChecked.HasValue && IsChecked.Value);
}
if (e.Property == IsCheckedProperty &&
_styleState.HasFlag(ControlStyleState.Enabled) &&
_styleState.HasFlag(ControlStyleState.On))
{
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.CircleWave);
}
}
}
private void CollectStyleState()
{
ControlStateUtils.InitCommonState(this, ref _styleState);
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 - DotPadding * 2;
}
}
else
{
targetValue = DotSizeValue * 0.6;
}
return targetValue;
}
public sealed override void Render(DrawingContext context)
{
var penWidth = RadioBorderThickness.Top;
PenUtils.TryModifyOrCreate(ref _cachedPen, RadioBorderBrush, RadioBorderThickness.Top);
context.DrawEllipse(RadioBackground, _cachedPen, Bounds.Deflate(penWidth / 2));
if (IsChecked.HasValue && IsChecked.Value)
{
var dotDiameter = RadioDotEffectSize / 2;
context.DrawEllipse(RadioInnerBackground, null, Bounds.Center, dotDiameter, dotDiameter);
}
}
public Rect WaveGeometry()
{
return new Rect(Bounds.Size);
}
public CornerRadius WaveBorderRadius()
{
return new CornerRadius(RadioSize / 2);
}
}