2
0
Эх сурвалжийг харах

Making progress on the UI classes.

Adam Blake 14 жил өмнө
parent
commit
67aa4b39c7

+ 1 - 1
gameplay/src/AbsoluteLayout.cpp

@@ -26,7 +26,7 @@ namespace gameplay
         return Layout::LAYOUT_ABSOLUTE;
     }
 
-    void AbsoluteLayout::update(std::vector<Control*> controls, const Vector2& size)
+    void AbsoluteLayout::update(std::vector<Control*> controls, const Vector2& size, Theme::Style* containerStyle)
     {
         // An AbsoluteLayout does nothing to modify the layout of Controls.
     }

+ 1 - 1
gameplay/src/AbsoluteLayout.h

@@ -13,7 +13,7 @@ public:
 
     Layout::Type getType();
 
-    void update(std::vector<Control*> controls, const Vector2& size);
+    void update(std::vector<Control*> controls, const Vector2& size, Theme::Style* containerStyle);
 
 private:
     AbsoluteLayout();

+ 5 - 0
gameplay/src/Base.h

@@ -118,6 +118,11 @@ extern void printError(const char* format, ...);
 #define M_1_PI                      0.31830988618379067154
 #endif
 
+inline float round(float r)
+{
+    return (r > 0.0f) ? floor(r + 0.5f) : ceil(r - 0.5f);
+}
+
 // NOMINMAX makes sure that windef.h doesn't add macros min and max
 #ifdef WIN32
     #define NOMINMAX

+ 17 - 11
gameplay/src/Button.cpp

@@ -56,20 +56,26 @@ namespace gameplay
 
     void Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
     {
-        switch (evt)
+        if (_state != STATE_DISABLED)
         {
-        case Touch::TOUCH_PRESS:
-            _state = Control::STATE_ACTIVE;
-            break;
-        case Touch::TOUCH_RELEASE:
-            if (_callback &&
-                x > 0 && x <= _size.x &&
-                y > 0 && y <= _size.y)
+            switch (evt)
             {
-                _callback->trigger(this);
+            case Touch::TOUCH_PRESS:
+                // TODO: button-down callback.
+
+                _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);
+                }
+                setState(Control::STATE_NORMAL);
+                break;
             }
-            setState(Control::STATE_NORMAL);
-            break;
         }
     }
 }

+ 12 - 13
gameplay/src/CheckBox.cpp

@@ -51,28 +51,23 @@ CheckBox* CheckBox::getCheckBox(const char* id)
 
 void CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
-
     switch (evt)
     {
-    case Touch::TOUCH_PRESS:
-        {
-            _state = Control::STATE_ACTIVE;
-        }
-        break;
     case Touch::TOUCH_RELEASE:
         {
             if (_state == Control::STATE_ACTIVE)
             {
-                if (_callback)
+                if (x > 0 && x <= _size.x &&
+                    y > 0 && y <= _size.y)
                 {
-                    _callback->trigger(this);
+                    _checked = !_checked;
                 }
-                _checked = !_checked;
-                setState(Control::STATE_NORMAL);
             }
         }
         break;
     }
+
+    Button::touchEvent(evt, x, y, contactIndex);
 }
 
 void CheckBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
@@ -80,7 +75,7 @@ void CheckBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
     // Left, v-center.
     // TODO: Set an alignment for icons.
     Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::CheckBoxIcon* icon = overlay->getCheckBoxIcon();
+    Theme::Icon* icon = overlay->getCheckBoxIcon();
     if (icon)
     {
         Theme::Border border = _style->getBorder();
@@ -89,7 +84,11 @@ void CheckBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
         Vector2 pos(position.x + _position.x + border.left + padding.left,
             position.y + _position.y + (_size.y - border.bottom - padding.bottom) / 2.0f - icon->size.y / 2.0f);
 
-        if (_checked)
+        if (_state == STATE_ACTIVE)
+        {
+            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->active.u1, icon->active.v1, icon->active.u2, icon->active.v2, overlay->getBorderColor());
+        }
+        else if (_checked)
         {
             spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->on.u1, icon->on.v1, icon->on.u2, icon->on.v2, overlay->getBorderColor());
         }
@@ -104,7 +103,7 @@ void CheckBox::drawText(const Vector2& position)
 {
     // TODO: Batch all labels that use the same font.
     Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::CheckBoxIcon* icon = overlay->getCheckBoxIcon();
+    Theme::Icon* icon = overlay->getCheckBoxIcon();
     Theme::Border border = _style->getBorder();
     Theme::Padding padding = _style->getPadding();
 

+ 123 - 3
gameplay/src/Container.cpp

@@ -2,6 +2,12 @@
 #include "Container.h"
 #include "Layout.h"
 #include "AbsoluteLayout.h"
+#include "VerticalLayout.h"
+#include "Label.h"
+#include "Button.h"
+#include "CheckBox.h"
+#include "RadioButton.h"
+#include "Slider.h"
 
 namespace gameplay
 {
@@ -30,6 +36,7 @@ namespace gameplay
         case Layout::LAYOUT_FLOW:
             break;
         case Layout::LAYOUT_VERTICAL:
+            layout = VerticalLayout::create();
             break;
         }
 
@@ -42,6 +49,74 @@ namespace gameplay
         return container;
     }
 
+    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->addControls(theme, properties);
+
+        return container;
+    }
+
+    void Container::addControls(Theme* theme, Properties* properties)
+    {
+        // Add all the controls to this container.
+        Properties* controlSpace = properties->getNextNamespace();
+        while (controlSpace != NULL)
+        {
+            Control* control = NULL;
+
+            const char* controlStyleName = controlSpace->getString("style");
+            Theme::Style* controlStyle = NULL;
+            if (controlStyleName)
+            {
+                 controlStyle = theme->getStyle(controlStyleName);
+            }
+
+            std::string controlName(controlSpace->getNamespace());
+            std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
+            if (controlName == "LABEL")
+            {
+                control = Label::create(controlStyle, controlSpace);
+            }
+            else if (controlName == "BUTTON")
+            {
+                control = Button::create(controlStyle, controlSpace);
+            }
+            else if (controlName == "CHECKBOX")
+            {
+                control = CheckBox::create(controlStyle, controlSpace);
+            }
+            else if (controlName == "RADIOBUTTON")
+            {
+                control = RadioButton::create(controlStyle, controlSpace);
+            }
+            else if (controlName == "CONTAINER")
+            {
+                control = Container::create(controlStyle, controlSpace, theme);
+            }
+            else if (controlName == "SLIDER")
+            {
+                control = Slider::create(controlStyle, controlSpace);
+            }
+
+            // Add the new control to the form.
+            if (control)
+            {
+                addControl(control);
+            }
+
+            // Get the next control.
+            controlSpace = properties->getNextNamespace();
+        }
+    }
+
     Container* Container::getContainer(const char* id)
     {
         std::vector<Container*>::const_iterator it;
@@ -133,17 +208,33 @@ namespace gameplay
         // Should probably have sizeChanged() for this.
         //if (isDirty())
         {
-            _layout->update(_controls, _size);
+            // Call update() on nested Containers.
+            std::vector<Control*>::const_iterator it;
+            for (it = _controls.begin(); it < _controls.end(); it++)
+            {
+                // Can't do this without enabling run-time type information!
+                //Container* container = dynamic_cast<Container*>(*it);
+
+                Control* control = *it;
+                if (control->isContainer())
+                {
+                    Container* container = static_cast<Container*>(control);
+                    container->update();
+                }
+            }
+            
+            _layout->update(_controls, _size, _style);
         }
     }
 
     void Container::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
     {
+        Vector2 pos(position.x + _position.x, position.y + _position.y);
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
             Control* control = *it;
-            control->drawSprites(spriteBatch, position);
+            control->drawSprites(spriteBatch, pos);
         }
 
         _dirty = false;
