From 207c412f0719e2af01b7f47e1ca29c171be6d41f Mon Sep 17 00:00:00 2001 From: chinboy Date: Sat, 22 Feb 2025 14:50:15 +0800 Subject: [PATCH] Refactor DrawerLayer --- src/AtomUI.Controls/Drawer/DrawerLayer.cs | 127 ++++++++++++++++-- src/AtomUI.Controls/Drawer/DrawerX.cs | 51 ++++++- src/AtomUI.Controls/Utils/UIStructureUtils.cs | 10 +- 3 files changed, 164 insertions(+), 24 deletions(-) diff --git a/src/AtomUI.Controls/Drawer/DrawerLayer.cs b/src/AtomUI.Controls/Drawer/DrawerLayer.cs index 8db756269..cfdcf99df 100644 --- a/src/AtomUI.Controls/Drawer/DrawerLayer.cs +++ b/src/AtomUI.Controls/Drawer/DrawerLayer.cs @@ -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 DrawerContainerProperty = AvaloniaProperty.RegisterAttached("DrawerContainer"); + + public static readonly StyledProperty LayerHostProperty = AvaloniaProperty + .Register(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()?.GetVisualChildren() - .FirstOrDefault(c => c is DrawerLayer) as DrawerLayer; - if (layer == null) + Layoutable? layerHost = visual.FindAncestorOfType(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(); + } } + 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(); + 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(); + 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((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 { diff --git a/src/AtomUI.Controls/Drawer/DrawerX.cs b/src/AtomUI.Controls/Drawer/DrawerX.cs index 8999ed06b..5f8da222a 100644 --- a/src/AtomUI.Controls/Drawer/DrawerX.cs +++ b/src/AtomUI.Controls/Drawer/DrawerX.cs @@ -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() { diff --git a/src/AtomUI.Controls/Utils/UIStructureUtils.cs b/src/AtomUI.Controls/Utils/UIStructureUtils.cs index 3280aa12e..b99afd65d 100644 --- a/src/AtomUI.Controls/Utils/UIStructureUtils.cs +++ b/src/AtomUI.Controls/Utils/UIStructureUtils.cs @@ -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); } }