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 x:Key="{x:Type u:CustomDialogControl}" TargetType="u:CustomDialogControl">
<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">
<ControlTemplate TargetType="u:CustomDialogControl">
<Border
@@ -46,6 +52,9 @@
</Border>
</ControlTemplate>
</Setter>
<Style Selector="^[IsClosed=True]">
<Setter Property="RenderTransform" Value="scale(0.95)"/>
</Style>
<Style Selector="^ /template/ Panel#PART_TitleArea">
<Setter Property="ContextFlyout">
<MenuFlyout>

View File

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

View File

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

View File

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

View File

@@ -8,11 +8,26 @@ namespace Ursa.Controls.OverlayShared;
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()
{
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>(
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);
}
}