Bläddra i källkod

Merge pull request #170 from blackberry-gaming/next-ablake

Next ablake: More UI commits.
Sean Paul Taylor 14 år sedan
förälder
incheckning
4f3287021f

+ 25 - 33
gameplay/src/Button.cpp

@@ -17,22 +17,7 @@ namespace gameplay
     Button* Button::create(Theme::Style* style, Properties* properties)
     {
         Button* button = new Button();
-        button->_style = style;
-        properties->getVector2("position", &button->_position);
-        properties->getVector2("size", &button->_size);
-
-        const char* id = properties->getId();
-        if (id)
-        {
-            button->_id = id;
-        }
-
-        const char* text = properties->getString("text");
-        if (text)
-        {
-            button->_text = text;
-        }
-
+        button->init(style, properties);
         __buttons.push_back(button);
 
         return button;
@@ -65,28 +50,35 @@ namespace gameplay
         return NULL;
     }
 
-    void Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+    bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
     {
-        if (_state != STATE_DISABLED)
+        if (!isEnabled())
         {
-            switch (evt)
-            {
-            case Touch::TOUCH_PRESS:
-                // TODO: button-down callback.
+            return false;
+        }
 
-                _state = Control::STATE_ACTIVE;
-                break;
-            case Touch::TOUCH_RELEASE:
-                if (_callback &&
-                    x > 0 && x <= _size.x &&
-                    y > 0 && y <= _size.y)
-                {
-                    // Button-clicked callback.
-                    _callback->trigger(this);
-                }
+        switch (evt)
+        {
+        case Touch::TOUCH_PRESS:
+            _state = Control::STATE_ACTIVE;
+            _dirty = true;
+            return _consumeTouchEvents;
+        case Touch::TOUCH_RELEASE:
+            if (_callback &&
+                x > 0 && x <= _size.x &&
+                y > 0 && y <= _size.y)
+            {
+                // Button-clicked callback.
+                _callback->trigger(this);
                 setState(Control::STATE_NORMAL);
-                break;
+                _dirty = true;
+                return _consumeTouchEvents;
             }
+            _dirty = true;
+            setState(Control::STATE_NORMAL);
+            break;
         }
+
+        return _consumeTouchEvents;
     }
 }

+ 11 - 11
gameplay/src/Button.h

@@ -14,19 +14,19 @@ class Button : public Label
     class Callback;
 
 public:
-    Button();
-    virtual ~Button();
-
     static Button* create(Theme::Style* style, Properties* properties);
     static Button* create(const char* id, unsigned int x, unsigned int y, unsigned int width, unsigned int height);
     static Button* getButton(const char* id);
 
     template <class ClassType>
-    void setCallback(ClassType* instance, void (ClassType::*callbackMethod)(Button*));
+    void setCallback(ClassType* instance, void (ClassType::*callbackMethod)(Control*));
 
-    void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
 protected:
+    Button();
+    virtual ~Button();
+
     Callback* _callback;
 
 private:
@@ -36,16 +36,16 @@ private:
     {
     public:
         virtual ~Callback() { }
-        virtual void trigger(Button* button) = 0;
+        virtual void trigger(Control* button) = 0;
     };
 
     template <class ClassType>
     class CallbackImpl : public Callback
     {
-        typedef void (ClassType::*CallbackMethod)(Button*);
+        typedef void (ClassType::*CallbackMethod)(Control*);
     public:
         CallbackImpl(ClassType* instance, CallbackMethod method);
-        void trigger(Button* button);
+        void trigger(Control* control);
     private:
         ClassType* _instance;
         CallbackMethod _method;
@@ -59,15 +59,15 @@ Button::CallbackImpl<ClassType>::CallbackImpl(ClassType* instance, CallbackMetho
 }
 
 template <class ClassType>
-void Button::setCallback(ClassType* instance, void (ClassType::*callbackMethod)(Button*))
+void Button::setCallback(ClassType* instance, void (ClassType::*callbackMethod)(Control*))
 {
     _callback = new CallbackImpl<ClassType>(instance, callbackMethod);
 }
 
 template <class ClassType>
-void Button::CallbackImpl<ClassType>::trigger(Button* button)
+void Button::CallbackImpl<ClassType>::trigger(Control* control)
 {
-    (_instance->*_method)(button);
+    (_instance->*_method)(control);
 }
 
 }

+ 56 - 49
gameplay/src/CheckBox.cpp

@@ -22,27 +22,13 @@ CheckBox::~CheckBox()
 
 CheckBox* CheckBox::create(Theme::Style* style, Properties* properties)
 {
-    CheckBox* checkbox = new CheckBox();
-    checkbox->_style = style;
-    properties->getVector2("position", &checkbox->_position);
-    properties->getVector2("size", &checkbox->_size);
-    checkbox->_checked = properties->getBool("checked");
-
-    const char* id = properties->getId();
-    if (id)
-    {
-        checkbox->_id = id;
-    }
-
-    const char* text = properties->getString("text");
-    if (text)
-    {
-        checkbox->_text = text;
-    }
-
-    __checkBoxes.push_back(checkbox);
+    CheckBox* checkBox = new CheckBox();
+    checkBox->init(style, properties);
+    properties->getVector2("iconSize", &checkBox->_iconSize);
+    checkBox->_checked = properties->getBool("checked");
+    __checkBoxes.push_back(checkBox);
 
-    return checkbox;
+    return checkBox;
 }
 
 CheckBox* CheckBox::getCheckBox(const char* id)
@@ -50,18 +36,28 @@ CheckBox* CheckBox::getCheckBox(const char* id)
     std::vector<CheckBox*>::const_iterator it;
     for (it = __checkBoxes.begin(); it < __checkBoxes.end(); it++)
     {
-        CheckBox* checkbox = *it;
-        if (strcmp(id, checkbox->getID()) == 0)
+        CheckBox* checkBox = *it;
+        if (strcmp(id, checkBox->getID()) == 0)
         {
-            return checkbox;
+            return checkBox;
         }
     }
 
     return NULL;
 }
 
-void CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+bool CheckBox::isChecked()
 {
+    return _checked;
+}
+
+bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    if (!isEnabled())
+    {
+        return false;
+    }
+
     switch (evt)
     {
     case Touch::TOUCH_RELEASE:
@@ -78,7 +74,35 @@ void CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
         break;
     }
 
