1#include <math.h>
2#include "demo3widget.h"
3
4enum
5{
6 PROP_PAINTABLE = 1,
7 PROP_SCALE
8};
9
10struct _Demo3Widget
11{
12 GtkWidget parent_instance;
13
14 GdkPaintable *paintable;
15 float scale;
16
17 GtkWidget *menu;
18};
19
20struct _Demo3WidgetClass
21{
22 GtkWidgetClass parent_class;
23};
24
25G_DEFINE_TYPE (Demo3Widget, demo3_widget, GTK_TYPE_WIDGET)
26
27static void
28demo3_widget_init (Demo3Widget *self)
29{
30 self->scale = 1.f;
31 gtk_widget_init_template (GTK_WIDGET (self));
32}
33
34static void
35demo3_widget_dispose (GObject *object)
36{
37 Demo3Widget *self = DEMO3_WIDGET (ptr: object);
38
39 g_clear_object (&self->paintable);
40 g_clear_pointer (&self->menu, gtk_widget_unparent);
41
42 G_OBJECT_CLASS (demo3_widget_parent_class)->dispose (object);
43}
44
45static void
46demo3_widget_snapshot (GtkWidget *widget,
47 GtkSnapshot *snapshot)
48{
49 Demo3Widget *self = DEMO3_WIDGET (ptr: widget);
50 int x, y, width, height;
51 double w, h;
52
53 width = gtk_widget_get_width (widget);
54 height = gtk_widget_get_height (widget);
55
56 w = self->scale * gdk_paintable_get_intrinsic_width (paintable: self->paintable);
57 h = self->scale * gdk_paintable_get_intrinsic_height (paintable: self->paintable);
58
59 x = MAX (0, (width - ceil (w)) / 2);
60 y = MAX (0, (height - ceil (h)) / 2);
61
62 gtk_snapshot_push_clip (snapshot, bounds: &GRAPHENE_RECT_INIT (0, 0, width, height));
63 gtk_snapshot_save (snapshot);
64 gtk_snapshot_translate (snapshot, point: &GRAPHENE_POINT_INIT (x, y));
65 gdk_paintable_snapshot (paintable: self->paintable, snapshot, width: w, height: h);
66 gtk_snapshot_restore (snapshot);
67 gtk_snapshot_pop (snapshot);
68}
69
70static void
71demo3_widget_measure (GtkWidget *widget,
72 GtkOrientation orientation,
73 int for_size,
74 int *minimum,
75 int *natural,
76 int *minimum_baseline,
77 int *natural_baseline)
78{
79 Demo3Widget *self = DEMO3_WIDGET (ptr: widget);
80 int size;
81
82 if (orientation == GTK_ORIENTATION_HORIZONTAL)
83 size = gdk_paintable_get_intrinsic_width (paintable: self->paintable);
84 else
85 size = gdk_paintable_get_intrinsic_height (paintable: self->paintable);
86
87 *minimum = *natural = self->scale * size;
88}
89
90static void
91demo3_widget_size_allocate (GtkWidget *widget,
92 int width,
93 int height,
94 int baseline)
95{
96 Demo3Widget *self = DEMO3_WIDGET (ptr: widget);
97
98 /* Since we are not using a layout manager (who would do this
99 * for us), we need to allocate a size for our menu by calling
100 * gtk_popover_present().
101 */
102 gtk_popover_present (GTK_POPOVER (self->menu));
103}
104
105static void
106demo3_widget_set_property (GObject *object,
107 guint prop_id,
108 const GValue *value,
109 GParamSpec *pspec)
110{
111 Demo3Widget *self = DEMO3_WIDGET (ptr: object);
112
113 switch (prop_id)
114 {
115 case PROP_PAINTABLE:
116 g_clear_object (&self->paintable);
117 self->paintable = g_value_dup_object (value);
118 gtk_widget_queue_resize (GTK_WIDGET (object));
119 break;
120
121 case PROP_SCALE:
122 self->scale = g_value_get_float (value);
123 gtk_widget_queue_resize (GTK_WIDGET (object));
124 break;
125
126 default:
127 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
128 break;
129 }
130}
131
132static void
133demo3_widget_get_property (GObject *object,
134 guint prop_id,
135 GValue *value,
136 GParamSpec *pspec)
137{
138 Demo3Widget *self = DEMO3_WIDGET (ptr: object);
139
140 switch (prop_id)
141 {
142 case PROP_PAINTABLE:
143 g_value_set_object (value, v_object: self->paintable);
144 break;
145
146 case PROP_SCALE:
147 g_value_set_float (value, v_float: self->scale);
148 break;
149
150 default:
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 break;
153 }
154}
155
156static void
157pressed_cb (GtkGestureClick *gesture,
158 guint n_press,
159 double x,
160 double y,
161 Demo3Widget *self)
162{
163 /* We are placing our menu at the point where
164 * the click happened, before popping it up.
165 */
166 gtk_popover_set_pointing_to (GTK_POPOVER (self->menu),
167 rect: &(const GdkRectangle){ x, y, 1, 1 });
168 gtk_popover_popup (GTK_POPOVER (self->menu));
169}
170
171static void
172zoom_cb (GtkWidget *widget,
173 const char *action_name,
174 GVariant *parameter)
175{
176 Demo3Widget *self = DEMO3_WIDGET (ptr: widget);
177 float scale;
178
179 if (g_str_equal (v1: action_name, v2: "zoom.in"))
180 scale = MIN (10, self->scale * M_SQRT2);
181 else if (g_str_equal (v1: action_name, v2: "zoom.out"))
182 scale = MAX (0.01, self->scale / M_SQRT2);
183 else
184 scale = 1.0;
185
186 gtk_widget_action_set_enabled (widget, action_name: "zoom.in", enabled: scale < 10);
187 gtk_widget_action_set_enabled (widget, action_name: "zoom.out", enabled: scale > 0.01);
188 gtk_widget_action_set_enabled (widget, action_name: "zoom.reset", enabled: scale != 1);
189
190 g_object_set (object: widget, first_property_name: "scale", scale, NULL);
191}
192
193static void
194demo3_widget_class_init (Demo3WidgetClass *class)
195{
196 GObjectClass *object_class = G_OBJECT_CLASS (class);
197 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
198
199 object_class->dispose = demo3_widget_dispose;
200 object_class->set_property = demo3_widget_set_property;
201 object_class->get_property = demo3_widget_get_property;
202
203 widget_class->snapshot = demo3_widget_snapshot;
204 widget_class->measure = demo3_widget_measure;
205 widget_class->size_allocate = demo3_widget_size_allocate;
206
207 g_object_class_install_property (oclass: object_class, property_id: PROP_PAINTABLE,
208 pspec: g_param_spec_object (name: "paintable", nick: "Paintable", blurb: "Paintable",
209 GDK_TYPE_PAINTABLE,
210 flags: G_PARAM_READWRITE));
211
212 g_object_class_install_property (oclass: object_class, property_id: PROP_SCALE,
213 pspec: g_param_spec_float (name: "scale", nick: "Scale", blurb: "Scale",
214 minimum: 0.0, maximum: 10.0, default_value: 1.0,
215 flags: G_PARAM_READWRITE));
216
217 /* These are the actions that we are using in the menu */
218 gtk_widget_class_install_action (widget_class, action_name: "zoom.in", NULL, activate: zoom_cb);
219 gtk_widget_class_install_action (widget_class, action_name: "zoom.out", NULL, activate: zoom_cb);
220 gtk_widget_class_install_action (widget_class, action_name: "zoom.reset", NULL, activate: zoom_cb);
221
222 gtk_widget_class_set_template_from_resource (widget_class, resource_name: "/menu/demo3widget.ui");
223 gtk_widget_class_bind_template_child (widget_class, Demo3Widget, menu);
224 gtk_widget_class_bind_template_callback (widget_class, pressed_cb);
225}
226
227GtkWidget *
228demo3_widget_new (const char *resource)
229{
230 Demo3Widget *self;
231 GdkPixbuf *pixbuf;
232 GdkPaintable *paintable;
233
234 pixbuf = gdk_pixbuf_new_from_resource (resource_path: resource, NULL);
235 paintable = GDK_PAINTABLE (ptr: gdk_texture_new_for_pixbuf (pixbuf));
236
237 self = g_object_new (DEMO3_TYPE_WIDGET, first_property_name: "paintable", paintable, NULL);
238
239 g_object_unref (object: pixbuf);
240 g_object_unref (object: paintable);
241
242 return GTK_WIDGET (self);
243}
244

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