mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:17:44 +00:00
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.
This commit is contained in:
parent
4c7f95e2f8
commit
788b16cbf8
3 changed files with 55 additions and 72 deletions
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
||||
Compositor::the().invalidate_occlusions();
|
||||
}
|
||||
|
||||
void WindowManager::do_move_to_front(Window& window, bool make_active, bool make_input)
|
||||
{
|
||||
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);
|
||||
|
||||
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<Menu>)
|
|||
|
||||
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;
|
||||
});
|
||||
|
|
|
@ -238,35 +238,31 @@ public:
|
|||
void maximize_windows(Window&, bool);
|
||||
void set_always_on_top(Window&, bool);
|
||||
|
||||
template<typename Function>
|
||||
IterationDecision for_each_window_in_modal_stack(Window& window, Function f)
|
||||
template<typename Callback>
|
||||
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<Window&> 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<Window*(Window&)> 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 maybe_break;
|
||||
};
|
||||
if (auto* modeless = window.modeless_ancestor(); modeless)
|
||||
return recurse(*modeless);
|
||||
return nullptr;
|
||||
}
|
||||
return f(*modal_stack_top, true);
|
||||
} else {
|
||||
// Not a modal window stack, just "iterate" over this window
|
||||
return f(window, true);
|
||||
}
|
||||
}
|
||||
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*&);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue