1 | |
2 | |
3 | #include <gtk/gtk.h> |
4 | |
5 | static const char *css = |
6 | "test>button {" |
7 | " all: unset; " |
8 | " background-color: white;" |
9 | " border: 30px solid teal;" |
10 | " margin: 40px;" |
11 | " padding: 40px;" |
12 | "}" |
13 | "test>button:hover {" |
14 | " background-color: blue;" |
15 | "}" |
16 | "test image {" |
17 | " background-color: purple;" |
18 | "}" |
19 | ; |
20 | |
21 | /* Just so we can avoid a signal */ |
22 | GtkWidget *transform_tester; |
23 | GtkWidget *test_widget; |
24 | GtkWidget *test_child; |
25 | float scale = 1; |
26 | gboolean do_picking = TRUE; |
27 | |
28 | static const GdkRGBA RED = {1, 0, 0, 0.4}; |
29 | static const GdkRGBA GREEN = {0, 1, 0, 0.7}; |
30 | static const GdkRGBA BLUE = {0, 0, 1, 0.4}; |
31 | static const GdkRGBA BLACK = {0, 0, 0, 1 }; |
32 | |
33 | |
34 | |
35 | /* ######################################################################### */ |
36 | /* ############################## MatrixChooser ############################ */ |
37 | /* ######################################################################### */ |
38 | |
39 | |
40 | #define GTK_TYPE_MATRIX_CHOOSER (gtk_matrix_chooser_get_type ()) |
41 | G_DECLARE_FINAL_TYPE (GtkMatrixChooser, gtk_matrix_chooser, GTK, MATRIX_CHOOSER, GtkWidget) |
42 | |
43 | struct _GtkMatrixChooser |
44 | { |
45 | GtkWidget parent_instance; |
46 | }; |
47 | |
48 | G_DEFINE_TYPE (GtkMatrixChooser, gtk_matrix_chooser, GTK_TYPE_WIDGET) |
49 | |
50 | static void |
51 | gtk_matrix_chooser_init (GtkMatrixChooser *self) |
52 | { |
53 | } |
54 | |
55 | static void |
56 | gtk_matrix_chooser_class_init (GtkMatrixChooserClass *klass) |
57 | { |
58 | |
59 | } |
60 | |
61 | |
62 | /* ######################################################################### */ |
63 | /* ############################# TransformTester ########################### */ |
64 | /* ######################################################################### */ |
65 | |
66 | #define TEST_WIDGET_MIN_SIZE 100 |
67 | |
68 | #define GTK_TYPE_TRANSFORM_TESTER (gtk_transform_tester_get_type ()) |
69 | G_DECLARE_FINAL_TYPE (GtkTransformTester, gtk_transform_tester, GTK, TRANSFORM_TESTER, GtkWidget); |
70 | |
71 | struct _GtkTransformTester |
72 | { |
73 | GtkWidget parent_instance; |
74 | |
75 | GtkWidget *test_widget; |
76 | int pick_increase; |
77 | }; |
78 | |
79 | G_DEFINE_TYPE (GtkTransformTester, gtk_transform_tester, GTK_TYPE_WIDGET); |
80 | |
81 | static void |
82 | gtk_transform_tester_measure (GtkWidget *widget, |
83 | GtkOrientation orientation, |
84 | int for_size, |
85 | int *minimum, |
86 | int *natural, |
87 | int *minimum_baseline, |
88 | int *natural_baseline) |
89 | { |
90 | GtkTransformTester *self = (GtkTransformTester *)widget; |
91 | |
92 | if (self->test_widget) |
93 | { |
94 | gtk_widget_measure (widget: self->test_widget, orientation, for_size, |
95 | minimum, natural, NULL, NULL); |
96 | } |
97 | } |
98 | |
99 | static void |
100 | gtk_transform_tester_size_allocate (GtkWidget *widget, |
101 | int width, |
102 | int height, |
103 | int baseline) |
104 | { |
105 | GtkTransformTester *self = (GtkTransformTester *)widget; |
106 | GskTransform *global_transform; |
107 | int w, h; |
108 | |
109 | if (!self->test_widget) |
110 | return; |
111 | |
112 | scale += 2.5f; |
113 | |
114 | gtk_widget_measure (widget: self->test_widget, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: -1, |
115 | minimum: &w, NULL, NULL, NULL); |
116 | gtk_widget_measure (widget: self->test_widget, orientation: GTK_ORIENTATION_VERTICAL, for_size: w, |
117 | minimum: &h, NULL, NULL, NULL); |
118 | |
119 | g_message ("%s: %d, %d" , __FUNCTION__, w, h); |
120 | |
121 | global_transform = NULL; |
122 | |
123 | global_transform = gsk_transform_translate (next: global_transform, point: &GRAPHENE_POINT_INIT (width / 2.0f, height / 2.0f)); |
124 | global_transform = gsk_transform_rotate (next: global_transform, angle: scale); |
125 | global_transform = gsk_transform_translate (next: global_transform, point: &GRAPHENE_POINT_INIT (-w / 2.0f, -h / 2.0f)); |
126 | |
127 | gtk_widget_allocate (widget: self->test_widget, |
128 | width: w, height: h, |
129 | baseline: -1, |
130 | transform: global_transform); |
131 | } |
132 | |
133 | static void |
134 | gtk_transform_tester_snapshot (GtkWidget *widget, |
135 | GtkSnapshot *snapshot) |
136 | { |
137 | GtkTransformTester *self = (GtkTransformTester *)widget; |
138 | const int width = gtk_widget_get_width (widget); |
139 | const int height = gtk_widget_get_height (widget); |
140 | const int inc = self->pick_increase; |
141 | graphene_rect_t child_bounds; |
142 | graphene_rect_t self_bounds; |
143 | int x, y; |
144 | |
145 | GTK_WIDGET_CLASS (gtk_transform_tester_parent_class)->snapshot (widget, snapshot); |
146 | |
147 | if (!do_picking || |
148 | !gtk_widget_compute_bounds (widget: self->test_widget, target: widget, out_bounds: &child_bounds) || |
149 | !gtk_widget_compute_bounds (widget: self->test_widget, target: self->test_widget, out_bounds: &self_bounds)) |
150 | return; |
151 | |
152 | { |
153 | const struct { |
154 | graphene_point_t coords; |
155 | GdkRGBA color; |
156 | } points[4] = { |
157 | { self_bounds.origin, {1, 0, 0, 1} }, |
158 | { GRAPHENE_POINT_INIT (self_bounds.origin.x + self_bounds.size.width, self_bounds.origin.y), {0, 1, 0, 1} }, |
159 | { GRAPHENE_POINT_INIT (self_bounds.origin.x + self_bounds.size.width, self_bounds.origin.y + self_bounds.size.height), {0, 0, 1, 1} }, |
160 | { GRAPHENE_POINT_INIT (self_bounds.origin.x, self_bounds.origin.y + self_bounds.size.height), {1, 0, 1, 1} } |
161 | }; |
162 | |
163 | for (x = 0; x < G_N_ELEMENTS (points); x ++) |
164 | { |
165 | double px, py; |
166 | |
167 | gtk_widget_translate_coordinates (src_widget: self->test_widget, dest_widget: widget, |
168 | src_x: points[x].coords.x, src_y: points[x].coords.y, |
169 | dest_x: &px, dest_y: &py); |
170 | |
171 | gtk_snapshot_append_color (snapshot, color: &points[x].color, |
172 | bounds: &GRAPHENE_RECT_INIT (px, py, |
173 | 4, |
174 | 4)); |
175 | } |
176 | } |
177 | |
178 | /* Now add custom drawing */ |
179 | for (x = 0; x < width; x += inc) |
180 | { |
181 | for (y = 0; y < height; y += inc) |
182 | { |
183 | const float px = x; |
184 | const float py = y; |
185 | GtkWidget *picked; |
186 | #if 1 |
187 | picked = gtk_widget_pick (widget, x: px, y: py, flags: GTK_PICK_DEFAULT); |
188 | #else |
189 | { |
190 | int dx, dy; |
191 | gtk_widget_translate_coordinates (widget, self->test_widget, px, py, &dx, &dy); |
192 | picked = gtk_widget_pick (self->test_widget, dx, dy, GTK_PICK_DEFAULT); |
193 | } |
194 | #endif |
195 | |
196 | if (picked == self->test_widget) |
197 | gtk_snapshot_append_color (snapshot, color: &GREEN, |
198 | bounds: &GRAPHENE_RECT_INIT (px - (inc / 2), py - (inc / 2), inc, inc)); |
199 | else if (picked == test_child) |
200 | gtk_snapshot_append_color (snapshot, color: &BLUE, |
201 | bounds: &GRAPHENE_RECT_INIT (px - (inc / 2), py - (inc / 2), inc, inc)); |
202 | |
203 | else |
204 | gtk_snapshot_append_color (snapshot, color: &RED, |
205 | bounds: &GRAPHENE_RECT_INIT (px - (inc / 2), py - (inc / 2), inc, inc)); |
206 | } |
207 | } |
208 | |
209 | gtk_snapshot_append_color (snapshot, color: &BLACK, |
210 | bounds: &GRAPHENE_RECT_INIT (child_bounds.origin.x, |
211 | child_bounds.origin.y, |
212 | child_bounds.size.width, |
213 | 1)); |
214 | |
215 | gtk_snapshot_append_color (snapshot, color: &BLACK, |
216 | bounds: &GRAPHENE_RECT_INIT (child_bounds.origin.x + child_bounds.size.width, |
217 | child_bounds.origin.y, |
218 | 1, |
219 | child_bounds.size.height)); |
220 | |
221 | gtk_snapshot_append_color (snapshot, color: &BLACK, |
222 | bounds: &GRAPHENE_RECT_INIT (child_bounds.origin.x, |
223 | child_bounds.origin.y + child_bounds.size.height, |
224 | child_bounds.size.width, |
225 | 1)); |
226 | |
227 | gtk_snapshot_append_color (snapshot, color: &BLACK, |
228 | bounds: &GRAPHENE_RECT_INIT (child_bounds.origin.x, |
229 | child_bounds.origin.y, |
230 | 1, |
231 | child_bounds.size.height)); |
232 | } |
233 | |
234 | static void |
235 | gtk_transform_tester_init (GtkTransformTester *self) |
236 | { |
237 | self->pick_increase = 4; |
238 | } |
239 | |
240 | static void |
241 | gtk_transform_tester_class_init (GtkTransformTesterClass *klass) |
242 | { |
243 | GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; |
244 | |
245 | widget_class->measure = gtk_transform_tester_measure; |
246 | widget_class->size_allocate = gtk_transform_tester_size_allocate; |
247 | widget_class->snapshot = gtk_transform_tester_snapshot; |
248 | |
249 | gtk_widget_class_set_css_name (widget_class, name: "test" ); |
250 | } |
251 | |
252 | static gboolean |
253 | tick_cb (GtkWidget *widget, |
254 | GdkFrameClock *frame_clock, |
255 | gpointer user_data) |
256 | { |
257 | gtk_widget_queue_allocate (widget); |
258 | |
259 | return G_SOURCE_CONTINUE; |
260 | } |
261 | |
262 | static void |
263 | gtk_transform_tester_set_test_widget (GtkTransformTester *self, |
264 | GtkWidget *widget) |
265 | { |
266 | g_assert (!self->test_widget); |
267 | |
268 | self->test_widget = widget; |
269 | gtk_widget_set_parent (widget, parent: (GtkWidget *)self); |
270 | |
271 | gtk_widget_add_tick_callback (GTK_WIDGET (self), callback: tick_cb, NULL, NULL); |
272 | } |
273 | |
274 | static void |
275 | toggled_cb (GtkToggleButton *source, |
276 | gpointer user_data) |
277 | { |
278 | do_picking = gtk_toggle_button_get_active (toggle_button: source); |
279 | } |
280 | |
281 | static void |
282 | quit_cb (GtkWidget *widget, |
283 | gpointer data) |
284 | { |
285 | gboolean *done = data; |
286 | |
287 | *done = TRUE; |
288 | |
289 | g_main_context_wakeup (NULL); |
290 | } |
291 | |
292 | int |
293 | main (int argc, char **argv) |
294 | { |
295 | GtkWidget *window; |
296 | GtkWidget *matrix_chooser; |
297 | GtkWidget *box; |
298 | GtkWidget *titlebar; |
299 | GtkWidget *toggle_button; |
300 | GtkCssProvider *provider; |
301 | gboolean done = FALSE; |
302 | |
303 | gtk_init (); |
304 | |
305 | provider = gtk_css_provider_new (); |
306 | gtk_css_provider_load_from_data (css_provider: provider, data: css, length: -1); |
307 | gtk_style_context_add_provider_for_display (display: gdk_display_get_default (), |
308 | GTK_STYLE_PROVIDER (provider), |
309 | GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); |
310 | |
311 | window = gtk_window_new (); |
312 | matrix_chooser = g_object_new (GTK_TYPE_MATRIX_CHOOSER, NULL); |
313 | transform_tester = g_object_new (GTK_TYPE_TRANSFORM_TESTER, NULL); |
314 | box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 12); |
315 | titlebar = gtk_header_bar_new (); |
316 | |
317 | gtk_window_set_titlebar (GTK_WINDOW (window), titlebar); |
318 | |
319 | toggle_button = gtk_toggle_button_new (); |
320 | gtk_button_set_label (GTK_BUTTON (toggle_button), label: "Picking" ); |
321 | gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_button), is_active: do_picking); |
322 | g_signal_connect (toggle_button, "toggled" , G_CALLBACK (toggled_cb), NULL); |
323 | gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), child: toggle_button); |
324 | |
325 | test_widget = gtk_button_new (); |
326 | gtk_widget_set_size_request (widget: test_widget, TEST_WIDGET_MIN_SIZE, TEST_WIDGET_MIN_SIZE); |
327 | gtk_widget_set_halign (widget: test_widget, align: GTK_ALIGN_CENTER); |
328 | gtk_widget_set_valign (widget: test_widget, align: GTK_ALIGN_CENTER); |
329 | |
330 | |
331 | test_child = gtk_image_new_from_icon_name (icon_name: "weather-clear" ); |
332 | gtk_widget_set_halign (widget: test_child, align: GTK_ALIGN_CENTER); |
333 | gtk_widget_set_valign (widget: test_child, align: GTK_ALIGN_CENTER); |
334 | gtk_widget_set_size_request (widget: test_child, TEST_WIDGET_MIN_SIZE / 2, TEST_WIDGET_MIN_SIZE / 2); |
335 | gtk_button_set_child (GTK_BUTTON (test_widget), child: test_child); |
336 | |
337 | |
338 | gtk_transform_tester_set_test_widget (self: GTK_TRANSFORM_TESTER (ptr: transform_tester), widget: test_widget); |
339 | |
340 | gtk_widget_set_vexpand (widget: transform_tester, TRUE); |
341 | gtk_box_append (GTK_BOX (box), child: transform_tester); |
342 | gtk_box_append (GTK_BOX (box), child: matrix_chooser); |
343 | gtk_window_set_child (GTK_WINDOW (window), child: box); |
344 | |
345 | gtk_window_set_default_size (window: (GtkWindow *)window, width: 200, height: 200); |
346 | g_signal_connect (window, "close-request" , G_CALLBACK (quit_cb), &done); |
347 | gtk_widget_show (widget: window); |
348 | |
349 | while (!done) |
350 | g_main_context_iteration (NULL, TRUE); |
351 | |
352 | return 0; |
353 | } |
354 | |