Foundation classes for a basic GUI implementing simple widgets and events
Dependents: TouchScreenGUIDemo
Revision 18:d849f3ada858, committed 2016-05-28
- Comitter:
- duncanFrance
- Date:
- Sat May 28 14:50:14 2016 +0000
- Parent:
- 17:5184762fda6c
- Commit message:
- Moved the event queue into the EventDispatcher; Improved event handling across Window/Widget
Changed in this revision
--- a/Core/GUI.cpp Sun May 22 16:35:23 2016 +0000 +++ b/Core/GUI.cpp Sat May 28 14:50:14 2016 +0000 @@ -1,7 +1,9 @@ #include "GUI.h" GUI::GUI(GraphicsContext* context) : _context(context), _rootWindow(context) -{} +{ + _context->eventDispatcher()->attachListener(&_rootWindow); +} GraphicsContext* GUI::getGraphicsContext() { return _context; @@ -11,36 +13,8 @@ return &_rootWindow; } -void GUI::queueEvent(const Event e) -{ - Event* qe = _mailbox.alloc(); - qe->type = e.type; - qe->target = e.target; - qe->screenX = e.screenX; - qe->screenY = e.screenY; - _mailbox.put(qe); +void GUI::run() { + _context->eventDispatcher()->pumpEvents(); + _rootWindow.draw(); + } - -void GUI::pumpEvents() -{ - - osEvent oe = _mailbox.get(1); - - if(oe.status == osEventMail) { - - Event* qe = (Event*)oe.value.p; - Event e; - e.target = qe->target; - e.type = qe->type; - e.screenX = qe->screenX; - e.screenY = qe->screenY; - - _context->eventDispatcher()->dispatchEvent(e); - _mailbox.free(qe); - } -} - -void GUI::updateWindow() -{ - _rootWindow.draw(); -} \ No newline at end of file
--- a/Core/GUI.h Sun May 22 16:35:23 2016 +0000 +++ b/Core/GUI.h Sat May 28 14:50:14 2016 +0000 @@ -3,8 +3,6 @@ class GUI; -#include "rtos.h" - #include "GraphicsContext.h" #include "Window.h" @@ -21,14 +19,12 @@ GraphicsContext* getGraphicsContext(); Window *rootWindow(); - void queueEvent(const Event e); - void pumpEvents(); - void updateWindow(); + void run(); private: GraphicsContext* _context; - Mail<Event, 64> _mailbox; Window _rootWindow; + }; #endif \ No newline at end of file
--- a/Core/LinkedList.h Sun May 22 16:35:23 2016 +0000 +++ b/Core/LinkedList.h Sat May 28 14:50:14 2016 +0000 @@ -17,6 +17,30 @@ }; template<class T> +class LinkedListIterator { + public: + LinkedListIterator(LinkedListNode<T> *first) { + _current = first; + } + + ~LinkedListIterator() {} + + T* next() { + + LinkedListNode<T>* p = _current; + if(p != NULL) { + _current = _current->next; + return p->data; + } + + return NULL; + } + +private: + LinkedListNode<T>* _current; +}; + +template<class T> class LinkedList { @@ -24,7 +48,11 @@ LinkedList() : _first(NULL), _next(NULL), _current(NULL), _size(0) {} ~LinkedList() {} - + + LinkedListIterator<T> getIterator() { + LinkedListIterator<T> iterator(_first); + return iterator; + } void append(T* data) {
--- a/Events/EventDispatcher.cpp Sun May 22 16:35:23 2016 +0000 +++ b/Events/EventDispatcher.cpp Sat May 28 14:50:14 2016 +0000 @@ -15,8 +15,35 @@ EventListener* l; _listeners.reset(); while((l=_listeners.next()) != NULL) { - if(l->isEventTarget(e)) { - l->handleEvent(e); - } + l->handleEvent(e); } -} \ No newline at end of file +} + +void EventDispatcher::queueEvent(const Event e) +{ + Event* qe = _mailbox.alloc(); + qe->type = e.type; + qe->target = e.target; + qe->screenX = e.screenX; + qe->screenY = e.screenY; + _mailbox.put(qe); +} + +void EventDispatcher::pumpEvents() +{ + + osEvent oe = _mailbox.get(1); + + if(oe.status == osEventMail) { + + Event* qe = (Event*)oe.value.p; + Event e; + e.target = qe->target; + e.type = qe->type; + e.screenX = qe->screenX; + e.screenY = qe->screenY; + + dispatchEvent(e); + _mailbox.free(qe); + } +}
--- a/Events/EventDispatcher.h Sun May 22 16:35:23 2016 +0000 +++ b/Events/EventDispatcher.h Sat May 28 14:50:14 2016 +0000 @@ -1,6 +1,8 @@ #ifndef SIMPLEGUI_EVENT_DISPATCHER_H #define SIMPLEGUI_EVENT_DISPATCHER_H +#include "rtos.h" + #include "EventListener.h" #include "LinkedList.h" @@ -15,10 +17,21 @@ void attachListener(EventListener* l); void detachListener(EventListener* l); void dispatchEvent(Event e); + + /** + * Normally called from a separate thread to queue an event for later processing by the main thread + **/ + void queueEvent(const Event e); + + /** + * Should be called on the main thread + **/ + void pumpEvents(); private: LinkedList<EventListener> _listeners; + Mail<Event, 64> _mailbox; };
--- a/Events/EventListener.cpp Sun May 22 16:35:23 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -#include "EventListener.h" - -EventListener::EventListener() { -} - -bool EventListener::isEventTarget(Event e) { - return false; -} - -void EventListener::setEventHandler(EventHandler* handler) { - _handlers.appendOnce(handler); -} - -void EventListener::unsetEventHandler(EventHandler* handler) { - - _handlers.remove(handler); -} - -void EventListener::handleEvent(Event e) { - - EventHandler* h; - _handlers.reset(); - - while((h = _handlers.next()) != NULL) { - if(h->type == e.type) { - e.target = this; - h->handle(e); - } - } -} \ No newline at end of file
--- a/Events/EventListener.h Sun May 22 16:35:23 2016 +0000 +++ b/Events/EventListener.h Sat May 28 14:50:14 2016 +0000 @@ -5,23 +5,16 @@ class EventListener; #include "EventHandler.h" -#include "LinkedList.h" class EventListener { public: - EventListener(); + virtual void handleEvent(Event e) =0; + virtual void setEventHandler(EventHandler *handler) =0; + virtual void unsetEventHandler(EventHandler* handler) =0; - virtual bool isEventTarget(Event e); - void handleEvent(Event e); - void setEventHandler(EventHandler *handler); - void unsetEventHandler(EventHandler* handler); - -protected: - - LinkedList<EventHandler> _handlers; }; #endif \ No newline at end of file
--- a/Events/EventSource.h Sun May 22 16:35:23 2016 +0000 +++ b/Events/EventSource.h Sat May 28 14:50:14 2016 +0000 @@ -1,7 +1,7 @@ #ifndef SIMPLEGUI_EVENT_SOURCE_H #define SIMPLEGUI_EVENT_SOURCE_H -#include "GUI.h" +#include "EventDispatcher.h" /** * Interface defining a class which sources events @@ -10,11 +10,11 @@ public: - EventSource(GUI* gui) : _gui(gui) {} + EventSource(EventDispatcher* dispatcher) : _eventDispatcher(dispatcher) {} protected: - GUI* _gui; + EventDispatcher* _eventDispatcher; };
--- a/Events/EventType.h Sun May 22 16:35:23 2016 +0000 +++ b/Events/EventType.h Sat May 28 14:50:14 2016 +0000 @@ -5,18 +5,18 @@ enum EventType { - TOUCH_START, - TOUCH_END, - TOUCH_MOVE, - TOUCH_TAP, - TOUCH_DOUBLE_TAP, + TOUCH_START = 1 << 0, + TOUCH_END = 1 << 1, + TOUCH_MOVE = 1 << 2, + TOUCH_TAP = 1 << 3, + TOUCH_DOUBLE_TAP = 1 << 4, - MOUSE_DOWN, - MOUSE_UP, - MOUSE_MOVE, - MOUSE_DRAG, - MOUSE_CLICK, - MOUSE_DOUBLE_CLICK + MOUSE_DOWN = 1 << 5, + MOUSE_UP = 1 << 6, + MOUSE_MOVE = 1 << 7, + MOUSE_DRAG = 1 << 8, + MOUSE_CLICK = 1 << 9, + MOUSE_DOUBLE_CLICK = 1 << 10 }; #endif \ No newline at end of file
--- a/Widgets/TextWidget.cpp Sun May 22 16:35:23 2016 +0000 +++ b/Widgets/TextWidget.cpp Sat May 28 14:50:14 2016 +0000 @@ -87,19 +87,24 @@ * VALIGN=MIDDLE : (0, inner.height/2 - numLines * font.height/2) * VALIGN=BOTTOM : (0, inner.height - numLines * font.height) **/ - int t=0; + int offset=0; switch(_valign) { - case TOP: t = 0; break; - case MIDDLE: t = (_inner.height - (numLines * _font->zoomedHeight()))/2; break; - case BOTTOM: t = (_inner.height - (numLines * _font->zoomedHeight())); break; + case TOP: offset = 0; break; + case MIDDLE: offset = (_inner.height - (numLines * _font->zoomedHeight()))/2; break; + case BOTTOM: offset = (_inner.height - (numLines * _font->zoomedHeight())); break; + } + + _renderer->setForeground(_fg); + _renderer->setBackground(_bg); + + // Renderer window is only high enough for the number of lines to draw. + int h = _font->zoomedHeight() * numLines; + // Clip to fit within the TextWidget inner + if((h + offset) > _inner.height) { + h = _inner.height - offset; } - _renderer->window(_inner.x, _inner.y + t, _inner.width, _font->zoomedHeight(), false); - _renderer->setForeground(_fg); - _renderer->setBackground(_bg); - display()->fillrect(_inner.x, _inner.y, _inner.x+_inner.width, _inner.y+_inner.height, _bg); - display()->rect(_inner.x, _inner.y, _inner.x+_inner.width, _inner.y+_inner.height, Red); - + _renderer->window(_inner.x, _inner.y + offset, _inner.width, h, false); _renderer->puts(_text, display(), _font); display()->copy_to_lcd(); } \ No newline at end of file
--- a/Widgets/Widget.cpp Sun May 22 16:35:23 2016 +0000 +++ b/Widgets/Widget.cpp Sat May 28 14:50:14 2016 +0000 @@ -8,31 +8,19 @@ _padding(0), _borderWidth(0), _borderColour(Black), _adjusting(false), _dirtied(true), _dirtying(false), - _damaged(true), _damaging(false) + _damaged(true), _damaging(false), + _handledEvents(0) { } void Widget::setParent(Widget* parent) { _parent = parent; - if(_parent != NULL && _handlers.size() != 0) { - _context->eventDispatcher()->attachListener(this); - } else if(_parent == NULL && _handlers.size() == 0) { - _context->eventDispatcher()->detachListener(this); - } } Widget *Widget::getParent() { return _parent; } -bool Widget::isEventTarget(Event e) -{ - return !_hidden - && e.screenX >= _outer.x - && e.screenX <= (_outer.x+_outer.width) - && e.screenY >= _outer.y - && e.screenY <= (_outer.y+_outer.height); -} // Location is referred to the offset void Widget::setLocation(int x, int y) { @@ -187,18 +175,58 @@ void Widget::setEventHandler(EventHandler* handler) { - EventListener::setEventHandler(handler); - _context->eventDispatcher()->attachListener(this); + _handlers.appendOnce(handler); + _reenumerateEvents(); } void Widget::unsetEventHandler(EventHandler *handler) { - EventListener::unsetEventHandler(handler); - if(_handlers.size() == 0) { - _context->eventDispatcher()->detachListener(this); - } + _handlers.remove(handler); + _reenumerateEvents(); +} + +bool Widget::_isEventTarget(Event e) +{ + return !_hidden + && e.screenX >= _outer.x + && e.screenX <= (_outer.x+_outer.width) + && e.screenY >= _outer.y + && e.screenY <= (_outer.y+_outer.height); } +void Widget::handleEvent(Event e) { + e.target = this; + if(_isEventTarget(e)) { + _handlers.reset(); + EventHandler *h; + while((h = _handlers.next()) != NULL) { + if(h->type == e.type) { + h->handle(e); + } + } + } +} + +void Widget::_reenumerateEvents() { + _reenumerateHandledEvents(); + if(_parent != NULL) { + _parent->_reenumerateEvents(); + } +} + +void Widget::_reenumerateHandledEvents() { + _handledEvents = 0; + EventHandler *h; + LinkedListIterator<EventHandler> eit = _handlers.getIterator(); + while((h = eit.next()) != NULL) { + _handledEvents |= h->type; + } +} + +uint16_t Widget::_getHandledEvents() { + return _handledEvents; +} + bool Widget::intersects(Widget *w) { return _outer.intersects(w->_outer); @@ -253,6 +281,7 @@ **********************************************************/ void Widget::_draw() { GraphicsDisplay *d = display(); + //_clear(); // Draw the border // Top d->fillrect(_outer.x, _outer.y, _outer.x+_outer.width, _outer.y+_borderWidth, _borderColour);
--- a/Widgets/Widget.h Sun May 22 16:35:23 2016 +0000 +++ b/Widgets/Widget.h Sat May 28 14:50:14 2016 +0000 @@ -32,9 +32,7 @@ /******************************************************** * Common Widget methods - ********************************************************/ - virtual bool isEventTarget(Event e); - + ********************************************************/ virtual void setLocation(int x, int y); virtual void setSize(int width, int height); virtual void setWidth(int width); @@ -64,8 +62,12 @@ void hide(); bool isHidden(); - void setEventHandler(EventHandler *handler); - void unsetEventHandler(EventHandler *handler); + /** + * Implementation of EventListener + **/ + virtual void handleEvent(Event e); + virtual void setEventHandler(EventHandler *handler); + virtual void unsetEventHandler(EventHandler *handler); bool intersects(Widget *widget); @@ -91,6 +93,9 @@ * Convenience method **/ GraphicsDisplay *display(); + virtual void _reenumerateEvents(); + virtual void _reenumerateHandledEvents(); + virtual uint16_t _getHandledEvents(); protected: @@ -101,6 +106,12 @@ virtual void _damage(); virtual void _adjust(); + /** + * Methods to help with event handling + **/ + bool _isEventTarget(Event e); + + uint16_t _handledEvents; Widget* _parent; GraphicsContext* _context; @@ -122,6 +133,8 @@ bool _damaged; bool _damaging; + + LinkedList<EventHandler> _handlers; }; #endif \ No newline at end of file
--- a/Widgets/Window.cpp Sun May 22 16:35:23 2016 +0000 +++ b/Widgets/Window.cpp Sat May 28 14:50:14 2016 +0000 @@ -1,6 +1,6 @@ #include "Window.h" -Window::Window(GraphicsContext* context) : Widget(context) +Window::Window(GraphicsContext* context) : Widget(context), _childHandledEvents(0) { setSize(context->display()->width(), context->display()->height()); } @@ -9,6 +9,7 @@ { _widgets.append(widget); widget->setParent(this); + _reenumerateEvents(); widget->show(); dirtyAll(); damage(); @@ -20,6 +21,7 @@ widget->hide(); _widgets.remove(widget); widget->setParent(NULL); + _reenumerateEvents(); dirty(); } @@ -74,11 +76,41 @@ void Window::_dirtyIntersected(Widget *w) { Widget *o; - _widgets.reset(); - while((o = _widgets.next()) != NULL) { + LinkedListIterator<Widget> it = _widgets.getIterator(); + while((o = it.next()) != NULL) { if((o != w) && o->intersects(w)) { o->dirtyAll(); } } } +void Window::handleEvent(Event e) { + Widget::handleEvent(e); + // At this point we can do bubbling, cancelling etc. One day + if(_childHandledEvents & e.type) { + LinkedListIterator<Widget> it = _widgets.getIterator(); + Widget *w; + while((w = it.next()) != NULL) { + w->handleEvent(e); + } + } +} + +void Window::_reenumerateHandledEvents() { + + Widget::_reenumerateHandledEvents(); + + _childHandledEvents = 0; + Widget *w; + LinkedListIterator<Widget> wit = _widgets.getIterator(); + while((w = wit.next()) != NULL) { + _childHandledEvents |= w->_getHandledEvents(); + w = _widgets.next(); + } +} + +uint16_t Window::_getHandledEvents() { + uint16_t m = Widget::_getHandledEvents(); + m |= _childHandledEvents; + return m; +}
--- a/Widgets/Window.h Sun May 22 16:35:23 2016 +0000 +++ b/Widgets/Window.h Sat May 28 14:50:14 2016 +0000 @@ -24,6 +24,10 @@ virtual void detach(Widget *widget); virtual void dirtyAll(); + virtual void handleEvent(Event e); + virtual void _reenumerateHandledEvents(); + virtual uint16_t _getHandledEvents(); + protected: /** @@ -32,6 +36,7 @@ virtual void _draw(); LinkedList<Widget> _widgets; + uint16_t _childHandledEvents; void _dirtyIntersected(Widget *w); };