After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 687842 - Support partially transparent widgets
Support partially transparent widgets
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Backend: X11
unspecified
Other All
: Normal normal
: ---
Assigned To: gtk-bugs
gtk-bugs
Depends on:
Blocks:
 
 
Reported: 2012-11-07 13:40 UTC by Alexander Larsson
Modified: 2013-02-12 16:03 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Add "parent widget" button to test property editor (2.80 KB, patch)
2012-11-07 13:40 UTC, Alexander Larsson
committed Details | Review
gdk: Add gdk_window_has_alpha helper (3.20 KB, patch)
2012-11-07 13:40 UTC, Alexander Larsson
committed Details | Review
gdkwindow: Store the implicit paint in a list (3.73 KB, patch)
2012-11-07 13:40 UTC, Alexander Larsson
accepted-commit_now Details | Review
gdkwindow: Allow gdk_window_set_opacity on non-native children (10.33 KB, patch)
2012-11-07 13:40 UTC, Alexander Larsson
reviewed Details | Review
Support opacity for all widgets (13.73 KB, patch)
2012-11-07 13:40 UTC, Alexander Larsson
reviewed Details | Review
gdkwindow: Store the implicit paint in a list (3.73 KB, patch)
2013-02-05 14:16 UTC, Alexander Larsson
committed Details | Review
gdkwindow: Allow gdk_window_set_opacity on non-native children (10.64 KB, patch)
2013-02-05 14:16 UTC, Alexander Larsson
committed Details | Review
Add gtk_widget_(un)register_window (50.09 KB, patch)
2013-02-05 14:16 UTC, Alexander Larsson
committed Details | Review
Add gtk_widget_get/set_opacity (19.22 KB, patch)
2013-02-05 14:16 UTC, Alexander Larsson
committed Details | Review
Add widget transparency test to testgtk (5.02 KB, patch)
2013-02-05 14:16 UTC, Alexander Larsson
committed Details | Review
TextHandle: Don't draw handles if not visible (1.44 KB, patch)
2013-02-05 14:16 UTC, Alexander Larsson
committed Details | Review
Add singleton for css number 1.0 (1.43 KB, patch)
2013-02-06 13:30 UTC, Alexander Larsson
committed Details | Review
css: Support opacity (8.01 KB, patch)
2013-02-06 13:30 UTC, Alexander Larsson
committed Details | Review
gdkwindow: Store the implicit paint in a list (3.73 KB, patch)
2013-02-07 10:12 UTC, Alexander Larsson
committed Details | Review
gdkwindow: Allow gdk_window_set_opacity on non-native children (10.64 KB, patch)
2013-02-07 10:12 UTC, Alexander Larsson
committed Details | Review
Add gtk_widget_(un)register_window (50.08 KB, patch)
2013-02-07 10:12 UTC, Alexander Larsson
committed Details | Review
Add gtk_widget_get/set_opacity (19.23 KB, patch)
2013-02-07 10:12 UTC, Alexander Larsson
committed Details | Review
Add widget transparency test to testgtk (5.02 KB, patch)
2013-02-07 10:12 UTC, Alexander Larsson
committed Details | Review
TextHandle: Don't draw handles if not visible (1.44 KB, patch)
2013-02-07 10:12 UTC, Alexander Larsson
committed Details | Review
Add singleton for css number 1.0 (1.43 KB, patch)
2013-02-07 10:12 UTC, Alexander Larsson
committed Details | Review
css: Support opacity (7.50 KB, patch)
2013-02-07 10:12 UTC, Alexander Larsson
committed Details | Review
css: Add opacity reftest (8.91 KB, patch)
2013-02-07 10:12 UTC, Alexander Larsson
committed Details | Review
Add gtk_widget_get/set_has_opacity_group (12.71 KB, patch)
2013-02-11 13:22 UTC, Alexander Larsson
none Details | Review
Clean up the opacity handling (8.36 KB, patch)
2013-02-11 19:40 UTC, Alexander Larsson
committed Details | Review
Add gtk_widget_get/set_has_opacity_group (6.85 KB, patch)
2013-02-11 19:40 UTC, Alexander Larsson
none Details | Review
GdStack: Add crossfade support (9.66 KB, patch)
2013-02-11 19:48 UTC, Alexander Larsson
none Details | Review
GdStack: Add crossfade support (9.86 KB, patch)
2013-02-12 14:38 UTC, Alexander Larsson
none Details | Review
Add opacity_group hack (5.33 KB, patch)
2013-02-12 16:01 UTC, Alexander Larsson
committed Details | Review

