1/* Drawing Area
2 * #Keywords: GtkDrawingArea
3 *
4 * GtkDrawingArea is a blank area where you can draw custom displays
5 * of various kinds.
6 *
7 * This demo has two drawing areas. The checkerboard area shows
8 * how you can just draw something; all you have to do is set a function
9 * via gtk_drawing_area_set_draw_func(), as shown here.
10 *
11 * The "scribble" area is a bit more advanced, and shows how to handle
12 * events such as button presses and mouse motion. Click the mouse
13 * and drag in the scribble area to draw squiggles. Resize the window
14 * to clear the area.
15 */
16
17#include <gtk/gtk.h>
18
19static GtkWidget *window = NULL;
20/* Pixmap for scribble area, to store current scribbles */
21static cairo_surface_t *surface = NULL;
22
23/* Create a new surface of the appropriate size to store our scribbles */
24static void
25create_surface (GtkWidget *widget)
26{
27 cairo_t *cr;
28
29 if (surface)
30 cairo_surface_destroy (surface);
31
32 surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32,
33 width: gtk_widget_get_width (widget),
34 height: gtk_widget_get_height (widget));
35
36 /* Initialize the surface to white */
37 cr = cairo_create (target: surface);
38
39 cairo_set_source_rgb (cr, red: 1, green: 1, blue: 1);
40 cairo_paint (cr);
41
42 cairo_destroy (cr);
43}
44
45static void
46scribble_resize (GtkWidget *widget,
47 int width,
48 int height)
49{
50 create_surface (widget);
51}
52
53/* Redraw the screen from the surface */
54static void
55scribble_draw (GtkDrawingArea *da,
56 cairo_t *cr,
57 int width,
58 int height,
59 gpointer data)
60{
61 cairo_set_source_surface (cr, surface, x: 0, y: 0);
62 cairo_paint (cr);
63}
64
65/* Draw a rectangle on the screen */
66static void
67draw_brush (GtkWidget *widget,
68 double x,
69 double y)
70{
71 GdkRectangle update_rect;
72 cairo_t *cr;
73
74 if (surface == NULL ||
75 cairo_image_surface_get_width (surface) != gtk_widget_get_width (widget) ||
76 cairo_image_surface_get_height (surface) != gtk_widget_get_height (widget))
77 create_surface (widget);
78
79 update_rect.x = x - 3;
80 update_rect.y = y - 3;
81 update_rect.width = 6;
82 update_rect.height = 6;
83
84 /* Paint to the surface, where we store our state */
85 cr = cairo_create (target: surface);
86
87 gdk_cairo_rectangle (cr, rectangle: &update_rect);
88 cairo_fill (cr);
89
90 cairo_destroy (cr);
91
92 gtk_widget_queue_draw (widget);
93}
94
95static double start_x;
96static double start_y;
97
98static void
99drag_begin (GtkGestureDrag *gesture,
100 double x,
101 double y,
102 GtkWidget *area)
103{
104 start_x = x;
105 start_y = y;
106
107 draw_brush (widget: area, x, y);
108}
109
110static void
111drag_update (GtkGestureDrag *gesture,
112 double x,
113 double y,
114 GtkWidget *area)
115{
116 draw_brush (widget: area, x: start_x + x, y: start_y + y);
117}
118
119static void
120drag_end (GtkGestureDrag *gesture,
121 double x,
122 double y,
123 GtkWidget *area)
124{
125 draw_brush (widget: area, x: start_x + x, y: start_y + y);
126}
127
128static void
129oval_path (cairo_t *cr,
130 double xc, double yc,
131 double xr, double yr)
132{
133 cairo_save (cr);
134
135 cairo_translate (cr, tx: xc, ty: yc);
136 cairo_scale (cr, sx: 1.0, sy: yr / xr);
137 cairo_move_to (cr, x: xr, y: 0.0);
138 cairo_arc (cr,
139 xc: 0, yc: 0,
140 radius: xr,
141 angle1: 0, angle2: 2 * G_PI);
142 cairo_close_path (cr);
143
144 cairo_restore (cr);
145}
146
147/* Fill the given area with checks in the standard style
148 * for showing compositing effects.
149 *
150 * It would make sense to do this as a repeating surface,
151 * but most implementations of RENDER currently have broken
152 * implementations of repeat + transform, even when the
153 * transform is a translation.
154 */
155static void
156fill_checks (cairo_t *cr,
157 int x, int y,
158 int width, int height)
159{
160 int i, j;
161
162#define CHECK_SIZE 16
163
164 cairo_rectangle (cr, x, y, width, height);
165 cairo_set_source_rgb (cr, red: 0.4, green: 0.4, blue: 0.4);
166 cairo_fill (cr);
167
168 /* Only works for CHECK_SIZE a power of 2 */
169 j = x & (-CHECK_SIZE);
170
171 for (; j < height; j += CHECK_SIZE)
172 {
173 i = y & (-CHECK_SIZE);
174 for (; i < width; i += CHECK_SIZE)
175 if ((i / CHECK_SIZE + j / CHECK_SIZE) % 2 == 0)
176 cairo_rectangle (cr, x: i, y: j, CHECK_SIZE, CHECK_SIZE);
177 }
178
179 cairo_set_source_rgb (cr, red: 0.7, green: 0.7, blue: 0.7);
180 cairo_fill (cr);
181
182#undef CHECK_SIZE
183}
184
185/* Draw a red, green, and blue circle equally spaced inside
186 * the larger circle of radius r at (xc, yc)
187 */
188static void
189draw_3circles (cairo_t *cr,
190 double xc, double yc,
191 double radius,
192 double alpha)
193{
194 double subradius = radius * (2 / 3. - 0.1);
195
196 cairo_set_source_rgba (cr, red: 1., green: 0., blue: 0., alpha);
197 oval_path (cr,
198 xc: xc + radius / 3. * cos (G_PI * (0.5)),
199 yc: yc - radius / 3. * sin (G_PI * (0.5)),
200 xr: subradius, yr: subradius);
201 cairo_fill (cr);
202
203 cairo_set_source_rgba (cr, red: 0., green: 1., blue: 0., alpha);
204 oval_path (cr,
205 xc: xc + radius / 3. * cos (G_PI * (0.5 + 2/.3)),
206 yc: yc - radius / 3. * sin (G_PI * (0.5 + 2/.3)),
207 xr: subradius, yr: subradius);
208 cairo_fill (cr);
209
210 cairo_set_source_rgba (cr, red: 0., green: 0., blue: 1., alpha);
211 oval_path (cr,
212 xc: xc + radius / 3. * cos (G_PI * (0.5 + 4/.3)),
213 yc: yc - radius / 3. * sin (G_PI * (0.5 + 4/.3)),
214 xr: subradius, yr: subradius);
215 cairo_fill (cr);
216}
217
218static void
219groups_draw (GtkDrawingArea *darea,
220 cairo_t *cr,
221 int width,
222 int height,
223 gpointer data)
224{
225 cairo_surface_t *overlay, *punch, *circles;
226 cairo_t *overlay_cr, *punch_cr, *circles_cr;
227
228 /* Fill the background */
229 double radius = 0.5 * (width < height ? width : height) - 10;
230 double xc = width / 2.;
231 double yc = height / 2.;
232
233 overlay = cairo_surface_create_similar (other: cairo_get_target (cr),
234 content: CAIRO_CONTENT_COLOR_ALPHA,
235 width, height);
236
237 punch = cairo_surface_create_similar (other: cairo_get_target (cr),
238 content: CAIRO_CONTENT_ALPHA,
239 width, height);
240
241 circles = cairo_surface_create_similar (other: cairo_get_target (cr),
242 content: CAIRO_CONTENT_COLOR_ALPHA,
243 width, height);
244
245 fill_checks (cr, x: 0, y: 0, width, height);
246
247 /* Draw a black circle on the overlay
248 */
249 overlay_cr = cairo_create (target: overlay);
250 cairo_set_source_rgb (cr: overlay_cr, red: 0., green: 0., blue: 0.);
251 oval_path (cr: overlay_cr, xc, yc, xr: radius, yr: radius);
252 cairo_fill (cr: overlay_cr);
253
254 /* Draw 3 circles to the punch surface, then cut
255 * that out of the main circle in the overlay
256 */
257 punch_cr = cairo_create (target: punch);
258 draw_3circles (cr: punch_cr, xc, yc, radius, alpha: 1.0);
259 cairo_destroy (cr: punch_cr);
260
261 cairo_set_operator (cr: overlay_cr, op: CAIRO_OPERATOR_DEST_OUT);
262 cairo_set_source_surface (cr: overlay_cr, surface: punch, x: 0, y: 0);
263 cairo_paint (cr: overlay_cr);
264
265 /* Now draw the 3 circles in a subgroup again
266 * at half intensity, and use OperatorAdd to join up
267 * without seams.
268 */
269 circles_cr = cairo_create (target: circles);
270
271 cairo_set_operator (cr: circles_cr, op: CAIRO_OPERATOR_OVER);
272 draw_3circles (cr: circles_cr, xc, yc, radius, alpha: 0.5);
273 cairo_destroy (cr: circles_cr);
274
275 cairo_set_operator (cr: overlay_cr, op: CAIRO_OPERATOR_ADD);
276 cairo_set_source_surface (cr: overlay_cr, surface: circles, x: 0, y: 0);
277 cairo_paint (cr: overlay_cr);
278
279 cairo_destroy (cr: overlay_cr);
280
281 cairo_set_source_surface (cr, surface: overlay, x: 0, y: 0);
282 cairo_paint (cr);
283
284 cairo_surface_destroy (surface: overlay);
285 cairo_surface_destroy (surface: punch);
286 cairo_surface_destroy (surface: circles);
287}
288
289static void
290close_window (void)
291{
292 window = NULL;
293
294 if (surface)
295 cairo_surface_destroy (surface);
296 surface = NULL;
297}
298
299GtkWidget *
300do_drawingarea (GtkWidget *do_widget)
301{
302 GtkWidget *frame;
303 GtkWidget *vbox;
304 GtkWidget *da;
305 GtkWidget *label;
306 GtkGesture *drag;
307
308 if (!window)
309 {
310 window = gtk_window_new ();
311 gtk_window_set_display (GTK_WINDOW (window),
312 display: gtk_widget_get_display (widget: do_widget));
313 gtk_window_set_title (GTK_WINDOW (window), title: "Drawing Area");
314 gtk_window_set_default_size (GTK_WINDOW (window), width: 250, height: -1);
315
316 g_signal_connect (window, "destroy",
317 G_CALLBACK (close_window), NULL);
318
319 vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8);
320 gtk_widget_set_margin_start (widget: vbox, margin: 16);
321 gtk_widget_set_margin_end (widget: vbox, margin: 16);
322 gtk_widget_set_margin_top (widget: vbox, margin: 16);
323 gtk_widget_set_margin_bottom (widget: vbox, margin: 16);
324 gtk_window_set_child (GTK_WINDOW (window), child: vbox);
325
326 /*
327 * Create the groups area
328 */
329 label = gtk_label_new (str: "Knockout groups");
330 gtk_widget_add_css_class (widget: label, css_class: "heading");
331 gtk_box_append (GTK_BOX (vbox), child: label);
332
333 frame = gtk_frame_new (NULL);
334 gtk_widget_set_vexpand (widget: frame, TRUE);
335 gtk_box_append (GTK_BOX (vbox), child: frame);
336
337 da = gtk_drawing_area_new ();
338 gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (da), width: 100);
339 gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (da), height: 100);
340 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func: groups_draw, NULL, NULL);
341 gtk_frame_set_child (GTK_FRAME (frame), child: da);
342
343 /*
344 * Create the scribble area
345 */
346
347 label = gtk_label_new (str: "Scribble area");
348 gtk_widget_add_css_class (widget: label, css_class: "heading");
349 gtk_box_append (GTK_BOX (vbox), child: label);
350
351 frame = gtk_frame_new (NULL);
352 gtk_widget_set_vexpand (widget: frame, TRUE);
353 gtk_box_append (GTK_BOX (vbox), child: frame);
354
355 da = gtk_drawing_area_new ();
356 gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (da), width: 100);
357 gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (da), height: 100);
358 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func: scribble_draw, NULL, NULL);
359 gtk_frame_set_child (GTK_FRAME (frame), child: da);
360
361 g_signal_connect (da, "resize",
362 G_CALLBACK (scribble_resize), NULL);
363
364 drag = gtk_gesture_drag_new ();
365 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
366 gtk_widget_add_controller (widget: da, GTK_EVENT_CONTROLLER (drag));
367
368 g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), da);
369 g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da);
370 g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da);
371
372 }
373
374 if (!gtk_widget_get_visible (widget: window))
375 gtk_widget_show (widget: window);
376 else
377 gtk_window_destroy (GTK_WINDOW (window));
378
379 return window;
380}
381

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