diff --git a/src/core/constraints.c b/src/core/constraints.c index c3ad4741f..4fe747e17 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -140,7 +140,10 @@ typedef struct GList *usable_screen_region; GList *usable_monitor_region; } ConstraintInfo; - +static gboolean constrain_modal_dialog (MetaWindow *window, + ConstraintInfo *info, + ConstraintPriority priority, + gboolean check_only); static gboolean constrain_maximization (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, @@ -210,6 +213,7 @@ typedef struct { } Constraint; static const Constraint all_constraints[] = { + {constrain_modal_dialog, "constrain_modal_dialog"}, {constrain_maximization, "constrain_maximization"}, {constrain_fullscreen, "constrain_fullscreen"}, {constrain_size_increments, "constrain_size_increments"}, @@ -726,6 +730,48 @@ get_size_limits (const MetaWindow *window, } } +static gboolean +constrain_modal_dialog (MetaWindow *window, + ConstraintInfo *info, + ConstraintPriority priority, + gboolean check_only) +{ + int x, y; + MetaWindow *parent = meta_window_get_transient_for (window); + gboolean constraint_already_satisfied; + + if (!meta_prefs_get_attach_modal_dialogs ()) + return TRUE; + if (window->type != META_WINDOW_MODAL_DIALOG || !parent || parent == window) + return TRUE; + + x = parent->rect.x + (parent->rect.width / 2 - info->current.width / 2); + y = 0; + if (parent->frame) + { + MetaFrameGeometry fgeom; + + x += parent->frame->rect.x; + y += parent->frame->rect.y; + + meta_frame_calc_geometry (parent->frame, &fgeom); + y += fgeom.top_height; + + y += info->fgeom->top_height; + } + else + y = parent->rect.y + info->fgeom->top_height; + + constraint_already_satisfied = (x == info->current.x) && (y == info->current.y); + + if (check_only || constraint_already_satisfied) + return constraint_already_satisfied; + + info->current.y = y; + info->current.x = x; + return TRUE; +} + static gboolean constrain_maximization (MetaWindow *window, ConstraintInfo *info, diff --git a/src/core/display.c b/src/core/display.c index e9b3bfdc3..8c6b70d84 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -5192,6 +5192,34 @@ prefs_changed_callback (MetaPreference pref, else disable_compositor (display); } + else if (pref == META_PREF_ATTACH_MODAL_DIALOGS) + { + MetaDisplay *display = data; + GSList *windows; + GSList *tmp; + + windows = meta_display_list_windows (display, META_LIST_DEFAULT); + + for (tmp = windows; tmp != NULL; tmp = tmp->next) + { + MetaWindow *w = tmp->data; + MetaWindow *parent = meta_window_get_transient_for (w); + meta_window_recalc_features (w); + + if (w->type == META_WINDOW_MODAL_DIALOG && parent && parent != w) + { + int x, y; + /* Forcing a call to move_resize() does two things: first, it handles + * resizing the dialog frame window to the correct size when we remove + * or add the decorations. Second, it will take care of positioning the + * dialog as "attached" to the parent when we turn the preference on + * via the constrain_modal_dialog() constraint. + **/ + meta_window_get_position (w, &x, &y); + meta_window_move (w, FALSE, x, y); + } + } + } } void diff --git a/src/core/frame.c b/src/core/frame.c index 0468da38b..a8d0b1f7d 100644 --- a/src/core/frame.c +++ b/src/core/frame.c @@ -276,7 +276,7 @@ meta_frame_get_flags (MetaFrame *frame) if (META_WINDOW_ALLOWS_VERTICAL_RESIZE (frame->window)) flags |= META_FRAME_ALLOWS_VERTICAL_RESIZE; - if (frame->window->has_focus) + if (meta_window_appears_focused (frame->window)) flags |= META_FRAME_HAS_FOCUS; if (frame->window->shaded) diff --git a/src/core/prefs.c b/src/core/prefs.c index b7b963901..6d3e4f086 100644 --- a/src/core/prefs.c +++ b/src/core/prefs.c @@ -86,6 +86,7 @@ static MetaVirtualModifier mouse_button_mods = Mod1Mask; static MetaFocusMode focus_mode = META_FOCUS_MODE_CLICK; static MetaFocusNewWindows focus_new_windows = META_FOCUS_NEW_WINDOWS_SMART; static gboolean raise_on_click = TRUE; +static gboolean attach_modal_dialogs = FALSE; static char* current_theme = NULL; static int num_workspaces = 4; static MetaActionTitlebar action_double_click_titlebar = META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE; @@ -361,6 +362,11 @@ static MetaEnumPreference preferences_enum[] = static MetaBoolPreference preferences_bool[] = { + { "/apps/mutter/general/attach_modal_dialogs", + META_PREF_ATTACH_MODAL_DIALOGS, + &attach_modal_dialogs, + TRUE, + }, { "/apps/metacity/general/raise_on_click", META_PREF_RAISE_ON_CLICK, &raise_on_click, @@ -1493,6 +1499,12 @@ meta_prefs_get_focus_new_windows (void) return focus_new_windows; } +gboolean +meta_prefs_get_attach_modal_dialogs (void) +{ + return attach_modal_dialogs; +} + gboolean meta_prefs_get_raise_on_click (void) { @@ -1914,6 +1926,9 @@ meta_preference_to_string (MetaPreference pref) case META_PREF_FOCUS_NEW_WINDOWS: return "FOCUS_NEW_WINDOWS"; + case META_PREF_ATTACH_MODAL_DIALOGS: + return "ATTACH_MODAL_DIALOGS"; + case META_PREF_RAISE_ON_CLICK: return "RAISE_ON_CLICK"; diff --git a/src/core/window-private.h b/src/core/window-private.h index 5520fd872..207145ed4 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -442,6 +442,8 @@ void meta_window_update_fullscreen_monitors (MetaWindow *window, unsigned long left, unsigned long right); +gboolean meta_window_appears_focused (MetaWindow *window); + /* args to move are window pos, not frame pos */ void meta_window_move (MetaWindow *window, gboolean user_op, diff --git a/src/core/window.c b/src/core/window.c index b0b66abed..c58a9c896 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -3779,6 +3779,19 @@ send_sync_request (MetaWindow *window) } #endif +static gboolean +move_attached_dialog (MetaWindow *window, + void *data) +{ + MetaWindow *parent = meta_window_get_transient_for (window); + + if (window->type == META_WINDOW_MODAL_DIALOG && parent && parent != window) + /* It ignores x,y for such a dialog */ + meta_window_move (window, FALSE, 0, 0); + + return FALSE; +} + static void meta_window_move_resize_internal (MetaWindow *window, MetaMoveResizeFlags flags, @@ -4235,6 +4248,9 @@ meta_window_move_resize_internal (MetaWindow *window, * server-side size/pos of window->xwindow and frame->xwindow * b) all constraints are obeyed by window->rect and frame->rect */ + + if (meta_prefs_get_attach_modal_dialogs ()) + meta_window_foreach_transient (window, move_attached_dialog, NULL); } void @@ -5839,6 +5855,22 @@ meta_window_client_message (MetaWindow *window, return FALSE; } +static void +check_ancestor_focus_appearance (MetaWindow *window) +{ + MetaWindow *parent = meta_window_get_transient_for (window); + + if (!meta_prefs_get_attach_modal_dialogs ()) + return; + + if (window->type != META_WINDOW_MODAL_DIALOG || !parent || parent == window) + return; + if (parent->frame) + meta_frame_queue_draw (parent->frame); + + check_ancestor_focus_appearance (parent); +} + gboolean meta_window_notify_focus (MetaWindow *window, XEvent *event) @@ -5981,6 +6013,9 @@ meta_window_notify_focus (MetaWindow *window, !meta_prefs_get_raise_on_click()) meta_display_ungrab_focus_window_button (window->display, window); + /* parent window become active. */ + check_ancestor_focus_appearance (window); + g_signal_emit (window, window_signals[FOCUS], 0); g_object_notify (G_OBJECT (window->display), "focus-window"); } @@ -6010,6 +6045,9 @@ meta_window_notify_focus (MetaWindow *window, window->display->focus_window = NULL; g_object_notify (G_OBJECT (window->display), "focus-window"); window->has_focus = FALSE; + /* parent window become inactive. */ + check_ancestor_focus_appearance (window); + if (window->frame) meta_frame_queue_draw (window->frame); @@ -6930,6 +6968,16 @@ recalc_window_features (MetaWindow *window) if (window->type == META_WINDOW_TOOLBAR) window->decorated = FALSE; + if (window->type == META_WINDOW_MODAL_DIALOG && meta_prefs_get_attach_modal_dialogs ()) + { + MetaWindow *parent = meta_window_get_transient_for (window); + if (parent) + { + window->has_resize_func = FALSE; + window->border_only = TRUE; + } + } + if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK || window->override_redirect) @@ -8854,6 +8902,28 @@ meta_window_get_frame (MetaWindow *window) return window->frame; } +static gboolean +transient_has_focus (MetaWindow *window, + void *data) +{ + if (window->type == META_WINDOW_MODAL_DIALOG && meta_window_appears_focused (window)) + *((gboolean *)data) = TRUE; + + return FALSE; +} + +gboolean +meta_window_appears_focused (MetaWindow *window) +{ + if (!window->has_focus && meta_prefs_get_attach_modal_dialogs ()) + { + gboolean focus = FALSE; + meta_window_foreach_transient (window, transient_has_focus, &focus); + return focus; + } + return window->has_focus; +} + gboolean meta_window_has_focus (MetaWindow *window) { diff --git a/src/include/prefs.h b/src/include/prefs.h index f2e4fb922..77aab0cc9 100644 --- a/src/include/prefs.h +++ b/src/include/prefs.h @@ -34,6 +34,7 @@ typedef enum META_PREF_MOUSE_BUTTON_MODS, META_PREF_FOCUS_MODE, META_PREF_FOCUS_NEW_WINDOWS, + META_PREF_ATTACH_MODAL_DIALOGS, META_PREF_RAISE_ON_CLICK, META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR, META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR, @@ -85,6 +86,7 @@ guint meta_prefs_get_mouse_button_resize (void); guint meta_prefs_get_mouse_button_menu (void); MetaFocusMode meta_prefs_get_focus_mode (void); MetaFocusNewWindows meta_prefs_get_focus_new_windows (void); +gboolean meta_prefs_get_attach_modal_dialogs (void); gboolean meta_prefs_get_raise_on_click (void); const char* meta_prefs_get_theme (void); /* returns NULL if GTK default should be used */ diff --git a/src/mutter.schemas.in b/src/mutter.schemas.in index bb8913b60..4eae0695a 100644 --- a/src/mutter.schemas.in +++ b/src/mutter.schemas.in @@ -37,6 +37,22 @@ + + /schemas/apps/mutter/general/attach_modal_dialogs + /apps/mutter/general/attach_modal_dialogs + mutter + bool + false + + Attach modal dialogs + + When true, instead + of having independent titlebars, modal dialogs appear attached to the titlebar + of the parent window and are moved together with the parent window. + + + + /schemas/apps/mutter/general/live_hidden_windows /apps/mutter/general/live_hidden_windows