Description Alexander Larsson 2012-11-07 13:40:05 UTC
We currently support transparent backgrounds, but widget rendering is
still based on the painters algorithm, which means we cannot create
transparent objects that are made up of multiple drawing steps, as that
requires some form of transparency groups.

This patchset adds support for this, both for window and no-window
widgets.
Comment 1 Alexander Larsson 2012-11-07 13:40:07 UTC
Created attachment 228366 [details] [review]
Add "parent widget" button to test property editor

Without this its hard to access the properties of container
widgets.
Comment 2 Alexander Larsson 2012-11-07 13:40:11 UTC
Created attachment 228367 [details] [review]
gdk: Add gdk_window_has_alpha helper

This centralizes the current checks for has_alpha_bg, which
lets us extend the check later.
Comment 3 Alexander Larsson 2012-11-07 13:40:14 UTC
Created attachment 228368 [details] [review]
gdkwindow: Store the implicit paint in a list

This changes nothing, but lets us later have multiple
implicit paints
Comment 4 Alexander Larsson 2012-11-07 13:40:17 UTC
Created attachment 228369 [details] [review]
gdkwindow: Allow gdk_window_set_opacity on non-native children

We now store the current opacity for all windows. For native windows
we just call into the native implementation whenever the opacity changes.
However, for non-native windows we implement opacity by pushing a
second implicit paint that "stacks" on the existing one, acting as
an opacity group while rendering the window and its children.

This works well in general, although any native child windows will of
course not be opaque. However, there is no way to implement
implicit paint flushing (i.e. draw the currently drawn double buffer
to the window in order to allow direct drawing to the window).
We can't flush in the stacked implicit paint case because there
is no way to get the right drawing behaviour when drawing directly
to the window. We *must* draw to the opacity group to get the right
behaviour.

We currently flush if:
* A widget disables double buffering
* You call move/resize/scroll a window and it has non-native children
  during the expose handler

In case this happens we warn and flush the outermost group, so there may
be drawing errors.
Comment 5 Alexander Larsson 2012-11-07 13:40:21 UTC
Created attachment 228370 [details] [review]
Support opacity for all widgets

This adds gtk_widget_get/set_opacity, as well as a GtkWidget.opacity
property. Additionally it deprectates gtk_window_get/set_opacity and
removes the GtkWindow.opacity property (in preference for the new
identical inherited property from GtkWidget, which should be ABI/API
compat).

The implementation is using the new gdk_window_set_opacity child
window support for windowed widgets, and cairo_push/pop_group()
bracketing in gtk_widget_draw() for non-window widgets.
Comment 6 Matthias Clasen 2012-11-08 01:39:48 UTC
Review of attachment 228366 [details] [review]:

sure
Comment 7 Matthias Clasen 2012-11-08 01:40:27 UTC
Review of attachment 228367 [details] [review]:

ok
Comment 8 Matthias Clasen 2012-11-08 01:41:01 UTC
Review of attachment 228368 [details] [review]:

ok
Comment 9 Matthias Clasen 2012-11-08 01:50:05 UTC
Review of attachment 228369 [details] [review]:

::: gdk/gdkwindow.c
@@ +11124,2 @@
  *
+ * For child windows this function only work

You didn't complete your thought here.
Comment 10 Matthias Clasen 2012-11-08 02:02:13 UTC
Review of attachment 228370 [details] [review]:

::: gtk/gtkwidget.c
@@ +13724,3 @@
+ * of the opacity parameter are clamped to the [0,1] range.).
+ * This works on both toplevel widget, and child widgets, although there
+ * are some limitiations:

Instead of 'Values of the opacity parameter' I would just say 'Opacity values'.

And you probably mean 'toplevel windows' instead of 'toplevel widget'.

Finally, 'limitations'.

@@ +13737,3 @@
+ * For child widgets without a window only the widget itself and child widgets
+ * without windows are made opaque. Any child window at all inside the widget
+ * will be opaque.

This sounds confusing to me. so many child widgets and windows. And do you mean 'translucent' when you say 'opaque' ?

@@ +13763,3 @@
+  if (alpha == priv->alpha)
+    return;
+

