[gtk+] Clean up the opacity handling



commit 7319f2934ff0364dad54f71bac9a3e9a7cc8f92f
Author: Alexander Larsson <alexl redhat com>
Date:   Mon Feb 11 14:20:40 2013 +0100

    Clean up the opacity handling
    
    This cleans up the internals but doesn't really change the behaviour.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=687842

 gtk/gtkwidget.c |  153 ++++++++++++++++++++++++-------------------------------
 1 files changed, 67 insertions(+), 86 deletions(-)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 8278f98..7bbd4fa 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -712,7 +712,7 @@ static void gtk_widget_set_device_enabled_internal (GtkWidget *widget,
 static gboolean event_window_is_still_viewable (GdkEvent *event);
 static void gtk_cairo_set_event (cairo_t        *cr,
 				 GdkEventExpose *event);
-static void gtk_widget_update_norender (GtkWidget *widget);
+static void gtk_widget_propagate_alpha (GtkWidget *widget);
 
 /* --- variables --- */
 static gpointer         gtk_widget_parent_class = NULL;
@@ -4038,7 +4038,7 @@ gtk_widget_unparent (GtkWidget *widget)
     g_object_notify_queue_clear (G_OBJECT (widget), nqueue);
   g_object_notify_queue_thaw (G_OBJECT (widget), nqueue);
 
-  gtk_widget_update_norender (widget);
+  gtk_widget_propagate_alpha (widget);
 
   gtk_widget_pop_verify_invariants (widget);
   g_object_unref (widget);
@@ -8168,7 +8168,7 @@ gtk_widget_set_parent (GtkWidget *widget,
       gtk_widget_queue_compute_expand (parent);
     }
 
-  gtk_widget_update_norender (widget);
+  gtk_widget_propagate_alpha (widget);
 
   gtk_widget_pop_verify_invariants (widget);
 }
@@ -13770,7 +13770,7 @@ gtk_widget_register_window (GtkWidget    *widget,
 
   if (!gtk_widget_get_has_window (widget) && !gdk_window_has_native (window))
     gdk_window_set_opacity (window,
-			    (priv->norender || priv->norender_children) ? 0.0 : 1.0);
+			    priv->norender_children ? 0.0 : 1.0);
 }
 
 /**
@@ -13864,72 +13864,82 @@ gtk_widget_set_support_multidevice (GtkWidget *widget,
     gdk_window_set_support_multidevice (priv->window, support_multidevice);
 }
 
-static void apply_norender (GtkWidget *widget, gboolean norender);
+/* There are multiple alpha related sources, the user can specify alpha
+ * in gtk_widget_set_opacity, secondly we can get it from the css opacity. These two
+ * are multiplied together to form the total alpha.
+ *
+ * We handle opacity in two ways. For a windowed widget, with opacity set but no opacity
+ * group we directly set the opacity of widget->window. This will cause gdk to properly
+ * redirect drawing inside the window to a buffer and do OVER paint_with_alpha.
+ *
+ * However, if the widget is not windowed,
+ * we do the opacity handling in the ::draw marshaller for the widget. A naive
+ * implementation of this would break for windowed widgets or descendant widgets with
+ * windows, as these would not be handled by the ::draw signal. To handle this we set
+ * all such gdkwindows as fully transparent and then override gtk_cairo_should_draw_window()
+ * to make the draw signal propagate to *all* child widgets/windows.
+ *
+ * Note: We don't make all child windows fully transparent, we stop at the first one
+ * in each branch when propagating down the hierarchy.
+ */
 
-static void
-apply_norender_cb (GtkWidget *widget, gpointer norender)
-{
-  apply_norender (widget, GPOINTER_TO_INT (norender));
-}
 
+/* This is called when priv->alpha changes, and should
+ * update priv->norender and GdkWindow opacity for this widget and any children that
+ * needs changing. It is also called whenver the parent changes, the parents
+ * norender_children state changes, or the has_window state of the widget changes.
+ */
 static void
