From 366b4db791b03b39275c6767dede2c2089f40506 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 6 Feb 2013 14:29:22 +0100 Subject: [PATCH] css: Support opacity https://bugzilla.gnome.org/show_bug.cgi?id=687842 --- gtk/gtkcssstylepropertyimpl.c | 16 +++++ gtk/gtkcsstypesprivate.h | 1 + gtk/gtkwidget.c | 107 ++++++++++++++++++++++------------ 3 files changed, 86 insertions(+), 38 deletions(-) diff --git a/gtk/gtkcssstylepropertyimpl.c b/gtk/gtkcssstylepropertyimpl.c index c58edd85e1..26726d73d7 100644 --- a/gtk/gtkcssstylepropertyimpl.c +++ b/gtk/gtkcssstylepropertyimpl.c @@ -420,6 +420,14 @@ parse_css_direction (GtkCssStyleProperty *property, return _gtk_css_array_value_parse (parser, parse_one_css_direction); } +static GtkCssValue * +opacity_parse (GtkCssStyleProperty *property, + GtkCssParser *parser) +{ + return _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER); +} + + static GtkCssValue * parse_one_css_play_state (GtkCssParser *parser) { @@ -1382,6 +1390,14 @@ _gtk_css_style_property_init_properties (void) NULL, NULL, _gtk_css_array_value_new (_gtk_css_fill_mode_value_new (GTK_CSS_FILL_NONE))); + gtk_css_style_property_register ("opacity", + GTK_CSS_PROPERTY_OPACITY, + G_TYPE_NONE, + GTK_STYLE_PROPERTY_ANIMATED | GTK_STYLE_PROPERTY_NO_RESIZE, + opacity_parse, + NULL, + NULL, + _gtk_css_number_value_new (1, GTK_CSS_NUMBER)); gtk_css_style_property_register ("engine", GTK_CSS_PROPERTY_ENGINE, diff --git a/gtk/gtkcsstypesprivate.h b/gtk/gtkcsstypesprivate.h index 9c1f92ba2b..b2881feaea 100644 --- a/gtk/gtkcsstypesprivate.h +++ b/gtk/gtkcsstypesprivate.h @@ -132,6 +132,7 @@ enum { /*< skip >*/ GTK_CSS_PROPERTY_ANIMATION_PLAY_STATE, GTK_CSS_PROPERTY_ANIMATION_DELAY, GTK_CSS_PROPERTY_ANIMATION_FILL_MODE, + GTK_CSS_PROPERTY_OPACITY, GTK_CSS_PROPERTY_ENGINE, GTK_CSS_PROPERTY_GTK_KEY_BINDINGS, /* add more */ diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index a2faff55e1..d99ee5552e 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -37,6 +37,7 @@ #include "gtkaccelmapprivate.h" #include "gtkclipboard.h" #include "gtkcssstylepropertyprivate.h" +#include "gtkcssnumbervalueprivate.h" #include "gtkiconfactory.h" #include "gtkintl.h" #include "gtkmarshalers.h" @@ -362,6 +363,7 @@ struct _GtkWidgetPrivate guint norender : 1; /* Don't expose windows, instead recurse via draw */ guint8 alpha; + guint8 user_alpha; /* The widget's name. If the widget does not have a name * (the name is NULL), then its name (as returned by @@ -610,7 +612,8 @@ static PangoContext* gtk_widget_peek_pango_context (GtkWidget *widget); static void gtk_widget_update_pango_context (GtkWidget *widget); static void gtk_widget_propagate_state (GtkWidget *widget, GtkStateData *data); -; +static void gtk_widget_update_alpha (GtkWidget *widget); + static gint gtk_widget_event_internal (GtkWidget *widget, GdkEvent *event); static gboolean gtk_widget_real_mnemonic_activate (GtkWidget *widget, @@ -3743,6 +3746,7 @@ gtk_widget_init (GtkWidget *widget) priv->allocation.y = -1; priv->allocation.width = 1; priv->allocation.height = 1; + priv->user_alpha = 255; priv->alpha = 255; priv->window = NULL; priv->parent = NULL; @@ -6801,6 +6805,7 @@ gtk_widget_real_style_updated (GtkWidget *widget) GtkWidgetPrivate *priv = widget->priv; gtk_widget_update_pango_context (widget); + gtk_widget_update_alpha (widget); if (priv->style != NULL && priv->style != gtk_widget_get_default_style ()) @@ -13927,6 +13932,65 @@ gtk_widget_update_norender (GtkWidget *widget) propagate_norender_non_window (widget, norender | widget->priv->norender_children); } +static void +gtk_widget_update_alpha (GtkWidget *widget) +{ + GtkWidgetPrivate *priv; + double opacity; + guint8 alpha; + + priv = widget->priv; + + alpha = priv->user_alpha; + + if (priv->context) + { + opacity = + _gtk_css_number_value_get (_gtk_style_context_peek_property (priv->context, + GTK_CSS_PROPERTY_OPACITY), + 100); + opacity = CLAMP (opacity, 0.0, 1.0); + alpha = round (priv->user_alpha * opacity); + } + + if (alpha == priv->alpha) + return; + + 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. + + 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); +} + /** * gtk_widget_set_opacity: * @widget: a #GtkWidget @@ -13960,48 +14024,15 @@ gtk_widget_set_opacity (GtkWidget *widget, priv = widget->priv; - if (opacity < 0.0) - opacity = 0.0; - else if (opacity > 1.0) - opacity = 1.0; + opacity = CLAMP (opacity, 0.0, 1.0); alpha = round (opacity * 255); - if (alpha == priv->alpha) + if (alpha == priv->user_alpha) return; - priv->alpha = alpha; + priv->user_alpha = alpha; - if (gtk_widget_get_has_window (widget)) - { - if (priv->window != NULL) - gdk_window_set_opacity (priv->window, - priv->norender ? 0 : opacity); - } - 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. - - 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); + gtk_widget_update_alpha (widget); } /**