1/* Paintable/Animated Paintable
2 *
3 * GdkPaintable also allows paintables to change.
4 *
5 * This demo code gives an example of how this could work. It builds
6 * on the previous simple example.
7 *
8 * Paintables can also change their size, this works similarly, but
9 * we will not demonstrate this here as our icon does not have any size.
10 */
11
12#include <gtk/gtk.h>
13
14#include "paintable.h"
15
16static GtkWidget *window = NULL;
17
18/* First, add the boilerplate for the object itself.
19 * This part would normally go in the header.
20 */
21#define GTK_TYPE_NUCLEAR_ANIMATION (gtk_nuclear_animation_get_type ())
22G_DECLARE_FINAL_TYPE (GtkNuclearAnimation, gtk_nuclear_animation, GTK, NUCLEAR_ANIMATION, GObject)
23
24/* Do a full rotation in 5 seconds.
25 * We will register the timeout for doing a single step to
26 * be executed every 10ms, which means after 1000 steps
27 * 10s will have elapsed.
28 */
29#define MAX_PROGRESS 500
30
31/* Declare the struct. */
32struct _GtkNuclearAnimation
33{
34 GObject parent_instance;
35
36 gboolean draw_background;
37
38 /* This variable stores the progress of our animation.
39 * We just count upwards until we hit MAX_PROGRESS and
40 * then start from scratch.
41 */
42 int progress;
43
44 /* This variable holds the ID of the timer that updates
45 * our progress variable.
46 * We need to keep track of it so that we can remove it
47 * again.
48 */
49 guint source_id;
50};
51
52struct _GtkNuclearAnimationClass
53{
54 GObjectClass parent_class;
55};
56
57/* Again, we implement the functionality required by the GdkPaintable interface */
58static void
59gtk_nuclear_animation_snapshot (GdkPaintable *paintable,
60 GdkSnapshot *snapshot,
61 double width,
62 double height)
63{
64 GtkNuclearAnimation *nuclear = GTK_NUCLEAR_ANIMATION (ptr: paintable);
65
66 /* We call the function from the previous example here. */
67 gtk_nuclear_snapshot (snapshot,
68 foreground: &(GdkRGBA) { 0, 0, 0, 1 }, /* black */
69 background: nuclear->draw_background
70 ? &(GdkRGBA) { 0.9, 0.75, 0.15, 1.0 } /* yellow */
71 : &(GdkRGBA) { 0, 0, 0, 0 }, /* transparent */
72 width, height,
73 rotation: 2 * G_PI * nuclear->progress / MAX_PROGRESS);
74}
75
76static GdkPaintable *
77gtk_nuclear_animation_get_current_image (GdkPaintable *paintable)
78{
79 GtkNuclearAnimation *nuclear = GTK_NUCLEAR_ANIMATION (ptr: paintable);
80
81 /* For non-static paintables, this function needs to be implemented.
82 * It must return a static paintable with the same contents
83 * as this one currently has.
84 *
85 * Luckily we added the rotation property to the nuclear icon
86 * object previously, so we can just return an instance of that one.
87 */
88 return gtk_nuclear_icon_new (rotation: 2 * G_PI * nuclear->progress / MAX_PROGRESS);
89}
90
91static GdkPaintableFlags
92gtk_nuclear_animation_get_flags (GdkPaintable *paintable)
93{
94 /* This time, we cannot set the static contents flag because our animation
95 * changes the contents.
96 * However, our size still doesn't change, so report that flag.
97 */
98 return GDK_PAINTABLE_STATIC_SIZE;
99}
100
101static void
102gtk_nuclear_animation_paintable_init (GdkPaintableInterface *iface)
103{
104 iface->snapshot = gtk_nuclear_animation_snapshot;
105 iface->get_current_image = gtk_nuclear_animation_get_current_image;
106 iface->get_flags = gtk_nuclear_animation_get_flags;
107}
108
109/* When defining the GType, we need to implement the GdkPaintable interface */
110G_DEFINE_TYPE_WITH_CODE (GtkNuclearAnimation, gtk_nuclear_animation, G_TYPE_OBJECT,
111 G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
112 gtk_nuclear_animation_paintable_init))
113
114/* This time, we need to implement the finalize function,
115 */
116static void
117gtk_nuclear_animation_finalize (GObject *object)
118{
119 GtkNuclearAnimation *nuclear = GTK_NUCLEAR_ANIMATION (ptr: object);
120
121 /* Remove the timeout we registered when constructing
122 * the object.
123 */
124 g_source_remove (tag: nuclear->source_id);
125
126 /* Don't forget to chain up to the parent class' implementation
127 * of the finalize function.
128 */
129 G_OBJECT_CLASS (gtk_nuclear_animation_parent_class)->finalize (object);
130}
131
132/* In the class declaration, we need to add our finalize function.
133 */
134static void
135gtk_nuclear_animation_class_init (GtkNuclearAnimationClass *klass)
136{
137 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
138
139 gobject_class->finalize = gtk_nuclear_animation_finalize;
140}
141
142static gboolean
143gtk_nuclear_animation_step (gpointer data)
144{
145 GtkNuclearAnimation *nuclear = data;
146
147 /* Add 1 to the progress and reset it when we've reached
148 * the maximum value.
149 * The animation will rotate by 360 degrees at MAX_PROGRESS
150 * so it will be identical to the original unrotated one.
151 */
152 nuclear->progress = (nuclear->progress + 1) % MAX_PROGRESS;
153
154 /* Now we need to tell all listeners that we've changed out contents
155 * so that they can redraw this paintable.
156 */
157 gdk_paintable_invalidate_contents (paintable: GDK_PAINTABLE (ptr: nuclear));
158
159 /* We want this timeout function to be called repeatedly,
160 * so we return this value here.
161 * If this was a single-shot timeout, we could also
162 * return G_SOURCE_REMOVE here to get rid of it.
163 */
164 return G_SOURCE_CONTINUE;
165}
166
167static void
168gtk_nuclear_animation_init (GtkNuclearAnimation *nuclear)
169{
170 /* Add a timer here that constantly updates our animations.
171 * We want to update it often enough to guarantee a smooth animation.
172 *
173 * Ideally, we'd attach to the frame clock, but because we do
174 * not have it available here, we just use a regular timeout
175 * that hopefully triggers often enough to be smooth.
176 */
177 nuclear->source_id = g_timeout_add (interval: 10,
178 function: gtk_nuclear_animation_step,
179 data: nuclear);
180}
181
182/* And finally, we add the simple constructor we declared in the header. */
183GdkPaintable *
184gtk_nuclear_animation_new (gboolean draw_background)
185{
186 GtkNuclearAnimation *nuclear;
187
188 nuclear = g_object_new (GTK_TYPE_NUCLEAR_ANIMATION, NULL);
189
190 nuclear->draw_background = draw_background;
191
192 return GDK_PAINTABLE (ptr: nuclear);
193}
194
195GtkWidget *
196do_paintable_animated (GtkWidget *do_widget)
197{
198 GdkPaintable *nuclear;
199 GtkWidget *image;
200
201 if (!window)
202 {
203 window = gtk_window_new ();
204 gtk_window_set_display (GTK_WINDOW (window),
205 display: gtk_widget_get_display (widget: do_widget));
206 gtk_window_set_title (GTK_WINDOW (window), title: "Nuclear Animation");
207 gtk_window_set_default_size (GTK_WINDOW (window), width: 300, height: 200);
208 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window);
209
210 nuclear = gtk_nuclear_animation_new (TRUE);
211 image = gtk_image_new_from_paintable (paintable: nuclear);
212 gtk_window_set_child (GTK_WINDOW (window), child: image);
213 g_object_unref (object: nuclear);
214 }
215
216 if (!gtk_widget_get_visible (widget: window))
217 gtk_widget_show (widget: window);
218 else
219 gtk_window_destroy (GTK_WINDOW (window));
220
221 return window;
222}
223

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