From 523ba50b188a81cf2e6c4a71dc6b26600d8461fc Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Sun, 5 Jun 2022 07:05:51 +0300 Subject: [PATCH 34/34] gtk4: Add initial app menu implementation Mostly lower level changes, but includes "Game" menu with some entries as an example and testing ground. See osdn #44756 Signed-off-by: Marko Lindqvist --- client/gui-gtk-4.0/gui_main.c | 31 +++-- client/gui-gtk-4.0/menu.c | 237 +++++++++++++++++----------------- client/gui-gtk-4.0/menu.h | 3 +- 3 files changed, 143 insertions(+), 128 deletions(-) diff --git a/client/gui-gtk-4.0/gui_main.c b/client/gui-gtk-4.0/gui_main.c index ee71a2a906..b28f7dc283 100644 --- a/client/gui-gtk-4.0/gui_main.c +++ b/client/gui-gtk-4.0/gui_main.c @@ -155,7 +155,7 @@ GtkWidget *econ_widget; const char *const gui_character_encoding = "UTF-8"; const bool gui_use_transliteration = FALSE; -static GtkWidget *main_menubar; +static GMenu *main_menubar = NULL; static GtkWidget *unit_image_table; static GtkWidget *unit_image; static GtkWidget *unit_below_image[MAX_NUM_UNITS_BELOW]; @@ -1029,20 +1029,27 @@ void reset_unit_table(void) void enable_menus(bool enable) { if (enable) { - main_menubar = setup_menus(toplevel); - /* Ensure the menus are really created before performing any operations - * on them. */ - while (g_main_context_pending(NULL)) { - g_main_context_iteration(NULL, FALSE); + if (main_menubar == NULL) { + setup_app_actions(G_APPLICATION(fc_app)); + main_menubar = setup_menus(fc_app); + /* Ensure the menus are really created before performing any operations + * on them. */ + while (g_main_context_pending(NULL)) { + g_main_context_iteration(NULL, FALSE); + } + + gtk_application_window_set_show_menubar(GTK_APPLICATION_WINDOW(toplevel), TRUE); } - gtk_grid_attach_next_to(GTK_GRID(top_vbox), main_menubar, NULL, GTK_POS_TOP, 1, 1); - menus_init(); - gtk_widget_show(main_menubar); + + gtk_application_set_menubar(fc_app, G_MENU_MODEL(main_menubar)); + } else { -#ifdef MENUS_GTK3 - gtk_widget_destroy(main_menubar); -#endif + gtk_application_set_menubar(fc_app, NULL); } + + /* Workaround for gtk bug that (re)setting the menubar after the window has + * been already created is not noticed. */ + g_object_notify(G_OBJECT(gtk_settings_get_default()), "gtk-shell-shows-menubar"); } /**********************************************************************//** diff --git a/client/gui-gtk-4.0/menu.c b/client/gui-gtk-4.0/menu.c index e50843fe27..198dca5442 100644 --- a/client/gui-gtk-4.0/menu.c +++ b/client/gui-gtk-4.0/menu.c @@ -74,7 +74,7 @@ #define GTK_STOCK_EDIT NULL #endif -static GtkBuilder *ui_builder = NULL; +static bool menus_built = FALSE; #ifndef FREECIV_DEBUG static void menu_entry_set_visible(const char *key, @@ -88,26 +88,43 @@ static void menu_entry_set_active(const char *key, static void menu_entry_set_sensitive(const char *key, gboolean is_sensitive); static void view_menu_update_sensitivity(void); +#endif /* MENUS_GTK3 */ enum menu_entry_grouping { MGROUP_SAFE, MGROUP_EDIT, MGROUP_PLAYING, MGROUP_UNIT, MGROUP_PLAYER, MGROUP_ALL }; +#ifdef MENUS_GTK3 static void menu_entry_group_set_sensitive(enum menu_entry_grouping group, gboolean is_sensitive); +#endif /* MENUS_GTK3 */ struct menu_entry_info { const char *key; const char *name; + const char *action; + const char *accl; +#ifdef MENUS_GTK3 guint accel; GdkModifierType accel_mod; GCallback cb; +#endif /* MENUS_GTK3 */ enum menu_entry_grouping grouping; }; -static void clear_chat_logs_callback(GtkMenuItem *item, gpointer data); -static void save_chat_logs_callback(GtkMenuItem *item, gpointer data); -static void local_options_callback(GtkMenuItem *item, gpointer data); -static void message_options_callback(GtkMenuItem *item, gpointer data); +static void clear_chat_logs_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data); +static void save_chat_logs_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data); +static void local_options_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data); +static void message_options_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data); + +#ifdef MENUS_GTK3 static void server_options_callback(GtkMenuItem *item, gpointer data); static void save_options_callback(GtkMenuItem *item, gpointer data); static void reload_tileset_callback(GtkMenuItem *item, gpointer data); @@ -118,8 +135,16 @@ static void save_mapimg_as_callback(GtkMenuItem *item, gpointer data); static void find_city_callback(GtkMenuItem *item, gpointer data); static void worklists_callback(GtkMenuItem *item, gpointer data); static void client_lua_script_callback(GtkMenuItem *item, gpointer data); -static void leave_callback(GtkMenuItem *item, gpointer data); -static void quit_callback(GtkMenuItem *item, gpointer data); +#endif /* MENUS_GTK3 */ + +static void leave_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data); +static void quit_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data); + +#ifdef MENUS_GTK3 static void map_view_callback(GtkMenuItem *item, gpointer data); static void report_units_callback(GtkMenuItem *item, gpointer data); static void report_nations_callback(GtkMenuItem *item, gpointer data); @@ -248,9 +273,25 @@ static void build_fortress_callback(GtkMenuItem *item, gpointer data); static void build_airbase_callback(GtkMenuItem *item, gpointer data); static void do_pillage_callback(GtkMenuItem *item, gpointer data); static void diplomat_action_callback(GtkMenuItem *item, gpointer data); +#endif /* MENUS_GTK3 */ static struct menu_entry_info menu_entries[] = { + { "CLEAR_CHAT_LOGS", N_("_Clear Chat Log"), + "app.clear_chat", NULL, MGROUP_SAFE }, + { "SAVE_CHAT_LOGS", N_("Save Chat _Log"), + "app.save_chat", NULL, MGROUP_SAFE, }, + { "LOCAL_OPTIONS", N_("_Local Client"), + "app.local_options", NULL, MGROUP_SAFE }, + { "MESSAGE_OPTIONS", N_("_Message"), + "app.message_options", NULL, MGROUP_SAFE }, + + { "LEAVE", N_("_Leave"), + "app.leave", NULL, MGROUP_SAFE }, + { "QUIT", N_("_Quit"), + "app.quit", "q", MGROUP_SAFE }, + +#ifdef MENUS_GTK3 { "MENU_GAME", N_("_Game"), 0, 0, NULL, MGROUP_SAFE }, { "MENU_OPTIONS", N_("_Options"), 0, 0, NULL, MGROUP_SAFE }, { "MENU_EDIT", N_("_Edit"), 0, 0, NULL, MGROUP_SAFE }, @@ -260,14 +301,6 @@ static struct menu_entry_info menu_entries[] = { "MENU_CIVILIZATION", N_("C_ivilization"), 0, 0, NULL, MGROUP_SAFE }, { "MENU_HELP", N_("_Help"), 0, 0, NULL, MGROUP_SAFE }, - { "CLEAR_CHAT_LOGS", N_("_Clear Chat Log"), 0, 0, - G_CALLBACK(clear_chat_logs_callback), MGROUP_SAFE }, - { "SAVE_CHAT_LOGS", N_("Save Chat _Log"), 0, 0, - G_CALLBACK(save_chat_logs_callback), MGROUP_SAFE }, - { "LOCAL_OPTIONS", N_("_Local Client"), 0, 0, - G_CALLBACK(local_options_callback), MGROUP_SAFE }, - { "MESSAGE_OPTIONS", N_("_Message"), 0, 0, - G_CALLBACK(message_options_callback), MGROUP_SAFE }, { "SERVER_OPTIONS", N_("_Remote Server"), 0, 0, G_CALLBACK(server_options_callback), MGROUP_SAFE }, { "SAVE_OPTIONS", N_("Save Options _Now"), 0, 0, @@ -283,9 +316,7 @@ static struct menu_entry_info menu_entries[] = G_CALLBACK(save_mapimg_callback), MGROUP_SAFE }, { "MAPIMG_SAVE_AS", N_("Save _Map Image As..."), 0, 0, G_CALLBACK(save_mapimg_as_callback), MGROUP_SAFE }, - { "LEAVE", N_("_Leave"), 0, 0, G_CALLBACK(leave_callback), MGROUP_SAFE }, - { "QUIT", N_("_Quit"), GDK_KEY_q, GDK_CONTROL_MASK, - G_CALLBACK(quit_callback), MGROUP_SAFE }, + { "FIND_CITY", N_("_Find City"), GDK_KEY_f, GDK_CONTROL_MASK, G_CALLBACK(find_city_callback), MGROUP_SAFE }, { "WORKLISTS", N_("Work_lists"), GDK_KEY_l, GDK_CONTROL_MASK, @@ -545,15 +576,30 @@ static struct menu_entry_info menu_entries[] = { "START_REVOLUTION", N_("_Revolution..."), GDK_KEY_r, GDK_SHIFT_MASK | GDK_CONTROL_MASK, G_CALLBACK(government_callback), MGROUP_PLAYING }, +#endif /* MENUS_GTK3 */ + { NULL } }; +const GActionEntry acts[] = { + { "clear_chat", clear_chat_logs_callback }, + { "save_chat", save_chat_logs_callback }, + { "local_options", local_options_callback }, + { "message_options", message_options_callback }, + + { "leave", leave_callback }, + { "quit", quit_callback } +}; + static struct menu_entry_info *menu_entry_info_find(const char *key); + /************************************************************************//** Item "CLEAR_CHAT_LOGS" callback. ****************************************************************************/ -static void clear_chat_logs_callback(GtkMenuItem *item, gpointer data) +static void clear_chat_logs_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data) { clear_output_window(); } @@ -561,7 +607,9 @@ static void clear_chat_logs_callback(GtkMenuItem *item, gpointer data) /************************************************************************//** Item "SAVE_CHAT_LOGS" callback. ****************************************************************************/ -static void save_chat_logs_callback(GtkMenuItem *item, gpointer data) +static void save_chat_logs_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data) { log_output_window(); } @@ -569,7 +617,9 @@ static void save_chat_logs_callback(GtkMenuItem *item, gpointer data) /************************************************************************//** Item "LOCAL_OPTIONS" callback. ****************************************************************************/ -static void local_options_callback(GtkMenuItem *item, gpointer data) +static void local_options_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data) { option_dialog_popup(_("Set local options"), client_optset); } @@ -577,11 +627,14 @@ static void local_options_callback(GtkMenuItem *item, gpointer data) /************************************************************************//** Item "MESSAGE_OPTIONS" callback. ****************************************************************************/ -static void message_options_callback(GtkMenuItem *item, gpointer data) +static void message_options_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data) { popup_messageopt_dialog(); } +#ifdef MENUS_GTK3 /************************************************************************//** Item "SERVER_OPTIONS" callback. ****************************************************************************/ @@ -637,6 +690,7 @@ static void save_mapimg_as_callback(GtkMenuItem *item, gpointer data) { save_mapimg_dialog_popup(); } +#endif /* MENUS_GTK3 */ /************************************************************************//** This is the response callback for the dialog with the message: @@ -657,7 +711,9 @@ static void leave_local_game_response(GtkWidget *dialog, gint response) /************************************************************************//** Item "LEAVE" callback. ****************************************************************************/ -static void leave_callback(GtkMenuItem *item, gpointer data) +static void leave_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data) { if (is_server_running()) { GtkWidget* dialog = @@ -676,11 +732,14 @@ static void leave_callback(GtkMenuItem *item, gpointer data) /************************************************************************//** Item "QUIT" callback. ****************************************************************************/ -static void quit_callback(GtkMenuItem *item, gpointer data) +static void quit_callback(GSimpleAction *action, + GVariant *parameter, + gpointer data) { popup_quit_dialog(); } +#ifdef MENUS_GTK3 /************************************************************************//** Item "FIND_CITY" callback. ****************************************************************************/ @@ -1883,132 +1942,79 @@ static void report_spaceship_callback(GtkMenuItem *action, gpointer data) popup_spaceship_dialog(client.conn.playing); } } +#endif /* MENUS_GTK3 */ /************************************************************************//** Set name of the menu item. ****************************************************************************/ -static void menu_entry_init(GtkBuildable *item) +static void menu_entry_init(GMenu *sub, const char *key) { - const char *key = gtk_buildable_get_name(item); struct menu_entry_info *info = menu_entry_info_find(key); if (info != NULL) { - gtk_menu_item_set_label(GTK_MENU_ITEM(item), - Q_(info->name)); - gtk_menu_item_set_use_underline(GTK_MENU_ITEM(item), TRUE); - if (info->cb != NULL) { - g_signal_connect(item, "activate", info->cb, NULL); - } - - if (info->accel != 0) { - const char *path = gtk_menu_item_get_accel_path(GTK_MENU_ITEM(item)); + GMenuItem *item; - if (path == NULL) { - char buf[512]; - - fc_snprintf(buf, sizeof(buf), "/%s", key); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), buf); - path = buf; /* Not NULL, but not usable either outside this block */ - } - - if (path != NULL) { - gtk_accel_map_add_entry(gtk_menu_item_get_accel_path(GTK_MENU_ITEM(item)), - info->accel, info->accel_mod); - } - } - - return; + item = g_menu_item_new(Q_(info->name), info->action); + g_menu_append_item(sub, item); } - - /* temporary naming solution */ - gtk_menu_item_set_label(GTK_MENU_ITEM(item), key); } -#endif /* MENUS_GTK3 */ /************************************************************************//** - Returns the name of the file readable by the GtkUIManager. + Registers menu actions for the application. ****************************************************************************/ -static const gchar *get_ui_filename(void) +void setup_app_actions(GApplication *fc_app) { - static char filename[256]; - const char *name; - - if ((name = getenv("FREECIV_MENUS")) - || (name = fileinfoname(get_data_dirs(), "gtk4_menus.xml"))) { - sz_strlcpy(filename, name); - } else { - log_error("Gtk menus: file definition not found"); - filename[0] = '\0'; - } - - log_verbose("ui menu file is \"%s\".", filename); - return filename; + g_action_map_add_action_entries(G_ACTION_MAP(fc_app), acts, + G_N_ELEMENTS(acts), fc_app); } /************************************************************************//** Creates the menu bar. ****************************************************************************/ -GtkWidget *setup_menus(GtkWidget *window) +GMenu *setup_menus(GtkApplication *app) { - GtkWidget *menubar = NULL; - GError *error = NULL; - - ui_builder = gtk_builder_new(); - if (!gtk_builder_add_from_file(ui_builder, get_ui_filename(), &error)) { - log_error("Gtk menus: %s", error->message); - g_error_free(error); - } else { - GSList *entries; - GSList *next; + GMenu *menubar; + GMenu *topmenu; + GMenu *submenu; + int i; - entries = gtk_builder_get_objects(ui_builder); - next = entries; + fc_assert(!menus_built); - while (next != NULL) { -#ifdef MENUS_GTK3 - GObject *obj = next->data; + menubar = g_menu_new(); - if (GTK_IS_MENU_ITEM(obj)) { - if (!GTK_IS_SEPARATOR_MENU_ITEM(obj)) { - menu_entry_init(GTK_BUILDABLE(obj)); - } - } else if (GTK_IS_MENU(obj)) { - const char *key = gtk_buildable_get_name(GTK_BUILDABLE(obj)); + topmenu = g_menu_new(); + menu_entry_init(topmenu, "CLEAR_CHAT_LOGS"); + menu_entry_init(topmenu, "SAVE_CHAT_LOGS"); + submenu = g_menu_new(); + menu_entry_init(submenu, "LOCAL_OPTIONS"); + menu_entry_init(submenu, "MESSAGE_OPTIONS"); - if (key[0] == '<') { - GtkAccelGroup *ac_group = gtk_menu_get_accel_group(GTK_MENU(obj)); + g_menu_append_submenu(topmenu, _("_Options"), G_MENU_MODEL(submenu)); - if (ac_group == NULL) { - ac_group = gtk_accel_group_new(); - gtk_menu_set_accel_group(GTK_MENU(obj), ac_group); - } + menu_entry_init(topmenu, "LEAVE"); + menu_entry_init(topmenu, "QUIT"); - gtk_window_add_accel_group(GTK_WINDOW(window), ac_group); + g_menu_append_submenu(menubar, _("_Game"), G_MENU_MODEL(topmenu)); - gtk_menu_set_accel_path(GTK_MENU(obj), key); - } - } +#ifndef FREECIV_DEBUG + menu_entry_set_visible("RELOAD_TILESET", FALSE, FALSE); +#endif /* FREECIV_DEBUG */ -#endif /* MENUS_GTK3 */ + for (i = 0; menu_entries[i].key != NULL; i++) { + if (menu_entries[i].accl != NULL) { + const char *accls[2] = { menu_entries[i].accl, NULL }; - next = next->next; + gtk_application_set_accels_for_action(app, + menu_entries[i].action, + accls); } - - g_slist_free(entries); - - menubar = GTK_WIDGET(gtk_builder_get_object(ui_builder, "MENU")); - gtk_widget_set_visible(menubar, TRUE); - gtk_widget_show(menubar); } -#ifndef FREECIV_DEBUG - menu_entry_set_visible("RELOAD_TILESET", FALSE, FALSE); -#endif /* FREECIV_DEBUG */ + menus_built = TRUE; return menubar; } -#ifdef MENUS_GTK3 /************************************************************************//** Find menu entry constrution data ****************************************************************************/ @@ -2025,6 +2031,7 @@ static struct menu_entry_info *menu_entry_info_find(const char *key) return NULL; } +#ifdef MENUS_GTK3 /************************************************************************//** Sets an menu entry sensitive. ****************************************************************************/ @@ -2169,7 +2176,7 @@ void real_menus_update(void) struct extra_type_list *extras; #endif /* MENUS_GTK3 */ - if (ui_builder == NULL || !can_client_change_view()) { + if (!menus_built || !can_client_change_view()) { return; } @@ -2683,7 +2690,7 @@ static void menu_remove_previous_entries(GtkMenu *menu) ****************************************************************************/ void real_menus_init(void) { - if (ui_builder == NULL) { + if (!menus_built) { return; } diff --git a/client/gui-gtk-4.0/menu.h b/client/gui-gtk-4.0/menu.h index 0500e08460..e0fbdae801 100644 --- a/client/gui-gtk-4.0/menu.h +++ b/client/gui-gtk-4.0/menu.h @@ -18,6 +18,7 @@ /* client */ #include "menu_g.h" -GtkWidget *setup_menus(GtkWidget *window); +void setup_app_actions(GApplication *fc_app); +GMenu *setup_menus(GtkApplication *app); #endif /* FC__MENU_H */ -- 2.35.1