GNOME Bugzilla – Bug 750568
Add support for windows that are transparent wrt input
Last modified: 2015-06-17 18:49:20 UTC
Currently, when a window does not select for input events we propagate that event to the parent window. This is the X semantics an makes a lot of sense for opaque windows. However, if a window is transparent it makes more sense to propagate the event to whatever is below the window (which is likely a sibling window). Jasper had issues with this trying to set up a grid of thumbnails that had hover and click with and overlayed semitransparent logo and entry. The entry worked, but in the whole rectangle for the logo vbox all events where stolen from the widgets below.
Created attachment 304773 [details] [review] testoverlay: Add new input stacking test In this case we have a bunch of interactive main children of the overlay, and then a centered overlay that contains both non-interactive (labels) and interactive (entry) widgets. This shows off a problem where the non-interactive parts (the labels) steals input from the overlay main children (breaks button click and hover effects).
Created attachment 304774 [details] [review] gdk: Add support for GDK_OUTPUT_ONLY windows An output-only window is something you can draw in but does not affect event handling. This is different from an input-output window with no event mask in that such a window would send input events over it to its parent window (X11 semantics), whereas an output-only window would send it to whatever window is below it. The later mode is useful when the window is partially transparent. Note that an output-only window can have input-output and input-only children. Semantically, these windows behave the same as an input-output window with gdk_window_set_child_input_shapes() called on it (and re-called any time a child is changed), but its far more efficient and easy to use. This allows us to fix the testoverlay input stacking test.
Created attachment 304775 [details] [review] GtkOverlay: Add support for transparent (for input) overlays For these widgets we allocate an output-only window so that input over these widgets (that are not on a child input window) goes to the window below the overlay.
Created attachment 304776 [details] [review] testoverlay: Fix test by usint add_transparent_overlay
This adds a test showing jaspers issue, then it implements GDK_OUTPUT_ONLY windows and uses these to fix the issue. I'm not super-fond of the word "transparent" used in the overlay part as it doesn't really talk about the visual side, but i can't think of a better name.
DOM calls this mode "pointer-events: none"
Comment on attachment 304775 [details] [review] GtkOverlay: Add support for transparent (for input) overlays I think this API is unfortunate. I think input acceptance should be a mutable boolean property on the window instead of a constuct-only property. I would imagine that toggling input acceptance on widgets might be a runtime decision (ie when toggling widget sensitivity) and that should not require recreation of the GDK window. Also technically, I don't see it requiring a lot more code to make it mutable.
Comment on attachment 304774 [details] [review] gdk: Add support for GDK_OUTPUT_ONLY windows That was the wrong patch I commented on, I meant to comment on this one. Also, for a name, I'd suggest gdk_window_set_pass_through(GdkWindow *, gboolean); It'd make not much sense to call that on input-only windows, so we might forbid it on those.
Yeah, making it a boolean is super easy, its in fact treated that way in the code already. And it would make the GtkOverlay property code much easier too.
Created attachment 304807 [details] [review] testoverlay: Add new input stacking test In this case we have a bunch of interactive main children of the overlay, and then a centered overlay that contains both non-interactive (labels) and interactive (entry) widgets. This shows off a problem where the non-interactive parts (the labels) steals input from the overlay main children (breaks button click and hover effects).
Created attachment 304808 [details] [review] gdk: Add gdk_window_set_pass_through An pass_through window is something you can draw in but does not affect event handling. Normally if a window has with no event mask set for a particular event then input events in it go to its parent window (X11 semantics), whereas if pass_through is enabled the window below the window will get the event. The later mode is useful when the window is partially transparent. Note that an pass-through windows can have child windows that are not pass-through so they can still get events on some parts. Semantically, this behaves the same as an regular window with gdk_window_set_child_input_shapes() called on it (and re-called any time a child is changed), but its far more efficient and easy to use. This allows us to fix the testoverlay input stacking test.
Created attachment 304809 [details] [review] GtkOverlay: Add support for input pass-through overlays For these widgets we set pass-through on the child window so that input over these widgets (that are not on a child input window) goes to the window below the overlay.
Created attachment 304810 [details] [review] testoverlay: Fix test by usint add_pass_through_overlay
New version uses set_pass_through()
Review of attachment 304775 [details] [review]: Very nice. Just think we should add a bit more to the docs on hit-target propagation. ::: gtk/gtkoverlay.c @@ -538,0 +556,50 @@ +static GtkOverlayChild * +gtk_overlay_get_overlay_child (GtkOverlay *overlay, + GtkWidget *child) ... 47 more ... Generally prefer the !! style, but I think with gvalue we can depend on 0 or 1 for gboolean. @@ -673,0 +818,11 @@ + +/** + * gtk_overlay_add_transparent_overlay: ... 8 more ... What if the overlay child is placed in an overlapping location with a second overlay child? Does the event dispatch based on implicit stacking, or always fall through to the GtkBin child?
Review of attachment 304774 [details] [review]: Does anything special need to happen if gdk_window_ensure_native() gets called on a GDK_OUTPUT_ONLY window?
Review of attachment 304775 [details] [review]: ::: gtk/gtkoverlay.c @@ -673,0 +818,11 @@ + +/** + * gtk_overlay_add_transparent_overlay: ... 8 more ... It will dispatch based on the gdkwindow stacking order, which i guess is somewhat related to the order you add overlays.
Review of attachment 304774 [details] [review]: No, the code works fine with native (child) windows (i tried). They are not especially useful for those, as they can't be transparent so you can't really see what you're delivering the events to, but it works. For toplevel windows it will stop delivery of events, but not actually propagate them to whatever window is below you. I guess technically we could auto-set the input region on the toplevels to achieve this, but i'm not sure how useful that will be.
Created attachment 304833 [details] [review] testoverlay: Add new input stacking test In this case we have a bunch of interactive main children of the overlay, and then a centered overlay that contains both non-interactive (labels) and interactive (entry) widgets. This shows off a problem where the non-interactive parts (the labels) steals input from the overlay main children (breaks button click and hover effects).
Created attachment 304834 [details] [review] gdk: Add gdk_window_set_pass_through An pass_through window is something you can draw in but does not affect event handling. Normally if a window has with no event mask set for a particular event then input events in it go to its parent window (X11 semantics), whereas if pass_through is enabled the window below the window will get the event. The later mode is useful when the window is partially transparent. Note that an pass-through windows can have child windows that are not pass-through so they can still get events on some parts. Semantically, this behaves the same as an regular window with gdk_window_set_child_input_shapes() called on it (and re-called any time a child is changed), but its far more efficient and easy to use. This allows us to fix the testoverlay input stacking test.
Created attachment 304835 [details] [review] overlay: Add reorder_overlay() This allows you to control the z-ordering of overlay children
Created attachment 304836 [details] [review] GtkOverlay: Add support for input pass-through overlays For these widgets we set pass-through on the child window so that input over these widgets (that are not on a child input window) goes to the window below the overlay.
Created attachment 304837 [details] [review] testoverlay: Fix test by using add_pass_through_overlay
Created attachment 304839 [details] [review] testoverlay: Add test for overlay z ordering
New set of patches which also includes z-ordering of overlay children. I also looked at trying to make this a more generic pass-through property on GtkWidget, but that was a bit iffy. Its hard to make it work generically as lots of widgets have extra windows, and its only useful for z-stacked overlapping widgets which really only happens for overlays, so i'm not sure how useful it is.
Review of attachment 304835 [details] [review]: ::: gtk/gtkoverlay.c @@ +534,3 @@ + * A widget’s position in the @overlay children list determines which order + * the children are drawn if they overlap. The first child is drawn at + * the bottom. It also determines the default focus chain, right ?
Review of attachment 304835 [details] [review]: ::: gtk/gtkoverlay.c @@ +534,3 @@ + * A widget’s position in the @overlay children list determines which order + * the children are drawn if they overlap. The first child is drawn at + * the bottom. I guess so.
Review of attachment 304833 [details] [review]: ok
Review of attachment 304834 [details] [review]: Looks good to me.
Review of attachment 304835 [details] [review]: ::: gtk/gtkoverlay.c @@ +578,3 @@ + priv->children = g_slist_insert_before (priv->children, new_link, child_info); + + gtk_widget_child_notify (child, "index"); If we're going to take this seriously, we need to notify the position of other children here too, I guess
Review of attachment 304836 [details] [review]: ::: gtk/gtkoverlay.c @@ +661,3 @@ { + case CHILD_PROP_PASS_THROUGH: + /* Ignore value on main child */ This should probably go into a doc comment for the child property.
Review of attachment 304837 [details] [review]: sure
Review of attachment 304839 [details] [review]: looks good, otherwise ::: tests/testoverlay.c @@ +501,3 @@ + gtk_widget_override_background_color (ebox, 0, &color); + G_GNUC_END_IGNORE_DEPRECATIONS + gtk_widget_set_halign (ebox, (i == 0 || i == 3) ? GTK_ALIGN_START : GTK_ALIGN_END); Indentation mishap
Review of attachment 304835 [details] [review]: ::: gtk/gtkoverlay.c @@ +578,3 @@ + priv->children = g_slist_insert_before (priv->children, new_link, child_info); + + gtk_widget_child_notify (child, "index"); Not only here, also when removing a child.
Pushed fixed versions to master.
Arriving a little late to the party, but I think I would have preferred if there was not another extra gtk_overlay_add_pass_through_overlay() method, because that makes it for three different methods to add children to an overlay. Could the API be implemented as a simple helper to set the child property instead? So a caller would do something like: gtk_overlay_add_overlay (overlay, child); gtk_overlay_set_pass_through (overlay, child, TRUE); Either way thanks for implementing this!
Cosimo: There is already a child property, we could easily make a method for that. Then we could either remove the add_pass_through_overlay or make it a helper for the new method. I don't *really* care, as long as it is possible to do this, and possible to figure out how to do it from the API (as in, not too many contortions).
I prefer Cosimo's API, personally.
I leave it up to you to figure out the api, I don't have a strong opinion. We don't generally have non-generic child property setters, but there's nothing wrong in principle with that. You can of course already do: gtk_overlay_add_overlay (overlay, child); gtk_container_child_set (GTK_CONTAINER (overlay), child, "passthrough", TRUE, NULL);
We should have setters for child properties where we don't. The reason why we don't in lots of places is that either those properties are meant more for glade than for actual code or because they were meant to be small knobs that most people shouldn't use. Both of these are not very good excuses IMO. If you look at GtkNotebook or GtkListBox for example, there is lots of API for accessing child properties. Because there the code needed to be able to fiddle with those.
Created attachment 305419 [details] [review] docs: add new GtkOverlay methods to gtk3-sections.txt
Created attachment 305420 [details] [review] overlay: add setters and getters for pass-through child property This will make the API easier to use from bindings too.
Created attachment 305421 [details] [review] overlay: remove gtk_overlay_add_pass_through_overlay() The API to access this functionality will be the setter we just added in the previous commit.
I guess this is the API I was thinking about. Feedback welcome.
Review of attachment 305419 [details] [review]: looks good
Review of attachment 305420 [details] [review]: looks good
Review of attachment 305421 [details] [review]: I don't really care deeply, but sure, it is kind of ugly to have multiple add versions, so lets go with this.
Thanks, I pushed these to master now. Attachment 305419 [details] pushed as 1e6ccf5 - docs: add new GtkOverlay methods to gtk3-sections.txt Attachment 305420 [details] pushed as 3b1b171 - overlay: add setters and getters for pass-through child property Attachment 305421 [details] pushed as 9c7b0f0 - overlay: remove gtk_overlay_add_pass_through_overlay()