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 | |
15 | static 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 ()) |
21 | G_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 */ |
24 | typedef enum |
25 | { |
26 | WARNING_NONE, |
27 | WARNING_ALERT, |
28 | WARNING_EMERGENCY |
29 | } WarningLevel; |
30 | |
31 | /* Declare the struct. */ |
32 | struct _GtkNuclearSymbolic |
33 | { |
34 | GObject parent_instance; |
35 | |
36 | WarningLevel warning_level; |
37 | }; |
38 | |
39 | struct _GtkNuclearSymbolicClass |
40 | { |
41 | GObjectClass parent_class; |
42 | }; |
43 | |
44 | /* Add a function to draw the nuclear icon in the given colors */ |
45 | static void |
46 | gtk_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 | |
85 | static void |
86 | gtk_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 */ |
92 | static void |
93 | gtk_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 | |
108 | static GdkPaintableFlags |
109 | gtk_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 | |
117 | static void |
118 | gtk_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 */ |
126 | G_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 | |
132 | static void |
133 | gtk_nuclear_symbolic_class_init (GtkNuclearSymbolicClass *klass) |
134 | { |
135 | } |
136 | |
137 | static void |
138 | gtk_nuclear_symbolic_init (GtkNuclearSymbolic *nuclear) |
139 | { |
140 | } |
141 | |
142 | /* And finally, we add the simple constructor we declared in the header. */ |
143 | GdkPaintable * |
144 | gtk_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 */ |
150 | static void |
151 | nuclear_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 | |
176 | GtkWidget * |
177 | do_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 | |