feat: replace mask with pure rectangle to reduce layout calculation

This commit is contained in:
rabbitism
2024-02-05 13:54:58 +08:00
parent 2ac5dfa170
commit 35b3a2f659
6 changed files with 99 additions and 18 deletions

View File

@@ -9,6 +9,12 @@
</ControlTheme> </ControlTheme>
<ControlTheme x:Key="{x:Type u:CustomDialogControl}" TargetType="u:CustomDialogControl"> <ControlTheme x:Key="{x:Type u:CustomDialogControl}" TargetType="u:CustomDialogControl">
<Setter Property="CornerRadius" Value="12" /> <Setter Property="CornerRadius" Value="12" />
<Setter Property="Transitions">
<Transitions>
<TransformOperationsTransition Duration="0.2" Property="RenderTransform"/>
</Transitions>
</Setter>
<Setter Property="RenderTransform" Value="scale(1.0)"></Setter>
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate TargetType="u:CustomDialogControl"> <ControlTemplate TargetType="u:CustomDialogControl">
<Border <Border
@@ -46,6 +52,9 @@
</Border> </Border>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
<Style Selector="^[IsClosed=True]">
<Setter Property="RenderTransform" Value="scale(0.95)"/>
</Style>
<Style Selector="^ /template/ Panel#PART_TitleArea"> <Style Selector="^ /template/ Panel#PART_TitleArea">
<Setter Property="ContextFlyout"> <Setter Property="ContextFlyout">
<MenuFlyout> <MenuFlyout>

View File

@@ -105,7 +105,6 @@ public partial class OverlayDialogHost
} }
else if (_modalDialogs.Contains(control)) else if (_modalDialogs.Contains(control))
{ {
_modalDialogs.Remove(control);
if (_masks.Count > 0) if (_masks.Count > 0)
{ {
var last = _masks.Last(); var last = _masks.Last();
@@ -117,8 +116,9 @@ public partial class OverlayDialogHost
_masks.Last().IsVisible = true; _masks.Last().IsVisible = true;
} }
} }
_modalDialogs.Remove(control);
} }
ResetZIndices(); ResetZIndices();
} }
} }
@@ -133,10 +133,6 @@ public partial class OverlayDialogHost
_masks.Add(mask); _masks.Add(mask);
_modalDialogs.Add(control); _modalDialogs.Add(control);
control.SetAsModal(true); control.SetAsModal(true);
for (int i = 0; i < _masks.Count-1; i++)
{
_masks[i].Opacity = 0.5;
}
ResetZIndices(); ResetZIndices();
this.Children.Add(mask); this.Children.Add(mask);
this.Children.Add(control); this.Children.Add(control);
@@ -146,6 +142,7 @@ public partial class OverlayDialogHost
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing); control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
control.AddHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged); control.AddHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
_maskAppearAnimation.RunAsync(mask); _maskAppearAnimation.RunAsync(mask);
control.IsClosed = false;
} }
// Handle dialog layer change event // Handle dialog layer change event

View File

@@ -25,8 +25,8 @@ public partial class OverlayDialogHost
this.Children.Add(mask); this.Children.Add(mask);
this.Children.Add(control); this.Children.Add(control);
control.Measure(this.Bounds.Size); control.Measure(this.Bounds.Size);
control.Arrange(new Rect(control.DesiredSize)); control.Arrange(new Rect(control.DesiredSize).WithHeight(this.Bounds.Height));
control.Height = this.Bounds.Height; // control.Height = this.Bounds.Height;
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDrawerControlClosing); control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDrawerControlClosing);
var animation = CreateAnimation(control.Bounds.Width); var animation = CreateAnimation(control.Bounds.Width);
var animation2 = CreateOpacityAnimation(); var animation2 = CreateOpacityAnimation();

View File

