1/* Drag-and-Drop
2 * #Keywords: dnd, menu, popover, gesture
3 *
4 * This demo shows dragging colors and widgets.
5 * The items in this demo can be moved, recolored
6 * and rotated.
7 *
8 * The demo also has an example for creating a
9 * menu-like popover without using a menu model.
10 */
11
12#include <gtk/gtk.h>
13
14G_DECLARE_FINAL_TYPE (CanvasItem, canvas_item, CANVAS, ITEM, GtkWidget)
15
16struct _CanvasItem {
17 GtkWidget parent;
18
19 GtkWidget *fixed;
20 GtkWidget *label;
21
22 double r;
23 double angle;
24 double delta;
25
26 GtkWidget *editor;
27};
28
29struct _CanvasItemClass {
30 GtkWidgetClass parent_class;
31};
32
33G_DEFINE_TYPE (CanvasItem, canvas_item, GTK_TYPE_WIDGET)
34
35static int n_items = 0;
36
37static void
38set_color (CanvasItem *item,
39 GdkRGBA *color)
40{
41 char *css;
42 char *str;
43 GtkStyleContext *context;
44 GtkCssProvider *provider;
45 const char *old_class;
46
47 str = gdk_rgba_to_string (rgba: color);
48 css = g_strdup_printf (format: "* { background: %s; }", str);
49
50 context = gtk_widget_get_style_context (widget: item->label);
51 provider = g_object_get_data (G_OBJECT (context), key: "style-provider");
52 if (provider)
53 gtk_style_context_remove_provider (context, GTK_STYLE_PROVIDER (provider));
54
55 old_class = (const char *)g_object_get_data (G_OBJECT (item->label), key: "css-class");
56 if (old_class)
57 gtk_widget_remove_css_class (widget: item->label, css_class: old_class);
58
59 provider = gtk_css_provider_new ();
60 gtk_css_provider_load_from_data (css_provider: provider, data: css, length: -1);
61 gtk_style_context_add_provider (context: gtk_widget_get_style_context (widget: item->label), GTK_STYLE_PROVIDER (provider), priority: 800);
62 g_object_set_data_full (G_OBJECT (context), key: "style-provider", data: provider, destroy: g_object_unref);
63
64 g_free (mem: str);
65 g_free (mem: css);
66}
67
68static void
69set_css (CanvasItem *item,
70 const char *class)
71{
72 GtkStyleContext *context;
73 GtkCssProvider *provider;
74 const char *old_class;
75
76 context = gtk_widget_get_style_context (widget: item->label);
77 provider = g_object_get_data (G_OBJECT (context), key: "style-provider");
78 if (provider)
79 gtk_style_context_remove_provider (context, GTK_STYLE_PROVIDER (provider));
80
81 old_class = (const char *)g_object_get_data (G_OBJECT (item->label), key: "css-class");
82 if (old_class)
83 gtk_widget_remove_css_class (widget: item->label, css_class: old_class);
84
85 g_object_set_data_full (G_OBJECT (item->label), key: "css-class", data: g_strdup (str: class), destroy: g_free);
86 gtk_widget_add_css_class (widget: item->label, css_class: class);
87}
88
89static gboolean
90item_drag_drop (GtkDropTarget *dest,
91 const GValue *value,
92 double x,
93 double y)
94{
95 GtkWidget *label = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
96 CanvasItem *item = CANVAS_ITEM (ptr: gtk_widget_get_parent (widget: gtk_widget_get_parent (widget: label)));
97
98 if (G_VALUE_TYPE (value) == GDK_TYPE_RGBA)
99 set_color (item, color: g_value_get_boxed (value));
100 else if (G_VALUE_TYPE (value) == G_TYPE_STRING)
101 set_css (item, class: g_value_get_string (value));
102
103 return TRUE;
104}
105
106static void
107apply_transform (CanvasItem *item)
108{
109 GskTransform *transform;
110 double x, y;
111
112 x = gtk_widget_get_allocated_width (widget: item->label) / 2.0;
113 y = gtk_widget_get_allocated_height (widget: item->label) / 2.0;
114 item->r = sqrt (x: x*x + y*y);
115
116 transform = gsk_transform_translate (NULL, point: &(graphene_point_t) { item->r, item->r });
117 transform = gsk_transform_rotate (next: transform, angle: item->angle + item->delta);
118 transform = gsk_transform_translate (next: transform, point: &(graphene_point_t) { -x, -y });
119
120 gtk_fixed_set_child_transform (GTK_FIXED (item->fixed), widget: item->label, transform);
121 gsk_transform_unref (self: transform);
122}
123
124static void
125angle_changed (GtkGestureRotate *gesture,
126 double angle,
127 double delta)
128{
129 CanvasItem *item = CANVAS_ITEM (ptr: gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
130
131 item->delta = angle / M_PI * 180.0;
132
133 apply_transform (item);
134}
135
136static void
137rotate_done (GtkGesture *gesture)
138{
139 CanvasItem *item = CANVAS_ITEM (ptr: gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
140
141 item->angle = item->angle + item->delta;
142 item->delta = 0;
143}
144
145static void
146click_done (GtkGesture *gesture)
147{
148 GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
149 GtkWidget *canvas = gtk_widget_get_parent (widget: item);
150 GtkWidget *last_child;
151
152 last_child = gtk_widget_get_last_child (widget: canvas);
153 if (item != last_child)
154 gtk_widget_insert_after (widget: item, parent: canvas, previous_sibling: last_child);
155}
156
157static gboolean
158theme_is_dark (void)
159{
160 GtkSettings *settings;
161 char *theme;
162 gboolean prefer_dark;
163 gboolean dark;
164
165 settings = gtk_settings_get_default ();
166 g_object_get (object: settings,
167 first_property_name: "gtk-theme-name", &theme,
168 "gtk-application-prefer-dark-theme", &prefer_dark,
169 NULL);
170
171 if ((strcmp (s1: theme, s2: "Adwaita") == 0 && prefer_dark) || strcmp (s1: theme, s2: "HighContrastInverse") == 0)
172 dark = TRUE;
173 else
174 dark = FALSE;
175
176 g_free (mem: theme);
177
178 return dark;
179}
180
181static void
182canvas_item_init (CanvasItem *item)
183{
184 char *text;
185 char *id;
186 GdkRGBA rgba;
187 GtkDropTarget *dest;
188 GtkGesture *gesture;
189 GType types[2] = { GDK_TYPE_RGBA, G_TYPE_STRING };
190
191 n_items++;
192
193 text = g_strdup_printf (format: "Item %d", n_items);
194 item->label = gtk_label_new (str: text);
195 gtk_widget_add_css_class (widget: item->label, css_class: "canvasitem");
196 g_free (mem: text);
197
198 item->fixed = gtk_fixed_new ();
199 gtk_widget_set_parent (widget: item->fixed, GTK_WIDGET (item));
200 gtk_fixed_put (GTK_FIXED (item->fixed), widget: item->label, x: 0, y: 0);
201
202 gtk_widget_add_css_class (widget: item->label, css_class: "frame");
203
204 id = g_strdup_printf (format: "item%d", n_items);
205 gtk_widget_set_name (widget: item->label, name: id);
206 g_free (mem: id);
207
208 if (theme_is_dark ())
209 gdk_rgba_parse (rgba: &rgba, spec: "blue");
210 else
211 gdk_rgba_parse (rgba: &rgba, spec: "yellow");
212
213 set_color (item, color: &rgba);
214
215 item->angle = 0;
216
217 dest = gtk_drop_target_new (G_TYPE_INVALID, actions: GDK_ACTION_COPY);
218 gtk_drop_target_set_gtypes (self: dest, types, G_N_ELEMENTS (types));
219 g_signal_connect (dest, "drop", G_CALLBACK (item_drag_drop), NULL);
220 gtk_widget_add_controller (GTK_WIDGET (item->label), GTK_EVENT_CONTROLLER (dest));
221
222 gesture = gtk_gesture_rotate_new ();
223 g_signal_connect (gesture, "angle-changed", G_CALLBACK (angle_changed), NULL);
224 g_signal_connect (gesture, "end", G_CALLBACK (rotate_done), NULL);
225 gtk_widget_add_controller (GTK_WIDGET (item), GTK_EVENT_CONTROLLER (gesture));
226
227 gesture = gtk_gesture_click_new ();
228 g_signal_connect (gesture, "released", G_CALLBACK (click_done), NULL);
229 gtk_widget_add_controller (GTK_WIDGET (item), GTK_EVENT_CONTROLLER (gesture));
230}
231
232static void
233canvas_item_dispose (GObject *object)
234{
235 CanvasItem *item = CANVAS_ITEM (ptr: object);
236
237 g_clear_pointer (&item->fixed, gtk_widget_unparent);
238 g_clear_pointer (&item->editor, gtk_widget_unparent);
239
240 G_OBJECT_CLASS (canvas_item_parent_class)->dispose (object);
241}
242
243static void
244canvas_item_map (GtkWidget *widget)
245{
246 CanvasItem *item = CANVAS_ITEM (ptr: widget);
247
248 GTK_WIDGET_CLASS (canvas_item_parent_class)->map (widget);
249
250 apply_transform (item);
251}
252
253static void
254canvas_item_class_init (CanvasItemClass *class)
255{
256 GObjectClass *object_class = G_OBJECT_CLASS (class);
257 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
258
259 object_class->dispose = canvas_item_dispose;
260
261 widget_class->map = canvas_item_map;
262
263 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
264 gtk_widget_class_set_css_name (widget_class, name: "item");
265}
266
267static GtkWidget *
268canvas_item_new (void)
269{
270 CanvasItem *item = g_object_new (object_type: canvas_item_get_type (), NULL);
271
272 return GTK_WIDGET (item);
273}
274
275static GdkPaintable *
276canvas_item_get_drag_icon (CanvasItem *item)
277{
278 return gtk_widget_paintable_new (widget: item->fixed);
279}
280
281static gboolean
282canvas_item_is_editing (CanvasItem *item)
283{
284 return item->editor != NULL;
285}
286
287static void
288scale_changed (GtkRange *range,
289 CanvasItem *item)
290{
291 item->angle = gtk_range_get_value (range);
292 apply_transform (item);
293}
294
295static void
296text_changed (GtkEditable *editable,
297 GParamSpec *pspec,
298 CanvasItem *item)
299{
300 gtk_label_set_text (GTK_LABEL (item->label), str: gtk_editable_get_text (editable));
301 apply_transform (item);
302}
303
304static void
305canvas_item_stop_editing (CanvasItem *item)
306{
307 GtkWidget *scale;
308
309 if (!item->editor)
310 return;
311
312 scale = gtk_widget_get_last_child (widget: item->editor);
313 g_signal_handlers_disconnect_by_func (scale, scale_changed, item);
314
315 gtk_fixed_remove (GTK_FIXED (gtk_widget_get_parent (item->editor)), widget: item->editor);
316 item->editor = NULL;
317}
318
319static void
320canvas_item_start_editing (CanvasItem *item)
321{
322 GtkWidget *canvas = gtk_widget_get_parent (GTK_WIDGET (item));
323 GtkWidget *entry;
324 GtkWidget *scale;
325 double x, y;
326
327 if (item->editor)
328 return;
329
330 item->editor = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 12);
331
332 entry = gtk_entry_new ();
333
334 gtk_editable_set_text (GTK_EDITABLE (entry),
335 text: gtk_label_get_text (GTK_LABEL (item->label)));
336
337 gtk_editable_set_width_chars (GTK_EDITABLE (entry), n_chars: 12);
338 g_signal_connect (entry, "notify::text", G_CALLBACK (text_changed), item);
339 g_signal_connect_swapped (entry, "activate", G_CALLBACK (canvas_item_stop_editing), item);
340
341 gtk_box_append (GTK_BOX (item->editor), child: entry);
342
343 scale = gtk_scale_new_with_range (orientation: GTK_ORIENTATION_HORIZONTAL, min: 0, max: 360, step: 1);
344 gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
345 gtk_range_set_value (GTK_RANGE (scale), value: fmod (x: item->angle, y: 360));
346
347 g_signal_connect (scale, "value-changed", G_CALLBACK (scale_changed), item);
348
349 gtk_box_append (GTK_BOX (item->editor), child: scale);
350
351 gtk_widget_translate_coordinates (GTK_WIDGET (item), dest_widget: canvas, src_x: 0, src_y: 0, dest_x: &x, dest_y: &y);
352 gtk_fixed_put (GTK_FIXED (canvas), widget: item->editor, x, y: y + 2 * item->r);
353 gtk_widget_grab_focus (widget: entry);
354
355}
356
357static GdkContentProvider *
358prepare (GtkDragSource *source,
359 double x,
360 double y)
361{
362 GtkWidget *canvas;
363 GtkWidget *item;
364
365 canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
366 item = gtk_widget_pick (widget: canvas, x, y, flags: GTK_PICK_DEFAULT);
367
368 item = gtk_widget_get_ancestor (widget: item, widget_type: canvas_item_get_type ());
369 if (!item)
370 return NULL;
371
372 g_object_set_data (G_OBJECT (canvas), key: "dragged-item", data: item);
373
374 return gdk_content_provider_new_typed (GTK_TYPE_WIDGET, item);
375}
376
377static void
378drag_begin (GtkDragSource *source,
379 GdkDrag *drag)
380{
381 GtkWidget *canvas;
382 CanvasItem *item;
383 GdkPaintable *paintable;
384
385 canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
386 item = CANVAS_ITEM (ptr: g_object_get_data (G_OBJECT (canvas), key: "dragged-item"));
387
388 paintable = canvas_item_get_drag_icon (item);
389 gtk_drag_source_set_icon (source, paintable, hot_x: item->r, hot_y: item->r);
390 g_object_unref (object: paintable);
391
392 gtk_widget_set_opacity (GTK_WIDGET (item), opacity: 0.3);
393}
394
395static void
396drag_end (GtkDragSource *source,
397 GdkDrag *drag)
398{
399 GtkWidget *canvas;
400 GtkWidget *item;
401
402 canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
403 item = g_object_get_data (G_OBJECT (canvas), key: "dragged-item");
404 g_object_set_data (G_OBJECT (canvas), key: "dragged-item", NULL);
405
406 gtk_widget_set_opacity (widget: item, opacity: 1.0);
407}
408
409static gboolean
410drag_cancel (GtkDragSource *source,
411 GdkDrag *drag,
412 GdkDragCancelReason reason)
413{
414 return FALSE;
415}
416
417static gboolean
418drag_drop (GtkDropTarget *target,
419 const GValue *value,
420 double x,
421 double y)
422{
423 CanvasItem *item;
424 GtkWidget *canvas;
425 GtkWidget *last_child;
426
427 item = g_value_get_object (value);
428
429 canvas = gtk_widget_get_parent (GTK_WIDGET (item));
430 last_child = gtk_widget_get_last_child (widget: canvas);
431 if (GTK_WIDGET (item) != last_child)
432 gtk_widget_insert_after (GTK_WIDGET (item), parent: canvas, previous_sibling: last_child);
433
434 gtk_fixed_move (GTK_FIXED (canvas), GTK_WIDGET (item), x: x - item->r, y: y - item->r);
435
436 return TRUE;
437}
438
439static void
440new_item_cb (GtkWidget *button, gpointer data)
441{
442 GtkWidget *canvas = data;
443 GtkWidget *popover;
444 GtkWidget *item;
445 GdkRectangle rect;
446
447 popover = gtk_widget_get_ancestor (widget: button, GTK_TYPE_POPOVER);
448 gtk_popover_get_pointing_to (GTK_POPOVER (popover), rect: &rect);
449
450 item = canvas_item_new ();
451 gtk_fixed_put (GTK_FIXED (canvas), widget: item, x: rect.x, y: rect.y);
452 apply_transform (item: CANVAS_ITEM (ptr: item));
453
454 gtk_popover_popdown (GTK_POPOVER (gtk_widget_get_ancestor (button, GTK_TYPE_POPOVER)));
455}
456
457static void
458edit_cb (GtkWidget *button, GtkWidget *child)
459{
460 CanvasItem *item = CANVAS_ITEM (ptr: child);
461
462 if (button)
463 gtk_popover_popdown (GTK_POPOVER (gtk_widget_get_ancestor (button, GTK_TYPE_POPOVER)));
464
465 if (!canvas_item_is_editing (item))
466 canvas_item_start_editing (item);
467}
468
469static void
470delete_cb (GtkWidget *button, GtkWidget *child)
471{
472 GtkWidget *canvas = gtk_widget_get_parent (widget: child);
473
474 gtk_fixed_remove (GTK_FIXED (canvas), widget: child);
475
476 gtk_popover_popdown (GTK_POPOVER (gtk_widget_get_ancestor (button, GTK_TYPE_POPOVER)));
477}
478
479static void
480pressed_cb (GtkGesture *gesture,
481 int n_press,
482 double x,
483 double y,
484 gpointer data)
485{
486 GtkWidget *widget;
487 GtkWidget *child;
488
489 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
490 child = gtk_widget_pick (widget, x, y, flags: GTK_PICK_DEFAULT);
491 child = gtk_widget_get_ancestor (widget: child, widget_type: canvas_item_get_type ());
492
493 if (gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)) == GDK_BUTTON_SECONDARY)
494 {
495 GtkWidget *menu;
496 GtkWidget *box;
497 GtkWidget *item;
498
499 menu = gtk_popover_new ();
500 gtk_widget_set_parent (widget: menu, parent: widget);
501 gtk_popover_set_has_arrow (GTK_POPOVER (menu), FALSE);
502 gtk_popover_set_pointing_to (GTK_POPOVER (menu), rect: &(GdkRectangle){ x, y, 1, 1});
503 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
504 gtk_popover_set_child (GTK_POPOVER (menu), child: box);
505
506 item = gtk_button_new_with_label (label: "New");
507 gtk_button_set_has_frame (GTK_BUTTON (item), FALSE);
508 g_signal_connect (item, "clicked", G_CALLBACK (new_item_cb), widget);
509 gtk_box_append (GTK_BOX (box), child: item);
510
511 item = gtk_separator_new (orientation: GTK_ORIENTATION_HORIZONTAL);
512 gtk_box_append (GTK_BOX (box), child: item);
513
514 item = gtk_button_new_with_label (label: "Edit");
515 gtk_button_set_has_frame (GTK_BUTTON (item), FALSE);
516 gtk_widget_set_sensitive (widget: item, sensitive: child != NULL && child != widget);
517 g_signal_connect (item, "clicked", G_CALLBACK (edit_cb), child);
518 gtk_box_append (GTK_BOX (box), child: item);
519
520 item = gtk_separator_new (orientation: GTK_ORIENTATION_HORIZONTAL);
521 gtk_box_append (GTK_BOX (box), child: item);
522
523 item = gtk_button_new_with_label (label: "Delete");
524 gtk_button_set_has_frame (GTK_BUTTON (item), FALSE);
525 gtk_widget_set_sensitive (widget: item, sensitive: child != NULL && child != widget);
526 g_signal_connect (item, "clicked", G_CALLBACK (delete_cb), child);
527 gtk_box_append (GTK_BOX (box), child: item);
528
529 gtk_popover_popup (GTK_POPOVER (menu));
530 }
531}
532
533static void
534released_cb (GtkGesture *gesture,
535 int n_press,
536 double x,
537 double y,
538 gpointer data)
539{
540 GtkWidget *widget;
541 GtkWidget *child;
542 CanvasItem *item;
543
544 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
545 child = gtk_widget_pick (widget, x, y, flags: 0);
546 item = (CanvasItem *)gtk_widget_get_ancestor (widget: child, widget_type: canvas_item_get_type ());
547 if (!item)
548 return;
549
550 if (gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)) == GDK_BUTTON_PRIMARY)
551 {
552 if (canvas_item_is_editing (item))
553 canvas_item_stop_editing (item);
554 else
555 canvas_item_start_editing (item);
556 }
557}
558
559static GtkWidget *
560canvas_new (void)
561{
562 GtkWidget *canvas;
563 GtkDragSource *source;
564 GtkDropTarget *dest;
565 GtkGesture *gesture;
566
567 canvas = gtk_fixed_new ();
568 gtk_widget_set_hexpand (widget: canvas, TRUE);
569 gtk_widget_set_vexpand (widget: canvas, TRUE);
570
571 source = gtk_drag_source_new ();
572 gtk_drag_source_set_actions (source, actions: GDK_ACTION_MOVE);
573 g_signal_connect (source, "prepare", G_CALLBACK (prepare), NULL);
574 g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), NULL);
575 g_signal_connect (source, "drag-end", G_CALLBACK (drag_end), NULL);
576 g_signal_connect (source, "drag-cancel", G_CALLBACK (drag_cancel), NULL);
577 gtk_widget_add_controller (widget: canvas, GTK_EVENT_CONTROLLER (source));
578
579 dest = gtk_drop_target_new (GTK_TYPE_WIDGET, actions: GDK_ACTION_MOVE);
580 g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), NULL);
581 gtk_widget_add_controller (widget: canvas, GTK_EVENT_CONTROLLER (dest));
582
583 gesture = gtk_gesture_click_new ();
584 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), button: 0);
585 g_signal_connect (gesture, "pressed", G_CALLBACK (pressed_cb), NULL);
586 g_signal_connect (gesture, "released", G_CALLBACK (released_cb), NULL);
587 gtk_widget_add_controller (widget: canvas, GTK_EVENT_CONTROLLER (gesture));
588
589 return canvas;
590}
591
592static GdkContentProvider *
593css_drag_prepare (GtkDragSource *source,
594 double x,
595 double y,
596 GtkWidget *button)
597{
598 const char *class;
599 GdkPaintable *paintable;
600
601 class = (const char *)g_object_get_data (G_OBJECT (button), key: "css-class");
602
603 paintable = gtk_widget_paintable_new (widget: button);
604 gtk_drag_source_set_icon (source, paintable, hot_x: 0, hot_y: 0);
605 g_object_unref (object: paintable);
606
607 return gdk_content_provider_new_typed (G_TYPE_STRING, class);
608}
609
610static GtkWidget *
611css_button_new (const char *class)
612{
613 GtkWidget *button;
614 GtkDragSource *source;
615
616 button = gtk_image_new ();
617 gtk_widget_set_size_request (widget: button, width: 48, height: 32);
618 gtk_widget_add_css_class (widget: button, css_class: class);
619 g_object_set_data (G_OBJECT (button), key: "css-class", data: (gpointer)class);
620
621 source = gtk_drag_source_new ();
622 g_signal_connect (source, "prepare", G_CALLBACK (css_drag_prepare), button);
623 gtk_widget_add_controller (widget: button, GTK_EVENT_CONTROLLER (source));
624
625 return button;
626}
627
628typedef struct
629{
630 GtkWidget parent_instance;
631 GdkRGBA color;
632} ColorSwatch;
633
634typedef struct
635{
636 GtkWidgetClass parent_class;
637} ColorSwatchClass;
638
639G_DEFINE_TYPE (ColorSwatch, color_swatch, GTK_TYPE_WIDGET)
640
641static GdkContentProvider *
642color_swatch_drag_prepare (GtkDragSource *source,
643 double x,
644 double y,
645 ColorSwatch *swatch)
646{
647 return gdk_content_provider_new_typed (GDK_TYPE_RGBA, &swatch->color);
648}
649
650static void
651color_swatch_init (ColorSwatch *swatch)
652{
653 GtkDragSource *source = gtk_drag_source_new ();
654 g_signal_connect (source, "prepare", G_CALLBACK (color_swatch_drag_prepare), swatch);
655 gtk_widget_add_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (source));
656}
657
658static void
659color_swatch_snapshot (GtkWidget *widget,
660 GtkSnapshot *snapshot)
661{
662 ColorSwatch *swatch = (ColorSwatch *)widget;
663 float w = gtk_widget_get_width (widget);
664 float h = gtk_widget_get_height (widget);
665
666 gtk_snapshot_append_color (snapshot, color: &swatch->color,
667 bounds: &GRAPHENE_RECT_INIT(0, 0, w, h));
668}
669
670void
671color_swatch_measure (GtkWidget *widget,
672 GtkOrientation orientation,
673 int for_size,
674 int *minimum_size,
675 int *natural_size,
676 int *minimum_baseline,
677 int *natural_baseline)
678{
679 if (orientation == GTK_ORIENTATION_HORIZONTAL)
680 *minimum_size = *natural_size = 48;
681 else
682 *minimum_size = *natural_size = 32;
683}
684
685static void
686color_swatch_class_init (ColorSwatchClass *class)
687{
688 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
689
690 widget_class->snapshot = color_swatch_snapshot;
691 widget_class->measure = color_swatch_measure;
692 gtk_widget_class_set_css_name (widget_class, name: "colorswatch");
693}
694
695static GtkWidget *
696color_swatch_new (const char *color)
697{
698 ColorSwatch *swatch = g_object_new (object_type: color_swatch_get_type (), NULL);
699
700 gdk_rgba_parse (rgba: &swatch->color, spec: color);
701
702 return GTK_WIDGET (swatch);
703}
704
705static GtkWidget *window = NULL;
706
707GtkWidget *
708do_dnd (GtkWidget *do_widget)
709{
710 if (!window)
711 {
712 GtkWidget *button;
713 GtkWidget *sw;
714 GtkWidget *canvas;
715 GtkWidget *box, *box2, *box3;
716 const char *colors[] = {
717 "red", "green", "blue", "magenta", "orange", "gray", "black", "yellow",
718 "white", "gray", "brown", "pink", "cyan", "bisque", "gold", "maroon",
719 "navy", "orchid", "olive", "peru", "salmon", "silver", "wheat",
720 NULL
721 };
722 int i;
723 int x, y;
724 GtkCssProvider *provider;
725
726 button = gtk_color_button_new ();
727 g_object_unref (g_object_ref_sink (button));
728
729 provider = gtk_css_provider_new ();
730 gtk_css_provider_load_from_resource (css_provider: provider, resource_path: "/dnd/dnd.css");
731 gtk_style_context_add_provider_for_display (display: gdk_display_get_default (),
732 GTK_STYLE_PROVIDER (provider),
733 priority: 800);
734 g_object_unref (object: provider);
735
736 window = gtk_window_new ();
737 gtk_window_set_display (GTK_WINDOW (window),
738 display: gtk_widget_get_display (widget: do_widget));
739 gtk_window_set_title (GTK_WINDOW (window), title: "Drag-and-Drop");
740 gtk_window_set_default_size (GTK_WINDOW (window), width: 640, height: 480);
741 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window);
742
743 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
744 gtk_window_set_child (GTK_WINDOW (window), child: box);
745
746 box2 = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
747 gtk_box_append (GTK_BOX (box), child: box2);
748
749 canvas = canvas_new ();
750 gtk_box_append (GTK_BOX (box2), child: canvas);
751
752 n_items = 0;
753
754 x = y = 40;
755 for (i = 0; i < 4; i++)
756 {
757 GtkWidget *item;
758
759 item = canvas_item_new ();
760 gtk_fixed_put (GTK_FIXED (canvas), widget: item, x, y);
761 apply_transform (item: CANVAS_ITEM (ptr: item));
762
763 x += 150;
764 y += 100;
765 }
766
767 gtk_box_append (GTK_BOX (box), child: gtk_separator_new (orientation: GTK_ORIENTATION_HORIZONTAL));
768
769 sw = gtk_scrolled_window_new ();
770 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
771 hscrollbar_policy: GTK_POLICY_AUTOMATIC,
772 vscrollbar_policy: GTK_POLICY_NEVER);
773 gtk_box_append (GTK_BOX (box), child: sw);
774
775 box3 = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
776 gtk_widget_add_css_class (widget: box3, css_class: "linked");
777 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: box3);
778
779 for (i = 0; colors[i]; i++)
780 gtk_box_append (GTK_BOX (box3), child: color_swatch_new (color: colors[i]));
781
782 gtk_box_append (GTK_BOX (box3), child: css_button_new (class: "rainbow1"));
783 gtk_box_append (GTK_BOX (box3), child: css_button_new (class: "rainbow2"));
784 gtk_box_append (GTK_BOX (box3), child: css_button_new (class: "rainbow3"));
785 }
786
787 if (!gtk_widget_get_visible (widget: window))
788 gtk_widget_show (widget: window);
789 else
790 gtk_window_destroy (GTK_WINDOW (window));
791
792 return window;
793}
794

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