From f6d9f9f93de1c3c5a7ab5d9c64783e941189d9b5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 21 Oct 2015 00:11:59 -0400 Subject: [PATCH] Add automatic help overlay support to GtkApplication When the $(resource_prefix)/gtk/help-overlay.ui resource exists, load a GtkShortcutsWindow from it for each GtkApplicationWindow, and set up a win.show-help-overlay action with accels F1 and ? to show it. --- docs/reference/gtk/gtk3-sections.txt | 2 + gtk/gtkapplication.c | 56 +++++++++++++++++--- gtk/gtkapplicationwindow.c | 78 +++++++++++++++++++++++++++- gtk/gtkapplicationwindow.h | 8 +++ 4 files changed, 134 insertions(+), 10 deletions(-) diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt index f4482e1378..d6274d75f5 100644 --- a/docs/reference/gtk/gtk3-sections.txt +++ b/docs/reference/gtk/gtk3-sections.txt @@ -7545,6 +7545,8 @@ gtk_application_window_new gtk_application_window_set_show_menubar gtk_application_window_get_show_menubar gtk_application_window_get_id +gtk_application_window_set_help_overlay +gtk_application_window_get_help_overlay GTK_TYPE_APPLICATION_WINDOW diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c index 0edfe925e2..0a6e668dce 100644 --- a/gtk/gtkapplication.c +++ b/gtk/gtkapplication.c @@ -38,6 +38,7 @@ #include "gtkaccelmapprivate.h" #include "gtkicontheme.h" #include "gtkbuilder.h" +#include "gtkshortcutswindow.h" #include "gtkintl.h" /* NB: please do not add backend-specific GDK headers here. This should @@ -74,7 +75,7 @@ * ## Automatic resources ## {#automatic-resources} * * #GtkApplication will automatically load menus from the #GtkBuilder - * file located at "gtk/menus.ui", relative to the application's + * resource located at "gtk/menus.ui", relative to the application's * resource base path (see g_application_set_resource_base_path()). The * menu with the ID "app-menu" is taken as the application's app menu * and the menu with the ID "menubar" is taken as the application's @@ -82,7 +83,7 @@ * and accessed via gtk_application_get_menu_by_id() which allows for * dynamic population of a part of the menu structure. * - * If the files "gtk/menus-appmenu.ui" or "gtk/menus-traditional.ui" are + * If the resources "gtk/menus-appmenu.ui" or "gtk/menus-traditional.ui" are * present then these files will be used in preference, depending on the * value of gtk_application_prefers_app_menu(). * @@ -95,6 +96,12 @@ * resources. See gtk_icon_theme_add_resource_path() for more * information. * + * If there is a resource located at "gtk/help-overlay.ui" which is + * defining a #GtkShortcutsWindow with ID "help_overlay" then GtkApplication + * associates an instance of this shortcuts window with each + * #GtkApplicationWindow and sets up keyboard accelerators (Control-F1 + * and Control-?) to open it. + * * ## A simple application ## {#gtkapplication} * * [A simple example](https://git.gnome.org/browse/gtk+/tree/examples/bp/bloatpad.c) @@ -485,9 +492,10 @@ struct _GtkApplicationPrivate Accels accels; guint last_window_id; - gboolean register_session; + gboolean register_session; GtkActionMuxer *muxer; GtkBuilder *menus_builder; + gchar *help_overlay_path; }; G_DEFINE_TYPE_WITH_PRIVATE (GtkApplication, gtk_application, G_TYPE_APPLICATION) @@ -588,6 +596,22 @@ gtk_application_load_resources (GtkApplication *application) gtk_application_set_menubar (application, G_MENU_MODEL (menu)); } } + + /* Help overlay */ + { + gchar *path; + + path = g_strconcat (base_path, "/gtk/help-overlay.ui", NULL); + if (g_resources_get_info (path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, NULL, NULL)) + { + const gchar * const accels[] = { "F1", "question", NULL }; + + application->priv->help_overlay_path = path; + gtk_application_set_accels_for_action (application, "win.show-help-overlay", accels); + } + else + g_free (path); + } } @@ -698,7 +722,21 @@ gtk_application_window_added (GtkApplication *application, GtkApplicationPrivate *priv = application->priv; if (GTK_IS_APPLICATION_WINDOW (window)) - gtk_application_window_set_id (GTK_APPLICATION_WINDOW (window), ++application->priv->last_window_id); + { + gtk_application_window_set_id (GTK_APPLICATION_WINDOW (window), ++priv->last_window_id); + if (priv->help_overlay_path) + { + GtkBuilder *builder; + GtkWidget *help_overlay; + + builder = gtk_builder_new_from_resource (priv->help_overlay_path); + help_overlay = GTK_WIDGET (gtk_builder_get_object (builder, "help_overlay")); + if (GTK_IS_SHORTCUTS_WINDOW (help_overlay)) + gtk_application_window_set_help_overlay (GTK_APPLICATION_WINDOW (window), + GTK_SHORTCUTS_WINDOW (help_overlay)); + g_object_unref (builder); + } + } priv->windows = g_list_prepend (priv->windows, window); gtk_window_set_application (window, application); @@ -708,9 +746,10 @@ gtk_application_window_added (GtkApplication *application, G_CALLBACK (gtk_application_focus_in_event_cb), application); - gtk_application_impl_window_added (application->priv->impl, window); + gtk_application_impl_window_added (priv->impl, window); + + gtk_application_impl_active_window_changed (priv->impl, window); - gtk_application_impl_active_window_changed (application->priv->impl, window); g_object_notify_by_pspec (G_OBJECT (application), gtk_application_props[PROP_ACTIVE_WINDOW]); } @@ -870,8 +909,9 @@ gtk_application_finalize (GObject *object) accels_finalize (&application->priv->accels); - G_OBJECT_CLASS (gtk_application_parent_class) - ->finalize (object); + g_free (application->priv->help_overlay_path); + + G_OBJECT_CLASS (gtk_application_parent_class)->finalize (object); } static void diff --git a/gtk/gtkapplicationwindow.c b/gtk/gtkapplicationwindow.c index 8602a988e8..919d92c719 100644 --- a/gtk/gtkapplicationwindow.c +++ b/gtk/gtkapplicationwindow.c @@ -228,6 +228,8 @@ struct _GtkApplicationWindowPrivate GMenu *menubar_section; guint id; + + GtkShortcutsWindow *help_overlay; }; static void @@ -788,9 +790,9 @@ gtk_application_window_dispose (GObject *object) g_clear_object (&window->priv->app_menu_section); g_clear_object (&window->priv->menubar_section); + g_clear_object (&window->priv->help_overlay); - G_OBJECT_CLASS (gtk_application_window_parent_class) - ->dispose (object); + G_OBJECT_CLASS (gtk_application_window_parent_class)->dispose (object); /* We do this below the chain-up above to give us a chance to be * removed from the GtkApplication (which is done in the dispose @@ -959,3 +961,75 @@ gtk_application_window_set_id (GtkApplicationWindow *window, g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window)); window->priv->id = id; } + +static void +show_help_overlay (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GtkApplicationWindow *window = user_data; + + if (window->priv->help_overlay) + gtk_widget_show (GTK_WIDGET (window->priv->help_overlay)); +} + +/** + * gtk_application_window_set_help_overlay: + * @window: a #GtkApplicationWindow + * @help_overlay: a #GtkShortcutsWindow + * + * Associates a shortcuts window with the application window, and + * sets up a action with the name win.show-help-overay to present + * it. + * + * Since: 3.20 + */ +void +gtk_application_window_set_help_overlay (GtkApplicationWindow *window, + GtkShortcutsWindow *help_overlay) +{ + g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window)); + g_return_if_fail (help_overlay == NULL || GTK_IS_SHORTCUTS_WINDOW (help_overlay)); + + if (window->priv->help_overlay) + g_signal_handlers_disconnect_by_func (window->priv->help_overlay, + G_CALLBACK (gtk_widget_hide_on_delete), NULL); + g_set_object (&window->priv->help_overlay, help_overlay); + + if (!window->priv->help_overlay) + return; + + gtk_window_set_modal (GTK_WINDOW (help_overlay), TRUE); + gtk_window_set_transient_for (GTK_WINDOW (help_overlay), GTK_WINDOW (window)); + g_signal_connect (help_overlay, "delete-event", + G_CALLBACK (gtk_widget_hide_on_delete), NULL); + + if (!g_action_map_lookup_action (G_ACTION_MAP (window->priv->actions), "show-help-overlay")) + { + GSimpleAction *action; + + action = g_simple_action_new ("show-help-overlay", NULL); + g_signal_connect (action, "activate", G_CALLBACK (show_help_overlay), window); + + g_action_map_add_action (G_ACTION_MAP (window->priv->actions), G_ACTION (action)); + } +} + +/** + * gtk_application_window_get_help_overlay: + * @window: a #GtkApplicationWindow + * + * Gets the #GtkShortcutsWindow that has been set up with + * a prior call to gtk_application_window_set_help_overlay(). + * + * Returns: the help overlay associated with @window, or %NULL + * + * Since: 3.20 + */ +GtkShortcutsWindow * +gtk_application_window_get_help_overlay (GtkApplicationWindow *window) +{ + g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (window), NULL); + + return window->priv->help_overlay; +} diff --git a/gtk/gtkapplicationwindow.h b/gtk/gtkapplicationwindow.h index 234283c6b4..abe41c9403 100644 --- a/gtk/gtkapplicationwindow.h +++ b/gtk/gtkapplicationwindow.h @@ -25,6 +25,7 @@ #endif #include +#include G_BEGIN_DECLS @@ -78,6 +79,13 @@ gboolean gtk_application_window_get_show_menubar (GtkApplicationWindow *windo GDK_AVAILABLE_IN_3_6 guint gtk_application_window_get_id (GtkApplicationWindow *window); +GDK_AVAILABLE_IN_3_20 +void gtk_application_window_set_help_overlay (GtkApplicationWindow *window, + GtkShortcutsWindow *help_overlay); +GDK_AVAILABLE_IN_3_20 +GtkShortcutsWindow * + gtk_application_window_get_help_overlay (GtkApplicationWindow *window); + G_END_DECLS #endif /* __GTK_APPLICATION_WINDOW_H__ */