Refactor ToggleButton

This commit is contained in:
CHINBOY
2025-03-08 17:04:02 +08:00
parent 84292d7f87
commit 08e863b4e1
8 changed files with 104 additions and 152 deletions
@@ -16,49 +16,6 @@ internal static class MotionInvoker
await motion.RunAsync(actor, aboutToStart, completedAction, cancellationToken);
});
}
public static void InvokeInPopupLayer(SceneMotionActorControl actor,
AbstractMotion motion,
Action? aboutToStart = null,
Action? completedAction = null,
CancellationToken cancellationToken = default)
{
var sceneLayer = PrepareSceneLayer(motion, actor);
var compositeDisposable = new CompositeDisposable();
compositeDisposable.Add(Disposable.Create(sceneLayer, (state) =>
{
sceneLayer.Hide();
sceneLayer.Dispose();
}));
sceneLayer.SetMotionActor(actor);
actor.NotifyMotionTargetAddedToScene();
sceneLayer.Topmost = true;
sceneLayer.Show();
sceneLayer.Activate();
actor.NotifySceneShowed();
actor.IsVisible = false;
// 等待一个事件循环,让动画窗口置顶
Dispatcher.UIThread.Post(() =>
{
Dispatcher.UIThread.InvokeAsync(async () =>
{
// 主要等待正常窗体显示出来再隐藏对话层,不然感觉会闪屏
await motion.RunAsync(actor, aboutToStart, () =>
{
if (completedAction is not null)
{
completedAction();
}
}, cancellationToken);
DispatcherTimer.RunOnce(() =>
{
compositeDisposable.Dispose();
compositeDisposable = null;
}, TimeSpan.FromMilliseconds(500));
});
});
}
public static void DispatchInMotionSceneLayer(SceneMotionActorControl actor,
AbstractMotion motion,
@@ -164,7 +164,7 @@ public class AddOnDecoratedInnerBox : ContentControl,
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == LeftAddOnContentProperty ||
change.Property == RightAddOnContentProperty)
{
@@ -172,7 +172,7 @@ public class AddOnDecoratedInnerBox : ContentControl,
{
oldControl.SetTemplatedParent(null);
}
if (change.NewValue is Control newControl)
{
newControl.SetTemplatedParent(this);
@@ -203,7 +203,6 @@ public class AddOnDecoratedInnerBox : ContentControl,
}
SetupContentPresenterMargin();
BuildEffectiveInnerBoxPadding();
}
private void HandleLayoutSizeChanged(object? sender, SizeChangedEventArgs args)
@@ -233,4 +232,10 @@ public class AddOnDecoratedInnerBox : ContentControl,
ContentPresenterMargin = new Thickness(marginLeft, 0, marginRight, 0);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
BuildEffectiveInnerBoxPadding();
}
}
@@ -1,7 +1,6 @@
using AtomUI.Data;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Diagnostics;
@@ -184,12 +183,6 @@ public class DropdownButton : Button
ExtraContainerVisible = true;
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
SetupFlyoutProperties();
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
@@ -199,6 +192,7 @@ public class DropdownButton : Button
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
SetupFlyoutProperties();
_flyoutStateHelper.NotifyAttachedToVisualTree();
}
@@ -212,7 +206,7 @@ public class DropdownButton : Button
BindUtils.RelayBind(this, IsShowArrowProperty, DropdownFlyout);
BindUtils.RelayBind(this, IsPointAtCenterProperty, DropdownFlyout);
BindUtils.RelayBind(this, MarginToAnchorProperty, DropdownFlyout);
BindUtils.RelayBind(this, IsMotionEnabledProperty, DropdownFlyout, MenuFlyout.IsMotionEnabledProperty);
BindUtils.RelayBind(this, IsMotionEnabledProperty, DropdownFlyout);
DropdownFlyout.Opened += HandleFlyoutOpened;
DropdownFlyout.Closed += HandleFlyoutClosed;
+2 -2
View File
@@ -196,9 +196,9 @@ public class IconButton : AvaloniaButton,
}
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
base.OnAttachedToVisualTree(e);
SetupIcon();
}
+39 -19
View File
@@ -33,6 +33,7 @@ public class SplitButton : ContentControl,
ICommandSource,
ISizeTypeAware,
IControlSharedTokenResourcesHost,
IAnimationAwareControl,
ITokenResourceConsumer
{
#region 公共属性定义
@@ -98,10 +99,13 @@ public class SplitButton : ContentControl,
public static readonly StyledProperty<bool> IsPrimaryButtonTypeProperty =
AvaloniaProperty.Register<SplitButton, bool>(nameof(IsPrimaryButtonType));
public static readonly StyledProperty<bool> IsMotionEnabledProperty
= AnimationAwareControlProperty.IsMotionEnabledProperty.AddOwner<SplitButton>();
/// <summary>
/// Raised when the user presses the primary part of the <see cref="SplitButton" />.
/// </summary>
public static readonly StyledProperty<bool> IsWaveAnimationEnabledProperty
= AnimationAwareControlProperty.IsWaveAnimationEnabledProperty.AddOwner<SplitButton>();
public event EventHandler<RoutedEventArgs>? Click
{
add => AddHandler(ClickEvent, value);
@@ -230,6 +234,18 @@ public class SplitButton : ContentControl,
get => GetValue(IsPrimaryButtonTypeProperty);
set => SetValue(IsPrimaryButtonTypeProperty, value);
}
public bool IsMotionEnabled
{
get => GetValue(IsMotionEnabledProperty);
set => SetValue(IsMotionEnabledProperty, value);
}
public bool IsWaveAnimationEnabled
{
get => GetValue(IsWaveAnimationEnabledProperty);
set => SetValue(IsWaveAnimationEnabledProperty, value);
}
#endregion
@@ -248,6 +264,7 @@ public class SplitButton : ContentControl,
set => SetAndRaise(EffectiveButtonTypeProperty, ref _effectiveButtonType, value);
}
Control IAnimationAwareControl.PropertyBindTarget => this;
Control IControlSharedTokenResourcesHost.HostControl => this;
string IControlSharedTokenResourcesHost.TokenId => ButtonToken.ID;
CompositeDisposable? ITokenResourceConsumer.TokenBindingsDisposable => _tokenBindingsDisposable;
@@ -279,14 +296,12 @@ public class SplitButton : ContentControl,
{
_flyoutStateHelper = new FlyoutStateHelper();
this.RegisterResources();
this.BindAnimationProperties(IsMotionEnabledProperty, IsWaveAnimationEnabledProperty);
}
internal virtual bool InternalIsChecked => false;
/// <inheritdoc />
protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute;
/// <inheritdoc />
void ICommandSource.CanExecuteChanged(object sender, EventArgs e)
{
CanExecuteChanged(sender, e);
@@ -409,12 +424,21 @@ public class SplitButton : ContentControl,
_flyoutStateHelper.NotifyAttachedToVisualTree();
UpdatePseudoClasses();
SetupEffectiveButtonType();
FlyoutButtonIcon ??= AntDesignIconPackage.EllipsisOutlined();
SetupDefaultFlyoutButtonIcon();
SetupFlyoutProperties();
RegisterFlyoutEvents(Flyout);
}
/// <inheritdoc />
private void SetupDefaultFlyoutButtonIcon()
{
if (FlyoutButtonIcon == null)
{
ClearValue(FlyoutButtonIconProperty);
SetValue(FlyoutButtonIconProperty, AntDesignIconPackage.EllipsisOutlined(), BindingPriority.Template);
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
@@ -442,16 +466,16 @@ public class SplitButton : ContentControl,
BindUtils.RelayBind(this, IsShowArrowProperty, Flyout);
BindUtils.RelayBind(this, IsPointAtCenterProperty, Flyout);
BindUtils.RelayBind(this, MarginToAnchorProperty, Flyout);
BindUtils.RelayBind(this, MarginToAnchorProperty, Flyout);
BindUtils.RelayBind(this, IsMotionEnabledProperty, Flyout);
}
}
/// <inheritdoc />
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
_tokenBindingsDisposable = new CompositeDisposable();
// Control attached again, set Hotkey to create a hotkey manager for this control
SetCurrentValue(HotKeyProperty, _hotkey);
@@ -468,8 +492,7 @@ public class SplitButton : ContentControl,
FlyoutStateHelper.MouseLeaveDelayProperty);
BindUtils.RelayBind(this, TriggerTypeProperty, _flyoutStateHelper, FlyoutStateHelper.TriggerTypeProperty);
}
/// <inheritdoc />
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
@@ -484,8 +507,7 @@ public class SplitButton : ContentControl,
}
this.DisposeTokenBindings();
}
/// <inheritdoc />
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == CommandProperty)
@@ -578,8 +600,7 @@ public class SplitButton : ContentControl,
_secondaryButton.CornerRadius = secondaryButtonCornerRadius;
}
}
/// <inheritdoc />
protected override void OnKeyDown(KeyEventArgs e)
{
var key = e.Key;
@@ -592,8 +613,7 @@ public class SplitButton : ContentControl,
base.OnKeyDown(e);
}
/// <inheritdoc />
protected override void OnKeyUp(KeyEventArgs e)
{
var key = e.Key;
@@ -6,10 +6,7 @@ using AtomUI.Theme.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.VisualTree;
namespace AtomUI.Controls;
@@ -115,27 +112,8 @@ public class ToggleIconButton : ToggleButton,
this.RegisterResources();
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
if (CheckedIcon is not null)
{
ConfigureIcon(CheckedIcon);
}
if (UnCheckedIcon is not null)
{
ConfigureIcon(UnCheckedIcon);
}
ApplyIconToContent();
}
protected virtual void ConfigureIcon(Icon icon)
{
icon.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Center, BindingPriority.Template);
icon.SetValue(VerticalAlignmentProperty, VerticalAlignment.Center, BindingPriority.Template);
icon.SetTemplatedParent(this);
BindUtils.RelayBind(this, IconWidthProperty, icon, WidthProperty);
BindUtils.RelayBind(this, IconHeightProperty, icon, HeightProperty);
@@ -151,60 +129,22 @@ public class ToggleIconButton : ToggleButton,
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (this.IsAttachedToVisualTree())
{
if (change.Property == IsCheckedProperty)
{
ApplyIconToContent();
}
else if (change.Property == IsPressedProperty ||
change.Property == IsPointerOverProperty)
{
var icon = IsChecked.HasValue && IsChecked.Value ? CheckedIcon : UnCheckedIcon;
if (icon is not null)
{
if (!PseudoClasses.Contains(StdPseudoClass.Disabled))
{
icon.IconMode = IconMode.Normal;
if (PseudoClasses.Contains(StdPseudoClass.Pressed))
{
icon.IconMode = IconMode.Selected;
}
else if (PseudoClasses.Contains(StdPseudoClass.PointerOver))
{
icon.IconMode = IconMode.Active;
}
}
else
{
icon.IconMode = IconMode.Disabled;
}
}
}
}
if (change.Property == CheckedIconProperty ||
change.Property == UnCheckedIconProperty)
{
if (change.OldValue is Icon oldIcon)
{
oldIcon.SetTemplatedParent(null);
}
if (change.NewValue is Icon newIcon)
{
newIcon.SetTemplatedParent(this);
ConfigureIcon(newIcon);
ApplyIconToContent();
}
}
}
internal virtual void ApplyIconToContent()
{
if (IsChecked.HasValue && IsChecked.Value)
{
Content = CheckedIcon;
}
else
{
Content = UnCheckedIcon;
}
}
public bool HitTest(Point point)
{
@@ -4,7 +4,10 @@ using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Styling;
namespace AtomUI.Controls;
@@ -12,7 +15,9 @@ namespace AtomUI.Controls;
[ControlThemeProvider]
internal class ToggleIconButtonTheme : BaseControlTheme
{
public const string ContentPresenterPart = "PART_ContentPresenter";
public const string RootLayoutPart = "PART_RootLayout";
public const string CheckedIconPresenterPart = "PART_CheckedIconPresenter";
public const string UnCheckedIconPresenterPart = "PART_UnCheckedIconPresenter";
public ToggleIconButtonTheme()
: base(typeof(ToggleIconButton))
@@ -27,23 +32,52 @@ internal class ToggleIconButtonTheme : BaseControlTheme
{
return new FuncControlTemplate<ToggleIconButton>((button, scope) =>
{
var contentPresenter = new ContentPresenter
var rootLayout = new Panel()
{
Name = ContentPresenterPart
Name = RootLayoutPart
};
CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentProperty,
ContentControl.ContentProperty);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentTemplateProperty,
ContentControl.ContentTemplateProperty);
return contentPresenter;
var checkedIconPresenter = new ContentPresenter
{
Name = CheckedIconPresenterPart
};
var uncheckedIconPresenter = new ContentPresenter()
{
Name = UnCheckedIconPresenterPart
};
rootLayout.Children.Add(checkedIconPresenter);
rootLayout.Children.Add(uncheckedIconPresenter);
CreateTemplateParentBinding(checkedIconPresenter, ContentPresenter.ContentProperty,
ToggleIconButton.CheckedIconProperty);
CreateTemplateParentBinding(checkedIconPresenter, ContentPresenter.IsVisibleProperty,
ToggleIconButton.IsCheckedProperty, BindingMode.Default,
new FuncValueConverter<bool?, bool>(input => input.HasValue && input.Value));
CreateTemplateParentBinding(uncheckedIconPresenter, ContentPresenter.ContentProperty,
ToggleIconButton.UnCheckedIconProperty);
CreateTemplateParentBinding(uncheckedIconPresenter, ContentPresenter.IsVisibleProperty,
ToggleIconButton.IsCheckedProperty, BindingMode.Default,
new FuncValueConverter<bool?, bool>(input => !input.HasValue || !input.Value));
return rootLayout;
});
}
protected override void BuildStyles()
{
{
var iconStyle = new Style(selector => selector.Nesting().Template().OfType<Icon>());
iconStyle.Add(Icon.HorizontalAlignmentProperty, HorizontalAlignment.Center);
iconStyle.Add(Icon.VerticalAlignmentProperty, VerticalAlignment.Center);
Add(iconStyle);
}
var enabledStyle =
new Style(selector => selector.Nesting().PropertyEquals(InputElement.IsEnabledProperty, true));
enabledStyle.Add(ToggleIconButton.CursorProperty, new SetterValueFactory<Cursor>(() => new Cursor(StandardCursorType.Hand)));
enabledStyle.Add(ToggleIconButton.CursorProperty,
new SetterValueFactory<Cursor>(() => new Cursor(StandardCursorType.Hand)));
{
{
var iconStyle = new Style(selector => selector.Nesting().Template().OfType<Icon>());
+4 -2
View File
@@ -527,10 +527,12 @@ public class TreeViewItem : AvaloniaTreeItem,
var motion = new ExpandMotion(Direction.Top,
MotionDuration,
new CubicEaseOut());
MotionInvoker.Invoke(_itemsPresenterMotionActor, motion, null,
() =>
MotionInvoker.Invoke(_itemsPresenterMotionActor, motion, () =>
{
_itemsPresenterMotionActor.IsVisible = true;
},
() =>
{
_animating = false;
if (_switcherButton != null)
{