-propagate_norender_non_window (GtkWidget *widget, gboolean norender)
+gtk_widget_propagate_alpha (GtkWidget *widget)
 {
+  GtkWidgetPrivate *priv = widget->priv;
+  GtkWidget *parent;
+  gboolean norender, norender_children;
   GList *l;
 
-  g_assert (!gtk_widget_get_has_window (widget));
-
-  for (l = widget->priv->registered_windows; l != NULL; l = l->next)
-    gdk_window_set_opacity (l->data,
-			    norender ? 0 : widget->priv->alpha / 255.0);
-
-  if (GTK_IS_CONTAINER (widget))
-    gtk_container_forall (GTK_CONTAINER (widget), apply_norender_cb,
-			  GINT_TO_POINTER (norender));
-}
-
-static void
-apply_norender (GtkWidget *widget, gboolean norender)
-{
-  if (widget->priv->norender == norender)
-    return;
+  parent = priv->parent;
 
-  widget->priv->norender = norender;
+  norender =
+    /* If the parent has norender_children, propagate that here */
+    (parent != NULL && parent->priv->norender_children);
+
+  /* Windowed widget children should norender if: */
+  norender_children =
+    /* The widget is no_window (otherwise its enought to mark it norender/alpha), and */
+    !gtk_widget_get_has_window (widget) &&
+     ( /* norender is set, or */
+      norender ||
+      /* widget has an alpha */
+      priv->alpha != 255
+       );
 
   if (gtk_widget_get_has_window (widget))
     {
-      if (widget->priv->window != NULL)
-	gdk_window_set_opacity (widget->priv->window,
-				norender ? 0 : widget->priv->alpha / 255.0);
+      if (priv->window != NULL && !gdk_window_has_native (priv->window))
+	gdk_window_set_opacity (priv->window,
+				norender ? 0 : priv->alpha / 255.0);
+    }
+  else /* !has_window */
+    {
+      for (l = priv->registered_windows; l != NULL; l = l->next)
+	{
+	  GdkWindow *w = l->data;
+	  if (!gdk_window_has_native (w))
+	    gdk_window_set_opacity (w, norender_children ? 0.0 : 1.0);
+	}
     }
-  else
-    propagate_norender_non_window (widget, norender | widget->priv->norender_children);
-}
-
-/* This is called when the norender_children state of a non-window widget changes,
- * its parent changes, or its has_window state changes. It means we need
- * to update the norender of all the windows owned by the widget and those
- * of child widgets, up to and including the first windowed widgets in the hierarchy.
- */
-static void
-gtk_widget_update_norender (GtkWidget *widget)
-{
-  gboolean norender;
-  GtkWidget *parent;
-
-  parent = widget->priv->parent;
 
-  norender =
-    parent != NULL &&
-    (parent->priv->norender_children ||
-     (parent->priv->norender && !gtk_widget_get_has_window (parent)));
+  priv->norender = norender;
+  if (priv->norender_children != norender_children)
+    {
+      priv->norender_children = norender_children;
 
-  apply_norender (widget, norender);
+      if (GTK_IS_CONTAINER (widget))
+	gtk_container_forall (GTK_CONTAINER (widget), (GtkCallback)gtk_widget_propagate_alpha, NULL);
+    }
 
-  /* The above may not have propagated to children if norender_children changed but
-     not norender, so we need to enforce propagation. */
-  if (!gtk_widget_get_has_window (widget))
-    propagate_norender_non_window (widget, norender | widget->priv->norender_children);
+  if (gtk_widget_get_realized (widget))
+    gtk_widget_queue_draw (widget);
 }
 
 static void
@@ -13958,37 +13968,8 @@ gtk_widget_update_alpha (GtkWidget *widget)
 
   priv->alpha = alpha;
 
-    if (gtk_widget_get_has_window (widget))
-    {
-      if (priv->window != NULL)
-	gdk_window_set_opacity (priv->window,
-				priv->norender ? 0 : priv->alpha / 255.0);
-    }
-  else
-    {
-      /* For non windowed widgets we can't use gdk_window_set_opacity() directly, as there is
-	 no GdkWindow at the right place in the hierarchy. For no-window widget this is not a problem,
-	 as we just push an opacity group in the draw marshaller.
-
-	 However, that only works for non-window descendant widgets. If any descendant has a
-	 window that window will not normally be rendered in the draw signal, so the opacity
-	 group will not work for it.
+  gtk_widget_propagate_alpha (widget);
 
-	 To fix this we set all such windows to a zero opacity, meaning they don't get drawn
-	 by gdk, and instead we set a NULL _gtk_cairo_get_event during expose so that the draw
-	 handler recurses into windowed widgets.
-
-	 We do this by setting "norender_children", which means that any windows in this widget
-	 or its ancestors (stopping at the first such windows at each branch in the hierarchy)
-	 are set to zero opacity. This is then propagated into all necessary children as norender,
-	 which controls whether a window should be exposed or not.
-      */
-      priv->norender_children = priv->alpha != 255;
-      gtk_widget_update_norender (widget);
-    }
-
-  if (gtk_widget_get_realized (widget))
-    gtk_widget_queue_draw (widget);
 }
 
 /**


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]