From 9bb71a248467f2827518b652797441e647d7ed30 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 21 May 2020 14:46:49 +0200 Subject: [PATCH] Added/tweaked docs. --- .../Primitives/IPopupHost.cs | 46 +++ .../PopupPositioning/IPopupPositioner.cs | 269 +++++++++++------- .../ManagedPopupPositioner.cs | 4 + 3 files changed, 218 insertions(+), 101 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs index 74a3ca8818..bb4daf38e1 100644 --- a/src/Avalonia.Controls/Primitives/IPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs @@ -5,19 +5,65 @@ using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives { + /// + /// Represents the top-level control opened by a . + /// + /// + /// A popup host can be either be a popup window created by the operating system + /// () or an which is created + /// on an . + /// public interface IPopupHost : IDisposable { + /// + /// Sets the control to display in the popup. + /// + /// void SetChild(IControl control); + + /// + /// Gets the presenter from the control's template. + /// IContentPresenter Presenter { get; } + + /// + /// Gets the root of the visual tree in the case where the popup is presented using a + /// separate visual tree. + /// IVisual HostedVisualTreeRoot { get; } + /// + /// Raised when the control's template is applied. + /// event EventHandler TemplateApplied; + /// + /// Configures the position of the popup according to a target control and a set of + /// placement parameters. + /// + /// The placement target. + /// The placement mode. + /// The offset, in device-independent pixels. + /// The anchor point. + /// The anchor gravity. void ConfigurePosition(IVisual target, PlacementMode placement, Point offset, PopupPositioningEdge anchor = PopupPositioningEdge.None, PopupPositioningEdge gravity = PopupPositioningEdge.None); + + /// + /// Shows the popup. + /// void Show(); + + /// + /// Hides the popup. + /// void Hide(); + + /// + /// Binds the constraints of the popup host to a set of properties, usally those present on + /// . + /// IDisposable BindConstraints(AvaloniaObject popup, StyledProperty widthProperty, StyledProperty minWidthProperty, StyledProperty maxWidthProperty, StyledProperty heightProperty, StyledProperty minHeightProperty, diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs index f0358ec04f..d2a403b602 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs @@ -50,45 +50,47 @@ using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives.PopupPositioning { /// - /// - /// The IPopupPositioner provides a collection of rules for the placement of a - /// a popup relative to its parent. Rules can be defined to ensure - /// the popup remains within the visible area's borders, and to - /// specify how the popup changes its position, such as sliding along - /// an axis, or flipping around a rectangle. These positioner-created rules are - /// constrained by the requirement that a popup must intersect with or - /// be at least partially adjacent to its parent surface. + /// Provides positioning parameters to . /// + /// + /// The IPopupPositioner provides a collection of rules for the placement of a a popup relative + /// to its parent. Rules can be defined to ensure the popup remains within the visible area's + /// borders, and to specify how the popup changes its position, such as sliding along an axis, + /// or flipping around a rectangle. These positioner-created rules are constrained by the + /// requirement that a popup must intersect with or be at least partially adjacent to its parent + /// surface. + /// public struct PopupPositionerParameters { private PopupPositioningEdge _gravity; private PopupPositioningEdge _anchor; /// - /// Set the size of the popup that is to be positioned with the positioner - /// object. The size is in scaled coordinates. + /// Set the size of the popup that is to be positioned with the positioner object, in device- + /// independent pixels. /// public Size Size { get; set; } /// - /// Specify the anchor rectangle within the parent that the popup - /// will be placed relative to. The rectangle is relative to the - /// parent geometry - /// - /// The anchor rectangle may not extend outside the window geometry of the - /// popup's parent. The anchor rectangle is in scaled coordinates + /// Specifies the anchor rectangle within the parent that the popup will be placed relative + /// to, in device-independent pixels. /// + /// + /// The rectangle is relative to the parent geometry and may not extend outside the window + /// geometry of the popup's parent. + /// public Rect AnchorRectangle { get; set; } - /// - /// Defines the anchor point for the anchor rectangle. The specified anchor - /// is used derive an anchor point that the popup will be - /// positioned relative to. If a corner anchor is set (e.g. 'TopLeft' or - /// 'BottomRight'), the anchor point will be at the specified corner; - /// otherwise, the derived anchor point will be centered on the specified - /// edge, or in the center of the anchor rectangle if no edge is specified. + /// Defines the anchor point for the anchor rectangle. /// + /// + /// The specified anchor is used derive an anchor point that the popup will be positioned + /// relative to. If a corner anchor is set (e.g. 'TopLeft' or 'BottomRight'), the anchor + /// point will be at the specified corner; otherwise, the derived anchor point will be + /// centered on the specified edge, or in the center of the anchor rectangle if no edge is + /// specified. + /// public PopupPositioningEdge Anchor { get => _anchor; @@ -100,13 +102,14 @@ namespace Avalonia.Controls.Primitives.PopupPositioning } /// - /// Defines in what direction a popup should be positioned, relative to - /// the anchor point of the parent. If a corner gravity is - /// specified (e.g. 'BottomRight' or 'TopLeft'), then the popup - /// will be placed towards the specified gravity; otherwise, the popup - /// will be centered over the anchor point on any axis that had no - /// gravity specified. + /// Defines in what direction a popup should be positioned, relative to the anchor point of + /// the parent. /// + /// + /// If a corner gravity is specified (e.g. 'BottomRight' or 'TopLeft'), then the popup will + /// be placed towards the specified gravity; otherwise, the popup will be centered over the + /// anchor point on any axis that had no gravity specified. + /// public PopupPositioningEdge Gravity { get => _gravity; @@ -118,48 +121,51 @@ namespace Avalonia.Controls.Primitives.PopupPositioning } /// - /// Specify how the popup should be positioned if the originally intended - /// position caused the popup to be constrained, meaning at least - /// partially outside positioning boundaries set by the positioner. The - /// adjustment is set by constructing a bitmask describing the adjustment to - /// be made when the popup is constrained on that axis. + /// Specify how the popup should be positioned if the originally intended position caused + /// the popup to be constrained. + /// + /// + /// Adjusts the popup position if the intended position caused the popup to be constrained; + /// meaning at least partially outside positioning boundaries set by the positioner. The + /// adjustment is set by constructing a bitmask describing the adjustment to be made when + /// the popup is constrained on that axis. /// - /// If no bit for one axis is set, the positioner will assume that the child - /// surface should not change its position on that axis when constrained. + /// If no bit for one axis is set, the positioner will assume that the child surface should + /// not change its position on that axis when constrained. /// - /// If more than one bit for one axis is set, the order of how adjustments - /// are applied is specified in the corresponding adjustment descriptions. + /// If more than one bit for one axis is set, the order of how adjustments are applied is + /// specified in the corresponding adjustment descriptions. /// /// The default adjustment is none. - /// + /// public PopupPositionerConstraintAdjustment ConstraintAdjustment { get; set; } - + /// /// Specify the popup position offset relative to the position of the - /// anchor on the anchor rectangle and the anchor on the popup. For - /// example if the anchor of the anchor rectangle is at (x, y), the popup - /// has the gravity bottom|right, and the offset is (ox, oy), the calculated - /// surface position will be (x + ox, y + oy). The offset position of the - /// surface is the one used for constraint testing. See - /// set_constraint_adjustment. - /// - /// An example use case is placing a popup menu on top of a user interface - /// element, while aligning the user interface element of the parent surface - /// with some user interface element placed somewhere in the popup. + /// anchor on the anchor rectangle and the anchor on the popup. /// + /// + /// For example if the anchor of the anchor rectangle is at (x, y), the popup has the + /// gravity bottom|right, and the offset is (ox, oy), the calculated surface position will + /// be (x + ox, y + oy). The offset position of the surface is the one used for constraint + /// testing. See set_constraint_adjustment. + /// + /// An example use case is placing a popup menu on top of a user interface element, while + /// aligning the user interface element of the parent surface with some user interface + /// element placed somewhere in the popup. + /// public Point Offset { get; set; } } - + /// - /// The constraint adjustment value define ways how popup position will - /// be adjusted if the unadjusted position would result in the popup - /// being partly constrained. - /// - /// Whether a popup is considered 'constrained' is left to the positioner - /// to determine. For example, the popup may be partly outside the - /// target platform defined 'work area', thus necessitating the popup's - /// position be adjusted until it is entirely inside the work area. + /// Defines how a popup position will be adjusted if the unadjusted position would result in + /// the popup being partly constrained. /// + /// + /// Whether a popup is considered 'constrained' is left to the positioner to determine. For + /// example, the popup may be partly outside the target platform defined 'work area', thus + /// necessitating the popup's position be adjusted until it is entirely inside the work area. + /// [Flags] public enum PopupPositionerConstraintAdjustment { @@ -171,62 +177,59 @@ namespace Avalonia.Controls.Primitives.PopupPositioning /// /// Slide the surface along the x axis until it is no longer constrained. - /// First try to slide towards the direction of the gravity on the x axis - /// until either the edge in the opposite direction of the gravity is - /// unconstrained or the edge in the direction of the gravity is - /// constrained. - /// - /// Then try to slide towards the opposite direction of the gravity on the - /// x axis until either the edge in the direction of the gravity is - /// unconstrained or the edge in the opposite direction of the gravity is - /// constrained. /// + /// + /// First try to slide towards the direction of the gravity on the x axis until either the + /// edge in the opposite direction of the gravity is unconstrained or the edge in the + /// direction of the gravity is constrained. + /// + /// Then try to slide towards the opposite direction of the gravity on the x axis until + /// either the edge in the direction of the gravity is unconstrained or the edge in the + /// opposite direction of the gravity is constrained. + /// SlideX = 1, - /// - /// Slide the surface along the y axis until it is no longer constrained. - /// - /// First try to slide towards the direction of the gravity on the y axis - /// until either the edge in the opposite direction of the gravity is - /// unconstrained or the edge in the direction of the gravity is - /// constrained. - /// - /// Then try to slide towards the opposite direction of the gravity on the - /// y axis until either the edge in the direction of the gravity is - /// unconstrained or the edge in the opposite direction of the gravity is - /// constrained. - /// */ + /// Slide the surface along the y axis until it is no longer constrained. /// + /// + /// First try to slide towards the direction of the gravity on the y axis until either the + /// edge in the opposite direction of the gravity is unconstrained or the edge in the + /// direction of the gravity is constrained. + /// + /// Then try to slide towards the opposite direction of the gravity on the y axis until + /// either the edge in the direction of the gravity is unconstrained or the edge in the + /// opposite direction of the gravity is constrained. + /// SlideY = 2, /// - /// Invert the anchor and gravity on the x axis if the surface is - /// constrained on the x axis. For example, if the left edge of the - /// surface is constrained, the gravity is 'left' and the anchor is - /// 'left', change the gravity to 'right' and the anchor to 'right'. - /// - /// If the adjusted position also ends up being constrained, the resulting - /// position of the flip_x adjustment will be the one before the - /// adjustment. + /// Invert the anchor and gravity on the x axis if the surface is constrained on the x axis. /// + /// + /// For example, if the left edge of the surface is constrained, the gravity is 'left' and + /// the anchor is 'left', change the gravity to 'right' and the anchor to 'right'. + /// + /// If the adjusted position also ends up being constrained, the resulting position of the + /// FlipX adjustment will be the one before the adjustment. + /// /// FlipX = 4, /// - /// Invert the anchor and gravity on the y axis if the surface is - /// constrained on the y axis. For example, if the bottom edge of the - /// surface is constrained, the gravity is 'bottom' and the anchor is - /// 'bottom', change the gravity to 'top' and the anchor to 'top'. + /// Invert the anchor and gravity on the y axis if the surface is constrained on the y axis. + /// + /// + /// For example, if the bottom edge of the surface is constrained, the gravity is 'bottom' + /// and the anchor is 'bottom', change the gravity to 'top' and the anchor to 'top'. /// - /// The adjusted position is calculated given the original anchor - /// rectangle and offset, but with the new flipped anchor and gravity - /// values. + /// The adjusted position is calculated given the original anchor rectangle and offset, but + /// with the new flipped anchor and gravity values. /// - /// If the adjusted position also ends up being constrained, the resulting - /// position of the flip_y adjustment will be the one before the - /// adjustment. - /// + /// If the adjusted position also ends up being constrained, the resulting position of the + /// FlipY adjustment will be the one before the adjustment. + /// FlipY = 8, + All = SlideX|SlideY|FlipX|FlipY } @@ -267,27 +270,91 @@ namespace Avalonia.Controls.Primitives.PopupPositioning } + /// + /// Defines the popup position edge for and + /// . + /// [Flags] public enum PopupPositioningEdge { + /// + /// The center of the anchor rectangle. + /// None, + + /// + /// The top edge of the anchor rectangle. + /// Top = 1, + + /// + /// The bottom edge of the anchor rectangle. + /// Bottom = 2, + + /// + /// The left edge of the anchor rectangle. + /// Left = 4, + + /// + /// The right edge of the anchor rectangle. + /// Right = 8, + + /// + /// The top-left corner of the anchor rectangle. + /// TopLeft = Top | Left, + + /// + /// The top-right corner of the anchor rectangle. + /// TopRight = Top | Right, + + /// + /// The bottom-left corner of the anchor rectangle. + /// BottomLeft = Bottom | Left, + + /// + /// The bottom-right corner of the anchor rectangle. + /// BottomRight = Bottom | Right, - + /// + /// A mask for the vertical component flags. + /// VerticalMask = Top | Bottom, + + /// + /// A mask for the horizontal component flags. + /// HorizontalMask = Left | Right, + + /// + /// A mask for all flags. + /// AllMask = VerticalMask|HorizontalMask } + /// + /// Positions an . + /// + /// + /// is an abstraction of the wayland xdg_positioner spec. + /// + /// The popup positioner implementation is determined by the platform implementation. A default + /// managed implementation is provided in for platforms + /// on which popups can be arbitrarily positioned. + /// public interface IPopupPositioner { + /// + /// Updates the position of the associated according to the + /// specified parameters. + /// + /// The positioning parameters. void Update(PopupPositionerParameters parameters); } diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs index 07348cdf78..bd23a13e7f 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs @@ -25,6 +25,10 @@ namespace Avalonia.Controls.Primitives.PopupPositioning } } + /// + /// An implementation for platforms on which a popup can be + /// aritrarily positioned. + /// public class ManagedPopupPositioner : IPopupPositioner { private readonly IManagedPopupPositionerPopup _popup;