1/* Paintable/Symbolic Paintable
2 *
3 * GdkPaintables can be made to follow the theme's colors. GTK calls
4 * icons that do this symbolic icons, paintables that want to have
5 * the same effect can implement the GtkSymbolicPaintable interface.
6 *
7 * We will adapt the original paintable example by adding the ability
8 * to recolor the paintable based on the symbolic colors.
9 */
10
11#include <gtk/gtk.h>
12
13#include "paintable.h"
14
15static GtkWidget *window = NULL;
16
17/* First, add the boilerplate for the object itself.
18 * This part would normally go in the header.
19 */
20#define GTK_TYPE_NUCLEAR_SYMBOLIC (gtk_nuclear_symbolic_get_type ())
21G_DECLARE_FINAL_TYPE (GtkNuclearSymbolic, gtk_nuclear_symbolic, GTK, NUCLEAR_SYMBOLIC, GObject)
22
23/* Declare a few warning levels, so we can pick colors based on them */
24typedef enum
25{
26 WARNING_NONE,
27 WARNING_ALERT,
28 WARNING_EMERGENCY
29} WarningLevel;
30
31/* Declare the struct. */
32struct _GtkNuclearSymbolic
33{
34 GObject parent_instance;
35
36 WarningLevel warning_level;
37};
38
39struct _GtkNuclearSymbolicClass
40{
41 GObjectClass parent_class;
42};
43
44/* Add a function to draw the nuclear icon in the given colors */
45static void
46gtk_nuclear_symbolic_snapshot_symbolic (GtkSymbolicPaintable *paintable,
47 GdkSnapshot *snapshot,
48 double width,
49 double height,
50 const GdkRGBA *colors,
51 gsize n_colors)
52{
53 GtkNuclearSymbolic *self = GTK_NUCLEAR_SYMBOLIC (ptr: paintable);
54 static const GdkRGBA transparent = { 0, };
55 const GdkRGBA *bg_color;
56
57 /* select the right background color from the warning level */
58 switch (self->warning_level)
59 {
60 case WARNING_NONE:
61 bg_color = &transparent;
62 break;
63 case WARNING_ALERT:
64 bg_color = &colors[GTK_SYMBOLIC_COLOR_WARNING];
65 break;
66 case WARNING_EMERGENCY:
67 bg_color = &colors[GTK_SYMBOLIC_COLOR_ERROR];
68 break;
69 default:
70 /* This should never happen, but we better do defensive coding
71 * with this critical icon */
72 g_assert_not_reached ();
73 bg_color = &transparent;
74 break;
75 }
76
77 /* Draw the icon with the selected warning color */
78 gtk_nuclear_snapshot (snapshot,
79 foreground: &colors[GTK_SYMBOLIC_COLOR_FOREGROUND],
80 background: bg_color,
81 width, height,
82 rotation: 0);
83}
84
85static void
86gtk_nuclear_symbolic_symbolic_paintable_init (GtkSymbolicPaintableInterface *iface)
87{
88 iface->snapshot_symbolic = gtk_nuclear_symbolic_snapshot_symbolic;
89}
90
91/* We need to implement the functionality required by the GdkPaintable interface */
92static void
93gtk_nuclear_symbolic_snapshot (GdkPaintable *paintable,
94 GdkSnapshot *snapshot,
95 double width,
96 double height)
97{
98 /* Calling this function without passing a color is a neat trick
99 * to make GTK use default colors and otherwise forward the call
100 * to the snapshotting function above.
101 */
102 gtk_symbolic_paintable_snapshot_symbolic (paintable: GTK_SYMBOLIC_PAINTABLE (ptr: paintable),
103 snapshot,
104 width, height,
105 NULL, n_colors: 0);
106}
107
108static GdkPaintableFlags
109gtk_nuclear_symbolic_get_flags (GdkPaintable *paintable)
110{
111 /* This image has a static size, but the contents may change:
112 * We draw different things when the warning level changes.
113 */
114 return GDK_PAINTABLE_STATIC_SIZE;
115}
116
117static void
118gtk_nuclear_symbolic_paintable_init (GdkPaintableInterface *iface)
119{
120 iface->snapshot = gtk_nuclear_symbolic_snapshot;
121 iface->get_flags = gtk_nuclear_symbolic_get_flags;
122}
123
124/* When defining the GType, we need to implement bot the GdkPaintable
125 * and the GtkSymbolicPaintable interface */
126G_DEFINE_TYPE_WITH_CODE (GtkNuclearSymbolic, gtk_nuclear_symbolic, G_TYPE_OBJECT,
127 G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
128 gtk_nuclear_symbolic_paintable_init)
129 G_IMPLEMENT_INTERFACE (GTK_TYPE_SYMBOLIC_PAINTABLE,
130 gtk_nuclear_symbolic_symbolic_paintable_init))
131
132static void
133gtk_nuclear_symbolic_class_init (GtkNuclearSymbolicClass *klass)
134{
135}
136
137static void
138gtk_nuclear_symbolic_init (GtkNuclearSymbolic *nuclear)
139{
140}
141
142/* And finally, we add the simple constructor we declared in the header. */
143GdkPaintable *
144gtk_nuclear_symbolic_new (void)
145{
146 return g_object_new (GTK_TYPE_NUCLEAR_SYMBOLIC, NULL);
147}
148
149/* Add some fun feature to the button */
150static void
151nuclear_button_clicked (GtkButton *button,
152 GtkNuclearSymbolic *nuclear)
153{
154 if (nuclear->warning_level >= WARNING_EMERGENCY)
155 {
156 /* On maximum warning level, reset the warning */
157 nuclear->warning_level = WARNING_NONE;
158 /* And sometimes (but not always to confuse people)
159 * close the window.
160 */
161 if (g_random_boolean ())
162 gtk_window_close (GTK_WINDOW (window));
163 }
164 else
165 {
166 /* Otherwise just increase the warning level */
167 nuclear->warning_level++;
168 }
169
170 /* Don't forget to emit the signal causing the paintable to redraw.
171 * Changing the warning level changes the background color after all.
172 */
173 gdk_paintable_invalidate_contents (paintable: GDK_PAINTABLE (ptr: nuclear));
174}
175
176GtkWidget *
177do_paintable_symbolic (GtkWidget *do_widget)
178{
179 GdkPaintable *nuclear;
180 GtkWidget *image, *button;
181
182 if (!window)
183 {
184 window = gtk_window_new ();
185 gtk_window_set_display (GTK_WINDOW (window),
186 display: gtk_widget_get_display (widget: do_widget));
187 gtk_window_set_title (GTK_WINDOW (window), title: "Don't click!");
188 gtk_window_set_default_size (GTK_WINDOW (window), width: 200, height: 200);
189 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window);
190
191 button = gtk_button_new ();
192 gtk_window_set_child (GTK_WINDOW (window), child: button);
193
194 nuclear = gtk_nuclear_symbolic_new ();
195 image = gtk_image_new_from_paintable (paintable: nuclear);
196
197 gtk_button_set_child (GTK_BUTTON (button), child: image);
198 g_signal_connect (button, "clicked", G_CALLBACK (nuclear_button_clicked), nuclear);
199 g_object_unref (object: nuclear);
200 }
201
202 if (!gtk_widget_get_visible (widget: window))
203 gtk_widget_show (widget: window);
204 else
205 gtk_window_destroy (GTK_WINDOW (window));
206
207 return window;
208}
209

source code of gtk/demos/gtk-demo/paintable_symbolic.c