Intense hacking on Widgets.

This commit is contained in:
Andreas Kling 2018-10-10 16:49:36 +02:00
parent 8c84f9749e
commit 6f37429f57
Notes: sideshowbarker 2024-07-19 18:51:18 +09:00
20 changed files with 290 additions and 5 deletions

3
Widgets/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*.o
*.swp
test

View file

@ -13,7 +13,8 @@ AbstractScreen& AbstractScreen::the()
}
AbstractScreen::AbstractScreen(unsigned width, unsigned height)
: m_width(width)
: Object(nullptr)
, m_width(width)
, m_height(height)
{
ASSERT(!s_the);
@ -24,6 +25,17 @@ AbstractScreen::~AbstractScreen()
{
}
void AbstractScreen::event(Event& event)
{
if (event.type() == Event::MouseMove) {
auto& me = static_cast<MouseEvent&>(event);
printf("AbstractScreen::onMouseMove: %d, %d\n", me.x(), me.y());
auto result = m_rootWidget->hitTest(me.x(), me.y());
printf("hit test for %d,%d found: %s{%p} %d,%d\n", me.x(), me.y(), result.widget->className(), result.widget, result.localX, result.localY);
}
}
void AbstractScreen::setRootWidget(Widget* widget)
{
// FIXME: Should we support switching root widgets?

View file

@ -1,14 +1,17 @@
#pragma once
#include "Object.h"
class Widget;
class AbstractScreen {
class AbstractScreen : public Object {
public:
virtual ~AbstractScreen();
unsigned width() const { return m_width; }
unsigned height() const { return m_height; }
Widget* rootWidget() { return m_rootWidget; }
void setRootWidget(Widget*);
static AbstractScreen& the();
@ -17,6 +20,8 @@ protected:
AbstractScreen(unsigned width, unsigned height);
private:
virtual void event(Event&) override;
unsigned m_width { 0 };
unsigned m_height { 0 };

14
Widgets/Color.h Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#include <AK/Types.h>
class Color {
public:
Color() { }
Color(byte r, byte g, byte b);
dword value() const { return m_value; }
private:
dword m_value { 0 };
};

7
Widgets/ColorSDL.cpp Normal file
View file

@ -0,0 +1,7 @@
#include "Color.h"
#include "FrameBufferSDL.h"
Color::Color(byte r, byte g, byte b)
{
m_value = SDL_MapRGB(FrameBufferSDL::the().surface()->format, r, g, b);
}

View file

@ -1,6 +1,8 @@
#include "EventLoopSDL.h"
#include "Event.h"
#include <SDL.h>
#include "AbstractScreen.h"
#include "Widget.h"
EventLoopSDL::EventLoopSDL()
{
@ -18,6 +20,16 @@ void EventLoopSDL::waitForEvent()
case SDL_QUIT:
postEvent(nullptr, make<QuitEvent>());
return;
case SDL_WINDOWEVENT:
if (sdlEvent.window.event == SDL_WINDOWEVENT_EXPOSED) {
// Spam paint events whenever we get exposed.
// This is obviously not ideal, but the SDL backend here is just a prototype anyway.
postEvent(AbstractScreen::the().rootWidget(), make<PaintEvent>());
}
return;
case SDL_MOUSEMOTION:
postEvent(&AbstractScreen::the(), make<MouseEvent>(Event::MouseMove, sdlEvent.motion.x, sdlEvent.motion.y));
return;
}
}
}

View file

@ -1,9 +1,19 @@
#include "FrameBufferSDL.h"
#include <AK/Assertions.h>
FrameBufferSDL* s_the = nullptr;
FrameBufferSDL& FrameBufferSDL::the()
{
ASSERT(s_the);
return *s_the;
}
FrameBufferSDL::FrameBufferSDL(unsigned width, unsigned height)
: AbstractScreen(width, height)
{
ASSERT(!s_the);
s_the = this;
initializeSDL();
}

View file

@ -10,6 +10,11 @@ public:
void show();
SDL_Surface* surface() { return m_surface; }
SDL_Window* window() { return m_window; }
static FrameBufferSDL& the();
private:
void initializeSDL();

26
Widgets/Label.cpp Normal file
View file

@ -0,0 +1,26 @@
#include "Label.h"
#include "Painter.h"
#include <cstdio>
Label::Label(Widget* parent)
: Widget(parent)
{
setRect(Rect(100, 100, 100, 100));
}
Label::~Label()
{
}
void Label::onPaint(PaintEvent&)
{
Painter painter(*this);
painter.fillRect({ 0, 0, width(), height() }, Color(0xc0, 0xc0, 0xc0));
}
void Label::onMouseMove(MouseEvent& event)
{
printf("Label::onMouseMove: x=%d, y=%d\n", event.x(), event.y());
Widget::onMouseMove(event);
}

16
Widgets/Label.h Normal file
View file

@ -0,0 +1,16 @@
#pragma once
#include "Widget.h"
class Label final : public Widget {
public:
explicit Label(Widget* parent);
virtual ~Label() override;
private:
virtual void onPaint(PaintEvent&) override;
virtual void onMouseMove(MouseEvent&) override;
virtual const char* className() const override { return "Label"; }
};

View file

@ -14,6 +14,9 @@ VFS_OBJS = \
Object.o \
Widget.o \
RootWidget.o \
ColorSDL.o \
Painter.o \
Label.o \
test.o
OBJS = $(AK_OBJS) $(VFS_OBJS)

View file

@ -5,10 +5,17 @@
Object::Object(Object* parent)
: m_parent(parent)
{
if (m_parent)
m_parent->addChild(*this);
}
Object::~Object()
{
if (m_parent)
m_parent->removeChild(*this);
for (auto* child : m_children) {
delete child;
}
}
void Object::event(Event& event)
@ -21,3 +28,19 @@ void Object::event(Event& event)
break;
}
}
void Object::addChild(Object& object)
{
m_children.append(&object);
}
void Object::removeChild(Object& object)
{
// Oh geez, Vector needs a remove() huh...
Vector<Object*> newList;
for (auto* child : m_children) {
if (child != &object)
newList.append(child);
}
m_children = std::move(newList);
}

View file

@ -1,5 +1,7 @@
#pragma once
#include <AK/Vector.h>
class Event;
class Object {
@ -7,8 +9,17 @@ public:
Object(Object* parent = nullptr);
virtual ~Object();
virtual const char* className() const { return "Object"; }
virtual void event(Event&);
Vector<Object*>& children() { return m_children; }
private:
void addChild(Object&);
void removeChild(Object&);
Object* m_parent { nullptr };
Vector<Object*> m_children;
};

30
Widgets/Painter.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "Painter.h"
#include "FrameBufferSDL.h"
#include "Widget.h"
#include <AK/Assertions.h>
#include <SDL.h>
Painter::Painter(Widget& widget)
: m_widget(widget)
{
}
Painter::~Painter()
{
int rc = SDL_UpdateWindowSurface(FrameBufferSDL::the().window());
ASSERT(rc == 0);
}
void Painter::fillRect(Rect rect, Color color)
{
rect.moveBy(m_widget.x(), m_widget.y());
SDL_Rect sdlRect;
sdlRect.x = rect.x();
sdlRect.y = rect.y();
sdlRect.w = rect.width();
sdlRect.h = rect.height();
int rc = SDL_FillRect(FrameBufferSDL::the().surface(), &sdlRect, color.value());
ASSERT(rc == 0);
}

16
Widgets/Painter.h Normal file
View file

@ -0,0 +1,16 @@
#pragma once
#include "Color.h"
#include "Rect.h"
class Widget;
class Painter {
public:
explicit Painter(Widget&);
~Painter();
void fillRect(Rect, Color);
private:
Widget& m_widget;
};

45
Widgets/Rect.h Normal file
View file

@ -0,0 +1,45 @@
#pragma once
class Rect {
public:
Rect() { }
Rect(int x, int y, int width, int height)
: m_x(x)
, m_y(y)
, m_width(width)
, m_height(height)
{
}
void moveBy(int dx, int dy)
{
m_x += dx;
m_y += dy;
}
bool contains(int x, int y) const
{
return x >= m_x && x <= right() && y >= m_y && y <= bottom();
}
int left() const { return m_x; }
int right() const { return m_x + m_width; }
int top() const { return m_y; }
int bottom() const { return m_y + m_height; }
int x() const { return m_x; }
int y() const { return m_y; }
int width() const { return m_width; }
int height() const { return m_height; }
void setX(int x) { m_x = x; }
void setY(int y) { m_y = y; }
void setWidth(int width) { m_width = width; }
void setHeight(int height) { m_height = height; }
private:
int m_x { 0 };
int m_y { 0 };
int m_width { 0 };
int m_height { 0 };
};

View file

@ -1,4 +1,5 @@
#include "RootWidget.h"
#include "Painter.h"
#include <cstdio>
RootWidget::RootWidget()
@ -12,6 +13,8 @@ RootWidget::~RootWidget()
void RootWidget::onPaint(PaintEvent& event)
{
printf("RootWidget::onPaint\n");
Painter painter(*this);
painter.fillRect(Rect(0, 0, 800, 600), Color(0x80, 0x80, 0x80));
Widget::onPaint(event);
}

View file

@ -12,6 +12,13 @@ Widget::~Widget()
{
}
void Widget::setRect(const Rect& rect)
{
// FIXME: Make some kind of event loop driven ResizeEvent?
m_rect = rect;
update();
}
void Widget::event(Event& event)
{
switch (event.type()) {
@ -36,8 +43,13 @@ void Widget::event(Event& event)
}
}
void Widget::onPaint(PaintEvent&)
void Widget::onPaint(PaintEvent& event)
{
printf("Widget::onPaint :)\n");
for (auto* ch : children()) {
auto* child = (Widget*)ch;
child->onPaint(event);
}
}
void Widget::onShow(ShowEvent&)
@ -74,3 +86,15 @@ void Widget::update()
EventLoop::main().postEvent(this, make<PaintEvent>());
}
Widget::HitTestResult Widget::hitTest(int x, int y)
{
// FIXME: Care about z-order.
for (auto* ch : children()) {
auto* child = (Widget*)ch;
if (child->rect().contains(x, y)) {
return child->hitTest(x - child->rect().x(), y - child->rect().y());
}
}
return { this, x, y };
}

View file

@ -2,6 +2,7 @@
#include "Event.h"
#include "Object.h"
#include "Rect.h"
class Widget : public Object {
public:
@ -18,9 +19,25 @@ public:
virtual void onMouseDown(MouseEvent&);
virtual void onMouseUp(MouseEvent&);
Rect rect() const { return m_rect; }
int x() const { return rect().x(); }
int y() const { return rect().y(); }
int width() const { return rect().width(); }
int height() const { return rect().height(); }
void update();
struct HitTestResult {
Widget* widget { nullptr };
int localX { 0 };
int localY { 0 };
};
HitTestResult hitTest(int x, int y);
virtual const char* className() const override { return "Widget"; }
void setRect(const Rect&);
private:
int m_x { 0 };
int m_y { 0 };
Rect m_rect;
};

View file

@ -1,6 +1,7 @@
#include "FrameBufferSDL.h"
#include "EventLoopSDL.h"
#include "RootWidget.h"
#include "Label.h"
#include <cstdio>
int main(int c, char** v)
@ -13,5 +14,7 @@ int main(int c, char** v)
RootWidget w;
fb.setRootWidget(&w);
Label l(&w);
return loop.exec();
}