Foundation classes for a basic GUI implementing simple widgets and events

Dependents:   TouchScreenGUIDemo

Widgets/Widget.cpp

Committer:
duncanFrance
Date:
2016-05-28
Revision:
18:d849f3ada858
Parent:
17:5184762fda6c

File content as of revision 18:d849f3ada858:

#include "Widget.h"
#include <algorithm>
Widget::Widget(GraphicsContext* context) 
:   _context(context), 
    _fg(White), _bg(Black), 
    _offset(0,0), _inner(0,0,0,0), _outer(0,0,0,0),
    _hidden(false), 
    _padding(0), _borderWidth(0), _borderColour(Black),
    _adjusting(false), 
    _dirtied(true), _dirtying(false), 
    _damaged(true), _damaging(false),
    _handledEvents(0)
{
}

void Widget::setParent(Widget* parent) {
    _parent = parent;
}

Widget *Widget::getParent() {
    return _parent;
}

// Location is referred to the offset
void Widget::setLocation(int x, int y)
{
    if( (_outer.x != (x+_offset.x())) || (_outer.y != y+_offset.y()) ) {
        _outer.x = x + _offset.x();
        _outer.y = y + _offset.y();
        damage();
        adjust();
    }
}

void Widget::setSize(int width, int height)
{
    if(_outer.width != width || _outer.height != height) {
        _outer.width = width;
        _outer.height = height;
        damage();
        adjust();
    }
}

void Widget::setWidth(int width) {
    if(_outer.width != width) {
        _outer.width = width;
        damage();
        adjust();
    }
}

void Widget::setHeight(int height) {
    if(_outer.height != height) {
        _outer.height = height;
        damage();
        adjust();
    }
}

int Widget::x()
{
    return _outer.x;
}

int Widget::y()
{
    return _outer.y;
}

int Widget::height()
{
    return _outer.height;
}

int Widget::width()
{
    return _outer.width;
}
/**
const Rectangle &Widget::outer() {
    return _outer;
}

const Rectangle &Widget::inner() {
    return _inner;
}
**/
    void Widget::setOffset(int x, int y) {
        _outer.x = _outer.x - _offset.x() + x;
        _outer.y = _outer.y - _offset.y() + y;
        _offset.x(x);
        _offset.y(y);
        damage();
        adjust();
    }
    
    int Widget::offsetX() {
        return _offset.x();
    }
    
    int Widget::offsetY() {
        return _offset.y();
    }


void Widget::setForeground(uint16_t color)
{
    if(_fg != color) {
        _fg = color;
        dirty();
    }
}

void Widget::setBackground(uint16_t color)
{
    if(_bg != color) {
        _bg = color;
        dirty();
    }
}


/**
* Set the amount of padding between the border and a widget edge
**/
void Widget::setPadding(int pixels)
{
    if(_padding != pixels) {
        _padding = pixels;
        dirty();
    }
}

void Widget::setBorder(int width, uint16_t colour)
{
    if(_borderWidth != width || _borderColour != colour) {
        _borderColour = colour;
        _borderWidth = width;
        dirty();
    }
}

void Widget::draw()
{
    if(!_hidden && (_dirtied | _damaged)) {
        _draw();
        _dirtied = false;
        _damaged = false;
    }
}

void Widget::clear()
{
    if(!_hidden) _clear();
}

void Widget::show()
{
    _hidden = false;
    dirty();
    damage();
}

void Widget::hide()
{
    _clear();
    _hidden = true;
}

bool Widget::isHidden()
{
    return _hidden;
}

void Widget::setEventHandler(EventHandler* handler)
{
    _handlers.appendOnce(handler);
    _reenumerateEvents();
}

void Widget::unsetEventHandler(EventHandler *handler)
{
    _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);
}

void Widget::dirty()
{
    if(!_dirtying) {
        _dirtying = true;
        _dirty();
        _dirtying = false;
    }
}

bool Widget::isDirty() {
    return _dirtied;
}

void Widget::dirtyAll()
{
    _dirtied = true;
}

void Widget::damage() {
    if(!_damaging) {
        _damaging = true;
        _damage();
        _damaging = false;
    }
}

bool Widget::isDamaged() {
    return _damaged;
}

void Widget::adjust()
{
    if(!_adjusting) {
        _adjusting = true;
        _adjust();
        _adjusting = false;
    }
}

GraphicsDisplay* Widget::display()
{
    return _context->display();
}

/**********************************************************
* Protected methods
**********************************************************/
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);
    // Bottom
    d->fillrect(_outer.x, _outer.y + _outer.height - _borderWidth, _outer.x + _outer.width, _outer.y+_outer.height, _borderColour);
    // Left
    d->fillrect(_outer.x, _outer.y, _outer.x+_borderWidth, _outer.y+_outer.height, _borderColour);
    // Right
    d->fillrect(_outer.x+_outer.width-_borderWidth, _outer.y, _outer.x+_outer.width, _outer.y+_outer.height, _borderColour);
}

void Widget::_clear() {
    display()->fillrect(_outer.x, _outer.y, _outer.x+_outer.width, _outer.y+_outer.height, display()->getBackground());
}

void Widget::_dirty()
{
    _dirtied = true;
    if(_parent != NULL) {
        _parent->dirty();
    }
}

void Widget::_damage()
{
    _damaged = true;
    if(_parent != NULL) {
        _parent->dirty();
    }
}

void Widget::_adjust() {
    // Recalculate size of inner Rect
    _inner.resize(_outer, _borderWidth + _padding);
}