this is the second place where we take opacity as a double, and store alpha as a byte - shouldn't we avoid the rounding errors and just store the double ? In particular, considering there's a getter which will currently give you back a quantized value thats different from what you passed in the setter.

::: gtk/gtkwindow.c
@@ +2689,3 @@
  *
  * Since: 2.12
+ * Deprecated: 3.8: Use gtk_widget_set_opacity instead.

Add () to get the function name linkified in the docs.

@@ +2708,3 @@
  *
  * Since: 2.12
+ * Deprecated: 3.8: Use gtk_widget_get_opacity instead.

here too
Comment 11 Alexander Larsson 2012-11-08 08:57:21 UTC
Review of attachment 228369 [details] [review]:

::: gdk/gdkwindow.c
@@ +11124,2 @@
  *
+ * For child windows this function only work

for non-native windows.
Comment 12 Alexander Larsson 2012-11-08 09:02:41 UTC
Review of attachment 228370 [details] [review]:

::: gtk/gtkwidget.c
@@ +13737,3 @@
+ * For child widgets without a window only the widget itself and child widgets
+ * without windows are made opaque. Any child window at all inside the widget
+ * will be opaque.

Yeah, the terminology is a bit confusing. But opaque is right, as in, if you make a no-window widget such as a GtkHBox transparent and it happens to have some windowed widget as a child (say an EventBox) that widget will not be translucent as the rest of the child widgets are, because for no-window widgets we implement translucency by just modifying the cairo_t that gets propagated in the draw() signal by the container widgets.

@@ +13763,3 @@
+  if (alpha == priv->alpha)
+    return;
+

Storing a 64bit value for every widget for something that is practically never gonna support anything beyond 255 levels seems wasteful to me, and it also leads to issues with comparisons to 1.0 to avoid the slow paths, as equality comparisons with floats are dangerous.
Comment 13 Benjamin Otte (Company) 2012-11-08 14:23:23 UTC
A few comments:

(1) The first commits should have just been pushed to master, they don't depend on this feature.

(2) I don't think gtk_widget_set_opacity() should be public API. We want the styling code to use it, not applications.

(3) Public properties for color or alpha values should use doubles. I don't care much about how they're represented internally - you can do priv->alpha = round (255 * g_value_get_double(value)) for all I care, but just like in the Cairo world, the public API should look sane and consistent.

But as long as this feature isn't gonna work reliably and in all the cases we care about, I don't think we want it in. In particular because we don't have a good reason to have it yet.
Comment 14 Alexander Larsson 2012-11-09 09:11:25 UTC
Attachment 228366 [details] pushed as c94002f - Add "parent widget" button to test property editor
Attachment 228367 [details] pushed as 8a40d8f - gdk: Add gdk_window_has_alpha helper
Comment 15 Alexander Larsson 2013-02-05 14:16:32 UTC
Created attachment 235206 [details] [review]
gdkwindow: Store the implicit paint in a list

This changes nothing, but lets us later have multiple
implicit paints
Comment 16 Alexander Larsson 2013-02-05 14:16:36 UTC
Created attachment 235207 [details] [review]
gdkwindow: Allow gdk_window_set_opacity on non-native children

We now store the current opacity for all windows. For native windows
we just call into the native implementation whenever the opacity changes.
However, for non-native windows we implement opacity by pushing a
second implicit paint that "stacks" on the existing one, acting as
an opacity group while rendering the window and its children.

This works well in general, although any native child windows will of
course not be opaque. However, there is no way to implement
implicit paint flushing (i.e. draw the currently drawn double buffer
to the window in order to allow direct drawing to the window).
We can't flush in the stacked implicit paint case because there
is no way to get the right drawing behaviour when drawing directly
to the window. We *must* draw to the opacity group to get the right
behaviour.

We currently flush if:
* A widget disables double buffering
* You call move/resize/scroll a window and it has non-native children
  during the expose handler

In case this happens we warn and flush the outermost group, so there may
be drawing errors.
Comment 17 Alexander Larsson 2013-02-05 14:16:39 UTC
Created attachment 235208 [details] [review]
Add gtk_widget_(un)register_window

This replaces the previously hardcoded calls to gdk_window_set_user_data,
and also lets us track which windows are a part of a widget. Old code
should continue working as is, but new features that require the
windows may not work perfectly.

We need this for the transparent widget support to work, as we need
to specially mark the windows of child widgets.

Iniital register_windows patch

