[balsa/gtk4: 22/105] toolbars: Port the popup menu to GtkPopover
- From: Peter Bloomfield <peterb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [balsa/gtk4: 22/105] toolbars: Port the popup menu to GtkPopover
- Date: Thu, 24 Sep 2020 19:49:44 +0000 (UTC)
commit a4a108f9f4161336f6f9ea682874ba7bf6f21645
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date: Mon May 18 20:47:18 2020 -0400
toolbars: Port the popup menu to GtkPopover
and refactor the customize-toobars dialog.
configure.ac: Harmonize with master
configure.ac | 2 +-
src/main-window.c | 4 +-
src/toolbar-factory.c | 247 +++++++++++++++++++++++++++-----------------------
src/toolbar-factory.h | 11 +--
src/toolbar-prefs.c | 18 ++--
src/toolbar-prefs.h | 10 +-
6 files changed, 154 insertions(+), 138 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a960ab9b3..602eef569 100644
--- a/configure.ac
+++ b/configure.ac
@@ -235,7 +235,7 @@ libical >= 2.0.0
PKG_CHECK_MODULES(BALSA_AB, [
glib-2.0
- gtk+-3.0 >= 3.22.0
+ gtk+-3.0
gmime-3.0 >= 3.2.6
])
diff --git a/src/main-window.c b/src/main-window.c
index cb501c3b1..34ae5a063 100644
--- a/src/main-window.c
+++ b/src/main-window.c
@@ -918,7 +918,9 @@ toolbars_activated(GSimpleAction * action,
GVariant * parameter,
gpointer user_data)
{
- customize_dialog_cb(user_data, user_data);
+ GtkWindow *window = GTK_WINDOW(user_data);
+
+ balsa_toolbar_customize(window, BALSA_TOOLBAR_TYPE_MAIN_WINDOW);
}
static void
diff --git a/src/toolbar-factory.c b/src/toolbar-factory.c
index 06bb446fe..44d7e132d 100644
--- a/src/toolbar-factory.c
+++ b/src/toolbar-factory.c
@@ -25,6 +25,7 @@
#include <string.h>
#include <glib/gi18n.h>
+#include "application-helpers.h"
#include "balsa-app.h"
#include "balsa-icons.h"
#include "main-window.h"
@@ -454,11 +455,13 @@ tm_set_tool_item_label(GtkToolItem * tool_item, const gchar * stock_id,
static GtkToolbarStyle tm_default_style(void);
-static const struct {
+typedef struct {
const gchar *text;
const gchar *config_name;
GtkToolbarStyle style;
-} tm_toolbar_options[] = {
+} ToolbarOption;
+
+static const ToolbarOption tm_toolbar_options[] = {
{N_("Text Be_low Icons"), "both", GTK_TOOLBAR_BOTH},
{N_("Priority Text Be_side Icons"), "both-horiz", GTK_TOOLBAR_BOTH_HORIZ},
{NULL, "both_horiz", GTK_TOOLBAR_BOTH_HORIZ},
@@ -479,11 +482,14 @@ tm_default_style(void)
if (str) {
guint i;
- for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++)
- if (strcmp(tm_toolbar_options[i].config_name, str) == 0) {
- default_style = tm_toolbar_options[i].style;
+ for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++) {
+ const ToolbarOption *option = &tm_toolbar_options[i];
+
+ if (strcmp(option->config_name, str) == 0) {
+ default_style = option->style;
break;
}
+ }
g_free(str);
}
g_object_unref(settings);
@@ -567,7 +573,8 @@ tm_changed_cb(BalsaToolbarModel * model, GtkWidget * toolbar)
typedef struct {
BalsaToolbarModel *model;
- GtkWidget *menu;
+ GtkWidget *toolbar;
+ GtkWidget *popup_menu;
} toolbar_info;
static void
@@ -577,39 +584,6 @@ tm_toolbar_weak_notify(toolbar_info * info, GtkWidget * toolbar)
g_free(info);
}
-#define BALSA_TOOLBAR_STYLE "balsa-toolbar-style"
-static void
-menu_item_toggled_cb(GtkCheckMenuItem * item, toolbar_info * info)
-{
- if (gtk_check_menu_item_get_active(item)) {
- info->model->style =
- GPOINTER_TO_INT(g_object_get_data
- (G_OBJECT(item), BALSA_TOOLBAR_STYLE));
- balsa_toolbar_model_changed(info->model);
- if (info->menu)
- gtk_menu_shell_deactivate(GTK_MENU_SHELL(info->menu));
- }
-}
-
-/* We want to destroy the popup menu after handling the "toggled"
- * signal; the "deactivate" signal is apparently emitted before
- * "toggled", so we have to use an idle callback. */
-static gboolean
-tm_popup_idle_cb(GtkWidget *menu)
-{
- gtk_widget_destroy(menu);
- return FALSE;
-}
-
-static void
-tm_popup_deactivated_cb(GtkWidget * menu, toolbar_info * info)
-{
- if (info->menu) {
- g_idle_add((GSourceFunc) tm_popup_idle_cb, menu);
- info->menu = NULL;
- }
-}
-
static gchar *
tm_remove_underscore(const gchar * text)
{
@@ -623,6 +597,38 @@ tm_remove_underscore(const gchar * text)
return r;
}
+static void
+tm_set_style_changed(GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ toolbar_info *info = user_data;
+ GtkToolbarStyle style;
+
+ style = g_variant_get_int32(parameter);
+ if (info->model->style != style) {
+ info->model->style = style;
+ balsa_toolbar_model_changed(info->model);
+ }
+
+ if (info->popup_menu != NULL)
+ gtk_popover_popdown(GTK_POPOVER(info->popup_menu));
+
+ g_simple_action_set_state(action, parameter);
+}
+
+static void
+tm_customize_activated(GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ toolbar_info *info = user_data;
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel(info->toolbar);
+ balsa_toolbar_customize(GTK_WINDOW(toplevel), info->model->type);
+}
+
static gboolean
tm_popup_context_menu_cb(GtkWidget * toolbar,
@@ -631,113 +637,130 @@ tm_popup_context_menu_cb(GtkWidget * toolbar,
gint button,
toolbar_info * info)
{
- GtkWidget *menu;
+ GSimpleActionGroup *simple;
+ static const char namespace[] = "toolbar";
+ static const GActionEntry entries[] = {
+ {"set-style", libbalsa_radio_activated, "i", "-1", tm_set_style_changed},
+ {"customize", tm_customize_activated}
+ };
+ GAction *set_style_action;
+ GMenu *menu;
+ GMenu *section;
guint i;
- GSList *group = NULL;
GtkToolbarStyle default_style;
- GdkEvent *event;
+ GtkWidget *popup_menu;
- if (info->menu != NULL)
- return FALSE;
+ simple = g_simple_action_group_new();
+ g_action_map_add_action_entries(G_ACTION_MAP(simple),
+ entries,
+ G_N_ELEMENTS(entries),
+ info);
+ set_style_action = g_action_map_lookup_action(G_ACTION_MAP(simple), "set-style");
+ gtk_widget_insert_action_group(toolbar, namespace, G_ACTION_GROUP(simple));
+ g_object_unref(simple);
- info->menu = menu = gtk_menu_new();
- g_signal_connect(menu, "deactivate",
- G_CALLBACK(tm_popup_deactivated_cb), info);
+ menu = g_menu_new();
/* ... add menu items ... */
+ section = g_menu_new();
+
for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++) {
- GtkWidget *item;
+ const ToolbarOption *option = &tm_toolbar_options[i];
+ GMenuItem *item;
- if (!tm_toolbar_options[i].text)
+ if (option->text == NULL)
continue;
- item =
- gtk_radio_menu_item_new_with_mnemonic(group,
- _(tm_toolbar_options[i].
- text));
- group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
-
- if (tm_toolbar_options[i].style == info->model->style)
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),
- TRUE);
-
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- g_object_set_data(G_OBJECT(item), BALSA_TOOLBAR_STYLE,
- GINT_TO_POINTER(tm_toolbar_options[i].style));
- g_signal_connect(item, "toggled", G_CALLBACK(menu_item_toggled_cb),
- info);
+ item = g_menu_item_new(_(option->text), NULL);
+ g_menu_item_set_action_and_target(item, "set-style", "i", option->style);
+ g_menu_append_item(section, item);
+ g_object_unref(item);
}
+ g_action_change_state(set_style_action, g_variant_new_int32(info->model->style));
+
+ g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
+ g_object_unref(section);
default_style = tm_default_style();
for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++) {
+ const ToolbarOption *option = &tm_toolbar_options[i];
- if (!tm_toolbar_options[i].text)
+ if (option->text == NULL)
continue;
- if (tm_toolbar_options[i].style == default_style) {
+ if (option->style == default_style) {
gchar *option_text, *text;
- GtkWidget *item;
+ GMenuItem *item;
- gtk_menu_shell_append(GTK_MENU_SHELL(menu),
- gtk_separator_menu_item_new());
+ section = g_menu_new();
- option_text =
- tm_remove_underscore(_(tm_toolbar_options[i].text));
+ option_text = tm_remove_underscore(_(option->text));
text =
g_strdup_printf(_("Use Desktop _Default (%s)"),
option_text);
g_free(option_text);
- item = gtk_radio_menu_item_new_with_mnemonic(group, text);
+ item = g_menu_item_new(text, NULL);
g_free(text);
- if (info->model->style == (GtkToolbarStyle) (-1))
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
- (item), TRUE);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- g_object_set_data(G_OBJECT(item), BALSA_TOOLBAR_STYLE,
- GINT_TO_POINTER(-1));
- g_signal_connect(item, "toggled",
- G_CALLBACK(menu_item_toggled_cb), info);
+ g_menu_item_set_action_and_target(item, "set-style", "i", -1);
+
+ g_menu_append_item(section, item);
+ g_object_unref(item);
+
+ g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
+ g_object_unref(section);
}
}
if (gtk_widget_is_sensitive(toolbar)) {
/* This is a real toolbar, not the template from the
* toolbar-prefs dialog. */
- GtkWidget *item;
-
- gtk_menu_shell_append(GTK_MENU_SHELL(menu),
- gtk_separator_menu_item_new());
- item =
- gtk_menu_item_new_with_mnemonic(_("_Customize Toolbars…"));
- g_signal_connect(item, "activate", G_CALLBACK(customize_dialog_cb),
- gtk_widget_get_toplevel(toolbar));
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-
- /* Pass the model type to the customize widget, so that it can
- * show the appropriate notebook page. */
- g_object_set_data(G_OBJECT(item), BALSA_TOOLBAR_MODEL_TYPE,
- GINT_TO_POINTER(info->model->type));
+ section = g_menu_new();
+
+ g_menu_append(section, _("_Customize Toolbars…"), "customize");
+
+ g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
+ g_object_unref(section);
}
- gtk_widget_show_all(menu);
- gtk_menu_attach_to_widget(GTK_MENU(menu), toolbar, NULL);
+ popup_menu = gtk_popover_new(toolbar);
+ gtk_popover_bind_model(GTK_POPOVER(popup_menu), G_MENU_MODEL(menu), namespace);
+ g_object_unref(menu);
+ info->popup_menu = popup_menu;
+
+ if (button != -1) {
+ /* We are called with (x, y) coordinates, but they are
+ * "relative to the root of the screen", and we want them
+ * "relative to the window". */
+ GdkEvent *event;
+ gdouble x_win, y_win;
+
+ event = gtk_get_current_event();
+
+ if (event != NULL &&
+ gdk_event_triggers_context_menu(event) &&
+ gdk_event_get_coords(event, &x_win, &y_win)) {
+ GdkRectangle rectangle;
+
+ /* Pop up above the pointer */
+ rectangle.x = (gint) x_win;
+ rectangle.width = 0;
+ rectangle.y = (gint) y_win;
+ rectangle.height = 0;
+ gtk_popover_set_pointing_to(GTK_POPOVER(popup_menu), &rectangle);
+ }
- event = gtk_get_current_event();
- if (event != NULL && gdk_event_get_event_type(event) == GDK_BUTTON_PRESS) {
- gtk_menu_popup_at_pointer(GTK_MENU(menu), event);
- } else {
- gtk_menu_popup_at_widget(GTK_MENU(menu),
- GTK_WIDGET(toolbar),
- GDK_GRAVITY_NORTH,
- GDK_GRAVITY_SOUTH,
- NULL);
+ if (event != NULL)
+ gdk_event_free(event);
}
- if (event != NULL)
- gdk_event_free(event);
+ /* Apparently, the popover is insensitive if the toolbar is
+ * insensitive, but we always want it to be sensitive. */
+ gtk_widget_set_sensitive(popup_menu, TRUE);
+
+ gtk_popover_popup(GTK_POPOVER(popup_menu));
return TRUE;
}
@@ -745,12 +768,8 @@ tm_popup_context_menu_cb(GtkWidget * toolbar,
GtkWidget *balsa_toolbar_new(BalsaToolbarModel * model,
GActionMap * action_map)
{
- toolbar_info *info;
GtkWidget *toolbar;
-
- info = g_new(toolbar_info, 1);
- info->model = model;
- info->menu = NULL;
+ toolbar_info *info;
toolbar = gtk_toolbar_new();
g_object_set_data_full(G_OBJECT(toolbar), BALSA_TOOLBAR_ACTION_MAP,
@@ -758,6 +777,10 @@ GtkWidget *balsa_toolbar_new(BalsaToolbarModel * model,
(GDestroyNotify) g_object_unref);
tm_populate(toolbar, model);
+ info = g_new0(toolbar_info, 1);
+ info->model = model;
+ info->toolbar = toolbar;
+
g_signal_connect(model, "changed", G_CALLBACK(tm_changed_cb), toolbar);
g_object_weak_ref(G_OBJECT(toolbar),
(GWeakNotify) tm_toolbar_weak_notify, info);
diff --git a/src/toolbar-factory.h b/src/toolbar-factory.h
index e307fed49..a464f1360 100644
--- a/src/toolbar-factory.h
+++ b/src/toolbar-factory.h
@@ -31,10 +31,7 @@ G_DECLARE_FINAL_TYPE(BalsaToolbarModel,
TOOLBAR_MODEL,
GObject);
-typedef void (*BalsaToolbarFunc) (GtkWidget *, gpointer);
-#define BALSA_TOOLBAR_FUNC(f) ((BalsaToolbarFunc) (f))
-
-typedef struct t_button_data {
+typedef struct {
char *pixmap_id; /* not translatable */
char *button_text; /* translatable */
gboolean is_important; /* whether to show beside icon */
@@ -48,12 +45,6 @@ typedef struct {
gchar *icon;
} BalsaToolbarEntry;
-typedef enum {
- BALSA_TOOLBAR_TYPE_MAIN_WINDOW,
- BALSA_TOOLBAR_TYPE_COMPOSE_WINDOW,
- BALSA_TOOLBAR_TYPE_MESSAGE_WINDOW
-} BalsaToolbarType;
-
void update_all_toolbars(void);
/* toolbar code for gtk+-2 */
diff --git a/src/toolbar-prefs.c b/src/toolbar-prefs.c
index 800c7e8e7..50eb071b3 100644
--- a/src/toolbar-prefs.c
+++ b/src/toolbar-prefs.c
@@ -45,8 +45,6 @@ enum {
TP_N_COLUMNS
};
-static GtkWidget *customize_widget;
-
/* Structure associated with each notebook page. */
typedef struct ToolbarPage_ ToolbarPage;
struct ToolbarPage_ {
@@ -102,19 +100,19 @@ static void tp_store_set(GtkListStore * store, GtkTreeIter * iter,
/* create the toolbar-customization dialog
*/
void
-customize_dialog_cb(GtkWidget * widget, gpointer data)
+balsa_toolbar_customize(GtkWindow * active_window, BalsaToolbarType type)
{
GtkWidget *notebook;
GtkWidget *child;
GtkWidget *option_frame;
GtkWidget *option_box;
GtkWidget *wrap_button;
- GtkWidget *active_window = data;
- BalsaToolbarType type;
BalsaToolbarModel *model;
GSimpleActionGroup *group;
GtkWidget *content_area;
+ static GtkWidget *customize_widget;
+
/* There can only be one */
if (customize_widget) {
gtk_window_present_with_time(GTK_WINDOW(customize_widget),
@@ -124,14 +122,14 @@ customize_dialog_cb(GtkWidget * widget, gpointer data)
customize_widget =
gtk_dialog_new_with_buttons(_("Customize Toolbars"),
- GTK_WINDOW(active_window),
+ active_window,
GTK_DIALOG_DESTROY_WITH_PARENT |
libbalsa_dialog_flags(),
_("_Close"), GTK_RESPONSE_CLOSE,
_("_Help"), GTK_RESPONSE_HELP,
NULL);
#if HAVE_MACOSX_DESKTOP
- libbalsa_macosx_menu_for_parent(customize_widget, GTK_WINDOW(active_window));
+ libbalsa_macosx_menu_for_parent(customize_widget, active_window);
#endif
g_object_add_weak_pointer(G_OBJECT(customize_widget),
(gpointer *) & customize_widget);
@@ -195,9 +193,6 @@ customize_dialog_cb(GtkWidget * widget, gpointer data)
/* Now that the pages are shown, we can switch to the page
* corresponding to the toolbar that the user clicked on. */
- type =
- GPOINTER_TO_INT(g_object_get_data
- (G_OBJECT(widget), BALSA_TOOLBAR_MODEL_TYPE));
gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), type);
}
@@ -669,9 +664,10 @@ tp_page_refresh_preview(ToolbarPage * page)
gint item;
gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
- if (item >= 0 && item < toolbar_button_count)
+ if (item >= 0 && item < toolbar_button_count) {
balsa_toolbar_model_append_icon(page->model,
toolbar_buttons[item].pixmap_id);
+ }
}
}
diff --git a/src/toolbar-prefs.h b/src/toolbar-prefs.h
index 75fe39fba..ae4f984f4 100644
--- a/src/toolbar-prefs.h
+++ b/src/toolbar-prefs.h
@@ -22,10 +22,14 @@
#include <gtk/gtk.h>
-int get_toolbar_button_index(const char *id);
+typedef enum {
+ BALSA_TOOLBAR_TYPE_MAIN_WINDOW,
+ BALSA_TOOLBAR_TYPE_COMPOSE_WINDOW,
+ BALSA_TOOLBAR_TYPE_MESSAGE_WINDOW
+} BalsaToolbarType;
-void customize_dialog_cb(GtkWidget *, gpointer);
+int get_toolbar_button_index(const char *id);
-#define BALSA_TOOLBAR_MODEL_TYPE "balsa-toolbar-model-type"
+void balsa_toolbar_customize(GtkWindow * window, BalsaToolbarType type);
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]