summaryrefslogtreecommitdiff
path: root/Userland/Services/WindowServer
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Services/WindowServer')
-rw-r--r--Userland/Services/WindowServer/Compositor.cpp250
-rw-r--r--Userland/Services/WindowServer/Compositor.h35
-rw-r--r--Userland/Services/WindowServer/Overlays.cpp36
-rw-r--r--Userland/Services/WindowServer/Overlays.h22
-rw-r--r--Userland/Services/WindowServer/Window.h4
-rw-r--r--Userland/Services/WindowServer/WindowManager.cpp417
-rw-r--r--Userland/Services/WindowServer/WindowManager.h166
-rw-r--r--Userland/Services/WindowServer/WindowStack.cpp33
-rw-r--r--Userland/Services/WindowServer/WindowStack.h106
-rw-r--r--Userland/Services/WindowServer/WindowSwitcher.cpp25
10 files changed, 885 insertions, 209 deletions
diff --git a/Userland/Services/WindowServer/Compositor.cpp b/Userland/Services/WindowServer/Compositor.cpp
index f851d64a88..f6bbb4458f 100644
--- a/Userland/Services/WindowServer/Compositor.cpp
+++ b/Userland/Services/WindowServer/Compositor.cpp
@@ -123,6 +123,9 @@ void Compositor::init_bitmaps()
void Compositor::did_construct_window_manager(Badge<WindowManager>)
{
auto& wm = WindowManager::the();
+
+ m_current_window_stack = &wm.current_window_stack();
+
m_wallpaper_mode = mode_to_enum(wm.config()->read_entry("Background", "Mode", "center"));
m_custom_background_color = Color::from_string(wm.config()->read_entry("Background", "Color", ""));
@@ -131,6 +134,17 @@ void Compositor::did_construct_window_manager(Badge<WindowManager>)
compose();
}
+Gfx::IntPoint Compositor::window_transition_offset(Window& window)
+{
+ if (WindowManager::is_stationary_window_type(window.type()))
+ return {};
+
+ if (window.is_moving_to_another_stack())
+ return {};
+
+ return window.outer_stack()->transition_offset();
+}
+
void Compositor::compose()
{
auto& wm = WindowManager::the();
@@ -158,33 +172,39 @@ void Compositor::compose()
auto dirty_screen_rects = move(m_dirty_screen_rects);
+ bool window_stack_transition_in_progress = m_transitioning_to_window_stack != nullptr;
+
// Mark window regions as dirty that need to be re-rendered
- wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& window) {
+ wm.for_each_visible_window_from_back_to_front([&](Window& window) {
+ auto transition_offset = window_transition_offset(window);
auto frame_rect = window.frame().render_rect();
+ auto frame_rect_on_screen = frame_rect.translated(transition_offset);
for (auto& dirty_rect : dirty_screen_rects.rects()) {
- auto invalidate_rect = dirty_rect.intersected(frame_rect);
+ auto invalidate_rect = dirty_rect.intersected(frame_rect_on_screen);
if (!invalidate_rect.is_empty()) {
auto inner_rect_offset = window.rect().location() - frame_rect.location();
- invalidate_rect.translate_by(-(frame_rect.location() + inner_rect_offset));
+ invalidate_rect.translate_by(-(frame_rect.location() + inner_rect_offset + transition_offset));
window.invalidate_no_notify(invalidate_rect);
m_invalidated_window = true;
}
}
window.prepare_dirty_rects();
+ if (window_stack_transition_in_progress)
+ window.dirty_rects().translate_by(transition_offset);
return IterationDecision::Continue;
});
// Any windows above or below a given window that need to be re-rendered
// also require us to re-render that window's intersecting area, regardless
// of whether that window has any dirty rectangles
- wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& window) {
+ wm.for_each_visible_window_from_back_to_front([&](Window& window) {
auto& transparency_rects = window.transparency_rects();
if (transparency_rects.is_empty())
return IterationDecision::Continue;
auto frame_rect = window.frame().render_rect();
auto& dirty_rects = window.dirty_rects();
- wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& w) {
+ wm.for_each_visible_window_from_back_to_front([&](Window& w) {
if (&w == &window)
return IterationDecision::Continue;
auto frame_rect2 = w.frame().render_rect();
@@ -311,8 +331,9 @@ void Compositor::compose()
// This window doesn't intersect with any screens, so there's nothing to render
return IterationDecision::Continue;
}
- auto frame_rect = window.frame().render_rect();
- auto window_rect = window.rect();
+ auto transition_offset = window_transition_offset(window);
+ auto frame_rect = window.frame().render_rect().translated(transition_offset);
+ auto window_rect = window.rect().translated(transition_offset);
auto frame_rects = frame_rect.shatter(window_rect);
dbgln_if(COMPOSE_DEBUG, " window {} frame rect: {}", window.title(), frame_rect);
@@ -323,8 +344,9 @@ void Compositor::compose()
rect.for_each_intersected(frame_rects, [&](const Gfx::IntRect& intersected_rect) {
Gfx::PainterStateSaver saver(painter);
painter.add_clip_rect(intersected_rect);
+ painter.translate(transition_offset);
dbgln_if(COMPOSE_DEBUG, " render frame: {}", intersected_rect);
- window.frame().paint(screen, painter, intersected_rect);
+ window.frame().paint(screen, painter, intersected_rect.translated(-transition_offset));
return IterationDecision::Continue;
});
}
@@ -481,7 +503,7 @@ void Compositor::compose()
compose_window(*fullscreen_window);
fullscreen_window->clear_dirty_rects();
} else {
- wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& window) {
+ wm.for_each_visible_window_from_back_to_front([&](Window& window) {
compose_window(window);
window.clear_dirty_rects();
return IterationDecision::Continue;
@@ -899,11 +921,14 @@ void Compositor::decrement_show_screen_number(Badge<ClientConnection>)
}
}
-bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_window, const Gfx::IntRect& rect)
+bool Compositor::any_opaque_window_above_this_one_contains_rect(Window& a_window, const Gfx::IntRect& rect)
{
+ auto* window_stack = a_window.outer_stack();
+ if (!window_stack)
+ return false;
bool found_containing_window = false;
bool checking = false;
- WindowManager::the().window_stack().for_each_visible_window_from_back_to_front([&](Window& window) {
+ WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& window) {
if (&window == &a_window) {
checking = true;
return IterationDecision::Continue;
@@ -942,6 +967,7 @@ void Compositor::overlay_rects_changed()
invalidate_occlusions();
for (auto& rect : m_overlay_rects.rects())
invalidate_screen(rect);
+ start_compose_async_timer();
}
void Compositor::recompute_overlay_rects()
@@ -973,17 +999,20 @@ void Compositor::recompute_overlay_rects()
void Compositor::recompute_occlusions()
{
auto& wm = WindowManager::the();
- wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& window) {
- if (wm.m_switcher.is_visible()) {
- window.set_occluded(false);
- } else {
+ bool is_switcher_visible = wm.m_switcher.is_visible();
+ wm.for_each_window_stack([&](WindowStack& window_stack) {
+ window_stack.set_all_occluded(!is_switcher_visible);
+ return IterationDecision::Continue;
+ });
+ if (!is_switcher_visible) {
+ wm.for_each_visible_window_from_back_to_front([&](Window& window) {
if (any_opaque_window_above_this_one_contains_rect(window, window.frame().rect()))
window.set_occluded(true);
else
window.set_occluded(false);
- }
- return IterationDecision::Continue;
- });
+ return IterationDecision::Continue;
+ });
+ }
if (m_overlay_rects_changed) {
m_overlay_rects_changed = false;
@@ -996,11 +1025,12 @@ void Compositor::recompute_occlusions()
dbgln(" overlay: {}", rect);
}
+ bool window_stack_transition_in_progress = m_transitioning_to_window_stack != nullptr;
auto& main_screen = Screen::main();
if (auto* fullscreen_window = wm.active_fullscreen_window()) {
// TODO: support fullscreen windows on all screens
auto screen_rect = main_screen.rect();
- WindowManager::the().window_stack().for_each_visible_window_from_front_to_back([&](Window& w) {
+ wm.for_each_visible_window_from_front_to_back([&](Window& w) {
auto& visible_opaque = w.opaque_rects();
auto& transparency_rects = w.transparency_rects();
auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects();
@@ -1029,7 +1059,7 @@ void Compositor::recompute_occlusions()
Gfx::DisjointRectSet visible_rects;
visible_rects.add_many(Screen::rects());
bool have_transparent = false;
- WindowManager::the().window_stack().for_each_visible_window_from_front_to_back([&](Window& w) {
+ wm.for_each_visible_window_from_front_to_back([&](Window& w) {
w.transparency_wallpaper_rects().clear();
auto& visible_opaque = w.opaque_rects();
visible_opaque.clear();
@@ -1039,8 +1069,13 @@ void Compositor::recompute_occlusions()
if (w.is_minimized())
return IterationDecision::Continue;
+ auto transition_offset = window_transition_offset(w);
auto transparent_frame_render_rects = w.frame().transparent_render_rects();
auto opaque_frame_render_rects = w.frame().opaque_render_rects();
+ if (window_stack_transition_in_progress) {
+ transparent_frame_render_rects.translate_by(transition_offset);
+ opaque_frame_render_rects.translate_by(transition_offset);
+ }
Gfx::DisjointRectSet visible_opaque_rects;
Screen::for_each([&](auto& screen) {
auto screen_rect = screen.rect();
@@ -1061,10 +1096,12 @@ void Compositor::recompute_occlusions()
visible_opaque = visible_rects.intersected(visible_opaque_rects);
auto render_rect = w.frame().render_rect();
-
+ auto render_rect_on_screen = render_rect;
+ if (window_stack_transition_in_progress)
+ render_rect_on_screen.translate_by(transition_offset);
Gfx::DisjointRectSet opaque_covering;
bool found_this_window = false;
- WindowManager::the().window_stack().for_each_visible_window_from_back_to_front([&](Window& w2) {
+ wm.for_each_visible_window_from_back_to_front([&](Window& w2) {
if (!found_this_window) {
if (&w == &w2)
found_this_window = true;
@@ -1074,17 +1111,28 @@ void Compositor::recompute_occlusions()
if (w2.is_minimized())
return IterationDecision::Continue;
- if (!render_rect.intersects(w2.frame().render_rect()))
+ auto w2_render_rect = w2.frame().render_rect();
+ auto w2_render_rect_on_screen = w2_render_rect;
+ auto w2_transition_offset = window_transition_offset(w2);
+ if (window_stack_transition_in_progress)
+ w2_render_rect_on_screen.translate_by(w2_transition_offset);
+ if (!render_rect_on_screen.intersects(w2_render_rect_on_screen))
return IterationDecision::Continue;
- auto opaque_rects = w2.frame().opaque_render_rects().intersected(render_rect);
- auto transparent_rects = w2.frame().transparent_render_rects().intersected(render_rect);
+ auto opaque_rects = w2.frame().opaque_render_rects();
+ auto transparent_rects = w2.frame().transparent_render_rects();
+ if (window_stack_transition_in_progress) {
+ auto transition_offset_2 = window_transition_offset(w2);
+ opaque_rects.translate_by(transition_offset_2);
+ transparent_rects.translate_by(transition_offset_2);
+ }
+ opaque_rects = opaque_rects.intersected(render_rect_on_screen);
+ transparent_rects = transparent_rects.intersected(render_rect_on_screen);
if (opaque_rects.is_empty() && transparent_rects.is_empty())
return IterationDecision::Continue;
-
for (auto& covering : opaque_rects.rects()) {
opaque_covering.add(covering);
- if (opaque_covering.contains(render_rect)) {
+ if (opaque_covering.contains(render_rect_on_screen)) {
// This window (including frame) is entirely covered by another opaque window
visible_opaque.clear();
transparency_rects.clear();
@@ -1156,7 +1204,7 @@ void Compositor::recompute_occlusions()
if (have_transparent) {
// Determine what transparent window areas need to render the wallpaper first
- WindowManager::the().window_stack().for_each_visible_window_from_back_to_front([&](Window& w) {
+ wm.for_each_visible_window_from_back_to_front([&](Window& w) {
auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects();
if (w.is_minimized()) {
transparency_wallpaper_rects.clear();
@@ -1184,7 +1232,7 @@ void Compositor::recompute_occlusions()
dbgln(" wallpaper opaque: {}", r);
}
- wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& w) {
+ wm.for_each_visible_window_from_back_to_front([&](Window& w) {
auto window_frame_rect = w.frame().render_rect();
if (w.is_minimized() || window_frame_rect.is_empty() || w.screens().is_empty())
return IterationDecision::Continue;
@@ -1237,4 +1285,148 @@ void Compositor::update_animations(Screen& screen, Gfx::DisjointRectSet& flush_r
}
}
+void Compositor::create_window_stack_switch_overlay(WindowStack& target_stack)
+{
+ if (m_stack_switch_overlay_timer) {
+ // Cancel any timer, we're going to delete the overlay
+ m_stack_switch_overlay_timer->stop();
+ m_stack_switch_overlay_timer = nullptr;
+ }
+ Screen::for_each([&](auto& screen) {
+ auto& screen_data = m_screen_data[screen.index()];
+ screen_data.m_window_stack_switch_overlay = nullptr; // delete it first
+ screen_data.m_window_stack_switch_overlay = create_overlay<WindowStackSwitchOverlay>(screen, target_stack);
+ screen_data.m_window_stack_switch_overlay->set_enabled(true);
+ return IterationDecision::Continue;
+ });
+}
+
+void Compositor::start_window_stack_switch_overlay_timer()
+{
+ if (m_stack_switch_overlay_timer) {
+ m_stack_switch_overlay_timer->stop();
+ m_stack_switch_overlay_timer = nullptr;
+ }
+ m_stack_switch_overlay_timer = Core::Timer::create_single_shot(
+ 500,
+ [this] {
+ Screen::for_each([&](auto& screen) {
+ auto& screen_data = m_screen_data[screen.index()];
+ screen_data.m_window_stack_switch_overlay = nullptr;
+ return IterationDecision::Continue;
+ });
+ },
+ this);
+ m_stack_switch_overlay_timer->start();
+}
+
+void Compositor::finish_window_stack_switch()
+{
+ VERIFY(m_transitioning_to_window_stack);
+ VERIFY(m_current_window_stack);
+ VERIFY(m_transitioning_to_window_stack != m_current_window_stack);
+
+ m_current_window_stack->set_transition_offset({}, {});
+ m_transitioning_to_window_stack->set_transition_offset({}, {});
+
+ auto* previous_window_stack = m_current_window_stack;
+ m_current_window_stack = m_transitioning_to_window_stack;
+ m_transitioning_to_window_stack = nullptr;
+
+ m_window_stack_transition_animation = nullptr;
+
+ auto& wm = WindowManager::the();
+ if (!wm.m_switcher.is_visible())
+ previous_window_stack->set_all_occluded(true);
+ wm.did_switch_window_stack({}, *previous_window_stack, *m_current_window_stack);
+
+ invalidate_occlusions();
+
+ // Rather than invalidating the entire we could invalidate all render rectangles
+ // that are affected by the transition offset before and after changing it.
+ invalidate_screen();
+
+ start_window_stack_switch_overlay_timer();
+}
+
+void Compositor::switch_to_window_stack(WindowStack& new_window_stack)
+{
+ if (m_transitioning_to_window_stack) {
+ if (m_transitioning_to_window_stack == &new_window_stack)
+ return;
+ // A switch is in progress, but the user is impatient. Finish the transition instantly
+ finish_window_stack_switch();
+ VERIFY(!m_window_stack_transition_animation);
+ // Now switch to the next target as usual
+ }
+ VERIFY(m_current_window_stack);
+
+ if (&new_window_stack == m_current_window_stack) {
+ // So that the user knows which stack they're on, show the overlay briefly
+ create_window_stack_switch_overlay(*m_current_window_stack);
+ start_window_stack_switch_overlay_timer();
+ return;
+ }
+ VERIFY(!m_transitioning_to_window_stack);
+ m_transitioning_to_window_stack = &new_window_stack;
+
+ auto window_stack_size = Screen::bounding_rect().size();
+
+ int delta_x = 0;
+ if (new_window_stack.column() < m_current_window_stack->column())
+ delta_x = window_stack_size.width();
+ else if (new_window_stack.column() > m_current_window_stack->column())
+ delta_x = -window_stack_size.width();
+ int delta_y = 0;
+ if (new_window_stack.row() < m_current_window_stack->row())
+ delta_y = window_stack_size.height();
+ else if (new_window_stack.row() > m_current_window_stack->row()) {
+ delta_y = -window_stack_size.height();
+ }
+
+ m_transitioning_to_window_stack->set_transition_offset({}, { -delta_x, -delta_y });
+ m_current_window_stack->set_transition_offset({}, {});
+
+ create_window_stack_switch_overlay(*m_transitioning_to_window_stack);
+ // We start the timer when the animation ends!
+
+ VERIFY(!m_window_stack_transition_animation);
+ m_window_stack_transition_animation = Animation::create();
+ m_window_stack_transition_animation->set_duration(250);
+ m_window_stack_transition_animation->on_update = [this, delta_x, delta_y](float progress, Gfx::Painter&, Screen&, Gfx::DisjointRectSet&) {
+ VERIFY(m_transitioning_to_window_stack);
+ VERIFY(m_current_window_stack);
+
+ // Set transition offset for the window stack we're transitioning out of
+ auto previous_transition_offset_from = m_current_window_stack->transition_offset();
+ Gfx::IntPoint transition_offset_from { (float)delta_x * progress, (float)delta_y * progress };
+ if (previous_transition_offset_from == transition_offset_from)
+ return;
+
+ {
+ // we need to render both, the existing dirty rectangles as well as where we're shifting to
+ auto translated_dirty_rects = m_dirty_screen_rects.clone();
+ auto transition_delta = transition_offset_from - previous_transition_offset_from;
+ translated_dirty_rects.translate_by(transition_delta);
+ m_dirty_screen_rects.add(translated_dirty_rects.intersected(Screen::bounding_rect()));
+ }
+ m_current_window_stack->set_transition_offset({}, transition_offset_from);
+
+ // Set transition offset for the window stack we're transitioning to
+ Gfx::IntPoint transition_offset_to { (float)-delta_x * (1.0f - progress), (float)-delta_y * (1.0f - progress) };
+ m_transitioning_to_window_stack->set_transition_offset({}, transition_offset_to);
+
+ invalidate_occlusions();
+
+ // Rather than invalidating the entire we could invalidate all render rectangles
+ // that are affected by the transition offset before and after changing it.
+ invalidate_screen();
+ };
+
+ m_window_stack_transition_animation->on_stop = [this] {
+ finish_window_stack_switch();
+ };
+ m_window_stack_transition_animation->start();
+}
+
}
diff --git a/Userland/Services/WindowServer/Compositor.h b/Userland/Services/WindowServer/Compositor.h
index 3d9e719cb6..f869d9d51f 100644
--- a/Userland/Services/WindowServer/Compositor.h
+++ b/Userland/Services/WindowServer/Compositor.h
@@ -22,6 +22,7 @@ class Cursor;
class MultiScaleBitmaps;
class Window;
class WindowManager;
+class WindowStack;
enum class WallpaperMode {
Tile,
@@ -92,6 +93,27 @@ public:
return IterationDecision::Continue;
}
+ template<typename F>
+ IterationDecision for_each_rendering_window_stack(F f)
+ {
+ VERIFY(m_current_window_stack);
+ IterationDecision decision = f(*m_current_window_stack);
+ if (decision != IterationDecision::Continue)
+ return decision;
+ if (m_transitioning_to_window_stack)
+ decision = f(*m_transitioning_to_window_stack);
+ return decision;
+ }
+
+ [[nodiscard]] WindowStack& get_rendering_window_stacks(WindowStack*& transitioning_window_stack)
+ {
+ transitioning_window_stack = m_transitioning_to_window_stack;
+ return *m_current_window_stack;
+ }
+
+ bool is_switching_window_stacks() const { return m_transitioning_to_window_stack != nullptr; }
+ void switch_to_window_stack(WindowStack&);
+
void did_construct_window_manager(Badge<WindowManager>);
const Gfx::Bitmap* cursor_bitmap_for_screenshot(Badge<ClientConnection>, Screen&) const;
@@ -114,10 +136,14 @@ private:
void start_compose_async_timer();
void recompute_overlay_rects();
void recompute_occlusions();
- bool any_opaque_window_above_this_one_contains_rect(const Window&, const Gfx::IntRect&);
+ bool any_opaque_window_above_this_one_contains_rect(Window&, const Gfx::IntRect&);
void change_cursor(const Cursor*);
void flush(Screen&);
+ Gfx::IntPoint window_transition_offset(Window&);
void update_animations(Screen&, Gfx::DisjointRectSet& flush_rects);
+ void create_window_stack_switch_overlay(WindowStack&);
+ void start_window_stack_switch_overlay_timer();
+ void finish_window_stack_switch();
RefPtr<Core::Timer> m_compose_timer;
RefPtr<Core::Timer> m_immediate_compose_timer;
@@ -139,6 +165,7 @@ private:
OwnPtr<Gfx::Painter> m_cursor_back_painter;
Gfx::IntRect m_last_cursor_rect;
OwnPtr<ScreenNumberOverlay> m_screen_number_overlay;
+ OwnPtr<WindowStackSwitchOverlay> m_window_stack_switch_overlay;
bool m_buffers_are_flipped { false };
bool m_screen_can_set_buffer { false };
bool m_cursor_back_is_valid { false };
@@ -197,6 +224,12 @@ private:
RefPtr<Core::Timer> m_display_link_notify_timer;
size_t m_display_link_count { 0 };
+ WindowStack* m_current_window_stack { nullptr };
+ WindowStack* m_transitioning_to_window_stack { nullptr };
+ RefPtr<Animation> m_window_stack_transition_animation;
+ OwnPtr<WindowStackSwitchOverlay> m_stack_switch_overlay;
+ RefPtr<Core::Timer> m_stack_switch_overlay_timer;
+
size_t m_show_screen_number_count { 0 };
Optional<Gfx::Color> m_custom_background_color;
diff --git a/Userland/Services/WindowServer/Overlays.cpp b/Userland/Services/WindowServer/Overlays.cpp
index c5cedf2907..f183375537 100644
--- a/Userland/Services/WindowServer/Overlays.cpp
+++ b/Userland/Services/WindowServer/Overlays.cpp
@@ -311,4 +311,40 @@ RefPtr<Gfx::Bitmap> DndOverlay::create_bitmap(int scale_factor)
return new_bitmap;
}
+void WindowStackSwitchOverlay::render_overlay_bitmap(Gfx::Painter& painter)
+{
+ // We should come up with a more elegant way to get the content rectangle
+ Gfx::IntRect content_rect({}, m_content_size);
+ content_rect.center_within({ {}, rect().size() });
+ auto active_color = WindowManager::the().palette().active_window_border1();
+ auto inactive_color = WindowManager::the().palette().inactive_window_border1();
+ for (int y = 0; y < m_rows; y++) {
+ for (int x = 0; x < m_columns; x++) {
+ Gfx::IntRect rect {
+ content_rect.left() + x * (default_screen_rect_width + default_screen_rect_padding),
+ content_rect.top() + y * (default_screen_rect_height + default_screen_rect_padding),
+ default_screen_rect_width,
+ default_screen_rect_height
+ };
+ bool is_target = y == m_target_row && x == m_target_column;
+ painter.fill_rect(rect, is_target ? active_color : inactive_color);
+ }
+ }
+}
+
+WindowStackSwitchOverlay::WindowStackSwitchOverlay(Screen& screen, WindowStack& target_window_stack)
+ : m_rows((int)WindowManager::the().window_stack_rows())
+ , m_columns((int)WindowManager::the().window_stack_columns())
+ , m_target_row((int)target_window_stack.row())
+ , m_target_column((int)target_window_stack.column())
+{
+ m_content_size = {
+ m_columns * (default_screen_rect_width + default_screen_rect_padding) - default_screen_rect_padding,
+ m_rows * (default_screen_rect_height + default_screen_rect_padding) - default_screen_rect_padding,
+ };
+ auto rect = calculate_frame_rect(Gfx::IntRect({}, m_content_size).inflated(2 * default_screen_rect_margin, 2 * default_screen_rect_margin));
+ rect.center_within(screen.rect());
+ set_rect(rect);
+}
+
}
diff --git a/Userland/Services/WindowServer/Overlays.h b/Userland/Services/WindowServer/Overlays.h
index b675e9b0c9..f94a870bd0 100644
--- a/Userland/Services/WindowServer/Overlays.h
+++ b/Userland/Services/WindowServer/Overlays.h
@@ -17,6 +17,7 @@ namespace WindowServer {
class Screen;
class Window;
+class WindowStack;
class Overlay {
friend class Compositor;
@@ -27,6 +28,7 @@ public:
enum class ZOrder {
WindowGeometry,
Dnd,
+ WindowStackSwitch,
ScreenNumber,
};
[[nodiscard]] virtual ZOrder zorder() const = 0;
@@ -168,4 +170,24 @@ private:
Gfx::IntRect m_label_rect;
};
+class WindowStackSwitchOverlay : public RectangularOverlay {
+public:
+ static constexpr int default_screen_rect_width = 40;
+ static constexpr int default_screen_rect_height = 30;
+ static constexpr int default_screen_rect_margin = 16;
+ static constexpr int default_screen_rect_padding = 8;
+
+ WindowStackSwitchOverlay(Screen&, WindowStack&);
+
+ virtual ZOrder zorder() const override { return ZOrder::WindowStackSwitch; }
+ virtual void render_overlay_bitmap(Gfx::Painter&) override;
+
+private:
+ Gfx::IntSize m_content_size;
+ const int m_rows;
+ const int m_columns;
+ const int m_target_row;
+ const int m_target_column;
+};
+
}
diff --git a/Userland/Services/WindowServer/Window.h b/Userland/Services/WindowServer/Window.h
index 09fc32431d..f6728da7f5 100644
--- a/Userland/Services/WindowServer/Window.h
+++ b/Userland/Services/WindowServer/Window.h
@@ -330,6 +330,9 @@ public:
frame().window_was_constructed({});
}
+ void set_moving_to_another_stack(bool value) { m_moving_to_another_stack = value; }
+ bool is_moving_to_another_stack() const { return m_moving_to_another_stack; }
+
private:
Window(ClientConnection&, WindowType, int window_id, bool modal, bool minimizable, bool frameless, bool resizable, bool fullscreen, bool accessory, Window* parent_window = nullptr);
Window(Core::Object&, WindowType);
@@ -382,6 +385,7 @@ private:
bool m_invalidated_frame { true };
bool m_hit_testing_enabled { true };
bool m_modified { false };
+ bool m_moving_to_another_stack { false };
WindowTileType m_tiled { WindowTileType::None };
Gfx::IntRect m_untiled_rect;
bool m_occluded { false };
diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp
index a2912affb6..039efac1bc 100644
--- a/Userland/Services/WindowServer/WindowManager.cpp
+++ b/Userland/Services/WindowServer/WindowManager.cpp
@@ -39,6 +39,26 @@ WindowManager::WindowManager(Gfx::PaletteImpl const& palette)
{
s_the = this;
+ {
+ // Create the default window stacks
+ auto row_count = m_window_stacks.capacity();
+ VERIFY(row_count > 0);
+ for (size_t row_index = 0; row_index < row_count; row_index++) {
+ auto row = adopt_own(*new RemoveReference<decltype(m_window_stacks[0])>());
+ auto column_count = row->capacity();
+ VERIFY(column_count > 0);
+ for (size_t column_index = 0; column_index < column_count; column_index++)
+ row->append(adopt_own(*new WindowStack(row_index, column_index)));
+ m_window_stacks.append(move(row));
+ }
+
+ m_current_window_stack = &m_window_stacks[0][0];
+ for (auto& row : m_window_stacks) {
+ for (auto& stack : row)
+ stack.set_stationary_window_stack(*m_current_window_stack);
+ }
+ }
+
reload_config();
Compositor::the().did_construct_window_manager({});
@@ -123,9 +143,12 @@ bool WindowManager::set_screen_layout(ScreenLayout&& screen_layout, bool save, S
client.notify_about_new_screen_rects(Screen::rects(), Screen::main().index());
});
- m_window_stack.for_each_window([](Window& window) {
- window.screens().clear_with_capacity();
- window.recalculate_rect();
+ for_each_window_stack([&](auto& window_stack) {
+ window_stack.for_each_window([](Window& window) {
+ window.screens().clear_with_capacity();
+ window.recalculate_rect();
+ return IterationDecision::Continue;
+ });
return IterationDecision::Continue;
});
@@ -178,11 +201,23 @@ int WindowManager::double_click_speed() const
return m_double_click_speed;
}
+WindowStack& WindowManager::window_stack_for_window(Window& window)
+{
+ if (is_stationary_window_type(window.type()))
+ return m_window_stacks[0][0];
+ if (auto* parent = window.parent_window(); parent && !is_stationary_window_type(parent->type())) {
+ if (auto* parent_window_stack = parent->outer_stack())
+ return *parent_window_stack;
+ }
+ return current_window_stack();
+}
+
void WindowManager::add_window(Window& window)
{
- bool is_first_window = m_window_stack.is_empty();
+ auto& window_stack = window_stack_for_window(window);
+ bool is_first_window = window_stack.is_empty();
- m_window_stack.add(window);
+ window_stack.add(window);
if (window.is_fullscreen()) {
auto& screen = Screen::main(); // TODO: support fullscreen windows on other screens!
@@ -235,7 +270,9 @@ void WindowManager::move_to_front_and_make_active(Window& window)
void WindowManager::do_move_to_front(Window& window, bool make_active, bool make_input)
{
- m_window_stack.move_to_front(window);
+ auto* window_stack = window.outer_stack();
+ VERIFY(window_stack);
+ window_stack->move_to_front(window);
if (make_active)
set_active_window(&window, make_input);
@@ -256,11 +293,11 @@ void WindowManager::do_move_to_front(Window& window, bool make_active, bool make
void WindowManager::remove_window(Window& window)
{
-
check_hide_geometry_overlay(window);
- m_window_stack.remove(window);
auto* active = active_window();
auto* active_input = active_input_window();
+ if (auto* window_stack = window.outer_stack())
+ window_stack->remove(window);
if (active == &window || active_input == &window || (active && window.is_descendant_of(*active)) || (active_input && active_input != active && window.is_descendant_of(*active_input)))
pick_new_active_window(&window);
@@ -285,11 +322,14 @@ void WindowManager::greet_window_manager(WMClientConnection& conn)
if (conn.window_id() < 0)
return;
- m_window_stack.for_each_window([&](Window& other_window) {
- //if (conn.window_id() != other_window.window_id()) {
- tell_wm_about_window(conn, other_window);
- tell_wm_about_window_icon(conn, other_window);
- //}
+ for_each_window_stack([&](auto& window_stack) {
+ window_stack.for_each_window([&](Window& other_window) {
+ //if (conn.window_id() != other_window.window_id()) {
+ tell_wm_about_window(conn, other_window);
+ tell_wm_about_window_icon(conn, other_window);
+ //}
+ return IterationDecision::Continue;
+ });
return IterationDecision::Continue;
});
if (auto* applet_area_window = AppletManager::the().window())
@@ -473,7 +513,8 @@ bool WindowManager::pick_new_active_window(Window* previous_active)
{
bool new_window_picked = false;
Window* first_candidate = nullptr;
- m_window_stack.for_each_visible_window_from_front_to_back([&](Window& candidate) {
+
+ for_each_visible_window_from_front_to_back([&](Window& candidate) {
if (candidate.type() != WindowType::Normal && candidate.type() != WindowType::ToolWindow)
return IterationDecision::Continue;
if (candidate.is_destroyed())
@@ -555,7 +596,7 @@ void WindowManager::start_window_resize(Window& window, Gfx::IntPoint const& pos
m_geometry_overlay = Compositor::the().create_overlay<WindowGeometryOverlay>(window);
m_geometry_overlay->set_enabled(true);
- m_active_input_tracking_window = nullptr;
+ current_window_stack().set_active_input_tracking_window(nullptr);
window.invalidate(true, true);
@@ -803,7 +844,7 @@ bool WindowManager::process_ongoing_drag(MouseEvent& event)
m_dnd_overlay->cursor_moved();
// We didn't let go of the drag yet, see if we should send some drag move events..
- m_window_stack.for_each_visible_window_from_front_to_back([&](Window& window) {
+ for_each_visible_window_from_front_to_back([&](Window& window) {
if (!window.rect().contains(event.position()))
return IterationDecision::Continue;
event.set_drag(true);
@@ -816,7 +857,7 @@ bool WindowManager::process_ongoing_drag(MouseEvent& event)
if (!(event.type() == Event::MouseUp && event.button() == MouseButton::Left))
return true;
- if (auto* window = m_window_stack.window_at(event.position())) {
+ if (auto* window = current_window_stack().window_at(event.position())) {
m_dnd_client->async_drag_accepted();
if (window->client()) {
auto translated_event = event.translated(-window->position());
@@ -971,7 +1012,9 @@ void WindowManager::deliver_mouse_event(Window& window, MouseEvent const& event,
bool WindowManager::process_ongoing_active_input_mouse_event(MouseEvent const& event)
{
- if (!m_active_input_tracking_window)
+ auto& window_stack = current_window_stack();
+ auto* input_tracking_window = window_stack.active_input_tracking_window();
+ if (!input_tracking_window)
return false;
// At this point, we have delivered the start of an input sequence to a
@@ -980,11 +1023,10 @@ bool WindowManager::process_ongoing_active_input_mouse_event(MouseEvent const& e
//
// This prevents e.g. moving on one window out of the bounds starting
// a move in that other unrelated window, and other silly shenanigans.
- deliver_mouse_event(*m_active_input_tracking_window, event, true);
+ deliver_mouse_event(*input_tracking_window, event, true);
- if (event.type() == Event::MouseUp && event.buttons() == 0) {
- m_active_input_tracking_window = nullptr;
- }
+ if (event.type() == Event::MouseUp && event.buttons() == 0)
+ window_stack.set_active_input_tracking_window(nullptr);
return true;
}
@@ -1049,7 +1091,7 @@ void WindowManager::process_mouse_event_for_window(HitTestResult& result, MouseE
}
if (event.type() == Event::MouseDown)
- m_active_input_tracking_window = window;
+ current_window_stack().set_active_input_tracking_window(&window);
}
void WindowManager::process_mouse_event(MouseEvent& event)
@@ -1065,8 +1107,9 @@ void WindowManager::process_mouse_event(MouseEvent& event)
// 2. Send the mouse event to all windows with global cursor tracking enabled.
// The active input tracking window is excluded here because we're sending the event to it
// in the next step.
- m_window_stack.for_each_visible_window_from_front_to_back([&](Window& window) {
- if (window.global_cursor_tracking() && &window != m_active_input_tracking_window)
+ auto& window_stack = current_window_stack();
+ for_each_visible_window_from_front_to_back([&](Window& window) {
+ if (window.global_cursor_tracking() && &window != window_stack.active_input_tracking_window())
deliver_mouse_event(window, event, false);
return IterationDecision::Continue;
});
@@ -1112,7 +1155,7 @@ void WindowManager::process_mouse_event(MouseEvent& event)
}
// 8. Hit test the window stack to see what's under the cursor.
- auto result = m_window_stack.hit_test(event.position());
+ auto result = current_window_stack().hit_test(event.position());
if (!result.has_value()) {
// No window is under the cursor.
@@ -1144,7 +1187,7 @@ void WindowManager::reevaluate_hovered_window(Window* updated_window)
if (fullscreen_window->hit_test(cursor_location).has_value())
hovered_window = fullscreen_window;
} else {
- hovered_window = m_window_stack.window_at(cursor_location);
+ hovered_window = current_window_stack().window_at(cursor_location);
}
if (set_hovered_window(hovered_window)) {
@@ -1213,7 +1256,8 @@ void WindowManager::event(Core::Event& event)
m_previous_event_was_super_keydown = false;
process_mouse_event(mouse_event);
- set_hovered_window(m_window_stack.window_at(mouse_event.position(), WindowStack::IncludeWindowFrame::No));
+ // TODO: handle transitioning between two stacks
+ set_hovered_window(current_window_stack().window_at(mouse_event.position(), WindowStack::IncludeWindowFrame::No));
return;
}
@@ -1225,6 +1269,117 @@ 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)
+{
+ auto result = for_each_window_in_modal_stack(window_in_modal_stack, [&](auto& window, auto) {
+ if (&other_window == &window)
+ return IterationDecision::Break;
+ for (auto& accessory : window.accessory_windows()) {
+ if (accessory.ptr() == &other_window)
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ });
+ return result == IterationDecision::Break;
+}
+
+void WindowManager::switch_to_window_stack(WindowStack& window_stack, Window* carry_window)
+{
+ m_carry_window_to_new_stack.clear();
+ m_switching_to_window_stack = &window_stack;
+ if (carry_window && !is_stationary_window_type(carry_window->type()) && carry_window->outer_stack() != &window_stack) {
+ auto& from_stack = *carry_window->outer_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.outer_stack() != carry_window->outer_stack())
+ return IterationDecision::Continue;
+ if (&window == carry_window || ((carry_window->is_modal() || blocking_modal) && is_window_in_modal_stack(*carry_window, window)))
+ m_carry_window_to_new_stack.append(window);
+ return IterationDecision::Continue;
+ },
+ &from_stack);
+
+ auto* from_active_window = from_stack.active_window();
+ auto* from_active_input_window = from_stack.active_input_window();
+ bool did_carry_active_window = false;
+ bool did_carry_active_input_window = false;
+ for (auto& window : m_carry_window_to_new_stack) {
+ if (window == from_active_window)
+ did_carry_active_window = true;
+ if (window == from_active_input_window)
+ did_carry_active_input_window = true;
+ window->set_moving_to_another_stack(true);
+ VERIFY(window->outer_stack() == &from_stack);
+ from_stack.remove(*window);
+ window_stack.add(*window);
+ }
+ // Before we change to the new stack, find a new active window on the stack we're switching from
+ if (did_carry_active_window || did_carry_active_input_window)
+ pick_new_active_window(from_active_window);
+
+ // Now switch to the new stack
+ m_current_window_stack = &window_stack;
+ if (did_carry_active_window && from_active_window)
+ set_active_window(from_active_window, from_active_input_window == from_active_window);
+ if (did_carry_active_input_window && from_active_input_window && from_active_input_window != from_active_window)
+ set_active_input_window(from_active_input_window);
+
+ // Because we moved windows between stacks we need to invalidate occlusions
+ Compositor::the().invalidate_occlusions();
+ } else {
+ m_current_window_stack = &window_stack;
+ }
+
+ Compositor::the().switch_to_window_stack(window_stack);
+}
+
+void WindowManager::did_switch_window_stack(Badge<Compositor>, WindowStack& previous_stack, WindowStack& new_stack)
+{
+ VERIFY(&previous_stack != &new_stack);
+
+ // We are being notified by the compositor, it should not be switching right now!
+ VERIFY(!Compositor::the().is_switching_window_stacks());
+ VERIFY(&current_window_stack() == &new_stack);
+
+ if (m_switching_to_window_stack == &new_stack) {
+ m_switching_to_window_stack = nullptr;
+ if (!m_carry_window_to_new_stack.is_empty()) {
+ // switched_to_stack may be different from the stack where the windows were
+ // carried to when the user rapidly tries to switch stacks, so make sure to
+ // only reset the moving flag if we arrived at our final destination
+ for (auto& window_ref : m_carry_window_to_new_stack) {
+ if (auto* window = window_ref.ptr())
+ window->set_moving_to_another_stack(false);
+ }
+ m_carry_window_to_new_stack.clear();
+ }
+ }
+
+ auto* previous_stack_active_window = previous_stack.active_window();
+ auto* previous_stack_active_input_window = previous_stack.active_input_window();
+ auto* new_stack_active_window = new_stack.active_window();
+ auto* new_stack_active_input_window = new_stack.active_input_window();
+ if (previous_stack_active_input_window && previous_stack_active_input_window != new_stack_active_input_window)
+ notify_previous_active_input_window(*previous_stack_active_input_window);
+ if (new_stack_active_input_window && previous_stack_active_input_window != new_stack_active_input_window)
+ notify_new_active_input_window(*new_stack_active_input_window);
+ if (previous_stack_active_window != new_stack_active_window) {
+ if (previous_stack_active_window && is_stationary_window_type(previous_stack_active_window->type()))
+ notify_previous_active_window(*previous_stack_active_window);
+ if (new_stack_active_window && is_stationary_window_type(new_stack_active_window->type()))
+ notify_new_active_window(*new_stack_active_window);
+ }
+
+ if (!new_stack_active_input_window)
+ pick_new_active_window(nullptr);
+
+ reevaluate_hovered_window();
+}
+
void WindowManager::process_key_event(KeyEvent& event)
{
m_keyboard_modifiers = event.modifiers();
@@ -1258,7 +1413,7 @@ void WindowManager::process_key_event(KeyEvent& event)
m_previous_event_was_super_keydown = true;
} else if (m_previous_event_was_super_keydown) {
m_previous_event_was_super_keydown = false;
- if (!m_dnd_client && !m_active_input_tracking_window && event.type() == Event::KeyUp && event.key() == Key_Super) {
+ if (!m_dnd_client && !current_window_stack().active_input_tracking_window() && event.type() == Event::KeyUp && event.key() == Key_Super) {
tell_wms_super_key_pressed();
return;
}
@@ -1281,59 +1436,110 @@ void WindowManager::process_key_event(KeyEvent& event)
return;
}
- if (!m_active_input_window)
+ if (event.type() == Event::KeyDown && (event.modifiers() == (Mod_Ctrl | Mod_Alt) || event.modifiers() == (Mod_Ctrl | Mod_Shift | Mod_Alt)) && (window_stack_columns() > 1 || window_stack_rows() > 1)) {
+ auto& current_stack = current_window_stack();
+ auto row = current_stack.row();
+ auto column = current_stack.column();
+ auto handle_window_stack_switch_key = [&]() {
+ switch (event.key()) {
+ case Key_Left:
+ if (column == 0)
+ return true;
+ column--;
+ return true;
+ case Key_Right:
+ if (column + 1 >= m_window_stacks[0].size())
+ return true;
+ column++;
+ return true;
+ case Key_Up:
+ if (row == 0)
+ return true;
+ row--;
+ return true;
+ case Key_Down:
+ if (row + 1 >= m_window_stacks.size())
+ return true;
+ row++;
+ return true;
+ default:
+ return false;
+ }
+ };
+ if (handle_window_stack_switch_key()) {
+ Window* carry_window = nullptr;
+ auto& new_window_stack = m_window_stacks[row][column];
+ if (&new_window_stack != &current_stack) {
+ if (event.modifiers() == (Mod_Ctrl | Mod_Shift | Mod_Alt))
+ carry_window = this->active_window();
+ }
+ // Call switch_to_window_stack even if we're not going to switch to another stack.
+ // We'll show the window stack switch overlay briefly!
+ switch_to_window_stack(new_window_stack, carry_window);
+ return;
+ }
+ }
+
+ auto* active_input_window = current_window_stack().active_input_window();
+ if (!active_input_window)
return;
- if (event.type() == Event::KeyDown && event.modifiers() == Mod_Super && m_active_input_window->type() != WindowType::Desktop) {
+ if (event.type() == Event::KeyDown && event.modifiers() == Mod_Super && active_input_window->type() != WindowType::Desktop) {
if (event.key() == Key_Down) {
- if (m_active_input_window->is_resizable() && m_active_input_window->is_maximized()) {
- maximize_windows(*m_active_input_window, false);
+ if (active_input_window->is_resizable() && active_input_window->is_maximized()) {
+ maximize_windows(*active_input_window, false);
return;
}
- if (m_active_input_window->is_minimizable())
- minimize_windows(*m_active_input_window, true);
+ if (active_input_window->is_minimizable())
+ minimize_windows(*active_input_window, true);
return;
}
- if (m_active_input_window->is_resizable()) {
+ if (active_input_window->is_resizable()) {
if (event.key() == Key_Up) {
- maximize_windows(*m_active_input_window, !m_active_input_window->is_maximized());
+ maximize_windows(*active_input_window, !active_input_window->is_maximized());
return;
}
if (event.key() == Key_Left) {
- if (m_active_input_window->tiled() == WindowTileType::Left)
+ if (active_input_window->tiled() == WindowTileType::Left)
return;
- if (m_active_input_window->tiled() != WindowTileType::None) {
- m_active_input_window->set_untiled();
+ if (active_input_window->tiled() != WindowTileType::None) {
+ active_input_window->set_untiled();
return;
}
- if (m_active_input_window->is_maximized())
- maximize_windows(*m_active_input_window, false);
- m_active_input_window->set_tiled(nullptr, WindowTileType::Left);
+ if (active_input_window->is_maximized())
+ maximize_windows(*active_input_window, false);
+ active_input_window->set_tiled(nullptr, WindowTileType::Left);
return;
}
if (event.key() == Key_Right) {
- if (m_active_input_window->tiled() == WindowTileType::Right)
+ if (active_input_window->tiled() == WindowTileType::Right)
return;
- if (m_active_input_window->tiled() != WindowTileType::None) {
- m_active_input_window->set_untiled();
+ if (active_input_window->tiled() != WindowTileType::None) {
+ active_input_window->set_untiled();
return;
}
- if (m_active_input_window->is_maximized())
- maximize_windows(*m_active_input_window, false);
- m_active_input_window->set_tiled(nullptr, WindowTileType::Right);
+ if (active_input_window->is_maximized())
+ maximize_windows(*active_input_window, false);
+ active_input_window->set_tiled(nullptr, WindowTileType::Right);
return;
}
}
}
- m_active_input_window->dispatch_event(event);
+ active_input_window->dispatch_event(event);
}
void WindowManager::set_highlight_window(Window* new_highlight_window)
{
- if (new_highlight_window == m_window_stack.highlight_window())
+ // NOTE: The highlight window is global across all stacks. That's because we
+ // can only have one and we want to be able to highlight it during transitions
+ auto* previous_highlight_window = highlight_window();
+ if (new_highlight_window == previous_highlight_window)
return;
- auto* previous_highlight_window = m_window_stack.highlight_window();
- m_window_stack.set_highlight_window(new_highlight_window);
+ if (!new_highlight_window)
+ m_highlight_window = nullptr;
+ else
+ m_highlight_window = new_highlight_window->make_weak_ptr<Window>();
+
if (previous_highlight_window) {
previous_highlight_window->invalidate(true, true);
Compositor::the().invalidate_screen(previous_highlight_window->frame().render_rect());
@@ -1377,23 +1583,31 @@ void WindowManager::restore_active_input_window(Window* window)
Window* WindowManager::set_active_input_window(Window* window)
{
- if (window == m_active_input_window)
+ auto& window_stack = current_window_stack();
+ auto* previous_input_window = window_stack.active_input_window();
+ if (window == previous_input_window)
return window;
- Window* previous_input_window = m_active_input_window;
if (previous_input_window)
- Core::EventLoop::current().post_event(*previous_input_window, make<Event>(Event::WindowInputLeft));
+ notify_previous_active_input_window(*previous_input_window);
- if (window) {
- m_active_input_window = *window;
- Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowInputEntered));
- } else {
- m_active_input_window = nullptr;
- }
+ window_stack.set_active_input_window(window);
+ if (window)
+ notify_new_active_input_window(*window);
return previous_input_window;
}
+void WindowManager::notify_new_active_input_window(Window& new_input_window)
+{
+ Core::EventLoop::current().post_event(new_input_window, make<Event>(Event::WindowInputEntered));
+}
+
+void WindowManager::notify_previous_active_input_window(Window& previous_input_window)
+{
+ Core::EventLoop::current().post_event(previous_input_window, make<Event>(Event::WindowInputLeft));
+}
+
void WindowManager::set_active_window(Window* new_active_window, bool make_input)
{
if (new_active_window) {
@@ -1418,32 +1632,44 @@ void WindowManager::set_active_window(Window* new_active_window, bool make_input
if (make_input)
set_active_input_window(new_active_input_window);
- if (new_active_window == m_window_stack.active_window())
+ auto& window_stack = current_window_stack();
+ if (new_active_window == window_stack.active_window())
return;
- if (auto* previously_active_window = m_window_stack.active_window()) {
- for (auto& child_window : previously_active_window->child_windows()) {
- if (child_window && child_window->type() == WindowType::Tooltip)
- child_window->request_close();
- }
- Core::EventLoop::current().post_event(*previously_active_window, make<Event>(Event::WindowDeactivated));
- previously_active_window->invalidate(true, true);
- m_window_stack.set_active_window(nullptr);
- m_active_input_tracking_window = nullptr;
- tell_wms_window_state_changed(*previously_active_window);
+ if (auto* previously_active_window = window_stack.active_window()) {
+ window_stack.set_active_window(nullptr);
+ window_stack.set_active_input_tracking_window(nullptr);
+ notify_previous_active_window(*previously_active_window);
}
if (new_active_window) {
- m_window_stack.set_active_window(new_active_window);
- Core::EventLoop::current().post_event(*new_active_window, make<Event>(Event::WindowActivated));
- new_active_window->invalidate(true, true);
- tell_wms_window_state_changed(*new_active_window);
+ window_stack.set_active_window(new_active_window);
+ notify_new_active_window(*new_active_window);
}
// Window shapes may have changed (e.g. shadows for inactive/active windows)
Compositor::the().invalidate_occlusions();
}
+void WindowManager::notify_new_active_window(Window& new_active_window)
+{
+ Core::EventLoop::current().post_event(new_active_window, make<Event>(Event::WindowActivated));
+ new_active_window.invalidate(true, true);
+ tell_wms_window_state_changed(new_active_window);
+}
+
+void WindowManager::notify_previous_active_window(Window& previously_active_window)
+{
+ for (auto& child_window : previously_active_window.child_windows()) {
+ if (child_window && child_window->type() == WindowType::Tooltip)
+ child_window->request_close();
+ }
+ Core::EventLoop::current().post_event(previously_active_window, make<Event>(Event::WindowDeactivated));
+ previously_active_window.invalidate(true, true);
+
+ tell_wms_window_state_changed(previously_active_window);
+}
+
bool WindowManager::set_hovered_window(Window* window)
{
if (m_hovered_window == window)
@@ -1461,7 +1687,7 @@ bool WindowManager::set_hovered_window(Window* window)
ClientConnection const* WindowManager::active_client() const
{
- if (auto* window = m_window_stack.active_window())
+ if (auto* window = const_cast<WindowManager*>(this)->current_window_stack().active_window())
return window->client();
return nullptr;
}
@@ -1534,7 +1760,7 @@ Gfx::IntRect WindowManager::maximized_window_rect(Window const& window, bool rel
if (screen.is_main_screen()) {
// Subtract taskbar window height if present
- const_cast<WindowManager*>(this)->m_window_stack.for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, [&rect](Window& taskbar_window) {
+ const_cast<WindowManager*>(this)->current_window_stack().for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, [&rect](Window& taskbar_window) {
rect.set_height(rect.height() - taskbar_window.height());
return IterationDecision::Break;
});
@@ -1558,7 +1784,7 @@ void WindowManager::start_dnd_drag(ClientConnection& client, String const& text,
m_dnd_overlay->set_enabled(true);
m_dnd_mime_data = mime_data;
Compositor::the().invalidate_cursor();
- m_active_input_tracking_window = nullptr;
+ current_window_stack().set_active_input_tracking_window(nullptr);
}
void WindowManager::end_dnd_drag()
@@ -1574,8 +1800,11 @@ void WindowManager::invalidate_after_theme_or_font_change()
{
Compositor::the().set_background_color(m_config->read_entry("Background", "Color", palette().desktop_background().to_string()));
WindowFrame::reload_config();
- m_window_stack.for_each_window([&](Window& window) {
- window.frame().theme_changed();
+ for_each_window_stack([&](auto& window_stack) {
+ window_stack.for_each_window([&](Window& window) {
+ window.frame().theme_changed();
+ return IterationDecision::Continue;
+ });
return IterationDecision::Continue;
});
ClientConnection::for_each_client([&](ClientConnection& client) {
@@ -1604,10 +1833,11 @@ bool WindowManager::update_theme(String theme_path, String theme_name)
void WindowManager::did_popup_a_menu(Badge<Menu>)
{
// Clear any ongoing input gesture
- if (!m_active_input_tracking_window)
+ auto* active_input_tracking_window = current_window_stack().active_input_tracking_window();
+ if (!active_input_tracking_window)
return;
- m_active_input_tracking_window->set_automatic_cursor_tracking_enabled(false);
- m_active_input_tracking_window = nullptr;
+ active_input_tracking_window->set_automatic_cursor_tracking_enabled(false);
+ active_input_tracking_window = nullptr;
}
void WindowManager::minimize_windows(Window& window, bool minimized)
@@ -1638,7 +1868,7 @@ Gfx::IntPoint WindowManager::get_recommended_window_position(Gfx::IntPoint const
int taskbar_height = 28;
Window const* overlap_window = nullptr;
- m_window_stack.for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, [&](Window& window) {
+ current_window_stack().for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, [&](Window& window) {
if (window.default_positioned() && (!overlap_window || overlap_window->window_id() < window.window_id())) {
overlap_window = &window;
}
@@ -1663,9 +1893,12 @@ Gfx::IntPoint WindowManager::get_recommended_window_position(Gfx::IntPoint const
void WindowManager::reload_icon_bitmaps_after_scale_change()
{
reload_config();
- m_window_stack.for_each_window([&](Window& window) {
- auto& window_frame = window.frame();
- window_frame.theme_changed();
+ for_each_window_stack([&](auto& window_stack) {
+ window_stack.for_each_window([&](Window& window) {
+ auto& window_frame = window.frame();
+ window_frame.theme_changed();
+ return IterationDecision::Continue;
+ });
return IterationDecision::Continue;
});
}
@@ -1679,4 +1912,10 @@ void WindowManager::set_window_with_active_menu(Window* window)
else
m_window_with_active_menu = nullptr;
}
+
+WindowStack& WindowManager::get_rendering_window_stacks(WindowStack*& transitioning_window_stack)
+{
+ return Compositor::the().get_rendering_window_stacks(transitioning_window_stack);
+}
+
}
diff --git a/Userland/Services/WindowServer/WindowManager.h b/Userland/Services/WindowServer/WindowManager.h
index 3dfa0b6ab6..6d1b64e572 100644
--- a/Userland/Services/WindowServer/WindowManager.h
+++ b/Userland/Services/WindowServer/WindowManager.h
@@ -90,17 +90,36 @@ public:
void start_dnd_drag(ClientConnection&, String const& text, Gfx::Bitmap const*, Core::MimeData const&);
void end_dnd_drag();
- Window* active_window() { return m_window_stack.active_window(); }
- Window const* active_window() const { return m_window_stack.active_window(); }
- Window* active_input_window() { return m_active_input_window.ptr(); }
- Window const* active_input_window() const { return m_active_input_window.ptr(); }
+ Window* active_window()
+ {
+ VERIFY(m_current_window_stack);
+ return m_current_window_stack->active_window();
+ }
+ Window const* active_window() const
+ {
+ VERIFY(m_current_window_stack);
+ return m_current_window_stack->active_window();
+ }
+
+ Window* active_input_window()
+ {
+ VERIFY(m_current_window_stack);
+ return m_current_window_stack->active_input_window();
+ }
+ Window const* active_input_window() const
+ {
+ VERIFY(m_current_window_stack);
+ return m_current_window_stack->active_input_window();
+ }
+
ClientConnection const* active_client() const;
Window* window_with_active_menu() { return m_window_with_active_menu; }
Window const* window_with_active_menu() const { return m_window_with_active_menu; }
void set_window_with_active_menu(Window*);
- Window const* highlight_window() const { return m_window_stack.highlight_window(); }
+ Window* highlight_window() { return m_highlight_window; }
+ Window const* highlight_window() const { return m_highlight_window; }
void set_highlight_window(Window*);
void move_to_front_and_make_active(Window&);
@@ -223,6 +242,7 @@ public:
return f(window, true);
}
}
+ bool is_window_in_modal_stack(Window& window_in_modal_stack, Window& other_window);
Gfx::IntPoint get_recommended_window_position(Gfx::IntPoint const& desired);
@@ -231,13 +251,61 @@ public:
void reevaluate_hovered_window(Window* = nullptr);
Window* hovered_window() const { return m_hovered_window.ptr(); }
- WindowStack& window_stack() { return m_window_stack; }
+ void switch_to_window_stack(WindowStack&, Window* = nullptr);
+
+ size_t window_stack_rows() const { return m_window_stacks.size(); }
+ size_t window_stack_columns() const { return m_window_stacks[0].size(); }
+
+ WindowStack& current_window_stack()
+ {
+ VERIFY(m_current_window_stack);
+ return *m_current_window_stack;
+ }
+
+ template<typename F>
+ IterationDecision for_each_window_stack(F f)
+ {
+ for (auto& row : m_window_stacks) {
+ for (auto& stack : row) {
+ IterationDecision decision = f(stack);
+ if (decision != IterationDecision::Continue)
+ return decision;
+ }
+ }
+ return IterationDecision::Continue;
+ }
+
+ WindowStack& window_stack_for_window(Window&);
+
+ static constexpr bool is_stationary_window_type(WindowType window_type)
+ {
+ switch (window_type) {
+ case WindowType::Normal:
+ case WindowType::ToolWindow:
+ case WindowType::Tooltip:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ void did_switch_window_stack(Badge<Compositor>, WindowStack&, WindowStack&);
+
+ template<typename Callback>
+ IterationDecision for_each_visible_window_from_back_to_front(Callback, WindowStack* = nullptr);
+ template<typename Callback>
+ IterationDecision for_each_visible_window_from_front_to_back(Callback, WindowStack* = nullptr);
MultiScaleBitmaps const* overlay_rect_shadow() const { return m_overlay_rect_shadow.ptr(); }
private:
RefPtr<Cursor> get_cursor(String const& name);
+ void notify_new_active_window(Window&);
+ void notify_new_active_input_window(Window&);
+ void notify_previous_active_window(Window&);
+ void notify_previous_active_input_window(Window&);
+
void process_mouse_event(MouseEvent&);
void process_event_for_doubleclick(Window& window, MouseEvent& event);
bool process_ongoing_window_resize(MouseEvent const&);
@@ -260,6 +328,8 @@ private:
void do_move_to_front(Window&, bool, bool);
+ [[nodiscard]] static WindowStack& get_rendering_window_stacks(WindowStack*&);
+
RefPtr<Cursor> m_hidden_cursor;
RefPtr<Cursor> m_arrow_cursor;
RefPtr<Cursor> m_hand_cursor;
@@ -279,7 +349,9 @@ private:
RefPtr<MultiScaleBitmaps> m_overlay_rect_shadow;
- WindowStack m_window_stack;
+ // Setup 2 rows 1 column by default
+ NonnullOwnPtrVector<NonnullOwnPtrVector<WindowStack, 3>, 2> m_window_stacks;
+ WindowStack* m_current_window_stack { nullptr };
struct DoubleClickInfo {
struct ClickMetadata {
@@ -317,8 +389,7 @@ private:
bool m_previous_event_was_super_keydown { false };
WeakPtr<Window> m_hovered_window;
- WeakPtr<Window> m_active_input_window;
- WeakPtr<Window> m_active_input_tracking_window;
+ WeakPtr<Window> m_highlight_window;
WeakPtr<Window> m_window_with_active_menu;
OwnPtr<WindowGeometryOverlay> m_geometry_overlay;
@@ -349,9 +420,86 @@ private:
String m_dnd_text;
RefPtr<Core::MimeData> m_dnd_mime_data;
+
+ WindowStack* m_switching_to_window_stack { nullptr };
+ Vector<WeakPtr<Window>, 4> m_carry_window_to_new_stack;
};
template<typename Callback>
+inline IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Callback callback, WindowStack* specific_stack)
+{
+ auto* window_stack = specific_stack;
+ WindowStack* transitioning_to_window_stack = nullptr;
+ if (!window_stack)
+ window_stack = &get_rendering_window_stacks(transitioning_to_window_stack);
+ auto for_each_window = [&]<WindowType window_type>() {
+ if constexpr (is_stationary_window_type(window_type)) {
+ auto& stationary_stack = window_stack->stationary_window_stack();
+ return stationary_stack.for_each_visible_window_of_type_from_back_to_front(window_type, callback);
+ } else {
+ auto decision = window_stack->for_each_visible_window_of_type_from_back_to_front(window_type, callback);
+ if (decision == IterationDecision::Continue && transitioning_to_window_stack)
+ decision = transitioning_to_window_stack->for_each_visible_window_of_type_from_back_to_front(window_type, callback);
+ return decision;
+ }
+ };
+ if (for_each_window.template operator()<WindowType::Desktop>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Normal>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::ToolWindow>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Taskbar>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::AppletArea>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Notification>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Tooltip>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Menu>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ return for_each_window.template operator()<WindowType::WindowSwitcher>();
+}
+
+template<typename Callback>
+inline IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Callback callback, WindowStack* specific_stack)
+{
+ auto* window_stack = specific_stack;
+ WindowStack* transitioning_to_window_stack = nullptr;
+ if (!window_stack)
+ window_stack = &get_rendering_window_stacks(transitioning_to_window_stack);
+ auto for_each_window = [&]<WindowType window_type>() {
+ if constexpr (is_stationary_window_type(window_type)) {
+ auto& stationary_stack = window_stack->stationary_window_stack();
+ return stationary_stack.for_each_visible_window_of_type_from_front_to_back(window_type, callback);
+ } else {
+ auto decision = window_stack->for_each_visible_window_of_type_from_front_to_back(window_type, callback);
+ if (decision == IterationDecision::Continue && transitioning_to_window_stack)
+ decision = transitioning_to_window_stack->for_each_visible_window_of_type_from_front_to_back(window_type, callback);
+ return decision;
+ }
+ };
+ if (for_each_window.template operator()<WindowType::WindowSwitcher>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Menu>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Tooltip>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Notification>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::AppletArea>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Taskbar>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::ToolWindow>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ if (for_each_window.template operator()<WindowType::Normal>() == IterationDecision::Break)
+ return IterationDecision::Break;
+ return for_each_window.template operator()<WindowType::Desktop>();
+}
+
+template<typename Callback>
void WindowManager::for_each_window_manager(Callback callback)
{
auto& connections = WMClientConnection::s_connections;
diff --git a/Userland/Services/WindowServer/WindowStack.cpp b/Userland/Services/WindowServer/WindowStack.cpp
index 3b914f2b8b..f10a9641b2 100644
--- a/Userland/Services/WindowServer/WindowStack.cpp
+++ b/Userland/Services/WindowServer/WindowStack.cpp
@@ -5,10 +5,13 @@
*/
#include "WindowStack.h"
+#include "WindowManager.h"
namespace WindowServer {
-WindowStack::WindowStack()
+WindowStack::WindowStack(unsigned row, unsigned column)
+ : m_row(row)
+ , m_column(column)
{
}
@@ -28,6 +31,12 @@ void WindowStack::remove(Window& window)
VERIFY(window.outer_stack() == this);
m_windows.remove(window);
window.set_outer_stack({}, nullptr);
+ if (m_active_window == &window)
+ m_active_window = nullptr;
+ if (m_active_input_window == &window)
+ m_active_input_window = nullptr;
+ if (m_active_input_tracking_window == &window)
+ m_active_input_tracking_window = nullptr;
}
void WindowStack::move_to_front(Window& window)
@@ -48,12 +57,11 @@ Window* WindowStack::window_at(Gfx::IntPoint const& position, IncludeWindowFrame
return result->window;
}
-void WindowStack::set_highlight_window(Window* window)
+Window* WindowStack::highlight_window() const
{
- if (!window)
- m_highlight_window = nullptr;
- else
- m_highlight_window = window->make_weak_ptr<Window>();
+ if (auto* window = WindowManager::the().highlight_window(); window && window->outer_stack() == this)
+ return window;
+ return nullptr;
}
void WindowStack::set_active_window(Window* window)
@@ -64,15 +72,24 @@ void WindowStack::set_active_window(Window* window)
m_active_window = window->make_weak_ptr<Window>();
}
+void WindowStack::set_all_occluded(bool occluded)
+{
+ for (auto& window : m_windows) {
+ if (!WindowManager::is_stationary_window_type(window.type()))
+ window.set_occluded(occluded);
+ }
+}
+
Optional<HitTestResult> WindowStack::hit_test(Gfx::IntPoint const& position) const
{
Optional<HitTestResult> result;
- const_cast<WindowStack*>(this)->for_each_visible_window_from_front_to_back([&](Window& window) {
+ WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& window) {
result = window.hit_test(position);
if (result.has_value())
return IterationDecision::Break;
return IterationDecision::Continue;
- });
+ },
+ const_cast<WindowStack*>(this));
return result;
}
diff --git a/Userland/Services/WindowServer/WindowStack.h b/Userland/Services/WindowServer/WindowStack.h
index ccabb8bb6a..a4aaec944e 100644
--- a/Userland/Services/WindowServer/WindowStack.h
+++ b/Userland/Services/WindowServer/WindowStack.h
@@ -10,9 +10,11 @@
namespace WindowServer {
+class Compositor;
+
class WindowStack {
public:
- WindowStack();
+ WindowStack(unsigned row, unsigned column);
~WindowStack();
bool is_empty() const { return m_windows.is_empty(); }
@@ -25,12 +27,9 @@ public:
No,
};
Window* window_at(Gfx::IntPoint const&, IncludeWindowFrame = IncludeWindowFrame::Yes) const;
+ Window* highlight_window() const;
template<typename Callback>
- IterationDecision for_each_visible_window_from_back_to_front(Callback);
- template<typename Callback>
- IterationDecision for_each_visible_window_from_front_to_back(Callback);
- template<typename Callback>
IterationDecision for_each_visible_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false);
template<typename Callback>
IterationDecision for_each_visible_window_of_type_from_back_to_front(WindowType, Callback, bool ignore_highlight = false);
@@ -42,26 +41,51 @@ public:
Window::List& windows() { return m_windows; }
- Window* highlight_window() { return m_highlight_window; }
- Window const* highlight_window() const { return m_highlight_window; }
- void set_highlight_window(Window*);
-
Window* active_window() { return m_active_window; }
Window const* active_window() const { return m_active_window; }
void set_active_window(Window*);
+ Window* active_input_window() { return m_active_input_window; }
+ Window const* active_input_window() const { return m_active_input_window; }
+ void set_active_input_window(Window* window) { m_active_input_window = window; }
+
+ Window* active_input_tracking_window() { return m_active_input_tracking_window; }
+ Window const* active_input_tracking_window() const { return m_active_input_tracking_window; }
+ void set_active_input_tracking_window(Window* window) { m_active_input_tracking_window = window; }
+
Optional<HitTestResult> hit_test(Gfx::IntPoint const&) const;
+ unsigned row() const { return m_row; }
+ unsigned column() const { return m_column; }
+
+ void set_transition_offset(Badge<Compositor>, Gfx::IntPoint const& transition_offset) { m_transition_offset = transition_offset; }
+ Gfx::IntPoint const& transition_offset() const { return m_transition_offset; }
+
+ void set_stationary_window_stack(WindowStack& window_stack) { m_stationary_window_stack = &window_stack; }
+ WindowStack& stationary_window_stack()
+ {
+ VERIFY(m_stationary_window_stack);
+ return *m_stationary_window_stack;
+ }
+
+ void set_all_occluded(bool);
+
private:
- WeakPtr<Window> m_highlight_window;
WeakPtr<Window> m_active_window;
+ WeakPtr<Window> m_active_input_window;
+ WeakPtr<Window> m_active_input_tracking_window;
Window::List m_windows;
+ unsigned m_row { 0 };
+ unsigned m_column { 0 };
+ Gfx::IntPoint m_transition_offset;
+ WindowStack* m_stationary_window_stack { nullptr };
};
template<typename Callback>
inline IterationDecision WindowStack::for_each_visible_window_of_type_from_back_to_front(WindowType type, Callback callback, bool ignore_highlight)
{
+ auto* highlight_window = this->highlight_window();
bool do_highlight_window_at_end = false;
for (auto& window : m_windows) {
if (!window.is_visible())
@@ -70,7 +94,7 @@ inline IterationDecision WindowStack::for_each_visible_window_of_type_from_back_
continue;
if (window.type() != type)
continue;
- if (!ignore_highlight && m_highlight_window == &window) {
+ if (!ignore_highlight && highlight_window == &window) {
do_highlight_window_at_end = true;
continue;
}
@@ -78,7 +102,7 @@ inline IterationDecision WindowStack::for_each_visible_window_of_type_from_back_
return IterationDecision::Break;
}
if (do_highlight_window_at_end) {
- if (callback(*m_highlight_window) == IterationDecision::Break)
+ if (callback(*highlight_window) == IterationDecision::Break)
return IterationDecision::Break;
}
return IterationDecision::Continue;
@@ -87,8 +111,9 @@ inline IterationDecision WindowStack::for_each_visible_window_of_type_from_back_
template<typename Callback>
inline IterationDecision WindowStack::for_each_visible_window_of_type_from_front_to_back(WindowType type, Callback callback, bool ignore_highlight)
{
- if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) {
- if (callback(*m_highlight_window) == IterationDecision::Break)
+ auto* highlight_window = this->highlight_window();
+ if (!ignore_highlight && highlight_window && highlight_window->type() == type && highlight_window->is_visible()) {
+ if (callback(*highlight_window) == IterationDecision::Break)
return IterationDecision::Break;
}
@@ -101,7 +126,7 @@ inline IterationDecision WindowStack::for_each_visible_window_of_type_from_front
continue;
if (window.type() != type)
continue;
- if (!ignore_highlight && &window == m_highlight_window)
+ if (!ignore_highlight && &window == highlight_window)
continue;
if (callback(window) == IterationDecision::Break)
return IterationDecision::Break;
@@ -121,54 +146,11 @@ inline void WindowStack::for_each_window(Callback callback)
}
template<typename Callback>
-inline IterationDecision WindowStack::for_each_visible_window_from_back_to_front(Callback callback)
-{
- if (for_each_visible_window_of_type_from_back_to_front(WindowType::Desktop, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_back_to_front(WindowType::Normal, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_back_to_front(WindowType::ToolWindow, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_back_to_front(WindowType::AppletArea, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_back_to_front(WindowType::Notification, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_back_to_front(WindowType::Tooltip, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menu, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- return for_each_visible_window_of_type_from_back_to_front(WindowType::WindowSwitcher, callback);
-}
-
-template<typename Callback>
-inline IterationDecision WindowStack::for_each_visible_window_from_front_to_back(Callback callback)
-{
- if (for_each_visible_window_of_type_from_front_to_back(WindowType::WindowSwitcher, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_front_to_back(WindowType::Menu, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_front_to_back(WindowType::Tooltip, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_front_to_back(WindowType::Notification, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_front_to_back(WindowType::AppletArea, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_front_to_back(WindowType::Taskbar, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_front_to_back(WindowType::ToolWindow, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- if (for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, callback) == IterationDecision::Break)
- return IterationDecision::Break;
- return for_each_visible_window_of_type_from_front_to_back(WindowType::Desktop, callback);
-}
-
-template<typename Callback>
inline IterationDecision WindowStack::for_each_window_of_type_from_front_to_back(WindowType type, Callback callback, bool ignore_highlight)
{
- if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) {
- if (callback(*m_highlight_window) == IterationDecision::Break)
+ auto* highlight_window = this->highlight_window();
+ if (!ignore_highlight && highlight_window && highlight_window->type() == type && highlight_window->is_visible()) {
+ if (callback(*highlight_window) == IterationDecision::Break)
return IterationDecision::Break;
}
@@ -177,7 +159,7 @@ inline IterationDecision WindowStack::for_each_window_of_type_from_front_to_back
auto& window = *reverse_iterator;
if (window.type() != type)
continue;
- if (!ignore_highlight && &window == m_highlight_window)
+ if (!ignore_highlight && &window == highlight_window)
continue;
if (callback(window) == IterationDecision::Break)
return IterationDecision::Break;
diff --git a/Userland/Services/WindowServer/WindowSwitcher.cpp b/Userland/Services/WindowServer/WindowSwitcher.cpp
index ecc75a9bce..2ba8aafcbd 100644
--- a/Userland/Services/WindowServer/WindowSwitcher.cpp
+++ b/Userland/Services/WindowServer/WindowSwitcher.cpp
@@ -202,18 +202,21 @@ void WindowSwitcher::refresh()
m_selected_index = 0;
int window_count = 0;
int longest_title_width = 0;
- wm.window_stack().for_each_window_of_type_from_front_to_back(
- WindowType::Normal, [&](Window& window) {
- if (window.is_frameless())
+ wm.for_each_window_stack([&](auto& window_stack) {
+ window_stack.for_each_window_of_type_from_front_to_back(
+ WindowType::Normal, [&](Window& window) {
+ if (window.is_frameless())
+ return IterationDecision::Continue;
+ ++window_count;
+ longest_title_width = max(longest_title_width, wm.font().width(window.computed_title()));
+ if (selected_window == &window)
+ m_selected_index = m_windows.size();
+ m_windows.append(window);
return IterationDecision::Continue;
- ++window_count;
- longest_title_width = max(longest_title_width, wm.font().width(window.computed_title()));
- if (selected_window == &window)
- m_selected_index = m_windows.size();
- m_windows.append(window);
- return IterationDecision::Continue;
- },
- true);
+ },
+ true);
+ return IterationDecision::Continue;
+ });
if (m_windows.is_empty()) {
hide();
return;