Flush out register_window
Comment 18 Alexander Larsson 2013-02-05 14:16:43 UTC
Created attachment 235209 [details] [review]
Add gtk_widget_get/set_opacity

This adds gtk_widget_get/set_opacity, as well as a GtkWidget.opacity
property. Additionally it deprectates gtk_window_get/set_opacity and
removes the GtkWindow.opacity property (in preference for the new
identical inherited property from GtkWidget, which should be ABI/API
compat).

The implementation is using the new gdk_window_set_opacity child
window support for windowed widgets, and cairo_push/pop_group()
bracketing in gtk_widget_draw() for non-window widgets.
Comment 19 Alexander Larsson 2013-02-05 14:16:46 UTC
Created attachment 235210 [details] [review]
Add widget transparency test to testgtk
Comment 20 Alexander Larsson 2013-02-05 14:16:50 UTC
Created attachment 235211 [details] [review]
TextHandle: Don't draw handles if not visible

When calling gtk_widget_draw() on the entry gtk_cairo_should_draw_window()
will return TRUE for all windows. This is used when rendering a widget to
somewhere other than the screen, and its now used for transparent widgets.
This caused the texthandle to always draw itself and terminate the draw
handler for the entry.

Instead we now only draw the markers when really visible, plus we return
FALSE to avoid stopping the entry drawing.
Comment 21 Alexander Larsson 2013-02-05 14:22:18 UTC
This new series of patches works in more or less all cases without native child windows, since it relies on the support for being able to render all subwindows similarly to gtk_widget_draw(). I had to fix GtkTextHandle to make this work for the entry, but that kind of bug is also a bug since before and should be fixed.

I'd like some feedback on this, but i think it should be basically ok for real use. We also need to hook it up to the css machinery, but I disagree with benjamin that is should only be availible there. You may very well want to use this for e.g. widget cross fading, in say a GtkStack like widget. 

We probably want to support both css and the app to be able to set the opacity though, maybe we should just multiply the two opacities in gtkwidget.c.
Comment 22 Matthias Clasen 2013-02-05 19:11:53 UTC
Review of attachment 235211 [details] [review]:

Makes sense
Comment 23 Matthias Clasen 2013-02-06 03:16:09 UTC
> We probably want to support both css and the app to be able to set the opacity
> though, maybe we should just multiply the two opacities in gtkwidget.c.

Agreed
Comment 24 Matthias Clasen 2013-02-06 12:42:49 UTC
Review of attachment 235208 [details] [review]:

Some squashing leftovers in the commit message.

Might also want to address https://bugzilla.gnome.org/show_bug.cgi?id=111664 while we are at it ?

::: gtk/gtkwidget.c
@@ +396,3 @@
    */
   GdkWindow *window;
+  GList *registred_windows;

msissing an e here: registered

::: gtk/gtkwidget.h
@@ +634,3 @@
+                                                         GdkWindow    *window);
+void                  gtk_widget_unregister_window      (GtkWidget    *widget,
+                                                         GdkWindow    *window);

GDK_AVAILABLE_IN_3_8
Comment 25 Alexander Larsson 2013-02-06 13:27:53 UTC
Review of attachment 235208 [details] [review]:

re bug 111664, I'm not sure how such a getter would be named.
Comment 26 Alexander Larsson 2013-02-06 13:30:13 UTC
Created attachment 235307 [details] [review]
Add singleton for css number 1.0

This will be nice as this is will be the default for opacity.
Comment 27 Alexander Larsson 2013-02-06 13:30:17 UTC
Created attachment 235308 [details] [review]
css: Support opacity
Comment 28 Matthias Clasen 2013-02-07 02:46:01 UTC
Review of attachment 235308 [details] [review]:

::: gtk/gtkwidget.c
@@ +14009,3 @@
+ *
+ * For child widgets it doesn't work if any affected widget has a native window, or
+ * disables double buffering.

Is this still the case ?
Comment 29 Alexander Larsson 2013-02-07 09:03:34 UTC
Review of attachment 235308 [details] [review]:

::: gtk/gtkwidget.c
@@ +14009,3 @@
+ *
+ * For child widgets it doesn't work if any affected widget has a native window, or
+ * disables double buffering.

Yes, once you get an X window in there is no way to make that transparent really.
Comment 30 Alexander Larsson 2013-02-07 10:12:27 UTC
Created attachment 235364 [details] [review]
gdkwindow: Store the implicit paint in a list