-    Button::touchEvent(evt, x, y, contactIndex);
+    return Button::touchEvent(evt, x, y, contactIndex);
+}
+
+void CheckBox::update(const Vector2& position)
+{
+    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+    Theme::Icon* icon = overlay->getCheckBoxIcon();
+    Theme::Border border;
+    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+    if (containerRegion)
+    {
+        border = overlay->getContainerRegion()->getBorder();
+    }
+    Theme::Padding padding = _style->getPadding();
+
+    // Set up the text viewport.
+    float iconWidth = 0.0f;
+    if (icon)
+    {
+        iconWidth = icon->getSize().x;
+    }
+
+    Font* font = overlay->getFont();
+    Vector2 pos(position.x + _position.x + border.left + padding.left + iconWidth,
+            position.y + _position.y + border.top + padding.top);
+
+    _viewport.set(pos.x, pos.y,
+        _size.x - border.left - padding.left - border.right - padding.right - iconWidth,
+        _size.y - border.top - padding.top - border.bottom - padding.bottom - overlay->getFontSize());
 }
 
 void CheckBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
@@ -97,7 +121,12 @@ void CheckBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
         }
         const Theme::Padding padding = _style->getPadding();
 
-        const Vector2 size = icon->getSize();
+        Vector2& size = _iconSize;
+        if (_iconSize.isZero())
+        {
+            size = icon->getSize();
+        }
+
         const Vector4 color = icon->getColor();
 
         Vector2 pos(position.x + _position.x + border.left + padding.left,
@@ -120,33 +149,11 @@ void CheckBox::drawText(const Vector2& position)
 {
     // TODO: Batch all labels that use the same font.
     Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    Theme::Border border;
-    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-    if (containerRegion)
-    {
-        border = overlay->getContainerRegion()->getBorder();
-    }
-    Theme::Padding padding = _style->getPadding();
-
-    // Set up the text viewport.
-    float iconWidth = 0.0f;
-    if (icon)
-    {
-        iconWidth = icon->getSize().x;
-    }
-
     Font* font = overlay->getFont();
-    Vector2 pos(position.x + _position.x + border.left + padding.left + iconWidth,
-            position.y + _position.y + border.top + padding.top);
-
-    Rectangle viewport(pos.x, pos.y,
-        _size.x - border.left - padding.left - border.right - padding.right - iconWidth,
-        _size.y - border.top - padding.top - border.bottom - padding.bottom - font->getSize());
     
     // Draw the text.
     font->begin();
-    font->drawText(_text.c_str(), viewport, overlay->getTextColor(), overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+    font->drawText(_text.c_str(), _viewport, overlay->getTextColor(), overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
     font->end();
 
     _dirty = false;

+ 9 - 5
gameplay/src/CheckBox.h

@@ -12,21 +12,25 @@ namespace gameplay
 class CheckBox : public Button
 {
 public:
-    CheckBox();
-    ~CheckBox();
-
     static CheckBox* create(Theme::Style* style, Properties* properties);
     static CheckBox* getCheckBox(const char* id);
 
-    void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+    bool isChecked();
+
+    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+    void update(const Vector2& position);
 
     void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
     void drawText(const Vector2& position);
 
-private:
+protected:
+    CheckBox();
     CheckBox(const CheckBox& copy);
+    ~CheckBox();
 
     bool _checked;
+    Vector2 _iconSize;
 };
 
 }

+ 21 - 23
gameplay/src/Container.cpp

@@ -33,7 +33,7 @@ namespace gameplay
         SAFE_RELEASE(_layout);
     }
 
-    Container* Container::create(const char* id, Layout::Type type)
+    Container* Container::create(Layout::Type type)
     {
         Layout* layout = NULL;
         switch(type)
@@ -49,7 +49,6 @@ namespace gameplay
         }
 
         Container* container = new Container();
-        container->_id = id;
         container->_layout = layout;
 
         __containers.push_back(container);
@@ -59,14 +58,9 @@ namespace gameplay
 
     Container* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
     {
-        const char* id = properties->getId();
         const char* layoutString = properties->getString("layout");
-        Container* container = Container::create(id, getLayoutType(layoutString));
-
-        container->_style = style;
-        properties->getVector2("position", &container->_position);
-        properties->getVector2("size", &container->_size);
-
+        Container* container = Container::create(getLayoutType(layoutString));
+        container->init(style, properties);
         container->addControls(theme, properties);
 
         return container;
@@ -226,8 +220,6 @@ namespace gameplay
         {
             _layout->update(this);
         }
-
-        _dirty = false;
     }
 
     void Container::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
@@ -277,10 +269,14 @@ namespace gameplay
         return false;
     }
 
-    void Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+    bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
     {
-        if (getState() == Control::STATE_DISABLED)
-            return;
+        if (!isEnabled())
+        {
+            return false;
+        }
+
+        bool eventConsumed = false;
 
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
@@ -290,35 +286,32 @@ namespace gameplay
             const Vector2& position = control->getPosition();
             
             if (control->getState() != Control::STATE_NORMAL ||
-                (x >= position.x &&
+                (evt == Touch::TOUCH_PRESS &&
+                 x >= position.x &&
                  x <= position.x + size.x &&
                  y >= position.y &&
                  y <= position.y + size.y))
             {
                 // Pass on the event's position relative to the control.
-                control->touchEvent(evt, x - position.x, y - position.y, contactIndex);
+                eventConsumed |= control->touchEvent(evt, x - position.x, y - position.y, contactIndex);
             }
         }
 
-        if (getState() == Control::STATE_DISABLED)
-            return;
-
         switch (evt)
         {
         case Touch::TOUCH_PRESS:
-            setState(Control::STATE_ACTIVE);
+            setState(Control::STATE_FOCUS);
             break;
         case Touch::TOUCH_RELEASE:
             setState(Control::STATE_NORMAL);
             break;
         }
+
+        return (_consumeTouchEvents | eventConsumed);
     }
 
     void Container::keyEvent(Keyboard::KeyEvent evt, int key)
     {
-        if (getState() == Control::STATE_DISABLED)
-            return;
-
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
@@ -329,6 +322,11 @@ namespace gameplay
 
     Layout::Type Container::getLayoutType(const char* layoutString)
     {
+        if (!layoutString)
+        {
+            return Layout::LAYOUT_ABSOLUTE;
+        }
+
         std::string layoutName(layoutString);
         std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
         if (layoutName == "LAYOUT_ABSOLUTE")

+ 2 - 2
gameplay/src/Container.h

@@ -13,7 +13,7 @@ public:
     /**
      * A Container's layout type must be specified at creation time.
      */
-    static Container* create(const char* id, Layout::Type type);
+    static Container* create(Layout::Type type);
     static Container* create(Theme::Style* style, Properties* properties, Theme* theme);
     static Container* getContainer(const char* id);
 
@@ -69,7 +69,7 @@ public:
     virtual void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
     virtual void drawText(const Vector2& position);
 
-    virtual void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+    virtual bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
     virtual void keyEvent(Keyboard::KeyEvent evt, int key);
 

+ 90 - 22
gameplay/src/Control.cpp

@@ -5,7 +5,7 @@ namespace gameplay
 {
     Control::Control()
         : _id(""), _state(Control::STATE_NORMAL), _size(Vector2::zero()), _position(Vector2::zero()), _border(Vector2::zero()), _padding(Vector2::zero()),
-          _autoWidth(true), _autoHeight(true), _dirty(true)
+          _autoWidth(true), _autoHeight(true), _dirty(true), _consumeTouchEvents(true)
     {
     }
 
@@ -17,6 +17,22 @@ namespace gameplay
     {
     }
 
+    void Control::init(Theme::Style* style, Properties* properties)
+    {
+        _style = style;
+
+        properties->getVector2("position", &_position);
+        properties->getVector2("size", &_size);
+
+        _state = Control::getStateFromString(properties->getString("state"));
+
+        const char* id = properties->getId();
+        if (id)
+        {
+            _id = id;
+        }
+    }
+
     const char* Control::getID()
     {
         return _id.c_str();
@@ -84,6 +100,11 @@ namespace gameplay
         _state = STATE_NORMAL;
     }
 
+    bool Control::isEnabled()
+    {
+        return _state != STATE_DISABLED;
+    }
+
     Theme::Style::OverlayType Control::getOverlayType() const
     {
         switch (_state)
@@ -94,14 +115,27 @@ namespace gameplay
             return Theme::Style::OVERLAY_FOCUS;
         case Control::STATE_ACTIVE:
             return Theme::Style::OVERLAY_ACTIVE;
+        case Control::STATE_DISABLED:
+            return Theme::Style::OVERLAY_DISABLED;
         default:
             return Theme::Style::OVERLAY_NORMAL;
         }
     }
 
-    void Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+    void Control::setConsumeTouchEvents(bool consume)
+    {
+        _consumeTouchEvents = consume;
+    }
+    
+    bool Control::getConsumeTouchEvents()
+    {
+        return _consumeTouchEvents;
+    }
+
+    bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
     {
         // Empty stub to be implemented by Button and its descendents.
+        return _consumeTouchEvents;
     }
 
     void Control::keyEvent(Keyboard::KeyEvent evt, int key)
@@ -145,26 +179,33 @@ namespace gameplay
             float bottomY = pos.y + _size.y - border.bottom;
 
             // Draw themed border sprites.
-            if (border.left && border.top)
-                spriteBatch->draw(pos.x, pos.y, border.left, border.top, topLeft.u1, topLeft.v1, topLeft.u2, topLeft.v2, borderColor);
-            if (border.top)
-                spriteBatch->draw(pos.x + border.left, pos.y, midWidth, border.top, top.u1, top.v1, top.u2, top.v2, borderColor);
-            if (border.right && border.top)
-                spriteBatch->draw(rightX, pos.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, borderColor);
-            if (border.left)
-                spriteBatch->draw(pos.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, borderColor);
-            
-            spriteBatch->draw(pos.x + border.left, pos.y + border.top, _size.x - border.left - border.right, _size.y - border.top - border.bottom,
-                center.u1, center.v1, center.u2, center.v2, borderColor);
-
-            if (border.right)
-                spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, borderColor);
-            if (border.bottom && border.left)
-                spriteBatch->draw(pos.x, bottomY, border.left, border.bottom, bottomLeft.u1, bottomLeft.v1, bottomLeft.u2, bottomLeft.v2, borderColor);
-            if (border.bottom)
-                spriteBatch->draw(midX, bottomY, midWidth, border.bottom, bottom.u1, bottom.v1, bottom.u2, bottom.v2, borderColor);
-            if (border.bottom && border.right)
-                spriteBatch->draw(rightX, bottomY, border.right, border.bottom, bottomRight.u1, bottomRight.v1, bottomRight.u2, bottomRight.v2, borderColor);
+            if (!border.left && !border.right && !border.top && !border.bottom)
+            {
+                // No border, just draw the image.
+                spriteBatch->draw(pos.x, pos.y, _size.x, _size.y, center.u1, center.v1, center.u2, center.v2, borderColor);
+            }
+            else
+            {
+                if (border.left && border.top)
+                    spriteBatch->draw(pos.x, pos.y, border.left, border.top, topLeft.u1, topLeft.v1, topLeft.u2, topLeft.v2, borderColor);
+                if (border.top)
+                    spriteBatch->draw(pos.x + border.left, pos.y, midWidth, border.top, top.u1, top.v1, top.u2, top.v2, borderColor);
+                if (border.right && border.top)
+                    spriteBatch->draw(rightX, pos.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, borderColor);
+                if (border.left)
+                    spriteBatch->draw(pos.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, borderColor);
+                if (border.left && border.right && border.top && border.bottom)
+                    spriteBatch->draw(pos.x + border.left, pos.y + border.top, _size.x - border.left - border.right, _size.y - border.top - border.bottom,
+                        center.u1, center.v1, center.u2, center.v2, borderColor);
+                if (border.right)
+                    spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, borderColor);
+                if (border.bottom && border.left)
+                    spriteBatch->draw(pos.x, bottomY, border.left, border.bottom, bottomLeft.u1, bottomLeft.v1, bottomLeft.u2, bottomLeft.v2, borderColor);
+                if (border.bottom)
+                    spriteBatch->draw(midX, bottomY, midWidth, border.bottom, bottom.u1, bottom.v1, bottom.u2, bottom.v2, borderColor);
+                if (border.bottom && border.right)
+                    spriteBatch->draw(rightX, bottomY, border.right, border.bottom, bottomRight.u1, bottomRight.v1, bottomRight.u2, bottomRight.v2, borderColor);
+            }
         }
     }
 
