Quellcode durchsuchen

Added ability to disable controls.
Touch events are now consumed by forms.
Particles are now billboarded.
Fixed drawing of text in CheckBoxes and RadioButtons.
Added missing methods to Slider.
Added an init method to Control for common initialization.

Adam Blake vor 14 Jahren
Ursprung
Commit
01b6c87a60

+ 25 - 33
gameplay/src/Button.cpp

@@ -17,22 +17,7 @@ namespace gameplay
     Button* Button::create(Theme::Style* style, Properties* properties)
     Button* Button::create(Theme::Style* style, Properties* properties)
     {
     {
         Button* button = new Button();
         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);
         __buttons.push_back(button);
 
 
         return button;
         return button;
@@ -65,28 +50,35 @@ namespace gameplay
         return NULL;
         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);
                 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;
     class Callback;
 
 
 public:
 public:
-    Button();
-    virtual ~Button();
-
     static Button* create(Theme::Style* style, Properties* properties);
     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* create(const char* id, unsigned int x, unsigned int y, unsigned int width, unsigned int height);
     static Button* getButton(const char* id);
     static Button* getButton(const char* id);
 
 
     template <class ClassType>
     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:
 protected:
+    Button();
+    virtual ~Button();
+
     Callback* _callback;
     Callback* _callback;
 
 
 private:
 private:
@@ -36,16 +36,16 @@ private:
     {
     {
     public:
     public:
         virtual ~Callback() { }
         virtual ~Callback() { }
-        virtual void trigger(Button* button) = 0;
+        virtual void trigger(Control* button) = 0;
     };
     };
 
 
     template <class ClassType>
     template <class ClassType>
     class CallbackImpl : public Callback
     class CallbackImpl : public Callback
     {
     {
-        typedef void (ClassType::*CallbackMethod)(Button*);
+        typedef void (ClassType::*CallbackMethod)(Control*);
     public:
     public:
         CallbackImpl(ClassType* instance, CallbackMethod method);
         CallbackImpl(ClassType* instance, CallbackMethod method);
-        void trigger(Button* button);
+        void trigger(Control* control);
     private:
     private:
         ClassType* _instance;
         ClassType* _instance;
         CallbackMethod _method;
         CallbackMethod _method;
@@ -59,15 +59,15 @@ Button::CallbackImpl<ClassType>::CallbackImpl(ClassType* instance, CallbackMetho
 }
 }
 
 
 template <class ClassType>
 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);
     _callback = new CallbackImpl<ClassType>(instance, callbackMethod);
 }
 }
 
 
 template <class ClassType>
 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::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)
 CheckBox* CheckBox::getCheckBox(const char* id)
@@ -50,18 +36,28 @@ CheckBox* CheckBox::getCheckBox(const char* id)
     std::vector<CheckBox*>::const_iterator it;
     std::vector<CheckBox*>::const_iterator it;
     for (it = __checkBoxes.begin(); it < __checkBoxes.end(); 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;
     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)
     switch (evt)
     {
     {
     case Touch::TOUCH_RELEASE:
     case Touch::TOUCH_RELEASE:
@@ -78,7 +74,35 @@ void CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
         break;
         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)
 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 Theme::Padding padding = _style->getPadding();
 
 
-        const Vector2 size = icon->getSize();
+        Vector2& size = _iconSize;
+        if (_iconSize.isZero())
+        {
+            size = icon->getSize();
+        }
+
         const Vector4 color = icon->getColor();
         const Vector4 color = icon->getColor();
 
 
         Vector2 pos(position.x + _position.x + border.left + padding.left,
         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.
     // TODO: Batch all labels that use the same font.
     Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
     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();
     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.
     // Draw the text.
     font->begin();
     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();
     font->end();
 
 
     _dirty = false;
     _dirty = false;

+ 9 - 5
gameplay/src/CheckBox.h

@@ -12,21 +12,25 @@ namespace gameplay
 class CheckBox : public Button
 class CheckBox : public Button
 {
 {
 public:
 public:
-    CheckBox();
-    ~CheckBox();
-
     static CheckBox* create(Theme::Style* style, Properties* properties);
     static CheckBox* create(Theme::Style* style, Properties* properties);
     static CheckBox* getCheckBox(const char* id);
     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 drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
     void drawText(const Vector2& position);
     void drawText(const Vector2& position);
 
 
-private:
+protected:
+    CheckBox();
     CheckBox(const CheckBox& copy);
     CheckBox(const CheckBox& copy);
+    ~CheckBox();
 
 
     bool _checked;
     bool _checked;
+    Vector2 _iconSize;
 };
 };
 
 
 }
 }

