mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-28 23:08:36 +08:00
Refactor ToggleButton
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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>());
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user