@@ -180,4 +221,31 @@ namespace gameplay
     {
         return _dirty;
     }
+
+    Control::State Control::getStateFromString(const char* state)
+    {
+        if (!state)
+        {
+            return STATE_NORMAL;
+        }
+
+        if (strcmp(state, "STATE_NORMAL") == 0)
+        {
+            return STATE_NORMAL;
+        }
+        else if (strcmp(state, "STATE_ACTIVE") == 0)
+        {
+            return STATE_ACTIVE;
+        }
+        else if (strcmp(state, "STATE_FOCUS") == 0)
+        {
+            return STATE_FOCUS;
+        }
+        else if (strcmp(state, "STATE_DISABLED") == 0)
+        {
+            return STATE_DISABLED;
+        }
+
+        return STATE_NORMAL;
+    }
 }

+ 10 - 2
gameplay/src/Control.h

@@ -82,13 +82,17 @@ public:
 
     void disable();
     void enable();
+    bool isEnabled();
 
     Theme::Style::OverlayType getOverlayType() const;
 
+    void setConsumeTouchEvents(bool consume);
+    bool getConsumeTouchEvents();
+
     /**
      * Defaults to empty stub.
      */
-    virtual void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+    virtual bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
     virtual void keyEvent(Keyboard::KeyEvent evt, int key);
 
