From 8e085bc2648a8fcea2522da12e5d465c969d0e90 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Mon, 5 Feb 2024 17:56:36 +0800 Subject: [PATCH] feat: refactor layer storage and index reset rule. --- demo/Ursa.Demo/Pages/DrawerDemo.axaml | 7 +- src/Ursa.Themes.Semi/Controls/Dialog.axaml | 27 +++--- src/Ursa/Controls/Dialog/DialogControlBase.cs | 1 + .../Dialog/Options/OverlayDialogOptions.cs | 2 + src/Ursa/Controls/Dialog/OverlayDialog.cs | 2 + .../OverlayShared/OverlayDialogHost.Dialog.cs | 77 ++++++++-------- .../OverlayShared/OverlayDialogHost.Drawer.cs | 31 ++----- .../OverlayShared/OverlayDialogHost.Shared.cs | 87 ++++++++----------- 8 files changed, 100 insertions(+), 134 deletions(-) diff --git a/demo/Ursa.Demo/Pages/DrawerDemo.axaml b/demo/Ursa.Demo/Pages/DrawerDemo.axaml index 10d780a..0c968a2 100644 --- a/demo/Ursa.Demo/Pages/DrawerDemo.axaml +++ b/demo/Ursa.Demo/Pages/DrawerDemo.axaml @@ -4,11 +4,12 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" xmlns:vm="clr-namespace:Ursa.Demo.ViewModels;assembly=Ursa.Demo" + xmlns:u="https://irihi.tech/ursa" x:DataType="vm:DrawerDemoViewModel" x:CompileBindings="True" d:DesignHeight="450" x:Class="Ursa.Demo.Pages.DrawerDemo"> - - - + + + diff --git a/src/Ursa.Themes.Semi/Controls/Dialog.axaml b/src/Ursa.Themes.Semi/Controls/Dialog.axaml index 4c88769..b17f16c 100644 --- a/src/Ursa.Themes.Semi/Controls/Dialog.axaml +++ b/src/Ursa.Themes.Semi/Controls/Dialog.axaml @@ -59,8 +59,7 @@ @@ -87,7 +86,7 @@ @@ -98,7 +97,7 @@ @@ -109,7 +108,7 @@ @@ -120,8 +119,7 @@ @@ -375,7 +372,7 @@ @@ -386,7 +383,7 @@ @@ -397,7 +394,7 @@ @@ -408,7 +405,7 @@ diff --git a/src/Ursa/Controls/Dialog/DialogControlBase.cs b/src/Ursa/Controls/Dialog/DialogControlBase.cs index 4ef7688..700a8ce 100644 --- a/src/Ursa/Controls/Dialog/DialogControlBase.cs +++ b/src/Ursa/Controls/Dialog/DialogControlBase.cs @@ -27,6 +27,7 @@ public abstract class DialogControlBase: OverlayFeedbackElement internal double? HorizontalOffsetRatio { get; set; } internal double? VerticalOffsetRatio { get; set; } internal bool CanClickOnMaskToClose { get; set; } + internal bool CanLightDismiss { get; set; } protected internal Button? _closeButton; private Panel? _titleArea; diff --git a/src/Ursa/Controls/Dialog/Options/OverlayDialogOptions.cs b/src/Ursa/Controls/Dialog/Options/OverlayDialogOptions.cs index 559441c..4b86b7a 100644 --- a/src/Ursa/Controls/Dialog/Options/OverlayDialogOptions.cs +++ b/src/Ursa/Controls/Dialog/Options/OverlayDialogOptions.cs @@ -26,4 +26,6 @@ public class OverlayDialogOptions public DialogButton Buttons { get; set; } = DialogButton.OKCancel; public string? Title { get; set; } = null; public bool IsCloseButtonVisible { get; set; } = true; + + public bool CanLightDismiss { get; set; } } \ No newline at end of file diff --git a/src/Ursa/Controls/Dialog/OverlayDialog.cs b/src/Ursa/Controls/Dialog/OverlayDialog.cs index 36a5191..95b2ffa 100644 --- a/src/Ursa/Controls/Dialog/OverlayDialog.cs +++ b/src/Ursa/Controls/Dialog/OverlayDialog.cs @@ -191,6 +191,7 @@ public static class OverlayDialog options.VerticalAnchor == VerticalPosition.Center ? null : options.VerticalOffset; control.CanClickOnMaskToClose = options.CanClickOnMaskToClose; control.IsCloseButtonVisible = options.IsCloseButtonVisible; + control.CanLightDismiss = options.CanLightDismiss; } private static void ConfigureDefaultDialogControl(DefaultDialogControl control, OverlayDialogOptions? options) @@ -208,6 +209,7 @@ public static class OverlayDialog control.Mode = options.Mode; control.Buttons = options.Buttons; control.Title = options.Title; + control.CanLightDismiss = options.CanLightDismiss; } diff --git a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Dialog.cs b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Dialog.cs index bf9d97c..5c810d6 100644 --- a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Dialog.cs +++ b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Dialog.cs @@ -10,6 +10,7 @@ using Avalonia.Media; using Avalonia.Styling; using Avalonia.Utilities; using Ursa.Controls.OverlayShared; +using Ursa.Controls.Shapes; using Ursa.EventArgs; namespace Ursa.Controls; @@ -21,7 +22,7 @@ public partial class OverlayDialogHost public Thickness SnapThickness { get; set; } = new Thickness(0); - private void ResetDialogPosition(DialogControlBase control, Size oldSize, Size newSize) + private void ResetDialogPosition(DialogControlBase control, Size newSize) { var width = newSize.Width - control.Bounds.Width; var height = newSize.Height - control.Bounds.Height; @@ -82,8 +83,17 @@ public partial class OverlayDialogHost internal void AddDialog(DialogControlBase control) { + PureRectangle? mask = null; + if (control.CanLightDismiss) + { + CreateOverlayMask(false, control.CanLightDismiss); + } + if (mask is not null) + { + Children.Add(mask); + } this.Children.Add(control); - _dialogs.Add(control); + _layers.Add(new DialogPair(mask, control)); control.Measure(this.Bounds.Size); control.Arrange(new Rect(control.DesiredSize)); SetToPosition(control); @@ -96,27 +106,19 @@ public partial class OverlayDialogHost { if (sender is DialogControlBase control) { - Children.Remove(control); + var layer = _layers.FirstOrDefault(a => a.Element == control); + if (layer is null) return; + _layers.Remove(layer); + control.RemoveHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing); control.RemoveHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged); - if (_dialogs.Contains(control)) + + Children.Remove(control); + + if (layer.Mask is not null) { - _dialogs.Remove(control); - } - else if (_modalDialogs.Contains(control)) - { - if (_masks.Count > 0) - { - var last = _masks.Last(); - await _maskDisappearAnimation.RunAsync(last); - this.Children.Remove(last); - _masks.Remove(last); - if (_masks.Count > 0) - { - _masks.Last().IsVisible = true; - } - } - _modalDialogs.Remove(control); + await _maskDisappearAnimation.RunAsync(layer.Mask); + Children.Remove(layer.Mask); } ResetZIndices(); @@ -129,9 +131,8 @@ public partial class OverlayDialogHost /// internal void AddModalDialog(DialogControlBase control) { - var mask = CreateOverlayMask(control.CanClickOnMaskToClose); - _masks.Add(mask); - _modalDialogs.Add(control); + var mask = CreateOverlayMask(true, control.CanClickOnMaskToClose); + _layers.Add(new DialogPair(mask, control)); control.SetAsModal(true); ResetZIndices(); this.Children.Add(mask); @@ -148,41 +149,31 @@ public partial class OverlayDialogHost // Handle dialog layer change event private void OnDialogLayerChanged(object sender, DialogLayerChangeEventArgs e) { - if (sender is not CustomDialogControl control) + if (sender is not DialogControlBase control) return; - if (!_dialogs.Contains(control)) - return; - int index = _dialogs.IndexOf(control); - _dialogs.Remove(control); + var layer = _layers.FirstOrDefault(a => a.Element == control); + if (layer is null) return; + int index = _layers.IndexOf(layer); + _layers.Remove(layer); int newIndex = index; switch (e.ChangeType) { case DialogLayerChangeType.BringForward: - newIndex = MathUtilities.Clamp(index + 1, 0, _dialogs.Count); + newIndex = MathUtilities.Clamp(index + 1, 0, _layers.Count); break; case DialogLayerChangeType.SendBackward: - newIndex = MathUtilities.Clamp(index - 1, 0, _dialogs.Count); + newIndex = MathUtilities.Clamp(index - 1, 0, _layers.Count); break; case DialogLayerChangeType.BringToFront: - newIndex = _dialogs.Count; + newIndex = _layers.Count; break; case DialogLayerChangeType.SendToBack: newIndex = 0; break; } - _dialogs.Insert(newIndex, control); - for (int i = 0; i < _dialogs.Count; i++) - { - _dialogs[i].ZIndex = i; - } - - for (int i = 0; i < _masks.Count * 2; i += 2) - { - _masks[i].ZIndex = _dialogs.Count + i; - (_modalDialogs[i] as Control)!.ZIndex = _dialogs.Count + i + 1; - } - + _layers.Insert(newIndex, layer); + ResetZIndices(); } private void SetToPosition(DialogControlBase? control) diff --git a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs index d2a4564..edd93be 100644 --- a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs +++ b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs @@ -12,15 +12,9 @@ public partial class OverlayDialogHost { internal async void AddDrawer(DrawerControlBase control) { - var mask = CreateOverlayMask(false); + var mask = CreateOverlayMask(true, false); mask.Opacity = 0; - _masks.Add(mask); - _modalDialogs.Add(control); - // control.SetAsModal(true); - for (int i = 0; i < _masks.Count-1; i++) - { - _masks[i].Opacity = 0.5; - } + _layers.Add(new DialogPair(mask, control)); ResetZIndices(); this.Children.Add(mask); this.Children.Add(control); @@ -66,23 +60,16 @@ public partial class OverlayDialogHost { if (sender is DrawerControlBase control) { + var layer = _layers.FirstOrDefault(a => a.Element == control); + if(layer is null) return; + _layers.Remove(layer); + if (layer.Mask is not null) + { + layer.Mask.RemoveHandler(PointerPressedEvent, ClickMaskToCloseDialog); + } Children.Remove(control); control.RemoveHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing); control.RemoveHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged); - if (_modalDialogs.Contains(control)) - { - _modalDialogs.Remove(control); - if (_masks.Count > 0) - { - var last = _masks.Last(); - this.Children.Remove(last); - _masks.Remove(last); - if (_masks.Count > 0) - { - _masks.Last().IsVisible = true; - } - } - } ResetZIndices(); } } diff --git a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs index fc055ba..24ad63f 100644 --- a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs +++ b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs @@ -7,6 +7,7 @@ using Avalonia.Input; using Avalonia.Media; using Ursa.Controls.OverlayShared; using Avalonia.Layout; +using Avalonia.Media.Immutable; using Avalonia.Styling; using Ursa.Controls.Shapes; @@ -14,18 +15,21 @@ namespace Ursa.Controls; public partial class OverlayDialogHost: Canvas { - private readonly List _dialogs = new(); - private readonly List _modalDialogs = new(); - private readonly List _masks = new(); private static readonly Animation _maskAppearAnimation; private static readonly Animation _maskDisappearAnimation; private readonly List _layers = new List(); - private struct DialogPair + private class DialogPair { - internal PureRectangle Mask; - internal OverlayFeedbackElement Dialog; + internal PureRectangle? Mask; + internal OverlayFeedbackElement Element; + + public DialogPair(PureRectangle? mask, OverlayFeedbackElement element) + { + Mask = mask; + Element = element; + } } static OverlayDialogHost() @@ -63,33 +67,38 @@ public partial class OverlayDialogHost: Canvas set => SetValue(OverlayMaskBrushProperty, value); } - private PureRectangle CreateOverlayMask(bool canCloseOnClick) + private PureRectangle CreateOverlayMask(bool modal, bool canCloseOnClick) { PureRectangle rec = new() { - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, Width = this.Bounds.Width, Height = this.Bounds.Height, - [!Shape.FillProperty] = this[!OverlayMaskBrushProperty], IsVisible = true, }; + if (modal) + { + rec[!Shape.FillProperty] = this[!OverlayMaskBrushProperty]; + } + else if(canCloseOnClick) + { + rec.SetCurrentValue(Shape.FillProperty, Brushes.Transparent); + } if (canCloseOnClick) { - rec.AddHandler(PointerReleasedEvent, ClickBorderToCloseDialog); + rec.AddHandler(PointerReleasedEvent, ClickMaskToCloseDialog); } return rec; } - private void ClickBorderToCloseDialog(object sender, PointerReleasedEventArgs e) + private void ClickMaskToCloseDialog(object sender, PointerReleasedEventArgs e) { if (sender is PureRectangle border) { - int i = _masks.IndexOf(border); - if (_modalDialogs[i] is { } element) + var layer = _layers.FirstOrDefault(a => a.Mask == border); + if (layer is not null) { - element.Close(); - border.RemoveHandler(PointerReleasedEvent, ClickBorderToCloseDialog); + layer.Element.Close(); + border.RemoveHandler(PointerReleasedEvent, ClickMaskToCloseDialog); } } } @@ -110,26 +119,16 @@ public partial class OverlayDialogHost: Canvas protected sealed override void OnSizeChanged(SizeChangedEventArgs e) { base.OnSizeChanged(e); - for (int i = 0; i < _masks.Count; i++) + for (int i = 0; i < _layers.Count; i++) { - _masks[i].Width = this.Bounds.Width; - _masks[i].Height = this.Bounds.Height; - } - - var oldSize = e.PreviousSize; - var newSize = e.NewSize; - foreach (var dialog in _dialogs) - { - if (dialog is DialogControlBase c) + if (_layers[i].Mask is { } rect) { - ResetDialogPosition(c, oldSize, newSize); + rect.Width = this.Bounds.Width; + rect.Height = this.Bounds.Height; } - } - foreach (var modalDialog in _modalDialogs) - { - if (modalDialog is DialogControlBase c) + if (_layers[i].Element is DialogControlBase d) { - ResetDialogPosition(c, oldSize, newSize); + ResetDialogPosition(d, e.NewSize); } } } @@ -137,31 +136,17 @@ public partial class OverlayDialogHost: Canvas private void ResetZIndices() { int index = 0; - for ( int i = 0; i< _dialogs.Count; i++) - { - _dialogs[i].ZIndex = index; - index++; - } - for(int i = 0; i< _masks.Count; i++) - { - _masks[i].ZIndex = index; - index++; - (_modalDialogs[i] as Control)!.ZIndex = index; - index++; - } - - int index2 = 0; for (int i = 0; i < _layers.Count; i++) { if(_layers[i].Mask is { } mask) { - mask.ZIndex = index2; - index2++; + mask.ZIndex = index; + index++; } - if(_layers[i].Dialog is { } dialog) + if(_layers[i].Element is { } dialog) { - dialog.ZIndex = index2; - index2++; + dialog.ZIndex = index; + index++; } } }