@@ -1,12 +1,14 @@
using Avalonia; using Avalonia;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using Ursa.Controls.OverlayShared; using Ursa.Controls.OverlayShared;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Styling; using Avalonia.Styling;
using Ursa.Controls.Shapes;
namespace Ursa.Controls; namespace Ursa.Controls;
@@ -14,9 +16,17 @@ public partial class OverlayDialogHost: Canvas
{ {
private readonly List<OverlayFeedbackElement> _dialogs = new(); private readonly List<OverlayFeedbackElement> _dialogs = new();
private readonly List<OverlayFeedbackElement> _modalDialogs = new(); private readonly List<OverlayFeedbackElement> _modalDialogs = new();
private readonly List<Border> _masks = new(); private readonly List<PureRectangle> _masks = new();
private static readonly Animation _maskAppearAnimation; private static readonly Animation _maskAppearAnimation;
private static readonly Animation _maskDisappearAnimation; private static readonly Animation _maskDisappearAnimation;
private readonly List<DialogPair> _layers = new List<DialogPair>();
private struct DialogPair
{
internal PureRectangle Mask;
internal OverlayFeedbackElement Dialog;
}
static OverlayDialogHost() static OverlayDialogHost()
{ {
@@ -29,9 +39,9 @@ public partial class OverlayDialogHost: Canvas
{ {
var animation = new Animation(); var animation = new Animation();
animation.FillMode = FillMode.Forward; animation.FillMode = FillMode.Forward;
var keyFrame1 = new KeyFrame(){ Cue = new Cue(0.0) }; var keyFrame1 = new KeyFrame{ Cue = new Cue(0.0) };
keyFrame1.Setters.Add(new Setter() { Property = OpacityProperty, Value = appear ? 0.0 : 1.0 }); keyFrame1.Setters.Add(new Setter() { Property = OpacityProperty, Value = appear ? 0.0 : 1.0 });
var keyFrame2 = new KeyFrame() { Cue = new Cue(1.0) }; var keyFrame2 = new KeyFrame{ Cue = new Cue(1.0) };
keyFrame2.Setters.Add(new Setter() { Property = OpacityProperty, Value = appear ? 1.0 : 0.0 }); keyFrame2.Setters.Add(new Setter() { Property = OpacityProperty, Value = appear ? 1.0 : 0.0 });
animation.Children.Add(keyFrame1); animation.Children.Add(keyFrame1);
animation.Children.Add(keyFrame2); animation.Children.Add(keyFrame2);
@@ -53,27 +63,27 @@ public partial class OverlayDialogHost: Canvas
set => SetValue(OverlayMaskBrushProperty, value); set => SetValue(OverlayMaskBrushProperty, value);
} }
private Border CreateOverlayMask(bool canCloseOnClick) private PureRectangle CreateOverlayMask(bool canCloseOnClick)
{ {
Border border = new() PureRectangle rec = new()
{ {
HorizontalAlignment = HorizontalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch,
Width = this.Bounds.Width, Width = this.Bounds.Width,
Height = this.Bounds.Height, Height = this.Bounds.Height,
[!BackgroundProperty] = this[!OverlayMaskBrushProperty], [!Shape.FillProperty] = this[!OverlayMaskBrushProperty],
IsVisible = true, IsVisible = true,
}; };
if (canCloseOnClick) if (canCloseOnClick)
{ {
border.AddHandler(PointerReleasedEvent, ClickBorderToCloseDialog); rec.AddHandler(PointerReleasedEvent, ClickBorderToCloseDialog);
} }
return border; return rec;
} }
private void ClickBorderToCloseDialog(object sender, PointerReleasedEventArgs e) private void ClickBorderToCloseDialog(object sender, PointerReleasedEventArgs e)
{ {
if (sender is Border border) if (sender is PureRectangle border)
{ {
int i = _masks.IndexOf(border); int i = _masks.IndexOf(border);
if (_modalDialogs[i] is { } element) if (_modalDialogs[i] is { } element)
@@ -139,6 +149,21 @@ public partial class OverlayDialogHost: Canvas
(_modalDialogs[i] as Control)!.ZIndex = index; (_modalDialogs[i] as Control)!.ZIndex = index;
index++; index++;
} }
int index2 = 0;
for (int i = 0; i < _layers.Count; i++)
{
if(_layers[i].Mask is { } mask)
{
mask.ZIndex = index2;
index2++;
}
if(_layers[i].Dialog is { } dialog)
{
dialog.ZIndex = index2;
index2++;
}
}
} }
internal IDataTemplate? GetDataTemplate(object? o) internal IDataTemplate? GetDataTemplate(object? o)

View File

@@ -8,11 +8,26 @@ namespace Ursa.Controls.OverlayShared;
public abstract class OverlayFeedbackElement: ContentControl public abstract class OverlayFeedbackElement: ContentControl
{ {
public static readonly StyledProperty<bool> IsClosedProperty =
AvaloniaProperty.Register<OverlayFeedbackElement, bool>(nameof(IsClosed), defaultValue: true);
public bool IsClosed
{
get => GetValue(IsClosedProperty);
set => SetValue(IsClosedProperty, value);
}
static OverlayFeedbackElement() static OverlayFeedbackElement()
{ {
DataContextProperty.Changed.AddClassHandler<CustomDialogControl, object?>((o, e) => o.OnDataContextChange(e)); DataContextProperty.Changed.AddClassHandler<CustomDialogControl, object?>((o, e) => o.OnDataContextChange(e));
ClosedEvent.AddClassHandler<OverlayFeedbackElement>((o,e)=>o.OnClosed(e));
} }
private void OnClosed(ResultEventArgs arg2)
{
SetCurrentValue(IsClosedProperty,true);
}
public static readonly RoutedEvent<ResultEventArgs> ClosedEvent = RoutedEvent.Register<DrawerControlBase, ResultEventArgs>( public static readonly RoutedEvent<ResultEventArgs> ClosedEvent = RoutedEvent.Register<DrawerControlBase, ResultEventArgs>(
nameof(Closed), RoutingStrategies.Bubble); nameof(Closed), RoutingStrategies.Bubble);

View File

@@ -0,0 +1,35 @@
using Avalonia;
using Avalonia.Controls.Shapes;
using Avalonia.Media;
namespace Ursa.Controls.Shapes;
/// <summary>
/// A rectangle, with no corner radius.
/// </summary>
public class PureRectangle: Shape
{
static PureRectangle()
{
FocusableProperty.OverrideDefaultValue<PureRectangle>(false);
AffectsGeometry<PureRectangle>(BoundsProperty);
}
protected override Geometry? CreateDefiningGeometry()
{
StreamGeometry geometry = new StreamGeometry();
Rect rect = new Rect(this.Bounds.Size).Deflate(this.StrokeThickness / 2.0);
using StreamGeometryContext context = geometry.Open();
context.BeginFigure(new Point(rect.Left, rect.Top), true);
context.LineTo(new Point(rect.Right, rect.Top));
context.LineTo(new Point(rect.Right, rect.Bottom));
context.LineTo(new Point(rect.Left, rect.Bottom));
context.LineTo(new Point(rect.Left, rect.Top));
context.EndFigure(true);
return geometry;
}
protected override Size MeasureOverride(Size availableSize)
{
return new Size(this.StrokeThickness, this.StrokeThickness);
}
}