소스 검색

WindowServer, LibGUI: Variable minimum window sizes

Minimum window size can now be customised and set at runtime via the
SetWindowMinimumSize WindowServer message and the set_minimum_size
LibGUI::Window method. The default minimum size remains at 50x50.

Some behind-the-scenes mechanics had to be added to LibGUI::Window to
ensure that the minimum size is remembered if set before the window is
shown. WindowServer sends a resize event to the client if it requests a
size on create that's smaller than it's minimum size.
Nick Vella 4 년 전
부모
커밋
15c1f7a40d

+ 26 - 0
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<Messages::WindowServer::GetWindowMinimumSize>(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<Messages::WindowServer::SetWindowMinimumSize>(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)

+ 6 - 0
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<Widget> m_automatic_cursor_tracking_widget;
     WeakPtr<Widget> 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<Gfx::IntRect, 32> m_pending_paint_event_rects;
     Gfx::IntSize m_size_increment;

+ 47 - 1
Userland/Services/WindowServer/ClientConnection.cpp

@@ -413,6 +413,48 @@ OwnPtr<Messages::WindowServer::GetWindowRectResponse> ClientConnection::handle(c
     return make<Messages::WindowServer::GetWindowRectResponse>(it->value->rect());
 }
 
+OwnPtr<Messages::WindowServer::SetWindowMinimumSizeResponse> 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<Messages::WindowServer::SetWindowMinimumSizeResponse>();
+}
+
+OwnPtr<Messages::WindowServer::GetWindowMinimumSizeResponse> 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<Messages::WindowServer::GetWindowMinimumSizeResponse>(it->value->minimum_size());
+}
+
 OwnPtr<Messages::WindowServer::GetWindowRectInMenubarResponse> ClientConnection::handle(const Messages::WindowServer::GetWindowRectInMenubar& message)
 {
     int window_id = message.window_id();
@@ -454,9 +496,13 @@ OwnPtr<Messages::WindowServer::CreateWindowResponse> 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());

+ 2 - 0
Userland/Services/WindowServer/ClientConnection.h

@@ -109,6 +109,8 @@ private:
     virtual void handle(const Messages::WindowServer::StartWindowResize&) override;
     virtual OwnPtr<Messages::WindowServer::SetWindowRectResponse> handle(const Messages::WindowServer::SetWindowRect&) override;
     virtual OwnPtr<Messages::WindowServer::GetWindowRectResponse> handle(const Messages::WindowServer::GetWindowRect&) override;
+    virtual OwnPtr<Messages::WindowServer::SetWindowMinimumSizeResponse> handle(const Messages::WindowServer::SetWindowMinimumSize&) override;
+    virtual OwnPtr<Messages::WindowServer::GetWindowMinimumSizeResponse> handle(const Messages::WindowServer::GetWindowMinimumSize&) override;
     virtual OwnPtr<Messages::WindowServer::GetWindowRectInMenubarResponse> handle(const Messages::WindowServer::GetWindowRectInMenubar&) override;
     virtual void handle(const Messages::WindowServer::InvalidateRect&) override;
     virtual void handle(const Messages::WindowServer::DidFinishPainting&) override;

+ 37 - 6
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) {

+ 8 - 1
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<Gfx::Bitmap> m_icon;
     RefPtr<Cursor> m_cursor;
     WindowFrame m_frame;

+ 4 - 0
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<Gfx::IntSize> 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) =|