@@ -110,13 +114,16 @@ public:
     void setStyle(Theme::Style* Style);
     Theme::Style* getStyle() const;
 
-    void themeChanged();
+    static State getStateFromString(const char* state);
 
 protected:
     Control();
     Control(const Control& copy);
     virtual ~Control();
 
+    // Set properties common to all Controls.
+    virtual void init(Theme::Style* style, Properties* properties);
+
     std::string _id;
     State _state;           // Determines overlay used during draw().
     Vector2 _size;
@@ -126,6 +133,7 @@ protected:
     bool _autoWidth;
     bool _autoHeight;
     bool _dirty;
+    bool _consumeTouchEvents;
     Theme::Style* _style;
 };
 

+ 5 - 0
gameplay/src/Font.cpp

@@ -1560,6 +1560,11 @@ SpriteBatch* Font::getSpriteBatch()
 
 Font::Justify Font::getJustifyFromString(const char* justify)
 {
+    if (!justify)
+    {
+        return Font::ALIGN_TOP_LEFT;
+    }
+
     if (strcmp(justify, "ALIGN_LEFT") == 0)
     {
         return Font::ALIGN_LEFT;

+ 92 - 76
gameplay/src/Form.cpp

@@ -59,28 +59,23 @@ namespace gameplay
         }
 
         // Create new form with given ID, theme and layout.
-        const char* id = formProperties->getId();
         const char* themeFile = formProperties->getString("theme");
         const char* layoutString = formProperties->getString("layout");
-        Form* form = Form::create(id, themeFile, getLayoutType(layoutString));
+        Form* form = Form::create(themeFile, getLayoutType(layoutString));
 
-        // Set form's position and dimensions.
-        formProperties->getVector2("position", &form->_position);
-        formProperties->getVector2("size", &form->_size);
-
-        // Set style from theme.
+        Theme* theme = form->getTheme();
         const char* styleName = formProperties->getString("style");
-        form->setStyle(form->getTheme()->getStyle(styleName));
+        form->init(theme->getStyle(styleName), formProperties);
 
         // Add all the controls to the form.
-        form->addControls(form->getTheme(), formProperties);
+        form->addControls(theme, formProperties);
 
         SAFE_DELETE(properties);
 
         return form;
     }
 
-    Form* Form::create(const char* id, const char* themeFile, Layout::Type type)
+    Form* Form::create(const char* themeFile, Layout::Type type)
     {
         Layout* layout;
         switch(type)
@@ -100,9 +95,7 @@ namespace gameplay
         assert(theme);
 
         Form* form = new Form();
-        form->_id = id;
         form->_layout = layout;
-        form->_frameBuffer = FrameBuffer::create(id);
         form->_theme = theme;
 
         __forms.push_back(form);
@@ -271,8 +264,14 @@ namespace gameplay
         // Bind the WorldViewProjection matrix
         material->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_MATRIX);
 
+        // Create a FrameBuffer if necessary.
+        if (!_frameBuffer)
+        {
+            _frameBuffer = FrameBuffer::create(_id.c_str());
+        }
+
         // Use the FrameBuffer to texture the quad.
-        if (_frameBuffer->getRenderTarget() == NULL)
+        if (!_frameBuffer->getRenderTarget())
         {
             RenderTarget* rt = RenderTarget::create(_id.c_str(), _size.x, _size.y);
             _frameBuffer->setRenderTarget(rt);
@@ -289,7 +288,7 @@ namespace gameplay
         SAFE_RELEASE(sampler);
     }
 
-    void Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+    bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
     {
         // Check for a collision with each Form in __forms.
         // Pass the event on.
@@ -298,74 +297,94 @@ namespace gameplay
         {
             Form* form = *it;
 
-            if (form->getState() == Control::STATE_DISABLED)
-                continue;
-
-            Node* node = form->_node;
-            if (node)
+            if (form->isEnabled())
             {
-                Scene* scene = node->getScene();
-                Camera* camera = scene->getActiveCamera();
-
-                if (camera)
+                Node* node = form->_node;
+                if (node)
                 {
-                    // Get info about the form's position.
-                    Matrix m = node->getMatrix();
-                    const Vector2& size = form->getSize();
-                    Vector3 min(0, 0, 0);
-                    m.transformPoint(&min);
-
-                    // Unproject point into world space.
-                    Ray ray;
-                    camera->pickRay(NULL, x, y, &ray);
-
-                    // Find the quad's plane.
-                    // We know its normal is the quad's forward vector.
-                    Vector3 normal = node->getForwardVectorWorld();
-
-                    // To get the plane's distance from the origin,
-                    // we'll find the distance from the plane defined
-                    // by the quad's forward vector and one of its points
-                    // to the plane defined by the same vector and the origin.
-                    const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
-                    const float d = -(a*min.x) - (b*min.y) - (c*min.z);
-                    const float distance = abs(d) /  sqrt(a*a + b*b + c*c);
-                    Plane plane(normal, -distance);
-
-                    // Check for collision with plane.
-                    float collisionDistance = ray.intersects(plane);
-                    if (collisionDistance != Ray::INTERSECTS_NONE)
-                    {
-                        // Multiply the ray's direction vector by collision distance
-                        // and add that to the ray's origin.
-                        Vector3 point = ray.getOrigin() + collisionDistance*ray.getDirection();
-
-                        // Project this point into the plane.
-                        m.invert();
-                        m.transformPoint(&point);
+                    Scene* scene = node->getScene();
+                    Camera* camera = scene->getActiveCamera();
 
-                        // Pass the touch event on.
-                        form->touchEvent(evt, point.x, size.y - point.y, contactIndex);
+                    if (camera)
+                    {
+                        // Get info about the form's position.
+                        Matrix m = node->getMatrix();
+                        const Vector2& size = form->getSize();
+                        Vector3 min(0, 0, 0);
+                        m.transformPoint(&min);
+
+                        // Unproject point into world space.
+                        Ray ray;
+                        camera->pickRay(NULL, x, y, &ray);
+
+                        // Find the quad's plane.
+                        // We know its normal is the quad's forward vector.
+                        Vector3 normal = node->getForwardVectorWorld();
+
+                        // To get the plane's distance from the origin,
+                        // we'll find the distance from the plane defined
+                        // by the quad's forward vector and one of its points
+                        // to the plane defined by the same vector and the origin.
+                        const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
+                        const float d = -(a*min.x) - (b*min.y) - (c*min.z);
+                        const float distance = abs(d) /  sqrt(a*a + b*b + c*c);
+                        Plane plane(normal, -distance);
+
+                        // Check for collision with plane.
+                        float collisionDistance = ray.intersects(plane);
+                        if (collisionDistance != Ray::INTERSECTS_NONE)
+                        {
+                            // Multiply the ray's direction vector by collision distance
+                            // and add that to the ray's origin.
+                            Vector3 point = ray.getOrigin() + collisionDistance*ray.getDirection();
+
+                            // Project this point into the plane.
+                            m.invert();
+                            m.transformPoint(&point);
+
+                            // Pass the touch event on.
+                            const Vector2& size = form->getSize();
+                            const Vector2& position = form->getPosition();
+
+                            if (form->getState() == Control::STATE_FOCUS ||
+                                (evt == Touch::TOUCH_PRESS &&
+                                 point.x >= position.x &&
+                                 point.x <= position.x + size.x &&
+                                 point.y >= position.y &&
+                                 point.y <= position.y + size.y))
+                            {
+                               if (form->touchEvent(evt, point.x, size.y - point.y, contactIndex))
+                               {
+                                   return true;
+                               }
+                            }
+                        }
                     }
                 }
-            }
-            else
-            {
-                // Simply compare with the form's bounds.
-                const Vector2& size = form->getSize();
-                const Vector2& position = form->getPosition();
-
-                if (form->getState() == Control::STATE_ACTIVE ||
-                    (x >= position.x &&
-                     x <= position.x + size.x &&
-                     y >= position.y &&
-                     y <= position.y + size.y))
+                else
                 {
-                    // Pass on the event's position relative to the form.
-                    form->touchEvent(evt, x - position.x, y - position.y, contactIndex);
+                    // Simply compare with the form's bounds.
+                    const Vector2& size = form->getSize();
+                    const Vector2& position = form->getPosition();
+
+                    if (form->getState() == Control::STATE_FOCUS ||
+                        (evt == Touch::TOUCH_PRESS &&
+                         x >= position.x &&
+                         x <= position.x + size.x &&
+                         y >= position.y &&
+                         y <= position.y + size.y))
+                    {
+                        // Pass on the event's position relative to the form.
+                        if (form->touchEvent(evt, x - position.x, y - position.y, contactIndex))
+                        {
+                            return true;
+                        }
+                    }
                 }
             }
         }
+
+        return false;
     }
 
     void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
@@ -374,9 +393,6 @@ namespace gameplay
         for (it = __forms.begin(); it < __forms.end(); it++)
         {
             Form* form = *it;
-            if (form->getState() == Control::STATE_DISABLED)
-                continue;
-
             form->keyEvent(evt, key);
         }
     }

+ 2 - 2
gameplay/src/Form.h

@@ -23,7 +23,7 @@ public:
      * Create from .form file.
      */
     static Form* create(const char* path);
-    static Form* create(const char* id, const char* textureFile, Layout::Type type);
+    static Form* create(const char* textureFile, Layout::Type type);
     static Form* getForm(const char* id);
 
     void setTheme(Theme* theme);
@@ -53,7 +53,7 @@ private:
     void draw(SpriteBatch* spriteBatch);
     void draw(SpriteBatch* spriteBatch, const Vector2& position);
 
-    static void touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+    static bool touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
     static void keyEventInternal(Keyboard::KeyEvent evt, int key);
 

+ 11 - 0
gameplay/src/Label.cpp

@@ -41,6 +41,17 @@ namespace gameplay
         return label;
     }
 
+    void Label::init(Theme::Style* style, Properties* properties)
+    {
+        Control::init(style, properties);
+
+        const char* text = properties->getString("text");
+        if (text)
+        {
+            _text = text;
+        }
+    }
+
     Label* Label::getLabel(const char* id)
     {
         std::vector<Label*>::const_iterator it;

+ 2 - 0
gameplay/src/Label.h

@@ -26,6 +26,8 @@ protected:
     Label(const Label& copy);
     virtual ~Label();
 
+    virtual void init(Theme::Style* style, Properties* properties);
+
     std::string _text;
     Rectangle _viewport;
 };

+ 14 - 23
gameplay/src/ParticleEmitter.cpp

@@ -2,6 +2,7 @@
 #include "ParticleEmitter.h"
 #include "Game.h"
 #include "Node.h"
+#include "Scene.h"
 #include "Quaternion.h"
 #include "Properties.h"
 
@@ -58,7 +59,7 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
 
     // Use default SpriteBatch material.
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);
-    texture->release(); // batch owns the texture
+    texture->release(); // batch owns the texture.
     assert(batch);
 
     ParticleEmitter* emitter = new ParticleEmitter(batch, particleCountMax);
@@ -890,30 +891,20 @@ void ParticleEmitter::draw()
         // Begin sprite batch drawing
         _spriteBatch->begin();
 
-        // Which draw call we use depends on whether particles are rotating.
-        if (_rotationPerParticleSpeedMin == 0.0f && _rotationPerParticleSpeedMax == 0.0f)
-        {
-            // No rotation.
-            for (unsigned int i = 0; i < _particleCount; i++)
-            {
-                Particle* p = &_particles[i];
-                _spriteBatch->draw(p->_position.x, p->_position.y, p->_position.z, p->_size, p->_size,
-                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color,
-                                   true);
-            }
-        }
-        else
+        // 2D Rotation.
+        Vector2 pivot(0.5f, 0.5f);
+
+        // 3D Rotation so that particles always face the camera.
+        Vector3 right = _node->getScene()->getActiveCamera()->getNode()->getRightVector();
+        Vector3 forward = _node->getScene()->getActiveCamera()->getNode()->getUpVector();
+
+        for (unsigned int i = 0; i < _particleCount; i++)
         {
-            // Rotation.
-            Vector2 pivot(0.5f, 0.5f);
+            Particle* p = &_particles[i];
 
-            for (unsigned int i = 0; i < _particleCount; i++)
-            {
-                Particle* p = &_particles[i];
-                _spriteBatch->draw(p->_position, p->_size, p->_size,
-                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color, pivot, p->_angle,
-                                   true);
-            }
+            _spriteBatch->draw(p->_position, right, forward, p->_size, p->_size,
+                               _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3],
+                               p->_color, pivot, p->_angle);
         }
 
         // Render.

+ 4 - 2
gameplay/src/PlatformWin32.cpp

@@ -669,8 +669,10 @@ void Platform::displayKeyboard(bool display)
 
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
-    Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-    Form::touchEventInternal(evt, x, y, contactIndex);
+    if (!Form::touchEventInternal(evt, x, y, contactIndex))
+    {
+        Game::getInstance()->touchEvent(evt, x, y, contactIndex);
+    }
 }
 
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)

+ 40 - 42
gameplay/src/RadioButton.cpp

@@ -23,27 +23,16 @@ RadioButton::~RadioButton()
 RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
 {
     RadioButton* radioButton = new RadioButton();
-    radioButton->_style = style;
-    properties->getVector2("position", &radioButton->_position);
-    properties->getVector2("size", &radioButton->_size);
+    radioButton->init(style, properties);
+
+    properties->getVector2("iconSize", &radioButton->_iconSize);
+
     if (properties->getBool("selected"))
     {
         RadioButton::clearSelected(radioButton->_groupId);
         radioButton->_selected = true;
     }
 
-    const char* id = properties->getId();
-    if (id)
-    {
-        radioButton->_id = id;
-    }
-
-    const char* text = properties->getString("text");
-    if (text)
-    {
-        radioButton->_text = text;
-    }
-
     const char* groupId = properties->getString("group");
     if (groupId)
     {
@@ -70,36 +59,35 @@ RadioButton* RadioButton::getRadioButton(const char* id)
     return NULL;
 }
 
-void RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+bool RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
-    switch (evt)
+    if (isEnabled())
     {
-    case Touch::TOUCH_PRESS:
-        {
-            _state = Control::STATE_ACTIVE;
-            _dirty = true;
-        }
-        break;
-    case Touch::TOUCH_RELEASE:
+        switch (evt)
         {
-            if (_state == Control::STATE_ACTIVE)
+        case Touch::TOUCH_RELEASE:
             {
-                if (x > 0 && x <= _size.x &&
-                    y > 0 && y <= _size.y)
+                if (_state == Control::STATE_ACTIVE)
                 {
-                    if (_callback)
+                    if (x > 0 && x <= _size.x &&
+                        y > 0 && y <= _size.y)
                     {
-                        _callback->trigger(this);
+                        if (_callback)
+                        {
+                            _callback->trigger(this);
+                        }
+                        RadioButton::clearSelected(_groupId);
+                        _selected = true;
                     }
-                    RadioButton::clearSelected(_groupId);
-                    _selected = true;
                 }
-                setState(Control::STATE_NORMAL);
-
             }
+            break;
         }
-        break;
+
+        return Button::touchEvent(evt, x, y, contactIndex);
     }
+
+    return false;
 }
 
 void RadioButton::clearSelected(const std::string& groupId)
@@ -131,7 +119,11 @@ void RadioButton::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
         }
         const Theme::Padding padding = _style->getPadding();
 
-        const Vector2 size = icon->getSize();
+        Vector2& size = _iconSize;
+        if (_iconSize.isZero())
+        {
+            size = icon->getSize();
+        }
         const Vector4 color = icon->getColor();
 
         Vector2 pos(position.x + _position.x + border.left + padding.left,
@@ -150,16 +142,15 @@ void RadioButton::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
     }
 }
 