+ 17 - 15
gameplay/src/Container.cpp

@@ -33,7 +33,7 @@ namespace gameplay
         SAFE_RELEASE(_layout);
         SAFE_RELEASE(_layout);
     }
     }
 
 
-    Container* Container::create(const char* id, Layout::Type type)
+    Container* Container::create(Layout::Type type)
     {
     {
         Layout* layout = NULL;
         Layout* layout = NULL;
         switch(type)
         switch(type)
@@ -49,7 +49,6 @@ namespace gameplay
         }
         }
 
 
         Container* container = new Container();
         Container* container = new Container();
-        container->_id = id;
         container->_layout = layout;
         container->_layout = layout;
 
 
         __containers.push_back(container);
         __containers.push_back(container);
@@ -59,14 +58,9 @@ namespace gameplay
 
 
     Container* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
     Container* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
     {
     {
-        const char* id = properties->getId();
         const char* layoutString = properties->getString("layout");
         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);
         container->addControls(theme, properties);
 
 
         return container;
         return container;
@@ -226,8 +220,6 @@ namespace gameplay
         {
         {
             _layout->update(this);
             _layout->update(this);
         }
         }
-
-        _dirty = false;
     }
     }
 
 
     void Container::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
     void Container::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
@@ -277,8 +269,10 @@ namespace gameplay
         return false;
         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)
     {
     {
+        bool eventConsumed = false;
+
         std::vector<Control*>::const_iterator it;
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
         {
@@ -287,25 +281,28 @@ namespace gameplay
             const Vector2& position = control->getPosition();
             const Vector2& position = control->getPosition();
             
             
             if (control->getState() != Control::STATE_NORMAL ||
             if (control->getState() != Control::STATE_NORMAL ||
-                (x >= position.x &&
+                (evt == Touch::TOUCH_PRESS &&
+                 x >= position.x &&
                  x <= position.x + size.x &&
                  x <= position.x + size.x &&
                  y >= position.y &&
                  y >= position.y &&
                  y <= position.y + size.y))
                  y <= position.y + size.y))
             {
             {
                 // Pass on the event's position relative to the control.
                 // 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);
             }
             }
         }
         }
 
 
         switch (evt)
         switch (evt)
         {
         {
         case Touch::TOUCH_PRESS:
         case Touch::TOUCH_PRESS:
-            setState(Control::STATE_ACTIVE);
+            setState(Control::STATE_FOCUS);
             break;
             break;
         case Touch::TOUCH_RELEASE:
         case Touch::TOUCH_RELEASE:
             setState(Control::STATE_NORMAL);
             setState(Control::STATE_NORMAL);
             break;
             break;
         }
         }
+
+        return (_consumeTouchEvents | eventConsumed);
     }
     }
 
 
     void Container::keyEvent(Keyboard::KeyEvent evt, int key)
     void Container::keyEvent(Keyboard::KeyEvent evt, int key)
@@ -320,6 +317,11 @@ namespace gameplay
 
 
     Layout::Type Container::getLayoutType(const char* layoutString)
     Layout::Type Container::getLayoutType(const char* layoutString)
     {
     {
+        if (!layoutString)
+        {
+            return Layout::LAYOUT_ABSOLUTE;
+        }
+
         std::string layoutName(layoutString);
         std::string layoutName(layoutString);
         std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
         std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
         if (layoutName == "LAYOUT_ABSOLUTE")
         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.
      * 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* create(Theme::Style* style, Properties* properties, Theme* theme);
     static Container* getContainer(const char* id);
     static Container* getContainer(const char* id);
 
 
@@ -69,7 +69,7 @@ public:
     virtual void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
     virtual void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
     virtual void drawText(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);
     virtual void keyEvent(Keyboard::KeyEvent evt, int key);
 
 

+ 59 - 3
gameplay/src/Control.cpp

@@ -5,7 +5,7 @@ namespace gameplay
 {
 {
     Control::Control()
     Control::Control()
         : _id(""), _state(Control::STATE_NORMAL), _size(Vector2::zero()), _position(Vector2::zero()), _border(Vector2::zero()), _padding(Vector2::zero()),
         : _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()
     const char* Control::getID()
     {
     {
         return _id.c_str();
         return _id.c_str();
@@ -86,7 +102,7 @@ namespace gameplay
 
 
     bool Control::isEnabled()
     bool Control::isEnabled()
     {
     {
-        return _state == STATE_DISABLED;
+        return _state != STATE_DISABLED;
     }
     }
 
 
     Theme::Style::OverlayType Control::getOverlayType() const
     Theme::Style::OverlayType Control::getOverlayType() const
@@ -99,14 +115,27 @@ namespace gameplay
             return Theme::Style::OVERLAY_FOCUS;
             return Theme::Style::OVERLAY_FOCUS;
         case Control::STATE_ACTIVE:
         case Control::STATE_ACTIVE:
             return Theme::Style::OVERLAY_ACTIVE;
             return Theme::Style::OVERLAY_ACTIVE;
+        case Control::STATE_DISABLED:
+            return Theme::Style::OVERLAY_DISABLED;
         default:
         default:
             return Theme::Style::OVERLAY_NORMAL;
             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.
         // Empty stub to be implemented by Button and its descendents.
+        return _consumeTouchEvents;
     }
     }
 
 
     void Control::keyEvent(Keyboard::KeyEvent evt, int key)
     void Control::keyEvent(Keyboard::KeyEvent evt, int key)
@@ -192,4 +221,31 @@ namespace gameplay
     {
     {
         return _dirty;
         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;
+    }
 }
 }

+ 9 - 2
gameplay/src/Control.h

@@ -86,10 +86,13 @@ public:
 
 
     Theme::Style::OverlayType getOverlayType() const;
     Theme::Style::OverlayType getOverlayType() const;
 
 
+    void setConsumeTouchEvents(bool consume);
+    bool getConsumeTouchEvents();
+
     /**
     /**
      * Defaults to empty stub.
      * 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);
     virtual void keyEvent(Keyboard::KeyEvent evt, int key);
 
 
@@ -111,13 +114,16 @@ public:
     void setStyle(Theme::Style* Style);
     void setStyle(Theme::Style* Style);
     Theme::Style* getStyle() const;
     Theme::Style* getStyle() const;
 
 
-    void themeChanged();
+    static State getStateFromString(const char* state);
 
 
 protected:
 protected:
     Control();
     Control();
     Control(const Control& copy);
     Control(const Control& copy);
     virtual ~Control();
     virtual ~Control();
 
 
+    // Set properties common to all Controls.
+    virtual void init(Theme::Style* style, Properties* properties);
+
     std::string _id;
     std::string _id;
     State _state;           // Determines overlay used during draw().
     State _state;           // Determines overlay used during draw().
     Vector2 _size;
     Vector2 _size;
@@ -127,6 +133,7 @@ protected:
     bool _autoWidth;
     bool _autoWidth;
     bool _autoHeight;
     bool _autoHeight;
     bool _dirty;
     bool _dirty;
+    bool _consumeTouchEvents;
     Theme::Style* _style;
     Theme::Style* _style;
 };
 };
 
 

+ 5 - 0
gameplay/src/Font.cpp

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

+ 37 - 18
gameplay/src/Form.cpp

@@ -59,28 +59,23 @@ namespace gameplay
         }
         }
 
 
         // Create new form with given ID, theme and layout.
         // Create new form with given ID, theme and layout.
-        const char* id = formProperties->getId();
         const char* themeFile = formProperties->getString("theme");
         const char* themeFile = formProperties->getString("theme");
         const char* layoutString = formProperties->getString("layout");
         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");
         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.
         // Add all the controls to the form.
-        form->addControls(form->getTheme(), formProperties);
+        form->addControls(theme, formProperties);
 
 
         SAFE_DELETE(properties);
         SAFE_DELETE(properties);
 
 
         return form;
         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;
         Layout* layout;
         switch(type)
         switch(type)
@@ -100,9 +95,7 @@ namespace gameplay
         assert(theme);
         assert(theme);
 
 
         Form* form = new Form();
         Form* form = new Form();
-        form->_id = id;
         form->_layout = layout;
         form->_layout = layout;
-        form->_frameBuffer = FrameBuffer::create(id);
         form->_theme = theme;
         form->_theme = theme;
 
 
         __forms.push_back(form);
         __forms.push_back(form);
@@ -271,8 +264,14 @@ namespace gameplay
         // Bind the WorldViewProjection matrix
         // Bind the WorldViewProjection matrix
         material->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_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.
         // 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);
             RenderTarget* rt = RenderTarget::create(_id.c_str(), _size.x, _size.y);
             _frameBuffer->setRenderTarget(rt);
             _frameBuffer->setRenderTarget(rt);
@@ -289,7 +288,7 @@ namespace gameplay
         SAFE_RELEASE(sampler);
         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.
         // Check for a collision with each Form in __forms.
         // Pass the event on.
         // Pass the event on.
@@ -344,7 +343,21 @@ namespace gameplay
                             m.transformPoint(&point);
                             m.transformPoint(&point);
 
 
                             // Pass the touch event on.
                             // Pass the touch event on.
-                            form->touchEvent(evt, point.x, size.y - point.y, contactIndex);
+                            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;
+                               }
+                            }
                         }
                         }
                     }
                     }
                 }
                 }
@@ -354,18 +367,24 @@ namespace gameplay
                     const Vector2& size = form->getSize();
                     const Vector2& size = form->getSize();
                     const Vector2& position = form->getPosition();
                     const Vector2& position = form->getPosition();
 
 
-                    if (form->getState() == Control::STATE_ACTIVE ||
-                        (x >= position.x &&
+                    if (form->getState() == Control::STATE_FOCUS ||
+                        (evt == Touch::TOUCH_PRESS &&
+                         x >= position.x &&
                          x <= position.x + size.x &&
                          x <= position.x + size.x &&
                          y >= position.y &&
                          y >= position.y &&
                          y <= position.y + size.y))
                          y <= position.y + size.y))
                     {
                     {
                         // Pass on the event's position relative to the form.
                         // Pass on the event's position relative to the form.
-                        form->touchEvent(evt, x - position.x, y - position.y, contactIndex);
+                        if (form->touchEvent(evt, x - position.x, y - position.y, contactIndex))
+                        {
+                            return true;
+                        }
                     }
                     }
                 }
                 }
             }
             }
         }
         }
+
+        return false;
     }
     }
 
 
     void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
     void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)

+ 2 - 2
gameplay/src/Form.h

@@ -23,7 +23,7 @@ public:
      * Create from .form file.
      * Create from .form file.
      */
      */
     static Form* create(const char* path);
     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);
     static Form* getForm(const char* id);
 
 
     void setTheme(Theme* theme);
     void setTheme(Theme* theme);
@@ -53,7 +53,7 @@ private:
     void draw(SpriteBatch* spriteBatch);
     void draw(SpriteBatch* spriteBatch);
     void draw(SpriteBatch* spriteBatch, const Vector2& position);
     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);
     static void keyEventInternal(Keyboard::KeyEvent evt, int key);
 
 

+ 11 - 0
gameplay/src/Label.cpp

@@ -41,6 +41,17 @@ namespace gameplay
         return label;
         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)
     Label* Label::getLabel(const char* id)
     {
     {
         std::vector<Label*>::const_iterator it;
         std::vector<Label*>::const_iterator it;

+ 2 - 0
gameplay/src/Label.h

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

+ 14 - 23
gameplay/src/ParticleEmitter.cpp

@@ -2,6 +2,7 @@
 #include "ParticleEmitter.h"
 #include "ParticleEmitter.h"
 #include "Game.h"
 #include "Game.h"
 #include "Node.h"
 #include "Node.h"
+#include "Scene.h"
 #include "Quaternion.h"
 #include "Quaternion.h"
 #include "Properties.h"
 #include "Properties.h"
 
 
@@ -58,7 +59,7 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
 
 
     // Use default SpriteBatch material.
     // Use default SpriteBatch material.
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);
-    texture->release(); // batch owns the texture
+    texture->release(); // batch owns the texture.
     assert(batch);
     assert(batch);
 
 
     ParticleEmitter* emitter = new ParticleEmitter(batch, particleCountMax);
     ParticleEmitter* emitter = new ParticleEmitter(batch, particleCountMax);
@@ -890,30 +891,20 @@ void ParticleEmitter::draw()
         // Begin sprite batch drawing
         // Begin sprite batch drawing
         _spriteBatch->begin();
         _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.
         // Render.

+ 4 - 2
gameplay/src/PlatformWin32.cpp

@@ -663,8 +663,10 @@ void Platform::displayKeyboard(bool display)
 
 
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 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)
 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::create(Theme::Style* style, Properties* properties)
 {
 {
     RadioButton* radioButton = new RadioButton();
     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"))
     if (properties->getBool("selected"))
     {
     {
         RadioButton::clearSelected(radioButton->_groupId);
         RadioButton::clearSelected(radioButton->_groupId);
         radioButton->_selected = true;
         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");
     const char* groupId = properties->getString("group");
     if (groupId)
     if (groupId)
     {
     {
@@ -70,36 +59,35 @@ RadioButton* RadioButton::getRadioButton(const char* id)
     return NULL;
     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)
 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 Theme::Padding padding = _style->getPadding();
 
 
-        const Vector2 size = icon->getSize();
+        Vector2& size = _iconSize;
+        if (_iconSize.isZero())
+        {
+            size = icon->getSize();
+        }
         const Vector4 color = icon->getColor();
         const Vector4 color = icon->getColor();
 
 
         Vector2 pos(position.x + _position.x + border.left + padding.left,
         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::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getRadioButtonIcon();
+    Theme::Icon* icon = overlay->getCheckBoxIcon();
     Theme::Border border;
     Theme::Border border;
     Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
     Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
     if (containerRegion)
     if (containerRegion)
     {
     {
-        border = containerRegion->getBorder();
+        border = overlay->getContainerRegion()->getBorder();
     }
     }
     Theme::Padding padding = _style->getPadding();
     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,
     Vector2 pos(position.x + _position.x + border.left + padding.left + iconWidth,
             position.y + _position.y + border.top + padding.top);
             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.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.
     // Draw the text.
     font->begin();
     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();
     font->end();
 
 
     _dirty = false;
     _dirty = false;

+ 4 - 1
gameplay/src/RadioButton.h

@@ -17,7 +17,9 @@ public:
     static RadioButton* create(Theme::Style* style, Properties* properties);
     static RadioButton* create(Theme::Style* style, Properties* properties);
     static RadioButton* getRadioButton(const char* id);
     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 drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
     void drawText(const Vector2& position);
     void drawText(const Vector2& position);
@@ -30,6 +32,7 @@ private:
 
 
     std::string _groupId;
     std::string _groupId;
     bool _selected;
     bool _selected;
+    Vector2 _iconSize;
 };
 };
 
 
 }
 }

+ 100 - 48
gameplay/src/Slider.cpp

@@ -16,27 +16,13 @@ Slider::~Slider()
 Slider* Slider::create(Theme::Style* style, Properties* properties)
 Slider* Slider::create(Theme::Style* style, Properties* properties)
 {
 {
     Slider* slider = new Slider();
     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->_min = properties->getFloat("min");
     slider->_max = properties->getFloat("max");
     slider->_max = properties->getFloat("max");
     slider->_value = properties->getFloat("value");
     slider->_value = properties->getFloat("value");
     slider->_step = properties->getFloat("step");
     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);
     __sliders.push_back(slider);
 
 
     return slider;
     return slider;
@@ -64,49 +50,115 @@ Slider* Slider::getSlider(const char* id)
     return NULL;
     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;
+}
+
+float Slider::getStep()
+{
+    return _step;
+}
+
+float Slider::getValue()
+{
+    return _value;
+}
 
 
-        _value = markerPosition * _max;
-        if (_step > 0.0f)
+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)
 void Slider::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)