This changes nothing, but lets us later have multiple
implicit paints
Comment 31 Alexander Larsson 2013-02-07 10:12:30 UTC
Created attachment 235365 [details] [review]
gdkwindow: Allow gdk_window_set_opacity on non-native children

We now store the current opacity for all windows. For native windows
we just call into the native implementation whenever the opacity changes.
However, for non-native windows we implement opacity by pushing a
second implicit paint that "stacks" on the existing one, acting as
an opacity group while rendering the window and its children.

This works well in general, although any native child windows will of
course not be opaque. However, there is no way to implement
implicit paint flushing (i.e. draw the currently drawn double buffer
to the window in order to allow direct drawing to the window).
We can't flush in the stacked implicit paint case because there
is no way to get the right drawing behaviour when drawing directly
to the window. We *must* draw to the opacity group to get the right
behaviour.

We currently flush if:
* A widget disables double buffering
* You call move/resize/scroll a window and it has non-native children
  during the expose handler

In case this happens we warn and flush the outermost group, so there may
be drawing errors.
Comment 32 Alexander Larsson 2013-02-07 10:12:34 UTC
Created attachment 235366 [details] [review]
Add gtk_widget_(un)register_window

This replaces the previously hardcoded calls to gdk_window_set_user_data,
and also lets us track which windows are a part of a widget. Old code
should continue working as is, but new features that require the
windows may not work perfectly.

We need this for the transparent widget support to work, as we need
to specially mark the windows of child widgets.
Comment 33 Alexander Larsson 2013-02-07 10:12:38 UTC
Created attachment 235367 [details] [review]
Add gtk_widget_get/set_opacity

This adds gtk_widget_get/set_opacity, as well as a GtkWidget.opacity
property. Additionally it deprectates gtk_window_get/set_opacity and
removes the GtkWindow.opacity property (in preference for the new
identical inherited property from GtkWidget, which should be ABI/API
compat).

The implementation is using the new gdk_window_set_opacity child
window support for windowed widgets, and cairo_push/pop_group()
bracketing in gtk_widget_draw() for non-window widgets.
Comment 34 Alexander Larsson 2013-02-07 10:12:42 UTC
Created attachment 235368 [details] [review]
Add widget transparency test to testgtk
Comment 35 Alexander Larsson 2013-02-07 10:12:46 UTC
Created attachment 235369 [details] [review]
TextHandle: Don't draw handles if not visible

When calling gtk_widget_draw() on the entry gtk_cairo_should_draw_window()
will return TRUE for all windows. This is used when rendering a widget to
somewhere other than the screen, and its now used for transparent widgets.
This caused the texthandle to always draw itself and terminate the draw
handler for the entry.

Instead we now only draw the markers when really visible, plus we return
FALSE to avoid stopping the entry drawing.
Comment 36 Alexander Larsson 2013-02-07 10:12:50 UTC
Created attachment 235370 [details] [review]
Add singleton for css number 1.0

This will be nice as this is will be the default for opacity.
Comment 37 Alexander Larsson 2013-02-07 10:12:53 UTC
Created attachment 235371 [details] [review]
css: Support opacity
Comment 38 Alexander Larsson 2013-02-07 10:12:58 UTC
Created attachment 235372 [details] [review]
css: Add opacity reftest
Comment 39 Alexander Larsson 2013-02-07 10:13:44 UTC
Attachment 235206 [details] pushed as ada6d81 - gdkwindow: Store the implicit paint in a list
Attachment 235207 [details] pushed as 4d3c77f - gdkwindow: Allow gdk_window_set_opacity on non-native children
Attachment 235208 [details] pushed as 3d4cd4d - Add gtk_widget_(un)register_window
Attachment 235209 [details] pushed as fa8b714 - Add gtk_widget_get/set_opacity
Attachment 235210 [details] pushed as b57a2c8 - Add widget transparency test to testgtk
Attachment 235211 [details] pushed as ebb84e8 - TextHandle: Don't draw handles if not visible
Attachment 235307 [details] pushed as 7d21ec2 - Add singleton for css number 1.0
Attachment 235308 [details] pushed as 366b4db - css: Support opacity
Attachment 235364 [details] pushed as ada6d81 - gdkwindow: Store the implicit paint in a list
Attachment 235365 [details] pushed as 4d3c77f - gdkwindow: Allow gdk_window_set_opacity on non-native children
Attachment 235366 [details] pushed as 3d4cd4d - Add gtk_widget_(un)register_window
Attachment 235367 [details] pushed as fa8b714 - Add gtk_widget_get/set_opacity
Attachment 235368 [details] pushed as b57a2c8 - Add widget transparency test to testgtk
Attachment 235369 [details] pushed as ebb84e8 - TextHandle: Don't draw handles if not visible
Attachment 235370 [details] pushed as 7d21ec2 - Add singleton for css number 1.0
Attachment 235371 [details] pushed as 366b4db - css: Support opacity
Attachment 235372 [details] pushed as 97c2354 - css: Add opacity reftest
Comment 40 Alexander Larsson 2013-02-11 13:22:41 UTC
Created attachment 235700 [details] [review]
Add gtk_widget_get/set_has_opacity_group

