diff --git a/data/themes/darktable.css b/data/themes/darktable.css index b6ea975459eb..70a61a387177 100644 --- a/data/themes/darktable.css +++ b/data/themes/darktable.css @@ -882,6 +882,22 @@ menuitem label, margin: 0; } +/*------------------------------------------------ + - Popup menu - + ----------------------------------------------*/ +popover.menu { + padding: 0; +} + +popover.menu modelbutton { + padding: 0.1em 0.5em; +} + +popover.menu modelbutton:hover { + background-color: @selected_bg_color; +} + + tooltip, #range-current { diff --git a/src/gui/gtk.c b/src/gui/gtk.c index 53438e0defcf..bf643c9fb035 100644 --- a/src/gui/gtk.c +++ b/src/gui/gtk.c @@ -4636,6 +4636,21 @@ void dt_gui_dialog_restore_size(GtkDialog *dialog, const char *conf) g_signal_connect(dialog, "configure-event", G_CALLBACK(_resize_dialog), (gpointer)conf); } +GtkWidget *dt_gui_popover_menu_from_model(GtkWidget *parent, GMenu *menu) +{ + GtkWidget *popover_menu; + +#if GTK_CHECK_VERSION(4, 0, 0) + popover_menu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu)); + gtk_widget_set_parent(popover_menu, parent); +#else + popover_menu = gtk_popover_new_from_model(parent, G_MENU_MODEL(menu)); +#endif + + return popover_menu; +} + + // clang-format off // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py // vim: shiftwidth=2 expandtab tabstop=2 cindent diff --git a/src/gui/gtk.h b/src/gui/gtk.h index 8ee46777e981..4d62dd0e2b33 100644 --- a/src/gui/gtk.h +++ b/src/gui/gtk.h @@ -637,6 +637,9 @@ void dt_gui_dialog_restore_size(GtkDialog *dialog, const char *conf); // returns the session type at runtime dt_gui_session_type_t dt_gui_get_session_type(void); +// Popover menu +GtkWidget *dt_gui_popover_menu_from_model(GtkWidget *parent, GMenu *menu); + G_END_DECLS diff --git a/src/libs/collect.c b/src/libs/collect.c index d02689e4510f..705ea3f2e590 100644 --- a/src/libs/collect.c +++ b/src/libs/collect.c @@ -96,6 +96,8 @@ typedef struct dt_lib_collect_t gboolean inited; GtkWidget *history_box; + + GSimpleActionGroup *action_group; } dt_lib_collect_t; typedef struct dt_lib_collect_params_rule_t @@ -2924,9 +2926,19 @@ static gboolean entry_focus_in_callback(GtkWidget *w, return FALSE; } -static void menuitem_mode(GtkMenuItem *menuitem, - dt_lib_collect_rule_t *d) +static void _menuitem_mode(GSimpleAction *action, + GVariant *parameter, + gpointer userdata) { + dt_lib_collect_t *m = (dt_lib_collect_t *)userdata; + + dt_lib_collect_mode_t mode = 0; + int rule_index = 0; + + g_variant_get(parameter, "(ii)", &mode, &rule_index); + + dt_lib_collect_rule_t *d = &m->rule[rule_index]; + // add next row with and operator const int _a = dt_conf_get_int("plugins/lighttable/collect/num_rules"); const int active = CLAMP(_a, 1, MAX_RULES); @@ -2935,8 +2947,6 @@ static void menuitem_mode(GtkMenuItem *menuitem, { char confname[200] = { 0 }; snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", active); - const dt_lib_collect_mode_t mode = - GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "menuitem_mode")); dt_conf_set_int(confname, mode); snprintf(confname, sizeof(confname), "plugins/lighttable/collect/string%1d", active); @@ -2951,17 +2961,25 @@ static void menuitem_mode(GtkMenuItem *menuitem, DT_COLLECTION_PROP_UNDEF, NULL); } -static void menuitem_mode_change(GtkMenuItem *menuitem, - dt_lib_collect_rule_t *d) +static void _menuitem_mode_change(GSimpleAction *action, + GVariant *parameter, + gpointer userdata) { + dt_lib_collect_t *m = (dt_lib_collect_t *)userdata; + + dt_lib_collect_mode_t mode = 0; + int rule_index = 0; + + g_variant_get(parameter, "(ii)", &mode, &rule_index); + + dt_lib_collect_rule_t *d = &m->rule[rule_index]; + // add next row with and operator const int num = d->num + 1; if(num < MAX_RULES && num > 0) { char confname[200] = { 0 }; snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", num); - const dt_lib_collect_mode_t mode = - GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "menuitem_mode")); dt_conf_set_int(confname, mode); } dt_lib_collect_t *c = get_collect(d); @@ -3184,9 +3202,15 @@ static void _metadata_changed(gpointer instance, } } -static void menuitem_clear(GtkMenuItem *menuitem, - dt_lib_collect_rule_t *d) +static void _menuitem_clear(GSimpleAction *simple, + GVariant *parameter, + gpointer userdata) { + dt_lib_collect_t *m = (dt_lib_collect_t*) userdata; + + const int index = g_variant_get_int32(parameter); + dt_lib_collect_rule_t *d = &m->rule[index]; + // remove this row, or if 1st, clear text entry box const int _a = dt_conf_get_int("plugins/lighttable/collect/num_rules"); const int active = CLAMP(_a, 1, MAX_RULES); @@ -3231,72 +3255,58 @@ static void menuitem_clear(GtkMenuItem *menuitem, DT_COLLECTION_PROP_UNDEF, NULL); } -static gboolean popup_button_callback(GtkWidget *widget, +static gboolean popup_button_callback(GtkWidget *button, GdkEventButton *event, dt_lib_collect_rule_t *d) { if(event->button != 1) return FALSE; - GtkWidget *menu = gtk_menu_new(); - GtkWidget *mi; + GMenu *menu = g_menu_new(); const int _a = dt_conf_get_int("plugins/lighttable/collect/num_rules"); const int active = CLAMP(_a, 1, MAX_RULES); - mi = gtk_menu_item_new_with_label(_("clear this rule")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); - g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_clear), d); - + gchar *action; + + action = g_strdup_printf("collect.clear(%d)", d->num); + g_menu_append(menu, _("clear this rule"), action); + g_free(action); + if(d->num == active - 1) { - mi = gtk_menu_item_new_with_label(_("narrow down search")); - g_object_set_data(G_OBJECT(mi), "menuitem_mode", - GINT_TO_POINTER(DT_LIB_COLLECT_MODE_AND)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); - g_signal_connect(G_OBJECT(mi), "activate", - G_CALLBACK(menuitem_mode), d); - - mi = gtk_menu_item_new_with_label(_("add more images")); - g_object_set_data(G_OBJECT(mi), "menuitem_mode", - GINT_TO_POINTER(DT_LIB_COLLECT_MODE_OR)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); - g_signal_connect(G_OBJECT(mi), "activate", - G_CALLBACK(menuitem_mode), d); - - mi = gtk_menu_item_new_with_label(_("exclude images")); - g_object_set_data(G_OBJECT(mi), "menuitem_mode", - GINT_TO_POINTER(DT_LIB_COLLECT_MODE_AND_NOT)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); - g_signal_connect(G_OBJECT(mi), "activate", - G_CALLBACK(menuitem_mode), d); + const char *fmt = "collect.mode((%d,%d))"; + + action = g_strdup_printf(fmt, DT_LIB_COLLECT_MODE_AND, d->num); + g_menu_append(menu, _("narrow down search"), action); + g_free(action); + + action = g_strdup_printf(fmt, DT_LIB_COLLECT_MODE_OR, d->num); + g_menu_append(menu, _("add more images"), action); + g_free(action); + + action = g_strdup_printf(fmt, DT_LIB_COLLECT_MODE_AND_NOT, d->num); + g_menu_append(menu, _("exclude images"), action); + g_free(action); } else if(d->num < active - 1) { - mi = gtk_menu_item_new_with_label(_("change to: and")); - g_object_set_data(G_OBJECT(mi), "menuitem_mode", - GINT_TO_POINTER(DT_LIB_COLLECT_MODE_AND)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); - g_signal_connect(G_OBJECT(mi), "activate", - G_CALLBACK(menuitem_mode_change), d); + const char *fmt = "collect.modechange((%d,%d))"; - mi = gtk_menu_item_new_with_label(_("change to: or")); - g_object_set_data(G_OBJECT(mi), "menuitem_mode", - GINT_TO_POINTER(DT_LIB_COLLECT_MODE_OR)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); - g_signal_connect(G_OBJECT(mi), "activate", - G_CALLBACK(menuitem_mode_change), d); + action = g_strdup_printf(fmt, DT_LIB_COLLECT_MODE_AND, d->num); + g_menu_append(menu, _("change to: and"), action); + g_free(action); - mi = gtk_menu_item_new_with_label(_("change to: except")); - g_object_set_data(G_OBJECT(mi), "menuitem_mode", - GINT_TO_POINTER(DT_LIB_COLLECT_MODE_AND_NOT)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); - g_signal_connect(G_OBJECT(mi), "activate", - G_CALLBACK(menuitem_mode_change), d); - } + action = g_strdup_printf(fmt, DT_LIB_COLLECT_MODE_OR, d->num); + g_menu_append(menu, _("change to: or"), action); + g_free(action); - gtk_widget_show_all(GTK_WIDGET(menu)); + action = g_strdup_printf(fmt, DT_LIB_COLLECT_MODE_AND_NOT, d->num); + g_menu_append(menu, _("change to: except"), action); + g_free(action); + } - gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event); + GtkWidget *popover_menu = dt_gui_popover_menu_from_model(button, menu); + gtk_popover_popup(GTK_POPOVER(popover_menu)); return TRUE; } @@ -3654,6 +3664,17 @@ void gui_init(dt_lib_module_t *self) self->widget = dt_gui_vbox(); dt_gui_add_class(self->widget, "dt_spacing_sw"); + // setup the actions for this module + const GActionEntry entries[] = { + { "clear", _menuitem_clear, "i" }, + { "mode", _menuitem_mode, "(ii)" }, + { "modechange", _menuitem_mode_change, "(ii)" } + }; + + d->action_group = g_simple_action_group_new(); + g_action_map_add_action_entries(G_ACTION_MAP(d->action_group), entries, G_N_ELEMENTS(entries), d); + gtk_widget_insert_action_group(self->widget, "collect", G_ACTION_GROUP(d->action_group)); + d->active_rule = 0; d->nb_rules = 0; d->params = (dt_lib_collect_params_t *)malloc(sizeof(dt_lib_collect_params_t)); @@ -3808,6 +3829,8 @@ void gui_cleanup(dt_lib_module_t *self) g_object_unref(d->listfilter); g_object_unref(d->vmonitor); + g_object_unref(d->action_group); + /* TODO: Make sure we are cleaning up all allocations */ free(self->data);