+ 11 - 2
gameplay/src/Slider.h

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

+ 48 - 51
gameplay/src/TextBox.cpp

@@ -21,23 +21,9 @@ TextBox::~TextBox()
 TextBox* TextBox::create(Theme::Style* style, Properties* properties)
 TextBox* TextBox::create(Theme::Style* style, Properties* properties)
 {
 {
     TextBox* textBox = new TextBox();
     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);
     __textBoxes.push_back(textBox);
+
     return textBox;
     return textBox;
 }
 }
 
 
@@ -71,44 +57,55 @@ void TextBox::setCursorLocation(int x, int y)
                        y - border.top - padding.top + _viewport.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)
 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,
         font->getIndexAtLocation(_text.c_str(), _viewport, overlay->getFontSize(), _cursorLocation, &_cursorLocation,
             overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
             overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
     }
     }
-
-    _dirty = false;
 }
 }
 
 
 void TextBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
 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);
             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* create(Theme::Style* style, Properties* properties);
     static TextBox* getTextBox(const char* id);
     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);
     void keyEvent(Keyboard::KeyEvent evt, int key);
 
 

+ 29 - 5
gameplay/src/Theme.cpp

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

+ 4 - 3
gameplay/src/Theme.h

@@ -15,7 +15,7 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
-#define MAX_OVERLAYS 3
+#define MAX_OVERLAYS 4
 #define MAX_OVERLAY_REGIONS 9
 #define MAX_OVERLAY_REGIONS 9
 
 
 /**
 /**
@@ -211,11 +211,12 @@ public:
         {
         {
             OVERLAY_NORMAL,
             OVERLAY_NORMAL,
             OVERLAY_FOCUS,
             OVERLAY_FOCUS,
-            OVERLAY_ACTIVE
+            OVERLAY_ACTIVE,
+            OVERLAY_DISABLED
         };
         };
 
 
         Style(const char* id, const Theme::Margin& margin, const Theme::Padding& padding,
         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();
         ~Style();
 
 

+ 4 - 0
gameplay/src/gameplay.h

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