-void RadioButton::drawText(const Vector2& position)
+void RadioButton::update(const Vector2& position)
 {
-    // TODO: Batch all labels that use the same font.
     Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getRadioButtonIcon();
+    Theme::Icon* icon = overlay->getCheckBoxIcon();
     Theme::Border border;
     Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
     if (containerRegion)
     {
-        border = containerRegion->getBorder();
+        border = overlay->getContainerRegion()->getBorder();
     }
     Theme::Padding padding = _style->getPadding();
 
@@ -174,13 +165,20 @@ void RadioButton::drawText(const Vector2& position)
     Vector2 pos(position.x + _position.x + border.left + padding.left + iconWidth,
             position.y + _position.y + border.top + padding.top);
 
-    Rectangle viewport(pos.x, pos.y,
+    _viewport.set(pos.x, pos.y,
         _size.x - border.left - padding.left - border.right - padding.right - iconWidth,
-        _size.y - border.top - padding.top - border.bottom - padding.bottom - font->getSize());
+        _size.y - border.top - padding.top - border.bottom - padding.bottom - overlay->getFontSize());
+}
+
+void RadioButton::drawText(const Vector2& position)
+{
+    // TODO: Batch all labels that use the same font.
+    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+    Font* font = overlay->getFont();
     
     // Draw the text.
     font->begin();
-    font->drawText(_text.c_str(), viewport, overlay->getTextColor(), overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+    font->drawText(_text.c_str(), _viewport, overlay->getTextColor(), overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
     font->end();
 
     _dirty = false;

+ 4 - 1
gameplay/src/RadioButton.h

@@ -17,7 +17,9 @@ public:
     static RadioButton* create(Theme::Style* style, Properties* properties);
     static RadioButton* getRadioButton(const char* id);
 
-    void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+    void update(const Vector2& position);
 
     void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
     void drawText(const Vector2& position);
@@ -30,6 +32,7 @@ private:
 
     std::string _groupId;
     bool _selected;
+    Vector2 _iconSize;
 };
 
 }

+ 100 - 53
gameplay/src/Slider.cpp

@@ -16,27 +16,13 @@ Slider::~Slider()
 Slider* Slider::create(Theme::Style* style, Properties* properties)
 {
     Slider* slider = new Slider();
+    slider->init(style, properties);
 
-    slider->_style = style;
-    properties->getVector2("position", &slider->_position);
-    properties->getVector2("size", &slider->_size);
     slider->_min = properties->getFloat("min");
     slider->_max = properties->getFloat("max");
     slider->_value = properties->getFloat("value");
     slider->_step = properties->getFloat("step");
 
-    const char* id = properties->getId();
-    if (id)
-    {
-        slider->_id = id;
-    }
-
-    const char* text = properties->getString("text");
-    if (text)
-    {
-        slider->_text = text;
-    }
-
     __sliders.push_back(slider);
 
     return slider;
@@ -64,49 +50,115 @@ Slider* Slider::getSlider(const char* id)
     return NULL;
 }
 
-void Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+void Slider::setMin(float min)
 {
-    Button::touchEvent(evt, x, y, contactIndex);
+    _min = min;
+}
 
-    if (_state == STATE_ACTIVE &&
-        x > 0 && x <= _size.x &&
-        y > 0 && y <= _size.y)
-    {
-        // Horizontal case.
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = _style->getOverlay(getOverlayType())->getContainerRegion();
-        if (containerRegion)
-        {
-            border = containerRegion->getBorder();
-        }
-        Theme::Padding padding = _style->getPadding();
+float Slider::getMin()
+{
+    return _min;
+}
 
-        const Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        const Theme::SliderIcon* icon = overlay->getSliderIcon();
+void Slider::setMax(float max)
+{
+    _max = max;
+}
 
-        const Vector2 minCapSize = icon->getMinCapSize();
-        const Vector2 maxCapSize = icon->getMaxCapSize();
+float Slider::getMax()
+{
+    return _max;
+}
 
-        float markerPosition = ((float)x - maxCapSize.x - border.left - padding.left) /
-                               (_size.x - border.left - border.right - padding.left - padding.right - minCapSize.x - maxCapSize.x);
-        if (markerPosition > 1.0f)
-        {
-            markerPosition = 1.0f;
-        }
-        else if (markerPosition < 0.0f)
-        {
-            markerPosition = 0.0f;
-        }
+void Slider::setStep(float step)
+{
+    _step = step;
+}
 
-        _value = markerPosition * _max;
-        if (_step > 0.0f)
+float Slider::getStep()
+{
+    return _step;
+}
+
+float Slider::getValue()
+{
+    return _value;
+}
+
+void Slider::setValue(float value)
+{
+    _value = value;
+}
+
+bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    if (!isEnabled())
+    {
+        return false;
+    }
+
+    bool consumeEvent = false;
+
+    switch (evt)
+    {
+    case Touch::TOUCH_PRESS:
+        _state = Control::STATE_ACTIVE;
+        _dirty = true;
+        // Fall through to calculate new value.
+
+    case Touch::TOUCH_MOVE:
+    case Touch::TOUCH_RELEASE:
+        if (_state == STATE_ACTIVE &&
+            x > 0 && x <= _size.x &&
+            y > 0 && y <= _size.y)
         {
-            float stepDistance = _step / (_max - _min);
+            // Horizontal case.
+            Theme::Border border;
+            Theme::ContainerRegion* containerRegion = _style->getOverlay(getOverlayType())->getContainerRegion();
+            if (containerRegion)
+            {
+                border = containerRegion->getBorder();
+            }
+            Theme::Padding padding = _style->getPadding();
+
+            const Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+            const Theme::SliderIcon* icon = overlay->getSliderIcon();
+
+            const Vector2 minCapSize = icon->getMinCapSize();
+            const Vector2 maxCapSize = icon->getMaxCapSize();
+
+            float markerPosition = ((float)x - maxCapSize.x - border.left - padding.left) /
+                                    (_size.x - border.left - border.right - padding.left - padding.right - minCapSize.x - maxCapSize.x);
+            if (markerPosition > 1.0f)
+            {
+                markerPosition = 1.0f;
+            }
+            else if (markerPosition < 0.0f)
+            {
+                markerPosition = 0.0f;
+            }
+
+            float oldValue = _value;
+            _value = markerPosition * _max;
+            if (_step > 0.0f)
+            {
+                float stepDistance = _step / (_max - _min);
             
-            int numSteps = round(_value / _step);
-            _value = _step * numSteps;
+                int numSteps = round(_value / _step);
+                _value = _step * numSteps;
+            }
+
+            // Call the callback if our value changed.
+            if (_callback && _value != oldValue)
+            {
+                _callback->trigger(this);
+            }
+
+            _dirty = true;
         }
     }
+
+    return consumeEvent;
 }
 
 void Slider::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
@@ -161,9 +213,4 @@ void Slider::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
     }
 }
 
-void Slider::drawText(const Vector2& position)
-{
-
-}
-
 }

+ 11 - 3
gameplay/src/Slider.h

@@ -17,14 +17,22 @@ public:
     static Slider* create(const char* id, float min, float max, float defaultPosition = 0.0f, float step = 1.0f);
     static Slider* getSlider(const char* id);
 
-    void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+    void setMin(float min);
+    float getMin();
 
-    void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
-    void drawText(const Vector2& position);
+    void setMax(float max);
+    float getMax();
+
+    void setStep(float step);
+    float getStep();
 
     void setValue(float value);
     float getValue();
 
+    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+    void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
+
 protected:
     Slider();
     ~Slider();

+ 48 - 51
gameplay/src/TextBox.cpp

@@ -21,23 +21,9 @@ TextBox::~TextBox()
 TextBox* TextBox::create(Theme::Style* style, Properties* properties)
 {
     TextBox* textBox = new TextBox();
-    textBox->_style = style;
-    properties->getVector2("position", &textBox->_position);
-    properties->getVector2("size", &textBox->_size);
-
-    const char* id = properties->getId();
-    if (id)
-    {
-        textBox->_id = id;
-    }
-
-    const char* text = properties->getString("text");
-    if (text)
-    {
-        textBox->_text = text;
-    }
-
+    textBox->init(style, properties);
     __textBoxes.push_back(textBox);
+
     return textBox;
 }
 
@@ -71,44 +57,55 @@ void TextBox::setCursorLocation(int x, int y)
                        y - border.top - padding.top + _viewport.y);
 }
 
