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