1 | #include <gtk/gtk.h> |
2 | |
3 | static GdkContentProvider * |
4 | prepare (GtkDragSource *source, |
5 | double x, |
6 | double y, |
7 | GtkWidget *row) |
8 | { |
9 | return gdk_content_provider_new_typed (GTK_TYPE_LIST_BOX_ROW, row); |
10 | } |
11 | |
12 | static void |
13 | drag_begin (GtkDragSource *source, |
14 | GdkDrag *drag, |
15 | GtkWidget *widget) |
16 | { |
17 | GtkWidget *row; |
18 | GtkAllocation alloc; |
19 | GdkPaintable *paintable; |
20 | double x, y; |
21 | |
22 | row = gtk_widget_get_ancestor (widget, GTK_TYPE_LIST_BOX_ROW); |
23 | gtk_widget_get_allocation (widget: row, allocation: &alloc); |
24 | |
25 | paintable = gtk_widget_paintable_new (widget: row); |
26 | gtk_widget_translate_coordinates (src_widget: widget, dest_widget: row, src_x: 0, src_y: 0, dest_x: &x, dest_y: &y); |
27 | gtk_drag_source_set_icon (source, paintable, hot_x: -x, hot_y: -y); |
28 | |
29 | g_object_unref (object: paintable); |
30 | } |
31 | |
32 | static gboolean |
33 | drag_drop (GtkDropTarget *dest, |
34 | const GValue *value, |
35 | double x, |
36 | double y, |
37 | gpointer data) |
38 | { |
39 | GtkWidget *target = data; |
40 | GtkWidget *source; |
41 | int pos; |
42 | |
43 | source = g_value_get_object (value); |
44 | if (source == NULL) |
45 | return FALSE; |
46 | |
47 | pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (target)); |
48 | if (source == target) |
49 | return FALSE; |
50 | |
51 | g_object_ref (source); |
52 | gtk_box_remove (GTK_BOX (gtk_widget_get_parent (source)), child: source); |
53 | gtk_list_box_insert (GTK_LIST_BOX (gtk_widget_get_parent (target)), child: source, position: pos); |
54 | g_object_unref (object: source); |
55 | |
56 | return TRUE; |
57 | } |
58 | |
59 | static GtkWidget * |
60 | create_row (const char *text) |
61 | { |
62 | GtkWidget *row, *box, *label, *image; |
63 | GtkDragSource *source; |
64 | GtkDropTarget *dest; |
65 | |
66 | row = gtk_list_box_row_new (); |
67 | image = gtk_image_new_from_icon_name (icon_name: "open-menu-symbolic" ); |
68 | box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 10); |
69 | g_object_set (object: box, first_property_name: "margin-start" , 10, "margin-end" , 10, NULL); |
70 | label = gtk_label_new (str: text); |
71 | gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), child: box); |
72 | gtk_widget_set_hexpand (widget: label, TRUE); |
73 | gtk_box_append (GTK_BOX (box), child: label); |
74 | gtk_box_append (GTK_BOX (box), child: image); |
75 | |
76 | source = gtk_drag_source_new (); |
77 | gtk_drag_source_set_actions (source, actions: GDK_ACTION_MOVE); |
78 | g_signal_connect (source, "drag-begin" , G_CALLBACK (drag_begin), image); |
79 | g_signal_connect (source, "prepare" , G_CALLBACK (prepare), row); |
80 | gtk_widget_add_controller (widget: image, GTK_EVENT_CONTROLLER (source)); |
81 | |
82 | dest = gtk_drop_target_new (GTK_TYPE_LIST_BOX_ROW, actions: GDK_ACTION_MOVE); |
83 | g_signal_connect (dest, "drop" , G_CALLBACK (drag_drop), row); |
84 | gtk_widget_add_controller (GTK_WIDGET (row), GTK_EVENT_CONTROLLER (dest)); |
85 | |
86 | return row; |
87 | } |
88 | |
89 | static void |
90 | on_row_activated (GtkListBox *self, |
91 | GtkWidget *child) |
92 | { |
93 | const char *id; |
94 | id = g_object_get_data (G_OBJECT (gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (child))), key: "id" ); |
95 | g_message ("Row activated %p: %s" , child, id); |
96 | } |
97 | |
98 | static void |
99 | on_selected_children_changed (GtkListBox *self) |
100 | { |
101 | g_message ("Selection changed" ); |
102 | } |
103 | |
104 | static void |
105 | selection_mode_changed (GtkComboBox *combo, gpointer data) |
106 | { |
107 | GtkListBox *list = data; |
108 | |
109 | gtk_list_box_set_selection_mode (box: list, mode: gtk_combo_box_get_active (combo_box: combo)); |
110 | } |
111 | |
112 | static const char *css = |
113 | ".during-dnd { " |
114 | " background: white; " |
115 | " border: 1px solid black; " |
116 | "}" ; |
117 | |
118 | int |
119 | main (int argc, char *argv[]) |
120 | { |
121 | GtkWidget *window, *list, *sw, *row; |
122 | GtkWidget *hbox, *vbox, *combo, *button; |
123 | int i; |
124 | char *text; |
125 | GtkCssProvider *provider; |
126 | |
127 | gtk_init (); |
128 | |
129 | provider = gtk_css_provider_new (); |
130 | gtk_css_provider_load_from_data (css_provider: provider, data: css, length: -1); |
131 | gtk_style_context_add_provider_for_display (display: gdk_display_get_default (), GTK_STYLE_PROVIDER (provider), priority: 800); |
132 | window = gtk_window_new (); |
133 | gtk_window_set_default_size (GTK_WINDOW (window), width: -1, height: 300); |
134 | |
135 | hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 12); |
136 | gtk_window_set_child (GTK_WINDOW (window), child: hbox); |
137 | vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 6); |
138 | gtk_widget_set_margin_start (widget: vbox, margin: 12); |
139 | gtk_widget_set_margin_end (widget: vbox, margin: 12); |
140 | gtk_widget_set_margin_top (widget: vbox, margin: 12); |
141 | gtk_widget_set_margin_bottom (widget: vbox, margin: 12); |
142 | gtk_box_append (GTK_BOX (hbox), child: vbox); |
143 | |
144 | list = gtk_list_box_new (); |
145 | gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), mode: GTK_SELECTION_NONE); |
146 | |
147 | g_signal_connect (list, "row-activated" , G_CALLBACK (on_row_activated), NULL); |
148 | g_signal_connect (list, "selected-rows-changed" , G_CALLBACK (on_selected_children_changed), NULL); |
149 | |
150 | sw = gtk_scrolled_window_new (); |
151 | gtk_widget_set_hexpand (widget: sw, TRUE); |
152 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), hscrollbar_policy: GTK_POLICY_NEVER, vscrollbar_policy: GTK_POLICY_ALWAYS); |
153 | gtk_box_append (GTK_BOX (hbox), child: sw); |
154 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: list); |
155 | |
156 | button = gtk_check_button_new_with_label (label: "Activate on single click" ); |
157 | g_object_bind_property (source: list, source_property: "activate-on-single-click" , |
158 | target: button, target_property: "active" , |
159 | flags: G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); |
160 | gtk_box_append (GTK_BOX (vbox), child: button); |
161 | |
162 | combo = gtk_combo_box_text_new (); |
163 | gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), text: "None" ); |
164 | gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), text: "Single" ); |
165 | gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), text: "Browse" ); |
166 | gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), text: "Multiple" ); |
167 | g_signal_connect (combo, "changed" , G_CALLBACK (selection_mode_changed), list); |
168 | gtk_box_append (GTK_BOX (vbox), child: combo); |
169 | gtk_combo_box_set_active (GTK_COMBO_BOX (combo), index_: gtk_list_box_get_selection_mode (GTK_LIST_BOX (list))); |
170 | |
171 | for (i = 0; i < 20; i++) |
172 | { |
173 | text = g_strdup_printf (format: "Row %d" , i); |
174 | row = create_row (text); |
175 | gtk_list_box_insert (GTK_LIST_BOX (list), child: row, position: -1); |
176 | } |
177 | |
178 | gtk_widget_show (widget: window); |
179 | |
180 | while (TRUE) |
181 | g_main_context_iteration (NULL, TRUE); |
182 | |
183 | return 0; |
184 | } |
185 | |