From 788b16cbf86b58350630a757d80f909bd63c7f38 Mon Sep 17 00:00:00 2001 From: thankyouverycool <66646555+thankyouverycool@users.noreply.github.com> Date: Mon, 22 Aug 2022 14:29:33 -0400 Subject: [PATCH] WindowServer: Replace modal stacks with modal chains And apply modal effects on move_to_front_and_make_active() Instead of building a vector of windows every time we want to loop over a group of related modals, simply recurse through their ancestory. This lineage is now called a modal chain. Modal chains begin at the first modeless parent, and a new chain starts at every modeless child. This lets apps spawn child windows independent of the main window's modal effects, restore state, and workspace, yet still remain descendants. Looping through a modal chain is recursive and includes the modeless window which begins it. --- Userland/Services/WindowServer/Window.cpp | 18 ++---- .../Services/WindowServer/WindowManager.cpp | 61 +++++++++---------- .../Services/WindowServer/WindowManager.h | 48 +++++++-------- 3 files changed, 55 insertions(+), 72 deletions(-) diff --git a/Userland/Services/WindowServer/Window.cpp b/Userland/Services/WindowServer/Window.cpp index 4c0d2abf47..4f9b8b38e7 100644 --- a/Userland/Services/WindowServer/Window.cpp +++ b/Userland/Services/WindowServer/Window.cpp @@ -679,18 +679,12 @@ bool Window::is_active() const Window* Window::blocking_modal_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 && !window->is_destroyed()) { - if (window->is_modal()) - return window; - - if (auto* blocking_window = window->blocking_modal_window()) - return blocking_window; - } - } - return nullptr; + auto maybe_blocker = WindowManager::the().for_each_window_in_modal_chain(*this, [&](auto& window) { + if (window.is_blocking() && this != &window) + return IterationDecision::Break; + return IterationDecision::Continue; + }); + return maybe_blocker; } void Window::set_default_icon() diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp index f9f3bd4c9c..46da200b65 100644 --- a/Userland/Services/WindowServer/WindowManager.cpp +++ b/Userland/Services/WindowServer/WindowManager.cpp @@ -342,23 +342,22 @@ void WindowManager::add_window(Window& window) void WindowManager::move_to_front_and_make_active(Window& window) { - auto move_window_to_front = [&](Window& wnd, bool make_active, bool make_input) { - do_move_to_front(wnd, make_active, make_input); - }; - for_each_window_in_modal_stack(window, [&](auto& w, bool is_stack_top) { - move_window_to_front(w, is_stack_top, is_stack_top); - return IterationDecision::Continue; - }); + if (auto* blocker = window.blocking_modal_window()) { + blocker->window_stack().move_to_front(*blocker); + set_active_window(blocker, true); + } else { + window.window_stack().move_to_front(window); + set_active_window(&window, true); - Compositor::the().invalidate_occlusions(); -} - -void WindowManager::do_move_to_front(Window& window, bool make_active, bool make_input) -{ - window.window_stack().move_to_front(window); - - if (make_active) - set_active_window(&window, make_input); + for (auto& child : window.child_windows()) { + if (!child || !child->is_modal()) + continue; + if (child->is_rendering_above()) + child->window_stack().move_to_front(*child); + if (child->is_capturing_input()) + set_active_input_window(child); + } + } if (m_switcher->is_visible()) { m_switcher->refresh(); @@ -368,10 +367,7 @@ void WindowManager::do_move_to_front(Window& window, bool make_active, bool make } } - for (auto& child_window : window.child_windows()) { - if (child_window) - do_move_to_front(*child_window, make_active, make_input); - } + Compositor::the().invalidate_occlusions(); } void WindowManager::remove_window(Window& window) @@ -1438,14 +1434,14 @@ void WindowManager::event(Core::Event& event) Core::Object::event(event); } -bool WindowManager::is_window_in_modal_stack(Window& window_in_modal_stack, Window& other_window) +bool WindowManager::is_window_in_modal_chain(Window& chain_window, Window& other_window) { - auto result = for_each_window_in_modal_stack(window_in_modal_stack, [&](auto& window, auto) { + auto result = for_each_window_in_modal_chain(chain_window, [&](auto& window) { if (&other_window == &window) return IterationDecision::Break; return IterationDecision::Continue; }); - return result == IterationDecision::Break; + return result; } void WindowManager::switch_to_window_stack(WindowStack& window_stack, Window* carry_window, bool show_overlay) @@ -1455,14 +1451,13 @@ void WindowManager::switch_to_window_stack(WindowStack& window_stack, Window* ca if (carry_window && !is_stationary_window_type(carry_window->type()) && &carry_window->window_stack() != &window_stack) { auto& from_stack = carry_window->window_stack(); - auto* blocking_modal = carry_window->blocking_modal_window(); for_each_visible_window_from_back_to_front([&](Window& window) { if (is_stationary_window_type(window.type())) return IterationDecision::Continue; if (&window.window_stack() != &carry_window->window_stack()) return IterationDecision::Continue; - if (&window == carry_window || ((carry_window->is_modal() || blocking_modal) && is_window_in_modal_stack(*carry_window, window))) + if (&window == carry_window || is_window_in_modal_chain(*carry_window, window)) m_carry_window_to_new_stack.append(window); return IterationDecision::Continue; }, @@ -2131,7 +2126,7 @@ void WindowManager::did_popup_a_menu(Badge) void WindowManager::minimize_windows(Window& window, bool minimized) { - for_each_window_in_modal_stack(window, [&](auto& w, bool) { + for_each_window_in_modal_chain(window, [&](auto& w) { w.set_minimized(minimized); return IterationDecision::Continue; }); @@ -2139,21 +2134,21 @@ void WindowManager::minimize_windows(Window& window, bool minimized) void WindowManager::hide_windows(Window& window, bool hidden) { - for_each_window_in_modal_stack(window, [&](auto& w, bool) { + for_each_window_in_modal_chain(window, [&](auto& w) { w.set_hidden(hidden); - if (!hidden) pick_new_active_window(&window); - return IterationDecision::Continue; }); } void WindowManager::maximize_windows(Window& window, bool maximized) { - for_each_window_in_modal_stack(window, [&](auto& w, bool stack_top) { - if (stack_top) - w.set_maximized(maximized); + for_each_window_in_modal_chain(window, [&](auto& w) { + if (&window == &w) { + window.set_maximized(maximized); + return IterationDecision::Continue; + } if (w.is_minimized()) w.set_minimized(false); return IterationDecision::Continue; @@ -2162,7 +2157,7 @@ void WindowManager::maximize_windows(Window& window, bool maximized) void WindowManager::set_always_on_top(Window& window, bool always_on_top) { - for_each_window_in_modal_stack(window, [&](auto& w, bool) { + for_each_window_in_modal_chain(window, [&](auto& w) { w.set_always_on_top(always_on_top); return IterationDecision::Continue; }); diff --git a/Userland/Services/WindowServer/WindowManager.h b/Userland/Services/WindowServer/WindowManager.h index e41d50d1c4..339beaa00d 100644 --- a/Userland/Services/WindowServer/WindowManager.h +++ b/Userland/Services/WindowServer/WindowManager.h @@ -238,35 +238,31 @@ public: void maximize_windows(Window&, bool); void set_always_on_top(Window&, bool); - template - IterationDecision for_each_window_in_modal_stack(Window& window, Function f) + template + Window* for_each_window_in_modal_chain(Window& window, Callback callback) { - auto* blocking_modal_window = window.blocking_modal_window(); - if (blocking_modal_window || window.is_modal()) { - 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()) { - auto* blocked_by = parent->blocking_modal_window(); - if (!blocked_by || (blocked_by != modal_stack_top && !modal_stack_top->is_descendant_of(*blocked_by))) - break; - modal_stack.append(*parent); - if (!parent->is_modal()) - break; + Window* maybe_break = nullptr; + Function recurse = [&](Window& w) -> Window* { + if (!w.is_modal()) { + auto decision = callback(w); + if (decision == IterationDecision::Break) + return maybe_break = &w; } - if (!modal_stack.is_empty()) { - for (size_t i = modal_stack.size(); i > 0; i--) { - IterationDecision decision = f(modal_stack[i - 1], false); - if (decision != IterationDecision::Continue) - return decision; - } + for (auto& child : w.child_windows()) { + if (!child || child->is_destroyed() || !child->is_modal()) + continue; + auto decision = callback(*child); + if (decision == IterationDecision::Break) + return maybe_break = child; + recurse(*child); } - return f(*modal_stack_top, true); - } else { - // Not a modal window stack, just "iterate" over this window - return f(window, true); - } + return maybe_break; + }; + if (auto* modeless = window.modeless_ancestor(); modeless) + return recurse(*modeless); + return nullptr; } - bool is_window_in_modal_stack(Window& window_in_modal_stack, Window& other_window); + bool is_window_in_modal_chain(Window& chain_window, Window& other_window); Gfx::IntPoint get_recommended_window_position(Gfx::IntPoint const& desired); @@ -376,8 +372,6 @@ private: void tell_wm_about_current_window_stack(WMConnectionFromClient&); bool pick_new_active_window(Window*); - void do_move_to_front(Window&, bool, bool); - bool sync_config_to_disk(); [[nodiscard]] static WindowStack& get_rendering_window_stacks(WindowStack*&);