Start working on a Widgets library.

This commit is contained in:
Andreas Kling 2018-10-10 15:12:38 +02:00
parent a181a8f6e7
commit 8c84f9749e
Notes: sideshowbarker 2024-07-19 18:51:21 +09:00
18 changed files with 594 additions and 0 deletions

View file

@ -0,0 +1,35 @@
#include "AbstractScreen.h"
#include "EventLoop.h"
#include "Event.h"
#include "Widget.h"
#include <AK/Assertions.h>
static AbstractScreen* s_the;
AbstractScreen& AbstractScreen::the()
{
ASSERT(s_the);
return *s_the;
}
AbstractScreen::AbstractScreen(unsigned width, unsigned height)
: m_width(width)
, m_height(height)
{
ASSERT(!s_the);
s_the = this;
}
AbstractScreen::~AbstractScreen()
{
}
void AbstractScreen::setRootWidget(Widget* widget)
{
// FIXME: Should we support switching root widgets?
ASSERT(!m_rootWidget);
ASSERT(widget);
m_rootWidget = widget;
EventLoop::main().postEvent(m_rootWidget, make<ShowEvent>());
}

25
Widgets/AbstractScreen.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
class Widget;
class AbstractScreen {
public:
virtual ~AbstractScreen();
unsigned width() const { return m_width; }
unsigned height() const { return m_height; }
void setRootWidget(Widget*);
static AbstractScreen& the();
protected:
AbstractScreen(unsigned width, unsigned height);
private:
unsigned m_width { 0 };
unsigned m_height { 0 };
Widget* m_rootWidget { nullptr };
};

118
Widgets/Event.h Normal file
View file

@ -0,0 +1,118 @@
#pragma once
#include <AK/Types.h>
static const char* eventNames[] = {
"Invalid",
"Quit",
"Show",
"Hide",
"Paint",
"MouseMove",
"MouseDown",
"MouseUp",
"KeyDown",
"KeyUp",
};
class Event {
public:
enum Type {
Invalid = 0,
Quit,
Show,
Hide,
Paint,
MouseMove,
MouseDown,
MouseUp,
KeyDown,
KeyUp,
};
Event() { }
~Event() { }
Type type() const { return m_type; }
const char* name() const { return eventNames[(unsigned)m_type]; }
protected:
explicit Event(Type type) : m_type(type) { }
private:
Type m_type { Invalid };
};
class QuitEvent final : public Event {
public:
QuitEvent()
: Event(Event::Quit)
{
}
};
class PaintEvent final : public Event {
public:
PaintEvent()
: Event(Event::Paint)
{
}
};
class ShowEvent final : public Event {
public:
ShowEvent()
: Event(Event::Show)
{
}
};
class HideEvent final : public Event {
public:
HideEvent()
: Event(Event::Hide)
{
}
};
enum class MouseButton : byte {
None = 0,
Left,
Right,
};
class KeyEvent : public Event {
public:
KeyEvent(Type type, int key)
: Event(type)
, m_key(key)
{
}
int key() const { return m_key; }
private:
int m_key { 0 };
};
class MouseEvent : public Event {
public:
MouseEvent(Type type, int x, int y, MouseButton button = MouseButton::None)
: Event(type)
, m_x(x)
, m_y(y)
, m_button(button)
{
}
int x() const { return m_x; }
int y() const { return m_y; }
MouseButton button() const { return m_button; }
private:
int m_x { 0 };
int m_y { 0 };
MouseButton m_button { MouseButton::None };
};

52
Widgets/EventLoop.cpp Normal file
View file

@ -0,0 +1,52 @@
#include "EventLoop.h"
#include "Event.h"
#include "Object.h"
static EventLoop* s_mainEventLoop;
EventLoop::EventLoop()
{
if (!s_mainEventLoop)
s_mainEventLoop = this;
}
EventLoop::~EventLoop()
{
}
EventLoop& EventLoop::main()
{
ASSERT(s_mainEventLoop);
return *s_mainEventLoop;
}
int EventLoop::exec()
{
for (;;) {
if (m_queuedEvents.isEmpty())
waitForEvent();
auto events = std::move(m_queuedEvents);
for (auto& queuedEvent : events) {
auto* receiver = queuedEvent.receiver;
auto& event = *queuedEvent.event;
printf("EventLoop: Object{%p} event %u (%s)\n", receiver, (unsigned)event.type(), event.name());
if (!receiver) {
switch (event.type()) {
case Event::Quit:
return 0;
default:
printf("event type %u with no receiver :(\n", event.type());
return 1;
}
} else {
receiver->event(event);
}
}
}
}
void EventLoop::postEvent(Object* receiver, OwnPtr<Event>&& event)
{
m_queuedEvents.append({ receiver, std::move(event) });
}

