From fe54a0ca27370151a3271a6333848e415ac29c89 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 3 Apr 2023 21:39:17 -0600 Subject: [PATCH] WindowServer: Implement tile window overlay This adds a tiling mode that will show a tile window overlay rather than immediately tiling a window, triggering window resizes. --- .../DisplaySettings/EffectsSettings.gml | 16 ++ .../DisplaySettings/EffectsSettingsWidget.cpp | 28 +++- .../DisplaySettings/EffectsSettingsWidget.h | 2 + .../WindowServer/ConnectionFromClient.cpp | 4 +- .../WindowServer/ConnectionFromClient.h | 2 +- Userland/Services/WindowServer/Overlays.cpp | 14 ++ Userland/Services/WindowServer/Overlays.h | 24 +++ .../Services/WindowServer/SystemEffects.h | 44 +++++- Userland/Services/WindowServer/Window.cpp | 49 +++--- Userland/Services/WindowServer/Window.h | 6 +- .../Services/WindowServer/WindowFrame.cpp | 2 +- Userland/Services/WindowServer/WindowFrame.h | 2 + .../Services/WindowServer/WindowManager.cpp | 140 +++++++++++++----- .../Services/WindowServer/WindowManager.h | 15 +- .../Services/WindowServer/WindowServer.ipc | 2 +- 15 files changed, 279 insertions(+), 71 deletions(-) diff --git a/Userland/Applications/DisplaySettings/EffectsSettings.gml b/Userland/Applications/DisplaySettings/EffectsSettings.gml index b0443358e19..7497b49c73e 100644 --- a/Userland/Applications/DisplaySettings/EffectsSettings.gml +++ b/Userland/Applications/DisplaySettings/EffectsSettings.gml @@ -121,6 +121,22 @@ } } + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout {} + + @GUI::Label { + text: "Tile Window Behavior:" + autosize: true + } + + @GUI::Layout::Spacer {} + + @GUI::ComboBox { + name: "tile_window_combobox" + fixed_width: 130 + } + } + @GUI::Widget { fixed_height: 4 } diff --git a/Userland/Applications/DisplaySettings/EffectsSettingsWidget.cpp b/Userland/Applications/DisplaySettings/EffectsSettingsWidget.cpp index f7bafe43eaa..99e073d121a 100644 --- a/Userland/Applications/DisplaySettings/EffectsSettingsWidget.cpp +++ b/Userland/Applications/DisplaySettings/EffectsSettingsWidget.cpp @@ -34,6 +34,13 @@ ErrorOr EffectsSettingsWidget::setup_interface() set_modified(true); }; + m_tile_window_combobox = find_descendant_of_type_named("tile_window_combobox"); + m_tile_window_combobox->set_only_allow_values_from_model(true); + m_tile_window_combobox->on_change = [this](auto&, auto&) { + m_system_effects.set_tile_window(static_cast(m_tile_window_combobox->selected_index())); + set_modified(true); + }; + if (auto result = load_settings(); result.is_error()) { warnln("Failed to load [Effects] from WindowServer.ini"); return {}; @@ -119,25 +126,36 @@ ErrorOr EffectsSettingsWidget::load_settings() ws_config->read_bool_entry("Effects", "TooltipShadow", true), }; auto geometry = WindowServer::ShowGeometryTools::string_to_enum(ws_config->read_entry("Effects", "ShowGeometry", "OnMoveAndResize")); - m_system_effects = { effects, geometry }; + auto tile_window = WindowServer::TileWindowTools::string_to_enum(ws_config->read_entry("Effects", "TileWindow", "ShowTileOverlay")); + m_system_effects = { effects, geometry, tile_window }; - static constexpr Array list = { + static constexpr Array geometry_list = { "On Move and Resize"sv, "On Move only"sv, "On Resize only"sv, "Never"sv }; - for (size_t i = 0; i < list.size(); ++i) - TRY(m_geometry_list.try_append(TRY(String::from_utf8(list[i])))); + for (size_t i = 0; i < geometry_list.size(); ++i) + TRY(m_geometry_list.try_append(TRY(String::from_utf8(geometry_list[i])))); m_geometry_combobox->set_model(ItemListModel::create(m_geometry_list)); m_geometry_combobox->set_selected_index(to_underlying(m_system_effects.geometry())); + static constexpr Array tile_window_list = { + "Tile immediately"sv, + "Show tile overlay"sv, + "Never"sv + }; + for (size_t i = 0; i < tile_window_list.size(); ++i) + TRY(m_tile_window_list.try_append(TRY(String::from_utf8(tile_window_list[i])))); + m_tile_window_combobox->set_model(ItemListModel::create(m_tile_window_list)); + m_tile_window_combobox->set_selected_index(static_cast(m_system_effects.tile_window())); + return {}; } void EffectsSettingsWidget::apply_settings() { - ConnectionToWindowServer::the().async_set_system_effects(m_system_effects.effects(), to_underlying(m_system_effects.geometry())); + ConnectionToWindowServer::the().async_set_system_effects(m_system_effects.effects(), to_underlying(m_system_effects.geometry()), to_underlying(m_system_effects.tile_window())); } } diff --git a/Userland/Applications/DisplaySettings/EffectsSettingsWidget.h b/Userland/Applications/DisplaySettings/EffectsSettingsWidget.h index 9abd977aa09..6e116602994 100644 --- a/Userland/Applications/DisplaySettings/EffectsSettingsWidget.h +++ b/Userland/Applications/DisplaySettings/EffectsSettingsWidget.h @@ -32,7 +32,9 @@ private: SystemEffects m_system_effects; Vector m_geometry_list; + Vector m_tile_window_list; RefPtr m_geometry_combobox; + RefPtr m_tile_window_combobox; }; } diff --git a/Userland/Services/WindowServer/ConnectionFromClient.cpp b/Userland/Services/WindowServer/ConnectionFromClient.cpp index 7bdb761a933..4ec9073a50d 100644 --- a/Userland/Services/WindowServer/ConnectionFromClient.cpp +++ b/Userland/Services/WindowServer/ConnectionFromClient.cpp @@ -980,9 +980,9 @@ Messages::WindowServer::SetSystemFontsResponse ConnectionFromClient::set_system_ return !g_config->sync().is_error(); } -void ConnectionFromClient::set_system_effects(Vector const& effects, u8 geometry) +void ConnectionFromClient::set_system_effects(Vector const& effects, u8 geometry, u8 tile_window) { - WindowManager::the().apply_system_effects(effects, static_cast(geometry)); + WindowManager::the().apply_system_effects(effects, static_cast(geometry), static_cast(tile_window)); ConnectionFromClient::for_each_client([&](auto& client) { client.async_update_system_effects(effects); }); diff --git a/Userland/Services/WindowServer/ConnectionFromClient.h b/Userland/Services/WindowServer/ConnectionFromClient.h index 626f9eae8af..988e537c88c 100644 --- a/Userland/Services/WindowServer/ConnectionFromClient.h +++ b/Userland/Services/WindowServer/ConnectionFromClient.h @@ -160,7 +160,7 @@ private: virtual Messages::WindowServer::GetCursorHighlightColorResponse get_cursor_highlight_color() override; virtual Messages::WindowServer::GetCursorThemeResponse get_cursor_theme() override; virtual Messages::WindowServer::SetSystemFontsResponse set_system_fonts(DeprecatedString const&, DeprecatedString const&, DeprecatedString const&) override; - virtual void set_system_effects(Vector const&, u8) override; + virtual void set_system_effects(Vector const&, u8, u8) override; virtual void set_window_base_size_and_size_increment(i32, Gfx::IntSize, Gfx::IntSize) override; virtual void set_window_resize_aspect_ratio(i32, Optional const&) override; virtual void enable_display_link() override; diff --git a/Userland/Services/WindowServer/Overlays.cpp b/Userland/Services/WindowServer/Overlays.cpp index db3ab5ddedc..58234e08514 100644 --- a/Userland/Services/WindowServer/Overlays.cpp +++ b/Userland/Services/WindowServer/Overlays.cpp @@ -351,4 +351,18 @@ WindowStackSwitchOverlay::WindowStackSwitchOverlay(Screen& screen, WindowStack& set_rect(calculate_frame_rect(Gfx::IntRect({}, m_content_size).inflated(2 * default_screen_rect_margin, 2 * default_screen_rect_margin)).centered_within(screen.rect())); } +TileWindowOverlay::TileWindowOverlay(Window& window, Gfx::IntRect const& tiled_frame_rect, Gfx::Palette&& palette) + : m_window(window) + , m_tiled_frame_rect(tiled_frame_rect) + , m_palette(std::move(palette)) +{ +} + +void TileWindowOverlay::render(Gfx::Painter& painter, Screen const&) +{ + Gfx::IntRect paint_rect { {}, rect().size() }; + painter.fill_rect(paint_rect, m_palette.rubber_band_fill()); + painter.draw_rect(paint_rect, m_palette.rubber_band_border()); +} + } diff --git a/Userland/Services/WindowServer/Overlays.h b/Userland/Services/WindowServer/Overlays.h index ddf6aceeeb5..bde7becd741 100644 --- a/Userland/Services/WindowServer/Overlays.h +++ b/Userland/Services/WindowServer/Overlays.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,7 @@ public: virtual ~Overlay(); enum class ZOrder { + SnapWindow, WindowGeometry, Dnd, WindowStackSwitch, @@ -192,4 +194,26 @@ private: int const m_target_column; }; +class TileWindowOverlay : public Overlay { +public: + TileWindowOverlay(Window&, Gfx::IntRect const&, Gfx::Palette&&); + + virtual ZOrder zorder() const override { return ZOrder::SnapWindow; } + virtual void render(Gfx::Painter&, Screen const&) override; + + void set_overlay_rect(Gfx::IntRect const& rect) + { + set_rect(rect); + } + + void set_tiled_frame_rect(Gfx::IntRect const& rect) { m_tiled_frame_rect = rect; } + Gfx::IntRect const& tiled_frame_rect() const { return m_tiled_frame_rect; } + bool is_window(Window& window) const { return &m_window == &window; } + +private: + Window& m_window; + Gfx::IntRect m_tiled_frame_rect; + Gfx::Palette m_palette; +}; + } diff --git a/Userland/Services/WindowServer/SystemEffects.h b/Userland/Services/WindowServer/SystemEffects.h index eed17fe0ca5..d6f00f4cd0d 100644 --- a/Userland/Services/WindowServer/SystemEffects.h +++ b/Userland/Services/WindowServer/SystemEffects.h @@ -12,6 +12,12 @@ namespace WindowServer { +enum class TileWindow : u8 { + TileImmediately, + ShowTileOverlay, + Never +}; + enum class ShowGeometry : u8 { OnMoveAndResize, OnMoveOnly, @@ -66,12 +72,42 @@ namespace ShowGeometryTools { }; +namespace TileWindowTools { + +[[maybe_unused]] static StringView enum_to_string(TileWindow tile_window) +{ + switch (tile_window) { + case TileWindow::Never: + return "Never"sv; + case TileWindow::TileImmediately: + return "TileImmediately"sv; + case TileWindow::ShowTileOverlay: + return "ShowTileOverlay"sv; + default: + VERIFY_NOT_REACHED(); + } +} + +[[maybe_unused]] static TileWindow string_to_enum(StringView tile_window) +{ + if (tile_window == "Never"sv) + return TileWindow::Never; + else if (tile_window == "TileImmediately"sv) + return TileWindow::TileImmediately; + else if (tile_window == "ShowTileOverlay"sv) + return TileWindow::ShowTileOverlay; + VERIFY_NOT_REACHED(); +} + +}; + class SystemEffects { public: SystemEffects() = default; - SystemEffects(Vector effects, ShowGeometry show) + SystemEffects(Vector effects, ShowGeometry show, TileWindow tile_window) : m_effects(effects) , m_geometry(show) + , m_tile_window(tile_window) { } SystemEffects(Vector effects) @@ -107,14 +143,18 @@ public: void set_geometry(ShowGeometry g) { m_geometry = g; } ShowGeometry geometry() const { return m_geometry; } + void set_tile_window(TileWindow tile_window) { m_tile_window = tile_window; } + TileWindow tile_window() const { return m_tile_window; } + bool operator==(SystemEffects const& other) const { - return m_effects == other.m_effects && m_geometry == other.m_geometry; + return m_effects == other.m_effects && m_geometry == other.m_geometry && m_tile_window == other.m_tile_window; } private: Vector m_effects; ShowGeometry m_geometry { ShowGeometry::Never }; + TileWindow m_tile_window { TileWindow::ShowTileOverlay }; }; } diff --git a/Userland/Services/WindowServer/Window.cpp b/Userland/Services/WindowServer/Window.cpp index fc291a3ff3a..cee941835d4 100644 --- a/Userland/Services/WindowServer/Window.cpp +++ b/Userland/Services/WindowServer/Window.cpp @@ -420,8 +420,8 @@ void Window::set_maximized(bool maximized) else set_rect(m_floating_rect); m_frame.did_set_maximized({}, maximized); - Core::EventLoop::current().post_event(*this, make(m_rect)); - Core::EventLoop::current().post_event(*this, make(m_rect)); + send_resize_event_to_client(); + send_move_event_to_client(); set_default_positioned(false); WindowManager::the().notify_minimization_state_changed(*this); @@ -842,8 +842,8 @@ void Window::set_fullscreen(bool fullscreen) new_window_rect = m_saved_nonfullscreen_rect; } - Core::EventLoop::current().post_event(*this, make(new_window_rect)); - Core::EventLoop::current().post_event(*this, make(new_window_rect)); + send_resize_event_to_client(); + send_move_event_to_client(); set_rect(new_window_rect); } @@ -858,7 +858,7 @@ WindowTileType Window::tile_type_based_on_rect(Gfx::IntRect const& rect) const bool tiling_to_left = current_tile_type == WindowTileType::Left || current_tile_type == WindowTileType::TopLeft || current_tile_type == WindowTileType::BottomLeft; bool tiling_to_right = current_tile_type == WindowTileType::Right || current_tile_type == WindowTileType::TopRight || current_tile_type == WindowTileType::BottomRight; - auto ideal_tiled_rect = WindowManager::the().tiled_window_rect(*this, current_tile_type); + auto ideal_tiled_rect = WindowManager::the().tiled_window_rect(*this, window_screen, current_tile_type); bool same_top = ideal_tiled_rect.top() == rect.top(); bool same_left = ideal_tiled_rect.left() == rect.left(); bool same_right = ideal_tiled_rect.right() == rect.right(); @@ -870,9 +870,9 @@ WindowTileType Window::tile_type_based_on_rect(Gfx::IntRect const& rect) const if (tiling_to_top && same_top && same_left && same_right) return WindowTileType::Top; else if ((tiling_to_top || tiling_to_left) && same_top && same_left) - return rect.bottom() == WindowManager::the().tiled_window_rect(*this, WindowTileType::Bottom).bottom() ? WindowTileType::Left : WindowTileType::TopLeft; + return rect.bottom() == WindowManager::the().tiled_window_rect(*this, window_screen, WindowTileType::Bottom).bottom() ? WindowTileType::Left : WindowTileType::TopLeft; else if ((tiling_to_top || tiling_to_right) && same_top && same_right) - return rect.bottom() == WindowManager::the().tiled_window_rect(*this, WindowTileType::Bottom).bottom() ? WindowTileType::Right : WindowTileType::TopRight; + return rect.bottom() == WindowManager::the().tiled_window_rect(*this, window_screen, WindowTileType::Bottom).bottom() ? WindowTileType::Right : WindowTileType::TopRight; else if (tiling_to_left && same_left && same_top && same_bottom) return WindowTileType::Left; else if (tiling_to_right && same_right && same_top && same_bottom) @@ -880,9 +880,9 @@ WindowTileType Window::tile_type_based_on_rect(Gfx::IntRect const& rect) const else if (tiling_to_bottom && same_bottom && same_left && same_right) return WindowTileType::Bottom; else if ((tiling_to_bottom || tiling_to_left) && same_bottom && same_left) - return rect.top() == WindowManager::the().tiled_window_rect(*this, WindowTileType::Left).top() ? WindowTileType::Left : WindowTileType::BottomLeft; + return rect.top() == WindowManager::the().tiled_window_rect(*this, window_screen, WindowTileType::Left).top() ? WindowTileType::Left : WindowTileType::BottomLeft; else if ((tiling_to_bottom || tiling_to_right) && same_bottom && same_right) - return rect.top() == WindowManager::the().tiled_window_rect(*this, WindowTileType::Right).top() ? WindowTileType::Right : WindowTileType::BottomRight; + return rect.top() == WindowManager::the().tiled_window_rect(*this, window_screen, WindowTileType::Right).top() ? WindowTileType::Right : WindowTileType::BottomRight; } return tile_type; } @@ -911,15 +911,11 @@ bool Window::set_untiled() VERIFY(!resize_aspect_ratio().has_value()); m_tile_type = WindowTileType::None; - set_rect(m_floating_rect); - - Core::EventLoop::current().post_event(*this, make(m_rect)); - Core::EventLoop::current().post_event(*this, make(m_rect)); - + tile_type_changed(); return true; } -void Window::set_tiled(WindowTileType tile_type) +void Window::set_tiled(WindowTileType tile_type, Optional tile_on_screen) { VERIFY(tile_type != WindowTileType::None); @@ -933,9 +929,26 @@ void Window::set_tiled(WindowTileType tile_type) set_maximized(false); m_tile_type = tile_type; + tile_type_changed(tile_on_screen); +} - set_rect(WindowManager::the().tiled_window_rect(*this, tile_type)); +void Window::tile_type_changed(Optional tile_on_screen) +{ + if (m_tile_type != WindowTileType::None) + set_rect(WindowManager::the().tiled_window_rect(*this, tile_on_screen, m_tile_type)); + else + set_rect(m_floating_rect); + send_resize_event_to_client(); + send_move_event_to_client(); +} + +void Window::send_resize_event_to_client() +{ Core::EventLoop::current().post_event(*this, make(m_rect)); +} + +void Window::send_move_event_to_client() +{ Core::EventLoop::current().post_event(*this, make(m_rect)); } @@ -951,7 +964,7 @@ void Window::recalculate_rect() bool send_event = true; if (is_tiled()) { - set_rect(WindowManager::the().tiled_window_rect(*this, m_tile_type)); + set_rect(WindowManager::the().tiled_window_rect(*this, {}, m_tile_type)); } else if (type() == WindowType::Desktop) { set_rect(WindowManager::the().arena_rect_for_type(Screen::main(), WindowType::Desktop)); } else { @@ -959,7 +972,7 @@ void Window::recalculate_rect() } if (send_event) { - Core::EventLoop::current().post_event(*this, make(m_rect)); + send_resize_event_to_client(); } } diff --git a/Userland/Services/WindowServer/Window.h b/Userland/Services/WindowServer/Window.h index 2885d2773cc..8cd86d07591 100644 --- a/Userland/Services/WindowServer/Window.h +++ b/Userland/Services/WindowServer/Window.h @@ -120,7 +120,7 @@ public: WindowTileType tile_type() const { return m_tile_type; } bool is_tiled() const { return m_tile_type != WindowTileType::None; } - void set_tiled(WindowTileType); + void set_tiled(WindowTileType, Optional = {}); WindowTileType tile_type_based_on_rect(Gfx::IntRect const&) const; void check_untile_due_to_resize(Gfx::IntRect const&); bool set_untiled(); @@ -376,6 +376,9 @@ public: void remove_all_stealing() { m_stealable_by_client_ids.clear(); } bool is_stealable_by_client(i32 client_id) const { return m_stealable_by_client_ids.contains_slow(client_id); } + void send_resize_event_to_client(); + void send_move_event_to_client(); + private: Window(ConnectionFromClient&, WindowType, WindowMode, int window_id, bool minimizable, bool closeable, bool frameless, bool resizable, bool fullscreen, Window* parent_window = nullptr); Window(Core::Object&, WindowType); @@ -386,6 +389,7 @@ private: void add_child_window(Window&); void ensure_window_menu(); void update_window_menu_items(); + void tile_type_changed(Optional = {}); ErrorOr> compute_title_username(ConnectionFromClient* client); ConnectionFromClient* m_client { nullptr }; diff --git a/Userland/Services/WindowServer/WindowFrame.cpp b/Userland/Services/WindowServer/WindowFrame.cpp index 6f5a451eed2..2092105251c 100644 --- a/Userland/Services/WindowServer/WindowFrame.cpp +++ b/Userland/Services/WindowServer/WindowFrame.cpp @@ -60,7 +60,7 @@ static DeprecatedString s_last_menu_shadow_path; static DeprecatedString s_last_taskbar_shadow_path; static DeprecatedString s_last_tooltip_shadow_path; -static Gfx::IntRect frame_rect_for_window(Window& window, Gfx::IntRect const& rect) +Gfx::IntRect WindowFrame::frame_rect_for_window(Window& window, Gfx::IntRect const& rect) { if (window.is_frameless()) return rect; diff --git a/Userland/Services/WindowServer/WindowFrame.h b/Userland/Services/WindowServer/WindowFrame.h index 29e520f77de..b5845c784ba 100644 --- a/Userland/Services/WindowServer/WindowFrame.h +++ b/Userland/Services/WindowServer/WindowFrame.h @@ -43,6 +43,8 @@ public: }; friend class RenderedCache; + static Gfx::IntRect frame_rect_for_window(Window&, Gfx::IntRect const&); + static void reload_config(); explicit WindowFrame(Window&); diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp index 89dd51d8e11..b666a069e25 100644 --- a/Userland/Services/WindowServer/WindowManager.cpp +++ b/Userland/Services/WindowServer/WindowManager.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -335,8 +336,8 @@ void WindowManager::add_window(Window& window) if (window.is_fullscreen()) { auto& screen = Screen::main(); // TODO: support fullscreen windows on other screens! - Core::EventLoop::current().post_event(window, make(screen.rect())); window.set_rect(screen.rect()); + window.send_resize_event_to_client(); } if (window.type() != WindowType::Desktop || is_first_window) @@ -415,6 +416,9 @@ void WindowManager::remove_window(Window& window) conn.async_window_removed(conn.window_id(), window.client_id(), window.window_id()); return IterationDecision::Continue; }); + + if (m_tile_window_overlay && m_tile_window_overlay->is_window(window)) + stop_tile_window_animation(); } void WindowManager::greet_window_manager(WMConnectionFromClient& conn) @@ -738,6 +742,44 @@ void WindowManager::start_window_resize(Window& window, MouseEvent const& event, start_window_resize(window, event.position(), event.button(), resize_direction); } +void WindowManager::start_tile_window_animation(Gfx::IntRect const& starting_rect) +{ + m_tile_window_overlay_animation = Animation::create(); + m_tile_window_overlay_animation->set_duration(150); + m_tile_window_overlay_animation->on_update = [this, starting_rect](float progress, Gfx::Painter&, Screen&, Gfx::DisjointIntRectSet&) { + if (m_tile_window_overlay) { + auto target_rect = starting_rect.interpolated_to(m_tile_window_overlay->tiled_frame_rect(), progress); + m_tile_window_overlay->set_overlay_rect(target_rect); + } + }; + m_tile_window_overlay_animation->start(); +} + +void WindowManager::stop_tile_window_animation() +{ + m_tile_window_overlay = nullptr; + m_tile_window_overlay_animation = nullptr; +} + +void WindowManager::show_tile_window_overlay(Window& window, Screen const& cursor_screen, WindowTileType tile_type) +{ + m_move_window_suggested_tile = tile_type; + if (tile_type != WindowTileType::None && (!m_tile_window_overlay || !m_tile_window_overlay->is_window(window))) { + auto tiled_frame_rect = WindowFrame::frame_rect_for_window(window, tiled_window_rect(window, cursor_screen, tile_type)); + m_tile_window_overlay = Compositor::the().create_overlay(window, tiled_frame_rect, palette()); + m_tile_window_overlay->set_enabled(true); + start_tile_window_animation(window.frame().rect()); + } else if (tile_type == WindowTileType::None) { + stop_tile_window_animation(); + } else { + auto tiled_frame_rect = WindowFrame::frame_rect_for_window(window, tiled_window_rect(window, cursor_screen, tile_type)); + if (m_tile_window_overlay->tiled_frame_rect() != tiled_frame_rect) { + m_tile_window_overlay->set_tiled_frame_rect(tiled_frame_rect); + start_tile_window_animation(m_tile_window_overlay->rect()); + } + } +} + bool WindowManager::process_ongoing_window_move(MouseEvent& event) { if (!m_move_window) @@ -746,10 +788,21 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event) dbgln_if(MOVE_DEBUG, "[WM] Finish moving Window({})", m_move_window); - if (!m_move_window->is_tiled()) + bool did_tile = false; + if (m_move_window_suggested_tile != WindowTileType::None && m_system_effects.tile_window() == TileWindow::ShowTileOverlay) { + auto& cursor_screen = Screen::closest_to_location(event.position()); + m_move_window->set_tiled(m_move_window_suggested_tile, cursor_screen); + m_move_window_suggested_tile = WindowTileType::None; + stop_tile_window_animation(); + did_tile = true; + } else { + did_tile = m_move_window->is_tiled(); + } + + if (!did_tile) m_move_window->set_floating_rect(m_move_window->rect()); - Core::EventLoop::current().post_event(*m_move_window, make(m_move_window->rect())); + m_move_window->send_move_event_to_client(); m_move_window->invalidate(true, true); if (m_move_window->is_resizable()) { process_event_for_doubleclick(*m_move_window, event); @@ -788,30 +841,44 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event) } } else { bool is_resizable = m_move_window->is_resizable(); + auto tile_window = m_system_effects.tile_window(); + bool allow_tile = is_resizable && tile_window != TileWindow::Never; auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin); - auto event_location_relative_to_screen = event.position().translated(-cursor_screen.rect().location()); - if (is_resizable && event_location_relative_to_screen.x() <= tiling_deadzone) { - if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop_relative_to_screen.top()) - m_move_window->set_tiled(WindowTileType::TopLeft); - else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone) - m_move_window->set_tiled(WindowTileType::BottomLeft); - else - m_move_window->set_tiled(WindowTileType::Left); - } else if (is_resizable && event_location_relative_to_screen.x() >= cursor_screen.width() - tiling_deadzone) { - if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop.top()) - m_move_window->set_tiled(WindowTileType::TopRight); - else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone) - m_move_window->set_tiled(WindowTileType::BottomRight); - else - m_move_window->set_tiled(WindowTileType::Right); - } else if (is_resizable && event_location_relative_to_screen.y() <= secondary_deadzone + desktop_relative_to_screen.top()) { - m_move_window->set_tiled(WindowTileType::Top); - } else if (is_resizable && event_location_relative_to_screen.y() >= desktop_relative_to_screen.bottom() - secondary_deadzone) { - m_move_window->set_tiled(WindowTileType::Bottom); - } else if (!m_move_window->is_tiled()) { - Gfx::IntPoint pos = m_move_window_origin.translated(event.position() - m_move_origin); + auto apply_window_tile = [&](WindowTileType tile_type) { + if (tile_window == TileWindow::ShowTileOverlay) { + show_tile_window_overlay(*m_move_window, cursor_screen, tile_type); + } else if (tile_window == TileWindow::TileImmediately) { + if (tile_type != WindowTileType::None) { + m_move_window->set_tiled(tile_type, cursor_screen); + return; + } + } + auto pos = m_move_window_origin.translated(event.position() - m_move_origin); m_move_window->set_position_without_repaint(pos); + }; + + auto event_location_relative_to_screen = event.position().translated(-cursor_screen.rect().location()); + if (allow_tile && event_location_relative_to_screen.x() <= tiling_deadzone) { + if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop_relative_to_screen.top()) + apply_window_tile(WindowTileType::TopLeft); + else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone) + apply_window_tile(WindowTileType::BottomLeft); + else + apply_window_tile(WindowTileType::Left); + } else if (allow_tile && event_location_relative_to_screen.x() >= cursor_screen.width() - tiling_deadzone) { + if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop.top()) + apply_window_tile(WindowTileType::TopRight); + else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone) + apply_window_tile(WindowTileType::BottomRight); + else + apply_window_tile(WindowTileType::Right); + } else if (allow_tile && event_location_relative_to_screen.y() <= secondary_deadzone + desktop_relative_to_screen.top()) { + apply_window_tile(WindowTileType::Top); + } else if (allow_tile && event_location_relative_to_screen.y() >= desktop_relative_to_screen.bottom() - secondary_deadzone) { + apply_window_tile(WindowTileType::Bottom); + } else if (!m_move_window->is_tiled()) { + apply_window_tile(WindowTileType::None); } else if (pixels_moved_from_start > 5) { Gfx::IntPoint adjusted_position = event.position().translated(-m_move_window_cursor_position); m_move_window->set_untiled(); @@ -824,7 +891,7 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event) m_geometry_overlay->window_rect_changed(); } } - Core::EventLoop::current().post_event(*m_move_window, make(m_move_window->rect())); + m_move_window->send_move_event_to_client(); return true; } @@ -875,7 +942,7 @@ bool WindowManager::process_ongoing_window_resize(MouseEvent const& event) if (!m_resize_window->is_tiled()) m_resize_window->set_floating_rect(m_resize_window->rect()); - Core::EventLoop::current().post_event(*m_resize_window, make(m_resize_window->rect())); + m_resize_window->send_resize_event_to_client(); m_resize_window->invalidate(true, true); m_resize_window = nullptr; m_geometry_overlay = nullptr; @@ -1013,7 +1080,7 @@ bool WindowManager::process_ongoing_window_resize(MouseEvent const& event) if (system_effects().geometry() == ShowGeometry::OnMoveAndResize || system_effects().geometry() == ShowGeometry::OnResizeOnly) { m_geometry_overlay->window_rect_changed(); } - Core::EventLoop::current().post_event(*m_resize_window, make(new_rect)); + m_resize_window->send_resize_event_to_client(); return true; } @@ -1424,7 +1491,7 @@ void WindowManager::clear_resize_candidate() m_resize_candidate = nullptr; } -Gfx::IntRect WindowManager::desktop_rect(Screen& screen) const +Gfx::IntRect WindowManager::desktop_rect(Screen const& screen) const { if (active_fullscreen_window()) return Screen::main().rect(); // TODO: we should support fullscreen windows on any screen @@ -1965,11 +2032,11 @@ ResizeDirection WindowManager::resize_direction_of_window(Window const& window) return m_resize_direction; } -Gfx::IntRect WindowManager::tiled_window_rect(Window const& window, WindowTileType tile_type, bool relative_to_window_screen) const +Gfx::IntRect WindowManager::tiled_window_rect(Window const& window, Optional cursor_screen, WindowTileType tile_type) const { VERIFY(tile_type != WindowTileType::None); - auto& screen = Screen::closest_to_rect(window.frame().rect()); + auto const& screen = cursor_screen.has_value() ? cursor_screen.value() : Screen::closest_to_rect(window.frame().rect()); auto rect = desktop_rect(screen); if (tile_type == WindowTileType::Maximized) { @@ -2022,9 +2089,6 @@ Gfx::IntRect WindowManager::tiled_window_rect(Window const& window, WindowTileTy rect.set_y(rect.y() + window_rect.y() - window_frame_rect.y()); rect.set_height(rect.height() - window_frame_rect.height() + window_rect.height()); } - - if (relative_to_window_screen) - rect.translate_by(-screen.rect().location()); return rect; } @@ -2312,12 +2376,12 @@ void WindowManager::set_cursor_highlight_color(Gfx::Color color) sync_config_to_disk(); } -void WindowManager::apply_system_effects(Vector effects, ShowGeometry geometry) +void WindowManager::apply_system_effects(Vector effects, ShowGeometry geometry, TileWindow tile_window) { - if (m_system_effects == SystemEffects { effects, geometry }) + if (m_system_effects == SystemEffects { effects, geometry, tile_window }) return; - m_system_effects = { effects, geometry }; + m_system_effects = { effects, geometry, tile_window }; g_config->write_bool_entry("Effects", "AnimateMenus", m_system_effects.animate_menus()); g_config->write_bool_entry("Effects", "FlashMenus", m_system_effects.flash_menus()); g_config->write_bool_entry("Effects", "AnimateWindows", m_system_effects.animate_windows()); @@ -2329,6 +2393,7 @@ void WindowManager::apply_system_effects(Vector effects, ShowGeometry geom g_config->write_bool_entry("Effects", "WindowShadow", m_system_effects.window_shadow()); g_config->write_bool_entry("Effects", "TooltipShadow", m_system_effects.tooltip_shadow()); g_config->write_entry("Effects", "ShowGeometry", ShowGeometryTools::enum_to_string(geometry)); + g_config->write_entry("Effects", "TileWindow", TileWindowTools::enum_to_string(tile_window)); sync_config_to_disk(); } @@ -2347,7 +2412,8 @@ void WindowManager::load_system_effects() g_config->read_bool_entry("Effects", "TooltipShadow", true) }; ShowGeometry geometry = ShowGeometryTools::string_to_enum(g_config->read_entry("Effects", "ShowGeometry", "OnMoveAndResize")); - m_system_effects = { effects, geometry }; + TileWindow tile_window = TileWindowTools::string_to_enum(g_config->read_entry("Effects", "TileWindow", "ShowTileOverlay")); + m_system_effects = { effects, geometry, tile_window }; ConnectionFromClient::for_each_client([&](auto& client) { client.async_update_system_effects(effects); diff --git a/Userland/Services/WindowServer/WindowManager.h b/Userland/Services/WindowServer/WindowManager.h index fd375e4423b..217e7f61f9c 100644 --- a/Userland/Services/WindowServer/WindowManager.h +++ b/Userland/Services/WindowServer/WindowManager.h @@ -43,6 +43,7 @@ class WindowSwitcher; class Button; class DndOverlay; class WindowGeometryOverlay; +class TileWindowOverlay; class WindowManager : public Core::Object { C_OBJECT(WindowManager) @@ -79,7 +80,7 @@ public: void notify_progress_changed(Window&); void notify_modified_changed(Window&); - Gfx::IntRect tiled_window_rect(Window const&, WindowTileType tile_type = WindowTileType::Maximized, bool relative_to_window_screen = false) const; + Gfx::IntRect tiled_window_rect(Window const&, Optional = {}, WindowTileType tile_type = WindowTileType::Maximized) const; ConnectionFromClient const* dnd_client() const { return m_dnd_client.ptr(); } Core::MimeData const& dnd_mime_data() const { return *m_dnd_mime_data; } @@ -115,7 +116,7 @@ public: void move_to_front_and_make_active(Window&); - Gfx::IntRect desktop_rect(Screen&) const; + Gfx::IntRect desktop_rect(Screen const&) const; Gfx::IntRect arena_rect_for_type(Screen&, WindowType) const; Cursor const& active_cursor() const; @@ -328,7 +329,7 @@ public: bool is_cursor_highlight_enabled() const { return m_cursor_highlight_radius > 0 && m_cursor_highlight_enabled; } void load_system_effects(); - void apply_system_effects(Vector, ShowGeometry); + void apply_system_effects(Vector, ShowGeometry, TileWindow); SystemEffects& system_effects() { return m_system_effects; } RefPtr keymap_switcher() { return m_keymap_switcher; } @@ -339,6 +340,9 @@ public: u8 last_processed_buttons() { return m_last_processed_buttons; } + void start_tile_window_animation(Gfx::IntRect const&); + void stop_tile_window_animation(); + private: explicit WindowManager(Gfx::PaletteImpl&); @@ -433,6 +437,8 @@ private: Gfx::IntPoint to_floating_cursor_position(Gfx::IntPoint) const; + void show_tile_window_overlay(Window&, Screen const&, WindowTileType); + DoubleClickInfo m_double_click_info; int m_double_click_speed { 0 }; int m_max_distance_for_double_click { 4 }; @@ -448,7 +454,10 @@ private: WeakPtr m_automatic_cursor_tracking_window; OwnPtr m_geometry_overlay; + OwnPtr m_tile_window_overlay; + RefPtr m_tile_window_overlay_animation; WeakPtr m_move_window; + WindowTileType m_move_window_suggested_tile { WindowTileType::None }; Gfx::IntPoint m_move_origin; Gfx::IntPoint m_move_window_origin; Gfx::IntPoint m_move_window_cursor_position; diff --git a/Userland/Services/WindowServer/WindowServer.ipc b/Userland/Services/WindowServer/WindowServer.ipc index 418d4b04724..19124a1fba7 100644 --- a/Userland/Services/WindowServer/WindowServer.ipc +++ b/Userland/Services/WindowServer/WindowServer.ipc @@ -152,7 +152,7 @@ endpoint WindowServer get_cursor_highlight_color() => (Gfx::Color color) set_system_fonts(DeprecatedString default_font_query, DeprecatedString fixed_width_font_query, DeprecatedString window_title_font_query) => (bool success) - set_system_effects(Vector effects, u8 geometry) =| + set_system_effects(Vector effects, u8 geometry, u8 tile_window) =| set_window_base_size_and_size_increment(i32 window_id, Gfx::IntSize base_size, Gfx::IntSize size_increment) =| set_window_resize_aspect_ratio(i32 window_id, Optional resize_aspect_ratio) =|