[gtk/wip/otte/paintable: 61/62] css: Add initial support for Houdini-style paint() images



commit a4ae7dc9c511dd7f5c0848731c4029745bd76b7d
Author: Benjamin Otte <otte redhat com>
Date:   Sun Feb 18 00:25:51 2018 +0100

    css: Add initial support for Houdini-style paint() images
    
    This adds gtk_style_context_add_paint_for_display() which allows to add
    a GdkPaintable with a name to the CSS machinery and then allows using it
    via "-gtk-paint(name)" inside the CSS as an image.
    
    This essentially allows applications to register snapshot functions for
    any part of the CSS machinery that is an image.
    I have no idea if this is a good idea, because it further blurs the
    lines between theme and application, but it *is* quite powerful.
    
    The inspiration was
    https://drafts.css-houdini.org/css-paint-api/
    Though we do not support properties or a constructor, but so far require
    the constructed image directly.

 gtk/gtkcssimage.c             |   8 ++-
 gtk/gtkcssimagepaint.c        | 147 ++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkcssimagepaintable.c    |   2 +-
 gtk/gtkcssimagepaintprivate.h |  53 +++++++++++++++
 gtk/gtkstylecascade.c         |  74 +++++++++++++++++++++
 gtk/gtkstylecascadeprivate.h  |   6 ++
 gtk/gtkstylecontext.c         |  28 ++++++++
 gtk/gtkstylecontext.h         |   7 ++
 gtk/gtkstyleprovider.c        |  16 +++++
 gtk/gtkstyleproviderprivate.h |  10 ++-
 gtk/meson.build               |   1 +
 11 files changed, 345 insertions(+), 7 deletions(-)
---
diff --git a/gtk/gtkcssimage.c b/gtk/gtkcssimage.c
index 9160333228..e84c2d5f68 100644
--- a/gtk/gtkcssimage.c
+++ b/gtk/gtkcssimage.c
@@ -26,13 +26,14 @@
 
 /* for the types only */
 #include "gtk/gtkcssimagecrossfadeprivate.h"
+#include "gtk/gtkcssimagefallbackprivate.h"
 #include "gtk/gtkcssimageiconthemeprivate.h"
 #include "gtk/gtkcssimagelinearprivate.h"
+#include "gtk/gtkcssimagepaintprivate.h"
 #include "gtk/gtkcssimageradialprivate.h"
-#include "gtk/gtkcssimageurlprivate.h"
-#include "gtk/gtkcssimagescaledprivate.h"
 #include "gtk/gtkcssimagerecolorprivate.h"
-#include "gtk/gtkcssimagefallbackprivate.h"
+#include "gtk/gtkcssimagescaledprivate.h"
+#include "gtk/gtkcssimageurlprivate.h"
 #include "gtk/gtkcssimagewin32private.h"
 
 G_DEFINE_ABSTRACT_TYPE (GtkCssImage, _gtk_css_image, G_TYPE_OBJECT)
