From 1f355f2a79eb8930b1e923f5bbe96982e7f0b0e7 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 9 Feb 2019 11:19:38 +0100 Subject: [PATCH] LibGUI: Start working on a GScrollBar. This widget is far from finished, but it's off to a good start. Also added a GResizeEvent and GWidget::resize_event() so that widgets can react to being resized. --- FileManager/DirectoryView.cpp | 9 +++ FileManager/DirectoryView.h | 5 ++ LibGUI/GEvent.h | 17 +++++ LibGUI/GScrollBar.cpp | 120 ++++++++++++++++++++++++++++++++++ LibGUI/GScrollBar.h | 42 ++++++++++++ LibGUI/GWidget.cpp | 27 ++++++-- LibGUI/GWidget.h | 4 ++ LibGUI/Makefile | 1 + SharedGraphics/Point.h | 3 + SharedGraphics/Rect.h | 3 + SharedGraphics/Size.h | 5 ++ 11 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 LibGUI/GScrollBar.cpp create mode 100644 LibGUI/GScrollBar.h diff --git a/FileManager/DirectoryView.cpp b/FileManager/DirectoryView.cpp index 9b6126ee55..53286c9455 100644 --- a/FileManager/DirectoryView.cpp +++ b/FileManager/DirectoryView.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "DirectoryView.h" @@ -14,12 +15,19 @@ DirectoryView::DirectoryView(GWidget* parent) m_directory_icon = GraphicsBitmap::load_from_file("/res/icons/folder16.rgb", { 16, 16 }); m_file_icon = GraphicsBitmap::load_from_file("/res/icons/file16.rgb", { 16, 16 }); m_symlink_icon = GraphicsBitmap::load_from_file("/res/icons/link16.rgb", { 16, 16 }); + + m_scrollbar = new GScrollBar(this); } DirectoryView::~DirectoryView() { } +void DirectoryView::resize_event(GResizeEvent& event) +{ + m_scrollbar->set_relative_rect(event.size().width() - 16, 0, 16, event.size().height()); +} + void DirectoryView::open(const String& path) { if (m_path == path) @@ -55,6 +63,7 @@ void DirectoryView::reload() entries.append(move(entry)); } closedir(dirp); + m_scrollbar->set_range(0, item_count() - 1); } const GraphicsBitmap& DirectoryView::icon_for(const Entry& entry) const diff --git a/FileManager/DirectoryView.h b/FileManager/DirectoryView.h index 3b06783f08..cf6e1935ed 100644 --- a/FileManager/DirectoryView.h +++ b/FileManager/DirectoryView.h @@ -4,6 +4,8 @@ #include #include +class GScrollBar; + class DirectoryView final : public GWidget { public: DirectoryView(GWidget* parent); @@ -19,6 +21,7 @@ public: private: virtual void paint_event(GPaintEvent&) override; + virtual void resize_event(GResizeEvent&) override; virtual void mousedown_event(GMouseEvent&) override; Rect row_rect(int item_index) const; @@ -47,4 +50,6 @@ private: RetainPtr m_directory_icon; RetainPtr m_file_icon; RetainPtr m_symlink_icon; + + GScrollBar* m_scrollbar { nullptr }; }; diff --git a/LibGUI/GEvent.h b/LibGUI/GEvent.h index 52846cb0ae..c6d82b1a1d 100644 --- a/LibGUI/GEvent.h +++ b/LibGUI/GEvent.h @@ -13,6 +13,7 @@ public: Show, Hide, Paint, + Resize, MouseMove, MouseDown, MouseUp, @@ -62,6 +63,22 @@ private: Rect m_rect; }; +class GResizeEvent final : public GEvent { +public: + explicit GResizeEvent(const Size& old_size, const Size& size) + : GEvent(GEvent::Resize) + , m_old_size(old_size) + , m_size(size) + { + } + + const Size& old_size() const { return m_old_size; } + const Size& size() const { return m_size; } +private: + Size m_old_size; + Size m_size; +}; + class GShowEvent final : public GEvent { public: GShowEvent() diff --git a/LibGUI/GScrollBar.cpp b/LibGUI/GScrollBar.cpp new file mode 100644 index 0000000000..3491383a45 --- /dev/null +++ b/LibGUI/GScrollBar.cpp @@ -0,0 +1,120 @@ +#include +#include +#include + +GScrollBar::GScrollBar(GWidget* parent) + : GWidget(parent) +{ +} + +GScrollBar::~GScrollBar() +{ +} + +void GScrollBar::set_range(int min, int max) +{ + ASSERT(min <= max); + if (m_min == min && m_max == max) + return; + + m_min = min; + m_max = max; + + int old_value = m_value; + if (m_value < m_min) + m_value = m_min; + if (m_value > m_max) + m_value = m_max; + if (on_change && m_value != old_value) + on_change(m_value); + + update(); +} + +void GScrollBar::set_value(int value) +{ + if (value < m_min) + value = m_min; + if (value > m_max) + value = m_max; + if (value == m_value) + return; + m_value = value; + if (on_change) + on_change(value); + update(); +} + +Rect GScrollBar::up_button_rect() const +{ + return { 0, 0, button_size(), button_size() }; +} + +Rect GScrollBar::down_button_rect() const +{ + return { 0, height() - button_size(), button_size(), button_size() }; +} + +Rect GScrollBar::pgup_rect() const +{ + return { 0, button_size(), button_size(), scrubber_rect().top() - button_size() }; +} + +Rect GScrollBar::pgdn_rect() const +{ + auto scrubber_rect = this->scrubber_rect(); + return { 0, scrubber_rect.bottom(), button_size(), height() - button_size() - scrubber_rect.bottom() }; +} + +Rect GScrollBar::scrubber_rect() const +{ + int range_size = m_max - m_min; + if (range_size == 0) + return { 0, button_size(), button_size(), height() - button_size() * 2 }; + float available_y = height() - button_size() * 3; + float y_step = available_y / range_size; + float y = button_size() + (y_step * m_value); + return { 0, (int)y, button_size(), button_size() }; +} + +void GScrollBar::paint_event(GPaintEvent&) +{ + Painter painter(*this); + + painter.fill_rect(rect(), Color(0xc0c0c0)); + + painter.draw_rect(up_button_rect(), Color::DarkGray, true); + painter.fill_rect(up_button_rect().shrunken(2, 2), Color::LightGray); + + painter.draw_rect(down_button_rect(), Color::DarkGray, true); + painter.fill_rect(down_button_rect().shrunken(2, 2), Color::LightGray); + + painter.fill_rect(pgup_rect(), Color::Magenta); + painter.fill_rect(pgdn_rect(), Color::Yellow); + + painter.fill_rect(scrubber_rect(), Color::Red); + + dbgprintf("Paint!\n"); +} + +void GScrollBar::mousedown_event(GMouseEvent& event) +{ + if (event.button() != GMouseButton::Left) + return; + if (up_button_rect().contains(event.position())) { + set_value(value() - m_step); + return; + } + if (down_button_rect().contains(event.position())) { + set_value(value() + m_step); + return; + } + if (pgup_rect().contains(event.position())) { + set_value(value() - m_big_step); + return; + } + if (pgdn_rect().contains(event.position())) { + set_value(value() + m_big_step); + return; + } +} diff --git a/LibGUI/GScrollBar.h b/LibGUI/GScrollBar.h new file mode 100644 index 0000000000..48f1199bb1 --- /dev/null +++ b/LibGUI/GScrollBar.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +class GScrollBar final : public GWidget { +public: + explicit GScrollBar(GWidget* parent); + virtual ~GScrollBar() override; + + int value() const { return m_value; } + int min() const { return m_min; } + int max() const { return m_max; } + int step() const { return m_step; } + int big_step() const { return m_big_step; } + + void set_range(int min, int max); + void set_value(int value); + void set_step(int step) { m_step = step; } + void set_big_step(int big_step) { m_big_step = big_step; } + + Function on_change; + +private: + virtual void paint_event(GPaintEvent&) override; + virtual void mousedown_event(GMouseEvent&) override; + virtual const char* class_name() const override { return "GScrollBar"; } + + int button_size() const { return 16; } + Rect up_button_rect() const; + Rect down_button_rect() const; + Rect pgup_rect() const; + Rect pgdn_rect() const; + Rect scrubber_rect() const; + + int m_min { 0 }; + int m_max { 0 }; + int m_value { 0 }; + int m_step { 1 }; + int m_big_step { 5 }; +}; + diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index 1c5d75c7af..4b308c52d0 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -22,7 +22,10 @@ void GWidget::set_relative_rect(const Rect& rect) { if (rect == m_relative_rect) return; - // FIXME: Make some kind of event loop driven ResizeEvent? + if (m_relative_rect.size() != rect.size()) { + auto event = make(m_relative_rect.size(), rect.size()); + GEventLoop::main().post_event(this, move(event)); + } m_relative_rect = rect; update(); } @@ -37,7 +40,9 @@ void GWidget::event(GEvent& event) switch (event.type()) { case GEvent::Paint: m_has_pending_paint_event = false; - return paint_event(static_cast(event)); + return handle_paint_event(static_cast(event)); + case GEvent::Resize: + return resize_event(static_cast(event)); case GEvent::FocusIn: return focusin_event(event); case GEvent::FocusOut: @@ -63,21 +68,33 @@ void GWidget::event(GEvent& event) } } -void GWidget::paint_event(GPaintEvent& event) +void GWidget::handle_paint_event(GPaintEvent& event) { if (fill_with_background_color()) { Painter painter(*this); painter.fill_rect(event.rect(), background_color()); } + paint_event(event); for (auto* ch : children()) { auto* child = (GWidget*)ch; if (child->relative_rect().intersects(event.rect())) { - // FIXME: Pass localized rect? - child->event(event); + auto local_rect = event.rect(); + local_rect.intersect(child->relative_rect()); + local_rect.move_by(-child->relative_rect().x(), -child->relative_rect().y()); + GPaintEvent local_event(local_rect); + child->event(local_event); } } } +void GWidget::resize_event(GResizeEvent&) +{ +} + +void GWidget::paint_event(GPaintEvent&) +{ +} + void GWidget::show_event(GShowEvent&) { } diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index 39785f1f8c..68db8a36d9 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -17,6 +17,7 @@ public: virtual void event(GEvent&) override; virtual void paint_event(GPaintEvent&); + virtual void resize_event(GResizeEvent&); virtual void show_event(GShowEvent&); virtual void hide_event(GHideEvent&); virtual void keydown_event(GKeyEvent&); @@ -56,6 +57,7 @@ public: virtual const char* class_name() const override { return "GWidget"; } void set_relative_rect(const Rect&); + void set_relative_rect(int x, int y, int width, int height) { set_relative_rect({ x, y, width, height }); } void move_to(const Point& point) { set_relative_rect({ point, relative_rect().size() }); } void move_to(int x, int y) { move_to({ x, y }); } @@ -97,6 +99,8 @@ public: bool global_cursor_tracking() const; private: + void handle_paint_event(GPaintEvent&); + GWindow* m_window { nullptr }; Rect m_relative_rect; diff --git a/LibGUI/Makefile b/LibGUI/Makefile index 6d1dc21472..046e667e87 100644 --- a/LibGUI/Makefile +++ b/LibGUI/Makefile @@ -14,6 +14,7 @@ LIBGUI_OBJS = \ GListBox.o \ GObject.o \ GTextBox.o \ + GScrollBar.o \ GWidget.o \ GWindow.o diff --git a/SharedGraphics/Point.h b/SharedGraphics/Point.h index a0c0e3a4aa..e9da12218a 100644 --- a/SharedGraphics/Point.h +++ b/SharedGraphics/Point.h @@ -1,5 +1,7 @@ #pragma once +#include + class Rect; struct GUI_Point; @@ -47,6 +49,7 @@ public: } operator GUI_Point() const; + String to_string() const { return String::format("[%d,%d]", x(), y()); } private: int m_x { 0 }; diff --git a/SharedGraphics/Rect.h b/SharedGraphics/Rect.h index eab74724fd..0c0b1fd410 100644 --- a/SharedGraphics/Rect.h +++ b/SharedGraphics/Rect.h @@ -2,6 +2,7 @@ #include "Point.h" #include "Size.h" +#include struct GUI_Rect; @@ -171,6 +172,8 @@ public: Rect united(const Rect&) const; + String to_string() const { return String::format("[%d,%d %dx%d]", x(), y(), width(), height()); } + private: Point m_location; Size m_size; diff --git a/SharedGraphics/Size.h b/SharedGraphics/Size.h index 87f2c5b088..d1af7d70c1 100644 --- a/SharedGraphics/Size.h +++ b/SharedGraphics/Size.h @@ -24,6 +24,11 @@ public: m_height == other.m_height; } + bool operator!=(const Size& other) const + { + return !(*this == other); + } + operator GUI_Size() const; private: