diff --git a/Userland/Libraries/LibGUI/Window.cpp b/Userland/Libraries/LibGUI/Window.cpp index 0cdb8b9c43..5ddc73b1a5 100644 --- a/Userland/Libraries/LibGUI/Window.cpp +++ b/Userland/Libraries/LibGUI/Window.cpp @@ -150,6 +150,7 @@ void Window::show() m_alpha_hit_threshold, m_base_size, m_size_increment, + m_minimum_size_when_windowless, m_resize_aspect_ratio, (i32)m_window_type, m_title_when_windowless, @@ -259,6 +260,23 @@ void Window::set_rect(const Gfx::IntRect& a_rect) m_main_widget->resize(window_rect.size()); } +Gfx::IntSize Window::minimum_size() const +{ + if (!is_visible()) + return m_minimum_size_when_windowless; + + return WindowServerConnection::the().send_sync(m_window_id)->size(); +} + +void Window::set_minimum_size(const Gfx::IntSize& size) +{ + m_minimum_size_modified = true; + m_minimum_size_when_windowless = size; + + if (is_visible()) + WindowServerConnection::the().send_sync(m_window_id, size); +} + void Window::center_on_screen() { auto window_rect = rect(); @@ -278,6 +296,14 @@ void Window::center_within(const Window& other) void Window::set_window_type(WindowType window_type) { m_window_type = window_type; + + if (!m_minimum_size_modified) { + // Apply minimum size defaults. + if (m_window_type == WindowType::Normal) + m_minimum_size_when_windowless = { 50, 50 }; + else + m_minimum_size_when_windowless = { 1, 1 }; + } } void Window::set_cursor(Gfx::StandardCursor cursor) diff --git a/Userland/Libraries/LibGUI/Window.h b/Userland/Libraries/LibGUI/Window.h index aca7e0842e..50843e5d78 100644 --- a/Userland/Libraries/LibGUI/Window.h +++ b/Userland/Libraries/LibGUI/Window.h @@ -108,6 +108,10 @@ public: Gfx::IntPoint position() const { return rect().location(); } + Gfx::IntSize minimum_size() const; + void set_minimum_size(const Gfx::IntSize&); + void set_minimum_size(int width, int height) { set_minimum_size({ width, height }); } + void move_to(int x, int y) { move_to({ x, y }); } void move_to(const Gfx::IntPoint& point) { set_rect({ point, size() }); } @@ -248,6 +252,8 @@ private: WeakPtr m_automatic_cursor_tracking_widget; WeakPtr m_hovered_widget; Gfx::IntRect m_rect_when_windowless; + Gfx::IntSize m_minimum_size_when_windowless { 50, 50 }; + bool m_minimum_size_modified { false }; String m_title_when_windowless; Vector m_pending_paint_event_rects; Gfx::IntSize m_size_increment; diff --git a/Userland/Services/WindowServer/ClientConnection.cpp b/Userland/Services/WindowServer/ClientConnection.cpp index f287b6ff6b..3778213805 100644 --- a/Userland/Services/WindowServer/ClientConnection.cpp +++ b/Userland/Services/WindowServer/ClientConnection.cpp @@ -413,6 +413,48 @@ OwnPtr ClientConnection::handle(c return make(it->value->rect()); } +OwnPtr ClientConnection::handle(const Messages::WindowServer::SetWindowMinimumSize& message) +{ + int window_id = message.window_id(); + auto it = m_windows.find(window_id); + if (it == m_windows.end()) { + did_misbehave("SetWindowMinimumSize: Bad window ID"); + return {}; + } + auto& window = *(*it).value; + if (window.is_fullscreen()) { + dbgln("ClientConnection: Ignoring SetWindowMinimumSize request for fullscreen window"); + return {}; + } + + window.set_minimum_size(message.size()); + + if (window.width() < window.minimum_size().width() || window.height() < window.minimum_size().height()) { + // New minimum size is larger than the current window size, resize accordingly. + auto new_rect = window.rect(); + bool did_size_clamp = window.apply_minimum_size(new_rect); + window.set_rect(new_rect); + window.nudge_into_desktop(); + window.request_update(window.rect()); + + if (did_size_clamp) + window.refresh_client_size(); + } + + return make(); +} + +OwnPtr ClientConnection::handle(const Messages::WindowServer::GetWindowMinimumSize& message) +{ + int window_id = message.window_id(); + auto it = m_windows.find(window_id); + if (it == m_windows.end()) { + did_misbehave("GetWindowMinimumSize: Bad window ID"); + return {}; + } + return make(it->value->minimum_size()); +} + OwnPtr ClientConnection::handle(const Messages::WindowServer::GetWindowRectInMenubar& message) { int window_id = message.window_id(); @@ -454,9 +496,13 @@ OwnPtr ClientConnection::handle(co rect = { WindowManager::the().get_recommended_window_position({ 100, 100 }), message.rect().size() }; window->set_default_positioned(true); } - window->apply_minimum_size(rect); + window->set_minimum_size(message.minimum_size()); + bool did_size_clamp = window->apply_minimum_size(rect); window->set_rect(rect); window->nudge_into_desktop(); + + if (did_size_clamp) + window->refresh_client_size(); } if (window->type() == WindowType::Desktop) { window->set_rect(WindowManager::the().desktop_rect()); diff --git a/Userland/Services/WindowServer/ClientConnection.h b/Userland/Services/WindowServer/ClientConnection.h index 9ffe869c45..767507e3f9 100644 --- a/Userland/Services/WindowServer/ClientConnection.h +++ b/Userland/Services/WindowServer/ClientConnection.h @@ -109,6 +109,8 @@ private: virtual void handle(const Messages::WindowServer::StartWindowResize&) override; virtual OwnPtr handle(const Messages::WindowServer::SetWindowRect&) override; virtual OwnPtr handle(const Messages::WindowServer::GetWindowRect&) override; + virtual OwnPtr handle(const Messages::WindowServer::SetWindowMinimumSize&) override; + virtual OwnPtr handle(const Messages::WindowServer::GetWindowMinimumSize&) override; virtual OwnPtr handle(const Messages::WindowServer::GetWindowRectInMenubar&) override; virtual void handle(const Messages::WindowServer::InvalidateRect&) override; virtual void handle(const Messages::WindowServer::DidFinishPainting&) override; diff --git a/Userland/Services/WindowServer/Window.cpp b/Userland/Services/WindowServer/Window.cpp index bdcf5e017a..30937dea14 100644 --- a/Userland/Services/WindowServer/Window.cpp +++ b/Userland/Services/WindowServer/Window.cpp @@ -37,6 +37,8 @@ namespace WindowServer { +const static Gfx::IntSize s_default_normal_minimum_size = { 50, 50 }; + static String default_window_icon_path() { return "/res/icons/16x16/window.png"; @@ -88,6 +90,10 @@ Window::Window(Core::Object& parent, WindowType type) , m_icon(default_window_icon()) , m_frame(*this) { + // Set default minimum size for Normal windows + if (m_type == WindowType::Normal) + m_minimum_size = s_default_normal_minimum_size; + WindowManager::the().add_window(*this); } @@ -112,6 +118,10 @@ Window::Window(ClientConnection& client, WindowType window_type, int window_id, m_listens_to_wm_events = true; } + // Set default minimum size for Normal windows + if (m_type == WindowType::Normal) + m_minimum_size = s_default_normal_minimum_size; + if (parent_window) set_parent_window(*parent_window); WindowManager::the().add_window(*this); @@ -176,14 +186,16 @@ void Window::set_rect_without_repaint(const Gfx::IntRect& rect) m_frame.notify_window_rect_changed(old_rect, rect); // recomputes occlusions } -void Window::apply_minimum_size(Gfx::IntRect& rect) +bool Window::apply_minimum_size(Gfx::IntRect& rect) { - Gfx::IntSize minimum_size { 1, 1 }; - if (type() == WindowType::Normal) - minimum_size = { 50, 50 }; + int new_width = max(m_minimum_size.width(), rect.width()); + int new_height = max(m_minimum_size.height(), rect.height()); + bool did_size_clamp = new_width != rect.width() || new_height != rect.height(); - rect.set_width(max(minimum_size.width(), rect.width())); - rect.set_height(max(minimum_size.height(), rect.height())); + rect.set_width(new_width); + rect.set_height(new_height); + + return did_size_clamp; } void Window::nudge_into_desktop(bool force_titlebar_visible) @@ -218,6 +230,20 @@ void Window::nudge_into_desktop(bool force_titlebar_visible) set_rect(new_window_rect); } +void Window::set_minimum_size(const Gfx::IntSize& size) +{ + ASSERT(!size.is_empty()); + + if (m_minimum_size == size) + return; + + // Disallow setting minimum zero widths or heights. + if (size.width() == 0 || size.height() == 0) + return; + + m_minimum_size = size; +} + void Window::handle_mouse_event(const MouseEvent& event) { set_automatic_cursor_tracking_enabled(event.buttons() != 0); @@ -523,6 +549,11 @@ bool Window::invalidate_no_notify(const Gfx::IntRect& rect, bool with_frame) return true; } +void Window::refresh_client_size() +{ + client()->post_message(Messages::WindowClient::WindowResized(m_window_id, m_rect)); +} + void Window::prepare_dirty_rects() { if (m_invalidated_all) { diff --git a/Userland/Services/WindowServer/Window.h b/Userland/Services/WindowServer/Window.h index 39e6418757..cf36a38df0 100644 --- a/Userland/Services/WindowServer/Window.h +++ b/Userland/Services/WindowServer/Window.h @@ -169,9 +169,13 @@ public: void set_rect(const Gfx::IntRect&); void set_rect(int x, int y, int width, int height) { set_rect({ x, y, width, height }); } void set_rect_without_repaint(const Gfx::IntRect&); - void apply_minimum_size(Gfx::IntRect&); + bool apply_minimum_size(Gfx::IntRect&); void nudge_into_desktop(bool force_titlebar_visible = true); + Gfx::IntSize minimum_size() const { return m_minimum_size; } + void set_minimum_size(const Gfx::IntSize&); + void set_minimum_size(int width, int height) { set_minimum_size({ width, height }); } + void set_taskbar_rect(const Gfx::IntRect&); const Gfx::IntRect& taskbar_rect() const { return m_taskbar_rect; } @@ -190,6 +194,8 @@ public: void invalidate(const Gfx::IntRect&, bool with_frame = false); bool invalidate_no_notify(const Gfx::IntRect& rect, bool with_frame = false); + void refresh_client_size(); + void prepare_dirty_rects(); void clear_dirty_rects(); Gfx::DisjointRectSet& dirty_rects() { return m_dirty_rects; } @@ -375,6 +381,7 @@ private: float m_alpha_hit_threshold { 0.0f }; Gfx::IntSize m_size_increment; Gfx::IntSize m_base_size; + Gfx::IntSize m_minimum_size { 1, 1 }; NonnullRefPtr m_icon; RefPtr m_cursor; WindowFrame m_frame; diff --git a/Userland/Services/WindowServer/WindowServer.ipc b/Userland/Services/WindowServer/WindowServer.ipc index 3746ad9b10..f6a88ee52e 100644 --- a/Userland/Services/WindowServer/WindowServer.ipc +++ b/Userland/Services/WindowServer/WindowServer.ipc @@ -44,6 +44,7 @@ endpoint WindowServer = 2 float alpha_hit_threshold, Gfx::IntSize base_size, Gfx::IntSize size_increment, + Gfx::IntSize minimum_size, Optional resize_aspect_ratio, i32 type, [UTF8] String title, @@ -59,6 +60,9 @@ endpoint WindowServer = 2 SetWindowRect(i32 window_id, Gfx::IntRect rect) => (Gfx::IntRect rect) GetWindowRect(i32 window_id) => (Gfx::IntRect rect) + SetWindowMinimumSize(i32 window_id, Gfx::IntSize size) => () + GetWindowMinimumSize(i32 window_id) => (Gfx::IntSize size) + GetWindowRectInMenubar(i32 window_id) => (Gfx::IntRect rect) StartWindowResize(i32 window_id) =|