1#include "demo2widget.h"
2#include "demo2layout.h"
3
4struct _Demo2Widget
5{
6 GtkWidget parent_instance;
7
8 gint64 start_time;
9 gint64 end_time;
10 float start_position;
11 float end_position;
12 float start_offset;
13 float end_offset;
14 gboolean animating;
15};
16
17struct _Demo2WidgetClass
18{
19 GtkWidgetClass parent_class;
20};
21
22G_DEFINE_TYPE (Demo2Widget, demo2_widget, GTK_TYPE_WIDGET)
23
24static void
25demo2_widget_init (Demo2Widget *self)
26{
27 gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
28}
29
30static void
31demo2_widget_dispose (GObject *object)
32{
33 GtkWidget *child;
34
35 while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
36 gtk_widget_unparent (widget: child);
37
38 G_OBJECT_CLASS (demo2_widget_parent_class)->dispose (object);
39}
40
41/* From clutter-easing.c, based on Robert Penner's
42 * infamous easing equations, MIT license.
43 */
44static double
45ease_out_cubic (double t)
46{
47 double p = t - 1;
48
49 return p * p * p + 1;
50}
51
52static gboolean
53update_position (GtkWidget *widget,
54 GdkFrameClock *clock,
55 gpointer data)
56{
57 Demo2Widget *self = DEMO2_WIDGET (ptr: widget);
58 Demo2Layout *layout = DEMO2_LAYOUT (ptr: gtk_widget_get_layout_manager (widget));
59 gint64 now;
60 double t;
61
62 now = gdk_frame_clock_get_frame_time (frame_clock: clock);
63
64 if (now >= self->end_time)
65 {
66 self->animating = FALSE;
67
68 return G_SOURCE_REMOVE;
69 }
70
71 t = (now - self->start_time) / (double) (self->end_time - self->start_time);
72
73 t = ease_out_cubic (t);
74
75 demo2_layout_set_position (layout, position: self->start_position + t * (self->end_position - self->start_position));
76 demo2_layout_set_offset (layout, offset: self->start_offset + t * (self->end_offset - self->start_offset));
77 gtk_widget_queue_allocate (widget);
78
79 return G_SOURCE_CONTINUE;
80}
81
82static void
83rotate_sphere (GtkWidget *widget,
84 const char *action,
85 GVariant *parameters)
86{
87 Demo2Widget *self = DEMO2_WIDGET (ptr: widget);
88 Demo2Layout *layout = DEMO2_LAYOUT (ptr: gtk_widget_get_layout_manager (widget));
89 GtkOrientation orientation;
90 int direction;
91
92 g_variant_get (value: parameters, format_string: "(ii)", &orientation, &direction);
93
94 self->end_position = self->start_position = demo2_layout_get_position (layout);
95 self->end_offset = self->start_offset = demo2_layout_get_offset (layout);
96 if (orientation == GTK_ORIENTATION_HORIZONTAL)
97 self->end_position += 10 * direction;
98 else
99 self->end_offset += 10 * direction;
100 self->start_time = g_get_monotonic_time ();
101 self->end_time = self->start_time + 0.5 * G_TIME_SPAN_SECOND;
102
103 if (!self->animating)
104 {
105 gtk_widget_add_tick_callback (widget, callback: update_position, NULL, NULL);
106 self->animating = TRUE;
107 }
108}
109
110static void
111demo2_widget_snapshot (GtkWidget *widget,
112 GtkSnapshot *snapshot)
113{
114 GtkWidget *child;
115
116 for (child = gtk_widget_get_first_child (widget);
117 child != NULL;
118 child = gtk_widget_get_next_sibling (widget: child))
119 {
120 /* our layout manager sets this for children that are out of view */
121 if (!gtk_widget_get_child_visible (widget: child))
122 continue;
123
124 gtk_widget_snapshot_child (widget, child, snapshot);
125 }
126}
127
128static void
129demo2_widget_class_init (Demo2WidgetClass *class)
130{
131 GObjectClass *object_class = G_OBJECT_CLASS (class);
132 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
133
134 object_class->dispose = demo2_widget_dispose;
135
136 widget_class->snapshot = demo2_widget_snapshot;
137
138 gtk_widget_class_install_action (widget_class, action_name: "rotate", parameter_type: "(ii)", activate: rotate_sphere);
139
140 gtk_widget_class_add_binding_action (widget_class,
141 GDK_KEY_Left, mods: 0,
142 action_name: "rotate",
143 format_string: "(ii)", GTK_ORIENTATION_HORIZONTAL, -1);
144 gtk_widget_class_add_binding_action (widget_class,
145 GDK_KEY_Right, mods: 0,
146 action_name: "rotate",
147 format_string: "(ii)", GTK_ORIENTATION_HORIZONTAL, 1);
148 gtk_widget_class_add_binding_action (widget_class,
149 GDK_KEY_Up, mods: 0,
150 action_name: "rotate",
151 format_string: "(ii)", GTK_ORIENTATION_VERTICAL, 1);
152 gtk_widget_class_add_binding_action (widget_class,
153 GDK_KEY_Down, mods: 0,
154 action_name: "rotate",
155 format_string: "(ii)", GTK_ORIENTATION_VERTICAL, -1);
156
157 /* here is where we use our custom layout manager */
158 gtk_widget_class_set_layout_manager_type (widget_class, DEMO2_TYPE_LAYOUT);
159}
160
161GtkWidget *
162demo2_widget_new (void)
163{
164 return g_object_new (DEMO2_TYPE_WIDGET, NULL);
165}
166
167void
168demo2_widget_add_child (Demo2Widget *self,
169 GtkWidget *child)
170{
171 gtk_widget_set_parent (widget: child, GTK_WIDGET (self));
172}
173

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