From c1d486297d78ac6712bbe6bf5eaadf8f64e69863 Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Fri, 18 Apr 2025 00:43:37 +0800 Subject: [PATCH 01/16] feat: add popconfirm. --- src/Ursa/Controls/PopConfirm/PopConfirm.cs | 125 ++++++++++++++++++ .../PopConfirm/PopConfirmTriggerMode.cs | 8 ++ 2 files changed, 133 insertions(+) create mode 100644 src/Ursa/Controls/PopConfirm/PopConfirm.cs create mode 100644 src/Ursa/Controls/PopConfirm/PopConfirmTriggerMode.cs diff --git a/src/Ursa/Controls/PopConfirm/PopConfirm.cs b/src/Ursa/Controls/PopConfirm/PopConfirm.cs new file mode 100644 index 0000000..f0096ca --- /dev/null +++ b/src/Ursa/Controls/PopConfirm/PopConfirm.cs @@ -0,0 +1,125 @@ +using System.Windows.Input; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; + +namespace Ursa.Controls; + +public class PopConfirm: ContentControl +{ + public static readonly StyledProperty PopupHeaderProperty = AvaloniaProperty.Register( + nameof(PopupHeader)); + + public object? PopupHeader + { + get => GetValue(PopupHeaderProperty); + set => SetValue(PopupHeaderProperty, value); + } + + public static readonly StyledProperty PopupHeaderTemplateProperty = AvaloniaProperty.Register( + nameof(PopupHeaderTemplate)); + + public IDataTemplate? PopupHeaderTemplate + { + get => GetValue(PopupHeaderTemplateProperty); + set => SetValue(PopupHeaderTemplateProperty, value); + } + + public static readonly StyledProperty PopupContentProperty = AvaloniaProperty.Register( + nameof(PopupContent)); + + public object? PopupContent + { + get => GetValue(PopupContentProperty); + set => SetValue(PopupContentProperty, value); + } + + public static readonly StyledProperty PopupContentTemplateProperty = AvaloniaProperty.Register( + nameof(PopupContentTemplate)); + + public IDataTemplate? PopupContentTemplate + { + get => GetValue(PopupContentTemplateProperty); + set => SetValue(PopupContentTemplateProperty, value); + } + + public static readonly StyledProperty ConfirmCommandProperty = AvaloniaProperty.Register( + nameof(ConfirmCommand)); + + public ICommand? ConfirmCommand + { + get => GetValue(ConfirmCommandProperty); + set => SetValue(ConfirmCommandProperty, value); + } + + public static readonly StyledProperty CancelCommandProperty = AvaloniaProperty.Register( + nameof(CancelCommand)); + + public ICommand? CancelCommand + { + get => GetValue(CancelCommandProperty); + set => SetValue(CancelCommandProperty, value); + } + + public static readonly StyledProperty ConfirmCommandParameterProperty = AvaloniaProperty.Register( + nameof(ConfirmCommandParameter)); + + public object? ConfirmCommandParameter + { + get => GetValue(ConfirmCommandParameterProperty); + set => SetValue(ConfirmCommandParameterProperty, value); + } + + public static readonly StyledProperty CancelCommandParameterProperty = AvaloniaProperty.Register( + nameof(CancelCommandParameter)); + + public object? CancelCommandParameter + { + get => GetValue(CancelCommandParameterProperty); + set => SetValue(CancelCommandParameterProperty, value); + } + + public static readonly StyledProperty TriggerModeProperty = + AvaloniaProperty.Register( + nameof(TriggerMode)); + + public PopConfirmTriggerMode TriggerMode + { + get => GetValue(TriggerModeProperty); + set => SetValue(TriggerModeProperty, value); + } + + static PopConfirm() + { + ConfirmCommandProperty.Changed.AddClassHandler((popconfirm, args) => popconfirm.OnCommandChanged(args)); + } + + private void OnCommandChanged(AvaloniaPropertyChangedEventArgs args) + { + var newValue = args.GetNewValue(); + newValue.CanExecuteChanged += (sender, e) => + { + if (args.Sender is PopConfirm popconfirm) + { + var b = newValue.CanExecute(this.ConfirmCommandParameter); + } + }; + } + + public PopConfirm() + { + + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + } + + protected override bool RegisterContentPresenter(ContentPresenter presenter) + { + return base.RegisterContentPresenter(presenter); + } +} \ No newline at end of file diff --git a/src/Ursa/Controls/PopConfirm/PopConfirmTriggerMode.cs b/src/Ursa/Controls/PopConfirm/PopConfirmTriggerMode.cs new file mode 100644 index 0000000..09113a5 --- /dev/null +++ b/src/Ursa/Controls/PopConfirm/PopConfirmTriggerMode.cs @@ -0,0 +1,8 @@ +namespace Ursa.Controls; + +public enum PopConfirmTriggerMode +{ + Tap, + Hover, + Focus, +} \ No newline at end of file From e9a94798e3553d452fafc6c3a39a70ef92ded3f4 Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Fri, 18 Apr 2025 02:08:12 +0800 Subject: [PATCH 02/16] feat: add demo. --- demo/Ursa.Demo/Pages/PopConfirmDemo.axaml | 19 ++++++++++++ demo/Ursa.Demo/Pages/PopConfirmDemo.axaml.cs | 25 +++++++++++++++ .../Ursa.Demo/ViewModels/MainViewViewModel.cs | 1 + demo/Ursa.Demo/ViewModels/MenuViewModel.cs | 2 ++ .../ViewModels/PopConfirmDemoViewModel.cs | 31 +++++++++++++++++++ .../Controls/PopConfirm.axaml | 26 ++++++++++++++++ src/Ursa.Themes.Semi/Controls/_index.axaml | 9 +++--- src/Ursa/Controls/PopConfirm/PopConfirm.cs | 30 +++++++++++++++++- 8 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 demo/Ursa.Demo/Pages/PopConfirmDemo.axaml create mode 100644 demo/Ursa.Demo/Pages/PopConfirmDemo.axaml.cs create mode 100644 demo/Ursa.Demo/ViewModels/PopConfirmDemoViewModel.cs create mode 100644 src/Ursa.Themes.Semi/Controls/PopConfirm.axaml diff --git a/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml b/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml new file mode 100644 index 0000000..aafb535 --- /dev/null +++ b/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml.cs b/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml.cs new file mode 100644 index 0000000..5f9713b --- /dev/null +++ b/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Ursa.Controls; +using Ursa.Demo.ViewModels; + +namespace Ursa.Demo.Pages; + +public partial class PopConfirmDemo : UserControl +{ + public PopConfirmDemo() + { + InitializeComponent(); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + if (this.DataContext is not PopConfirmDemoViewModel vm) return; + var manager = WindowToastManager.TryGetToastManager(TopLevel.GetTopLevel(this), out var m) + ? m + : new WindowToastManager(TopLevel.GetTopLevel(this)); + vm.ToastManager = manager; + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs index 181c82d..e591d80 100644 --- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs @@ -69,6 +69,7 @@ public partial class MainViewViewModel : ViewModelBase MenuKeys.MenuKeyNumPad => new NumPadDemoViewModel(), MenuKeys.MenuKeyPagination => new PaginationDemoViewModel(), MenuKeys.MenuKeyPinCode => new PinCodeDemoViewModel(), + MenuKeys.MenuKeyPopConfirm => new PopConfirmDemoViewModel(), MenuKeys.MenuKeyRangeSlider => new RangeSliderDemoViewModel(), MenuKeys.MenuKeyRating => new RatingDemoViewModel(), MenuKeys.MenuKeyScrollToButton => new ScrollToButtonDemoViewModel(), diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs index fde221b..7dfffbd 100644 --- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs @@ -44,6 +44,7 @@ public class MenuViewModel : ViewModelBase new() { MenuHeader = "Loading", Key = MenuKeys.MenuKeyLoading, Status = "Updated" }, new() { MenuHeader = "Message Box", Key = MenuKeys.MenuKeyMessageBox }, new() { MenuHeader = "Notification", Key = MenuKeys.MenuKeyNotification }, + new() { MenuHeader = "PopConfirm", Key = MenuKeys.MenuKeyPopConfirm }, new() { MenuHeader = "Toast", Key = MenuKeys.MenuKeyToast }, new() { MenuHeader = "Skeleton", Key = MenuKeys.MenuKeySkeleton }, } @@ -135,6 +136,7 @@ public static class MenuKeys public const string MenuKeyNumPad = "NumPad"; public const string MenuKeyPagination = "Pagination"; public const string MenuKeyPinCode = "PinCode"; + public const string MenuKeyPopConfirm = "PopConfirm"; public const string MenuKeyRangeSlider = "RangeSlider"; public const string MenuKeyRating = "Rating"; public const string MenuKeyScrollToButton = "ScrollToButton"; diff --git a/demo/Ursa.Demo/ViewModels/PopConfirmDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/PopConfirmDemoViewModel.cs new file mode 100644 index 0000000..78618e6 --- /dev/null +++ b/demo/Ursa.Demo/ViewModels/PopConfirmDemoViewModel.cs @@ -0,0 +1,31 @@ +using System.Windows.Input; +using Avalonia.Controls.Notifications; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Ursa.Controls; + +namespace Ursa.Demo.ViewModels; + +public partial class PopConfirmDemoViewModel: ObservableObject +{ + internal WindowToastManager? ToastManager { get; set; } + + public ICommand ConfirmCommand { get; } + public ICommand CancelCommand { get; } + + public PopConfirmDemoViewModel() + { + ConfirmCommand = new RelayCommand(OnConfirm); + CancelCommand = new RelayCommand(OnCancel); + } + + private void OnConfirm() + { + ToastManager?.Show(new Toast("Confirmed"), type: NotificationType.Success, classes: ["Light"]); + } + + private void OnCancel() + { + ToastManager?.Show(new Toast("Canceled"), type:NotificationType.Error, classes: ["Light"]); + } +} \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml b/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml new file mode 100644 index 0000000..e58c249 --- /dev/null +++ b/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml b/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml index a41ce95..7740c6c 100644 --- a/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml +++ b/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml @@ -6,27 +6,38 @@ - + + Name="{x:Static u:PopConfirm.PART_Popup}" + Placement="{TemplateBinding Placement}" > - - - - + + + + + + + diff --git a/demo/Ursa.Demo/ViewModels/PopConfirmDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/PopConfirmDemoViewModel.cs index 175a8c9..925705a 100644 --- a/demo/Ursa.Demo/ViewModels/PopConfirmDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/PopConfirmDemoViewModel.cs @@ -7,27 +7,42 @@ using Ursa.Controls; namespace Ursa.Demo.ViewModels; -public partial class PopConfirmDemoViewModel: ObservableObject +public class PopConfirmDemoViewModel : ObservableObject { - internal WindowToastManager? ToastManager { get; set; } - - public ICommand ConfirmCommand { get; } - public ICommand CancelCommand { get; } - public PopConfirmDemoViewModel() { - ConfirmCommand = new AsyncRelayCommand(OnConfirm); + AsyncConfirmCommand = new AsyncRelayCommand(OnConfirmAsync); + AsyncCancelCommand = new RelayCommand(OnCancelAsync); + ConfirmCommand = new RelayCommand(OnConfirm); CancelCommand = new RelayCommand(OnCancel); } - - private async Task OnConfirm() - { - await Task.Delay(3000); - ToastManager?.Show(new Toast("Confirmed"), type: NotificationType.Success, classes: ["Light"]); - } - + + internal WindowToastManager? ToastManager { get; set; } + + public ICommand ConfirmCommand { get; } + public ICommand CancelCommand { get; } + + public ICommand AsyncConfirmCommand { get; } + public ICommand AsyncCancelCommand { get; } + private void OnCancel() { - ToastManager?.Show(new Toast("Canceled"), type:NotificationType.Error, classes: ["Light"]); + ToastManager?.Show(new Toast("Canceled"), NotificationType.Error, classes: ["Light"]); + } + + private void OnConfirm() + { + ToastManager?.Show(new Toast("Confirmed"), NotificationType.Success, classes: ["Light"]); + } + + private async Task OnConfirmAsync() + { + await Task.Delay(3000); + ToastManager?.Show(new Toast("Async Confirmed"), NotificationType.Success, classes: ["Light"]); + } + + private void OnCancelAsync() + { + ToastManager?.Show(new Toast("Async Canceled"), NotificationType.Error, classes: ["Light"]); } } \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml b/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml index 7740c6c..6d8f92f 100644 --- a/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml +++ b/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml @@ -12,7 +12,7 @@ ContentTemplate="{TemplateBinding ContentTemplate}" /> diff --git a/src/Ursa/Controls/PopConfirm/PopConfirm.cs b/src/Ursa/Controls/PopConfirm/PopConfirm.cs index a4d13ef..3c73cf6 100644 --- a/src/Ursa/Controls/PopConfirm/PopConfirm.cs +++ b/src/Ursa/Controls/PopConfirm/PopConfirm.cs @@ -201,8 +201,11 @@ public class PopConfirm : ContentControl protected override bool RegisterContentPresenter(ContentPresenter presenter) { var result = base.RegisterContentPresenter(presenter); - _childChangeDisposable = presenter.GetPropertyChangedObservable(ContentPresenter.ChildProperty) - .Subscribe(OnChildChanged); + if (result) + { + _childChangeDisposable = presenter.GetPropertyChangedObservable(ContentPresenter.ChildProperty) + .Subscribe(OnChildChanged); + } return result; } From 5ba8789cf723c6322ab3c99b3b7c47e19a6be916 Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Fri, 18 Apr 2025 17:59:07 +0800 Subject: [PATCH 07/16] feat: update PopConfirm to support non-button triggers and rename Tap to Click --- demo/Ursa.Demo/Pages/PopConfirmDemo.axaml | 8 ++++++++ src/Ursa/Controls/PopConfirm/PopConfirm.cs | 2 +- src/Ursa/Controls/PopConfirm/PopConfirmTriggerMode.cs | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml b/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml index bee6bb4..38440a8 100644 --- a/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml +++ b/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml @@ -22,6 +22,14 @@ CancelCommand="{Binding Path=CancelCommand}" > + + + + Date: Fri, 18 Apr 2025 18:22:40 +0800 Subject: [PATCH 08/16] feat: improve icon and theme. --- demo/Ursa.Demo/Pages/PopConfirmDemo.axaml | 20 ++- .../Controls/PopConfirm.axaml | 134 ++++++++++++++---- src/Ursa/Controls/PopConfirm/PopConfirm.cs | 14 +- 3 files changed, 136 insertions(+), 32 deletions(-) diff --git a/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml b/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml index 38440a8..91b0191 100644 --- a/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml +++ b/demo/Ursa.Demo/Pages/PopConfirmDemo.axaml @@ -10,31 +10,43 @@ x:Class="Ursa.Demo.Pages.PopConfirmDemo"> - - - - + + + + + + diff --git a/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml b/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml index 6d8f92f..e2fa045 100644 --- a/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml +++ b/src/Ursa.Themes.Semi/Controls/PopConfirm.axaml @@ -1,49 +1,131 @@ + xmlns:u="https://irihi.tech/ursa" + xmlns:helpers="clr-namespace:Irihi.Avalonia.Shared.Helpers;assembly=Irihi.Avalonia.Shared"> - + + - - - - - - - + CancelCommand="{Binding Path=CancelCommand}"> + + CancelCommand="{Binding Path=CancelCommand}"> + + CancelCommand="{Binding Path=AsyncCancelCommand}"> +