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.
This commit is contained in:
Benjamin Otte
2018-02-18 00:25:51 +01:00
parent 3797b7e998
commit 50373a91a2
11 changed files with 345 additions and 7 deletions

View File

@@ -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 },

147
gtk/gtkcssimagepaint.c Normal file
View File

@@ -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)
{
}

View File

@@ -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 *

View File

@@ -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__ */

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);

View File

@@ -48,6 +48,7 @@ gtk_private_sources = files([
'gtkcssimageicontheme.c',
'gtkcssimageinvalid.c',
'gtkcssimagelinear.c',
'gtkcssimagepaint.c',
'gtkcssimagepaintable.c',
'gtkcssimageradial.c',
'gtkcssimagerecolor.c',