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.
This commit is contained in:
Andreas Kling 2019-02-09 11:19:38 +01:00
parent 4d5fe39494
commit 1f355f2a79
11 changed files with 231 additions and 5 deletions

View file

@ -5,6 +5,7 @@
#include <unistd.h>
#include <SharedGraphics/GraphicsBitmap.h>
#include <SharedGraphics/Painter.h>
#include <LibGUI/GScrollBar.h>
#include <AK/FileSystemPath.h>
#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

View file

@ -4,6 +4,8 @@
#include <AK/Function.h>
#include <sys/stat.h>
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<GraphicsBitmap> m_directory_icon;
RetainPtr<GraphicsBitmap> m_file_icon;
RetainPtr<GraphicsBitmap> m_symlink_icon;
GScrollBar* m_scrollbar { nullptr };
};

View file

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

120
LibGUI/GScrollBar.cpp Normal file
View file

@ -0,0 +1,120 @@
#include <LibGUI/GScrollBar.h>
#include <SharedGraphics/GraphicsBitmap.h>
#include <SharedGraphics/Painter.h>
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;
}
}

42
LibGUI/GScrollBar.h Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include <LibGUI/GWidget.h>
#include <AK/Function.h>
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<void(int)> 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 };
};

View file

@ -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<GResizeEvent>(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<GPaintEvent&>(event));
return handle_paint_event(static_cast<GPaintEvent&>(event));
case GEvent::Resize:
return resize_event(static_cast<GResizeEvent&>(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&)
{
}

View file

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

View file

@ -14,6 +14,7 @@ LIBGUI_OBJS = \
GListBox.o \
GObject.o \
GTextBox.o \
GScrollBar.o \
GWidget.o \
GWindow.o

View file

@ -1,5 +1,7 @@
#pragma once
#include <AK/AKString.h>
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 };

View file

@ -2,6 +2,7 @@
#include "Point.h"
#include "Size.h"
#include <AK/AKString.h>
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;

View file

@ -24,6 +24,11 @@ public:
m_height == other.m_height;
}
bool operator!=(const Size& other) const
{
return !(*this == other);
}
operator GUI_Size() const;
private: