From 7cdbdb663c35b72f8934698e8823d96ebef78ce5 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 28 Aug 2021 03:46:55 +0200 Subject: [PATCH] gtk-demo: Add a symbolic paintable demo Don't click the button! --- demos/gtk-demo/demo.gresource.xml | 1 + demos/gtk-demo/meson.build | 1 + demos/gtk-demo/paintable_symbolic.c | 208 ++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 demos/gtk-demo/paintable_symbolic.c diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index 09709d48d8..83e798fd25 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -321,6 +321,7 @@ paintable_emblem.c paintable_mediastream.c paintable_svg.c + paintable_symbolic.c panes.c password_entry.c peg_solitaire.c diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build index 24e4abe05d..9201655988 100644 --- a/demos/gtk-demo/meson.build +++ b/demos/gtk-demo/meson.build @@ -67,6 +67,7 @@ demos = files([ 'paintable_animated.c', 'paintable_emblem.c', 'paintable_mediastream.c', + 'paintable_symbolic.c', 'panes.c', 'password_entry.c', 'peg_solitaire.c', diff --git a/demos/gtk-demo/paintable_symbolic.c b/demos/gtk-demo/paintable_symbolic.c new file mode 100644 index 0000000000..6be093f6e6 --- /dev/null +++ b/demos/gtk-demo/paintable_symbolic.c @@ -0,0 +1,208 @@ +/* Paintable/Symbolic Paintable + * + * GdkPaintables can be made to follow the theme's colors. GTK calls + * icons that do this symbolic icons, paintables that want to have + * the same effect can implement the GtkSymbolicPaintable interface. + * + * We will adapt the original paintable example by adding the ability + * to recolor the paintable based on the symbolic colors. + */ + +#include + +#include "paintable.h" + +static GtkWidget *window = NULL; + +/* First, add the boilerplate for the object itself. + * This part would normally go in the header. + */ +#define GTK_TYPE_NUCLEAR_SYMBOLIC (gtk_nuclear_symbolic_get_type ()) +G_DECLARE_FINAL_TYPE (GtkNuclearSymbolic, gtk_nuclear_symbolic, GTK, NUCLEAR_SYMBOLIC, GObject) + +/* Declare a few warning levels, so we can pick colors based on them */ +typedef enum +{ + WARNING_NONE, + WARNING_ALERT, + WARNING_EMERGENCY +} WarningLevel; + +/* Declare the struct. */ +struct _GtkNuclearSymbolic +{ + GObject parent_instance; + + WarningLevel warning_level; +}; + +struct _GtkNuclearSymbolicClass +{ + GObjectClass parent_class; +}; + +/* Add a function to draw the nuclear icon in the given colors */ +static void +gtk_nuclear_symbolic_snapshot_symbolic (GtkSymbolicPaintable *paintable, + GdkSnapshot *snapshot, + double width, + double height, + const GdkRGBA *colors, + gsize n_colors) +{ + GtkNuclearSymbolic *self = GTK_NUCLEAR_SYMBOLIC (paintable); + static const GdkRGBA transparent = { 0, }; + const GdkRGBA *bg_color; + + /* select the right background color from the warning level */ + switch (self->warning_level) + { + case WARNING_NONE: + bg_color = &transparent; + break; + case WARNING_ALERT: + bg_color = &colors[2]; /* warning color */ + break; + case WARNING_EMERGENCY: + bg_color = &colors[1]; /* error color */ + break; + default: + /* This should never happen, but we better do defensive coding + * with this critical icon */ + g_assert_not_reached (); + bg_color = &transparent; + break; + } + + /* Draw the icon with the selected warning color */ + gtk_nuclear_snapshot (snapshot, + &colors[0], /* foreground color */ + bg_color, + width, height, + 0); +} + +static void +gtk_nuclear_symbolic_symbolic_paintable_init (GtkSymbolicPaintableInterface *iface) +{ + iface->snapshot_symbolic = gtk_nuclear_symbolic_snapshot_symbolic; +} + +/* We need to implement the functionality required by the GdkPaintable interface */ +static void +gtk_nuclear_symbolic_snapshot (GdkPaintable *paintable, + GdkSnapshot *snapshot, + double width, + double height) +{ + /* Calling this function without passing a color is a neat trick + * to make GTK use default colors and otherwise forward the call + * to the snapshotting function above. + */ + gtk_symbolic_paintable_snapshot_symbolic (GTK_SYMBOLIC_PAINTABLE (paintable), + snapshot, + width, height, + NULL, 0); +} + +static GdkPaintableFlags +gtk_nuclear_symbolic_get_flags (GdkPaintable *paintable) +{ + /* This image has a static size, but the contents may change: + * We draw different things when the warning level changes. + */ + return GDK_PAINTABLE_STATIC_SIZE; +} + +static void +gtk_nuclear_symbolic_paintable_init (GdkPaintableInterface *iface) +{ + iface->snapshot = gtk_nuclear_symbolic_snapshot; + iface->get_flags = gtk_nuclear_symbolic_get_flags; +} + +/* When defining the GType, we need to implement bot the GdkPaintable + * and the GtkSymbolicPaintable interface */ +G_DEFINE_TYPE_WITH_CODE (GtkNuclearSymbolic, gtk_nuclear_symbolic, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, + gtk_nuclear_symbolic_paintable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_SYMBOLIC_PAINTABLE, + gtk_nuclear_symbolic_symbolic_paintable_init)) + +static void +gtk_nuclear_symbolic_class_init (GtkNuclearSymbolicClass *klass) +{ +} + +static void +gtk_nuclear_symbolic_init (GtkNuclearSymbolic *nuclear) +{ +} + +/* And finally, we add the simple constructor we declared in the header. */ +GdkPaintable * +gtk_nuclear_symbolic_new (void) +{ + return g_object_new (GTK_TYPE_NUCLEAR_SYMBOLIC, NULL); +} + +/* Add some fun feature to the button */ +static void +nuclear_button_clicked (GtkButton *button, + GtkNuclearSymbolic *nuclear) +{ + if (nuclear->warning_level >= WARNING_EMERGENCY) + { + /* On maximum warning level, reset the warning */ + nuclear->warning_level = WARNING_NONE; + /* And sometimes (but not always to confuse people) + * close the window. + */ + if (g_random_boolean ()) + gtk_window_close (GTK_WINDOW (window)); + } + else + { + /* Otherwise just increase the warning level */ + nuclear->warning_level++; + } + + /* Don't forget to emit the signal causing the paintable to redraw. + * Changing the warning level changes the background color after all. + */ + gdk_paintable_invalidate_contents (GDK_PAINTABLE (nuclear)); +} + +GtkWidget * +do_paintable_symbolic (GtkWidget *do_widget) +{ + GdkPaintable *nuclear; + GtkWidget *image, *button; + + if (!window) + { + window = gtk_window_new (); + gtk_window_set_display (GTK_WINDOW (window), + gtk_widget_get_display (do_widget)); + gtk_window_set_title (GTK_WINDOW (window), "Don't click!"); + gtk_window_set_default_size (GTK_WINDOW (window), 200, 200); + g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window); + + button = gtk_button_new (); + gtk_window_set_child (GTK_WINDOW (window), button); + + nuclear = gtk_nuclear_symbolic_new (); + image = gtk_image_new_from_paintable (nuclear); + + gtk_button_set_child (GTK_BUTTON (button), image); + g_signal_connect (button, "clicked", G_CALLBACK (nuclear_button_clicked), nuclear); + g_object_unref (nuclear); + } + + if (!gtk_widget_get_visible (window)) + gtk_widget_show (window); + else + gtk_window_destroy (GTK_WINDOW (window)); + + return window; +}