Foundation classes for a basic GUI implementing simple widgets and events

Dependents:   TouchScreenGUIDemo

Files at this revision

API Documentation at this revision

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

Core/GUI.cpp Show annotated file Show diff for this revision Revisions of this file
Core/GUI.h Show annotated file Show diff for this revision Revisions of this file
Core/LinkedList.h Show annotated file Show diff for this revision Revisions of this file
Events/EventDispatcher.cpp Show annotated file Show diff for this revision Revisions of this file
Events/EventDispatcher.h Show annotated file Show diff for this revision Revisions of this file
Events/EventListener.cpp Show diff for this revision Revisions of this file
Events/EventListener.h Show annotated file Show diff for this revision Revisions of this file
Events/EventSource.h Show annotated file Show diff for this revision Revisions of this file
Events/EventType.h Show annotated file Show diff for this revision Revisions of this file
Widgets/TextWidget.cpp Show annotated file Show diff for this revision Revisions of this file
Widgets/Widget.cpp Show annotated file Show diff for this revision Revisions of this file
Widgets/Widget.h Show annotated file Show diff for this revision Revisions of this file
Widgets/Window.cpp Show annotated file Show diff for this revision Revisions of this file
Widgets/Window.h Show annotated file Show diff for this revision Revisions of this file
--- 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);
 };