@@ -151,11 +242,12 @@ namespace gameplay
 
     void Container::drawText(const Vector2& position)
     {
+        Vector2 pos(position.x + _position.x, position.y + _position.y);
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
             Control* control = *it;
-            control->drawText(position);
+            control->drawText(pos);
         }
 
         _dirty = false;
@@ -212,4 +304,32 @@ namespace gameplay
             break;
         }
     }
+
+    Layout::Type Container::getLayoutType(const char* layoutString)
+    {
+        std::string layoutName(layoutString);
+        std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
+        if (layoutName == "LAYOUT_ABSOLUTE")
+        {
+            return Layout::LAYOUT_ABSOLUTE;
+        }
+        else if (layoutName == "LAYOUT_VERTICAL")
+        {
+            return Layout::LAYOUT_VERTICAL;
+        }
+        else if (layoutName == "LAYOUT_FLOW")
+        {
+            return Layout::LAYOUT_FLOW;
+        }
+        else
+        {
+            // Default.
+            return Layout::LAYOUT_ABSOLUTE;
+        }
+    }
+
+    bool Container::isContainer()
+    {
+        return true;
+    }
 }

+ 6 - 0
gameplay/src/Container.h

@@ -14,6 +14,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(Theme::Style* style, Properties* properties, Theme* theme);
     static Container* getContainer(const char* id);
 
     Layout* getLayout();
@@ -68,12 +69,17 @@ public:
 
     void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    bool isContainer();
+
 protected:
     Container();
     Container(const Container& copy);
     virtual ~Container();
 
+    static Layout::Type getLayoutType(const char* layoutString);
+
     bool isDirty();
+    void addControls(Theme* theme, Properties* properties);
 
     Layout* _layout;
     std::vector<Control*> _controls;

+ 15 - 20
gameplay/src/Control.cpp

@@ -53,26 +53,6 @@ namespace gameplay
         _autoHeight = height;
     }
 
-    void Control::setBorder(float horizontal, float vertical)
-    {
-        _border.set(horizontal, vertical);
-    }
-
-    const Vector2& Control::getBorder() const
-    {
-        return _border;
-    }
-
-    void Control::setPadding(float horizontal, float vertical)
-    {
-        _padding.set(horizontal, vertical);
-    }
-
-    const Vector2& Control::getPadding() const
-    {
-        return _padding;
-    }
-
     void Control::setStyle(Theme::Style* style)
     {
         _style = style;
@@ -93,6 +73,16 @@ namespace gameplay
         return _state;
     }
 
+    void Control::disable()
+    {
+        _state = STATE_DISABLED;
+    }
+
+    void Control::enable()
+    {
+        _state = STATE_NORMAL;
+    }
+
     Theme::Style::OverlayType Control::getOverlayType()
     {
         switch (_state)
@@ -188,4 +178,9 @@ namespace gameplay
 
         return false;
     }
+
+    bool Control::isContainer()
+    {
+        return false;
+    }
 }

+ 32 - 17
gameplay/src/Control.h

@@ -19,7 +19,8 @@ public:
     {
         STATE_NORMAL,
         STATE_FOCUS,
-        STATE_ACTIVE
+        STATE_ACTIVE,
+        STATE_DISABLED
     };
 
     const char* getID();
@@ -34,12 +35,33 @@ public:
     const Rectangle& getBounds(bool includePadding) const;
 
     /**
-     * Position of this Control relative to its parent Container.
+     * Set the position of this Control relative to its parent Container.
+     *
+     * @param x The x coordinate.
+     * @param y The y coordinate.
      */
     void setPosition(float x, float y);
+
+    /**
+     * Get the position of this Control relative to its parent Container.
+     *
+     * @return The position vector.
+     */
     const Vector2& getPosition() const;
 
+    /**
+     * Set the size of this Control, including its border and padding.
+     *
+     * @param width The width.
+     * @param height The height.
+     */
     void setSize(float width, float height);
+
+    /**
+     * Get the size of this Control, including its border and padding.
+     *
+     * @return The size vector.
+     */
     const Vector2& getSize() const;
 
     /**
@@ -48,27 +70,18 @@ public:
      *
      * Similarly set this on the width and/or height of a Container to tightly fit
      * the Container around all its children.
+     *
+     * @param width Whether to automatically determine this Control's width.
+     * @param height Whether to automatically determine this Control's height.
      */
     void setAutoSize(bool width, bool height);
 
-    /**
-     * 
-     * When auto-size is set on width and/or height:
-     * Space added to the calculated (tightly bound) width and height.
-     */
-    void setBorder(float horizontal, float vertical);
-    const Vector2& getBorder() const;
-
-    /**
-     * In a layout of any type other than Absolute,
-     * guarantee this much empty space surrounding this Control.
-     */
-    void setPadding(float horizontal, float vertical);
-    const Vector2& getPadding() const;
-
     void setState(State state);
     State getState();
 
+    void disable();
+    void enable();
+
     Theme::Style::OverlayType getOverlayType();
 
     /**
@@ -94,6 +107,8 @@ public:
 
     void themeChanged();
 
+    virtual bool isContainer();
+
 protected:
     Control();
     Control(const Control& copy);

+ 11 - 10
gameplay/src/Font.cpp

@@ -177,10 +177,11 @@ void Font::begin()
 void Font::drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft)
 {
     float scale = (float)size / _size;
-    char* cursor = NULL;
+    const char* cursor = NULL;
+
     if (rightToLeft)
     {
-        cursor = const_cast<char*>(text);
+        cursor = text;
     }
 
     int xPos = x, yPos = y;
@@ -289,7 +290,7 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
 void Font::drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify, bool wrap, bool rightToLeft)
 {
     float scale = (float)size / _size;
-    char* token = const_cast<char*>(text);
+    const char* token = text;
     const int length = strlen(text);
     int yPos = area.y;
 
@@ -305,7 +306,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         hAlign = ALIGN_LEFT;
     }
 
-    token = const_cast<char*>(text);
+    token = text;
 
     // For alignments other than top-left, need to calculate the y position to begin drawing from
     // and the starting x position of each line.  For right-to-left text, need to determine
@@ -476,12 +477,12 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         xPos = *xPositionsIt++;
     }
 
-    token = const_cast<char*>(text);
+    token = text;
     
     int iteration = 1;
     unsigned int lineLength;
     unsigned int currentLineLength = 0;
-    char* lineStart;
+    const char* lineStart;
     std::vector<unsigned int>::const_iterator lineLengthsIt;
     if (rightToLeft)
     {
@@ -651,7 +652,7 @@ void Font::measureText(const char* text, unsigned int size, unsigned int* width,
 {
     float scale = (float)size / _size;
     const int length = strlen(text);
-    char* token = const_cast<char*>(text);
+    const char* token = text;
 
     *width = 0;
     *height = 0;
@@ -691,7 +692,7 @@ void Font::measureText(const char* text, const Rectangle& viewport, unsigned int
         hAlign = ALIGN_LEFT;
     }
 
-    char* token = const_cast<char*>(text);
+    const char* token = text;
     std::vector<bool> emptyLines;
     std::vector<Vector2> lines;
 
@@ -1027,7 +1028,7 @@ unsigned int Font::getTokenWidth(const char* token, unsigned int length, unsigne
 
 unsigned int Font::getReversedTokenLength(const char* token, const char* bufStart)
 {
-    char* cursor = const_cast<char*>(token);
+    const char* cursor = token;
     char c = cursor[0];
     unsigned int length = 0;
 
@@ -1046,7 +1047,7 @@ unsigned int Font::getReversedTokenLength(const char* token, const char* bufStar
     return length;
 }
 
-bool Font::handleDelimiters(char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
+bool Font::handleDelimiters(const char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
                       std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd)
 {
     char delimiter = *token[0];

+ 1 - 1
gameplay/src/Font.h

@@ -202,7 +202,7 @@ private:
     unsigned int getReversedTokenLength(const char* token, const char* bufStart);
 
     // Returns false if EOF was reached, true otherwise.
-    bool handleDelimiters(char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
+    bool handleDelimiters(const char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
                           std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd);
     void addLineInfo(const Rectangle& area, int lineWidth, int lineLength, Justify hAlign,
                      std::vector<int>* xPositions, std::vector<unsigned int>* lineLengths, bool rightToLeft);

+ 2 - 61
gameplay/src/Form.cpp

@@ -12,7 +12,7 @@ namespace gameplay
 {
     static std::vector<Form*> __forms;
 
-    Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL)
+    Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL), _viewport(NULL)
     {
     }
 
@@ -72,45 +72,7 @@ namespace gameplay
         form->setStyle(form->getTheme()->getStyle(styleName));
 
         // Add all the controls to the form.
-        Properties* controlSpace = formProperties->getNextNamespace();
-        while (controlSpace != NULL)
-        {
-            Control* control = NULL;
-
-            const char* controlStyleName = controlSpace->getString("style");
-            Theme::Style* controlStyle = NULL;
-            if (controlStyleName)
-            {
-                 controlStyle = form->getTheme()->getStyle(controlStyleName);
-            }
-
-            const char* controlName = controlSpace->getNamespace();
-            if (strcmp(controlName, "label") == 0)
-            {
-                control = Label::create(controlStyle, controlSpace);
-            }
-            else if (strcmp(controlName, "button") == 0)
-            {
-                control = Button::create(controlStyle, controlSpace);
-            }
-            else if (strcmp(controlName, "checkbox") == 0)
-            {
-                control = CheckBox::create(controlStyle, controlSpace);
-            }
-            else if (strcmp(controlName, "radioGroup") == 0)
-            {
-                //control = RadioGroup::create(controlStyle, controlSpace);
-            }
-
-            // Add the new control to the form.
-            if (control)
-            {
-                form->addControl(control);
-            }
-
-            // Get the next control.
-            controlSpace = formProperties->getNextNamespace();
-        }
+        form->addControls(form->getTheme(), formProperties);
 
         return form;
     }
@@ -317,27 +279,6 @@ namespace gameplay
         SAFE_RELEASE(sampler);
     }
 
-    Layout::Type Form::getLayoutType(const char* layoutString)
-    {
-        if (strcmp(layoutString, "LAYOUT_ABSOLUTE") == 0)
-        {
-            return Layout::LAYOUT_ABSOLUTE;
-        }
-        else if (strcmp(layoutString, "LAYOUT_VERTICAL") == 0)
-        {
-            return Layout::LAYOUT_VERTICAL;
-        }
-        else if (strcmp(layoutString, "LAYOUT_FLOW") == 0)
-        {
-            return Layout::LAYOUT_FLOW;
-        }
-        else
-        {
-            // Default.
-            return Layout::LAYOUT_ABSOLUTE;
-        }
-    }
-
     void Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
     {
         // Check for a collision with each Form in __forms.

+ 0 - 2
gameplay/src/Form.h

@@ -52,8 +52,6 @@ private:
     void draw(SpriteBatch* spriteBatch, const Vector2& position);
 
     static void touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
-    
-    static Layout::Type getLayoutType(const char* layoutString);
 
     Theme* _theme;              // The Theme applied to this Form.
     Model* _quad;               // Quad for rendering this Form in world-space.

+ 1 - 2
gameplay/src/Label.h

@@ -7,8 +7,6 @@
 namespace gameplay
 {
 
-    class Theme;
-
 class Label : public Control
 {
 public:
@@ -21,6 +19,7 @@ public:
     void drawText(const Vector2& position);
 
 protected:
+
     Label();
     Label(const Label& copy);
     virtual ~Label();

+ 1 - 1
gameplay/src/Layout.h

@@ -19,7 +19,7 @@ public:
 
     virtual Type getType() = 0;
 
-    virtual void update(std::vector<Control*> controls, const Vector2& size) = 0;
+    virtual void update(std::vector<Control*> controls, const Vector2& size, Theme::Style* containerStyle) = 0;
 
 protected:
     //Layout();

+ 37 - 15
gameplay/src/Properties.cpp

@@ -6,11 +6,30 @@
 namespace gameplay
 {
 
+Properties::Properties()
+{
+}
+
+Properties::Properties(const Properties& copy)
+{
+    _namespace = copy._namespace;
+    _id = copy._id;
+    _parentID = copy._parentID;
+    _properties = copy._properties;
+    
+    _namespaces = std::vector<Properties*>();
+    std::vector<Properties*>::const_iterator it;
+    for (it = copy._namespaces.begin(); it < copy._namespaces.end(); it++)
+    {
+        _namespaces.push_back(new Properties(**it));
+    }
+    rewind();
+}
+
 Properties::Properties(FILE* file)
 {
     readProperties(file);
-    _propertiesItr = _properties.end();
-    _namespacesItr = _namespaces.end();
+    rewind();
 }
 
 Properties::Properties(FILE* file, const char* name, const char* id, const char* parentID) : _namespace(name)
@@ -24,8 +43,7 @@ Properties::Properties(FILE* file, const char* name, const char* id, const char*
         _parentID = parentID;
     }
     readProperties(file);
-    _propertiesItr = _properties.end();
-    _namespacesItr = _namespaces.end();
+    rewind();
 }
 
 Properties* Properties::create(const char* filePath)
@@ -275,25 +293,29 @@ void Properties::resolveInheritance(const char* id)
 
                 // Copy the child.
                 Properties* overrides = new Properties(*derived);
-                overrides->_propertiesItr = overrides->_properties.end();
-                overrides->_namespacesItr = overrides->_namespaces.end();
+
+                // Delete the child's data.
+                unsigned int count = derived->_namespaces.size();
+                for (unsigned int i = 0; i < count; i++)
+                {
+                    SAFE_DELETE(derived->_namespaces[i]);
+                }
 
                 // Copy data from the parent into the child.
                 derived->_properties = parent->_properties;
-                derived->_propertiesItr = derived->_properties.end();
                 derived->_namespaces = std::vector<Properties*>();
-                std::vector<Properties*>::const_iterator it;
-                for (it = parent->_namespaces.begin(); it < parent->_namespaces.end(); it++)
+                std::vector<Properties*>::const_iterator itt;
+                for (itt = parent->_namespaces.begin(); itt < parent->_namespaces.end(); itt++)
                 {
-                    derived->_namespaces.push_back(new Properties(**it));
+                    derived->_namespaces.push_back(new Properties(**itt));
                 }
-                derived->_namespacesItr = derived->_namespaces.end();
+                derived->rewind();
 
                 // Take the original copy of the child and override the data copied from the parent.
                 derived->mergeWith(overrides);
 
                 // Delete the child copy.
-                delete overrides;
+                SAFE_DELETE(overrides);
             }
         }
 
@@ -316,12 +338,14 @@ void Properties::mergeWith(Properties* overrides)
 {
     // Overwrite or add each property found in child.
     char* value = new char[255];
+    overrides->rewind();
     const char* name = overrides->getNextProperty(&value);
     while (name)
     {
         this->_properties[name] = value;
         name = overrides->getNextProperty(&value);
     }
+    SAFE_DELETE(value);
     this->_propertiesItr = this->_properties.end();
 
     // Merge all common nested namespaces, add new ones.
@@ -336,7 +360,7 @@ void Properties::mergeWith(Properties* overrides)
         {
             if (strcmp(derivedNamespace->getNamespace(), overridesNamespace->getNamespace()) == 0 &&
                 strcmp(derivedNamespace->getId(), overridesNamespace->getId()) == 0)
-            {
+            {   
                 derivedNamespace->mergeWith(overridesNamespace);
                 merged = true;
             }
@@ -348,8 +372,6 @@ void Properties::mergeWith(Properties* overrides)
         {
             // Add this new namespace.
             Properties* newNamespace = new Properties(*overridesNamespace);
-            newNamespace->_propertiesItr = newNamespace->_properties.end();
-            newNamespace->_namespacesItr = newNamespace->_namespaces.end();
 
             this->_namespaces.push_back(newNamespace);
             this->_namespacesItr = this->_namespaces.end();

+ 3 - 3
gameplay/src/Properties.h

@@ -371,17 +371,17 @@ public:
 private:
     
     /**
-     * Constructor.
+     * Constructors.
      */
+    Properties();
     Properties(FILE* file);
+    Properties(const Properties& copy);
 
     /**
      * Constructor. Read from the beginning of namespace specified
      */
     Properties(FILE* file, const char* name, const char* id = NULL, const char* parentID = NULL);
 
-    //Properties(const Properties& copy);
-
     void readProperties(FILE* file);
 
     void skipWhiteSpace(FILE* file);

+ 161 - 0
gameplay/src/RadioButton.cpp

@@ -0,0 +1,161 @@
+#include "Base.h"
+#include "RadioButton.h"
+
+namespace gameplay
+{
+
+static std::vector<RadioButton*> __radioButtons;
+
+RadioButton::RadioButton() : _groupId(NULL), _selected(false)
+{
+}
+
+RadioButton::RadioButton(const RadioButton& copy)
+{
+    // Hidden.
+}
+
+RadioButton::~RadioButton()
+{
+
+}
+
+RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
+{
+    RadioButton* radioButton = new RadioButton();
+    radioButton->_style = style;
+    radioButton->_id = properties->getId();
+    properties->getVector2("position", &radioButton->_position);
+    properties->getVector2("size", &radioButton->_size);
+    radioButton->_text = properties->getString("text");
+    radioButton->_groupId = properties->getString("group");
+    if (properties->getBool("selected"))
+    {
+        RadioButton::clearSelected(radioButton->_groupId);
+        radioButton->_selected = true;
+    }
+
+    __radioButtons.push_back(radioButton);
+
+    return radioButton;
+}
+
+RadioButton* RadioButton::getRadioButton(const char* id)
+{
+    std::vector<RadioButton*>::const_iterator it;
+    for (it = __radioButtons.begin(); it < __radioButtons.end(); it++)
+    {
+        RadioButton* radioButton = *it;
+        if (strcmp(id, radioButton->getID()) == 0)
+        {
+            return radioButton;
+        }
+    }
+
+    return NULL;
+}
+
+void RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    switch (evt)
+    {
+    case Touch::TOUCH_PRESS:
+        {
+            _state = Control::STATE_ACTIVE;
+        }
+        break;
+    case Touch::TOUCH_RELEASE:
+        {
+            if (_state == Control::STATE_ACTIVE)
+            {
+                if (x > 0 && x <= _size.x &&
+                    y > 0 && y <= _size.y)
+                {
+                    if (_callback)
+                    {
+                        _callback->trigger(this);
+                    }
+                    RadioButton::clearSelected(_groupId);
+                    _selected = true;
+                }
+                setState(Control::STATE_NORMAL);
+            }
+        }
+        break;
+    }
+}
+
+void RadioButton::clearSelected(const char* groupId)
+{
+    std::vector<RadioButton*>::const_iterator it;
+    for (it = __radioButtons.begin(); it < __radioButtons.end(); it++)
+    {
+        RadioButton* radioButton = *it;
+        if (strcmp(groupId, radioButton->_groupId) == 0)
+        {
+            radioButton->_selected = false;
+        }
+    }
+}
+
+void RadioButton::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
+{
+    // Left, v-center.
+    // TODO: Set an alignment for icons.
+    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+    Theme::Icon* icon = overlay->getRadioButtonIcon();
+    if (icon)
+    {
+        Theme::Border border = _style->getBorder();
+        Theme::Padding padding = _style->getPadding();
+
+        Vector2 pos(position.x + _position.x + border.left + padding.left,
+            position.y + _position.y + (_size.y - border.bottom - padding.bottom) / 2.0f - icon->size.y / 2.0f);
+
+        if (_state == STATE_ACTIVE)
+        {
+            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->active.u1, icon->active.v1, icon->active.u2, icon->active.v2, overlay->getBorderColor());
+        }
+        else if (_selected)
+        {
+            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->on.u1, icon->on.v1, icon->on.u2, icon->on.v2, overlay->getBorderColor());
+        }
+        else
+        {
+            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->off.u1, icon->off.v1, icon->off.u2, icon->off.v2, overlay->getBorderColor());
+        }
+    }
+}
+
+void RadioButton::drawText(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::Border border = _style->getBorder();
+    Theme::Padding padding = _style->getPadding();
+
+    // Set up the text viewport.
+    float iconWidth = 0.0f;
+    if (icon)
+    {
+        iconWidth = icon->size.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->end();
+
+    _dirty = false;
+}
+
+}

+ 37 - 0
gameplay/src/RadioButton.h

@@ -0,0 +1,37 @@
+#ifndef RADIOBUTTON_H_
+#define RADIOBUTTON_H_
+
+#include "Button.h"
+#include "Theme.h"
+#include "Properties.h"
+
+namespace gameplay
+{
+
+class RadioButton : public Button
+{
+public:
+    RadioButton();
+    virtual ~RadioButton();
+
+    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);
+
+    void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
+    void drawText(const Vector2& position);
+
+private:
+    RadioButton(const RadioButton& copy);
+
+    // Clear the _selected flag of all RadioButtons in the given group.
+    static void clearSelected(const char* groupId);
+
+    const char* _groupId;
+    bool _selected;
+};
+
+}
+
+#endif

+ 0 - 29
gameplay/src/RadioGroup.h

@@ -1,29 +0,0 @@
-#ifndef RADIOGROUP_H_
-#define RADIOGROUP_H_
-
-#include "Container.h"
-#include "Theme.h"
-#include "Properties.h"
-
-namespace gameplay
-{
-
-class RadioGroup : public Container
-{
-public:
-    RadioGroup();
-    virtual ~RadioGroup();
-
-    static RadioGroup* create(Theme::Style* style, Properties* properties);
-    static RadioGroup* getRadioGroup(const char* id);
-
-private:
-    RadioGroup(const RadioGroup& copy);
-
-    unsigned int _selectedIndex;
-    std::vector<RadioButton*> _choices;
-}
-
-}
-
-#endif

+ 133 - 0
gameplay/src/Slider.cpp

@@ -0,0 +1,133 @@
+#include "Slider.h"
+
+namespace gameplay
+{
+
+static std::vector<Slider*> __sliders;
+
+Slider::Slider()
+{
+}
+
+Slider::~Slider()
+{
+}
+
+Slider* Slider::create(Theme::Style* style, Properties* properties)
+{
+    Slider* slider = new Slider();
+
+    slider->_style = style;
+    slider->_id = properties->getId();
+    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");
+    slider->_text = properties->getString("text");
+
+    __sliders.push_back(slider);
+
+    return slider;
+}
+
+Slider* Slider::create(const char* id, float min, float max, float default, float step)
+{
+    Slider* slider = new Slider();
+
+    return slider;
+}
+
+Slider* Slider::getSlider(const char* id)
+{
+    std::vector<Slider*>::const_iterator it;
+    for (it = __sliders.begin(); it < __sliders.end(); it++)
+    {
+        Slider* slider = *it;
+        if (strcmp(id, slider->getID()) == 0)
+        {
+            return slider;
+        }
+    }
+
+    return NULL;
+}
+
+void Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    Button::touchEvent(evt, x, y, contactIndex);
+
+    if (_state == STATE_ACTIVE &&
+        x > 0 && x <= _size.x &&
+        y > 0 && y <= _size.y)
+    {
+        // Horizontal case.
+        Theme::Border border = _style->getBorder();
+        Theme::Padding padding = _style->getPadding();
+
+        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+        Theme::SliderIcon* icon = overlay->getSliderIcon();
+
+        float markerPosition = ((float)x - icon->rightCapSize.x - border.left - padding.left) /
+                               (_size.x - border.left - border.right - padding.left - padding.right - icon->leftCapSize.x - icon->rightCapSize.x);
+        if (markerPosition > 1.0f)
+        {
+            markerPosition = 1.0f;
+        }
+        else if (markerPosition < 0.0f)
+        {
+            markerPosition = 0.0f;
+        }
+
+        _value = markerPosition * _max;
+        if (_step > 0.0f)
+        {
+            float stepDistance = _step / (_max - _min);
+            
+            int numSteps = round(_value / _step);
+            _value = _step * numSteps;
+        }
+    }
+}
+
+void Slider::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
+{
+    // TODO: Vertical slider.
+
+    // The slider is drawn in the center of the control (perpendicular to orientation).
+    // The track is stretched according to orientation.
+    // Caps and marker are not stretched.
+    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+    Theme::SliderIcon* icon = overlay->getSliderIcon();
+    if (icon)
+    {
+        Theme::Border border = _style->getBorder();
+        Theme::Padding padding = _style->getPadding();
+
+        // Draw order: track, caps, marker.
+        float midY = position.y + _position.y + (_size.y - border.bottom - padding.bottom) / 2.0f;
+        Vector2 pos(position.x + _position.x + border.left + padding.left, midY - icon->trackSize.y / 2.0f);
+        spriteBatch->draw(pos.x, pos.y, _size.x, icon->trackSize.y, icon->track.u1, icon->track.v1, icon->track.u2, icon->track.v2, overlay->getBorderColor());
+
+        pos.y = midY - icon->leftCapSize.y / 2.0f;
+        spriteBatch->draw(pos.x, pos.y, icon->leftCapSize.x, icon->leftCapSize.y, icon->leftCap.u1, icon->leftCap.v1, icon->leftCap.u2, icon->leftCap.v2, overlay->getBorderColor());
+        
+        pos.x = position.x + _position.x + _size.x - border.left - padding.left - icon->rightCapSize.x;
+        spriteBatch->draw(pos.x, pos.y, icon->rightCapSize.x, icon->rightCapSize.y, icon->rightCap.u1, icon->rightCap.v1, icon->rightCap.u2, icon->rightCap.v2, overlay->getBorderColor());
+
+        // Percent across.
+        float markerPosition = _value / (_max - _min);
+        markerPosition *= _size.x - border.left - padding.left - border.right - padding.right - icon->leftCapSize.x - icon->rightCapSize.x - icon->markerSize.x;
+        pos.x = position.x + _position.x + border.left + padding.left + icon->leftCapSize.x + markerPosition;
+        pos.y = midY - icon->markerSize.y / 2.0f;
+        spriteBatch->draw(pos.x, pos.y, icon->markerSize.x, icon->markerSize.y, icon->marker.u1, icon->marker.v1, icon->marker.u2, icon->marker.v2, overlay->getBorderColor());
+    }
+}
+
+void Slider::drawText(const Vector2& position)
+{
+
+}
+
+}

+ 42 - 0
gameplay/src/Slider.h

@@ -0,0 +1,42 @@
+#ifndef SLIDER_H_
+#define SLIDER_H_
+
+#include "Base.h"
+#include "Theme.h"
+#include "Properties.h"
+#include "Button.h"
+#include "Touch.h"
+
+namespace gameplay
+{
+
+class Slider : public Button
+{
+public:
+
+    Slider();
+    ~Slider();
+
+    static Slider* create(Theme::Style* style, Properties* properties);
+    static Slider* create(const char* id, float min, float max, float default = 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 drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
+    void drawText(const Vector2& position);
+
+    void setValue(float value);
+    float getValue();
+
+private:
+
+    float _min;
+    float _max;
+    float _step;
+    float _value;
+};
+
+}
+
+#endif

+ 256 - 57
gameplay/src/Theme.cpp

@@ -21,31 +21,34 @@ namespace gameplay
     Theme::~Theme()
     {
         // Destroy all the cursors, styles and , fonts.
-        for (unsigned int i = 0, count = _cursors.size(); i < count; ++i)
+        for (unsigned int i = 0, count = _styles.size(); i < count; ++i)
         {
-            Cursor* cursor = _cursors[i];
-            if (cursor)
-            {
-                delete cursor;
-            }
+            Style* style = _styles[i];
+            SAFE_DELETE(style);
         }
 
-        for (unsigned int i = 0, count = _styles.size(); i < count; ++i)
+        for (unsigned int i = 0, count = _cursors.size(); i < count; ++i)
         {
-            Style* style = _styles[i];
-            if (style)
-            {
-                delete style;
-            }
+            Cursor* cursor = _cursors[i];
+            SAFE_DELETE(cursor);
         }
 
         for (unsigned int i = 0, count = _fonts.size(); i < count; ++i)
         {
             Font* font = _fonts[i];
-            if (font)
-            {
-                SAFE_RELEASE(font);
-            }
+            SAFE_RELEASE(font);
+        }
+
+        for (unsigned int i = 0, count = _icons.size(); i < count; ++i)
+        {
+            Icon* icon = _icons[i];
+            SAFE_RELEASE(icon);
+        }
+
+        for (unsigned int i = 0, count = _sliders.size(); i < count; ++i)
+        {
+            SliderIcon* slider = _sliders[i];
+            SAFE_RELEASE(slider);
         }
 
         SAFE_DELETE(_spriteBatch);
@@ -60,6 +63,9 @@ namespace gameplay
 
     Theme::Style::Overlay::~Overlay()
     {
+        //SAFE_RELEASE(_cursor);
+        SAFE_RELEASE(_checkBoxIcon);
+        SAFE_RELEASE(_radioButtonIcon);
         SAFE_RELEASE(_font);
     }
 
@@ -67,7 +73,7 @@ namespace gameplay
     {
         for (int i = 0; i < MAX_OVERLAYS; i++)
         {
-            SAFE_DELETE(_overlays[i]);
+            SAFE_RELEASE(_overlays[i]);
         }
     }
 
@@ -131,41 +137,73 @@ namespace gameplay
                 Theme::Cursor* c = new Theme::Cursor(space->getId(), region, color);
                 theme->_cursors.push_back(c);
             }
-            else if (strcmp(spacename, "checkBox") == 0)
+            else if (strcmp(spacename, "icon") == 0)
             {
-                Vector2 unchecked;
-                space->getVector2("uncheckedPosition", &unchecked);
-                Vector2 checked;
-                space->getVector2("checkedPosition", &checked);
+                Vector2 offVec;
+                Vector2 onVec;
                 Vector2 size;
+                Vector2 activeVec;
+                space->getVector2("offPosition", &offVec);
+                space->getVector2("onPosition", &onVec);
+                space->getVector2("activePosition", &activeVec);
                 space->getVector2("size", &size);
 
-                float tw = 1.0f / theme->_texture->getWidth();
-                float th = 1.0f / theme->_texture->getHeight();
+                float tw = theme->_texture->getWidth();
+                float th = theme->_texture->getHeight();
 
                 UVs on;
-                on.u1 = checked.x * tw;
-                on.u2 = (checked.x + size.x) * tw;
-                on.v1 = 1.0f - (checked.y * tw);
-                on.v2 = 1.0f - ((checked.y + size.y) * tw);
-
                 UVs off;
-                off.u1 = unchecked.x * tw;
-                off.u2 = (unchecked.x + size.x) * tw;
-                off.v1 = 1.0f - (unchecked.y * tw);
-                off.v2 = 1.0f - ((unchecked.y + size.y) * tw);
+                UVs active;
+                generateUVs(onVec.x, onVec.y, size.x, size.y, tw, th, &on);
+                generateUVs(offVec.x, offVec.y, size.x, size.y, tw, th, &off);
+                generateUVs(activeVec.x, activeVec.y, size.x, size.y, tw, th, &active);
 
-                CheckBoxIcon* icon = new CheckBoxIcon();
+                Icon* icon = new Icon();
                 icon->id = space->getId();
                 icon->on = on;
                 icon->off = off;
+                icon->active = active;
                 icon->size = size;
 
-                theme->_checkBoxIcons.push_back(icon);
+                theme->_icons.push_back(icon);
             }
-            else if (strcmp(spacename, "radiobutton") == 0)
+            else if (strcmp(spacename, "slider") == 0)
             {
-
+                Vector4 leftCapRegion;
+                Vector4 rightCapRegion;
+                Vector4 trackRegion;
+                Vector4 markerRegion;
+                space->getVector4("leftCapRegion", &leftCapRegion);
+                space->getVector4("rightCapRegion", &rightCapRegion);
+                space->getVector4("trackRegion", &trackRegion);
+                space->getVector4("markerRegion", &markerRegion);
+
+                float tw = theme->_texture->getWidth();
+                float th = theme->_texture->getHeight();
+
+                // Vec4f regions:
+                // .z == width, .w == height
+                UVs leftCap;
+                UVs rightCap;
+                UVs track;
+                UVs marker;
+                generateUVs(leftCapRegion.x, leftCapRegion.y, leftCapRegion.z, leftCapRegion.w, tw, th, &leftCap);
+                generateUVs(rightCapRegion.x, rightCapRegion.y, rightCapRegion.z, rightCapRegion.w, tw, th, &rightCap);
+                generateUVs(trackRegion.x, trackRegion.y, trackRegion.z, trackRegion.w, tw, th, &track);
+                generateUVs(markerRegion.x, markerRegion.y, markerRegion.z, markerRegion.w, tw, th, &marker);
+
+                SliderIcon* sliderIcon = new SliderIcon();
+                sliderIcon->id = space->getId();
+                sliderIcon->leftCap = leftCap;
+                sliderIcon->rightCap = rightCap;
+                sliderIcon->track = track;
+                sliderIcon->marker = marker;
+                sliderIcon->leftCapSize.set(leftCapRegion.z, leftCapRegion.w);
+                sliderIcon->rightCapSize.set(rightCapRegion.z, rightCapRegion.w);
+                sliderIcon->trackSize.set(trackRegion.z, trackRegion.w);
+                sliderIcon->markerSize.set(markerRegion.z, markerRegion.w);
+
+                theme->_sliders.push_back(sliderIcon);
             }
 
             space = themeProperties->getNextNamespace();
@@ -220,14 +258,42 @@ namespace gameplay
                         bool rightToLeft = innerSpace->getBool("rightToLeft");
 
                         const char* checkBoxString = innerSpace->getString("checkBox");
-                        CheckBoxIcon* icon = NULL;
+                        Icon* checkBoxIcon = NULL;
                         if (checkBoxString)
                         {
-                            for (unsigned int i = 0; i < theme->_checkBoxIcons.size(); i++)
+                            for (unsigned int i = 0; i < theme->_icons.size(); i++)
                             {
-                                if (strcmp(theme->_checkBoxIcons[i]->id.c_str(), checkBoxString) == 0)
+                                if (strcmp(theme->_icons[i]->id.c_str(), checkBoxString) == 0)
                                 {
-                                    icon = theme->_checkBoxIcons[i];
+                                    checkBoxIcon = theme->_icons[i];
+                                    break;
+                                }
+                            }
+                        }
+
+                        const char* radioButtonString = innerSpace->getString("radioButton");
+                        Icon* radioButtonIcon = NULL;
+                        if (radioButtonString)
+                        {
+                            for (unsigned int i = 0; i < theme->_icons.size(); i++)
+                            {
+                                if (strcmp(theme->_icons[i]->id.c_str(), radioButtonString) == 0)
+                                {
+                                    radioButtonIcon = theme->_icons[i];
+                                    break;
+                                }
+                            }
+                        }
+
+                        const char* sliderString = innerSpace->getString("slider");
+                        SliderIcon* sliderIcon = NULL;
+                        if (sliderString)
+                        {
+                            for (unsigned int i = 0; i < theme->_sliders.size(); ++i)
+                            {
+                                if (strcmp(theme->_sliders[i]->id.c_str(), sliderString) == 0)
+                                {
+                                    sliderIcon = theme->_sliders[i];
                                     break;
                                 }
                             }
@@ -243,7 +309,9 @@ namespace gameplay
                         normal->setFontSize(fontSize);
                         normal->setTextAlignment(alignment);
                         normal->setTextRightToLeft(rightToLeft);
-                        normal->setCheckBoxIcon(icon);
+                        normal->setCheckBoxIcon(checkBoxIcon);
+                        normal->setRadioButtonIcon(radioButtonIcon);
+                        normal->setSliderIcon(sliderIcon);
 
                         // Done with this pass.
                         break;
@@ -351,14 +419,14 @@ namespace gameplay
                         }
 
                         const char* checkBoxString = innerSpace->getString("checkBox");
-                        CheckBoxIcon* checkBoxIcon = NULL;
+                        Icon* checkBoxIcon = NULL;
                         if (checkBoxString)
                         {
-                            for (unsigned int i = 0; i < theme->_checkBoxIcons.size(); i++)
+                            for (unsigned int i = 0; i < theme->_icons.size(); i++)
                             {
-                                if (strcmp(theme->_checkBoxIcons[i]->id.c_str(), checkBoxString) == 0)
+                                if (strcmp(theme->_icons[i]->id.c_str(), checkBoxString) == 0)
                                 {
-                                    checkBoxIcon = theme->_checkBoxIcons[i];
+                                    checkBoxIcon = theme->_icons[i];
                                     break;
                                 }
                             }
@@ -368,6 +436,42 @@ namespace gameplay
                             checkBoxIcon = normal->getCheckBoxIcon();
                         }
 
+                        const char* radioButtonString = innerSpace->getString("radioButton");
+                        Icon* radioButtonIcon = NULL;
+                        if (radioButtonString)
+                        {
+                            for (unsigned int i = 0; i < theme->_icons.size(); i++)
+                            {
+                                if (strcmp(theme->_icons[i]->id.c_str(), radioButtonString) == 0)
+                                {
+                                    radioButtonIcon = theme->_icons[i];
+                                    break;
+                                }
+                            }
+                        }
+                        if (!radioButtonIcon)
+                        {
+                            radioButtonIcon = normal->getRadioButtonIcon();
+                        }
+
+                        const char* sliderString = innerSpace->getString("slider");
+                        SliderIcon* sliderIcon = NULL;
+                        if (sliderString)
+                        {
+                            for (unsigned int i = 0; i < theme->_sliders.size(); ++i)
+                            {
+                                if (strcmp(theme->_sliders[i]->id.c_str(), sliderString) == 0)
+                                {
+                                    sliderIcon = theme->_sliders[i];
+                                    break;
+                                }
+                            }
+                        }
+                        if (!sliderIcon)
+                        {
+                            sliderIcon = normal->getSliderIcon();
+                        }
+
                         // TODO: Cursor.
                         
                         if (strcmp(innerSpacename, "focus") == 0)
@@ -381,6 +485,8 @@ namespace gameplay
                             focus->setTextAlignment(alignment);
                             focus->setTextRightToLeft(rightToLeft);
                             focus->setCheckBoxIcon(checkBoxIcon);
+                            focus->setRadioButtonIcon(radioButtonIcon);
+                            focus->setSliderIcon(sliderIcon);
                         }
                         else if (strcmp(innerSpacename, "active") == 0)
                         {
@@ -393,6 +499,8 @@ namespace gameplay
                             active->setTextAlignment(alignment);
                             active->setTextRightToLeft(rightToLeft);
                             active->setCheckBoxIcon(checkBoxIcon);
+                            active->setRadioButtonIcon(radioButtonIcon);
+                            active->setSliderIcon(sliderIcon);
                         }
                     }
 
