1 | /* Lists/Selections |
2 | * |
3 | * The GtkDropDown widget is a modern alternative to GtkComboBox. |
4 | * It uses list models instead of tree models, and the content is |
5 | * displayed using widgets instead of cell renderers. |
6 | * |
7 | * This example also shows a custom widget that can replace |
8 | * GtkEntryCompletion or GtkComboBoxText. It is not currently |
9 | * part of GTK. |
10 | */ |
11 | |
12 | #include <gtk/gtk.h> |
13 | #include "suggestionentry.h" |
14 | |
15 | #define STRING_TYPE_HOLDER (string_holder_get_type ()) |
16 | G_DECLARE_FINAL_TYPE (StringHolder, string_holder, STRING, HOLDER, GObject) |
17 | |
18 | struct _StringHolder { |
19 | GObject parent_instance; |
20 | char *title; |
21 | char *icon; |
22 | char *description; |
23 | }; |
24 | |
25 | G_DEFINE_TYPE (StringHolder, string_holder, G_TYPE_OBJECT); |
26 | |
27 | static void |
28 | string_holder_init (StringHolder *holder) |
29 | { |
30 | } |
31 | |
32 | static void |
33 | string_holder_finalize (GObject *object) |
34 | { |
35 | StringHolder *holder = STRING_HOLDER (ptr: object); |
36 | |
37 | g_free (mem: holder->title); |
38 | g_free (mem: holder->icon); |
39 | g_free (mem: holder->description); |
40 | |
41 | G_OBJECT_CLASS (string_holder_parent_class)->finalize (object); |
42 | } |
43 | |
44 | static void |
45 | string_holder_class_init (StringHolderClass *class) |
46 | { |
47 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
48 | |
49 | object_class->finalize = string_holder_finalize; |
50 | } |
51 | |
52 | static StringHolder * |
53 | string_holder_new (const char *title, const char *icon, const char *description) |
54 | { |
55 | StringHolder *holder = g_object_new (STRING_TYPE_HOLDER, NULL); |
56 | holder->title = g_strdup (str: title); |
57 | holder->icon = g_strdup (str: icon); |
58 | holder->description = g_strdup (str: description); |
59 | return holder; |
60 | } |
61 | |
62 | static void |
63 | strings_setup_item_single_line (GtkSignalListItemFactory *factory, |
64 | GtkListItem *item) |
65 | { |
66 | GtkWidget *box, *image, *title; |
67 | GtkWidget *checkmark; |
68 | |
69 | box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 10); |
70 | |
71 | image = gtk_image_new (); |
72 | title = gtk_label_new (str: "" ); |
73 | gtk_label_set_xalign (GTK_LABEL (title), xalign: 0.0); |
74 | checkmark = gtk_image_new_from_icon_name (icon_name: "object-select-symbolic" ); |
75 | |
76 | gtk_box_append (GTK_BOX (box), child: image); |
77 | gtk_box_append (GTK_BOX (box), child: title); |
78 | gtk_box_append (GTK_BOX (box), child: checkmark); |
79 | |
80 | g_object_set_data (G_OBJECT (item), key: "title" , data: title); |
81 | g_object_set_data (G_OBJECT (item), key: "image" , data: image); |
82 | g_object_set_data (G_OBJECT (item), key: "checkmark" , data: checkmark); |
83 | |
84 | gtk_list_item_set_child (self: item, child: box); |
85 | } |
86 | |
87 | static void |
88 | strings_setup_item_full (GtkSignalListItemFactory *factory, |
89 | GtkListItem *item) |
90 | { |
91 | GtkWidget *box, *box2, *image, *title, *description; |
92 | GtkWidget *checkmark; |
93 | |
94 | image = gtk_image_new (); |
95 | title = gtk_label_new (str: "" ); |
96 | gtk_label_set_xalign (GTK_LABEL (title), xalign: 0.0); |
97 | description = gtk_label_new (str: "" ); |
98 | gtk_label_set_xalign (GTK_LABEL (description), xalign: 0.0); |
99 | gtk_widget_add_css_class (widget: description, css_class: "dim-label" ); |
100 | checkmark = gtk_image_new_from_icon_name (icon_name: "object-select-symbolic" ); |
101 | |
102 | box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 10); |
103 | box2 = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 2); |
104 | |
105 | gtk_box_append (GTK_BOX (box), child: image); |
106 | gtk_box_append (GTK_BOX (box), child: box2); |
107 | gtk_box_append (GTK_BOX (box2), child: title); |
108 | gtk_box_append (GTK_BOX (box2), child: description); |
109 | gtk_box_append (GTK_BOX (box), child: checkmark); |
110 | |
111 | g_object_set_data (G_OBJECT (item), key: "title" , data: title); |
112 | g_object_set_data (G_OBJECT (item), key: "image" , data: image); |
113 | g_object_set_data (G_OBJECT (item), key: "description" , data: description); |
114 | g_object_set_data (G_OBJECT (item), key: "checkmark" , data: checkmark); |
115 | |
116 | gtk_list_item_set_child (self: item, child: box); |
117 | } |
118 | |
119 | static void |
120 | selected_item_changed (GtkDropDown *dropdown, |
121 | GParamSpec *pspec, |
122 | GtkListItem *item) |
123 | { |
124 | GtkWidget *checkmark; |
125 | |
126 | checkmark = g_object_get_data (G_OBJECT (item), key: "checkmark" ); |
127 | |
128 | if (gtk_drop_down_get_selected_item (self: dropdown) == gtk_list_item_get_item (self: item)) |
129 | gtk_widget_set_opacity (widget: checkmark, opacity: 1.0); |
130 | else |
131 | gtk_widget_set_opacity (widget: checkmark, opacity: 0.0); |
132 | } |
133 | |
134 | static void |
135 | strings_bind_item (GtkSignalListItemFactory *factory, |
136 | GtkListItem *item, |
137 | gpointer data) |
138 | { |
139 | GtkDropDown *dropdown = data; |
140 | GtkWidget *image, *title, *description; |
141 | GtkWidget *checkmark; |
142 | StringHolder *holder; |
143 | GtkWidget *; |
144 | |
145 | holder = gtk_list_item_get_item (self: item); |
146 | |
147 | title = g_object_get_data (G_OBJECT (item), key: "title" ); |
148 | image = g_object_get_data (G_OBJECT (item), key: "image" ); |
149 | description = g_object_get_data (G_OBJECT (item), key: "description" ); |
150 | checkmark = g_object_get_data (G_OBJECT (item), key: "checkmark" ); |
151 | |
152 | gtk_label_set_label (GTK_LABEL (title), str: holder->title); |
153 | if (image) |
154 | { |
155 | gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name: holder->icon); |
156 | gtk_widget_set_visible (widget: image, visible: holder->icon != NULL); |
157 | } |
158 | if (description) |
159 | { |
160 | gtk_label_set_label (GTK_LABEL (description), str: holder->description); |
161 | gtk_widget_set_visible (widget: description , visible: holder->description != NULL); |
162 | } |
163 | |
164 | popup = gtk_widget_get_ancestor (widget: title, GTK_TYPE_POPOVER); |
165 | if (popup && gtk_widget_is_ancestor (widget: popup, GTK_WIDGET (dropdown))) |
166 | { |
167 | gtk_widget_show (widget: checkmark); |
168 | g_signal_connect (dropdown, "notify::selected-item" , |
169 | G_CALLBACK (selected_item_changed), item); |
170 | selected_item_changed (dropdown, NULL, item); |
171 | } |
172 | else |
173 | { |
174 | gtk_widget_hide (widget: checkmark); |
175 | } |
176 | } |
177 | |
178 | static void |
179 | strings_unbind_item (GtkSignalListItemFactory *factory, |
180 | GtkListItem *list_item, |
181 | gpointer data) |
182 | { |
183 | GtkDropDown *dropdown = data; |
184 | |
185 | g_signal_handlers_disconnect_by_func (dropdown, selected_item_changed, list_item); |
186 | } |
187 | |
188 | static GtkListItemFactory * |
189 | strings_factory_new (gpointer data, gboolean full) |
190 | { |
191 | GtkListItemFactory *factory; |
192 | |
193 | factory = gtk_signal_list_item_factory_new (); |
194 | if (full) |
195 | g_signal_connect (factory, "setup" , G_CALLBACK (strings_setup_item_full), data); |
196 | else |
197 | g_signal_connect (factory, "setup" , G_CALLBACK (strings_setup_item_single_line), data); |
198 | g_signal_connect (factory, "bind" , G_CALLBACK (strings_bind_item), data); |
199 | g_signal_connect (factory, "unbind" , G_CALLBACK (strings_unbind_item), data); |
200 | |
201 | return factory; |
202 | } |
203 | |
204 | static GListModel * |
205 | strings_model_new (const char *const *titles, |
206 | const char *const *icons, |
207 | const char *const *descriptions) |
208 | { |
209 | GListStore *store; |
210 | int i; |
211 | |
212 | store = g_list_store_new (STRING_TYPE_HOLDER); |
213 | for (i = 0; titles[i]; i++) |
214 | { |
215 | StringHolder *holder = string_holder_new (title: titles[i], |
216 | icon: icons ? icons[i] : NULL, |
217 | description: descriptions ? descriptions[i] : NULL); |
218 | g_list_store_append (store, item: holder); |
219 | g_object_unref (object: holder); |
220 | } |
221 | |
222 | return G_LIST_MODEL (ptr: store); |
223 | } |
224 | |
225 | static GtkWidget * |
226 | drop_down_new_from_strings (const char *const *titles, |
227 | const char *const *icons, |
228 | const char *const *descriptions) |
229 | { |
230 | GtkWidget *widget; |
231 | GListModel *model; |
232 | GtkListItemFactory *factory; |
233 | GtkListItemFactory *list_factory; |
234 | |
235 | g_return_val_if_fail (titles != NULL, NULL); |
236 | g_return_val_if_fail (icons == NULL || g_strv_length ((char **)icons) == g_strv_length ((char **)titles), NULL); |
237 | g_return_val_if_fail (descriptions == NULL || g_strv_length ((char **)icons) == g_strv_length ((char **)descriptions), NULL); |
238 | |
239 | model = strings_model_new (titles, icons, descriptions); |
240 | widget = g_object_new (GTK_TYPE_DROP_DOWN, |
241 | first_property_name: "model" , model, |
242 | NULL); |
243 | g_object_unref (object: model); |
244 | |
245 | factory = strings_factory_new (data: widget, FALSE); |
246 | if (icons != NULL || descriptions != NULL) |
247 | list_factory = strings_factory_new (data: widget, TRUE); |
248 | else |
249 | list_factory = NULL; |
250 | |
251 | g_object_set (object: widget, |
252 | first_property_name: "factory" , factory, |
253 | "list-factory" , list_factory, |
254 | NULL); |
255 | |
256 | g_object_unref (object: factory); |
257 | if (list_factory) |
258 | g_object_unref (object: list_factory); |
259 | |
260 | return widget; |
261 | } |
262 | |
263 | static char * |
264 | get_family_name (gpointer item) |
265 | { |
266 | return g_strdup (str: pango_font_family_get_name (PANGO_FONT_FAMILY (item))); |
267 | } |
268 | |
269 | static char * |
270 | get_title (gpointer item) |
271 | { |
272 | return g_strdup (str: STRING_HOLDER (ptr: item)->title); |
273 | } |
274 | |
275 | static char * |
276 | get_file_name (gpointer item) |
277 | { |
278 | return g_strdup (str: g_file_info_get_display_name (G_FILE_INFO (item))); |
279 | } |
280 | |
281 | static void |
282 | setup_item (GtkSignalListItemFactory *factory, |
283 | GtkListItem *item) |
284 | { |
285 | GtkWidget *box; |
286 | GtkWidget *icon; |
287 | GtkWidget *label; |
288 | |
289 | box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 10); |
290 | icon = gtk_image_new (); |
291 | label = gtk_label_new (str: "" ); |
292 | gtk_label_set_xalign (GTK_LABEL (label), xalign: 0); |
293 | gtk_box_append (GTK_BOX (box), child: icon); |
294 | gtk_box_append (GTK_BOX (box), child: label); |
295 | gtk_list_item_set_child (self: item, child: box); |
296 | } |
297 | |
298 | static void |
299 | bind_item (GtkSignalListItemFactory *factory, |
300 | GtkListItem *item) |
301 | { |
302 | MatchObject *match = MATCH_OBJECT (gtk_list_item_get_item (item)); |
303 | GFileInfo *info = G_FILE_INFO (match_object_get_item (match)); |
304 | GtkWidget *box = gtk_list_item_get_child (self: item); |
305 | GtkWidget *icon = gtk_widget_get_first_child (widget: box); |
306 | GtkWidget *label = gtk_widget_get_last_child (widget: box); |
307 | |
308 | gtk_image_set_from_gicon (GTK_IMAGE (icon), icon: g_file_info_get_icon (info)); |
309 | gtk_label_set_label (GTK_LABEL (label), str: g_file_info_get_display_name (info)); |
310 | } |
311 | |
312 | static void |
313 | setup_highlight_item (GtkSignalListItemFactory *factory, |
314 | GtkListItem *item) |
315 | { |
316 | GtkWidget *label; |
317 | |
318 | label = gtk_label_new (str: "" ); |
319 | gtk_label_set_xalign (GTK_LABEL (label), xalign: 0); |
320 | gtk_list_item_set_child (self: item, child: label); |
321 | } |
322 | |
323 | static void |
324 | bind_highlight_item (GtkSignalListItemFactory *factory, |
325 | GtkListItem *item) |
326 | { |
327 | MatchObject *obj; |
328 | GtkWidget *label; |
329 | PangoAttrList *attrs; |
330 | PangoAttribute *attr; |
331 | const char *str; |
332 | |
333 | obj = MATCH_OBJECT (gtk_list_item_get_item (item)); |
334 | label = gtk_list_item_get_child (self: item); |
335 | |
336 | str = match_object_get_string (object: obj); |
337 | |
338 | gtk_label_set_label (GTK_LABEL (label), str); |
339 | attrs = pango_attr_list_new (); |
340 | attr = pango_attr_weight_new (weight: PANGO_WEIGHT_BOLD); |
341 | attr->start_index = match_object_get_match_start (object: obj); |
342 | attr->end_index = match_object_get_match_end (object: obj); |
343 | pango_attr_list_insert (list: attrs, attr); |
344 | gtk_label_set_attributes (GTK_LABEL (label), attrs); |
345 | pango_attr_list_unref (list: attrs); |
346 | } |
347 | |
348 | static void |
349 | match_func (MatchObject *obj, |
350 | const char *search, |
351 | gpointer user_data) |
352 | { |
353 | char *tmp1, *tmp2; |
354 | char *p; |
355 | |
356 | tmp1 = g_utf8_normalize (str: match_object_get_string (object: obj), len: -1, mode: G_NORMALIZE_ALL); |
357 | tmp2 = g_utf8_normalize (str: search, len: -1, mode: G_NORMALIZE_ALL); |
358 | |
359 | if ((p = strstr (haystack: tmp1, needle: tmp2)) != NULL) |
360 | match_object_set_match (object: obj, |
361 | start: p - tmp1, |
362 | end: (p - tmp1) + g_utf8_strlen (p: search, max: -1), |
363 | score: 1); |
364 | else |
365 | match_object_set_match (object: obj, start: 0, end: 0, score: 0); |
366 | |
367 | g_free (mem: tmp1); |
368 | g_free (mem: tmp2); |
369 | } |
370 | |
371 | GtkWidget * |
372 | do_dropdown (GtkWidget *do_widget) |
373 | { |
374 | static GtkWidget *window = NULL; |
375 | GtkWidget *button, *box, *spin, *check, *hbox, *label, *entry; |
376 | GListModel *model; |
377 | GtkExpression *expression; |
378 | GtkListItemFactory *factory; |
379 | const char * const times[] = { "1 minute" , "2 minutes" , "5 minutes" , "20 minutes" , NULL }; |
380 | const char * const many_times[] = { |
381 | "1 minute" , "2 minutes" , "5 minutes" , "10 minutes" , "15 minutes" , "20 minutes" , |
382 | "25 minutes" , "30 minutes" , "35 minutes" , "40 minutes" , "45 minutes" , "50 minutes" , |
383 | "55 minutes" , "1 hour" , "2 hours" , "3 hours" , "5 hours" , "6 hours" , "7 hours" , |
384 | "8 hours" , "9 hours" , "10 hours" , "11 hours" , "12 hours" , NULL |
385 | }; |
386 | const char * const device_titles[] = { "Digital Output" , "Headphones" , "Digital Output" , "Analog Output" , NULL }; |
387 | const char * const device_icons[] = { "audio-card-symbolic" , "audio-headphones-symbolic" , "audio-card-symbolic" , "audio-card-symbolic" , NULL }; |
388 | const char * const device_descriptions[] = { |
389 | "Built-in Audio" , "Built-in audio" , "Thinkpad Tunderbolt 3 Dock USB Audio" , "Thinkpad Tunderbolt 3 Dock USB Audio" , NULL |
390 | }; |
391 | char *cwd; |
392 | GFile *file; |
393 | GListModel *dir; |
394 | GtkStringList *strings; |
395 | |
396 | if (!window) |
397 | { |
398 | window = gtk_window_new (); |
399 | gtk_window_set_display (GTK_WINDOW (window), |
400 | display: gtk_widget_get_display (widget: do_widget)); |
401 | gtk_window_set_title (GTK_WINDOW (window), title: "Selections" ); |
402 | gtk_window_set_resizable (GTK_WINDOW (window), FALSE); |
403 | g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window); |
404 | |
405 | hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 20); |
406 | |
407 | gtk_widget_set_margin_start (widget: hbox, margin: 20); |
408 | gtk_widget_set_margin_end (widget: hbox, margin: 20); |
409 | gtk_widget_set_margin_top (widget: hbox, margin: 20); |
410 | gtk_widget_set_margin_bottom (widget: hbox, margin: 20); |
411 | gtk_window_set_child (GTK_WINDOW (window), child: hbox); |
412 | |
413 | box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 10); |
414 | gtk_box_append (GTK_BOX (hbox), child: box); |
415 | |
416 | label = gtk_label_new (str: "Dropdowns" ); |
417 | gtk_widget_add_css_class (widget: label, css_class: "title-4" ); |
418 | gtk_box_append (GTK_BOX (box), child: label); |
419 | |
420 | /* A basic dropdown */ |
421 | button = drop_down_new_from_strings (titles: times, NULL, NULL); |
422 | gtk_box_append (GTK_BOX (box), child: button); |
423 | |
424 | /* A dropdown using an expression to obtain strings */ |
425 | button = drop_down_new_from_strings (titles: many_times, NULL, NULL); |
426 | gtk_drop_down_set_enable_search (self: GTK_DROP_DOWN (ptr: button), TRUE); |
427 | expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL, |
428 | n_params: 0, NULL, |
429 | callback_func: (GCallback)get_title, |
430 | NULL, NULL); |
431 | gtk_drop_down_set_expression (self: GTK_DROP_DOWN (ptr: button), expression); |
432 | gtk_expression_unref (self: expression); |
433 | gtk_box_append (GTK_BOX (box), child: button); |
434 | |
435 | button = gtk_drop_down_new (NULL, NULL); |
436 | |
437 | model = G_LIST_MODEL (ptr: pango_cairo_font_map_get_default ()); |
438 | gtk_drop_down_set_model (self: GTK_DROP_DOWN (ptr: button), model); |
439 | gtk_drop_down_set_selected (self: GTK_DROP_DOWN (ptr: button), position: 0); |
440 | |
441 | expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL, |
442 | n_params: 0, NULL, |
443 | callback_func: (GCallback)get_family_name, |
444 | NULL, NULL); |
445 | gtk_drop_down_set_expression (self: GTK_DROP_DOWN (ptr: button), expression); |
446 | gtk_expression_unref (self: expression); |
447 | gtk_box_append (GTK_BOX (box), child: button); |
448 | |
449 | spin = gtk_spin_button_new_with_range (min: -1, max: g_list_model_get_n_items (list: G_LIST_MODEL (ptr: model)), step: 1); |
450 | gtk_widget_set_halign (widget: spin, align: GTK_ALIGN_START); |
451 | gtk_widget_set_margin_start (widget: spin, margin: 20); |
452 | g_object_bind_property (source: button, source_property: "selected" , target: spin, target_property: "value" , flags: G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); |
453 | gtk_box_append (GTK_BOX (box), child: spin); |
454 | |
455 | check = gtk_check_button_new_with_label (label: "Enable search" ); |
456 | gtk_widget_set_margin_start (widget: check, margin: 20); |
457 | g_object_bind_property (source: button, source_property: "enable-search" , target: check, target_property: "active" , flags: G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); |
458 | gtk_box_append (GTK_BOX (box), child: check); |
459 | |
460 | g_object_unref (object: model); |
461 | |
462 | /* A dropdown with a separate list factory */ |
463 | button = drop_down_new_from_strings (titles: device_titles, icons: device_icons, descriptions: device_descriptions); |
464 | gtk_box_append (GTK_BOX (box), child: button); |
465 | |
466 | gtk_box_append (GTK_BOX (hbox), child: gtk_separator_new (orientation: GTK_ORIENTATION_VERTICAL)); |
467 | |
468 | box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 10); |
469 | gtk_box_append (GTK_BOX (hbox), child: box); |
470 | |
471 | label = gtk_label_new (str: "Suggestions" ); |
472 | gtk_widget_add_css_class (widget: label, css_class: "title-4" ); |
473 | gtk_box_append (GTK_BOX (box), child: label); |
474 | |
475 | /* A basic suggestion entry */ |
476 | entry = suggestion_entry_new (); |
477 | g_object_set (object: entry, first_property_name: "placeholder-text" , "Words with T or G…" , NULL); |
478 | strings = gtk_string_list_new (strings: (const char *[]){ |
479 | "GNOME" , |
480 | "gnominious" , |
481 | "Gnomonic projection" , |
482 | "total" , |
483 | "totally" , |
484 | "toto" , |
485 | "tottery" , |
486 | "totterer" , |
487 | "Totten trust" , |
488 | "totipotent" , |
489 | "totipotency" , |
490 | "totemism" , |
491 | "totem pole" , |
492 | "Totara" , |
493 | "totalizer" , |
494 | "totalizator" , |
495 | "totalitarianism" , |
496 | "total parenteral nutrition" , |
497 | "total hysterectomy" , |
498 | "total eclipse" , |
499 | "Totipresence" , |
500 | "Totipalmi" , |
501 | "Tomboy" , |
502 | "zombie" , |
503 | NULL}); |
504 | suggestion_entry_set_model (SUGGESTION_ENTRY (entry), model: G_LIST_MODEL (ptr: strings)); |
505 | g_object_unref (object: strings); |
506 | |
507 | gtk_box_append (GTK_BOX (box), child: entry); |
508 | |
509 | /* A suggestion entry using a custom model, and no filtering */ |
510 | entry = suggestion_entry_new (); |
511 | |
512 | cwd = g_get_current_dir (); |
513 | file = g_file_new_for_path (path: cwd); |
514 | dir = G_LIST_MODEL (ptr: gtk_directory_list_new (attributes: "standard::display-name,standard::content-type,standard::icon,standard::size" , file)); |
515 | suggestion_entry_set_model (SUGGESTION_ENTRY (entry), model: dir); |
516 | g_object_unref (object: dir); |
517 | g_object_unref (object: file); |
518 | g_free (mem: cwd); |
519 | |
520 | expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL, |
521 | n_params: 0, NULL, |
522 | callback_func: (GCallback)get_file_name, |
523 | NULL, NULL); |
524 | suggestion_entry_set_expression (SUGGESTION_ENTRY (entry), expression); |
525 | gtk_expression_unref (self: expression); |
526 | |
527 | factory = gtk_signal_list_item_factory_new (); |
528 | g_signal_connect (factory, "setup" , G_CALLBACK (setup_item), NULL); |
529 | g_signal_connect (factory, "bind" , G_CALLBACK (bind_item), NULL); |
530 | |
531 | suggestion_entry_set_factory (SUGGESTION_ENTRY (entry), factory); |
532 | g_object_unref (object: factory); |
533 | |
534 | suggestion_entry_set_use_filter (SUGGESTION_ENTRY (entry), FALSE); |
535 | suggestion_entry_set_show_arrow (SUGGESTION_ENTRY (entry), TRUE); |
536 | |
537 | gtk_box_append (GTK_BOX (box), child: entry); |
538 | |
539 | /* A suggestion entry with match highlighting */ |
540 | entry = suggestion_entry_new (); |
541 | g_object_set (object: entry, first_property_name: "placeholder-text" , "Destination" , NULL); |
542 | |
543 | strings = gtk_string_list_new (strings: (const char *[]){ |
544 | "app-mockups" , |
545 | "settings-mockups" , |
546 | "os-mockups" , |
547 | "software-mockups" , |
548 | "mocktails" , |
549 | NULL}); |
550 | suggestion_entry_set_model (SUGGESTION_ENTRY (entry), model: G_LIST_MODEL (ptr: strings)); |
551 | g_object_unref (object: strings); |
552 | |
553 | gtk_box_append (GTK_BOX (box), child: entry); |
554 | |
555 | suggestion_entry_set_match_func (SUGGESTION_ENTRY (entry), func: match_func, NULL, NULL); |
556 | |
557 | factory = gtk_signal_list_item_factory_new (); |
558 | g_signal_connect (factory, "setup" , G_CALLBACK (setup_highlight_item), NULL); |
559 | g_signal_connect (factory, "bind" , G_CALLBACK (bind_highlight_item), NULL); |
560 | suggestion_entry_set_factory (SUGGESTION_ENTRY (entry), factory); |
561 | g_object_unref (object: factory); |
562 | |
563 | } |
564 | |
565 | if (!gtk_widget_get_visible (widget: window)) |
566 | gtk_widget_show (widget: window); |
567 | else |
568 | gtk_window_destroy (GTK_WINDOW (window)); |
569 | |
570 | return window; |
571 | } |
572 | |