From 2def3d8d3ff3c913919cd64b2bf75b77ee57d1cf Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 10 Feb 2019 11:07:13 +0100 Subject: [PATCH] LibGUI: Start adding an automatic widget layout system. My needs are really quite simple, so I'm just going to add what I need as I go along. The first thing I needed was a simple box layout with widgets being able to say whether they prefer fixed or fill for both their vertical and horizontal sizes. I also made a simple GStatusBar so FileManager can show how many bytes worth of files are in the current directory. --- Applications/Clock/Makefile | 2 +- Applications/FileManager/DirectoryView.cpp | 21 ++++- Applications/FileManager/DirectoryView.h | 3 + Applications/FileManager/Makefile | 2 +- Applications/FileManager/main.cpp | 18 +++-- Applications/FontEditor/Makefile | 2 +- Applications/Launcher/Makefile | 2 +- Applications/Terminal/Makefile | 2 +- Kernel/makeall.sh | 20 ++--- LibC/entry.cpp | 6 ++ LibGUI/GBoxLayout.cpp | 89 ++++++++++++++++++++++ LibGUI/GBoxLayout.h | 18 +++++ LibGUI/GLayout.cpp | 41 ++++++++++ LibGUI/GLayout.h | 31 ++++++++ LibGUI/GStatusBar.cpp | 35 +++++++++ LibGUI/GStatusBar.h | 20 +++++ LibGUI/GWidget.cpp | 68 ++++++++++++++++- LibGUI/GWidget.h | 26 +++++++ LibGUI/Makefile | 3 + SharedGraphics/Painter.cpp | 10 +-- SharedGraphics/Rect.h | 10 +++ SharedGraphics/Size.h | 11 +++ 22 files changed, 411 insertions(+), 29 deletions(-) create mode 100644 LibGUI/GBoxLayout.cpp create mode 100644 LibGUI/GBoxLayout.h create mode 100644 LibGUI/GLayout.cpp create mode 100644 LibGUI/GLayout.h create mode 100644 LibGUI/GStatusBar.cpp create mode 100644 LibGUI/GStatusBar.h diff --git a/Applications/Clock/Makefile b/Applications/Clock/Makefile index 13d43b94f4..1939a1d220 100644 --- a/Applications/Clock/Makefile +++ b/Applications/Clock/Makefile @@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections all: $(APP) $(APP): $(OBJS) - $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibGUI/LibGUI.a ../LibC/LibC.a + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Applications/FileManager/DirectoryView.cpp b/Applications/FileManager/DirectoryView.cpp index c87d652e28..4ffc28feb6 100644 --- a/Applications/FileManager/DirectoryView.cpp +++ b/Applications/FileManager/DirectoryView.cpp @@ -53,6 +53,8 @@ void DirectoryView::reload() } m_directories.clear(); m_files.clear(); + + size_t bytes_in_files = 0; while (auto* de = readdir(dirp)) { Entry entry; entry.name = de->d_name; @@ -66,10 +68,21 @@ void DirectoryView::reload() entry.mode = st.st_mode; auto& entries = S_ISDIR(st.st_mode) ? m_directories : m_files; entries.append(move(entry)); + + if (S_ISREG(entry.mode)) + bytes_in_files += st.st_size; } closedir(dirp); int excess_height = max(0, (item_count() * item_height()) - height()); m_scrollbar->set_range(0, excess_height); + + + + set_status_message(String::format("%d item%s (%u byte%s)", + item_count(), + item_count() != 1 ? "s" : "", + bytes_in_files, + bytes_in_files != 1 ? "s" : "")); } const GraphicsBitmap& DirectoryView::icon_for(const Entry& entry) const @@ -128,7 +141,7 @@ void DirectoryView::paint_event(GPaintEvent&) Rect icon_rect(horizontal_padding, y, icon_size, item_height()); Rect name_rect(icon_rect.right() + horizontal_padding, y, 100, item_height()); Rect size_rect(name_rect.right() + horizontal_padding, y, 64, item_height()); - painter.fill_rect(row_rect(painted_item_index), i % 2 ? Color::LightGray : Color::White); + painter.fill_rect(row_rect(painted_item_index), i % 2 ? Color(210, 210, 210) : Color::White); painter.blit_with_alpha(icon_rect.location(), icon_for(entry), { 0, 0, icon_size, icon_size }); painter.draw_text(name_rect, entry.name, Painter::TextAlignment::CenterLeft, Color::Black); if (should_show_size_for(entry)) @@ -143,3 +156,9 @@ void DirectoryView::paint_event(GPaintEvent&) unpainted_rect.intersect(rect()); painter.fill_rect(unpainted_rect, Color::White); } + +void DirectoryView::set_status_message(String&& message) +{ + if (on_status_message) + on_status_message(move(message)); +} diff --git a/Applications/FileManager/DirectoryView.h b/Applications/FileManager/DirectoryView.h index cf6e1935ed..60bc82ebf0 100644 --- a/Applications/FileManager/DirectoryView.h +++ b/Applications/FileManager/DirectoryView.h @@ -15,6 +15,7 @@ public: void reload(); Function on_path_change; + Function on_status_message; int item_height() const { return 16; } int item_count() const { return m_directories.size() + m_files.size(); } @@ -24,6 +25,8 @@ private: virtual void resize_event(GResizeEvent&) override; virtual void mousedown_event(GMouseEvent&) override; + void set_status_message(String&&); + Rect row_rect(int item_index) const; struct Entry { diff --git a/Applications/FileManager/Makefile b/Applications/FileManager/Makefile index 1fd1842e8e..f41a35cfca 100644 --- a/Applications/FileManager/Makefile +++ b/Applications/FileManager/Makefile @@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections all: $(APP) $(APP): $(OBJS) - $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibGUI/LibGUI.a ../LibC/LibC.a + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Applications/FileManager/main.cpp b/Applications/FileManager/main.cpp index e8bb846976..2bf781fccb 100644 --- a/Applications/FileManager/main.cpp +++ b/Applications/FileManager/main.cpp @@ -1,14 +1,10 @@ -#include #include #include -#include -#include +#include #include -#include -#include +#include #include #include -#include #include "DirectoryView.h" static GWindow* make_window(); @@ -33,13 +29,21 @@ GWindow* make_window() auto* widget = new GWidget; window->set_main_widget(widget); + widget->set_layout(make(Orientation::Vertical)); + auto* directory_view = new DirectoryView(widget); - directory_view->set_relative_rect({ 0, 0, 240, 300 }); + + auto* statusbar = new GStatusBar(widget); + statusbar->set_text("Welcome!"); directory_view->on_path_change = [window] (const String& new_path) { window->set_title(String::format("FileManager: %s", new_path.characters())); }; + directory_view->on_status_message = [statusbar] (String message) { + statusbar->set_text(move(message)); + }; + directory_view->open("/"); return window; diff --git a/Applications/FontEditor/Makefile b/Applications/FontEditor/Makefile index eafdbb0266..dd592ab7c8 100644 --- a/Applications/FontEditor/Makefile +++ b/Applications/FontEditor/Makefile @@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections all: $(APP) $(APP): $(OBJS) - $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibGUI/LibGUI.a ../LibC/LibC.a + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Applications/Launcher/Makefile b/Applications/Launcher/Makefile index 05ee7ea0ba..cd6a918d08 100644 --- a/Applications/Launcher/Makefile +++ b/Applications/Launcher/Makefile @@ -22,7 +22,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections all: $(APP) $(APP): $(OBJS) - $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibGUI/LibGUI.a ../LibC/LibC.a + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Applications/Terminal/Makefile b/Applications/Terminal/Makefile index bb7f277265..6f8dde1e3e 100644 --- a/Applications/Terminal/Makefile +++ b/Applications/Terminal/Makefile @@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections all: $(APP) $(APP): $(OBJS) - $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibC/LibC.a + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibC/LibC.a .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Kernel/makeall.sh b/Kernel/makeall.sh index 3a688d3065..5e3a145c75 100755 --- a/Kernel/makeall.sh +++ b/Kernel/makeall.sh @@ -8,16 +8,16 @@ make -C ../LibGUI clean && \ make -C ../LibGUI && \ make -C ../Userland clean && \ make -C ../Userland && \ -make -C ../Application/Terminal clean && \ -make -C ../Application/Terminal && \ -make -C ../Application/FontEditor clean && \ -make -C ../Application/FontEditor && \ -make -C ../Application/Clock clean && \ -make -C ../Application/Clock && \ -make -C ../Application/Launcher clean && \ -make -C ../Application/Launcher && \ -make -C ../Application/FileManager clean && \ -make -C ../Application/FileManager && \ +make -C ../Applications/Terminal clean && \ +make -C ../Applications/Terminal && \ +make -C ../Applications/FontEditor clean && \ +make -C ../Applications/FontEditor && \ +make -C ../Applications/Clock clean && \ +make -C ../Applications/Clock && \ +make -C ../Applications/Launcher clean && \ +make -C ../Applications/Launcher && \ +make -C ../Applications/FileManager clean && \ +make -C ../Applications/FileManager && \ make clean &&\ make && \ sudo ./sync.sh diff --git a/LibC/entry.cpp b/LibC/entry.cpp index 393aec2ddc..ec610296f9 100644 --- a/LibC/entry.cpp +++ b/LibC/entry.cpp @@ -38,3 +38,9 @@ epilogue: // Birger's birthday <3 return 20150614; } + +extern "C" void __cxa_pure_virtual() NORETURN; +extern "C" void __cxa_pure_virtual() +{ + ASSERT_NOT_REACHED(); +} diff --git a/LibGUI/GBoxLayout.cpp b/LibGUI/GBoxLayout.cpp new file mode 100644 index 0000000000..bc9872bf2d --- /dev/null +++ b/LibGUI/GBoxLayout.cpp @@ -0,0 +1,89 @@ +#include +#include + +GBoxLayout::GBoxLayout(Orientation orientation) + : m_orientation(orientation) +{ +} + +GBoxLayout::~GBoxLayout() +{ +} + +#if 0 +Size GLayout::compute_preferred_size() const +{ + +} + + +static Size compute_preferred_size(GLayout::Entry& entry) +{ + if (entry.layout) + return entry.layout->compute_preferred_size(); + else { + return entry.widget->preferred_size(); + } +} +#endif + +void GBoxLayout::run(GWidget& widget) +{ + if (m_entries.is_empty()) + return; + + Size available_size = widget.size(); + int number_of_entries_with_fixed_size = 0; + + for (auto& entry : m_entries) { + if (entry.widget && entry.widget->size_policy(orientation()) == SizePolicy::Fixed) { + available_size -= entry.widget->preferred_size(); + ++number_of_entries_with_fixed_size; + } + } + + int number_of_entries_with_automatic_size = m_entries.size() - number_of_entries_with_fixed_size; + + dbgprintf("GBoxLayout: available_size=%d, fixed=%d, fill=%d\n", available_size.height(), number_of_entries_with_fixed_size, number_of_entries_with_automatic_size); + + Size automatic_size; + + if (m_orientation == Orientation::Horizontal) { + automatic_size.set_width(available_size.width() / number_of_entries_with_automatic_size); + automatic_size.set_height(widget.height()); + } else { + automatic_size.set_width(widget.width()); + automatic_size.set_height(available_size.height() / number_of_entries_with_automatic_size); + } + + dbgprintf("GBoxLayout: automatic_size=%s\n", automatic_size.to_string().characters()); + + int current_x = 0; + int current_y = 0; + for (auto& entry : m_entries) { + Rect rect(current_x, current_y, 0, 0); + if (entry.layout) { + // FIXME: Implement recursive layout. + ASSERT_NOT_REACHED(); + } + ASSERT(entry.widget); + if (entry.widget->size_policy(orientation()) == SizePolicy::Fixed) { + rect.set_size(automatic_size); + if (orientation() == Orientation::Vertical) { + rect.set_height(entry.widget->preferred_size().height()); + } else { + rect.set_width(entry.widget->preferred_size().height()); + } + } else { + rect.set_size(automatic_size); + } + + dbgprintf("GBoxLayout: apply, %s{%p} <- %s\n", entry.widget->class_name(), entry.widget.ptr(), rect.to_string().characters()); + entry.widget->set_relative_rect(rect); + + if (orientation() == Orientation::Horizontal) + current_x += rect.width(); + else + current_y += rect.height(); + } +} diff --git a/LibGUI/GBoxLayout.h b/LibGUI/GBoxLayout.h new file mode 100644 index 0000000000..605c7d5d62 --- /dev/null +++ b/LibGUI/GBoxLayout.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +class GBoxLayout final : public GLayout { +public: + explicit GBoxLayout(Orientation); + virtual ~GBoxLayout() override; + + Orientation orientation() const { return m_orientation; } + + virtual void run(GWidget&) override; + +private: + Orientation m_orientation; +}; + diff --git a/LibGUI/GLayout.cpp b/LibGUI/GLayout.cpp new file mode 100644 index 0000000000..9a16f36580 --- /dev/null +++ b/LibGUI/GLayout.cpp @@ -0,0 +1,41 @@ +#include +#include + +GLayout::GLayout() +{ +} + +GLayout::~GLayout() +{ +} + +void GLayout::notify_adopted(Badge, GWidget& widget) +{ + if (m_owner.ptr() == &widget) + return; + m_owner = widget.make_weak_ptr(); +} + +void GLayout::notify_disowned(Badge, GWidget& widget) +{ + ASSERT(m_owner.ptr() == &widget); + m_owner.clear(); +} + +void GLayout::add_layout(OwnPtr&& layout) +{ + Entry entry; + entry.layout = move(layout); + m_entries.append(move(entry)); + if (m_owner) + m_owner->notify_layout_changed(Badge()); +} + +void GLayout::add_widget(GWidget& widget) +{ + Entry entry; + entry.widget = widget.make_weak_ptr(); + m_entries.append(move(entry)); + if (m_owner) + m_owner->notify_layout_changed(Badge()); +} diff --git a/LibGUI/GLayout.h b/LibGUI/GLayout.h new file mode 100644 index 0000000000..7a513c8408 --- /dev/null +++ b/LibGUI/GLayout.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include + +class GWidget; + +class GLayout { +public: + GLayout(); + virtual ~GLayout(); + + void add_widget(GWidget&); + void add_layout(OwnPtr&&); + + virtual void run(GWidget&) = 0; + + void notify_adopted(Badge, GWidget&); + void notify_disowned(Badge, GWidget&); + +protected: + struct Entry { + WeakPtr widget; + OwnPtr layout; + }; + WeakPtr m_owner; + Vector m_entries; +}; + diff --git a/LibGUI/GStatusBar.cpp b/LibGUI/GStatusBar.cpp new file mode 100644 index 0000000000..a8b7232428 --- /dev/null +++ b/LibGUI/GStatusBar.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +GStatusBar::GStatusBar(GWidget* parent) + : GWidget(parent) +{ + set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); + set_preferred_size({ 0, 16 }); + set_layout(make(Orientation::Horizontal)); + m_label = new GLabel(this); + m_label->set_fill_with_background_color(false); +} + +GStatusBar::~GStatusBar() +{ +} + +void GStatusBar::set_text(String&& text) +{ + m_label->set_text(move(text)); +} + +String GStatusBar::text() const +{ + return m_label->text(); +} + +void GStatusBar::paint_event(GPaintEvent&) +{ + Painter painter(*this); + painter.fill_rect({ 0, 1, width(), height() - 1 }, Color::LightGray); + painter.draw_line({ 0, 0 }, { width() - 1, 0 }, Color::DarkGray); +} diff --git a/LibGUI/GStatusBar.h b/LibGUI/GStatusBar.h new file mode 100644 index 0000000000..18ccb0ca59 --- /dev/null +++ b/LibGUI/GStatusBar.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +class GLabel; + +class GStatusBar : public GWidget { +public: + explicit GStatusBar(GWidget* parent); + virtual ~GStatusBar() override; + + String text() const; + void set_text(String&&); + +private: + virtual const char* class_name() const override { return "GStatusBar"; } + virtual void paint_event(GPaintEvent&) override; + + GLabel* m_label { nullptr }; +}; diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index 287b9c6e45..d29052a69c 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -2,6 +2,7 @@ #include "GEvent.h" #include "GEventLoop.h" #include "GWindow.h" +#include #include #include #include @@ -12,6 +13,9 @@ GWidget::GWidget(GWidget* parent) set_font(nullptr); m_background_color = Color::LightGray; m_foreground_color = Color::Black; + + if (parent && parent->layout()) + parent->layout()->add_widget(*this); } GWidget::~GWidget() @@ -42,7 +46,7 @@ void GWidget::event(GEvent& event) m_has_pending_paint_event = false; return handle_paint_event(static_cast(event)); case GEvent::Resize: - return resize_event(static_cast(event)); + return handle_resize_event(static_cast(event)); case GEvent::FocusIn: return focusin_event(event); case GEvent::FocusOut: @@ -87,6 +91,41 @@ void GWidget::handle_paint_event(GPaintEvent& event) } } +void GWidget::set_layout(OwnPtr&& layout) +{ + if (m_layout.ptr() == layout.ptr()) + return; + if (m_layout) + m_layout->notify_disowned(Badge(), *this); + m_layout = move(layout); + if (m_layout) { + m_layout->notify_adopted(Badge(), *this); + do_layout(); + } else { + update(); + } +} + +void GWidget::do_layout() +{ + if (!m_layout) + return; + m_layout->run(*this); + update(); +} + +void GWidget::notify_layout_changed(Badge) +{ + do_layout(); +} + +void GWidget::handle_resize_event(GResizeEvent& event) +{ + if (layout()) + do_layout(); + return resize_event(event); +} + void GWidget::resize_event(GResizeEvent&) { } @@ -216,3 +255,30 @@ bool GWidget::global_cursor_tracking() const return false; return win->global_cursor_tracking_widget() == this; } + +void GWidget::set_preferred_size(const Size& size) +{ + if (m_preferred_size == size) + return; + m_preferred_size = size; + invalidate_layout(); +} + +void GWidget::set_size_policy(SizePolicy horizontal_policy, SizePolicy vertical_policy) +{ + if (m_horizontal_size_policy == horizontal_policy && m_vertical_size_policy == vertical_policy) + return; + m_horizontal_size_policy = horizontal_policy; + m_vertical_size_policy = vertical_policy; + invalidate_layout(); +} + +void GWidget::invalidate_layout() +{ + auto* w = window(); + if (!w) + return; + if (!w->main_widget()) + return; + w->main_widget()->do_layout(); +} diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index cb40a6dfa0..8c64635898 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -5,16 +5,32 @@ #include #include #include +#include #include class GraphicsBitmap; +class GLayout; class GWindow; +enum class SizePolicy { Fixed, Fill }; +enum class Orientation { Horizontal, Vertical }; + class GWidget : public GObject { public: explicit GWidget(GWidget* parent = nullptr); virtual ~GWidget() override; + GLayout* layout() { return m_layout.ptr(); } + void set_layout(OwnPtr&&); + + SizePolicy horizontal_size_policy() const { return m_horizontal_size_policy; } + SizePolicy vertical_size_policy() const { return m_vertical_size_policy; } + SizePolicy size_policy(Orientation orientation) { return orientation == Orientation::Horizontal ? m_horizontal_size_policy : m_vertical_size_policy; } + void set_size_policy(SizePolicy horizontal_policy, SizePolicy vertical_policy); + + Size preferred_size() const { return m_preferred_size; } + void set_preferred_size(const Size&); + virtual void event(GEvent&) override; virtual void paint_event(GPaintEvent&); virtual void resize_event(GResizeEvent&); @@ -100,16 +116,26 @@ public: void set_global_cursor_tracking(bool); bool global_cursor_tracking() const; + void notify_layout_changed(Badge); + private: void handle_paint_event(GPaintEvent&); + void handle_resize_event(GResizeEvent&); + void do_layout(); + void invalidate_layout(); GWindow* m_window { nullptr }; + OwnPtr m_layout; Rect m_relative_rect; Color m_background_color { 0xffffff }; Color m_foreground_color { 0x000000 }; RetainPtr m_font; + SizePolicy m_horizontal_size_policy { SizePolicy::Fill }; + SizePolicy m_vertical_size_policy { SizePolicy::Fill }; + Size m_preferred_size; + bool m_has_pending_paint_event { false }; bool m_fill_with_background_color { true }; }; diff --git a/LibGUI/Makefile b/LibGUI/Makefile index 9bc857475d..ee4cbe3e6b 100644 --- a/LibGUI/Makefile +++ b/LibGUI/Makefile @@ -15,8 +15,11 @@ LIBGUI_OBJS = \ GObject.o \ GTextBox.o \ GScrollBar.o \ + GStatusBar.o \ GWidget.o \ GStyle.o \ + GLayout.o \ + GBoxLayout.o \ GWindow.o OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS) diff --git a/SharedGraphics/Painter.cpp b/SharedGraphics/Painter.cpp index fe2ee5448f..dde6846508 100644 --- a/SharedGraphics/Painter.cpp +++ b/SharedGraphics/Painter.cpp @@ -4,7 +4,7 @@ #include #include -#ifdef LIBGUI +#ifdef USERLAND #include #include #include @@ -20,7 +20,7 @@ Painter::Painter(GraphicsBitmap& bitmap) m_clip_rect = { { 0, 0 }, bitmap.size() }; } -#ifdef LIBGUI +#ifdef USERLAND Painter::Painter(GWidget& widget) : m_font(&widget.font()) { @@ -34,9 +34,9 @@ Painter::Painter(GWidget& widget) m_target = GraphicsBitmap::create_wrapper(backing.size, backing.pixels); ASSERT(m_target); m_window = widget.window(); - m_translation.move_by(widget.relative_position()); + m_translation.move_by(widget.window_relative_rect().location()); // NOTE: m_clip_rect is in Window coordinates since we are painting into its backing store. - m_clip_rect = widget.relative_rect(); + m_clip_rect = widget.window_relative_rect(); m_clip_rect.intersect(m_target->rect()); #ifdef DEBUG_WIDGET_UNDERDRAW @@ -49,7 +49,7 @@ Painter::Painter(GWidget& widget) Painter::~Painter() { -#ifdef LIBGUI +#ifdef USERLAND m_target = nullptr; int rc = gui_release_window_backing_store(m_backing_store_id); ASSERT(rc == 0); diff --git a/SharedGraphics/Rect.h b/SharedGraphics/Rect.h index 56279d2e62..9f6f89cefd 100644 --- a/SharedGraphics/Rect.h +++ b/SharedGraphics/Rect.h @@ -52,6 +52,16 @@ public: return { x() + width() / 2, y() + height() / 2 }; } + void set_location(const Point& location) + { + m_location = location; + } + + void set_size(const Size& size) + { + m_size = size; + } + void inflate(int w, int h) { set_x(x() - w / 2); diff --git a/SharedGraphics/Size.h b/SharedGraphics/Size.h index d1af7d70c1..7fe41d125d 100644 --- a/SharedGraphics/Size.h +++ b/SharedGraphics/Size.h @@ -1,5 +1,7 @@ #pragma once +#include + struct GUI_Size; class Size { @@ -29,8 +31,17 @@ public: return !(*this == other); } + Size& operator-=(const Size& other) + { + m_width -= other.m_width; + m_height -= other.m_height; + return *this; + } + operator GUI_Size() const; + String to_string() const { return String::format("[%d,%d]", m_width, m_height); } + private: int m_width { 0 }; int m_height { 0 };