@@ -411,6 +519,7 @@ namespace gameplay
                 else
                 {
                     focus = normal;
+                    focus->addRef();
                 }
                 if (active)
                 {
@@ -419,6 +528,7 @@ namespace gameplay
                 else
                 {
                     active = normal;
+                    active->addRef();
                 }
 
                 Theme::Style* s = new Theme::Style(space->getId(), margin, border, padding, normal, focus, active);
@@ -456,8 +566,6 @@ namespace gameplay
             Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active)
         : _id(id), _margin(margin), _border(border), _padding(padding)
     {
-        // 
-
         _overlays[OVERLAY_NORMAL] = normal;
         _overlays[OVERLAY_FOCUS] = focus;
         _overlays[OVERLAY_ACTIVE] = active;
@@ -508,12 +616,12 @@ namespace gameplay
         return _padding;
     }
 
-    Theme::Style::Overlay::Overlay()
+    Theme::Style::Overlay::Overlay() : _cursor(NULL), _checkBoxIcon(NULL), _radioButtonIcon(NULL), _font(NULL)
     {
     }
 
     Theme::Style::Overlay::Overlay(Theme::Style::OverlayType type)
-        : _type(type)
+        : _cursor(NULL), _checkBoxIcon(NULL), _radioButtonIcon(NULL), _sliderIcon(NULL), _font(NULL), _type(type)
     {
     }
 
@@ -613,11 +721,16 @@ namespace gameplay
 
     void Theme::Style::Overlay::setFont(Font* font)
     {
-        _font = font;
-
-        if (_font)
+        if (_font != font)
         {
-            font->addRef();
+            SAFE_RELEASE(_font);
+
+            _font = font;
+
+            if (font)
+            {
+                font->addRef();
+            }
         }
     }
 
@@ -682,13 +795,99 @@ namespace gameplay
         _borderColor = color;
     }
 
-    void Theme::Style::Overlay::setCheckBoxIcon(CheckBoxIcon* icon)
+    void Theme::Style::Overlay::setCheckBoxIcon(Icon* icon)
     {
-        _checkBoxIcon = icon;
+        if (_checkBoxIcon != icon)
+        {
+            SAFE_RELEASE(_checkBoxIcon);
+
+            _checkBoxIcon = icon;
+
+            if (icon)
+            {
+                icon->addRef();
+            }
+        }
     }
 
-    Theme::CheckBoxIcon* Theme::Style::Overlay::getCheckBoxIcon()
+    Theme::Icon* Theme::Style::Overlay::getCheckBoxIcon()
     {
         return _checkBoxIcon;
     }
