| 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 | |