1 | #include <gtk/gtk.h> |
2 | |
3 | #define TEST_TYPE_OBJECT (test_object_get_type ()) |
4 | G_DECLARE_FINAL_TYPE (TestObject, test_object, TEST, OBJECT, GObject) |
5 | |
6 | struct _TestObject { |
7 | GObject parent_instance; |
8 | char *string; |
9 | guint number; |
10 | gboolean allow_children; |
11 | }; |
12 | |
13 | enum { |
14 | PROP_STRING = 1, |
15 | PROP_NUMBER, |
16 | PROP_ALLOW_CHILDREN, |
17 | PROP_NUM_PROPERTIES |
18 | }; |
19 | |
20 | G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT); |
21 | |
22 | static void |
23 | test_object_init (TestObject *obj) |
24 | { |
25 | } |
26 | |
27 | static void |
28 | test_object_finalize (GObject *object) |
29 | { |
30 | TestObject *obj = TEST_OBJECT (ptr: object); |
31 | |
32 | g_free (mem: obj->string); |
33 | |
34 | G_OBJECT_CLASS (test_object_parent_class)->finalize (object); |
35 | } |
36 | |
37 | static void |
38 | test_object_set_property (GObject *object, |
39 | guint property_id, |
40 | const GValue *value, |
41 | GParamSpec *pspec) |
42 | { |
43 | TestObject *obj = TEST_OBJECT (ptr: object); |
44 | |
45 | switch (property_id) |
46 | { |
47 | case PROP_STRING: |
48 | g_free (mem: obj->string); |
49 | obj->string = g_value_dup_string (value); |
50 | break; |
51 | |
52 | case PROP_NUMBER: |
53 | obj->number = g_value_get_uint (value); |
54 | break; |
55 | |
56 | case PROP_ALLOW_CHILDREN: |
57 | obj->allow_children = g_value_get_boolean (value); |
58 | break; |
59 | |
60 | default: |
61 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
62 | break; |
63 | } |
64 | } |
65 | |
66 | static void |
67 | test_object_get_property (GObject *object, |
68 | guint property_id, |
69 | GValue *value, |
70 | GParamSpec *pspec) |
71 | { |
72 | TestObject *obj = TEST_OBJECT (ptr: object); |
73 | |
74 | switch (property_id) |
75 | { |
76 | case PROP_STRING: |
77 | g_value_set_string (value, v_string: obj->string); |
78 | break; |
79 | |
80 | case PROP_NUMBER: |
81 | g_value_set_uint (value, v_uint: obj->number); |
82 | break; |
83 | |
84 | case PROP_ALLOW_CHILDREN: |
85 | g_value_set_boolean (value, v_boolean: obj->allow_children); |
86 | break; |
87 | |
88 | default: |
89 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
90 | break; |
91 | } |
92 | } |
93 | |
94 | static void |
95 | test_object_class_init (TestObjectClass *class) |
96 | { |
97 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
98 | |
99 | object_class->finalize = test_object_finalize; |
100 | object_class->set_property = test_object_set_property; |
101 | object_class->get_property = test_object_get_property; |
102 | |
103 | g_object_class_install_property (oclass: object_class, property_id: PROP_STRING, |
104 | pspec: g_param_spec_string (name: "string" , nick: "String" , blurb: "String" , |
105 | NULL, |
106 | flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
107 | |
108 | g_object_class_install_property (oclass: object_class, property_id: PROP_NUMBER, |
109 | pspec: g_param_spec_uint (name: "number" , nick: "Number" , blurb: "Number" , |
110 | minimum: 0, G_MAXUINT, default_value: 0, |
111 | flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
112 | |
113 | g_object_class_install_property (oclass: object_class, property_id: PROP_ALLOW_CHILDREN, |
114 | pspec: g_param_spec_boolean (name: "allow-children" , nick: "Allow children" , blurb: "Allow children" , |
115 | FALSE, |
116 | flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
117 | } |
118 | |
119 | static TestObject * |
120 | test_object_new (const char *string, |
121 | guint number, |
122 | gboolean allow_children) |
123 | { |
124 | return g_object_new (TEST_TYPE_OBJECT, |
125 | first_property_name: "string" , string, |
126 | "number" , number, |
127 | "allow-children" , allow_children, |
128 | NULL); |
129 | } |
130 | |
131 | static const char * |
132 | test_object_get_string (TestObject *obj) |
133 | { |
134 | return obj->string; |
135 | } |
136 | |
137 | static guint |
138 | test_object_get_number (TestObject *obj) |
139 | { |
140 | return obj->number; |
141 | } |
142 | |
143 | static gboolean |
144 | test_object_get_allow_children (TestObject *obj) |
145 | { |
146 | return obj->allow_children; |
147 | } |
148 | |
149 | /* * * */ |
150 | |
151 | static GListModel * |
152 | create_model (guint base, |
153 | guint n, |
154 | guint increment, |
155 | gboolean allow_children) |
156 | { |
157 | GListStore *store; |
158 | guint i; |
159 | |
160 | store = g_list_store_new (TEST_TYPE_OBJECT); |
161 | for (i = 0; i < n; i++) |
162 | { |
163 | char *string; |
164 | guint number; |
165 | TestObject *obj; |
166 | |
167 | number = base + i * increment; |
168 | string = g_strdup_printf (format: "%u" , number); |
169 | obj = test_object_new (string, number, allow_children); |
170 | g_list_store_append (store, item: obj); |
171 | g_object_unref (object: obj); |
172 | g_free (mem: string); |
173 | } |
174 | |
175 | return G_LIST_MODEL (ptr: store); |
176 | } |
177 | |
178 | static GListModel * |
179 | create_child_model (gpointer item, |
180 | gpointer user_data) |
181 | { |
182 | guint size = GPOINTER_TO_UINT (user_data); |
183 | guint base = test_object_get_number (obj: TEST_OBJECT (ptr: item)); |
184 | |
185 | if (test_object_get_allow_children (obj: TEST_OBJECT (ptr: item))) |
186 | return create_model (base, n: size, increment: 1, FALSE); |
187 | else |
188 | return NULL; |
189 | } |
190 | |
191 | static GListModel * |
192 | create_tree_model (guint n, guint m) |
193 | { |
194 | return G_LIST_MODEL (ptr: gtk_tree_list_model_new (root: create_model (base: 0, n, increment: m, TRUE), |
195 | FALSE, |
196 | FALSE, |
197 | create_func: create_child_model, |
198 | GUINT_TO_POINTER (m), NULL)); |
199 | } |
200 | |
201 | static void |
202 | setup_item (GtkSignalListItemFactory *factory, |
203 | GtkListItem *item) |
204 | { |
205 | GtkWidget *entry; |
206 | |
207 | entry = gtk_editable_label_new (str: "" ); |
208 | gtk_editable_set_width_chars (GTK_EDITABLE (entry), n_chars: 3); |
209 | gtk_list_item_set_child (self: item, child: entry); |
210 | } |
211 | |
212 | static void |
213 | text_changed (GObject *object, |
214 | GParamSpec *pspec, |
215 | gpointer data) |
216 | { |
217 | const char *text; |
218 | |
219 | text = gtk_editable_get_text (GTK_EDITABLE (object)); |
220 | g_print (format: "text changed to '%s'\n" , text); |
221 | g_object_set (object: data, first_property_name: "string" , text, NULL); |
222 | } |
223 | |
224 | static void |
225 | bind_item (GtkSignalListItemFactory *factory, |
226 | GtkListItem *item) |
227 | { |
228 | TestObject *obj; |
229 | GtkWidget *entry; |
230 | |
231 | obj = gtk_list_item_get_item (self: item); |
232 | entry = gtk_list_item_get_child (self: item); |
233 | gtk_editable_set_text (GTK_EDITABLE (entry), text: test_object_get_string (obj)); |
234 | g_signal_connect (entry, "notify::text" , G_CALLBACK (text_changed), obj); |
235 | } |
236 | |
237 | static void |
238 | unbind_item (GtkSignalListItemFactory *factory, |
239 | GtkListItem *item) |
240 | { |
241 | TestObject *obj; |
242 | GtkWidget *entry; |
243 | |
244 | obj = gtk_list_item_get_item (self: item); |
245 | entry = gtk_list_item_get_child (self: item); |
246 | g_signal_handlers_disconnect_by_func (entry, text_changed, obj); |
247 | } |
248 | |
249 | static void |
250 | setup_tree_item (GtkSignalListItemFactory *factory, |
251 | GtkListItem *item) |
252 | { |
253 | GtkWidget *expander; |
254 | GtkWidget *entry; |
255 | |
256 | entry = gtk_editable_label_new (str: "" ); |
257 | gtk_editable_set_width_chars (GTK_EDITABLE (entry), n_chars: 3); |
258 | expander = gtk_tree_expander_new (); |
259 | gtk_tree_expander_set_child (self: GTK_TREE_EXPANDER (ptr: expander), child: entry); |
260 | gtk_list_item_set_child (self: item, child: expander); |
261 | } |
262 | |
263 | static void |
264 | bind_tree_item (GtkSignalListItemFactory *factory, |
265 | GtkListItem *item) |
266 | { |
267 | GtkTreeListRow *row; |
268 | GtkTreeExpander *expander; |
269 | TestObject *obj; |
270 | GtkWidget *entry; |
271 | |
272 | row = gtk_list_item_get_item (self: item); |
273 | expander = GTK_TREE_EXPANDER (ptr: gtk_list_item_get_child (self: item)); |
274 | gtk_tree_expander_set_list_row (self: expander, list_row: row); |
275 | obj = gtk_tree_list_row_get_item (self: row); |
276 | entry = gtk_tree_expander_get_child (self: expander); |
277 | gtk_editable_set_text (GTK_EDITABLE (entry), text: test_object_get_string (obj)); |
278 | |
279 | g_signal_connect (entry, "notify::text" , G_CALLBACK (text_changed), obj); |
280 | } |
281 | |
282 | static void |
283 | unbind_tree_item (GtkSignalListItemFactory *factory, |
284 | GtkListItem *item) |
285 | { |
286 | GtkTreeListRow *row; |
287 | GtkTreeExpander *expander; |
288 | TestObject *obj; |
289 | GtkWidget *entry; |
290 | |
291 | row = gtk_list_item_get_item (self: item); |
292 | expander = GTK_TREE_EXPANDER (ptr: gtk_list_item_get_child (self: item)); |
293 | obj = gtk_tree_list_row_get_item (self: row); |
294 | entry = gtk_tree_expander_get_child (self: expander); |
295 | g_signal_handlers_disconnect_by_func (entry, text_changed, obj); |
296 | } |
297 | |
298 | int |
299 | main (int argc, char *argv[]) |
300 | { |
301 | GtkWidget *window; |
302 | GtkWidget *box; |
303 | GtkWidget *label; |
304 | GtkWidget *box2; |
305 | GtkWidget *stack; |
306 | GtkWidget *switcher; |
307 | GtkWidget *sw; |
308 | GtkWidget *grid; |
309 | GtkWidget *list; |
310 | GtkWidget *cv; |
311 | GListModel *model; |
312 | GtkListItemFactory *factory; |
313 | |
314 | gtk_init (); |
315 | |
316 | window = gtk_window_new (); |
317 | gtk_window_set_default_size (GTK_WINDOW (window), width: 600, height: 400); |
318 | |
319 | box2 = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 10); |
320 | gtk_window_set_child (GTK_WINDOW (window), child: box2); |
321 | |
322 | switcher = gtk_stack_switcher_new (); |
323 | gtk_widget_set_halign (GTK_WIDGET (switcher), align: GTK_ALIGN_CENTER); |
324 | gtk_widget_set_margin_top (GTK_WIDGET (switcher), margin: 10); |
325 | gtk_widget_set_margin_bottom (GTK_WIDGET (switcher), margin: 10); |
326 | gtk_box_append (GTK_BOX (box2), child: switcher); |
327 | |
328 | box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 10); |
329 | gtk_box_set_homogeneous (GTK_BOX (box), TRUE); |
330 | gtk_box_append (GTK_BOX (box2), child: box); |
331 | |
332 | label = gtk_editable_label_new (str: "Drag me" ); |
333 | gtk_box_append (GTK_BOX (box), child: label); |
334 | |
335 | stack = gtk_stack_new (); |
336 | gtk_widget_set_vexpand (widget: stack, TRUE); |
337 | gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher), GTK_STACK (stack)); |
338 | gtk_box_append (GTK_BOX (box), child: stack); |
339 | |
340 | /* grid */ |
341 | sw = gtk_scrolled_window_new (); |
342 | gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE); |
343 | gtk_stack_add_titled (GTK_STACK (stack), child: sw, name: "grid" , title: "GtkGridView" ); |
344 | |
345 | model = create_model (base: 0, n: 400, increment: 1, FALSE); |
346 | factory = gtk_signal_list_item_factory_new (); |
347 | g_signal_connect (factory, "setup" , G_CALLBACK (setup_item), NULL); |
348 | g_signal_connect (factory, "bind" , G_CALLBACK (bind_item), NULL); |
349 | g_signal_connect (factory, "unbind" , G_CALLBACK (unbind_item), NULL); |
350 | |
351 | grid = gtk_grid_view_new (model: GTK_SELECTION_MODEL (ptr: gtk_single_selection_new (model)), factory); |
352 | gtk_grid_view_set_min_columns (GTK_GRID_VIEW (grid), min_columns: 20); |
353 | gtk_grid_view_set_max_columns (GTK_GRID_VIEW (grid), max_columns: 20); |
354 | |
355 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: grid); |
356 | |
357 | /* list */ |
358 | sw = gtk_scrolled_window_new (); |
359 | gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE); |
360 | gtk_stack_add_titled (GTK_STACK (stack), child: sw, name: "list" , title: "GtkListView" ); |
361 | |
362 | list = gtk_list_view_new (model: GTK_SELECTION_MODEL (ptr: gtk_single_selection_new (model: create_model (base: 0, n: 400, increment: 1, FALSE))), NULL); |
363 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: list); |
364 | |
365 | factory = gtk_signal_list_item_factory_new (); |
366 | g_signal_connect (factory, "setup" , G_CALLBACK (setup_item), NULL); |
367 | g_signal_connect (factory, "bind" , G_CALLBACK (bind_item), NULL); |
368 | g_signal_connect (factory, "unbind" , G_CALLBACK (unbind_item), NULL); |
369 | |
370 | gtk_list_view_set_factory (GTK_LIST_VIEW (list), factory); |
371 | g_object_unref (object: factory); |
372 | |
373 | /* columnview */ |
374 | sw = gtk_scrolled_window_new (); |
375 | gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE); |
376 | gtk_stack_add_titled (GTK_STACK (stack), child: sw, name: "column" , title: "GtkColumnView" ); |
377 | |
378 | cv = gtk_column_view_new (model: GTK_SELECTION_MODEL (ptr: gtk_single_selection_new (model: create_model (base: 0, n: 400, increment: 1, FALSE)))); |
379 | |
380 | for (guint i = 0; i < 20; i++) |
381 | { |
382 | GtkColumnViewColumn *column; |
383 | char *title; |
384 | |
385 | factory = gtk_signal_list_item_factory_new (); |
386 | g_signal_connect (factory, "setup" , G_CALLBACK (setup_item), NULL); |
387 | g_signal_connect (factory, "bind" , G_CALLBACK (bind_item), NULL); |
388 | g_signal_connect (factory, "unbind" , G_CALLBACK (unbind_item), NULL); |
389 | |
390 | title = g_strdup_printf (format: "Column %u" , i); |
391 | column = gtk_column_view_column_new (title, factory); |
392 | gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column); |
393 | g_object_unref (object: column); |
394 | g_free (mem: title); |
395 | } |
396 | |
397 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: cv); |
398 | |
399 | /* tree */ |
400 | sw = gtk_scrolled_window_new (); |
401 | gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE); |
402 | gtk_stack_add_titled (GTK_STACK (stack), child: sw, name: "tree" , title: "Tree" ); |
403 | |
404 | list = gtk_list_view_new (model: GTK_SELECTION_MODEL (ptr: gtk_single_selection_new (model: create_tree_model (n: 20, m: 20))), NULL); |
405 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: list); |
406 | |
407 | factory = gtk_signal_list_item_factory_new (); |
408 | g_signal_connect (factory, "setup" , G_CALLBACK (setup_tree_item), NULL); |
409 | g_signal_connect (factory, "bind" , G_CALLBACK (bind_tree_item), NULL); |
410 | g_signal_connect (factory, "unbind" , G_CALLBACK (unbind_tree_item), NULL); |
411 | |
412 | gtk_list_view_set_factory (GTK_LIST_VIEW (list), factory); |
413 | g_object_unref (object: factory); |
414 | |
415 | gtk_window_present (GTK_WINDOW (window)); |
416 | |
417 | while (g_list_model_get_n_items (list: gtk_window_get_toplevels ()) > 0) |
418 | g_main_context_iteration (NULL, TRUE); |
419 | |
420 | gtk_window_destroy (GTK_WINDOW (window)); |
421 | |
422 | return 0; |
423 | } |
424 | |