@@ -501,6 +502,7 @@ gtk_css_image_get_parser_type (GtkCssParser *parser)
   } image_types[] = {
     { "url", _gtk_css_image_url_get_type },
     { "-gtk-icontheme", _gtk_css_image_icon_theme_get_type },
+    { "-gtk-paint", gtk_css_image_paint_get_type },
     { "-gtk-scaled", _gtk_css_image_scaled_get_type },
     { "-gtk-recolor", _gtk_css_image_recolor_get_type },
     { "-gtk-win32-theme-part", _gtk_css_image_win32_get_type },
diff --git a/gtk/gtkcssimagepaint.c b/gtk/gtkcssimagepaint.c
new file mode 100644
index 0000000000..5850dfe929
--- /dev/null
+++ b/gtk/gtkcssimagepaint.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gtkcssimagepaintprivate.h"
+
+#include "gtkcssimageinvalidprivate.h"
+#include "gtkcssimagepaintableprivate.h"
+#include "gtkstyleproviderprivate.h"
+
+G_DEFINE_TYPE (GtkCssImagePaint, gtk_css_image_paint, GTK_TYPE_CSS_IMAGE)
+
+static void
+gtk_css_image_paint_snapshot (GtkCssImage *image,
+                              GtkSnapshot *snapshot,
+                              double       width,
+                              double       height)
+{
+}
+
+static GtkCssImage *
+gtk_css_image_paint_compute (GtkCssImage      *image,
+                             guint             property_id,
+                             GtkStyleProvider *provider,
+                             GtkCssStyle      *style,
+                             GtkCssStyle      *parent_style)
+{
+  GtkCssImagePaint *paint = GTK_CSS_IMAGE_PAINT (image);
+  GdkPaintable *paintable, *static_paintable;
+  GtkCssImage *result;
+
+  paintable = gtk_style_provider_get_paint (provider, paint->name);
+  if (paintable == NULL)
+    return gtk_css_image_invalid_new ();
+
+  static_paintable = gdk_paintable_get_current_image (paintable);
+  result = gtk_css_image_paintable_new (paintable, static_paintable);
+  g_object_unref (static_paintable);
+
+  return result;
+}
+
+static gboolean
+gtk_css_image_paint_equal (GtkCssImage *image1,
+                           GtkCssImage *image2)
+{
+  GtkCssImagePaint *paint1 = GTK_CSS_IMAGE_PAINT (image1);
+  GtkCssImagePaint *paint2 = GTK_CSS_IMAGE_PAINT (image2);
+
+  return g_str_equal (paint1->name, paint2->name);
+}
+
+static gboolean
+gtk_css_image_paint_parse (GtkCssImage  *image,
+                           GtkCssParser *parser)
+{
+  GtkCssImagePaint *paint = GTK_CSS_IMAGE_PAINT (image);
+
+  if (!_gtk_css_parser_try (parser, "-gtk-paint", TRUE))
+    {
+      _gtk_css_parser_error (parser, "'-gtk-paint'");
+      return FALSE;
+    }
+
+  if (!_gtk_css_parser_try (parser, "(", TRUE))
+    {
+      _gtk_css_parser_error (parser, "Expected '(' after '-gtk-paint'");
+      return FALSE;
+    }
+
+  paint->name = _gtk_css_parser_try_ident (parser, TRUE);
+  if (paint->name == NULL)
+    {
+      _gtk_css_parser_error (parser, "Expected the name of the paint");
+      return FALSE;
+    }
+
+  if (!_gtk_css_parser_try (parser, ")", TRUE))
+    {
+      _gtk_css_parser_error (parser,
+                             "Expected ')' at end of '-gtk-paint'");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+gtk_css_image_paint_print (GtkCssImage *image,
+                           GString     *string)
+{
+  GtkCssImagePaint *paint = GTK_CSS_IMAGE_PAINT (image);
+
+  g_string_append (string, "paint (");
+  g_string_append (string, paint->name);
+  g_string_append (string, ")");
+}
+
+static void
+gtk_css_image_paint_dispose (GObject *object)
+{
+  GtkCssImagePaint *paint = GTK_CSS_IMAGE_PAINT (object);
+
+  g_clear_pointer (&paint->name, g_free);
+
+  G_OBJECT_CLASS (gtk_css_image_paint_parent_class)->dispose (object);
+}
+
+static void
+gtk_css_image_paint_class_init (GtkCssImagePaintClass *klass)
+{
+  GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  image_class->compute = gtk_css_image_paint_compute;
+  image_class->snapshot = gtk_css_image_paint_snapshot;
+  image_class->parse = gtk_css_image_paint_parse;
+  image_class->print = gtk_css_image_paint_print;
+  image_class->equal = gtk_css_image_paint_equal;
+
+  object_class->dispose = gtk_css_image_paint_dispose;
+}
+
+static void
+gtk_css_image_paint_init (GtkCssImagePaint *image_paint)
+{
+}
+
diff --git a/gtk/gtkcssimagepaintable.c b/gtk/gtkcssimagepaintable.c
index 327ca90862..e25262bacd 100644
--- a/gtk/gtkcssimagepaintable.c
+++ b/gtk/gtkcssimagepaintable.c
@@ -122,7 +122,7 @@ gtk_css_image_paintable_is_dynamic (GtkCssImage *image)
 {
   GtkCssImagePaintable *paintable = GTK_CSS_IMAGE_PAINTABLE (image);
 
-  return (gdk_paintable_get_flags (paintable->paintable) & GDK_PAINTABLE_IMMUTABLE) == 
GDK_PAINTABLE_IMMUTABLE;
+  return (gdk_paintable_get_flags (paintable->paintable) & GDK_PAINTABLE_IMMUTABLE) != 
GDK_PAINTABLE_IMMUTABLE;
 }
 
 static GtkCssImage *
diff --git a/gtk/gtkcssimagepaintprivate.h b/gtk/gtkcssimagepaintprivate.h
new file mode 100644
index 0000000000..514bb88042
--- /dev/null
+++ b/gtk/gtkcssimagepaintprivate.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_CSS_IMAGE_PAINT_PRIVATE_H__
+#define __GTK_CSS_IMAGE_PAINT_PRIVATE_H__
+
+#include "gtk/gtkcssimageprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CSS_IMAGE_PAINT           (gtk_css_image_paint_get_type ())
+#define GTK_CSS_IMAGE_PAINT(obj)           (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_CSS_IMAGE_PAINT, 
GtkCssImagePaint))
+#define GTK_CSS_IMAGE_PAINT_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_CSS_IMAGE_PAINT, 
GtkCssImagePaintClass))
+#define GTK_IS_CSS_IMAGE_PAINT(obj)        (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_CSS_IMAGE_PAINT))
+#define GTK_IS_CSS_IMAGE_PAINT_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_CSS_IMAGE_PAINT))
+#define GTK_CSS_IMAGE_PAINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CSS_IMAGE_PAINT, 
GtkCssImagePaintClass))
+
+typedef struct _GtkCssImagePaint           GtkCssImagePaint;
+typedef struct _GtkCssImagePaintClass      GtkCssImagePaintClass;
+
+struct _GtkCssImagePaint
+{
+  GtkCssImage parent;
+
+  char          *name;                /* the name of the paint */
+};
+
+struct _GtkCssImagePaintClass
+{
+  GtkCssImageClass parent_class;
+};
+
+GType           gtk_css_image_paint_get_type                    (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_IMAGE_PAINT_PRIVATE_H__ */
diff --git a/gtk/gtkstylecascade.c b/gtk/gtkstylecascade.c
index 4c94696742..b879f46032 100644
--- a/gtk/gtkstylecascade.c
+++ b/gtk/gtkstylecascade.c
@@ -155,6 +155,42 @@ gtk_style_cascade_get_color (GtkStyleProvider *provider,
   return NULL;
 }
 
+static GdkPaintable *
+gtk_style_cascade_get_paint (GtkStyleProvider *provider,
+                             const char       *name)
+{
+  GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
+  GtkStyleCascadeIter iter;
+  GdkPaintable *paintable;
+  GtkStyleProvider *item;
+
+  for (item = gtk_style_cascade_iter_init (cascade, &iter);
+       item;
+       item = gtk_style_cascade_iter_next (cascade, &iter))
+    {
+      if (GTK_IS_STYLE_PROVIDER (item))
+        {
+          paintable = gtk_style_provider_get_paint (GTK_STYLE_PROVIDER (item), name);
+          if (paintable)
+            {
+              gtk_style_cascade_iter_clear (&iter);
+              return paintable;
+            }
+        }
+      else
+        {
+          /* If somebody hits this code path, shout at them */
+        }
+    }
+
+  gtk_style_cascade_iter_clear (&iter);
+
+  if (cascade->paints == NULL)
+    return NULL;
+
+  return g_hash_table_lookup (cascade->paints, name);
+}
+
 static int
 gtk_style_cascade_get_scale (GtkStyleProvider *provider)
 {
@@ -227,6 +263,7 @@ static void
 gtk_style_cascade_provider_iface_init (GtkStyleProviderInterface *iface)
 {
   iface->get_color = gtk_style_cascade_get_color;
+  iface->get_paint = gtk_style_cascade_get_paint;
   iface->get_settings = gtk_style_cascade_get_settings;
   iface->get_scale = gtk_style_cascade_get_scale;
   iface->get_keyframes = gtk_style_cascade_get_keyframes;
@@ -244,6 +281,7 @@ gtk_style_cascade_dispose (GObject *object)
 
   _gtk_style_cascade_set_parent (cascade, NULL);
   g_array_unref (cascade->providers);
+  g_clear_pointer (&cascade->paints, g_hash_table_unref);
 
   G_OBJECT_CLASS (_gtk_style_cascade_parent_class)->dispose (object);
 }
@@ -386,3 +424,39 @@ _gtk_style_cascade_get_scale (GtkStyleCascade *cascade)
 
   return cascade->scale;
 }
+
+gboolean
+gtk_style_cascade_add_paint (GtkStyleCascade *cascade,
+                             const char      *name,
+                             GdkPaintable    *paintable)
+{
+  if (cascade->paints == NULL)
+    {
+      cascade->paints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+    }
+  else
+    {
+      if (g_hash_table_contains (cascade->paints, name))
+        return FALSE;
+    }
+
+  g_hash_table_insert (cascade->paints, g_strdup (name), g_object_ref (paintable));
+  gtk_style_provider_changed (GTK_STYLE_PROVIDER (cascade));
+
+  return TRUE;
+}
+
+gboolean
+gtk_style_cascade_remove_paint (GtkStyleCascade *cascade,
+                                const char      *name)
+{
+  gboolean result;
+
+  if (cascade->paints == NULL)
+    return FALSE;
+
+  result = g_hash_table_remove (cascade->paints, name);
+  if (result)
+    gtk_style_provider_changed (GTK_STYLE_PROVIDER (cascade));
+  return result;
+}
diff --git a/gtk/gtkstylecascadeprivate.h b/gtk/gtkstylecascadeprivate.h
index ad2f7e69d0..5748453d23 100644
--- a/gtk/gtkstylecascadeprivate.h
+++ b/gtk/gtkstylecascadeprivate.h
@@ -38,6 +38,7 @@ struct _GtkStyleCascade
   GObject object;
 
   GtkStyleCascade *parent;
+  GHashTable *paints;
   GArray *providers;
   int scale;
 };