30
Widgets/EventLoop.h Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Vector.h>
class Event;
class Object;
class EventLoop {
public:
virtual ~EventLoop();
int exec();
virtual void waitForEvent() = 0;
void postEvent(Object* receiver, OwnPtr<Event>&&);
static EventLoop& main();
protected:
EventLoop();
private:
struct QueuedEvent {
Object* receiver { nullptr };
OwnPtr<Event> event;
};
Vector<QueuedEvent> m_queuedEvents;
};

24
Widgets/EventLoopSDL.cpp Normal file
View file

@ -0,0 +1,24 @@
#include "EventLoopSDL.h"
#include "Event.h"
#include <SDL.h>
EventLoopSDL::EventLoopSDL()
{
}
EventLoopSDL::~EventLoopSDL()
{
}
void EventLoopSDL::waitForEvent()
{
SDL_Event sdlEvent;
while (SDL_WaitEvent(&sdlEvent) != 0) {
switch (sdlEvent.type) {
case SDL_QUIT:
postEvent(nullptr, make<QuitEvent>());
return;
}
}
}

13
Widgets/EventLoopSDL.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include "EventLoop.h"
class EventLoopSDL final : public EventLoop {
public:
EventLoopSDL();
virtual ~EventLoopSDL() override;
private:
virtual void waitForEvent() override;
};

BIN
Widgets/FrameBuffer.o Normal file

Binary file not shown.

View file

@ -0,0 +1,49 @@
#include "FrameBufferSDL.h"
#include <AK/Assertions.h>
FrameBufferSDL::FrameBufferSDL(unsigned width, unsigned height)
: AbstractScreen(width, height)
{
initializeSDL();
}
FrameBufferSDL::~FrameBufferSDL()
{
SDL_DestroyWindow(m_window);
m_surface = nullptr;
m_window = nullptr;
SDL_Quit();
}
void FrameBufferSDL::show()
{
}
void FrameBufferSDL::initializeSDL()
{
if (m_window)
return;
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
ASSERT_NOT_REACHED();
}
m_window = SDL_CreateWindow(
"FrameBuffer",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
width(),
height(),
SDL_WINDOW_SHOWN);
ASSERT(m_window);
m_surface = SDL_GetWindowSurface(m_window);
ASSERT(m_surface);
SDL_FillRect(m_surface, nullptr, SDL_MapRGB(m_surface->format, 0xff, 0xff, 0xff));
SDL_UpdateWindowSurface(m_window);
}

19
Widgets/FrameBufferSDL.h Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include "AbstractScreen.h"
#include <SDL.h>
class FrameBufferSDL final : public AbstractScreen {
public:
FrameBufferSDL(unsigned width, unsigned height);
virtual ~FrameBufferSDL() override;
void show();
private:
void initializeSDL();
SDL_Window* m_window { nullptr };
SDL_Surface* m_surface { nullptr };
};

37
Widgets/Makefile Normal file
View file

@ -0,0 +1,37 @@
PROGRAM = test
AK_OBJS = \
../AK/String.o \
../AK/StringImpl.o \
../AK/SimpleMalloc.o \
../AK/kmalloc.o
VFS_OBJS = \
AbstractScreen.o \
FrameBufferSDL.o \
EventLoop.o \
EventLoopSDL.o \
Object.o \
Widget.o \
RootWidget.o \
test.o
OBJS = $(AK_OBJS) $(VFS_OBJS)
CXXFLAGS = -std=c++17 -O0 -W -Wall -Wextra -Wconversion -I. -I.. -g `sdl2-config --cflags`
LDFLAGS = `sdl2-config --libs`
#test.o: BlockDevice.h FileBackedBlockDevice.h FileSystem.h Ext2FileSystem.h VirtualFileSystem.h FileHandle.h
all: $(PROGRAM)
.cpp.o:
$(CXX) $(CXXFLAGS) -o $@ -c $<
clean:
rm -f $(OBJS) $(PROGRAM)
$(PROGRAM): $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS)

23
Widgets/Object.cpp Normal file
View file

@ -0,0 +1,23 @@
#include "Object.h"
#include "Event.h"
#include <AK/Assertions.h>
Object::Object(Object* parent)
: m_parent(parent)
{
}
Object::~Object()
{
}
void Object::event(Event& event)
{
switch (event.type()) {
case Event::Invalid:
ASSERT_NOT_REACHED();
break;
default:
break;
}
}

14
Widgets/Object.h Normal file
View file

@ -0,0 +1,14 @@
#pragma once
class Event;
class Object {
public:
Object(Object* parent = nullptr);
virtual ~Object();
virtual void event(Event&);
private:
Object* m_parent { nullptr };
};

23
Widgets/RootWidget.cpp Normal file
View file

@ -0,0 +1,23 @@
#include "RootWidget.h"
#include <cstdio>
RootWidget::RootWidget()
{
}
RootWidget::~RootWidget()
{
}
void RootWidget::onPaint(PaintEvent& event)
{
printf("RootWidget::onPaint\n");
Widget::onPaint(event);
}
void RootWidget::onMouseMove(MouseEvent& event)
{
printf("RootWidget::onMouseMove: x=%d, y=%d\n", event.x(), event.y());
Widget::onMouseMove(event);
}

13
Widgets/RootWidget.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include "Widget.h"
class RootWidget final : public Widget {
public:
RootWidget();
virtual ~RootWidget() override;
private:
virtual void onPaint(PaintEvent&) override;
virtual void onMouseMove(MouseEvent&) override;
};

76
Widgets/Widget.cpp Normal file
View file

@ -0,0 +1,76 @@
#include "Widget.h"
#include "Event.h"
#include "EventLoop.h"
#include <AK/Assertions.h>
Widget::Widget(Widget* parent)
: Object(parent)
{
}
Widget::~Widget()
{
}
void Widget::event(Event& event)
{
switch (event.type()) {
case Event::Paint:
return onPaint(static_cast<PaintEvent&>(event));
case Event::Show:
return onShow(static_cast<ShowEvent&>(event));
case Event::Hide:
return onHide(static_cast<HideEvent&>(event));
case Event::KeyDown:
return onKeyDown(static_cast<KeyEvent&>(event));
case Event::KeyUp:
return onKeyUp(static_cast<KeyEvent&>(event));
case Event::MouseMove:
return onMouseMove(static_cast<MouseEvent&>(event));
case Event::MouseDown:
return onMouseDown(static_cast<MouseEvent&>(event));
case Event::MouseUp:
return onMouseUp(static_cast<MouseEvent&>(event));
default:
return Object::event(event);
}
}
void Widget::onPaint(PaintEvent&)
{
}
void Widget::onShow(ShowEvent&)
{
update();
}
void Widget::onHide(HideEvent&)
{
}
void Widget::onKeyDown(KeyEvent&)
{
}
void Widget::onKeyUp(KeyEvent&)
{
}
void Widget::onMouseDown(MouseEvent&)
{
}
void Widget::onMouseUp(MouseEvent&)
{
}
void Widget::onMouseMove(MouseEvent&)
{
}
void Widget::update()
{
EventLoop::main().postEvent(this, make<PaintEvent>());
}

26
Widgets/Widget.h Normal file
View file

@ -0,0 +1,26 @@
#pragma once
#include "Event.h"
#include "Object.h"
class Widget : public Object {
public:
explicit Widget(Widget* parent = nullptr);
virtual ~Widget();
virtual void event(Event&);
virtual void onPaint(PaintEvent&);
virtual void onShow(ShowEvent&);
virtual void onHide(HideEvent&);
virtual void onKeyDown(KeyEvent&);
virtual void onKeyUp(KeyEvent&);
virtual void onMouseMove(MouseEvent&);
virtual void onMouseDown(MouseEvent&);
virtual void onMouseUp(MouseEvent&);
void update();
private:
int m_x { 0 };
int m_y { 0 };
};

17
Widgets/test.cpp Normal file
View file

@ -0,0 +1,17 @@
#include "FrameBufferSDL.h"
#include "EventLoopSDL.h"
#include "RootWidget.h"
#include <cstdio>
int main(int c, char** v)
{
FrameBufferSDL fb(800, 600);
fb.show();
EventLoopSDL loop;
RootWidget w;
fb.setRootWidget(&w);
return loop.exec();
}