This adds a way to get the gtk_widget_set_opacity liike behaviour
of retargeting GdkWindows and exposing every child in ::draw, without
actually having an alpha. This is needed if you're doing more complex things
such as cross fading of widgets.
Comment 41 Alexander Larsson 2013-02-11 19:40:48 UTC
Created attachment 235734 [details] [review]
Clean up the opacity handling

This cleans up the internals but doesn't really change the behaviour.
Comment 42 Alexander Larsson 2013-02-11 19:40:56 UTC
Created attachment 235735 [details] [review]
Add gtk_widget_get/set_has_opacity_group

This adds a way to get the gtk_widget_set_opacity liike behaviour
of retargeting GdkWindows and exposing every child in ::draw, without
actually having an alpha. This is needed if you're doing more complex things
such as cross fading of widgets.
Comment 43 Alexander Larsson 2013-02-11 19:43:47 UTC
Comment on attachment 235734 [details] [review]
Clean up the opacity handling

Attachment 235734 [details] pushed as 7319f29 - Clean up the opacity handling
Comment 44 Alexander Larsson 2013-02-11 19:46:22 UTC
Benjamin had some reservations on the opacity group stuff, so i split out the pure cleanup from the patch and attached a rebased patch which is more focused on the problem at hand. 

I also commited a stack widget in libgd, and i have a patch for it that adds cross-fading based on this patch. Will attach that patch here.
Comment 45 Alexander Larsson 2013-02-11 19:48:42 UTC
Created attachment 235738 [details] [review]
GdStack: Add crossfade support
Comment 46 Alexander Larsson 2013-02-12 10:16:50 UTC
So, benjamin dislikes this patch because its some weird, hard to understand call that you have to magically know when to use when Gtk+ should just figure out when this was needed by itself.

So, let me back up a bit and try to describe the issue as I see it, and maybe we can come up with a way forward.

The issue appears when someone wants to write a container widget that treats its children in a different way than the traditional way of "each widget overpaints the previous in back-to-front order". The simplest example is to just have an alpha value and draw all the children to an offscreen buffer (via cairo_push_group()) and then draw the buffer using OPERATOR_OVER and paint_with_alpha. This is what the set_opacity() patch currently in does, but there are some cases where you need something more complicated. For instance, the code needed for the crossfade in the GdStack patch is essentially:

// opacity group where we combine two things with ADD
cairo_push_group (cr); 

cairo_set_source_surface (cr, xfade_surface, 0, 0);
cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
cairo_paint_with_alpha (cr, 1.0 - xfade_pos);

// group to merge all the child drawing into one for the ADD
cairo_push_group (cr); 
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
gtk_container_propagate_draw (GTK_CONTAINER (stack),child,cr);
cairo_pop_group_to_source (cr);

cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
cairo_paint_with_alpha (cr, xfade_pos);

cairo_pop_group_to_source (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_paint (cr);

If you were to put this code in the ::draw signal in a container things would work fine for trivial cases, but as soon as any descendant widget of the container had a GdkWindow things would break down for two reasons:

1) The container widget draw will draw on its (or its parents) GdkWindow, and such rendering will normally be clipped by a child GdkWindow. In fact, such clipping will be done by the expose machinery such that the expose region for the container widget will not even contain ares covered by child regions.

2) Even if we didn't clip, the normal gtk_container_propagate_draw() will not actually propagate into child widgets that have other windows, nor will the draw event draw to GdkWindows other than the one specified in the ExposeEvent. These will instead be draw in separate expose events later. This means that we will not get the right stack set up in order to redirect child widget drawing via cairo_push_group.

