feat: refactor layer storage and index reset rule.

This commit is contained in:
rabbitism
2024-02-05 17:56:36 +08:00
parent 35b3a2f659
commit 8e085bc264
8 changed files with 100 additions and 134 deletions

View File

@@ -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">
<StackPanel HorizontalAlignment="Left">
<Button Content="Call Drawer" Command="{Binding OpenDrawerCommand}"></Button>
</StackPanel>
<Canvas HorizontalAlignment="Left" Width="500">
<Button Content="Call Drawer" HorizontalAlignment="Stretch" Command="{Binding OpenDrawerCommand}"></Button>
</Canvas>
</UserControl>

View File

@@ -59,8 +59,7 @@
<Setter Property="ContextFlyout">
<MenuFlyout>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].CloseDialog}"
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
Command="{Binding $parent[u:DialogControlBase].Close}"
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem.Icon>
<PathIcon
@@ -76,7 +75,7 @@
<Setter Property="ContextFlyout">
<MenuFlyout>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].UpdateLayer}"
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
Header="{DynamicResource STRING_MENU_BRING_FORWARD}">
<MenuItem.Icon>
@@ -87,7 +86,7 @@
</MenuItem.Icon>
</MenuItem>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].UpdateLayer}"
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
CommandParameter="{x:Static u:DialogLayerChangeType.BringToFront}"
Header="{DynamicResource STRING_MENU_BRING_TO_FRONT}">
<MenuItem.Icon>
@@ -98,7 +97,7 @@
</MenuItem.Icon>
</MenuItem>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].UpdateLayer}"
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
CommandParameter="{x:Static u:DialogLayerChangeType.SendBackward}"
Header="{DynamicResource STRING_MENU_SEND_BACKWARD}">
<MenuItem.Icon>
@@ -109,7 +108,7 @@
</MenuItem.Icon>
</MenuItem>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].UpdateLayer}"
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
CommandParameter="{x:Static u:DialogLayerChangeType.SendToBack}"
Header="{DynamicResource STRING_MENU_SEND_TO_BACK}">
<MenuItem.Icon>
@@ -120,8 +119,7 @@
</MenuItem.Icon>
</MenuItem>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].CloseDialog}"
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
Command="{Binding $parent[u:DialogControlBase].Close}"
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem.Icon>
<PathIcon
@@ -347,8 +345,7 @@
<Setter Property="ContextFlyout">
<MenuFlyout>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].CloseDialog}"
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
Command="{Binding $parent[u:DialogControlBase].Close}"
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem.Icon>
<PathIcon
@@ -364,7 +361,7 @@
<Setter Property="ContextFlyout">
<MenuFlyout>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].UpdateLayer}"
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
Header="{DynamicResource STRING_MENU_BRING_FORWARD}">
<MenuItem.Icon>
@@ -375,7 +372,7 @@
</MenuItem.Icon>
</MenuItem>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].UpdateLayer}"
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
CommandParameter="{x:Static u:DialogLayerChangeType.BringToFront}"
Header="{DynamicResource STRING_MENU_BRING_TO_FRONT}">
<MenuItem.Icon>
@@ -386,7 +383,7 @@
</MenuItem.Icon>
</MenuItem>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].UpdateLayer}"
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
CommandParameter="{x:Static u:DialogLayerChangeType.SendBackward}"
Header="{DynamicResource STRING_MENU_SEND_BACKWARD}">
<MenuItem.Icon>
@@ -397,7 +394,7 @@
</MenuItem.Icon>
</MenuItem>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].UpdateLayer}"
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
CommandParameter="{x:Static u:DialogLayerChangeType.SendToBack}"
Header="{DynamicResource STRING_MENU_SEND_TO_BACK}">
<MenuItem.Icon>
@@ -408,7 +405,7 @@
</MenuItem.Icon>
</MenuItem>
<MenuItem
Command="{Binding $parent[u:CustomDialogControl].CloseDialog}"
Command="{Binding $parent[u:DialogControlBase].Close}"
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem.Icon>

View File

@@ -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;

View File

@@ -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; }
}

View File

@@ -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;
}

View File

@@ -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
/// <param name="control"></param>
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)

View File

@@ -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();
}
}

View File

@@ -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<OverlayFeedbackElement> _dialogs = new();
private readonly List<OverlayFeedbackElement> _modalDialogs = new();
private readonly List<PureRectangle> _masks = new();
private static readonly Animation _maskAppearAnimation;
private static readonly Animation _maskDisappearAnimation;
private readonly List<DialogPair> _layers = new List<DialogPair>();
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;
if (_layers[i].Mask is { } rect)
{
rect.Width = this.Bounds.Width;
rect.Height = this.Bounds.Height;
}
var oldSize = e.PreviousSize;
var newSize = e.NewSize;
foreach (var dialog in _dialogs)
if (_layers[i].Element is DialogControlBase d)
{
if (dialog is DialogControlBase c)
{
ResetDialogPosition(c, oldSize, newSize);
}
}
foreach (var modalDialog in _modalDialogs)
{
if (modalDialog is DialogControlBase c)
{
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++;
}
}
}