diff --git a/Services/WindowServer/ClientConnection.cpp b/Services/WindowServer/ClientConnection.cpp index 9089ece782..d3eaf9ee4a 100644 --- a/Services/WindowServer/ClientConnection.cpp +++ b/Services/WindowServer/ClientConnection.cpp @@ -689,16 +689,6 @@ OwnPtr ClientConnection::handle(const Mes return make(client_id(), Screen::the().rect(), Gfx::current_system_theme_buffer_id()); } -bool ClientConnection::is_showing_modal_window() const -{ - for (auto& it : m_windows) { - auto& window = *it.value; - if (window.is_visible() && window.is_modal()) - return true; - } - return false; -} - void ClientConnection::handle(const Messages::WindowServer::WM_SetWindowTaskbarRect& message) { auto* client = ClientConnection::from_client_id(message.client_id()); diff --git a/Services/WindowServer/ClientConnection.h b/Services/WindowServer/ClientConnection.h index 331dd79327..7c5ff5aeca 100644 --- a/Services/WindowServer/ClientConnection.h +++ b/Services/WindowServer/ClientConnection.h @@ -61,8 +61,6 @@ public: MenuBar* app_menubar() { return m_app_menubar.ptr(); } - bool is_showing_modal_window() const; - void notify_about_new_screen_rect(const Gfx::IntRect&); void post_paint_message(Window&, bool ignore_occlusion = false); diff --git a/Services/WindowServer/Window.cpp b/Services/WindowServer/Window.cpp index d4488c47df..afb0f8d449 100644 --- a/Services/WindowServer/Window.cpp +++ b/Services/WindowServer/Window.cpp @@ -387,16 +387,20 @@ bool Window::is_active() const return WindowManager::the().active_window() == this; } -bool Window::is_blocked_by_modal_window() const +Window* Window::is_blocked_by_modal_window() { - bool is_any_modal = false; - const Window* next = this; - while (!is_any_modal && next) { - is_any_modal = next->is_modal(); - next = next->parent_window(); + // A window is blocked if any immediate child, or any child further + // down the chain is modal + for (auto& window: m_child_windows) { + if (window) { + if (window->is_modal()) + return window; + + if (auto* blocking_modal_window = window->is_blocked_by_modal_window()) + return blocking_modal_window; + } } - - return !is_any_modal && client() && client()->is_showing_modal_window(); + return nullptr; } void Window::set_default_icon() diff --git a/Services/WindowServer/Window.h b/Services/WindowServer/Window.h index bc1f124c6f..c7033853b8 100644 --- a/Services/WindowServer/Window.h +++ b/Services/WindowServer/Window.h @@ -116,7 +116,7 @@ public: WindowFrame& frame() { return m_frame; } const WindowFrame& frame() const { return m_frame; } - bool is_blocked_by_modal_window() const; + Window* is_blocked_by_modal_window(); bool listens_to_wm_events() const { return m_listens_to_wm_events; } @@ -145,7 +145,7 @@ public: bool is_visible() const { return m_visible; } void set_visible(bool); - bool is_modal() const { return m_modal; } + bool is_modal() const { return m_modal && m_parent_window; } Gfx::IntRect rect() const { return m_rect; } void set_rect(const Gfx::IntRect&); diff --git a/Services/WindowServer/WindowManager.cpp b/Services/WindowServer/WindowManager.cpp index afee322f74..97093a4a75 100644 --- a/Services/WindowServer/WindowManager.cpp +++ b/Services/WindowServer/WindowManager.cpp @@ -205,22 +205,46 @@ void WindowManager::add_window(Window& window) void WindowManager::move_to_front_and_make_active(Window& window) { - if (window.is_blocked_by_modal_window()) - return; - - bool make_active = true; - if (window.is_accessory()) { - auto* parent = window.parent_window(); - do_move_to_front(*parent, true, false); - make_active = false; - - for (auto& accessory_window : parent->accessory_windows()) { - if (accessory_window && accessory_window.ptr() != &window) - do_move_to_front(*accessory_window, false, false); + auto move_window_to_front = [&](Window& wnd, bool make_active, bool make_input) { + if (wnd.is_accessory()) { + auto* parent = wnd.parent_window(); + do_move_to_front(*parent, true, false); + make_active = false; + + for (auto& accessory_window : parent->accessory_windows()) { + if (accessory_window && accessory_window.ptr() != &wnd) + do_move_to_front(*accessory_window, false, false); + } } - } - do_move_to_front(window, make_active, true); + do_move_to_front(wnd, make_active, make_input); + }; + + auto* blocking_modal_window = window.is_blocked_by_modal_window(); + if (blocking_modal_window || window.is_modal()) { + // If a window that is currently blocked by a modal child is being + // brought to the front, bring the entire stack of modal windows + // to the front and activate the modal window. Also set the + // active input window to that same window (which would pull + // active input from any accessory window) + Vector modal_stack; + auto* modal_stack_top = blocking_modal_window ? blocking_modal_window : &window; + for (auto* parent = modal_stack_top->parent_window(); parent; parent = parent->parent_window()) { + if (parent->is_blocked_by_modal_window() != blocking_modal_window) + break; + modal_stack.append(parent); + if (!parent->is_modal()) + break; + } + if (!modal_stack.is_empty()) { + for (size_t i = modal_stack.size(); i > 0; i--) { + move_window_to_front(*modal_stack[i - 1], false, false); + } + } + move_window_to_front(*modal_stack_top, true, true); + } else { + move_window_to_front(window, true, true); + } } void WindowManager::do_move_to_front(Window& window, bool make_active, bool make_input) @@ -858,7 +882,7 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind HashTable windows_who_received_mouse_event_due_to_cursor_tracking; for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { - if (!window->global_cursor_tracking() || !window->is_visible() || window->is_minimized()) + if (!window->global_cursor_tracking() || !window->is_visible() || window->is_minimized() || window->is_blocked_by_modal_window()) continue; windows_who_received_mouse_event_due_to_cursor_tracking.set(window); auto translated_event = event.translated(-window->position()); @@ -866,7 +890,7 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind } // FIXME: Now that the menubar has a dedicated window, is this special-casing really necessary? - if (MenuManager::the().has_open_menu() || (!active_window_is_modal() && menubar_rect().contains(event.position()))) { + if (MenuManager::the().has_open_menu() || menubar_rect().contains(event.position())) { clear_resize_candidate(); MenuManager::the().dispatch_event(event); return; @@ -939,7 +963,7 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind } hovered_window = &window; - if (!window.global_cursor_tracking() && !windows_who_received_mouse_event_due_to_cursor_tracking.contains(&window)) { + if (!window.global_cursor_tracking() && !windows_who_received_mouse_event_due_to_cursor_tracking.contains(&window) && !window.is_blocked_by_modal_window()) { auto translated_event = event.translated(-window.position()); deliver_mouse_event(window, translated_event); if (event.type() == Event::MouseDown) { @@ -1139,8 +1163,14 @@ Window* WindowManager::set_active_input_window(Window* window) void WindowManager::set_active_window(Window* window, bool make_input) { - if (window && window->is_blocked_by_modal_window()) - return; + if (window) { + if (auto* modal_window = window->is_blocked_by_modal_window()) { + ASSERT(modal_window->is_modal()); + ASSERT(modal_window != window); + window = modal_window; + make_input = true; + } + } if (window && !window_type_can_become_active(window->type())) return; diff --git a/Services/WindowServer/WindowManager.h b/Services/WindowServer/WindowManager.h index 190750d040..08f15c0b77 100644 --- a/Services/WindowServer/WindowManager.h +++ b/Services/WindowServer/WindowManager.h @@ -112,7 +112,6 @@ public: Window* active_input_window() { return m_active_input_window.ptr(); } const Window* active_input_window() const { return m_active_input_window.ptr(); } const ClientConnection* active_client() const; - bool active_window_is_modal() const { return m_active_window && m_active_window->is_modal(); } const Window* highlight_window() const { return m_highlight_window.ptr(); } void set_highlight_window(Window*);