From 8c84f9749e3220de767c78a2588808f0d9c54d79 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 10 Oct 2018 15:12:38 +0200 Subject: [PATCH] Start working on a Widgets library. --- Widgets/AbstractScreen.cpp | 35 +++++++++++ Widgets/AbstractScreen.h | 25 ++++++++ Widgets/Event.h | 118 +++++++++++++++++++++++++++++++++++++ Widgets/EventLoop.cpp | 52 ++++++++++++++++ Widgets/EventLoop.h | 30 ++++++++++ Widgets/EventLoopSDL.cpp | 24 ++++++++ Widgets/EventLoopSDL.h | 13 ++++ Widgets/FrameBuffer.o | Bin 0 -> 47688 bytes Widgets/FrameBufferSDL.cpp | 49 +++++++++++++++ Widgets/FrameBufferSDL.h | 19 ++++++ Widgets/Makefile | 37 ++++++++++++ Widgets/Object.cpp | 23 ++++++++ Widgets/Object.h | 14 +++++ Widgets/RootWidget.cpp | 23 ++++++++ Widgets/RootWidget.h | 13 ++++ Widgets/Widget.cpp | 76 ++++++++++++++++++++++++ Widgets/Widget.h | 26 ++++++++ Widgets/test.cpp | 17 ++++++ 18 files changed, 594 insertions(+) create mode 100644 Widgets/AbstractScreen.cpp create mode 100644 Widgets/AbstractScreen.h create mode 100644 Widgets/Event.h create mode 100644 Widgets/EventLoop.cpp create mode 100644 Widgets/EventLoop.h create mode 100644 Widgets/EventLoopSDL.cpp create mode 100644 Widgets/EventLoopSDL.h create mode 100644 Widgets/FrameBuffer.o create mode 100644 Widgets/FrameBufferSDL.cpp create mode 100644 Widgets/FrameBufferSDL.h create mode 100644 Widgets/Makefile create mode 100644 Widgets/Object.cpp create mode 100644 Widgets/Object.h create mode 100644 Widgets/RootWidget.cpp create mode 100644 Widgets/RootWidget.h create mode 100644 Widgets/Widget.cpp create mode 100644 Widgets/Widget.h create mode 100644 Widgets/test.cpp diff --git a/Widgets/AbstractScreen.cpp b/Widgets/AbstractScreen.cpp new file mode 100644 index 0000000000..d9216de1e1 --- /dev/null +++ b/Widgets/AbstractScreen.cpp @@ -0,0 +1,35 @@ +#include "AbstractScreen.h" +#include "EventLoop.h" +#include "Event.h" +#include "Widget.h" +#include + +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()); +} diff --git a/Widgets/AbstractScreen.h b/Widgets/AbstractScreen.h new file mode 100644 index 0000000000..a607221777 --- /dev/null +++ b/Widgets/AbstractScreen.h @@ -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 }; +}; + diff --git a/Widgets/Event.h b/Widgets/Event.h new file mode 100644 index 0000000000..28297a9b6c --- /dev/null +++ b/Widgets/Event.h @@ -0,0 +1,118 @@ +#pragma once + +#include + +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 }; +}; + diff --git a/Widgets/EventLoop.cpp b/Widgets/EventLoop.cpp new file mode 100644 index 0000000000..aa227eb99b --- /dev/null +++ b/Widgets/EventLoop.cpp @@ -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) +{ + m_queuedEvents.append({ receiver, std::move(event) }); +} + diff --git a/Widgets/EventLoop.h b/Widgets/EventLoop.h new file mode 100644 index 0000000000..ca8b912bc0 --- /dev/null +++ b/Widgets/EventLoop.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +class Event; +class Object; + +class EventLoop { +public: + virtual ~EventLoop(); + + int exec(); + + virtual void waitForEvent() = 0; + + void postEvent(Object* receiver, OwnPtr&&); + + static EventLoop& main(); + +protected: + EventLoop(); + +private: + struct QueuedEvent { + Object* receiver { nullptr }; + OwnPtr event; + }; + Vector m_queuedEvents; +}; diff --git a/Widgets/EventLoopSDL.cpp b/Widgets/EventLoopSDL.cpp new file mode 100644 index 0000000000..8092a67bd0 --- /dev/null +++ b/Widgets/EventLoopSDL.cpp @@ -0,0 +1,24 @@ +#include "EventLoopSDL.h" +#include "Event.h" +#include + +EventLoopSDL::EventLoopSDL() +{ +} + +EventLoopSDL::~EventLoopSDL() +{ +} + +void EventLoopSDL::waitForEvent() +{ + SDL_Event sdlEvent; + while (SDL_WaitEvent(&sdlEvent) != 0) { + switch (sdlEvent.type) { + case SDL_QUIT: + postEvent(nullptr, make()); + return; + } + } +} + diff --git a/Widgets/EventLoopSDL.h b/Widgets/EventLoopSDL.h new file mode 100644 index 0000000000..b5ac349060 --- /dev/null +++ b/Widgets/EventLoopSDL.h @@ -0,0 +1,13 @@ +#pragma once + +#include "EventLoop.h" + +class EventLoopSDL final : public EventLoop { +public: + EventLoopSDL(); + virtual ~EventLoopSDL() override; + +private: + virtual void waitForEvent() override; +}; + diff --git a/Widgets/FrameBuffer.o b/Widgets/FrameBuffer.o new file mode 100644 index 0000000000000000000000000000000000000000..73b4ce2597edbb49f2a2db8cb012a34f3d1f3bd4 GIT binary patch literal 47688 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 + +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); +} + diff --git a/Widgets/FrameBufferSDL.h b/Widgets/FrameBufferSDL.h new file mode 100644 index 0000000000..9d58894427 --- /dev/null +++ b/Widgets/FrameBufferSDL.h @@ -0,0 +1,19 @@ +#pragma once + +#include "AbstractScreen.h" +#include + +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 }; +}; + diff --git a/Widgets/Makefile b/Widgets/Makefile new file mode 100644 index 0000000000..58c3678f0c --- /dev/null +++ b/Widgets/Makefile @@ -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) + diff --git a/Widgets/Object.cpp b/Widgets/Object.cpp new file mode 100644 index 0000000000..bf7fa7469b --- /dev/null +++ b/Widgets/Object.cpp @@ -0,0 +1,23 @@ +#include "Object.h" +#include "Event.h" +#include + +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; + } +} diff --git a/Widgets/Object.h b/Widgets/Object.h new file mode 100644 index 0000000000..4b0d0a64f9 --- /dev/null +++ b/Widgets/Object.h @@ -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 }; +}; diff --git a/Widgets/RootWidget.cpp b/Widgets/RootWidget.cpp new file mode 100644 index 0000000000..b1b54a5682 --- /dev/null +++ b/Widgets/RootWidget.cpp @@ -0,0 +1,23 @@ +#include "RootWidget.h" +#include + +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); +} + diff --git a/Widgets/RootWidget.h b/Widgets/RootWidget.h new file mode 100644 index 0000000000..c025ed688d --- /dev/null +++ b/Widgets/RootWidget.h @@ -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; +}; diff --git a/Widgets/Widget.cpp b/Widgets/Widget.cpp new file mode 100644 index 0000000000..3a1ec8e0f8 --- /dev/null +++ b/Widgets/Widget.cpp @@ -0,0 +1,76 @@ +#include "Widget.h" +#include "Event.h" +#include "EventLoop.h" +#include + +Widget::Widget(Widget* parent) + : Object(parent) +{ +} + +Widget::~Widget() +{ +} + +void Widget::event(Event& event) +{ + switch (event.type()) { + case Event::Paint: + return onPaint(static_cast(event)); + case Event::Show: + return onShow(static_cast(event)); + case Event::Hide: + return onHide(static_cast(event)); + case Event::KeyDown: + return onKeyDown(static_cast(event)); + case Event::KeyUp: + return onKeyUp(static_cast(event)); + case Event::MouseMove: + return onMouseMove(static_cast(event)); + case Event::MouseDown: + return onMouseDown(static_cast(event)); + case Event::MouseUp: + return onMouseUp(static_cast(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()); +} + diff --git a/Widgets/Widget.h b/Widgets/Widget.h new file mode 100644 index 0000000000..be6966300d --- /dev/null +++ b/Widgets/Widget.h @@ -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 }; +}; diff --git a/Widgets/test.cpp b/Widgets/test.cpp new file mode 100644 index 0000000000..b42c7a8275 --- /dev/null +++ b/Widgets/test.cpp @@ -0,0 +1,17 @@ +#include "FrameBufferSDL.h" +#include "EventLoopSDL.h" +#include "RootWidget.h" +#include + +int main(int c, char** v) +{ + FrameBufferSDL fb(800, 600); + fb.show(); + + EventLoopSDL loop; + + RootWidget w; + fb.setRootWidget(&w); + + return loop.exec(); +}