diff --git a/Userland/Services/WindowServer/CMakeLists.txt b/Userland/Services/WindowServer/CMakeLists.txt index 5eb4948f0e..210c11c906 100644 --- a/Userland/Services/WindowServer/CMakeLists.txt +++ b/Userland/Services/WindowServer/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES Window.cpp WindowFrame.cpp WindowManager.cpp + WindowStack.cpp WindowSwitcher.cpp WindowServerEndpoint.h WindowClientEndpoint.h diff --git a/Userland/Services/WindowServer/Compositor.cpp b/Userland/Services/WindowServer/Compositor.cpp index 7f0213b9d6..8793e295af 100644 --- a/Userland/Services/WindowServer/Compositor.cpp +++ b/Userland/Services/WindowServer/Compositor.cpp @@ -130,7 +130,7 @@ void Compositor::compose() } // Mark window regions as dirty that need to be re-rendered - wm.for_each_visible_window_from_back_to_front([&](Window& window) { + wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& window) { auto frame_rect = window.frame().render_rect(); for (auto& dirty_rect : dirty_screen_rects.rects()) { auto invalidate_rect = dirty_rect.intersected(frame_rect); @@ -148,14 +148,14 @@ void Compositor::compose() // 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.for_each_visible_window_from_back_to_front([&](Window& window) { + wm.window_stack().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.for_each_visible_window_from_back_to_front([&](Window& w) { + wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& w) { if (&w == &window) return IterationDecision::Continue; auto frame_rect2 = w.frame().render_rect(); @@ -402,7 +402,7 @@ void Compositor::compose() compose_window(*fullscreen_window); fullscreen_window->clear_dirty_rects(); } else { - wm.for_each_visible_window_from_back_to_front([&](Window& window) { + wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& window) { compose_window(window); window.clear_dirty_rects(); return IterationDecision::Continue; @@ -639,7 +639,7 @@ void Compositor::run_animations(Gfx::DisjointRectSet& flush_rects) Gfx::PainterStateSaver saver(painter); painter.set_draw_op(Gfx::Painter::DrawOp::Invert); - WindowManager::the().for_each_window([&](Window& window) { + WindowManager::the().window_stack().for_each_window([&](Window& window) { if (window.in_minimize_animation()) { int animation_index = window.minimize_animation_index(); @@ -824,7 +824,7 @@ bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_ { bool found_containing_window = false; bool checking = false; - WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& window) { + WindowManager::the().window_stack().for_each_visible_window_from_back_to_front([&](Window& window) { if (&window == &a_window) { checking = true; return IterationDecision::Continue; @@ -849,7 +849,7 @@ bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_ void Compositor::recompute_occlusions() { auto& wm = WindowManager::the(); - wm.for_each_visible_window_from_back_to_front([&](Window& window) { + wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& window) { if (wm.m_switcher.is_visible()) { window.set_occluded(false); } else { @@ -866,7 +866,7 @@ void Compositor::recompute_occlusions() auto screen_rect = Screen::the().rect(); if (auto* fullscreen_window = wm.active_fullscreen_window()) { - WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& w) { + WindowManager::the().window_stack().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(); @@ -892,7 +892,7 @@ void Compositor::recompute_occlusions() } else { Gfx::DisjointRectSet visible_rects(screen_rect); bool have_transparent = false; - WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& w) { + WindowManager::the().window_stack().for_each_visible_window_from_front_to_back([&](Window& w) { w.transparency_wallpaper_rects().clear(); auto& visible_opaque = w.opaque_rects(); auto& transparency_rects = w.transparency_rects(); @@ -917,7 +917,7 @@ void Compositor::recompute_occlusions() Gfx::DisjointRectSet opaque_covering; bool found_this_window = false; - WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& w2) { + WindowManager::the().window_stack().for_each_visible_window_from_back_to_front([&](Window& w2) { if (!found_this_window) { if (&w == &w2) found_this_window = true; @@ -979,7 +979,7 @@ void Compositor::recompute_occlusions() if (have_transparent) { // Determine what transparent window areas need to render the wallpaper first - WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& w) { + WindowManager::the().window_stack().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(); @@ -1007,7 +1007,7 @@ void Compositor::recompute_occlusions() dbgln(" wallpaper opaque: {}", r); } - wm.for_each_visible_window_from_back_to_front([&](Window& w) { + wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& w) { auto window_frame_rect = w.frame().render_rect().intersected(screen_rect); if (w.is_minimized() || window_frame_rect.is_empty()) return IterationDecision::Continue; diff --git a/Userland/Services/WindowServer/WindowFrame.cpp b/Userland/Services/WindowServer/WindowFrame.cpp index cc85bd47c7..5068e1e4a0 100644 --- a/Userland/Services/WindowServer/WindowFrame.cpp +++ b/Userland/Services/WindowServer/WindowFrame.cpp @@ -240,7 +240,7 @@ Gfx::WindowTheme::WindowState WindowFrame::window_state_for_theme() const if (m_flash_timer && m_flash_timer->is_active()) return m_flash_counter & 1 ? Gfx::WindowTheme::WindowState::Active : Gfx::WindowTheme::WindowState::Inactive; - if (&m_window == wm.m_highlight_window) + if (&m_window == wm.highlight_window()) return Gfx::WindowTheme::WindowState::Highlighted; if (&m_window == wm.m_move_window) return Gfx::WindowTheme::WindowState::Moving; diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp index 8c69c0d4d3..d860333a13 100644 --- a/Userland/Services/WindowServer/WindowManager.cpp +++ b/Userland/Services/WindowServer/WindowManager.cpp @@ -8,18 +8,14 @@ #include "Compositor.h" #include "EventLoop.h" #include "Menu.h" -#include "MenuItem.h" -#include "Menubar.h" #include "Screen.h" #include "Window.h" #include #include #include -#include #include #include #include -#include #include #include #include @@ -27,11 +23,6 @@ #include #include #include -#include -#include -#include -#include -#include namespace WindowServer { @@ -109,7 +100,7 @@ bool WindowManager::set_resolution(int width, int height, int scale) client.notify_about_new_screen_rect(Screen::the().rect()); }); if (success) { - for_each_window([](Window& window) { + m_window_stack.for_each_window([](Window& window) { window.recalculate_rect(); return IterationDecision::Continue; }); @@ -174,9 +165,9 @@ int WindowManager::scale_factor() const void WindowManager::add_window(Window& window) { - bool is_first_window = m_windows_in_order.is_empty(); + bool is_first_window = m_window_stack.is_empty(); - m_windows_in_order.append(window); + m_window_stack.windows().append(window); if (window.is_fullscreen()) { Core::EventLoop::current().post_event(window, make(Screen::the().rect())); @@ -228,10 +219,7 @@ void WindowManager::move_to_front_and_make_active(Window& window) void WindowManager::do_move_to_front(Window& window, bool make_active, bool make_input) { - if (m_windows_in_order.last() != &window) - window.invalidate(); - m_windows_in_order.remove(window); - m_windows_in_order.append(window); + m_window_stack.move_to_front(window); if (make_active) set_active_window(&window, make_input); @@ -252,7 +240,7 @@ void WindowManager::do_move_to_front(Window& window, bool make_active, bool make void WindowManager::remove_window(Window& window) { - m_windows_in_order.remove(window); + m_window_stack.remove(window); auto* active = active_window(); auto* active_input = active_input_window(); if (active == &window || active_input == &window || (active && window.is_descendant_of(*active)) || (active_input && active_input != active && window.is_descendant_of(*active_input))) @@ -279,7 +267,7 @@ void WindowManager::greet_window_manager(WMClientConnection& conn) if (conn.window_id() < 0) return; - for_each_window([&](Window& other_window) { + 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); @@ -456,7 +444,7 @@ bool WindowManager::pick_new_active_window(Window* previous_active) { bool new_window_picked = false; Window* first_candidate = nullptr; - for_each_visible_window_from_front_to_back([&](Window& candidate) { + m_window_stack.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()) @@ -762,7 +750,7 @@ bool WindowManager::process_ongoing_drag(MouseEvent& event, Window*& hovered_win if (event.type() == Event::MouseMove) { // We didn't let go of the drag yet, see if we should send some drag move events.. - for_each_visible_window_from_front_to_back([&](Window& window) { + m_window_stack.for_each_visible_window_from_front_to_back([&](Window& window) { if (!window.rect().contains(event.position())) return IterationDecision::Continue; hovered_window = &window; @@ -778,7 +766,7 @@ bool WindowManager::process_ongoing_drag(MouseEvent& event, Window*& hovered_win return true; hovered_window = nullptr; - for_each_visible_window_from_front_to_back([&](auto& window) { + m_window_stack.for_each_visible_window_from_front_to_back([&](auto& window) { if (window.hit_test(event.position())) { hovered_window = &window; return IterationDecision::Break; @@ -976,7 +964,7 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind if (MenuManager::the().has_open_menu() || hitting_menu_in_window_with_active_menu) { - for_each_visible_window_of_type_from_front_to_back(WindowType::Applet, [&](auto& window) { + m_window_stack.for_each_visible_window_of_type_from_front_to_back(WindowType::Applet, [&](auto& window) { if (!window.rect_in_applet_area().contains(event.position())) return IterationDecision::Continue; hovered_window = &window; @@ -1007,7 +995,7 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind m_active_input_tracking_window = nullptr; } - for_each_visible_window_from_front_to_back([&](auto& window) { + m_window_stack.for_each_visible_window_from_front_to_back([&](auto& window) { if (window.hit_test(event.position())) { hovered_window = &window; return IterationDecision::Break; @@ -1079,7 +1067,7 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind if (auto* fullscreen_window = active_fullscreen_window()) { process_mouse_event_for_window(*fullscreen_window); } else { - for_each_visible_window_from_front_to_back([&](Window& window) { + m_window_stack.for_each_visible_window_from_front_to_back([&](Window& window) { if (!window.hit_test(event.position())) return IterationDecision::Continue; process_mouse_event_for_window(window); @@ -1092,8 +1080,8 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind set_active_window(nullptr); } - auto reverse_iterator = m_windows_in_order.rbegin(); - for (; reverse_iterator != m_windows_in_order.rend(); ++reverse_iterator) { + auto reverse_iterator = m_window_stack.windows().rbegin(); + for (; reverse_iterator != m_window_stack.windows().rend(); ++reverse_iterator) { auto& window = *reverse_iterator; if (received_mouse_event == &window) continue; @@ -1124,7 +1112,7 @@ void WindowManager::reevaluate_hovered_window(Window* updated_window) if (fullscreen_window->hit_test(cursor_location)) hovered_window = fullscreen_window; } else { - for_each_visible_window_from_front_to_back([&](Window& window) { + m_window_stack.for_each_visible_window_from_front_to_back([&](Window& window) { if (!window.hit_test(cursor_location)) return IterationDecision::Continue; hovered_window = &window; @@ -1298,19 +1286,19 @@ void WindowManager::event(Core::Event& event) Core::Object::event(event); } -void WindowManager::set_highlight_window(Window* window) +void WindowManager::set_highlight_window(Window* new_highlight_window) { - if (window == m_highlight_window) + if (new_highlight_window == m_window_stack.highlight_window()) return; - auto* previous_highlight_window = m_highlight_window.ptr(); - m_highlight_window = window; + auto* previous_highlight_window = m_window_stack.highlight_window(); + m_window_stack.set_highlight_window(new_highlight_window); if (previous_highlight_window) { previous_highlight_window->invalidate(true, true); Compositor::the().invalidate_screen(previous_highlight_window->frame().render_rect()); } - if (m_highlight_window) { - m_highlight_window->invalidate(true, true); - Compositor::the().invalidate_screen(m_highlight_window->frame().render_rect()); + if (new_highlight_window) { + new_highlight_window->invalidate(true, true); + Compositor::the().invalidate_screen(new_highlight_window->frame().render_rect()); } // Invalidate occlusions in case the state change changes geometry Compositor::the().invalidate_occlusions(); @@ -1507,7 +1495,7 @@ Gfx::IntRect WindowManager::maximized_window_rect(const Window& window) const rect.set_height(rect.height() - window.frame().titlebar_rect().height() - window.frame().menubar_rect().height()); // Subtract taskbar window height if present - const_cast(this)->for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, [&rect](Window& taskbar_window) { + const_cast(this)->m_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; }); @@ -1553,7 +1541,7 @@ 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(); - for_each_window([&](Window& window) { + m_window_stack.for_each_window([&](Window& window) { window.frame().theme_changed(); return IterationDecision::Continue; }); @@ -1618,7 +1606,7 @@ Gfx::IntPoint WindowManager::get_recommended_window_position(const Gfx::IntPoint int taskbar_height = 28; const Window* overlap_window = nullptr; - for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, [&](Window& window) { + m_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; } @@ -1650,7 +1638,7 @@ void WindowManager::reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icon { m_allow_hidpi_icons = allow_hidpi_icons; reload_config(); - for_each_window([&](Window& window) { + m_window_stack.for_each_window([&](Window& window) { auto& window_frame = window.frame(); window_frame.theme_changed(); return IterationDecision::Continue; diff --git a/Userland/Services/WindowServer/WindowManager.h b/Userland/Services/WindowServer/WindowManager.h index 30706dfb66..b81e0121e8 100644 --- a/Userland/Services/WindowServer/WindowManager.h +++ b/Userland/Services/WindowServer/WindowManager.h @@ -1,11 +1,12 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2021, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include "WindowStack.h" #include #include #include @@ -21,7 +22,6 @@ #include #include #include -#include #include #include @@ -100,7 +100,7 @@ public: const Window* window_with_active_menu() const { return m_window_with_active_menu; } void set_window_with_active_menu(Window*); - const Window* highlight_window() const { return m_highlight_window.ptr(); } + Window const* highlight_window() const { return m_window_stack.highlight_window(); } void set_highlight_window(Window*); void move_to_front_and_make_active(Window&); @@ -231,6 +231,8 @@ public: void reevaluate_hovered_window(Window* = nullptr); Window* hovered_window() const { return m_hovered_window.ptr(); } + WindowStack& window_stack() { return m_window_stack; } + private: NonnullRefPtr get_cursor(const String& name); @@ -240,24 +242,10 @@ private: bool process_ongoing_window_move(MouseEvent&, Window*& hovered_window); bool process_ongoing_drag(MouseEvent&, Window*& hovered_window); - template - IterationDecision for_each_visible_window_of_type_from_back_to_front(WindowType, Callback, bool ignore_highlight = false); - template - IterationDecision for_each_visible_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); - template - IterationDecision for_each_visible_window_from_front_to_back(Callback); - template - IterationDecision for_each_visible_window_from_back_to_front(Callback); - template - void for_each_window(Callback); - template - IterationDecision for_each_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); - template void for_each_window_manager(Callback); virtual void event(Core::Event&) override; - void paint_window_frame(const Window&); void tell_wm_about_window(WMClientConnection& conn, Window&); void tell_wm_about_window_icon(WMClientConnection& conn, Window&); void tell_wm_about_window_rect(WMClientConnection& conn, Window&); @@ -283,7 +271,7 @@ private: RefPtr m_wait_cursor; RefPtr m_crosshair_cursor; - Window::List m_windows_in_order; + WindowStack m_window_stack; struct DoubleClickInfo { struct ClickMetadata { @@ -322,7 +310,6 @@ private: WeakPtr m_active_window; WeakPtr m_hovered_window; - WeakPtr m_highlight_window; WeakPtr m_active_input_window; WeakPtr m_active_input_tracking_window; WeakPtr m_window_with_active_menu; @@ -355,100 +342,6 @@ private: RefPtr m_dnd_bitmap; }; -template -IterationDecision WindowManager::for_each_visible_window_of_type_from_back_to_front(WindowType type, Callback callback, bool ignore_highlight) -{ - bool do_highlight_window_at_end = false; - for (auto& window : m_windows_in_order) { - if (!window.is_visible()) - continue; - if (window.is_minimized()) - continue; - if (window.type() != type) - continue; - if (!ignore_highlight && m_highlight_window == &window) { - do_highlight_window_at_end = true; - continue; - } - if (callback(window) == IterationDecision::Break) - return IterationDecision::Break; - } - if (do_highlight_window_at_end) { - if (callback(*m_highlight_window) == IterationDecision::Break) - return IterationDecision::Break; - } - return IterationDecision::Continue; -} - -template -IterationDecision WindowManager::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 -IterationDecision WindowManager::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) - return IterationDecision::Break; - } - - auto reverse_iterator = m_windows_in_order.rbegin(); - for (; reverse_iterator != m_windows_in_order.rend(); ++reverse_iterator) { - auto& window = *reverse_iterator; - if (!window.is_visible()) - continue; - if (window.is_minimized()) - continue; - if (window.type() != type) - continue; - if (!ignore_highlight && &window == m_highlight_window) - continue; - if (callback(window) == IterationDecision::Break) - return IterationDecision::Break; - } - return IterationDecision::Continue; -} - -template -IterationDecision WindowManager::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 void WindowManager::for_each_window_manager(Callback callback) { @@ -461,36 +354,4 @@ void WindowManager::for_each_window_manager(Callback callback) } } -template -void WindowManager::for_each_window(Callback callback) -{ - auto reverse_iterator = m_windows_in_order.rbegin(); - for (; reverse_iterator != m_windows_in_order.rend(); ++reverse_iterator) { - auto& window = *reverse_iterator; - if (callback(window) == IterationDecision::Break) - return; - } -} - -template -IterationDecision WindowManager::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) - return IterationDecision::Break; - } - - auto reverse_iterator = m_windows_in_order.rbegin(); - for (; reverse_iterator != m_windows_in_order.rend(); ++reverse_iterator) { - auto& window = *reverse_iterator; - if (window.type() != type) - continue; - if (!ignore_highlight && &window == m_highlight_window) - continue; - if (callback(window) == IterationDecision::Break) - return IterationDecision::Break; - } - return IterationDecision::Continue; -} - } diff --git a/Userland/Services/WindowServer/WindowStack.cpp b/Userland/Services/WindowServer/WindowStack.cpp new file mode 100644 index 0000000000..324e11fa4c --- /dev/null +++ b/Userland/Services/WindowServer/WindowStack.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "WindowStack.h" + +namespace WindowServer { + +WindowStack::WindowStack() +{ +} + +WindowStack::~WindowStack() +{ +} + +void WindowStack::add(Window& window) +{ + m_windows.append(window); +} + +void WindowStack::remove(Window& window) +{ + m_windows.remove(window); +} + +void WindowStack::move_to_front(Window& window) +{ + if (m_windows.last() != &window) + window.invalidate(); + m_windows.remove(window); + m_windows.append(window); +} + +void WindowStack::set_highlight_window(Window* window) +{ + if (!window) + m_highlight_window = nullptr; + else + m_highlight_window = window->make_weak_ptr(); +} + +} diff --git a/Userland/Services/WindowServer/WindowStack.h b/Userland/Services/WindowServer/WindowStack.h new file mode 100644 index 0000000000..6a2b7fb97a --- /dev/null +++ b/Userland/Services/WindowServer/WindowStack.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2018-2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "Window.h" + +namespace WindowServer { + +class WindowStack { +public: + WindowStack(); + ~WindowStack(); + + bool is_empty() const { return m_windows.is_empty(); } + void add(Window&); + void remove(Window&); + void move_to_front(Window&); + + template + IterationDecision for_each_visible_window_from_back_to_front(Callback); + template + IterationDecision for_each_visible_window_from_front_to_back(Callback); + template + IterationDecision for_each_visible_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); + template + IterationDecision for_each_visible_window_of_type_from_back_to_front(WindowType, Callback, bool ignore_highlight = false); + template + IterationDecision for_each_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); + + template + void for_each_window(Callback); + + 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*); + +private: + WeakPtr m_highlight_window; + + Window::List m_windows; +}; + +template +inline IterationDecision WindowStack::for_each_visible_window_of_type_from_back_to_front(WindowType type, Callback callback, bool ignore_highlight) +{ + bool do_highlight_window_at_end = false; + for (auto& window : m_windows) { + if (!window.is_visible()) + continue; + if (window.is_minimized()) + continue; + if (window.type() != type) + continue; + if (!ignore_highlight && m_highlight_window == &window) { + do_highlight_window_at_end = true; + continue; + } + if (callback(window) == IterationDecision::Break) + return IterationDecision::Break; + } + if (do_highlight_window_at_end) { + if (callback(*m_highlight_window) == IterationDecision::Break) + return IterationDecision::Break; + } + return IterationDecision::Continue; +} + +template +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) + return IterationDecision::Break; + } + + auto reverse_iterator = m_windows.rbegin(); + for (; reverse_iterator != m_windows.rend(); ++reverse_iterator) { + auto& window = *reverse_iterator; + if (!window.is_visible()) + continue; + if (window.is_minimized()) + continue; + if (window.type() != type) + continue; + if (!ignore_highlight && &window == m_highlight_window) + continue; + if (callback(window) == IterationDecision::Break) + return IterationDecision::Break; + } + return IterationDecision::Continue; +} + +template +inline void WindowStack::for_each_window(Callback callback) +{ + auto reverse_iterator = m_windows.rbegin(); + for (; reverse_iterator != m_windows.rend(); ++reverse_iterator) { + auto& window = *reverse_iterator; + if (callback(window) == IterationDecision::Break) + return; + } +} + +template +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 +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 +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) + return IterationDecision::Break; + } + + auto reverse_iterator = m_windows.rbegin(); + for (; reverse_iterator != m_windows.rend(); ++reverse_iterator) { + auto& window = *reverse_iterator; + if (window.type() != type) + continue; + if (!ignore_highlight && &window == m_highlight_window) + continue; + if (callback(window) == IterationDecision::Break) + return IterationDecision::Break; + } + return IterationDecision::Continue; +} + +} diff --git a/Userland/Services/WindowServer/WindowSwitcher.cpp b/Userland/Services/WindowServer/WindowSwitcher.cpp index 77ca52427e..36d7418db6 100644 --- a/Userland/Services/WindowServer/WindowSwitcher.cpp +++ b/Userland/Services/WindowServer/WindowSwitcher.cpp @@ -202,7 +202,7 @@ void WindowSwitcher::refresh() m_selected_index = 0; int window_count = 0; int longest_title_width = 0; - wm.for_each_window_of_type_from_front_to_back( + wm.window_stack().for_each_window_of_type_from_front_to_back( WindowType::Normal, [&](Window& window) { if (window.is_frameless()) return IterationDecision::Continue;