[gtk/wip/otte/symbolic-paintable: 5/5] gtk-demo: Add a symbolic paintable demo




commit 7cdbdb663c35b72f8934698e8823d96ebef78ce5
Author: Benjamin Otte <otte redhat com>
Date:   Sat Aug 28 03:46:55 2021 +0200

    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(+)
---
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 @@
     <file>paintable_emblem.c</file>
     <file>paintable_mediastream.c</file>
     <file>paintable_svg.c</file>
+    <file>paintable_symbolic.c</file>
     <file>panes.c</file>
     <file>password_entry.c</file>
     <file>peg_solitaire.c</file>
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 <gtk/gtk.h>
+
+#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;
+}


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