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.
This commit is contained in:
Nick Vella 2021-02-16 00:59:43 +11:00 committed by Andreas Kling
parent dea0d22ab3
commit 15c1f7a40d
Notes: sideshowbarker 2024-07-18 22:13:10 +09:00
7 changed files with 130 additions and 8 deletions

View file

@ -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)

View file

@ -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;

View file

@ -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());

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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) =|