Refactor DrawerLayer

This commit is contained in:
chinboy
2025-02-22 14:50:15 +08:00
parent edc8420631
commit 207c412f07
3 changed files with 164 additions and 24 deletions
+113 -14
View File
@@ -1,14 +1,18 @@
using System.Collections.Specialized;
using System.Diagnostics;
using System.Reflection;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.VisualTree;
namespace AtomUI.Controls;
internal class DrawerLayer : Canvas
public class DrawerLayer : Canvas
{
private const int DrawerLayerZIndex = int.MaxValue - 1000;
@@ -22,6 +26,15 @@ internal class DrawerLayer : Canvas
public static readonly AttachedProperty<Control?> DrawerContainerProperty =
AvaloniaProperty.RegisterAttached<DrawerLayer, Visual, Control?>("DrawerContainer");
public static readonly StyledProperty<Visual?> LayerHostProperty = AvaloniaProperty
.Register<DrawerLayer, Visual?>(nameof(LayerHost));
public Visual? LayerHost
{
get => GetValue(LayerHostProperty);
set => SetValue(LayerHostProperty, value);
}
#endregion
@@ -50,26 +63,49 @@ internal class DrawerLayer : Canvas
Children.CollectionChanged += ChildrenCollectionChanged;
}
public static Visual? GetAttachTargetElement(Visual drawerContainer)
public static Visual? GetAttachTargetElement(Visual control)
{
return drawerContainer.GetValue(AttachTargetElementProperty);
return control.GetValue(AttachTargetElementProperty);
}
public static void SetAttachTargetElement(Visual drawerContainer, Visual? attachTarget)
public static void SetAttachTargetElement(Visual control, Visual? attachTarget)
{
drawerContainer.SetValue(AttachTargetElementProperty, attachTarget);
control.SetValue(AttachTargetElementProperty, attachTarget);
}
public static DrawerLayer GetDrawerLayer(Visual visual)
public static DrawerLayer? GetDrawerLayer(Visual visual)
{
var layer = visual.FindAncestorOfType<VisualLayerManager>()?.GetVisualChildren()
.FirstOrDefault(c => c is DrawerLayer) as DrawerLayer;
if (layer == null)
Layoutable? layerHost = visual.FindAncestorOfType<ScrollContentPresenter>(true);
var attachTarget = GetAttachTargetElement(visual);
if (layerHost != null && attachTarget != null)
{
layer = new DrawerLayer();
AddLayerToVisualLayerManager(visual, layer, DrawerLayerZIndex);
while (layerHost != null && layerHost.IsVisualAncestorOf(attachTarget) == false)
{
layerHost = layerHost.FindAncestorOfType<ScrollContentPresenter>();
}
}
if (layerHost is ScrollContentPresenter scrollContentPresenter)
{
scrollContentPresenter.PropertyChanged += (sender, args) =>
{
if (args.Property == ScrollContentPresenter.OffsetProperty)
{
Console.WriteLine($"{attachTarget?.TranslatePoint(new Point(0, 0), TopLevel.GetTopLevel(visual)!)}");
}
};
}
layerHost ??= visual.FindAncestorOfType<VisualLayerManager>();
layerHost ??= TopLevel.GetTopLevel(visual);
if (layerHost == null)
{
return null;
}
var layer = layerHost.GetVisualChildren().FirstOrDefault(c => c is DrawerLayer) as DrawerLayer;
layer ??= InjectLayer(layerHost);
return layer;
}
@@ -180,9 +216,8 @@ internal class DrawerLayer : Canvas
}
info.Subscription = attachTarget.GetObservable(BoundsProperty).Subscribe(x =>
{
var attachTargetToplevel = TopLevel.GetTopLevel(attachTarget);
var translationMatrix = attachTargetToplevel != null
? attachTarget.TransformToVisual(attachTargetToplevel) ?? Matrix.Identity
var translationMatrix = LayerHost != null
? attachTarget.TransformToVisual(LayerHost) ?? Matrix.Identity
: Matrix.Identity;
info.Bounds = new TransformedBounds(new Rect(attachTarget.DesiredSize), new Rect(attachTarget.DesiredSize),
translationMatrix);
@@ -317,6 +352,70 @@ internal class DrawerLayer : Canvas
AddLayerMethodInfo.Invoke(visualLayerManager, [layer, zindex]);
}
}
private static void AddLayerToVisualLayerManager(VisualLayerManager visualLayerManager, Control layer, int zindex)
{
AddLayerMethodInfo.Invoke(visualLayerManager, [layer, zindex]);
}
private static DrawerLayer InjectLayer(Layoutable layerHost)
{
var drawerLayer = layerHost.FindDescendantOfType<DrawerLayer>();
if (drawerLayer != null)
{
return drawerLayer;
}
drawerLayer = new DrawerLayer
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
ZIndex = DrawerLayerZIndex
};
drawerLayer.LayerHost = layerHost;
if (layerHost is VisualLayerManager visualLayerManager)
{
AddLayerToVisualLayerManager(visualLayerManager, drawerLayer, visualLayerManager.ZIndex);
}
else if (layerHost is ScrollContentPresenter scrollContentPresenter)
{
if (scrollContentPresenter.Content is Control controlContent)
{
// 直接内容控件
scrollContentPresenter.Content = null;
scrollContentPresenter.UpdateChild();
var panel = new Panel
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
};
panel.Children.Add(controlContent);
panel.Children.Add(drawerLayer);
scrollContentPresenter.Content = panel;
}
else if (scrollContentPresenter.Content != null && scrollContentPresenter.ContentTemplate != null)
{
// 模版处理
var injectTemplate = new FuncDataTemplate<object?>((o, scope) =>
{
var originControl = scrollContentPresenter.ContentTemplate.Build(o);
var panel = new Panel
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
};
Debug.Assert(originControl != null);
panel.Children.Add(originControl);
panel.Children.Add(drawerLayer);
return panel;
});
scrollContentPresenter.ContentTemplate = injectTemplate;
}
}
return drawerLayer;
}
private class AttachTargetElementInfo
{
+46 -5
View File
@@ -1,5 +1,4 @@
using System.Diagnostics;
using AtomUI.Controls.Primitives;
using AtomUI.Data;
using AtomUI.Theme;
using AtomUI.Theme.Utils;
@@ -250,9 +249,19 @@ public class DrawerX : Control,
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == IsOpenProperty)
if (VisualRoot != null)
{
HandleIsOpenChanged();
if (change.Property == IsOpenProperty)
{
HandleIsOpenChanged();
}
}
if (change.Property == OpenOnProperty)
{
if (OpenOn != null)
{
DrawerLayer.SetAttachTargetElement(this, OpenOn);
}
}
}
@@ -270,9 +279,10 @@ public class DrawerX : Control,
private void Open()
{
CreateDrawerContainer();
var layer = DrawerLayer.GetDrawerLayer(this);
Debug.Assert(layer != null);
NotifyBeforeOpen(layer);
CreateDrawerContainer();
Debug.Assert(_container != null);
_container.Open(layer);
}
@@ -281,13 +291,13 @@ public class DrawerX : Control,
{
var layer = DrawerLayer.GetDrawerLayer(this);
Debug.Assert(layer != null);
NotifyBeforeClose(layer);
Debug.Assert(_container != null);
_container.Close(layer);
}
private void CreateDrawerContainer()
{
if (_container is null)
{
_container = new DrawerContainerX()
@@ -308,6 +318,37 @@ public class DrawerX : Control,
BindUtils.RelayBind(this, CloseWhenClickOnMaskProperty, _container, DrawerContainerX.CloseWhenClickOnMaskProperty);
}
}
protected internal virtual void NotifyBeforeOpen(DrawerLayer layer)
{
var current = Parent;
while (current != null && current.GetType() != typeof(DrawerLayer))
{
if (current is DrawerContainerX container)
{
if (container.Drawer != null && container.Drawer.TryGetTarget(out var drawer))
{
drawer.NotifyChildDrawerAboutToOpen(this);
}
}
current = current.Parent;
}
}
internal void NotifyChildDrawerAboutToOpen(DrawerX childDrawer)
{
Console.WriteLine(childDrawer);
if (_container != null)
{
_container.Background = Brushes.Brown;
}
}
protected internal virtual void NotifyBeforeClose(DrawerLayer layer)
{
}
protected internal virtual void NotifyOpened()
{
@@ -36,21 +36,21 @@ internal static class UIStructureUtils
((ISetLogicalParent)control).SetParent(parent);
}
public static void ClearVisualParentRecursive(Visual control, Control? parent)
public static void ClearVisualParentRecursive(Visual control)
{
SetVisualParent(control, null);
foreach (var child in control.GetVisualChildren())
{
ClearVisualParentRecursive(child, parent);
ClearVisualParentRecursive(child);
}
}
public static void ClearLogicalParentRecursive(ILogical control, Control? parent)
public static void ClearLogicalParentRecursive(ILogical control)
{
((ISetLogicalParent)control).SetParent(parent);
((ISetLogicalParent)control).SetParent(null);
foreach (var child in control.GetLogicalChildren())
{
ClearLogicalParentRecursive(child, parent);
ClearLogicalParentRecursive(child);
}
}