1#include <gtk/gtk.h>
2
3typedef struct
4{
5 GtkWidget parent_instance;
6 GtkWidget *child;
7 float scale;
8 float angle;
9} GtkZoom;
10
11typedef struct
12{
13 GtkWidgetClass parent_class;
14} GtkZoomClass;
15
16enum {
17 PROP_0,
18 PROP_CHILD,
19 PROP_SCALE,
20 PROP_ANGLE,
21 NUM_PROPERTIES
22};
23
24static GParamSpec *props[NUM_PROPERTIES] = { NULL, };
25
26GType gtk_zoom_get_type (void);
27
28G_DEFINE_TYPE (GtkZoom, gtk_zoom, GTK_TYPE_WIDGET)
29
30static void
31gtk_zoom_init (GtkZoom *zoom)
32{
33 zoom->child = NULL;
34 zoom->scale = 1.0;
35 zoom->angle = 0.0;
36}
37
38static void
39gtk_zoom_dispose (GObject *object)
40{
41 GtkZoom *zoom = (GtkZoom *)object;
42
43 g_clear_pointer (&zoom->child, gtk_widget_unparent);
44
45 G_OBJECT_CLASS (gtk_zoom_parent_class)->dispose (object);
46}
47
48static void
49update_transform (GtkZoom *zoom)
50{
51 GtkLayoutManager *manager;
52 GtkLayoutChild *child;
53 GskTransform *transform;
54 int w, h;
55 int x, y;
56
57 manager = gtk_widget_get_layout_manager (GTK_WIDGET (zoom));
58 child = gtk_layout_manager_get_layout_child (manager, child: zoom->child);
59
60 w = gtk_widget_get_width (GTK_WIDGET (zoom));
61 h = gtk_widget_get_height (GTK_WIDGET (zoom));
62
63 x = gtk_widget_get_allocated_width (GTK_WIDGET (zoom->child));
64 y = gtk_widget_get_allocated_height (GTK_WIDGET (zoom->child));
65
66 transform = NULL;
67 transform = gsk_transform_translate (next: transform, point: &GRAPHENE_POINT_INIT (w/2, h/2));
68 transform = gsk_transform_scale (next: transform, factor_x: zoom->scale, factor_y: zoom->scale);
69 transform = gsk_transform_rotate (next: transform, angle: zoom->angle);
70 transform = gsk_transform_translate (next: transform, point: &GRAPHENE_POINT_INIT (-x/2, -y/2));
71 gtk_fixed_layout_child_set_transform (child: GTK_FIXED_LAYOUT_CHILD (ptr: child), transform);
72 gsk_transform_unref (self: transform);
73}
74
75static void
76gtk_zoom_set_scale (GtkZoom *zoom,
77 float scale)
78{
79
80 if (zoom->scale == scale)
81 return;
82
83 zoom->scale = scale;
84
85 update_transform (zoom);
86
87 g_object_notify_by_pspec (G_OBJECT (zoom), pspec: props[PROP_SCALE]);
88
89 gtk_widget_queue_resize (GTK_WIDGET (zoom));
90}
91
92static void
93gtk_zoom_set_angle (GtkZoom *zoom,
94 float angle)
95{
96
97 if (zoom->angle == angle)
98 return;
99
100 zoom->angle = angle;
101
102 update_transform (zoom);
103
104 g_object_notify_by_pspec (G_OBJECT (zoom), pspec: props[PROP_ANGLE]);
105
106 gtk_widget_queue_resize (GTK_WIDGET (zoom));
107}
108
109static void
110gtk_zoom_set_child (GtkZoom *zoom,
111 GtkWidget *child)
112{
113 g_clear_pointer (&zoom->child, gtk_widget_unparent);
114
115 zoom->child = child;
116
117 if (zoom->child)
118 gtk_widget_set_parent (widget: zoom->child, GTK_WIDGET (zoom));
119
120 update_transform (zoom);
121
122 g_object_notify_by_pspec (G_OBJECT (zoom), pspec: props[PROP_CHILD]);
123}
124
125static void
126gtk_zoom_set_property (GObject *object,
127 guint prop_id,
128 const GValue *value,
129 GParamSpec *pspec)
130{
131 GtkZoom *zoom = (GtkZoom *)object;
132
133 switch (prop_id)
134 {
135 case PROP_SCALE:
136 gtk_zoom_set_scale (zoom, scale: g_value_get_float (value));
137 break;
138
139 case PROP_ANGLE:
140 gtk_zoom_set_angle (zoom, angle: g_value_get_float (value));
141 break;
142
143 case PROP_CHILD:
144 gtk_zoom_set_child (zoom, child: g_value_get_object (value));
145 break;
146
147 default:
148 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
149 break;
150 }
151}
152
153static void
154gtk_zoom_get_property (GObject *object,
155 guint prop_id,
156 GValue *value,
157 GParamSpec *pspec)
158{
159 GtkZoom *zoom = (GtkZoom *)object;
160
161 switch (prop_id)
162 {
163 case PROP_SCALE:
164 g_value_set_float (value, v_float: zoom->scale);
165 break;
166
167 case PROP_ANGLE:
168 g_value_set_float (value, v_float: zoom->angle);
169 break;
170
171 case PROP_CHILD:
172 g_value_set_object (value, v_object: zoom->child);
173 break;
174
175 default:
176 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177 break;
178 }
179}
180
181static void
182gtk_zoom_class_init (GtkZoomClass *class)
183{
184 GObjectClass *object_class = G_OBJECT_CLASS (class);
185 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
186
187 object_class->dispose = gtk_zoom_dispose;
188 object_class->set_property = gtk_zoom_set_property;
189 object_class->get_property = gtk_zoom_get_property;
190
191 props[PROP_SCALE] = g_param_spec_float (name: "scale", nick: "", blurb: "",
192 minimum: 0.0, maximum: 100.0, default_value: 1.0,
193 flags: G_PARAM_READWRITE);
194
195 props[PROP_ANGLE] = g_param_spec_float (name: "angle", nick: "", blurb: "",
196 minimum: 0.0, maximum: 360.0, default_value: 1.0,
197 flags: G_PARAM_READWRITE);
198
199 props[PROP_CHILD] = g_param_spec_object (name: "child", nick: "", blurb: "",
200 GTK_TYPE_WIDGET,
201 flags: G_PARAM_READWRITE);
202
203 g_object_class_install_properties (oclass: object_class, n_pspecs: NUM_PROPERTIES, pspecs: props);
204
205 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_FIXED_LAYOUT);
206}
207
208static GtkWidget *
209gtk_zoom_new (void)
210{
211 return g_object_new (object_type: gtk_zoom_get_type (), NULL);
212}
213
214static gboolean
215update_transform_once (GtkWidget *widget,
216 GdkFrameClock *frame_clock,
217 gpointer data)
218{
219 static int count = 0;
220
221 update_transform (zoom: (GtkZoom *)widget);
222 count++;
223
224 if (count == 2)
225 return G_SOURCE_REMOVE;
226
227 return G_SOURCE_CONTINUE;
228}
229
230int
231main (int argc, char *argv[])
232{
233 GtkWindow *window;
234 GtkWidget *zoom;
235 GtkWidget *box;
236 GtkWidget *grid;
237 GtkWidget *scale;
238 GtkWidget *angle;
239 GtkWidget *child;
240 GtkAdjustment *adjustment;
241
242 gtk_init ();
243
244 window = GTK_WINDOW (gtk_window_new ());
245 gtk_window_set_default_size (GTK_WINDOW (window), width: 600, height: 400);
246 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
247 gtk_window_set_child (window, child: box);
248
249 grid = gtk_grid_new ();
250 gtk_grid_set_column_spacing (GTK_GRID (grid), spacing: 10);
251
252 scale = gtk_scale_new_with_range (orientation: GTK_ORIENTATION_HORIZONTAL, min: 1.0, max: 10.0, step: 1.0);
253 gtk_widget_set_hexpand (widget: scale, TRUE);
254 gtk_grid_attach (GTK_GRID (grid), child: gtk_label_new (str: "Scale"), column: 0, row: 0, width: 1, height: 1);
255 gtk_grid_attach (GTK_GRID (grid), child: scale, column: 1, row: 0, width: 1, height: 1);
256
257 angle = gtk_scale_new_with_range (orientation: GTK_ORIENTATION_HORIZONTAL, min: 0.0, max: 360.0, step: 1.0);
258 gtk_scale_add_mark (GTK_SCALE (angle), value: 90.0, position: GTK_POS_BOTTOM, NULL);
259 gtk_scale_add_mark (GTK_SCALE (angle), value: 180.0, position: GTK_POS_BOTTOM, NULL);
260 gtk_scale_add_mark (GTK_SCALE (angle), value: 270.0, position: GTK_POS_BOTTOM, NULL);
261 gtk_widget_set_hexpand (widget: angle, TRUE);
262 gtk_grid_attach (GTK_GRID (grid), child: gtk_label_new (str: "Angle"), column: 0, row: 1, width: 1, height: 1);
263 gtk_grid_attach (GTK_GRID (grid), child: angle, column: 1, row: 1, width: 1, height: 1);
264 gtk_box_append (GTK_BOX (box), child: grid);
265
266 zoom = gtk_zoom_new ();
267 gtk_widget_set_hexpand (widget: zoom, TRUE);
268 gtk_widget_set_vexpand (widget: zoom, TRUE);
269 gtk_box_append (GTK_BOX (box), child: zoom);
270
271 adjustment = gtk_range_get_adjustment (GTK_RANGE (scale));
272 g_object_bind_property (source: adjustment, source_property: "value",
273 target: zoom, target_property: "scale",
274 flags: G_BINDING_DEFAULT);
275
276 adjustment = gtk_range_get_adjustment (GTK_RANGE (angle));
277 g_object_bind_property (source: adjustment, source_property: "value",
278 target: zoom, target_property: "angle",
279 flags: G_BINDING_DEFAULT);
280
281 if (argc > 1)
282 {
283 GtkBuilder *builder = gtk_builder_new ();
284 gtk_builder_add_from_file (builder, filename: argv[1], NULL);
285 child = GTK_WIDGET (gtk_builder_get_object (builder, "child"));
286 gtk_zoom_set_child (zoom: (GtkZoom *)zoom, child);
287 g_object_unref (object: builder);
288 }
289 else
290 gtk_zoom_set_child (zoom: (GtkZoom *)zoom, child: gtk_button_new_with_label (label: "Click me!"));
291
292 gtk_window_present (window);
293
294 /* HACK to get the transform initally updated */
295 gtk_widget_add_tick_callback (widget: zoom, callback: update_transform_once, NULL, NULL);
296
297 while (g_list_model_get_n_items (list: gtk_window_get_toplevels ()) > 0)
298 g_main_context_iteration (NULL, TRUE);
299
300 return 0;
301}
302

source code of gtk/tests/testzoom.c