@@ -62,6 +63,11 @@ void                  _gtk_style_cascade_add_provider           (GtkStyleCascade
                                                                  guint                priority);
 void                  _gtk_style_cascade_remove_provider        (GtkStyleCascade     *cascade,
                                                                  GtkStyleProvider    *provider);
+gboolean              gtk_style_cascade_add_paint               (GtkStyleCascade     *cascade,
+                                                                 const char          *name,
+                                                                 GdkPaintable        *paintable);
+gboolean              gtk_style_cascade_remove_paint            (GtkStyleCascade     *cascade,
+                                                                 const char          *name);
 
 
 G_END_DECLS
diff --git a/gtk/gtkstylecontext.c b/gtk/gtkstylecontext.c
index 16897067d0..2a599db505 100644
--- a/gtk/gtkstylecontext.c
+++ b/gtk/gtkstylecontext.c
@@ -638,6 +638,34 @@ gtk_style_context_remove_provider_for_display (GdkDisplay       *display,
   _gtk_style_cascade_remove_provider (cascade, provider);
 }
 
+gboolean
+gtk_style_context_add_paint_for_display (GdkDisplay   *display,
+                                         const char   *name,
+                                         GdkPaintable *paintable)
+{
+  GtkStyleCascade *cascade;
+
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+  g_return_val_if_fail (GDK_IS_PAINTABLE (paintable), FALSE);
+
+  cascade = _gtk_settings_get_style_cascade (gtk_settings_get_for_display (display), 1);
+  return gtk_style_cascade_add_paint (cascade, name, paintable);
+}
+
+gboolean
+gtk_style_context_remove_paint_for_display (GdkDisplay *display,
+                                            const char *name)
+{
+  GtkStyleCascade *cascade;
+
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  cascade = _gtk_settings_get_style_cascade (gtk_settings_get_for_display (display), 1);
+  return gtk_style_cascade_remove_paint (cascade, name);
+}
+
 /**
  * gtk_style_context_get_section:
  * @context: a #GtkStyleContext
diff --git a/gtk/gtkstylecontext.h b/gtk/gtkstylecontext.h
index bfe513067f..f5f84cc407 100644
--- a/gtk/gtkstylecontext.h
+++ b/gtk/gtkstylecontext.h
@@ -945,6 +945,13 @@ void gtk_style_context_add_provider_for_display    (GdkDisplay       *display,
 GDK_AVAILABLE_IN_ALL
 void gtk_style_context_remove_provider_for_display (GdkDisplay       *display,
                                                     GtkStyleProvider *provider);
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_style_context_add_paint_for_display   (GdkDisplay       *display,
+                                                    const char       *name,
+                                                    GdkPaintable     *paintable);
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_style_context_remove_paint_for_display(GdkDisplay       *display,
+                                                    const char       *name);
 
 GDK_AVAILABLE_IN_ALL
 void gtk_style_context_add_provider    (GtkStyleContext  *context,
diff --git a/gtk/gtkstyleprovider.c b/gtk/gtkstyleprovider.c
index 3fb0f49afa..d9160d79d8 100644
--- a/gtk/gtkstyleprovider.c
+++ b/gtk/gtkstyleprovider.c
@@ -153,6 +153,22 @@ gtk_style_provider_get_scale (GtkStyleProvider *provider)
   return iface->get_scale (provider);
 }
 
+GdkPaintable *
+gtk_style_provider_get_paint (GtkStyleProvider *provider,
+                              const char       *name)
+{
+  GtkStyleProviderInterface *iface;
+
+  gtk_internal_return_val_if_fail (GTK_IS_STYLE_PROVIDER (provider), NULL);
+
+  iface = GTK_STYLE_PROVIDER_GET_INTERFACE (provider);
+
+  if (!iface->get_paint)
+    return NULL;
+
+  return iface->get_paint (provider, name);
+}
+
 void
 gtk_style_provider_emit_error (GtkStyleProvider *provider,
                                GtkCssSection    *section,
diff --git a/gtk/gtkstyleproviderprivate.h b/gtk/gtkstyleproviderprivate.h
index c33438dd99..ed9ac36a5d 100644
--- a/gtk/gtkstyleproviderprivate.h
+++ b/gtk/gtkstyleproviderprivate.h
@@ -35,7 +35,9 @@ struct _GtkStyleProviderInterface
 {
   GTypeInterface g_iface;
 
-  GtkCssValue *         (* get_color)           (GtkStyleProvider *provider,
+  GtkCssValue *         (* get_color)           (GtkStyleProvider        *provider,
+                                                 const char              *name);
+  GdkPaintable *        (* get_paint)           (GtkStyleProvider        *provider,
                                                  const char              *name);
   GtkSettings *         (* get_settings)        (GtkStyleProvider *provider);
   GtkCssKeyframes *     (* get_keyframes)       (GtkStyleProvider *provider,
@@ -52,8 +54,10 @@ struct _GtkStyleProviderInterface
   void                  (* changed)             (GtkStyleProvider *provider);
 };
 
-GtkSettings *           gtk_style_provider_get_settings          (GtkStyleProvider *provider);
-GtkCssValue *           gtk_style_provider_get_color             (GtkStyleProvider *provider,
+GtkSettings *           gtk_style_provider_get_settings          (GtkStyleProvider        *provider);
+GtkCssValue *           gtk_style_provider_get_color             (GtkStyleProvider        *provider,
+                                                                  const char              *name);
+GdkPaintable *          gtk_style_provider_get_paint             (GtkStyleProvider        *provider,
                                                                   const char              *name);
 GtkCssKeyframes *       gtk_style_provider_get_keyframes         (GtkStyleProvider *provider,
                                                                   const char              *name);
diff --git a/gtk/meson.build b/gtk/meson.build
index 1f347dd2d4..89138ea2a7 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -48,6 +48,7 @@ gtk_private_sources = files([
   'gtkcssimageicontheme.c',
   'gtkcssimageinvalid.c',
   'gtkcssimagelinear.c',
+  'gtkcssimagepaint.c',
   'gtkcssimagepaintable.c',
   'gtkcssimageradial.c',
   'gtkcssimagerecolor.c',


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