From 3a119a3d3a5eb676c1235d7c4e7c451ba595b7ba Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 27 Oct 2020 15:35:34 -0400 Subject: [PATCH 1/3] dropdown: Make search entry shrink We don't want the popup to be wider than the button if we can help it. The search entry does not need to be very wide. --- gtk/ui/gtkdropdown.ui | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gtk/ui/gtkdropdown.ui b/gtk/ui/gtkdropdown.ui index 5623663553..e2dbdaf4d1 100644 --- a/gtk/ui/gtkdropdown.ui +++ b/gtk/ui/gtkdropdown.ui @@ -59,6 +59,8 @@ 0 + 6 + 6 Search… From 25d2efeabefc85fd711ad19b4fecf940bc6cf595 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 27 Oct 2020 12:17:37 -0400 Subject: [PATCH 2/3] dropdown: Add a checkmark to the selected item Make the default factory add a checkmark to the currently selected item (not the hovered item) in the popup. This will unfortunately have to be done in non-default factories too. Related: #3291 --- gtk/gtkdropdown.c | 55 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/gtk/gtkdropdown.c b/gtk/gtkdropdown.c index 80dc7a1ae6..32e5c21c08 100644 --- a/gtk/gtkdropdown.c +++ b/gtk/gtkdropdown.c @@ -44,6 +44,7 @@ #include "gtkbuildable.h" #include "gtkbuilderprivate.h" #include "gtkstringlist.h" +#include "gtkbox.h" /** * SECTION:gtkdropdown @@ -537,11 +538,34 @@ setup_item (GtkSignalListItemFactory *factory, GtkListItem *list_item, gpointer data) { + GtkWidget *box; GtkWidget *label; + GtkWidget *icon; + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); label = gtk_label_new (NULL); gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_list_item_set_child (list_item, label); + gtk_box_append (GTK_BOX (box), label); + icon = gtk_image_new_from_icon_name ("object-select-symbolic"); + gtk_box_append (GTK_BOX (box), icon); + gtk_list_item_set_child (list_item, box); +} + +static void +selected_item_changed (GtkDropDown *self, + GParamSpec *pspec, + GtkListItem *list_item) +{ + GtkWidget *box; + GtkWidget *icon; + + box = gtk_list_item_get_child (list_item); + icon = gtk_widget_get_last_child (box); + + if (gtk_drop_down_get_selected_item (self) == gtk_list_item_get_item (list_item)) + gtk_widget_set_opacity (icon, 1.0); + else + gtk_widget_set_opacity (icon, 0.0); } static void @@ -551,11 +575,15 @@ bind_item (GtkSignalListItemFactory *factory, { GtkDropDown *self = data; gpointer item; + GtkWidget *box; GtkWidget *label; + GtkWidget *icon; GValue value = G_VALUE_INIT; item = gtk_list_item_get_item (list_item); - label = gtk_list_item_get_child (list_item); + box = gtk_list_item_get_child (list_item); + label = gtk_widget_get_first_child (box); + icon = gtk_widget_get_last_child (box); if (self->expression && gtk_expression_evaluate (self->expression, item, &value)) @@ -574,6 +602,28 @@ bind_item (GtkSignalListItemFactory *factory, { g_critical ("Either GtkDropDown:factory or GtkDropDown:expression must be set"); } + + if (gtk_widget_get_ancestor (box, GTK_TYPE_POPOVER) == self->popup) + { + gtk_widget_show (icon); + g_signal_connect (self, "notify::selected-item", + G_CALLBACK (selected_item_changed), list_item); + selected_item_changed (self, NULL, list_item); + } + else + { + gtk_widget_hide (icon); + } +} + +static void +unbind_item (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + gpointer data) +{ + GtkDropDown *self = data; + + g_signal_handlers_disconnect_by_func (self, selected_item_changed, list_item); } static void @@ -585,6 +635,7 @@ set_default_factory (GtkDropDown *self) g_signal_connect (factory, "setup", G_CALLBACK (setup_item), self); g_signal_connect (factory, "bind", G_CALLBACK (bind_item), self); + g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item), self); gtk_drop_down_set_factory (self, factory); From 907fc30fac813a537048067e24a1200a4a0e2ca5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 27 Oct 2020 16:13:44 -0400 Subject: [PATCH 3/3] gtk-demo: Add checkmarks to all dropdown demos This follows the mockups that these demos are copied from. Unfortunately, it has to be implemented for every item factory, so we repeat it here. Fixes: #3291 --- demos/gtk-demo/dropdown.c | 83 ++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/demos/gtk-demo/dropdown.c b/demos/gtk-demo/dropdown.c index dcd43203f5..75b2415167 100644 --- a/demos/gtk-demo/dropdown.c +++ b/demos/gtk-demo/dropdown.c @@ -65,18 +65,22 @@ strings_setup_item_single_line (GtkSignalListItemFactory *factory, GtkListItem *item) { GtkWidget *box, *image, *title; + GtkWidget *checkmark; box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); image = gtk_image_new (); title = gtk_label_new (""); gtk_label_set_xalign (GTK_LABEL (title), 0.0); + checkmark = gtk_image_new_from_icon_name ("object-select-symbolic"); gtk_box_append (GTK_BOX (box), image); gtk_box_append (GTK_BOX (box), title); + gtk_box_append (GTK_BOX (box), checkmark); g_object_set_data (G_OBJECT (item), "title", title); g_object_set_data (G_OBJECT (item), "image", image); + g_object_set_data (G_OBJECT (item), "checkmark", checkmark); gtk_list_item_set_child (item, box); } @@ -86,6 +90,7 @@ strings_setup_item_full (GtkSignalListItemFactory *factory, GtkListItem *item) { GtkWidget *box, *box2, *image, *title, *description; + GtkWidget *checkmark; image = gtk_image_new (); title = gtk_label_new (""); @@ -93,6 +98,7 @@ strings_setup_item_full (GtkSignalListItemFactory *factory, description = gtk_label_new (""); gtk_label_set_xalign (GTK_LABEL (description), 0.0); gtk_widget_add_css_class (description, "dim-label"); + checkmark = gtk_image_new_from_icon_name ("object-select-symbolic"); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); @@ -101,26 +107,48 @@ strings_setup_item_full (GtkSignalListItemFactory *factory, gtk_box_append (GTK_BOX (box), box2); gtk_box_append (GTK_BOX (box2), title); gtk_box_append (GTK_BOX (box2), description); + gtk_box_append (GTK_BOX (box), checkmark); g_object_set_data (G_OBJECT (item), "title", title); g_object_set_data (G_OBJECT (item), "image", image); g_object_set_data (G_OBJECT (item), "description", description); + g_object_set_data (G_OBJECT (item), "checkmark", checkmark); gtk_list_item_set_child (item, box); } static void -strings_bind_item (GtkSignalListItemFactory *factory, - GtkListItem *item) +selected_item_changed (GtkDropDown *dropdown, + GParamSpec *pspec, + GtkListItem *item) { + GtkWidget *checkmark; + + checkmark = g_object_get_data (G_OBJECT (item), "checkmark"); + + if (gtk_drop_down_get_selected_item (dropdown) == gtk_list_item_get_item (item)) + gtk_widget_set_opacity (checkmark, 1.0); + else + gtk_widget_set_opacity (checkmark, 0.0); +} + +static void +strings_bind_item (GtkSignalListItemFactory *factory, + GtkListItem *item, + gpointer data) +{ + GtkDropDown *dropdown = data; GtkWidget *image, *title, *description; + GtkWidget *checkmark; StringHolder *holder; + GtkWidget *popup; holder = gtk_list_item_get_item (item); title = g_object_get_data (G_OBJECT (item), "title"); image = g_object_get_data (G_OBJECT (item), "image"); description = g_object_get_data (G_OBJECT (item), "description"); + checkmark = g_object_get_data (G_OBJECT (item), "checkmark"); gtk_label_set_label (GTK_LABEL (title), holder->title); if (image) @@ -133,19 +161,43 @@ strings_bind_item (GtkSignalListItemFactory *factory, gtk_label_set_label (GTK_LABEL (description), holder->description); gtk_widget_set_visible (description , holder->description != NULL); } + + popup = gtk_widget_get_ancestor (title, GTK_TYPE_POPOVER); + if (popup && gtk_widget_is_ancestor (popup, GTK_WIDGET (dropdown))) + { + gtk_widget_show (checkmark); + g_signal_connect (dropdown, "notify::selected-item", + G_CALLBACK (selected_item_changed), item); + selected_item_changed (dropdown, NULL, item); + } + else + { + gtk_widget_hide (checkmark); + } +} + +static void +strings_unbind_item (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + gpointer data) +{ + GtkDropDown *dropdown = data; + + g_signal_handlers_disconnect_by_func (dropdown, selected_item_changed, list_item); } static GtkListItemFactory * -strings_factory_new (gboolean full) +strings_factory_new (gpointer data, gboolean full) { GtkListItemFactory *factory; factory = gtk_signal_list_item_factory_new (); if (full) - g_signal_connect (factory, "setup", G_CALLBACK (strings_setup_item_full), NULL); + g_signal_connect (factory, "setup", G_CALLBACK (strings_setup_item_full), data); else - g_signal_connect (factory, "setup", G_CALLBACK (strings_setup_item_single_line), NULL); - g_signal_connect (factory, "bind", G_CALLBACK (strings_bind_item), NULL); + g_signal_connect (factory, "setup", G_CALLBACK (strings_setup_item_single_line), data); + g_signal_connect (factory, "bind", G_CALLBACK (strings_bind_item), data); + g_signal_connect (factory, "unbind", G_CALLBACK (strings_unbind_item), data); return factory; } @@ -186,19 +238,22 @@ drop_down_new_from_strings (const char *const *titles, g_return_val_if_fail (descriptions == NULL || g_strv_length ((char **)icons) == g_strv_length ((char **)descriptions), NULL); model = strings_model_new (titles, icons, descriptions); - factory = strings_factory_new (FALSE); + widget = g_object_new (GTK_TYPE_DROP_DOWN, + "model", model, + NULL); + g_object_unref (model); + + factory = strings_factory_new (widget, FALSE); if (icons != NULL || descriptions != NULL) - list_factory = strings_factory_new (TRUE); + list_factory = strings_factory_new (widget, TRUE); else list_factory = NULL; - widget = g_object_new (GTK_TYPE_DROP_DOWN, - "model", model, - "factory", factory, - "list-factory", list_factory, - NULL); + g_object_set (widget, + "factory", factory, + "list-factory", list_factory, + NULL); - g_object_unref (model); g_object_unref (factory); if (list_factory) g_object_unref (list_factory);