using System.Linq; using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives { public class OverlayLayer : Control { /// /// Defines the Left attached property. /// public static readonly AttachedProperty LeftProperty = AvaloniaProperty.RegisterAttached("Left", 0); /// /// Defines the Top attached property. /// public static readonly AttachedProperty TopProperty = AvaloniaProperty.RegisterAttached("Top", 0); /// /// Defines the InfiniteAvailableSize attached property. /// public static readonly AttachedProperty InfiniteAvailableSizeProperty = AvaloniaProperty.RegisterAttached("InfiniteAvailableSize", false); static OverlayLayer() { foreach (var p in new []{LeftProperty, TopProperty}) { p.Changed.AddClassHandler((target, e) => { if (target.GetVisualParent() is OverlayLayer layer) layer.InvalidateArrange(); }); } } public Size AvailableSize { get; private set; } /// /// Gets the value of the Left attached property for a control. /// /// The control. /// The control's left coordinate. public static double GetLeft(AvaloniaObject element) { return element.GetValue(LeftProperty); } /// /// Sets the value of the Left attached property for a control. /// /// The control. /// The left value. public static void SetLeft(AvaloniaObject element, double value) { element.SetValue(LeftProperty, value); } /// /// Gets the value of the Top attached property for a control. /// /// The control. /// The control's top coordinate. public static double GetTop(AvaloniaObject element) { return element.GetValue(TopProperty); } /// /// Sets the value of the Top attached property for a control. /// /// The control. /// The top value. public static void SetTop(AvaloniaObject element, double value) { element.SetValue(TopProperty, value); } /// /// Gets the value of the Top attached property for a control. /// /// The control. /// The control's top coordinate. public static bool GetInfiniteAvailableSize(AvaloniaObject element) { return element.GetValue(InfiniteAvailableSizeProperty); } /// /// Sets the value of the Top attached property for a control. /// /// The control. /// The top value. public static void SetInfiniteAvailableSize(AvaloniaObject element, bool value) { element.SetValue(InfiniteAvailableSizeProperty, value); } public static OverlayLayer GetOverlayLayer(IVisual visual) { foreach(var v in visual.GetVisualAncestors()) if(v is VisualLayerManager vlm) if (vlm.OverlayLayer != null) return vlm.OverlayLayer; if (visual is TopLevel tl) { var layers = tl.GetVisualDescendants().OfType().FirstOrDefault(); return layers?.OverlayLayer; } return null; } public void Add(Control v) { VisualChildren.Add(v); InvalidateMeasure(); InvalidateArrange(); } public void Remove(Control v) => VisualChildren.Remove(v); protected override Size MeasureOverride(Size availableSize) { var infinite = new Size(double.PositiveInfinity, double.PositiveInfinity); foreach (Control v in VisualChildren) v.Measure(GetInfiniteAvailableSize(v) ? infinite : availableSize); return new Size(); } protected override Size ArrangeOverride(Size finalSize) { // We are saving it here since child controls might need to know the entire size of the overlay // and Bounds won't be updated in time AvailableSize = finalSize; foreach (Control v in VisualChildren) v.Arrange(new Rect(GetLeft(v), GetTop(v), v.DesiredSize.Width, v.DesiredSize.Height)); return finalSize; } } }