From 0d1ac8cb5be63e066598a0a3ff92f5dfebbc3f91 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 17 Apr 2010 16:57:58 -0400 Subject: [PATCH] [ShellApp] Add method to focus an app (all windows), make _activate do this The design calls for raising all windows for a given app in certain circumstances; implement this. The new _focus method raises all windows for the app if it's running. We further change the _activate method (which a lot of the shell UI calls now) to invoke _focus for the running case, which means that e.g. the application well will now raise all app windows. https://bugzilla.gnome.org/show_bug.cgi?id=616051 --- src/shell-app.c | 161 ++++++++++++++++++++++++++++++++++++++++++------ src/shell-app.h | 2 + 2 files changed, 145 insertions(+), 18 deletions(-) diff --git a/src/shell-app.c b/src/shell-app.c index 41d031da9..6c78d23b1 100644 --- a/src/shell-app.c +++ b/src/shell-app.c @@ -6,6 +6,7 @@ #include "shell-app-private.h" #include "shell-global.h" #include "shell-enum-types.h" +#include "display.h" #include @@ -245,14 +246,153 @@ shell_app_is_transient (ShellApp *app) return shell_app_info_is_transient (app->info); } +typedef struct { + MetaWorkspace *workspace; + GSList **transients; +} CollectTransientsData; + +static gboolean +collect_transients_on_workspace (MetaWindow *window, + gpointer datap) +{ + CollectTransientsData *data = datap; + + if (data->workspace && meta_window_get_workspace (window) != data->workspace) + return TRUE; + + *data->transients = g_slist_prepend (*data->transients, window); + return TRUE; +} + +/* The basic idea here is that when we're targeting a window, + * if it has transients we want to pick the most recent one + * the user interacted with. + * This function makes raising GEdit with the file chooser + * open work correctly. + */ +static MetaWindow * +find_most_recent_transient_on_same_workspace (MetaDisplay *display, + MetaWindow *reference) +{ + GSList *transients, *transients_sorted, *iter; + MetaWindow *result; + CollectTransientsData data; + + transients = NULL; + data.workspace = meta_window_get_workspace (reference); + data.transients = &transients; + + meta_window_foreach_transient (reference, collect_transients_on_workspace, &data); + + transients_sorted = meta_display_sort_windows_by_stacking (display, transients); + /* Reverse this so we're top-to-bottom (yes, we should probably change the order + * returned from the sort_windows_by_stacking function) + */ + transients_sorted = g_slist_reverse (transients_sorted); + g_slist_free (transients); + transients = NULL; + + result = NULL; + for (iter = transients_sorted; iter; iter = iter->next) + { + MetaWindow *window = iter->data; + MetaWindowType wintype = meta_window_get_window_type (window); + + /* Don't want to focus UTILITY types, like the Gimp toolbars */ + if (wintype == META_WINDOW_NORMAL || + wintype == META_WINDOW_DIALOG) + { + result = window; + break; + } + } + g_slist_free (transients_sorted); + return result; +} + +/** + * shell_app_activate_window: + * @app: a #ShellApp + * @window: (allow-none): Window to be focused + * @timestamp: Event timestamp + * + * Bring all windows for the given app to the foreground, + * but ensure that @window is on top. If @window is %NULL, + * the window with the most recent user time for the app + * will be used. + * + * This function has no effect if @app is not currently running. + */ +void +shell_app_activate_window (ShellApp *app, + MetaWindow *window, + guint32 timestamp) +{ + GSList *windows; + + if (shell_app_get_state (app) != SHELL_APP_STATE_RUNNING) + return; + + windows = shell_app_get_windows (app); + if (window == NULL && windows) + window = windows->data; + + if (!g_slist_find (windows, window)) + return; + else + { + GSList *iter; + ShellGlobal *global = shell_global_get (); + MetaScreen *screen = shell_global_get_screen (global); + MetaDisplay *display = meta_screen_get_display (screen); + MetaWorkspace *active = meta_screen_get_active_workspace (screen); + MetaWorkspace *workspace = meta_window_get_workspace (window); + guint32 last_user_timestamp = meta_display_get_last_user_time (display); + MetaWindow *most_recent_transient; + + if (meta_display_xserver_time_is_before (display, timestamp, last_user_timestamp)) + { + meta_window_set_demands_attention (window); + return; + } + + /* Now raise all the other windows for the app that are on + * the same workspace, in reverse order to preserve the stacking. + */ + for (iter = windows; iter; iter = iter->next) + { + MetaWindow *other_window = iter->data; + + if (other_window != window) + meta_window_raise (other_window); + } + + /* If we have a transient that the user's interacted with more recently than + * the window, pick that. + */ + most_recent_transient = find_most_recent_transient_on_same_workspace (display, window); + if (most_recent_transient + && meta_display_xserver_time_is_before (display, + meta_window_get_user_time (window), + meta_window_get_user_time (most_recent_transient))) + window = most_recent_transient; + + if (active != workspace) + meta_workspace_activate_with_focus (workspace, window, timestamp); + else + meta_window_activate (window, timestamp); + } +} + /** * shell_app_activate: * @app: a #ShellApp * * Perform an appropriate default action for operating on this application, * dependent on its current state. For example, if the application is not - * currently running, launch it. If it is running, activate the most recently - * used window. + * currently running, launch it. If it is running, activate the most + * recently used NORMAL window (or if that window has a transient, the most + * recently used transient for that window). */ void shell_app_activate (ShellApp *app) @@ -266,22 +406,7 @@ shell_app_activate (ShellApp *app) case SHELL_APP_STATE_STARTING: break; case SHELL_APP_STATE_RUNNING: - { - GSList *windows = shell_app_get_windows (app); - if (windows) - { - ShellGlobal *global = shell_global_get (); - MetaScreen *screen = shell_global_get_screen (global); - MetaWorkspace *active = meta_screen_get_active_workspace (screen); - MetaWindow *window = windows->data; - MetaWorkspace *workspace = meta_window_get_workspace (window); - - if (active != workspace) - meta_workspace_activate_with_focus (workspace, window, shell_global_get_current_time (global)); - else - meta_window_activate (window, shell_global_get_current_time (global)); - } - } + shell_app_activate_window (app, NULL, shell_global_get_current_time (shell_global_get ())); break; } } diff --git a/src/shell-app.h b/src/shell-app.h index 42e071eac..de969152f 100644 --- a/src/shell-app.h +++ b/src/shell-app.h @@ -41,6 +41,8 @@ char *shell_app_get_name (ShellApp *app); char *shell_app_get_description (ShellApp *app); gboolean shell_app_is_transient (ShellApp *app); +void shell_app_activate_window (ShellApp *app, MetaWindow *window, guint32 timestamp); + void shell_app_activate (ShellApp *app); void shell_app_open_new_window (ShellApp *app);