From 0e91c844a70265223699468f985fc858275a8dd7 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Fri, 19 Jan 2024 15:31:30 +0800 Subject: [PATCH 01/28] feat: initialize dialog. --- src/Ursa/Controls/Dialog/DialogBox.cs | 20 +++++++++++ src/Ursa/Controls/Dialog/DialogControl.cs | 8 +++++ src/Ursa/Controls/Dialog/DialogHost.cs | 39 ++++++++++++++++++++++ src/Ursa/Controls/Dialog/IDialogContext.cs | 6 ++++ 4 files changed, 73 insertions(+) create mode 100644 src/Ursa/Controls/Dialog/DialogBox.cs create mode 100644 src/Ursa/Controls/Dialog/DialogControl.cs create mode 100644 src/Ursa/Controls/Dialog/DialogHost.cs create mode 100644 src/Ursa/Controls/Dialog/IDialogContext.cs diff --git a/src/Ursa/Controls/Dialog/DialogBox.cs b/src/Ursa/Controls/Dialog/DialogBox.cs new file mode 100644 index 0000000..996ee88 --- /dev/null +++ b/src/Ursa/Controls/Dialog/DialogBox.cs @@ -0,0 +1,20 @@ +using Avalonia.Controls; + +namespace Ursa.Controls; + +public static class DialogBox +{ + public static async Task ShowAsync() + { + return; + } + + public static async Task ShowAsync(TViewModel vm) + where TView: Control, new() + where TViewModel: new() + { + TView t = new TView(); + t.DataContext = vm; + return; + } +} \ No newline at end of file diff --git a/src/Ursa/Controls/Dialog/DialogControl.cs b/src/Ursa/Controls/Dialog/DialogControl.cs new file mode 100644 index 0000000..d46adc5 --- /dev/null +++ b/src/Ursa/Controls/Dialog/DialogControl.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace Ursa.Controls; + +public class DialogControl: ContentControl +{ + +} \ No newline at end of file diff --git a/src/Ursa/Controls/Dialog/DialogHost.cs b/src/Ursa/Controls/Dialog/DialogHost.cs new file mode 100644 index 0000000..7166146 --- /dev/null +++ b/src/Ursa/Controls/Dialog/DialogHost.cs @@ -0,0 +1,39 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Utilities; + +namespace Ursa.Controls; + +public class DialogHost: Canvas +{ + private Point _lastPoint; + + protected override void OnPointerMoved(PointerEventArgs e) + { + base.OnPointerMoved(e); + if (e.Source is DialogControl item) + { + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + { + var p = e.GetPosition(this); + var left= p.X - _lastPoint.X; + var top = p.Y - _lastPoint.Y; + left = MathUtilities.Clamp(left, 0, Bounds.Width - item.Bounds.Width); + top = MathUtilities.Clamp(top, 0, Bounds.Height - item.Bounds.Height); + Canvas.SetLeft(item, left); + Canvas.SetTop(item, top); + } + } + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + if (e.Source is DialogControl item) + { + _lastPoint = e.GetPosition(item); + } + } +} \ No newline at end of file diff --git a/src/Ursa/Controls/Dialog/IDialogContext.cs b/src/Ursa/Controls/Dialog/IDialogContext.cs new file mode 100644 index 0000000..b88b355 --- /dev/null +++ b/src/Ursa/Controls/Dialog/IDialogContext.cs @@ -0,0 +1,6 @@ +namespace Ursa.Controls; + +public interface IDialogContext +{ + +} \ No newline at end of file From 497a8c2d027602804ec5c52b1efe3466d41085e3 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sat, 20 Jan 2024 22:46:23 +0800 Subject: [PATCH 02/28] feat: setup demo. --- demo/Ursa.Demo/Models/MenuKeys.cs | 1 + demo/Ursa.Demo/Pages/DialogDemo.axaml | 29 +++++++++++++++++ demo/Ursa.Demo/Pages/DialogDemo.axaml.cs | 13 ++++++++ .../ViewModels/DialogDemoViewModel.cs | 31 +++++++++++++++++++ .../Ursa.Demo/ViewModels/MainViewViewModel.cs | 1 + demo/Ursa.Demo/ViewModels/MenuViewModel.cs | 1 + demo/Ursa.Demo/Views/MainView.axaml | 1 + src/Ursa/Controls/Dialog/DialogBox.cs | 19 +++++++++++- .../{DialogHost.cs => OverlayDialogHost.cs} | 28 +++++++++++++++-- .../Controls/Dialog/OverlayDialogManager.cs | 23 ++++++++++++++ 10 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 demo/Ursa.Demo/Pages/DialogDemo.axaml create mode 100644 demo/Ursa.Demo/Pages/DialogDemo.axaml.cs create mode 100644 demo/Ursa.Demo/ViewModels/DialogDemoViewModel.cs rename src/Ursa/Controls/Dialog/{DialogHost.cs => OverlayDialogHost.cs} (54%) create mode 100644 src/Ursa/Controls/Dialog/OverlayDialogManager.cs diff --git a/demo/Ursa.Demo/Models/MenuKeys.cs b/demo/Ursa.Demo/Models/MenuKeys.cs index b3e2753..64c2e37 100644 --- a/demo/Ursa.Demo/Models/MenuKeys.cs +++ b/demo/Ursa.Demo/Models/MenuKeys.cs @@ -6,6 +6,7 @@ public static class MenuKeys public const string MenuKeyBadge = "Badge"; public const string MenuKeyBanner = "Banner"; public const string MenuKeyButtonGroup = "ButtonGroup"; + public const string MenuKeyDialog = "Dialog"; public const string MenuKeyDivider = "Divider"; public const string MenuKeyDualBadge = "DualBadge"; public const string MenuKeyImageViewer = "ImageViewer"; diff --git a/demo/Ursa.Demo/Pages/DialogDemo.axaml b/demo/Ursa.Demo/Pages/DialogDemo.axaml new file mode 100644 index 0000000..4bc5427 --- /dev/null +++ b/demo/Ursa.Demo/Pages/DialogDemo.axaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/demo/Ursa.Demo/Pages/DialogDemo.axaml.cs b/demo/Ursa.Demo/Pages/DialogDemo.axaml.cs new file mode 100644 index 0000000..2453f1a --- /dev/null +++ b/demo/Ursa.Demo/Pages/DialogDemo.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Ursa.Demo.Pages; + +public partial class DialogDemo : UserControl +{ + public DialogDemo() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/DialogDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/DialogDemoViewModel.cs new file mode 100644 index 0000000..7210529 --- /dev/null +++ b/demo/Ursa.Demo/ViewModels/DialogDemoViewModel.cs @@ -0,0 +1,31 @@ +using System; +using System.Windows.Input; +using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Ursa.Controls; + +namespace Ursa.Demo.ViewModels; + +public class DialogDemoViewModel: ObservableObject +{ + public ICommand ShowLocalOverlayDialogCommand { get; } + public ICommand ShowGlobalOverlayDialogCommand { get; } + + public DialogDemoViewModel() + { + ShowLocalOverlayDialogCommand = new RelayCommand(ShowLocalOverlayDialog); + ShowGlobalOverlayDialogCommand = new RelayCommand(ShowGlobalOverlayDialog); + } + + private async void ShowGlobalOverlayDialog() + { + await DialogBox.ShowOverlayAsync(DateTime.Now, "GlobalHost"); + } + + private async void ShowLocalOverlayDialog() + { + await DialogBox.ShowOverlayAsync(DateTime.Now, "LocalHost"); + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs index 85312ef..a4a24ab 100644 --- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs @@ -28,6 +28,7 @@ public class MainViewViewModel : ViewModelBase MenuKeys.MenuKeyBadge => new BadgeDemoViewModel(), MenuKeys.MenuKeyBanner => new BannerDemoViewModel(), MenuKeys.MenuKeyButtonGroup => new ButtonGroupDemoViewModel(), + MenuKeys.MenuKeyDialog => new DialogDemoViewModel(), MenuKeys.MenuKeyDivider => new DividerDemoViewModel(), MenuKeys.MenuKeyDualBadge => new DualBadgeDemoViewModel(), MenuKeys.MenuKeyImageViewer => new ImageViewerDemoViewModel(), diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs index ea66a5e..0195ee8 100644 --- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs @@ -15,6 +15,7 @@ public class MenuViewModel: ViewModelBase new() { MenuHeader = "Badge", Key = MenuKeys.MenuKeyBadge }, new() { MenuHeader = "Banner", Key = MenuKeys.MenuKeyBanner }, new() { MenuHeader = "ButtonGroup", Key = MenuKeys.MenuKeyButtonGroup }, + new() { MenuHeader = "Dialog", Key = MenuKeys.MenuKeyDialog }, new() { MenuHeader = "Divider", Key = MenuKeys.MenuKeyDivider }, new() { MenuHeader = "DualBadge", Key = MenuKeys.MenuKeyDualBadge }, new() { MenuHeader = "IconButton", Key = MenuKeys.MenuKeyIconButton }, diff --git a/demo/Ursa.Demo/Views/MainView.axaml b/demo/Ursa.Demo/Views/MainView.axaml index 710026c..b6f6bb6 100644 --- a/demo/Ursa.Demo/Views/MainView.axaml +++ b/demo/Ursa.Demo/Views/MainView.axaml @@ -80,6 +80,7 @@ + diff --git a/src/Ursa/Controls/Dialog/DialogBox.cs b/src/Ursa/Controls/Dialog/DialogBox.cs index 996ee88..278a065 100644 --- a/src/Ursa/Controls/Dialog/DialogBox.cs +++ b/src/Ursa/Controls/Dialog/DialogBox.cs @@ -1,4 +1,7 @@ +using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Media; namespace Ursa.Controls; @@ -15,6 +18,20 @@ public static class DialogBox { TView t = new TView(); t.DataContext = vm; - return; + } + + public static async Task ShowOverlayAsync(TViewModel vm, string hostId) + where TView : Control, new() + where TViewModel: new() + { + var t = new Border() + { + Width = 100, Height = 100, Background = Brushes.Aqua, BorderBrush = Brushes.Yellow, + BorderThickness = new Thickness(1) + }; + t.DataContext = vm; + var host = OverlayDialogManager.GetOverlayDialogHost(hostId); + host?.Children.Add(t); + return null; } } \ No newline at end of file diff --git a/src/Ursa/Controls/Dialog/DialogHost.cs b/src/Ursa/Controls/Dialog/OverlayDialogHost.cs similarity index 54% rename from src/Ursa/Controls/Dialog/DialogHost.cs rename to src/Ursa/Controls/Dialog/OverlayDialogHost.cs index 7166146..cdef578 100644 --- a/src/Ursa/Controls/Dialog/DialogHost.cs +++ b/src/Ursa/Controls/Dialog/OverlayDialogHost.cs @@ -2,18 +2,40 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Input; +using Avalonia.Media; using Avalonia.Utilities; namespace Ursa.Controls; -public class DialogHost: Canvas +public class OverlayDialogHost: Canvas { + public static readonly StyledProperty HostIdProperty = AvaloniaProperty.Register( + nameof(HostId)); + + public string HostId + { + get => GetValue(HostIdProperty); + set => SetValue(HostIdProperty, value); + } + private Point _lastPoint; + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + OverlayDialogManager.RegisterOverlayDialogHost(this, HostId); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + OverlayDialogManager.UnregisterOverlayDialogHost(HostId); + base.OnDetachedFromVisualTree(e); + } + protected override void OnPointerMoved(PointerEventArgs e) { base.OnPointerMoved(e); - if (e.Source is DialogControl item) + if (e.Source is Control item) { if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { @@ -31,7 +53,7 @@ public class DialogHost: Canvas protected override void OnPointerPressed(PointerPressedEventArgs e) { base.OnPointerPressed(e); - if (e.Source is DialogControl item) + if (e.Source is Control item) { _lastPoint = e.GetPosition(item); } diff --git a/src/Ursa/Controls/Dialog/OverlayDialogManager.cs b/src/Ursa/Controls/Dialog/OverlayDialogManager.cs new file mode 100644 index 0000000..1dc1480 --- /dev/null +++ b/src/Ursa/Controls/Dialog/OverlayDialogManager.cs @@ -0,0 +1,23 @@ +using System.Collections.Concurrent; + +namespace Ursa.Controls; + +internal static class OverlayDialogManager +{ + private static ConcurrentDictionary _hosts = new(); + + public static void RegisterOverlayDialogHost(OverlayDialogHost host, string id) + { + _hosts.TryAdd(id, host); + } + + public static void UnregisterOverlayDialogHost(string id) + { + _hosts.TryRemove(id, out _); + } + + public static OverlayDialogHost? GetOverlayDialogHost(string id) + { + return _hosts.TryGetValue(id, out var host) ? host : null; + } +} \ No newline at end of file From a0598b6a47e0624138aaff4133511a9d8380262c Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sun, 21 Jan 2024 15:45:12 +0800 Subject: [PATCH 03/28] WIP --- src/Ursa/Controls/Dialog/DialogBox.cs | 6 +++--- src/Ursa/Controls/Dialog/OverlayDialogOptions.cs | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 src/Ursa/Controls/Dialog/OverlayDialogOptions.cs diff --git a/src/Ursa/Controls/Dialog/DialogBox.cs b/src/Ursa/Controls/Dialog/DialogBox.cs index 278a065..38a3641 100644 --- a/src/Ursa/Controls/Dialog/DialogBox.cs +++ b/src/Ursa/Controls/Dialog/DialogBox.cs @@ -24,10 +24,10 @@ public static class DialogBox where TView : Control, new() where TViewModel: new() { - var t = new Border() + var t = new DialogControl() { - Width = 100, Height = 100, Background = Brushes.Aqua, BorderBrush = Brushes.Yellow, - BorderThickness = new Thickness(1) + Content = new TView(){ DataContext = vm }, + DataContext = vm, }; t.DataContext = vm; var host = OverlayDialogManager.GetOverlayDialogHost(hostId); diff --git a/src/Ursa/Controls/Dialog/OverlayDialogOptions.cs b/src/Ursa/Controls/Dialog/OverlayDialogOptions.cs new file mode 100644 index 0000000..0f6c489 --- /dev/null +++ b/src/Ursa/Controls/Dialog/OverlayDialogOptions.cs @@ -0,0 +1,6 @@ +namespace Ursa.Controls; + +public record OverlayDialogOptions +{ + public bool ShowCloseButton { get; set; } = true; +} \ No newline at end of file From 2c361f9f3c53d3968f92ac3f1b47989f1078a8c2 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sun, 21 Jan 2024 23:40:06 +0800 Subject: [PATCH 04/28] feat: WIP dialog window. --- demo/Ursa.Demo/Pages/DialogDemo.axaml | 1 + .../ViewModels/DialogDemoViewModel.cs | 17 ++++-- src/Ursa.Themes.Semi/Controls/Dialog.axaml | 52 +++++++++++++++++++ src/Ursa.Themes.Semi/Controls/_index.axaml | 1 + src/Ursa/Controls/Dialog/DialogBox.cs | 45 +++++++++++++--- src/Ursa/Controls/Dialog/DialogControl.cs | 19 +++++++ src/Ursa/Controls/Dialog/DialogWindow.cs | 30 +++++++++++ src/Ursa/Controls/Dialog/IDialogContext.cs | 3 +- src/Ursa/Controls/Dialog/OverlayDialogHost.cs | 44 +++++++++++++--- 9 files changed, 193 insertions(+), 19 deletions(-) create mode 100644 src/Ursa.Themes.Semi/Controls/Dialog.axaml create mode 100644 src/Ursa/Controls/Dialog/DialogWindow.cs diff --git a/demo/Ursa.Demo/Pages/DialogDemo.axaml b/demo/Ursa.Demo/Pages/DialogDemo.axaml index 4bc5427..24e36eb 100644 --- a/demo/Ursa.Demo/Pages/DialogDemo.axaml +++ b/demo/Ursa.Demo/Pages/DialogDemo.axaml @@ -13,6 +13,7 @@ mc:Ignorable="d"> + diff --git a/demo/Ursa.Demo/ViewModels/DialogDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/DialogDemoViewModel.cs index 7210529..e96ee23 100644 --- a/demo/Ursa.Demo/ViewModels/DialogDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/DialogDemoViewModel.cs @@ -1,10 +1,12 @@ using System; +using System.Threading.Tasks; using System.Windows.Input; using Avalonia.Controls; using Avalonia.Controls.Shapes; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Ursa.Controls; +using Ursa.Demo.Pages; namespace Ursa.Demo.ViewModels; @@ -12,19 +14,26 @@ public class DialogDemoViewModel: ObservableObject { public ICommand ShowLocalOverlayDialogCommand { get; } public ICommand ShowGlobalOverlayDialogCommand { get; } + public ICommand ShowGlobalDialogCommand { get; } public DialogDemoViewModel() { - ShowLocalOverlayDialogCommand = new RelayCommand(ShowLocalOverlayDialog); - ShowGlobalOverlayDialogCommand = new RelayCommand(ShowGlobalOverlayDialog); + ShowLocalOverlayDialogCommand = new AsyncRelayCommand(ShowLocalOverlayDialog); + ShowGlobalOverlayDialogCommand = new AsyncRelayCommand(ShowGlobalOverlayDialog); + ShowGlobalDialogCommand = new AsyncRelayCommand(ShowGlobalDialog); } - private async void ShowGlobalOverlayDialog() + private async Task ShowGlobalDialog() + { + await DialogBox.ShowAsync(new BadgeDemoViewModel()); + } + + private async Task ShowGlobalOverlayDialog() { await DialogBox.ShowOverlayAsync(DateTime.Now, "GlobalHost"); } - private async void ShowLocalOverlayDialog() + private async Task ShowLocalOverlayDialog() { await DialogBox.ShowOverlayAsync(DateTime.Now, "LocalHost"); } diff --git a/src/Ursa.Themes.Semi/Controls/Dialog.axaml b/src/Ursa.Themes.Semi/Controls/Dialog.axaml new file mode 100644 index 0000000..62355cb --- /dev/null +++ b/src/Ursa.Themes.Semi/Controls/Dialog.axaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Full + BorderOnly + + + + + + + + + + + + + + + + + + + diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml index edf9abb..e9ab67e 100644 --- a/src/Ursa.Themes.Semi/Controls/_index.axaml +++ b/src/Ursa.Themes.Semi/Controls/_index.axaml @@ -4,6 +4,7 @@ + diff --git a/src/Ursa/Controls/Dialog/DialogBox.cs b/src/Ursa/Controls/Dialog/DialogBox.cs index 38a3641..980cf57 100644 --- a/src/Ursa/Controls/Dialog/DialogBox.cs +++ b/src/Ursa/Controls/Dialog/DialogBox.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Shapes; using Avalonia.Media; @@ -7,18 +8,46 @@ namespace Ursa.Controls; public static class DialogBox { - public static async Task ShowAsync() + public static async Task ShowAsync(TViewModel vm) where TView : Control, new() { - return; + var window = new DialogWindow() + { + Content = new TView() + { + DataContext = vm, + }, + DataContext = vm, + }; + var lifetime = Application.Current?.ApplicationLifetime; + if (lifetime is IClassicDesktopStyleApplicationLifetime classLifetime) + { + var main = classLifetime.MainWindow; + if (main is null) + { + window.Show(); + return default(TResult); + } + else + { + var result = await window.ShowDialog(main); + return result; + } + } + else + { + return default(TResult); + } } - - public static async Task ShowAsync(TViewModel vm) - where TView: Control, new() - where TViewModel: new() + + public static async Task ShowAsync(Window owner, TViewModel vm) where + TView: Control, new() { - TView t = new TView(); - t.DataContext = vm; + var window = new DialogWindow(); + window.Content = new TView(); + window.DataContext = vm; + return await window.ShowDialog(owner); } + public static async Task ShowOverlayAsync(TViewModel vm, string hostId) where TView : Control, new() diff --git a/src/Ursa/Controls/Dialog/DialogControl.cs b/src/Ursa/Controls/Dialog/DialogControl.cs index d46adc5..e3cae7a 100644 --- a/src/Ursa/Controls/Dialog/DialogControl.cs +++ b/src/Ursa/Controls/Dialog/DialogControl.cs @@ -1,8 +1,27 @@ using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; namespace Ursa.Controls; +[TemplatePart(PART_CloseButton, typeof(Button))] public class DialogControl: ContentControl { + public const string PART_CloseButton = "PART_CloseButton"; + + + private Button? _closeButton; + public event EventHandler OnClose; + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + } + + public void Show() + { + + } } \ No newline at end of file diff --git a/src/Ursa/Controls/Dialog/DialogWindow.cs b/src/Ursa/Controls/Dialog/DialogWindow.cs new file mode 100644 index 0000000..9ec685e --- /dev/null +++ b/src/Ursa/Controls/Dialog/DialogWindow.cs @@ -0,0 +1,30 @@ +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; + +namespace Ursa.Controls; + +[TemplatePart(PART_CloseButton, typeof(Button))] +public class DialogWindow: Window +{ + public const string PART_CloseButton = "PART_CloseButton"; + + protected override Type StyleKeyOverride { get; } = typeof(DialogWindow); + + private Button? _closeButton; + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + _closeButton = e.NameScope.Find