[PATCH wayland-protocols v3 7/7] xdg-shell: Introduce xdg_positioner
Jonas Ådahl
jadahl at gmail.com
Tue Jun 14 17:01:15 UTC 2016
On Thu, Jun 09, 2016 at 12:05:28AM +0200, Benoit Gschwind wrote:
> Hello Jonas,
>
> The proposal of xdg_positioner is a good idea. I still need to review
> your proposal deeper, but at the moment I have few question to help my
> understanding.
>
> On 26/05/2016 06:32, Jonas Ådahl wrote:
> > xdg_positioner is a method for declarative positioning of child surfaces
> > (currently only xdg_popup surfaces). A client creates a description of a
> > positioning logic using the xdg_positioner interface. The xdg_positioner
> > object is then used when creating a xdg_popup for describing how the
> > child surface should be positioned in relation to the parent surface.
> >
> > Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
> > Signed-off-by: Mike Blumenkrantz <zmike at samsung.com>
> > ---
> >
> > Changes since v2:
> > - Grammar, typos and spelling fixes
> > - Reordered enum values to be the same as already present similar enum
> > - Added enum attribute to relevant requests
> >
> >
> > unstable/xdg-shell/xdg-shell-unstable-v6.xml | 252 ++++++++++++++++++++++++++-
> > 1 file changed, 249 insertions(+), 3 deletions(-)
> >
> > diff --git a/unstable/xdg-shell/xdg-shell-unstable-v6.xml b/unstable/xdg-shell/xdg-shell-unstable-v6.xml
> > index 07cbbc9..1baafa7 100644
> > --- a/unstable/xdg-shell/xdg-shell-unstable-v6.xml
> > +++ b/unstable/xdg-shell/xdg-shell-unstable-v6.xml
> > @@ -45,6 +45,8 @@
> > summary="the client specified an invalid popup parent surface"/>
> > <entry name="invalid_surface_state" value="4"
> > summary="the client provided an invalid surface state"/>
> > + <entry name="invalid_positioner" value="5"
> > + summary="the client provided an invalid positioner"/>
> > </enum>
> >
> > <request name="destroy" type="destructor">
> > @@ -57,6 +59,15 @@
> > </description>
> > </request>
> >
> > + <request name="create_positioner">
> > + <description summary="create a positioner object">
> > + Create a positioner object. A positioner object is used to position
> > + surfaces relative to some parent surface. See the interface description
> > + and xdg_surface.get_popup for details.
> > + </description>
> > + <arg name="id" type="new_id" interface="zxdg_positioner_v6"/>
> > + </request>
> > +
> > <request name="get_xdg_surface">
> > <description summary="create a shell surface from a surface">
> > This creates an xdg_surface for the given surface. While xdg_surface
> > @@ -96,6 +107,225 @@
> > </event>
> > </interface>
> >
> > + <interface name="zxdg_positioner_v6" version="1">
> > + <description summary="child surface positioner">
> > + The xdg_positioner provides an interface for constructing positioning
> > + rules used for positioning a child surface relative to a parent surface
> > + in a certain way. It allows methods for defining a rule that will make
> > + the child surface stay within the border of the visible area of the
> > + screen, with different ways in which the child surface should change
> > + its position, including sliding along an axis, or flipping around a
> > + rectangle.
> > +
> > + See the various requests for details about possible rules.
> > +
> > + Semantically, an xdg_positioner is a collection of positioning rules. When
> > + used for positioning a surface, for example when passed as an argument to
> > + xdg_surface.get_popup, the compositor copies the rules that were set up at
> > + the time of the request. Making any changes or destroying the object after
> > + it was used has no effect on previous usages.
> > +
> > + For an xdg_positioner object to be considered complete, it must have a
> > + non-zero size set by set_size, and a non-zero anchor rectangle set by
> > + set_anchor_rect. Passing an incomplete xdg_positioner object when
> > + positioning a surface raises an error.
> > + </description>
> > +
> > + <enum name="error">
> > + <entry name="invalid_input" value="0" summary="invalid input provided"/>
> > + </enum>
> > +
> > + <request name="destroy" type="destructor">
> > + <description summary="destroy the xdg_positioner object">
> > + Notify the compositor that the xdg_positioner will no longer be used.
> > + </description>
> > + </request>
> > +
> > + <request name="set_size">
> > + <description summary="set the size of the to-be positioned rectangle">
> > + Set the size of the surface that is to be positioned with the positioner
> > + object. The size is in surface-local coordinates and corresponds to the
> > + window geometry. See xdg_surface.set_window_geometry.
> > +
> > + If a zero or negative size is set the invalid_input error is raised.
> > + </description>
> > + <arg name="width" type="int" summary="width of positioned rectangle"/>
> > + <arg name="height" type="int" summary="height of positioned rectangle"/>
> > + </request>
> > +
> > + <request name="set_anchor_rect">
> > + <description summary="set the anchor rectangle within the parent surface">
> > + Specify the anchor rectangle within the parent surface that the child
> > + surface will be placed relative to. The rectangle is relative to the
> > + window geometry as defined by xdg_surface.set_window_geometry of the
> > + parent surface. The rectangle must be at least 1x1 large.
> > +
> > + When the xdg_positioner object is used to position a child surface, the
> > + anchor rectangle may not extend outside the window geometry of the
> > + positioned child's parent surface.
> > +
> > + If a zero or negative size is set the invalid_input error is raised.
>
> If I understand the idea of rectangular anchor, this is the area where
> the edge of the popup will be "glued" ? For instance the rectangle
> around the word "File" in menu bar ? or the rectangle around a menu entry ?
Yes, the anchor rect would be the rectangle around "File" in the menu
bar.
>
> > + </description>
> > + <arg name="x" type="int" summary="x position of anchor rectangle"/>
> > + <arg name="y" type="int" summary="y position of anchor rectangle"/>
> > + <arg name="width" type="int" summary="width of anchor rectangle"/>
> > + <arg name="height" type="int" summary="height of anchor rectangle"/>
> > + </request>
> > +
> > + <enum name="anchor" bitfield="true">
> > + <entry name="none" value="0"
> > + summary="the center of the anchor rectangle"/>
> > + <entry name="top" value="1"
> > + summary="the top edge of the anchor rectangle"/>
> > + <entry name="bottom" value="2"
> > + summary="the bottom edge of the anchor rectangle"/>
> > + <entry name="left" value="4"
> > + summary="the left edge of the anchor rectangle"/>
> > + <entry name="right" value="8"
> > + summary="the right edge of the anchor rectangle"/>
> > + </enum>
> > +
> > + <request name="set_anchor">
> > + <description summary="set anchor rectangle anchor edges">
> > + Set the anchor edges of the anchor rectangle. The anchor edges define
> > + where on the anchor rectangle the child surface should be positioned
> > + relative to. An anchor is a bit mask of up to two values of the anchor
> > + enum.
> > +
> > + If two values on the same axis (for example left and right) are set the
> > + invalid_input error is raised.
> > +
> > + If no anchor is set on any axis, the anchor position will be positioned
> > + at the center of the anchor rectangle on the unset axis. The default
> > + value is "none".
> > + </description>
> > + <arg name="anchor" type="uint" enum="anchor"
> > + summary="bit mask of anchor edges"/>
> > + </request>
> > +
> > + <enum name="gravity" bitfield="true">
> > + <entry name="none" value="0"
> > + summary="center over the anchor edge"/>
> > + <entry name="top" value="1"
> > + summary="position above the anchor edge"/>
> > + <entry name="bottom" value="2"
> > + summary="position below the anchor edge"/>
> > + <entry name="left" value="4"
> > + summary="position to the left of the anchor edge"/>
> > + <entry name="right" value="8"
> > + summary="position to the right of the anchor edge"/>
> > + </enum>
> > +
> > + <request name="set_gravity">
> > + <description summary="set child surface gravity">
> > + The gravity defines in what direction a surface would be positioned,
> > + relative to the anchor edges on the parent surface. A gravity is a
> > + bit mask of up to two values of the gravity enum.
> > +
> > + If two values on the same axis (for example left and right) are set the
> > + invalid_input error is raised.
> > +
> > + If no gravity is set on an axis, the gravity for that axis will be
> > + equivalent to setting "none" for that axis, resulting in the child being
> > + centered over the anchor edges on that axis.
>
> Gravity is the hot point that will match the anchor point once the popup
> will be positioned by defaults i.e. without any constraint ?
>
> The gravity point is define relative to the rectangle of the popup ?
The gravity defines how the popup will "hang" from the anchor. Kind of
the invertion of what edge will be glued to the anchor edge of the
anchor rect.
For example, the [File] menu would be positioned like
[File]
+-------+
|New |
|Save |
|Save as|
|Exit |
+-------+
The "anchors" on the anchor rect would be the left and bottom edges and
the the gravity is RIGHT|BOTTOM, as it "hangs" towards the bottom and
the right.
>
> > + </description>
> > + <arg name="gravity" type="uint" enum="gravity"
> > + summary="bit mask of gravity directions"/>
> > + </request>
> > +
> > + <enum name="constrain_adjustment" bitfield="true">
> > + <entry name="none" value="0">
> > + <description summary="don't move the child surface when constrained">
> > + Don't alter the surface position even if it is constrained on some
> > + axis, for example partially outside the edge of a monitor.
> > + </description>
> > + </entry>
> > + <entry name="slide_x" value="1">
> > + <description summary="move along the x axis until unconstrained">
> > + 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.
> > + </description>
> > + </entry>
> > + <entry name="slide_y" value="2">
> > + <description summary="move along the y axis until unconstrained">
> > + 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.
> > + </description>
> > + </entry>
> > + <entry name="flip_x" value="4">
> > + <description summary="invert the anchor and gravity on the x axis">
> > + 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 resulting position after inverting ends up also being constrained,
> > + the behaviour is undefined.
> > + </description>
> > + </entry>
> > + <entry name="flip_y" value="8">
> > + <description summary="invert the anchor and gravity on the y axis">
> > + 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'. If
> > + the resulting position after inverting ends up also being constrained,
> > + the behaviour is undefined.
> > + </description>
> > + </entry>
> > + </enum>
> > +
> > + <request name="set_constrain_adjustment">
> > + <description summary="set the adjustment to be done when constrained">
> > + Specify how the window should be positioned if the originally intended
> > + position caused the surface to be constrained, meaning at least
> > + partially outside positioning boundaries set by the compositor. The
> > + adjustment is set by constructing a bitmask with one bit per axis set
> > + describing the adjustment to be made when the surface is constrained on
> > + that axis. If no bit for one axis is set, the compositor will assume
> > + that the child surface should not change its position on that axis when
> > + constrained. The default adjustment is none.
> > + </description>
> > + <arg name="constrain_adjustment" type="uint"
> > + summary="bit mask of constrain adjustments"/>
>
> Does setting all the bitmask mean do what you prefer to the compositor ?
No, as written, its "one bit per axis".
>
> Would it be better to give a priority list like: try flip_x first, then
> try slice_x then try flip_y for example ?
That would complicate the semantics of the constraints we have. For
example, you could not try flipping after having tried sliding, since
sliding is defined in a moving mechanism, not an boolean "on-off".
I think it might be more sane to just leave that case (flip and fallback
to slide) out right now, and if we actually end up needing it, adding
more rules to the enum is easy, and we can clearly specify the semantics
without having to deal with inter-rule semantics.
>
>
> > + </request>
> > +
> > + <request name="set_offset">
> > + <description summary="set surface position offset">
> > + Specify the surface position offset relative to the position of the
> > + anchor on the anchor rectangle and the anchor on the surface. For
> > + example if the anchor of the anchor rectangle is at (x, y), the surface
> > + 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_constrain_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 surface.
> > + </description>
> > + <arg name="x" type="int" summary="surface position x offset"/>
> > + <arg name="y" type="int" summary="surface position y offset"/>
>
> does it equivalent to move the anchor. I mean if I move the anchor by
> the given offset, can I get the same result ?
Pretty much. The reason for adding this is mostly because it could be
complicated for the client to have to calculate a rectangle while
keeping it within the surface. It adds very little complexity compositor
side, while making the client much simpler.
>
> > + </request>
> > + </interface>
> > +
> > <interface name="zxdg_surface_v6" version="1">
> > <description summary="desktop user interface surface base interface">
> > An interface that may be implemented by a wl_surface, for
> > @@ -163,8 +393,7 @@
> > </description>
> > <arg name="id" type="new_id" interface="zxdg_popup_v6"/>
> > <arg name="parent" type="object" interface="zxdg_surface_v6"/>
> > - <arg name="x" type="int"/>
> > - <arg name="y" type="int"/>
> > + <arg name="positioner" type="object" interface="zxdg_positioner_v6"/>
> > </request>
> >
> > <request name="set_window_geometry">
> > @@ -724,7 +953,7 @@
> > button press, key press, or touch down event. The serial number of the
> > event should be passed as 'serial'.
> >
> > - The parent of a grabbing popup must either be a xdg_toplevel surface or
> > + The parent of a grabbing popup must either be an xdg_toplevel surface or
> > another xdg_popup with an explicit grab. If the parent is another
> > xdg_popup it means that the popups are nested, with this popup now being
> > the topmost popup.
> > @@ -759,6 +988,23 @@
> > <arg name="serial" type="uint" summary="the serial of the user event"/>
> > </request>
> >
> > + <event name="configure">
> > + <description summary="configure the popup surface">
> > + This event asks the popup surface to configure itself given the
> > + configuration. It is not sent by itself but is as a latched state prior
> > + to the xdg_surface.configure event. See xdg_surface.configure for
> > + details.
> > +
> > + The x and y arguments represent the position the popup was placed at
> > + given the xdg_positioner rule, relative to the upper left corner of the
> > + window geometry of the parent surface.
> > + </description>
> > + <arg name="x" type="int"
> > + summary="x position relative to parent surface window geometry"/>
> > + <arg name="y" type="int"
> > + summary="y position relative to parent surface window geometry"/>
> > + </event>
> > +
> > <event name="popup_done">
> > <description summary="popup interaction is done">
> > The popup_done event is sent out when a popup is dismissed by the
> >
Thanks for taking your time to review.
Jonas
>
> Best regards
> --
> Benoit Gschwind
More information about the wayland-devel
mailing list