The way gtk_widget_set_opacity() fixes this is that once you've set that on a GtkWidget we traverse the GtkWidget hierarchy below it, finding all the GdkWindows that could cause problems like this and set them to be completely transparent using gdk_window_set_opacity(window, 0). This way we avoid all clipping in the parent windows, and we don't get any expose events for these windows.

This doesn't completely fix 2) above, so we also have to fiddle with the cairo_t to make should_draw_window() and propagate_draw() do the right thing for all child widgets.

With this set up code like the above cross fade will just work. So, why don't we *always* do things like this? Then we would need no magic for enabling cross fades. The reason for this is scrolling performance. In almost all our UIs it ends up that things that scroll are opaque (i.e. no pixels from below the scrolled widget blends through), which lets us do scrolling by copying an area in the window and then just rendering the newly exposed region. It turns out that this is actually very important (for instance, gnome-documents recently had to ensure a scrolledwindow was opaque to fix a really slow scrolling problem).

What gdk does to handle scrolling is to track all areas of a native window that are "layered" (i.e multple windows blend together), and whenever a window scrolls regions that are opaque gets scrolled by XCopyArea, whereas the layered areas are just re-exposed in their entierty. The way we track layered areas is by looking at information on the GdkWindow, such as the background (does it have alpha?) and the opacity (is it != 1.0?).

But, in an example like the above, there is really no way for Gdk to know that the windows in the cross-faded childs are layered, in fact the container window may not even have some GdkWindow in the gdk hierarchy, so we can't even flag this on some object. If we had such a window iin the hierarchy we can make that transparent to get the right behaviour, but if not we need to traverse the Gtk hierarchy to find the right child GdkWindows to make transparent.

So, IMHO we have the following options:

1) Don't allow draw signals to use push_group() style rendering, apart from what we do inside GtkWidget (i.e. gtk_widget_set_opacity).

2) Make *all* non-native GdkWindows transparent and do all hierarchy and clipping inside Gtk, allowing full drawing capabilities but disallow efficient scrolling.

3) Have a magic function that tells Gtk that this widget renders in a "fancy" way and needs to get all child windows made transparent, etc. For most things this will allow efficient scrolling, but not when inside a widget marked as fancy. This is the approach taken in the gtk_widget_set_has_opacity_group() patch.

4) Have a magic function that tells Gtk that a widget is *not* rendering in a "fancy" way, and unless that is called we make windows transparent, etc. Then we modify all the typical Gtk widgets that do have non-INPUT_ONLY widgets to set this flag, allowing us to get good performance in the "normal" case where the scrolledwindow is packed only inside typical containers.
Comment 47 Alexander Larsson 2013-02-12 10:22:02 UTC
I'd like to add that there is a compat risk with 2 and 4, as it changes how widgets are rendered in the "normal" case, so things that don't correctly handle gtk_cairo_should_draw_window() and expect multiple draws, one for each window, may break.
Comment 48 Alexander Larsson 2013-02-12 14:25:02 UTC
Another problem with 4 is that classes may derive from a class marked as not-fancy, or override the signal handler and make it fancy and they'd then need to mark it as fancy or things would break.
Comment 49 Alexander Larsson 2013-02-12 14:38:05 UTC
Created attachment 235776 [details] [review]
GdStack: Add crossfade support
Comment 50 Alexander Larsson 2013-02-12 14:38:33 UTC
added new rebase of crossfade patch
Comment 51 Alexander Larsson 2013-02-12 16:01:46 UTC
Created attachment 235782 [details] [review]
Add opacity_group hack

This adds a way to get the gtk_widget_set_opacity liike behaviour
of retargeting GdkWindows and exposing every child in ::draw, without
actually having an alpha. This is needed if you're doing more complex things
such as cross fading of widgets.

We do this as a hack by using opacity values that round to 255 yet not
really 1.0 in order to avoid having some magical API call for this
mainly internal call.
Comment 52 Alexander Larsson 2013-02-12 16:02:44 UTC
New patch which "hides" the API in gtk_widget_set_opacity, as this is kinda weird, mostly-internal API which we don't want to litter the API docs with.
Comment 53 Alexander Larsson 2013-02-12 16:03:23 UTC
Attachment 235782 [details] pushed as 8b9254c - Add opacity_group hack