From 1f02b4e7ca597b6ab8f948bf9c245a51a8699737 Mon Sep 17 00:00:00 2001 From: polarboy Date: Tue, 27 Aug 2024 12:55:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=20PopComfirm=20=E6=8E=A7?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 完成 PopComfirm 控件 --- .../ShowCase/PopupConfirmShowCase.axaml | 21 +++ .../ShowCase/PopupConfirmShowCase.axaml.cs | 11 ++ .../AtomUI.Demo.Desktop/Views/MainView.axaml | 3 + .../ArrowDecoratedBoxTheme.cs | 1 + src/AtomUI.Controls/Buttons/Button.cs | 23 ++- src/AtomUI.Controls/Flyouts/Flyout.cs | 20 +- src/AtomUI.Controls/Flyouts/FlyoutHost.cs | 24 +-- .../Flyouts/FlyoutPresenter.cs | 8 +- src/AtomUI.Controls/Flyouts/MenuFlyout.cs | 6 - .../ControlThemeRegister.g.cs | 1 + .../ControlTokenRegister.g.cs | 1 + .../TokenResourceConst.g.cs | 10 + .../PopupConfirm/PopupConfirm.cs | 114 ++++++++++++ .../PopupConfirm/PopupConfirmContainer.cs | 93 ++++++++++ .../PopupConfirmContainerTheme.cs | 171 ++++++++++++++++++ .../PopupConfirm/PopupConfirmFlyout.cs | 39 ++++ .../PopupConfirm/PopupConfirmToken.cs | 56 ++++++ src/AtomUI.Theme/BaseControlTheme.cs | 2 + src/AtomUI.Theme/ThemeManager.cs | 1 + 19 files changed, 564 insertions(+), 41 deletions(-) create mode 100644 samples/AtomUI.Demo.Desktop/ShowCase/PopupConfirmShowCase.axaml create mode 100644 samples/AtomUI.Demo.Desktop/ShowCase/PopupConfirmShowCase.axaml.cs create mode 100644 src/AtomUI.Controls/PopupConfirm/PopupConfirm.cs create mode 100644 src/AtomUI.Controls/PopupConfirm/PopupConfirmContainer.cs create mode 100644 src/AtomUI.Controls/PopupConfirm/PopupConfirmContainerTheme.cs create mode 100644 src/AtomUI.Controls/PopupConfirm/PopupConfirmFlyout.cs create mode 100644 src/AtomUI.Controls/PopupConfirm/PopupConfirmToken.cs diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/PopupConfirmShowCase.axaml b/samples/AtomUI.Demo.Desktop/ShowCase/PopupConfirmShowCase.axaml new file mode 100644 index 000000000..143ede550 --- /dev/null +++ b/samples/AtomUI.Demo.Desktop/ShowCase/PopupConfirmShowCase.axaml @@ -0,0 +1,21 @@ + + + + + Delete + + + + \ No newline at end of file diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/PopupConfirmShowCase.axaml.cs b/samples/AtomUI.Demo.Desktop/ShowCase/PopupConfirmShowCase.axaml.cs new file mode 100644 index 000000000..ef28018f7 --- /dev/null +++ b/samples/AtomUI.Demo.Desktop/ShowCase/PopupConfirmShowCase.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace AtomUI.Demo.Desktop.ShowCase; + +public partial class PopupConfirmShowCase : UserControl +{ + public PopupConfirmShowCase() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/samples/AtomUI.Demo.Desktop/Views/MainView.axaml b/samples/AtomUI.Demo.Desktop/Views/MainView.axaml index 5e19bfd68..eb90f4fc8 100644 --- a/samples/AtomUI.Demo.Desktop/Views/MainView.axaml +++ b/samples/AtomUI.Demo.Desktop/Views/MainView.axaml @@ -84,6 +84,9 @@ + + + diff --git a/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxTheme.cs b/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxTheme.cs index 5ef86adf9..bc53d9c87 100644 --- a/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxTheme.cs +++ b/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxTheme.cs @@ -5,6 +5,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; +using Avalonia.Input; using Avalonia.Styling; namespace AtomUI.Controls; diff --git a/src/AtomUI.Controls/Buttons/Button.cs b/src/AtomUI.Controls/Buttons/Button.cs index 5c26e9677..ab80296aa 100644 --- a/src/AtomUI.Controls/Buttons/Button.cs +++ b/src/AtomUI.Controls/Buttons/Button.cs @@ -16,6 +16,7 @@ using Avalonia.Data; using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Media; +using Avalonia.VisualTree; namespace AtomUI.Controls; @@ -249,13 +250,6 @@ public class Button : AvaloniaButton, var size = base.MeasureOverride(availableSize); var targetWidth = size.Width; var targetHeight = size.Height; - - // targetHeight += Padding.Top + Padding.Bottom; - // if (Shape != ButtonShape.Round) { - // targetWidth += Padding.Left + Padding.Right; - // } else { - // targetWidth += Math.Max(Padding.Left + Padding.Right, targetHeight); - // } targetHeight = Math.Max(targetHeight, ControlHeight); @@ -399,17 +393,13 @@ public class Button : AvaloniaButton, }; } } - - TokenResourceBinder.CreateGlobalTokenBinding(this, BorderThicknessProperty, GlobalTokenResourceKey.BorderThickness, - BindingPriority.Template, - new RenderScaleAwareThicknessConfigure(this)); + _customStyle.CollectStyleState(); ApplyShapeStyleConfig(); ApplyIconModeStyleConfig(); UpdatePseudoClasses(); SetupIcon(); SetupIconBrush(); - SetupEffectiveBorderThickness(); } protected virtual void ApplyIconModeStyleConfig() @@ -490,6 +480,15 @@ public class Button : AvaloniaButton, } } + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + TokenResourceBinder.CreateGlobalTokenBinding(this, BorderThicknessProperty, GlobalTokenResourceKey.BorderThickness, + BindingPriority.Template, + new RenderScaleAwareThicknessConfigure(this)); + SetupEffectiveBorderThickness(); + } + private void SetupEffectiveBorderThickness() { if (ButtonType == ButtonType.Default) { diff --git a/src/AtomUI.Controls/Flyouts/Flyout.cs b/src/AtomUI.Controls/Flyouts/Flyout.cs index 9edc6c7a0..b8ef3c231 100644 --- a/src/AtomUI.Controls/Flyouts/Flyout.cs +++ b/src/AtomUI.Controls/Flyouts/Flyout.cs @@ -6,12 +6,12 @@ using AtomUI.Utils; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives.PopupPositioning; +using Avalonia.Input; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Metadata; using Avalonia.Styling; using Avalonia.Threading; -using Avalonia.VisualTree; namespace AtomUI.Controls; @@ -134,7 +134,7 @@ public class Flyout : PopupFlyoutBase SetupArrowPosition(Popup); } - private void SetupArrowPosition(Popup popup, FlyoutPresenter? flyoutPresenter = null) + protected void SetupArrowPosition(Popup popup, FlyoutPresenter? flyoutPresenter = null) { if (flyoutPresenter is null) { var child = popup.Child; @@ -323,14 +323,14 @@ public class Flyout : PopupFlyoutBase } } - if (Popup.PlacementTarget?.GetVisualRoot() is null) { - return base.HideCore(false); - } - IsOpen = false; - Dispatcher.UIThread.Post(() => - { - Popup.CloseAnimation(HandlePopupClosed); - }); + // if (Popup.PlacementTarget?.GetVisualRoot() is null) { + // return base.HideCore(false); + // } + // IsOpen = false; + // Dispatcher.UIThread.Post(() => + // { + // Popup.CloseAnimation(HandlePopupClosed); + // }); return true; } diff --git a/src/AtomUI.Controls/Flyouts/FlyoutHost.cs b/src/AtomUI.Controls/Flyouts/FlyoutHost.cs index 012fa1e3d..59d1af99d 100644 --- a/src/AtomUI.Controls/Flyouts/FlyoutHost.cs +++ b/src/AtomUI.Controls/Flyouts/FlyoutHost.cs @@ -146,8 +146,7 @@ public class FlyoutHost : Control } #endregion - - private bool _initialized = false; + private DispatcherTimer? _mouseEnterDelayTimer; private DispatcherTimer? _mouseLeaveDelayTimer; private CompositeDisposable? _subscriptions; @@ -157,35 +156,36 @@ public class FlyoutHost : Control PlacementProperty.OverrideDefaultValue(PlacementMode.Top); } - public sealed override void ApplyTemplate() + public override void ApplyTemplate() { base.ApplyTemplate(); - if (!_initialized) { - if (AnchorTarget is not null) { - ((ISetLogicalParent)AnchorTarget).SetParent(this); - VisualChildren.Add(AnchorTarget); - } - TokenResourceBinder.CreateGlobalTokenBinding(this, MarginToAnchorProperty, GlobalTokenResourceKey.MarginXXS); - _initialized = true; + if (AnchorTarget is not null) { + ((ISetLogicalParent)AnchorTarget).SetParent(this); + VisualChildren.Add(AnchorTarget); } + TokenResourceBinder.CreateGlobalTokenBinding(this, MarginToAnchorProperty, GlobalTokenResourceKey.MarginXXS); + SetupFlyoutProperties(); } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); SetupTriggerHandler(); - SetupFlyoutProperties(); } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); + if (AnchorTarget is not null) { + ((ISetLogicalParent)AnchorTarget).SetParent(null); + VisualChildren.Remove(AnchorTarget); + } StopMouseLeaveTimer(); StopMouseEnterTimer(); _subscriptions?.Dispose(); } - private void SetupFlyoutProperties() + protected virtual void SetupFlyoutProperties() { if (Flyout is not null) { BindUtils.RelayBind(this, PlacementProperty, Flyout); diff --git a/src/AtomUI.Controls/Flyouts/FlyoutPresenter.cs b/src/AtomUI.Controls/Flyouts/FlyoutPresenter.cs index 5d009c49a..069346dce 100644 --- a/src/AtomUI.Controls/Flyouts/FlyoutPresenter.cs +++ b/src/AtomUI.Controls/Flyouts/FlyoutPresenter.cs @@ -1,4 +1,5 @@ -using Avalonia.Input; +using Avalonia.Data; +using Avalonia.Input; using Avalonia.LogicalTree; namespace AtomUI.Controls; @@ -20,4 +21,9 @@ public class FlyoutPresenter : ArrowDecoratedBox } base.OnKeyDown(e); } + + public FlyoutPresenter() + { + SetValue(CursorProperty, new Cursor(StandardCursorType.Arrow), BindingPriority.Template); + } } \ No newline at end of file diff --git a/src/AtomUI.Controls/Flyouts/MenuFlyout.cs b/src/AtomUI.Controls/Flyouts/MenuFlyout.cs index fe307e91b..be28a9a28 100644 --- a/src/AtomUI.Controls/Flyouts/MenuFlyout.cs +++ b/src/AtomUI.Controls/Flyouts/MenuFlyout.cs @@ -64,8 +64,6 @@ public class MenuFlyout : Flyout set => SetValue(ItemTemplateProperty, value); } - private Classes? _classes; - protected override Control CreatePresenter() { var presenter = new MenuFlyoutPresenter() @@ -106,10 +104,6 @@ public class MenuFlyout : Flyout protected override void OnOpening(CancelEventArgs args) { if (Popup.Child is { } presenter) { - if (_classes != null) { - SetPresenterClasses(presenter, FlyoutPresenterClasses); - } - if (FlyoutPresenterTheme is { } theme) { presenter.SetValue(Control.ThemeProperty, theme); } diff --git a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ControlThemeRegisterGenerator/ControlThemeRegister.g.cs b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ControlThemeRegisterGenerator/ControlThemeRegister.g.cs index 1d8be452f..f53f1633d 100644 --- a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ControlThemeRegisterGenerator/ControlThemeRegister.g.cs +++ b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ControlThemeRegisterGenerator/ControlThemeRegister.g.cs @@ -44,6 +44,7 @@ ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.NumericUpDownTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.OptionButtonGroupTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.OptionButtonTheme()); + ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.PopupConfirmContainerTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.CircleProgressTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.DashboardProgressTheme()); ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.ProgressBarTheme()); diff --git a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/ControlTokenRegister.g.cs b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/ControlTokenRegister.g.cs index 3676d82b7..57c5d5a08 100644 --- a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/ControlTokenRegister.g.cs +++ b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/ControlTokenRegister.g.cs @@ -23,6 +23,7 @@ ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.MenuToken)); ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.NumericUpDownToken)); ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.OptionButtonToken)); + ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.PopupConfirmToken)); ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.ProgressBarToken)); ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.RadioButtonToken)); ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.SegmentedToken)); diff --git a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs index 13f7ddb2c..67ad2bdaa 100644 --- a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs +++ b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs @@ -317,6 +317,16 @@ namespace AtomUI.Theme.Styling public static readonly TokenResourceKey PaddingSM = new TokenResourceKey("OptionButton.PaddingSM", "AtomUI.Token"); } + public static class PopupConfirmTokenResourceKey + { + public static readonly TokenResourceKey PopupMinWidth = new TokenResourceKey("PopupConfirm.PopupMinWidth", "AtomUI.Token"); + public static readonly TokenResourceKey PopupMinHeight = new TokenResourceKey("PopupConfirm.PopupMinHeight", "AtomUI.Token"); + public static readonly TokenResourceKey ButtonMargin = new TokenResourceKey("PopupConfirm.ButtonMargin", "AtomUI.Token"); + public static readonly TokenResourceKey IconMargin = new TokenResourceKey("PopupConfirm.IconMargin", "AtomUI.Token"); + public static readonly TokenResourceKey ContentContainerMargin = new TokenResourceKey("PopupConfirm.ContentContainerMargin", "AtomUI.Token"); + public static readonly TokenResourceKey TitleMargin = new TokenResourceKey("PopupConfirm.TitleMargin", "AtomUI.Token"); + } + public static class ProgressBarTokenResourceKey { public static readonly TokenResourceKey DefaultColor = new TokenResourceKey("ProgressBar.DefaultColor", "AtomUI.Token"); diff --git a/src/AtomUI.Controls/PopupConfirm/PopupConfirm.cs b/src/AtomUI.Controls/PopupConfirm/PopupConfirm.cs new file mode 100644 index 000000000..fdaa85d2a --- /dev/null +++ b/src/AtomUI.Controls/PopupConfirm/PopupConfirm.cs @@ -0,0 +1,114 @@ +using Avalonia; +using Avalonia.Controls.Templates; + +namespace AtomUI.Controls; + +public enum PopupConfirmStatus +{ + Info, + Warning, + Error +} + +public class PopupConfirm : FlyoutHost +{ + #region 公共属性属性 + + public static readonly StyledProperty OkTextProperty = + AvaloniaProperty.Register(nameof(OkText), "Ok"); + + public static readonly StyledProperty CancelTextProperty = + AvaloniaProperty.Register(nameof(CancelText), "Cancel"); + + public static readonly StyledProperty OkButtonTypeProperty = + AvaloniaProperty.Register(nameof(OkButtonType), ButtonType.Primary); + + public static readonly StyledProperty IsShowCancelButtonProperty = + AvaloniaProperty.Register(nameof(IsShowCancelButton), true); + + public static readonly StyledProperty TitleProperty = + AvaloniaProperty.Register(nameof(Title)); + + public static readonly StyledProperty ConfirmContentProperty = + AvaloniaProperty.Register(nameof(ConfirmContent)); + + public static readonly StyledProperty ConfirmContentTemplateProperty = + AvaloniaProperty.Register(nameof(ConfirmContentTemplate)); + + public static readonly StyledProperty IconProperty + = AvaloniaProperty.Register(nameof(Icon)); + + public static readonly StyledProperty ConfirmStatusProperty + = AvaloniaProperty.Register(nameof(ConfirmStatus), PopupConfirmStatus.Warning); + + public string OkText + { + get => GetValue(OkTextProperty); + set => SetValue(OkTextProperty, value); + } + + public string CancelText + { + get => GetValue(CancelTextProperty); + set => SetValue(CancelTextProperty, value); + } + + public ButtonType OkButtonType + { + get => GetValue(OkButtonTypeProperty); + set => SetValue(OkButtonTypeProperty, value); + } + + public bool IsShowCancelButton + { + get => GetValue(IsShowCancelButtonProperty); + set => SetValue(IsShowCancelButtonProperty, value); + } + + public string Title + { + get => GetValue(TitleProperty); + set => SetValue(TitleProperty, value); + } + + public object? ConfirmContent + { + get => GetValue(ConfirmContentProperty); + set => SetValue(ConfirmContentProperty, value); + } + + public IDataTemplate? ConfirmContentTemplate + { + get => GetValue(ConfirmContentTemplateProperty); + set => SetValue(ConfirmContentTemplateProperty, value); + } + + public PathIcon? Icon + { + get => GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public PopupConfirmStatus ConfirmStatus + { + get => GetValue(ConfirmStatusProperty); + set => SetValue(ConfirmStatusProperty, value); + } + + #endregion + + public sealed override void ApplyTemplate() + { + if (Flyout is null) { + Flyout = new PopupConfirmFlyout(this); + } + + if (Icon is null) { + Icon = new PathIcon() + { + Kind = "ExclamationCircleFilled" + }; + } + base.ApplyTemplate(); + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/PopupConfirm/PopupConfirmContainer.cs b/src/AtomUI.Controls/PopupConfirm/PopupConfirmContainer.cs new file mode 100644 index 000000000..2902c97ac --- /dev/null +++ b/src/AtomUI.Controls/PopupConfirm/PopupConfirmContainer.cs @@ -0,0 +1,93 @@ +using Avalonia; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; + +namespace AtomUI.Controls; + +internal class PopupConfirmContainer : TemplatedControl +{ + #region 内部属性定义 + + internal static readonly StyledProperty OkTextProperty = + PopupConfirm.OkTextProperty.AddOwner(); + + internal static readonly StyledProperty CancelTextProperty = + PopupConfirm.CancelTextProperty.AddOwner(); + + internal static readonly StyledProperty OkButtonTypeProperty = + PopupConfirm.OkButtonTypeProperty.AddOwner(); + + internal static readonly StyledProperty IsShowCancelButtonProperty = + PopupConfirm.IsShowCancelButtonProperty.AddOwner(); + + internal static readonly StyledProperty TitleProperty = + PopupConfirm.TitleProperty.AddOwner(); + + internal static readonly StyledProperty ConfirmContentProperty = + PopupConfirm.ConfirmContentProperty.AddOwner(); + + internal static readonly StyledProperty ConfirmContentTemplateProperty = + PopupConfirm.ConfirmContentTemplateProperty.AddOwner(); + + internal static readonly StyledProperty IconProperty = + PopupConfirm.IconProperty.AddOwner(); + + internal static readonly StyledProperty ConfirmStatusProperty + = PopupConfirm.ConfirmStatusProperty.AddOwner(); + + internal string OkText + { + get => GetValue(OkTextProperty); + set => SetValue(OkTextProperty, value); + } + + internal string CancelText + { + get => GetValue(CancelTextProperty); + set => SetValue(CancelTextProperty, value); + } + + internal ButtonType OkButtonType + { + get => GetValue(OkButtonTypeProperty); + set => SetValue(OkButtonTypeProperty, value); + } + + internal bool IsShowCancelButton + { + get => GetValue(IsShowCancelButtonProperty); + set => SetValue(IsShowCancelButtonProperty, value); + } + + internal string Title + { + get => GetValue(TitleProperty); + set => SetValue(TitleProperty, value); + } + + internal object? ConfirmContent + { + get => GetValue(ConfirmContentProperty); + set => SetValue(ConfirmContentProperty, value); + } + + internal IDataTemplate? ConfirmContentTemplate + { + get => GetValue(ConfirmContentTemplateProperty); + set => SetValue(ConfirmContentTemplateProperty, value); + } + + public PathIcon? Icon + { + get => GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public PopupConfirmStatus ConfirmStatus + { + get => GetValue(ConfirmStatusProperty); + set => SetValue(ConfirmStatusProperty, value); + } + + #endregion +} \ No newline at end of file diff --git a/src/AtomUI.Controls/PopupConfirm/PopupConfirmContainerTheme.cs b/src/AtomUI.Controls/PopupConfirm/PopupConfirmContainerTheme.cs new file mode 100644 index 000000000..66db57437 --- /dev/null +++ b/src/AtomUI.Controls/PopupConfirm/PopupConfirmContainerTheme.cs @@ -0,0 +1,171 @@ +using AtomUI.Theme; +using AtomUI.Theme.Styling; +using AtomUI.Utils; +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Styling; + +namespace AtomUI.Controls; + +[ControlThemeProvider] +internal class PopupConfirmContainerTheme : BaseControlTheme +{ + public const string MainLayoutPart = "PART_MainLayout"; + public const string OkButtonPart = "PART_OkButton"; + public const string TitlePart = "PART_Title"; + public const string ContentPart = "PART_Content"; + public const string IconContentPart = "PART_IconContent"; + + public PopupConfirmContainerTheme() + : base(typeof(PopupConfirmContainer)) { } + + protected override IControlTemplate? BuildControlTemplate() + { + return new FuncControlTemplate((popupConfirmContainer, scope) => + { + var mainLayout = new DockPanel() + { + Name = MainLayoutPart, + LastChildFill = true + }; + var buttons = BuildButtons(popupConfirmContainer, scope); + mainLayout.Children.Add(buttons); + var content = BuildContent(popupConfirmContainer, scope); + mainLayout.Children.Add(content); + TokenResourceBinder.CreateTokenBinding(mainLayout, DockPanel.MinWidthProperty, PopupConfirmTokenResourceKey.PopupMinWidth); + TokenResourceBinder.CreateTokenBinding(mainLayout, DockPanel.MinHeightProperty, PopupConfirmTokenResourceKey.PopupMinHeight); + BuildInstanceStyles(popupConfirmContainer); + return mainLayout; + }); + } + + private DockPanel BuildContent(PopupConfirmContainer popupConfirmContainer, INameScope scope) + { + var wrapperLayout = new DockPanel() + { + LastChildFill = true + }; + + var contentLayout = new DockPanel() + { + LastChildFill = true + }; + + var iconContentPresenter = new ContentPresenter() + { + Name = IconContentPart + }; + + CreateTemplateParentBinding(iconContentPresenter, ContentPresenter.ContentProperty, PopupConfirmContainer.IconProperty); + CreateTemplateParentBinding(iconContentPresenter, ContentPresenter.IsVisibleProperty, PopupConfirmContainer.IconProperty, + BindingMode.Default, + ObjectConverters.IsNotNull); + + DockPanel.SetDock(iconContentPresenter, Dock.Left); + wrapperLayout.Children.Add(iconContentPresenter); + wrapperLayout.Children.Add(contentLayout); + + var titleTextBlock = new TextBlock() + { + Name = TitlePart, + TextWrapping = TextWrapping.NoWrap + }; + CreateTemplateParentBinding(titleTextBlock, TextBlock.TextProperty, PopupConfirmContainer.TitleProperty); + DockPanel.SetDock(titleTextBlock, Dock.Top); + contentLayout.Children.Add(titleTextBlock); + + var contentPresenter = new ContentPresenter() + { + Name = ContentPart + }; + CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentProperty, PopupConfirmContainer.ConfirmContentProperty); + CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentTemplateProperty, PopupConfirmContainer.ConfirmContentTemplateProperty); + + contentLayout.Children.Add(contentPresenter); + + return wrapperLayout; + } + + private StackPanel BuildButtons(PopupConfirmContainer popupConfirmContainer, INameScope scope) + { + var buttonLayout = new StackPanel() + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Right, + VerticalAlignment = VerticalAlignment.Bottom + }; + DockPanel.SetDock(buttonLayout, Dock.Bottom); + var cancelButton = new Button() + { + SizeType = SizeType.Small, + HorizontalAlignment = HorizontalAlignment.Right + }; + TokenResourceBinder.CreateTokenBinding(cancelButton, Button.MarginProperty, PopupConfirmTokenResourceKey.ButtonMargin); + CreateTemplateParentBinding(cancelButton, Button.TextProperty, PopupConfirmContainer.CancelTextProperty); + CreateTemplateParentBinding(cancelButton, Button.IsVisibleProperty, PopupConfirmContainer.IsShowCancelButtonProperty); + buttonLayout.Children.Add(cancelButton); + + var okButton = new Button() + { + Name = OkButtonPart, + SizeType = SizeType.Small, + HorizontalAlignment = HorizontalAlignment.Right + }; + okButton.RegisterInNameScope(scope); + TokenResourceBinder.CreateTokenBinding(okButton, Button.MarginProperty, PopupConfirmTokenResourceKey.ButtonMargin); + CreateTemplateParentBinding(okButton, Button.TextProperty, PopupConfirmContainer.OkTextProperty); + CreateTemplateParentBinding(okButton, Button.ButtonTypeProperty, PopupConfirmContainer.OkButtonTypeProperty); + buttonLayout.Children.Add(okButton); + return buttonLayout; + } + + protected override void BuildInstanceStyles(Control control) + { + { + var iconStyle = new Style(selector => selector.Name(IconContentPart).Child().OfType()); + iconStyle.Add(PathIcon.WidthProperty, GlobalTokenResourceKey.IconSizeLG); + iconStyle.Add(PathIcon.HeightProperty, GlobalTokenResourceKey.IconSizeLG); + iconStyle.Add(PathIcon.VerticalAlignmentProperty, VerticalAlignment.Top); + control.Styles.Add(iconStyle); + } + var infoStatusStyle = new Style(selector => selector.PropertyEquals(PopupConfirmContainer.ConfirmStatusProperty, PopupConfirmStatus.Info) + .Descendant().Name(IconContentPart).Child().OfType()); + infoStatusStyle.Add(PathIcon.NormalFilledBrushProperty, GlobalTokenResourceKey.ColorPrimary); + control.Styles.Add(infoStatusStyle); + + var warningStatusStyle = new Style(selector => selector.PropertyEquals(PopupConfirmContainer.ConfirmStatusProperty, PopupConfirmStatus.Warning) + .Descendant().Name(IconContentPart).Child().OfType()); + warningStatusStyle.Add(PathIcon.NormalFilledBrushProperty, GlobalTokenResourceKey.ColorWarning); + control.Styles.Add(warningStatusStyle); + + var errorStatusStyle = new Style(selector => selector.PropertyEquals(PopupConfirmContainer.ConfirmStatusProperty, PopupConfirmStatus.Error) + .Descendant().Name(IconContentPart).Child().OfType()); + errorStatusStyle.Add(PathIcon.NormalFilledBrushProperty, GlobalTokenResourceKey.ColorError); + control.Styles.Add(errorStatusStyle); + } + + protected override void BuildStyles() + { + var commonStyle = new Style(selector => selector.Nesting()); + var iconContentPresenter = new Style(selector => selector.Nesting().Template().Name(IconContentPart)); + iconContentPresenter.Add(ContentPresenter.MarginProperty, PopupConfirmTokenResourceKey.IconMargin); + + var titleStyle = new Style(selector => selector.Nesting().Template().Name(TitlePart)); + titleStyle.Add(TextBlock.MarginProperty, PopupConfirmTokenResourceKey.TitleMargin); + titleStyle.Add(TextBlock.ForegroundProperty, GlobalTokenResourceKey.ColorTextHeading); + titleStyle.Add(TextBlock.FontWeightProperty, FontWeight.SemiBold); + commonStyle.Add(titleStyle); + + var contentStyle = new Style(selector => selector.Nesting().Template().Name(ContentPart)); + contentStyle.Add(ContentPresenter.MarginProperty, PopupConfirmTokenResourceKey.ContentContainerMargin); + commonStyle.Add(contentStyle); + + commonStyle.Add(iconContentPresenter); + Add(commonStyle); + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/PopupConfirm/PopupConfirmFlyout.cs b/src/AtomUI.Controls/PopupConfirm/PopupConfirmFlyout.cs new file mode 100644 index 000000000..70ecec414 --- /dev/null +++ b/src/AtomUI.Controls/PopupConfirm/PopupConfirmFlyout.cs @@ -0,0 +1,39 @@ +using AtomUI.Data; +using Avalonia.Controls; + +namespace AtomUI.Controls; + +internal class PopupConfirmFlyout : Flyout +{ + internal WeakReference PopupConfirmRef { get; set; } + + public PopupConfirmFlyout(PopupConfirm popupConfirm) + { + PopupConfirmRef = new WeakReference(popupConfirm); + } + + protected override Control CreatePresenter() + { + var presenter = new FlyoutPresenter(); + + if (PopupConfirmRef.TryGetTarget(out var popupConfirm)) { + var popupConfirmContainer = new PopupConfirmContainer(); + BindUtils.RelayBind(popupConfirm, PopupConfirm.OkTextProperty, popupConfirmContainer, PopupConfirmContainer.OkTextProperty); + BindUtils.RelayBind(popupConfirm, PopupConfirm.CancelTextProperty, popupConfirmContainer, PopupConfirmContainer.CancelTextProperty); + BindUtils.RelayBind(popupConfirm, PopupConfirm.OkButtonTypeProperty, popupConfirmContainer, PopupConfirmContainer.OkButtonTypeProperty); + BindUtils.RelayBind(popupConfirm, PopupConfirm.IsShowCancelButtonProperty, popupConfirmContainer, PopupConfirmContainer.IsShowCancelButtonProperty); + BindUtils.RelayBind(popupConfirm, PopupConfirm.TitleProperty, popupConfirmContainer, PopupConfirmContainer.TitleProperty); + BindUtils.RelayBind(popupConfirm, PopupConfirm.ConfirmStatusProperty, popupConfirmContainer, PopupConfirmContainer.ConfirmStatusProperty); + BindUtils.RelayBind(popupConfirm, PopupConfirm.IconProperty, popupConfirmContainer, PopupConfirmContainer.IconProperty); + BindUtils.RelayBind(popupConfirm, PopupConfirm.ConfirmContentProperty, popupConfirmContainer, PopupConfirmContainer.ConfirmContentProperty); + BindUtils.RelayBind(popupConfirm, PopupConfirm.ConfirmContentTemplateProperty, popupConfirmContainer, PopupConfirmContainer.ConfirmContentTemplateProperty); + presenter.Content = popupConfirmContainer; + } + + BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, presenter, IsShowArrowProperty); + + CalculateShowArrowEffective(); + SetupArrowPosition(Popup, presenter); + return presenter; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/PopupConfirm/PopupConfirmToken.cs b/src/AtomUI.Controls/PopupConfirm/PopupConfirmToken.cs new file mode 100644 index 000000000..7288bbe78 --- /dev/null +++ b/src/AtomUI.Controls/PopupConfirm/PopupConfirmToken.cs @@ -0,0 +1,56 @@ +using AtomUI.Theme.TokenSystem; +using Avalonia; + +namespace AtomUI.Controls; + +[ControlDesignToken] +internal class PopupConfirmToken : AbstractControlDesignToken +{ + public const string ID = "PopupConfirm"; + + public PopupConfirmToken() + : base(ID) + { + } + + /// + /// 菜单 Popup 最小宽度 + /// + public double PopupMinWidth { get; set; } + + /// + /// 菜单 Popup 最小高度 + /// + public double PopupMinHeight { get; set; } + + /// + /// 按钮的外边距 + /// + public Thickness ButtonMargin { get; set; } + + /// + /// Icon 外边距 + /// + public Thickness IconMargin { get; set; } + + /// + /// 主内容区域外边距 + /// + public Thickness ContentContainerMargin { get; set; } + + /// + /// 标题栏外边距 + /// + public Thickness TitleMargin { get; set; } + + internal override void CalculateFromAlias() + { + base.CalculateFromAlias(); + PopupMinWidth = 240; + PopupMinHeight= 80; + ButtonMargin = new Thickness(_globalToken.MarginXS, 0, 0, 0); + IconMargin = new Thickness(_globalToken.MarginXS, _globalToken.MarginXS + _globalToken.MarginXXS / 2, _globalToken.MarginXS, 0); + ContentContainerMargin = new Thickness(0, 0, 0, _globalToken.MarginXS); + TitleMargin = new Thickness(0, _globalToken.MarginXS, 0, _globalToken.MarginXS); + } +} \ No newline at end of file diff --git a/src/AtomUI.Theme/BaseControlTheme.cs b/src/AtomUI.Theme/BaseControlTheme.cs index a8e32c17d..d326c1f38 100644 --- a/src/AtomUI.Theme/BaseControlTheme.cs +++ b/src/AtomUI.Theme/BaseControlTheme.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Data; @@ -35,6 +36,7 @@ public abstract class BaseControlTheme : ControlTheme } protected virtual void BuildStyles() { } + protected virtual void BuildInstanceStyles(Control control) { } protected virtual void NotifyPreBuild() { } protected virtual void NotifyBuildCompleted() { } diff --git a/src/AtomUI.Theme/ThemeManager.cs b/src/AtomUI.Theme/ThemeManager.cs index 9250206c7..61920032e 100644 --- a/src/AtomUI.Theme/ThemeManager.cs +++ b/src/AtomUI.Theme/ThemeManager.cs @@ -32,6 +32,7 @@ public class ThemeManager : Styles, IThemeManager public IReadOnlyList CustomThemeDirs => _customThemeDirs; public static ThemeManager Current { get; } public string DefaultThemeId { get; set; } + public double RenderScaling { get; internal set; } internal List ControlTokenTypes { get; set; } private CultureInfo? _cultureInfo;