From f6f14777ac9df82b5d619b1638042ffe8ccc084f Mon Sep 17 00:00:00 2001 From: Andres Crucitti Date: Tue, 6 Jul 2021 00:15:26 -0700 Subject: [PATCH] WindowServer: Allow windows to be pinnable (always on top) This patch adds the concept of a window being "Pinnable" (always drawn on top of other windows). This can be toggled through a new checkable action in the top left corner's window menu. --- Base/res/icons/16x16/window-pin.png | Bin 0 -> 178 bytes Userland/Services/WindowServer/Window.cpp | 37 ++++++++++++++++++ Userland/Services/WindowServer/Window.h | 6 +++ .../Services/WindowServer/WindowManager.cpp | 8 ++++ .../Services/WindowServer/WindowManager.h | 1 + .../Services/WindowServer/WindowStack.cpp | 34 ++++++++++++++++ Userland/Services/WindowServer/WindowStack.h | 1 + 7 files changed, 87 insertions(+) create mode 100644 Base/res/icons/16x16/window-pin.png diff --git a/Base/res/icons/16x16/window-pin.png b/Base/res/icons/16x16/window-pin.png new file mode 100644 index 0000000000000000000000000000000000000000..9fa1af4c7d263393b499eb4c69fbb32886c0e6f6 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`m7Xq+Ar`0aPT0uXU?9LOE5#+Y zAVFMzLTTORc!kXym={&AQV>ynA}G3}Gexzp_CUHz(VCx!n|tOy`J~3c5H{)aRgQDN z!XkpbGK*u?GF5y-&z@Cie-i8>ki2Fa?_RO;{@uH^Z~ql`S;D&Ee=*y#RnKyZRxP=^ c=)bb;?*FDinTKtCfsSDCboFyt=akR{05qyYDF6Tf literal 0 HcmV?d00001 diff --git a/Userland/Services/WindowServer/Window.cpp b/Userland/Services/WindowServer/Window.cpp index 8ebfbe44c2d..601f09c7036 100644 --- a/Userland/Services/WindowServer/Window.cpp +++ b/Userland/Services/WindowServer/Window.cpp @@ -67,6 +67,13 @@ static Gfx::Bitmap& close_icon() return *s_icon; } +static Gfx::Bitmap& pin_icon() +{ + static Gfx::Bitmap* s_icon; + if (!s_icon) + s_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-pin.png").leak_ref(); + return *s_icon; +} Window::Window(Core::Object& parent, WindowType type) : Core::Object(&parent) , m_type(type) @@ -276,6 +283,9 @@ void Window::update_window_menu_items() m_window_menu_maximize_item->set_enabled(m_resizable); m_window_menu_move_item->set_enabled(m_minimized_state == WindowMinimizedState::None && !m_maximized && !m_fullscreen); + + if (m_window_menu_pin_item) + m_window_menu_pin_item->set_text(m_pinned ? "Un-&Pin Window" : "&Pin Window"); } void Window::set_minimized(bool minimized) @@ -467,6 +477,16 @@ void Window::set_maximized(bool maximized, Optional fixed_point) Core::EventLoop::current().post_event(*this, make(m_rect)); set_default_positioned(false); } +void Window::set_pinned(bool pinned) +{ + if (m_pinned == pinned) + return; + + m_pinned = pinned; + update_window_menu_items(); + + window_stack().move_pinned_windows_to_front(); +} void Window::set_vertically_maximized() { if (m_maximized) @@ -776,6 +796,16 @@ void Window::ensure_window_menu() m_window_menu->add_item(make(*m_window_menu, MenuItem::Type::Separator)); + if (!m_modal) { + auto pin_item = make(*m_window_menu, (unsigned)WindowMenuAction::TogglePinned, "&Pin Window"); + m_window_menu_pin_item = pin_item.ptr(); + m_window_menu_pin_item->set_icon(&pin_icon()); + m_window_menu_pin_item->set_checkable(true); + m_window_menu->add_item(move(pin_item)); + } + + m_window_menu->add_item(make(*m_window_menu, MenuItem::Type::Separator)); + auto close_item = make(*m_window_menu, (unsigned)WindowMenuAction::Close, "&Close"); m_window_menu_close_item = close_item.ptr(); m_window_menu_close_item->set_icon(&close_icon()); @@ -818,6 +848,13 @@ void Window::handle_window_menu_action(WindowMenuAction action) invalidate_last_rendered_screen_rects(); break; } + case WindowMenuAction::TogglePinned: { + auto& item = *m_window_menu->item_by_identifier((unsigned)action); + auto new_is_checked = !item.is_checked(); + item.set_checked(new_is_checked); + WindowManager::the().set_pinned(*this, new_is_checked); + break; + } } } diff --git a/Userland/Services/WindowServer/Window.h b/Userland/Services/WindowServer/Window.h index 51aee13c35f..329ad887c31 100644 --- a/Userland/Services/WindowServer/Window.h +++ b/Userland/Services/WindowServer/Window.h @@ -56,6 +56,7 @@ enum class WindowMenuAction { ToggleMenubarVisibility, Close, Move, + TogglePinned, }; enum class WindowMenuDefaultAction { @@ -103,6 +104,9 @@ public: bool is_maximized() const { return m_maximized; } void set_maximized(bool, Optional fixed_point = {}); + bool is_pinned() const { return m_pinned; } + void set_pinned(bool); + void set_vertically_maximized(); bool is_fullscreen() const { return m_fullscreen; } @@ -411,6 +415,7 @@ private: bool m_invalidated_frame { true }; bool m_hit_testing_enabled { true }; bool m_modified { false }; + bool m_pinned { false }; bool m_moving_to_another_stack { false }; bool m_invalidate_last_render_rects { false }; WindowTileType m_tiled { WindowTileType::None }; @@ -439,6 +444,7 @@ private: MenuItem* m_window_menu_maximize_item { nullptr }; MenuItem* m_window_menu_move_item { nullptr }; MenuItem* m_window_menu_close_item { nullptr }; + MenuItem* m_window_menu_pin_item { nullptr }; MenuItem* m_window_menu_menubar_visibility_item { nullptr }; Optional m_progress; bool m_should_show_menubar { true }; diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp index 70572246c11..b9145a79d26 100644 --- a/Userland/Services/WindowServer/WindowManager.cpp +++ b/Userland/Services/WindowServer/WindowManager.cpp @@ -2005,6 +2005,14 @@ void WindowManager::maximize_windows(Window& window, bool maximized) }); } +void WindowManager::set_pinned(Window& window, bool pinned) +{ + for_each_window_in_modal_stack(window, [&](auto& w, bool) { + w.set_pinned(pinned); + return IterationDecision::Continue; + }); +} + Gfx::IntPoint WindowManager::get_recommended_window_position(Gfx::IntPoint const& desired) { // FIXME: Find a better source for the width and height to shift by. diff --git a/Userland/Services/WindowServer/WindowManager.h b/Userland/Services/WindowServer/WindowManager.h index 179b5e944e5..58024814adb 100644 --- a/Userland/Services/WindowServer/WindowManager.h +++ b/Userland/Services/WindowServer/WindowManager.h @@ -223,6 +223,7 @@ public: void minimize_windows(Window&, bool); void hide_windows(Window&, bool); void maximize_windows(Window&, bool); + void set_pinned(Window&, bool); template IterationDecision for_each_window_in_modal_stack(Window& window, Function f) diff --git a/Userland/Services/WindowServer/WindowStack.cpp b/Userland/Services/WindowServer/WindowStack.cpp index 057b4ef695e..2ed107528d0 100644 --- a/Userland/Services/WindowServer/WindowStack.cpp +++ b/Userland/Services/WindowServer/WindowStack.cpp @@ -24,6 +24,8 @@ void WindowStack::add(Window& window) VERIFY(!window.is_on_any_window_stack({})); m_windows.append(window); window.set_window_stack({}, this); + + move_pinned_windows_to_front(); } void WindowStack::add_to_back(Window& window) @@ -50,13 +52,45 @@ void WindowStack::move_to_front(Window& window) { if (m_windows.last() != &window) window.invalidate(); + m_windows.remove(window); m_windows.append(window); + + move_pinned_windows_to_front(); + + if (window.is_pinned()) { + m_windows.remove(window); + m_windows.append(window); + window.invalidate(); + } +} + +void WindowStack::move_pinned_windows_to_front() +{ + Window::List pinned_list; + for (auto iterator = m_windows.begin(); iterator != m_windows.end(); ++iterator) { + auto& window = *iterator; + if (window.is_pinned()) { + m_windows.remove(window); + pinned_list.append(window); + iterator = m_windows.begin(); + } + } + + while (!pinned_list.is_empty()) { + auto& window = *pinned_list.begin(); + pinned_list.remove(window); + m_windows.append(window); + window.invalidate(); + } } void WindowStack::move_all_windows(WindowStack& new_window_stack, Vector& windows_moved, MoveAllWindowsTo move_to) { VERIFY(this != &new_window_stack); + + move_pinned_windows_to_front(); + if (move_to == MoveAllWindowsTo::Front) { while (auto* window = m_windows.take_first()) { window->set_window_stack({}, nullptr); diff --git a/Userland/Services/WindowServer/WindowStack.h b/Userland/Services/WindowServer/WindowStack.h index 513ed3c3cac..d396df04595 100644 --- a/Userland/Services/WindowServer/WindowStack.h +++ b/Userland/Services/WindowServer/WindowStack.h @@ -22,6 +22,7 @@ public: void add_to_back(Window&); void remove(Window&); void move_to_front(Window&); + void move_pinned_windows_to_front(); enum class MoveAllWindowsTo { Front,