-void TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {   
-    if (_state != STATE_DISABLED)
+    if (!isEnabled())
     {
-        switch (evt)
+        return false;
+    }
+
+    switch (evt)
+    {
+    case Touch::TOUCH_PRESS: 
+        if (_state == STATE_NORMAL)
         {
-        case Touch::TOUCH_PRESS: 
-            if (_state == STATE_NORMAL)
-            {
-                _state = STATE_ACTIVE;
-                Game::getInstance()->displayKeyboard(true);
-            }
-            break;
-        case Touch::TOUCH_MOVE:
-            if (_state == STATE_FOCUS &&
-                x > 0 && x <= _size.x &&
-                y > 0 && y <= _size.y)
-            {
-                setCursorLocation(x, y);
-                _dirty = true;
-            }
-            break;
-        case Touch::TOUCH_RELEASE:
-            if (x > 0 && x <= _size.x &&
-                y > 0 && y <= _size.y)
-            {
-                setCursorLocation(x, y);
-                _state = STATE_FOCUS;
-                _dirty = true;
-            }
-            else
-            {
-                _state = STATE_NORMAL;
-                Game::getInstance()->displayKeyboard(false);
-            }
-            break;
+            _state = STATE_ACTIVE;
+            Game::getInstance()->displayKeyboard(true);
+            _dirty = true;
+            return _consumeTouchEvents;
+        }
+        else if (!(x > 0 && x <= _size.x &&
+                    y > 0 && y <= _size.y))
+        {
+            _state = STATE_NORMAL;
+            Game::getInstance()->displayKeyboard(false);
+            _dirty = true;
+            return _consumeTouchEvents;
+        }
+        break;
+    case Touch::TOUCH_MOVE:
+        if (_state == STATE_FOCUS &&
+            x > 0 && x <= _size.x &&
+            y > 0 && y <= _size.y)
+        {
+            setCursorLocation(x, y);
+            _dirty = true;
+            return _consumeTouchEvents;
         }
+        break;
+    case Touch::TOUCH_RELEASE:
+        if (x > 0 && x <= _size.x &&
+            y > 0 && y <= _size.y)
+        {
+            setCursorLocation(x, y);
+            _state = STATE_FOCUS;
+            _dirty = true;
+            return _consumeTouchEvents;
+        }
+        break;
     }
+
+    return _consumeTouchEvents;
 }
 
 void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
@@ -274,8 +271,6 @@ void TextBox::update(const Vector2& position)
         font->getIndexAtLocation(_text.c_str(), _viewport, overlay->getFontSize(), _cursorLocation, &_cursorLocation,
             overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
     }
-
-    _dirty = false;
 }
 
 void TextBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
@@ -302,6 +297,8 @@ void TextBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
             spriteBatch->draw(_cursorLocation.x - (size.x / 2.0f), _cursorLocation.y, size.x, fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
         }
     }
+
+    _dirty = false;
 }
 
 }

+ 1 - 1
gameplay/src/TextBox.h

@@ -15,7 +15,7 @@ public:
     static TextBox* create(Theme::Style* style, Properties* properties);
     static TextBox* getTextBox(const char* id);
 
-    void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
     void keyEvent(Keyboard::KeyEvent evt, int key);
 

+ 29 - 5
gameplay/src/Theme.cpp

@@ -148,8 +148,8 @@ namespace gameplay
                 Vector4 maxCapRegion;
                 Vector4 trackRegion;
                 Vector4 markerRegion;
-                space->getVector4("leftCapRegion", &minCapRegion);
-                space->getVector4("rightCapRegion", &maxCapRegion);
+                space->getVector4("minCapRegion", &minCapRegion);
+                space->getVector4("maxCapRegion", &maxCapRegion);
                 space->getVector4("trackRegion", &trackRegion);
                 space->getVector4("markerRegion", &markerRegion);
                 
@@ -224,6 +224,7 @@ namespace gameplay
                 Theme::Style::Overlay* normal = NULL;
                 Theme::Style::Overlay* focus = NULL;
                 Theme::Style::Overlay* active = NULL;
+                Theme::Style::Overlay* disabled = NULL;
 
                 // Need to load OVERLAY_NORMAL first so that the other overlays can inherit from it.
                 Properties* innerSpace = space->getNextNamespace();
@@ -398,7 +399,6 @@ namespace gameplay
                             containerRegion = normal->getContainerRegion();
                         }
 
-                        
                         if (strcmp(innerSpacename, "focus") == 0)
                         {
                             focus = Theme::Style::Overlay::create();
@@ -432,6 +432,23 @@ namespace gameplay
                             active->setTextAlignment(alignment);
                             active->setTextRightToLeft(rightToLeft);
 
+                            theme->_fonts.insert(font);
+                        }
+                        else if (strcmp(innerSpacename, "disabled") == 0)
+                        {
+                            disabled = Theme::Style::Overlay::create();
+                            disabled->setContainerRegion(containerRegion);
+                            disabled->setTextCursor(textCursor);
+                            disabled->setMouseCursor(mouseCursor);
+                            disabled->setCheckBoxIcon(checkBoxIcon);
+                            disabled->setRadioButtonIcon(radioButtonIcon);
+                            disabled->setSliderIcon(sliderIcon);
+                            disabled->setTextColor(textColor);
+                            disabled->setFont(font);
+                            disabled->setFontSize(fontSize);
+                            disabled->setTextAlignment(alignment);
+                            disabled->setTextRightToLeft(rightToLeft);
+
                             theme->_fonts.insert(font);
                         }
                     }
@@ -451,7 +468,13 @@ namespace gameplay
                     active->addRef();
                 }
 
-                Theme::Style* s = new Theme::Style(space->getId(), margin, padding, normal, focus, active);
+                if (!disabled)
+                {
+                    disabled = normal;
+                    disabled->addRef();
+                }
+
+                Theme::Style* s = new Theme::Style(space->getId(), margin, padding, normal, focus, active, disabled);
                 theme->_styles.push_back(s);
             }
 
@@ -789,12 +812,13 @@ namespace gameplay
      * Theme::Style *
      ****************/
     Theme::Style::Style(const char* id, const Theme::Margin& margin, const Theme::Padding& padding,
-            Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active)
+            Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled)
         : _id(id), _margin(margin), _padding(padding)
     {
         _overlays[OVERLAY_NORMAL] = normal;
         _overlays[OVERLAY_FOCUS] = focus;
         _overlays[OVERLAY_ACTIVE] = active;
+        _overlays[OVERLAY_DISABLED] = disabled;
     }
 
     Theme::Style::~Style()

+ 4 - 3
gameplay/src/Theme.h

@@ -15,7 +15,7 @@
 namespace gameplay
 {
 
-#define MAX_OVERLAYS 3
+#define MAX_OVERLAYS 4
 #define MAX_OVERLAY_REGIONS 9
 
 /**
@@ -211,11 +211,12 @@ public:
         {
             OVERLAY_NORMAL,
             OVERLAY_FOCUS,
-            OVERLAY_ACTIVE
+            OVERLAY_ACTIVE,
+            OVERLAY_DISABLED
         };
 
         Style(const char* id, const Theme::Margin& margin, const Theme::Padding& padding,
-            Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active);
+            Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled);
 
         ~Style();
 

+ 4 - 0
gameplay/src/gameplay.h

@@ -79,3 +79,7 @@
 #include "AbsoluteLayout.h"
 #include "Label.h"
 #include "Button.h"
+#include "CheckBox.h"
+#include "TextBox.h"
+#include "RadioButton.h"
+#include "Slider.h"