+
+    void Theme::Style::Overlay::setRadioButtonIcon(Icon* icon)
+    {
+        if (_radioButtonIcon != icon)
+        {
+            SAFE_RELEASE(_radioButtonIcon);
+
+            _radioButtonIcon = icon;
+
+            if (icon)
+            {
+                icon->addRef();
+            }
+        }
+    }
+
+    Theme::Icon* Theme::Style::Overlay::getRadioButtonIcon()
+    {
+        return _radioButtonIcon;
+    }
+
+    void Theme::Style::Overlay::setSliderIcon(SliderIcon* slider)
+    {
+        if (_sliderIcon != slider)
+        {
+            SAFE_RELEASE(_sliderIcon);
+
+            _sliderIcon = slider;
+
+            if (slider)
+            {
+                slider->addRef();
+            }
+        }
+    }
+
+    Theme::SliderIcon* Theme::Style::Overlay::getSliderIcon()
+    {
+        return _sliderIcon;
+    }
+
+    void Theme::Style::Overlay::addRef()
+    {
+        Ref::addRef();
+        
+        if (_font)
+        {
+            _font->addRef();
+        }
+        
+        if (_radioButtonIcon)
+        {
+            _radioButtonIcon->addRef();
+        }
+        
+        if (_checkBoxIcon)
+        {
+            _checkBoxIcon->addRef();
+        }
+
+        if (_sliderIcon)
+        {
+            _sliderIcon->addRef();
+        }
+    }
+
+    void Theme::generateUVs(float x, float y, float width, float height, float textureWidth, float textureHeight, UVs* uvs)
+    {
+        float tw = 1.0f / textureWidth;
+        float th = 1.0f / textureHeight;
+
+        uvs->u1 = x * tw;
+        uvs->u2 = (x + width) * tw;
+        uvs->v1 = 1.0f - (y * th);
+        uvs->v2 = 1.0f - ((y + height) * th);
+    }
 }

+ 65 - 12
gameplay/src/Theme.h

@@ -17,6 +17,7 @@ namespace gameplay
 
 #define MAX_OVERLAYS 3
 #define MAX_OVERLAY_REGIONS 9
+
 /**
  * This class represents the apperance of an UI form described in a 
  * theme file. A theme file, at its simplest, contains a source texture atlas,
@@ -45,13 +46,29 @@ public:
         float right;
     } Margin, Border, Padding;
 
-    typedef struct icon
+    class Icon : public Ref
     {
+    public:
         std::string id;
         UVs off;
         UVs on;
+        UVs active;
         Vector2 size;
-    } CheckBoxIcon, RadioButtonIcon;
+    };
+
+    class SliderIcon : public Ref
+    {
+    public:
+        std::string id;
+        UVs leftCap;
+        UVs rightCap;
+        UVs track;
+        UVs marker;
+        Vector2 leftCapSize;
+        Vector2 rightCapSize;
+        Vector2 trackSize;
+        Vector2 markerSize;
+    };
 
     /**
      * Creates an instance of a Theme from a theme file.
@@ -135,24 +152,47 @@ public:
         Theme::Style::Overlay* getOverlay(OverlayType overlayType) const;
 
         /**
-         * Gets the Border region of this overlay.
+         * Gets the Border region of this style.
          */
         const Theme::Border& getBorder() const;
 
         /**
-         * Gets the Padding region of this overlay.
+         * Gets the Padding region of this style.
          */
         const Theme::Padding& getPadding() const;
 
         /**
-         * Gets the Margin region of this overlay.
+         * Gets the Margin region of this style.
          */
         const Theme::Margin& getMargin() const;
+
+        /**
+         * Set the size of this Style's border.
+         *
+         * When auto-size is set on width and/or height:
+         * Space added to the calculated (tightly bound) width and height.
+         *
+         */
+        void setBorder(float top, float bottom, float left, float right);
+
+        /**
+         * Set this size of this Style's padding.
+         *
+         * Padding is the space between a Control's content (all icons and text) and its border.
+         */
+        void setPadding(float top, float bottom, float left, float right);
+
+        /**
+         * Set the size of this Style's margin.
+         *
+         * The margin is used by Layouts other than AbsoluteLayout to put space between Controls.
+         */
+        void setMargin(float top, float bottom, float left, float right);
        
         /**
          * This class represents a control's overlay for one of the 3 modes: normal, focussed or active.
          */
-        class Overlay
+        class Overlay : public Ref
         {
         public:
             Overlay();
@@ -223,18 +263,28 @@ public:
 
             void setBorderColor(const Vector4& color);
             
-            void setCheckBoxIcon(CheckBoxIcon* icon);
+            void setCheckBoxIcon(Icon* icon);
+
+            Icon* getCheckBoxIcon();
 
-            CheckBoxIcon* getCheckBoxIcon();
+            void setRadioButtonIcon(Icon* icon);
+
+            Icon* getRadioButtonIcon();
+
+            void setSliderIcon(SliderIcon* slider);
+
+            SliderIcon* getSliderIcon();
+
+            void addRef();
         
         private:
-           
             OverlayType _type;
             Rectangle _region;
             UVs _uvs[MAX_OVERLAY_REGIONS];
             Cursor* _cursor;
-            CheckBoxIcon* _checkBoxIcon;
-            RadioButtonIcon* _radioButtonIcon;
+            Icon* _checkBoxIcon;
+            Icon* _radioButtonIcon;
+            SliderIcon* _sliderIcon;
             Font* _font;
             unsigned int _fontSize;
             Font::Justify _alignment;
@@ -268,13 +318,16 @@ private:
      */
     ~Theme();
 
+    static void generateUVs(float x, float y, float width, float height, float textureWidth, float textureHeight, UVs* uvs);
+
     std::string _path;
     Texture* _texture;
     SpriteBatch* _spriteBatch;
     std::vector<Cursor *> _cursors;
     std::vector<Style *> _styles;
     std::vector<Font *> _fonts;
-    std::vector<CheckBoxIcon *> _checkBoxIcons;
+    std::vector<Icon*> _icons;
+    std::vector<SliderIcon*> _sliders;
 };
 
 }

+ 5 - 4
gameplay/src/VerticalLayout.cpp

@@ -3,7 +3,7 @@
 
 namespace gameplay
 {
-    VerticalLayout::VerticalLayout()
+    VerticalLayout::VerticalLayout() : _bottomToTop(false)
     {
     }
 
@@ -31,9 +31,10 @@ namespace gameplay
         return Layout::LAYOUT_VERTICAL;
     }
 
-    void VerticalLayout::update(std::vector<Control*> controls, const Vector2& size)
+    void VerticalLayout::update(std::vector<Control*> controls, const Vector2& size, Theme::Style* containerStyle)
     {
-        float yPosition = 0.0f;
+        float yPosition = containerStyle->getBorder().top + containerStyle->getPadding().top;
+        float xPosition = containerStyle->getBorder().left + containerStyle->getPadding().left;
 
         unsigned int i, end, iter;
         if (_bottomToTop)
@@ -57,7 +58,7 @@ namespace gameplay
 
             yPosition += margin.top;
 
-            control->setPosition(0.0f, yPosition);
+            control->setPosition(xPosition, yPosition);
 
             yPosition += size.y + margin.bottom;
 

+ 1 - 1
gameplay/src/VerticalLayout.h

@@ -15,7 +15,7 @@ public:
 
     Layout::Type getType();
 
-    void update(std::vector<Control*> controls, const Vector2& size);
+    void update(std::vector<Control*> controls, const Vector2& size, Theme::Style* containerStyle);
 
 private:
     VerticalLayout();