[PATCH wayland-protocols v3 7/7] xdg-shell: Introduce xdg_positioner

Jonas Ådahl jadahl at gmail.com
Mon May 30 07:30:53 UTC 2016


On Sat, May 28, 2016 at 08:22:53AM -0500, Yong Bakos wrote:
> Hi Jonas and Mike,
> 
> On May 25, 2016, at 11:32 PM, Jonas Ådahl <jadahl at gmail.com> 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>
> 
> Whenever I see declarative positioning schemes, my warning bell goes off
> because, from the perspective of a gui/app dev, resulting implementations
> of constraint systems, etc can sometimes be a hassle. I'm not saying that
> what's proposed here is bad, rather, I ask, "Was this based on an
> existing system?" (Forgive me if that question is obvious / naive).

It was roughly based on an API used by Enlightenment. Support for using
it has also been prototyped in GTK+, and so far it seems possible to
implement all the popups etc available there. William Hua from
Cannonical has done most of the GTK+ side of things, although with the
intention to use it for the Mir backend. Not that Mir uses a slightly
similar approach[0] i.e. aligning a menu against a rectangle on a parent
surface.

> 
> In other words, there's an opportunity to create a simple, flexible,
> and anticipatable positioning mechanism here at the protocol level, so
> it's important to Do It Really Well.

Indeed. We have so far done it with the intention to make it
declarative, i.e. you don't "program" your position, you describe the
intention, and the compositor "programs" it given state only known to
it. I'm not sure how well versed you are of the background to this
protocol, but its main purpose is to keep popups (menus etc) within the
working region (maybe monitor area, or smaller, or larger, depending on
the compositor). Initially, the idea was to add a "probe" mechanism
letting clients check what parts of a would-be popup rectangle would be
outside of the work area, but that method did not play out well when
there are trasnformations and other things going on. Thus, we started
moving towards going for a more declerative direction where you set up
rules while letting the compositor do the positioning.

Note that given how xdg_positioner is made (rules added to an isolated
object), it is easy to add new rules in the future.

There are also a couple of things that are yet to be solved:

 - position adapted to a moving parent - the protocol does not handle
   repositioning of the popup when the parent changes the position. This
   would need various subsurface-like synchonization behaviours added.
   I think we can add this in the future if we actually want to, and I
   think it can be made in a backward compatible way.

 - maximum sized popups - it does not support letting a popup fill the
   work area in a predictable way (for example a vertical menu with very
   many elements should not be higher than the height of the work area
   it is drawn on). We could maybe consider adding using -intmax/intmax
   on the x/width or y/height to declare that and add a width/height to
   xdg_popup.configure. I have not prototyped nor do I know whether this
   is the kind of API GTK+ wants, and anyhow, its not supported as
   of today.

> 
> Acked-by: Yong Bakos <ybakos at humanoriented.com>

Thanks


Jonas


[0] https://unity.ubuntu.com/mir/group__mir__toolkit.html#ga1bd26c06914622303d36b10fc473afed

> 
> yong
> 
> 
> > ---
> > 
> > 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.
> > +      </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.
> > +      </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"/>
> > +    </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"/>
> > +    </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
> > -- 
> > 2.5.5
> > 
> > _______________________________________________
> > wayland-devel mailing list
> > wayland-devel at lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/wayland-devel
> 


More information about the wayland-devel mailing list