Răsfoiți Sursa

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

Latest UI API features.
Sean Paul Taylor 13 ani în urmă
părinte
comite
3466f2fde0

+ 1 - 1
gameplay/src/AnimationTarget.h

@@ -91,7 +91,7 @@ protected:
 
     TargetType _targetType;             // The type of target this is.
 
-    char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.
+    unsigned char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.
 
 private:
 

+ 67 - 40
gameplay/src/CheckBox.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "CheckBox.h"
+#include "Game.h"
 
 namespace gameplay
 {
@@ -22,7 +23,7 @@ CheckBox* CheckBox::create(Theme::Style* style, Properties* properties)
 {
     CheckBox* checkBox = new CheckBox();
     checkBox->init(style, properties);
-    properties->getVector2("iconSize", &checkBox->_iconSize);
+    properties->getVector2("iconSize", &checkBox->_imageSize);
     checkBox->_checked = properties->getBool("checked");
 
     return checkBox;
@@ -33,29 +34,22 @@ bool CheckBox::isChecked()
     return _checked;
 }
 
-void CheckBox::setIconSize(float width, float height)
+void CheckBox::setImageSize(float width, float height)
 {
-    _iconSize.set(width, height);
+    _imageSize.set(width, height);
 }
 
-const Vector2& CheckBox::getIconSize() const
+const Vector2& CheckBox::getImageSize() const
 {
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    if (_iconSize.isZero() && icon)
-    {
-        return icon->getSize();
-    }
-
-    return _iconSize;
+    return _imageSize;
 }
 
 void CheckBox::addListener(Control::Listener* listener, int eventFlags)
 {
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
         assert("TEXT_CHANGED event is not applicable to CheckBox.");
-        eventFlags &= ~Listener::TEXT_CHANGED;
+        eventFlags &= ~Control::Listener::TEXT_CHANGED;
     }
 
     Control::addListener(listener, eventFlags);
@@ -78,7 +72,15 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
                     y > 0 && y <= _bounds.height)
                 {
                     _checked = !_checked;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
+
+                    // Animate between icons.  Old fades out, then the new fades in.
+                    /*
+                    AnimationController* animationController = Game::getInstance()->getAnimationController();
+                    float from[1] = { 1.0f };
+                    float to[1] = { 0.0f };
+                    animationController->createAnimationFromTo("CheckBox::toggle", this, CheckBox::ANIMATE_SPRITE_ALPHA, from, to, Curve::QUADRATIC_IN_OUT, 200L);
+                    */
                 }
             }
         }
@@ -92,12 +94,23 @@ void CheckBox::update(const Rectangle& clip)
 {
     Control::update(clip);
 
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    Vector2& size = _iconSize;
-    if (_iconSize.isZero() && icon)
+    Vector2 size;
+    if (_imageSize.isZero())
+    {
+        if (_checked)
+        {
+            const Rectangle& selectedRegion = getImageRegion("checked", _state);
+            size.set(selectedRegion.width, selectedRegion.height);
+        }
+        else
+        {
+            const Rectangle& unselectedRegion = getImageRegion("unchecked", _state);
+            size.set(unselectedRegion.width, unselectedRegion.height);
+        }
+    }
+    else
     {
-        size = icon->getSize();
+        size.set(_imageSize);
     }
     float iconWidth = size.x;
 
@@ -105,43 +118,57 @@ void CheckBox::update(const Rectangle& clip)
     _textBounds.width -= iconWidth;
 }
 
-void CheckBox::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     // Left, v-center.
     // TODO: Set an alignment for icons.
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    if (icon)
+    const Theme::Border border = getBorder(_state);
+    const Theme::Padding padding = _style->getPadding();
+    float opacity = getOpacity(_state);
+
+    if (_checked)
     {
-        const Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        Theme::Border border;
-        if (containerRegion)
+        const Rectangle& selectedRegion = getImageRegion("checked", _state);
+        const Theme::UVs& selected = getImageUVs("checked", _state);
+        Vector4 selectedColor = getImageColor("checked", _state);
+        selectedColor.w *= opacity;
+
+        Vector2 size;
+        if (_imageSize.isZero())
         {
-                border = containerRegion->getBorder();
+            size.set(selectedRegion.width, selectedRegion.height);
         }
-        const Theme::Padding padding = _style->getPadding();
-
-        Vector2& size = _iconSize;
-        if (_iconSize.isZero())
+        else
         {
-            size = icon->getSize();
+            size.set(_imageSize);
         }
 
-        const Vector4 color = icon->getColor();
-
         Vector2 pos(clip.x + _position.x + border.left + padding.left,
             clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
-        if (_checked)
+        spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
+    }
+    else
+    {
+        const Rectangle& unselectedRegion = getImageRegion("unchecked", _state);
+        const Theme::UVs& unselected = getImageUVs("unchecked", _state);
+        Vector4 unselectedColor = getImageColor("unchecked", _state);
+        unselectedColor.w *= opacity;
+
+        Vector2 size;
+        if (_imageSize.isZero())
         {
-            const Theme::UVs on = icon->getOnUVs();
-            spriteBatch->draw(pos.x, pos.y, size.x, size.y, on.u1, on.v1, on.u2, on.v2, color, _clip);
+            size.set(unselectedRegion.width, unselectedRegion.height);
         }
         else
         {
-            const Theme::UVs off = icon->getOffUVs();
-            spriteBatch->draw(pos.x, pos.y, size.x, size.y, off.u1, off.v1, off.u2, off.v2, color, _clip);
+            size.set(_imageSize);
         }
+
+        Vector2 pos(clip.x + _position.x + border.left + padding.left,
+            clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+
+        spriteBatch->draw(pos.x, pos.y, size.x, size.y, unselected.u1, unselected.v1, unselected.u2, unselected.v2, unselectedColor, _clip);
     }
 }
 

+ 7 - 5
gameplay/src/CheckBox.h

@@ -24,7 +24,7 @@ namespace gameplay
  *      iconSize    = <width, height>   // The size to draw the checkbox icon, if different from its size in the texture.
  * }
  */
-class CheckBox : public Button
+class CheckBox : public Button //, public AnimationClip::Listener
 {
     friend class Container;
 
@@ -42,14 +42,14 @@ public:
      * @param width The width to draw the checkbox icon.
      * @param height The height to draw the checkbox icon.
      */
-    void setIconSize(float width, float height);
+    void setImageSize(float width, float height);
 
     /**
      * Get the size at which the checkbox icon will be drawn.
      *
      * @return The size of the checkbox icon.
      */
-    const Vector2& getIconSize() const;
+    const Vector2& getImageSize() const;
 
     /**
      * Add a listener to be notified of specific events affecting
@@ -63,6 +63,8 @@ public:
      */
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
+  //  virtual void animationEvent(AnimationClip* clip, EventType type);
+
 protected:
     CheckBox();
     ~CheckBox();
@@ -105,10 +107,10 @@ protected:
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param position The container position this control is relative to.
      */
-    void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     bool _checked;      // Whether this checkbox is currently checked.
-    Vector2 _iconSize;  // The size to draw the checkbox icon, if different from its size in the texture.
+    Vector2 _imageSize;  // The size to draw the checkbox icon, if different from its size in the texture.
 
 private:
     CheckBox(const CheckBox& copy);

+ 4 - 14
gameplay/src/Container.cpp

@@ -219,8 +219,6 @@ namespace gameplay
         Control::drawBorder(spriteBatch, clip);
 
         // Now call drawBorder on all controls within this container.
-        //Vector2 pos(clip.x + _position.x, clip.y + _position.y);
-        //const Rectangle newClip(clip.x + _position.x, clip.y + _position.y, _size.x, _size.y);
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
@@ -229,14 +227,13 @@ namespace gameplay
         }
     }
 
-    void Container::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+    void Container::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     {
-        //const Rectangle newClip(clip.x + _position.x, clip.y + _position.y, _size.x, _size.y);
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
             Control* control = *it;
-            control->drawSprites(spriteBatch, _clip);
+            control->drawImages(spriteBatch, _clip);
         }
 
         _dirty = false;
@@ -244,7 +241,6 @@ namespace gameplay
 
     void Container::drawText(const Rectangle& clip)
     {
-        //const Rectangle newClip(clip.x + _position.x, clip.y + _position.y, _size.x, _size.y);
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
@@ -285,14 +281,8 @@ namespace gameplay
 
         bool eventConsumed = false;
 
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        if (containerRegion)
-        {
-            border = overlay->getContainerRegion()->getBorder();
-        }
-        Theme::Padding padding = _style->getPadding();
+        const Theme::Border& border = getBorder(_state);
+        const Theme::Padding& padding = getPadding();
         float xPos = border.left + padding.left;
         float yPos = border.top + padding.top;
 

+ 1 - 1
gameplay/src/Container.h

@@ -146,7 +146,7 @@ protected:
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param clip The clipping rectangle of this container's parent container.
      */
-    virtual void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     /**
      * Draws the text of all controls within this container.

+ 609 - 70
gameplay/src/Control.cpp

@@ -1,11 +1,12 @@
 #include "Base.h"
+#include "Game.h"
 #include "Control.h"
 
 namespace gameplay
 {
     Control::Control()
         : _id(""), _state(Control::NORMAL), _position(Vector2::zero()), _size(Vector2::zero()), _bounds(Rectangle::empty()), _clip(Rectangle::empty()),
-          _autoWidth(true), _autoHeight(true), _dirty(true), _consumeTouchEvents(true), _listeners(NULL)
+            _autoWidth(true), _autoHeight(true), _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false)
     {
     }
 
@@ -24,6 +25,11 @@ namespace gameplay
             }
             SAFE_DELETE(_listeners);
         }
+
+        if (_styleOverridden)
+        {
+            SAFE_DELETE(_style);
+        }
     }
 
     void Control::init(Theme::Style* style, Properties* properties)
@@ -47,9 +53,24 @@ namespace gameplay
         return _id.c_str();
     }
 
-    void Control::setPosition(float x, float y)
+    void Control::setPosition(float x, float y, unsigned long duration)
     {
-        _position.set(x, y);
+        if (duration > 0L)
+        {
+            AnimationController* animationController = Game::getInstance()->getAnimationController();
+            float from[2] = { _position.x, _position.y };
+            float to[2] = { x, y };
+            Animation* moveAnimation = animationController->createAnimationFromTo("Control::setPosition", this, Control::ANIMATE_POSITION,
+                from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration);
+            AnimationClip* clip = moveAnimation->getClip();
+            clip->play();
+        }
+        else
+        {
+            _position.set(x, y);
+        }
+
+        _dirty = true;
     }
 
     const Vector2& Control::getPosition() const
@@ -57,9 +78,24 @@ namespace gameplay
         return _position;
     }
 
-    void Control::setSize(float width, float height)
+    void Control::setSize(float width, float height, unsigned long duration)
     {
-        _size.set(width, height);
+        if (duration > 0L)
+        {
+            AnimationController* animationController = Game::getInstance()->getAnimationController();
+            float from[2] = { _size.x, _size.y };
+            float to[2] = { width, height };
+            Animation* resizeAnimation = animationController->createAnimationFromTo("Control::setSize", this, Control::ANIMATE_SIZE,
+                from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration);
+            AnimationClip* clip = resizeAnimation->getClip();
+            clip->play();
+        }
+        else
+        {
+            _size.set(width, height);
+        }
+
+        _dirty = true;
     }
 
     const Vector2& Control::getSize() const
@@ -67,6 +103,312 @@ namespace gameplay
         return _size;
     }
 
+    void Control::setOpacity(float opacity, unsigned char states, unsigned long duration)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            if (duration > 0L)
+            {
+                float from[1] = { overlays[i]->getOpacity() };
+                float to[1] = { opacity };
+
+                // Fun with chaining.
+                Game::getInstance()->getAnimationController()->createAnimationFromTo("Overlay::setOpacity", overlays[i], Theme::Style::Overlay::ANIMATE_OPACITY,
+                    from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration)->getClip()->play();
+            }
+            else
+            {
+                overlays[i]->setOpacity(opacity);
+            }
+        }
+        
+        if (duration > 0L)
+        {
+            // All this animation does is make sure this control sets its dirty flag during the animation.
+            float from[1] = { 0.0f };
+            float to[1] = { 1.0f };
+
+            Game::getInstance()->getAnimationController()->createAnimationFromTo("Control::setOpacity", this, Control::ANIMATE_OPACITY,
+                from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration)->getClip()->play();
+        }
+        
+        _dirty = true;
+    }
+
+    float Control::getOpacity(State state) const
+    {
+        return getOverlay(state)->getOpacity();
+    }
+
+    void Control::setBorder(float top, float bottom, float left, float right, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setBorder(top, bottom, left, right);
+        }
+
+        _dirty = true;
+    }
+
+    const Theme::Border& Control::getBorder(State state) const
+    {
+        return getOverlay(state)->getBorder();
+    }
+
+    void Control::setSkinRegion(const Rectangle& region, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setSkinRegion(region, _style->_tw, _style->_th);
+        }
+
+        _dirty = true;
+    }
+
+    const Rectangle& Control::getSkinRegion(State state) const
+    {
+        return getOverlay(state)->getSkinRegion();
+    }
+
+    const Theme::UVs& Control::getSkinUVs(Theme::Skin::SkinArea area, State state) const
+    {
+        return getOverlay(state)->getSkinUVs(area);
+    }
+
+    void Control::setSkinColor(const Vector4& color, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setSkinColor(color);
+        }
+
+        _dirty = true;
+    }
+
+    const Vector4& Control::getSkinColor(State state) const
+    {
+        return getOverlay(state)->getSkinColor();
+    }
+
+    void Control::setMargin(float top, float bottom, float left, float right)
+    {
+        _style->setMargin(top, bottom, left, right);
+        _dirty = true;
+    }
+
+    const Theme::Margin& Control::getMargin() const
+    {
+        return _style->getMargin();
+    }
+
+    void Control::setPadding(float top, float bottom, float left, float right)
+    {
+        _style->setPadding(top, bottom, left, right);
+        _dirty = true;
+    }
+    
+    const Theme::Padding& Control::getPadding() const
+    {
+        return _style->getPadding();
+    }
+
+    void Control::setImageRegion(const char* id, const Rectangle& region, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setImageRegion(id, region, _style->_tw, _style->_th);
+        }
+
+        _dirty = true;
+    }
+
+    const Rectangle& Control::getImageRegion(const char* id, State state) const
+    {
+        return getOverlay(state)->getImageRegion(id);
+    }
+
+    void Control::setImageColor(const char* id, const Vector4& color, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setImageColor(id, color);
+        }
+
+        _dirty = true;
+    }
+
+    const Vector4& Control::getImageColor(const char* id, State state) const
+    {
+        return getOverlay(state)->getImageColor(id);
+    }
+
+    const Theme::UVs& Control::getImageUVs(const char* id, State state) const
+    {
+        return getOverlay(state)->getImageUVs(id);
+    }
+
+    void Control::setCursorRegion(const Rectangle& region, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setCursorRegion(region, _style->_tw, _style->_th);
+        }
+
+        _dirty = true;
+    }
+
+    const Rectangle& Control::getCursorRegion(State state) const
+    {
+        return getOverlay(state)->getCursorRegion();
+    }
+
+    void Control::setCursorColor(const Vector4& color, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setCursorColor(color);
+        }
+
+        _dirty = true;
+    }
+
+    const Vector4& Control::getCursorColor(State state)
+    {
+        return getOverlay(state)->getCursorColor();
+    }
+    
+    const Theme::UVs& Control::getCursorUVs(State state)
+    {
+        return getOverlay(state)->getCursorUVs();
+    }
+
+    void Control::setFont(Font* font, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setFont(font);
+        }
+
+        _dirty = true;
+    }
+
+    Font* Control::getFont(State state) const
+    {
+        return getOverlay(state)->getFont();
+    }
+
+    void Control::setFontSize(unsigned int fontSize, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setFontSize(fontSize);
+        }
+
+        _dirty = true;
+    }
+
+    unsigned int Control::getFontSize(State state) const
+    {
+        return getOverlay(state)->getFontSize();
+    }
+
+    void Control::setTextColor(const Vector4& color, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setTextColor(color);
+        }
+
+        _dirty = true;
+    }
+
+    const Vector4& Control::getTextColor(State state) const
+    {
+        return getOverlay(state)->getTextColor();
+    }
+
+    void Control::setTextAlignment(Font::Justify alignment, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setTextAlignment(alignment);
+        }
+
+        _dirty = true;
+    }
+
+    Font::Justify Control::getTextAlignment(State state) const
+    {
+        return getOverlay(state)->getTextAlignment();
+    }
+
+    void Control::setTextRightToLeft(bool rightToLeft, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setTextRightToLeft(rightToLeft);
+        }
+
+        _dirty = true;
+    }
+
+    bool Control::getTextRightToLeft(State state) const
+    {
+        return getOverlay(state)->getTextRightToLeft();
+    }
+
     const Rectangle& Control::getBounds() const
     {
         return _bounds;
@@ -81,6 +423,7 @@ namespace gameplay
     {
         _autoWidth = width;
         _autoHeight = height;
+        _dirty = true;
     }
 
     void Control::setStyle(Theme::Style* style)
@@ -101,9 +444,10 @@ namespace gameplay
     void Control::setState(State state)
     {
         _state = state;
+        _dirty = true;
     }
 
-    Control::State Control::getState()
+    Control::State Control::getState() const
     {
         return _state;
     }
@@ -111,11 +455,13 @@ namespace gameplay
     void Control::disable()
     {
         _state = DISABLED;
+        _dirty = true;
     }
 
     void Control::enable()
     {
         _state = NORMAL;
+        _dirty = true;
     }
 
     bool Control::isEnabled()
@@ -270,14 +616,8 @@ namespace gameplay
         _bounds.set(_position.x, _position.y, width, height);
 
         // Calculate the clipping viewport.
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        if (containerRegion)
-        {
-            border = overlay->getContainerRegion()->getBorder();
-        }
-        Theme::Padding padding = _style->getPadding();
+        const Theme::Border& border = getBorder(_state);
+        const Theme::Padding& padding = getPadding();
 
         x +=  border.left + padding.left;
         y +=  border.top + padding.top;
@@ -318,65 +658,61 @@ namespace gameplay
         Vector2 pos(clip.x + _position.x, clip.y + _position.y);
 
         // Get the border and background images for this control's current state.
-        Theme::ContainerRegion* containerRegion = _style->getOverlay(getOverlayType())->getContainerRegion();
-        if (containerRegion)
-        {
-            // Get the UVs.
-            Theme::UVs topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight;
-            topLeft = containerRegion->getUVs(Theme::ContainerRegion::TOP_LEFT);
-            top = containerRegion->getUVs(Theme::ContainerRegion::TOP);
-            topRight = containerRegion->getUVs(Theme::ContainerRegion::TOP_RIGHT);
-            left = containerRegion->getUVs(Theme::ContainerRegion::LEFT);
-            center = containerRegion->getUVs(Theme::ContainerRegion::CENTER);
-            right = containerRegion->getUVs(Theme::ContainerRegion::RIGHT);
-            bottomLeft = containerRegion->getUVs(Theme::ContainerRegion::BOTTOM_LEFT);
-            bottom = containerRegion->getUVs(Theme::ContainerRegion::BOTTOM);
-            bottomRight = containerRegion->getUVs(Theme::ContainerRegion::BOTTOM_RIGHT);
-
-            // Calculate screen-space positions.
-            Theme::Border border = containerRegion->getBorder();
-            Theme::Padding padding = _style->getPadding();
-            Vector4 borderColor = containerRegion->getColor();
-
-            float midWidth = _size.x - border.left - border.right;
-            float midHeight = _size.y - border.top - border.bottom;
-            float midX = pos.x + border.left;
-            float midY = pos.y + border.top;
-            float rightX = pos.x + _size.x - border.right;
-            float bottomY = pos.y + _size.y - border.bottom;
-
-            // Draw themed border sprites.
-            if (!border.left && !border.right && !border.top && !border.bottom)
-            {
-                // No border, just draw the image.
-                spriteBatch->draw(pos.x, pos.y, _size.x, _size.y, center.u1, center.v1, center.u2, center.v2, borderColor, clip);
-            }
-            else
-            {
-                if (border.left && border.top)
-                    spriteBatch->draw(pos.x, pos.y, border.left, border.top, topLeft.u1, topLeft.v1, topLeft.u2, topLeft.v2, borderColor, clip);
-                if (border.top)
-                    spriteBatch->draw(pos.x + border.left, pos.y, midWidth, border.top, top.u1, top.v1, top.u2, top.v2, borderColor, clip);
-                if (border.right && border.top)
-                    spriteBatch->draw(rightX, pos.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, borderColor, clip);
-                if (border.left)
-                    spriteBatch->draw(pos.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, borderColor, clip);
-                if (border.left && border.right && border.top && border.bottom)
-                    spriteBatch->draw(pos.x + border.left, pos.y + border.top, _size.x - border.left - border.right, _size.y - border.top - border.bottom,
-                        center.u1, center.v1, center.u2, center.v2, borderColor, clip);
-                if (border.right)
-                    spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, borderColor, clip);
-                if (border.bottom && border.left)
-                    spriteBatch->draw(pos.x, bottomY, border.left, border.bottom, bottomLeft.u1, bottomLeft.v1, bottomLeft.u2, bottomLeft.v2, borderColor, clip);
-                if (border.bottom)
-                    spriteBatch->draw(midX, bottomY, midWidth, border.bottom, bottom.u1, bottom.v1, bottom.u2, bottom.v2, borderColor, clip);
-                if (border.bottom && border.right)
-                    spriteBatch->draw(rightX, bottomY, border.right, border.bottom, bottomRight.u1, bottomRight.v1, bottomRight.u2, bottomRight.v2, borderColor, clip);
-            }
+        //Theme::UVs topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight;
+        const Theme::UVs& topLeft = getSkinUVs(Theme::Skin::TOP_LEFT, _state);
+        const Theme::UVs& top = getSkinUVs(Theme::Skin::TOP, _state);
+        const Theme::UVs& topRight = getSkinUVs(Theme::Skin::TOP_RIGHT, _state);
+        const Theme::UVs& left = getSkinUVs(Theme::Skin::LEFT, _state);
+        const Theme::UVs& center = getSkinUVs(Theme::Skin::CENTER, _state);
+        const Theme::UVs& right = getSkinUVs(Theme::Skin::RIGHT, _state);
+        const Theme::UVs& bottomLeft = getSkinUVs(Theme::Skin::BOTTOM_LEFT, _state);
+        const Theme::UVs& bottom = getSkinUVs(Theme::Skin::BOTTOM, _state);
+        const Theme::UVs& bottomRight = getSkinUVs(Theme::Skin::BOTTOM_RIGHT, _state);
+
+        // Calculate screen-space positions.
+        const Theme::Border& border = getBorder(_state);
+        const Theme::Padding& padding = getPadding();
+        Vector4 skinColor = getSkinColor(_state);
+        skinColor.w *= getOpacity(_state);
+
+        float midWidth = _size.x - border.left - border.right;
+        float midHeight = _size.y - border.top - border.bottom;
+        float midX = pos.x + border.left;
+        float midY = pos.y + border.top;
+        float rightX = pos.x + _size.x - border.right;
+        float bottomY = pos.y + _size.y - border.bottom;
+
+        // Draw themed border sprites.
+        if (!border.left && !border.right && !border.top && !border.bottom)
+        {
+            // No border, just draw the image.
+            spriteBatch->draw(pos.x, pos.y, _size.x, _size.y, center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+        }
+        else
+        {
+            if (border.left && border.top)
+                spriteBatch->draw(pos.x, pos.y, border.left, border.top, topLeft.u1, topLeft.v1, topLeft.u2, topLeft.v2, skinColor, clip);
+            if (border.top)
+                spriteBatch->draw(pos.x + border.left, pos.y, midWidth, border.top, top.u1, top.v1, top.u2, top.v2, skinColor, clip);
+            if (border.right && border.top)
+                spriteBatch->draw(rightX, pos.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, skinColor, clip);
+            if (border.left)
+                spriteBatch->draw(pos.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
+            if (border.left && border.right && border.top && border.bottom)
+                spriteBatch->draw(pos.x + border.left, pos.y + border.top, _size.x - border.left - border.right, _size.y - border.top - border.bottom,
+                    center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+            if (border.right)
+                spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, skinColor, clip);
+            if (border.bottom && border.left)
+                spriteBatch->draw(pos.x, bottomY, border.left, border.bottom, bottomLeft.u1, bottomLeft.v1, bottomLeft.u2, bottomLeft.v2, skinColor, clip);
+            if (border.bottom)
+                spriteBatch->draw(midX, bottomY, midWidth, border.bottom, bottom.u1, bottom.v1, bottom.u2, bottom.v2, skinColor, clip);
+            if (border.bottom && border.right)
+                spriteBatch->draw(rightX, bottomY, border.right, border.bottom, bottomRight.u1, bottomRight.v1, bottomRight.u2, bottomRight.v2, skinColor, clip);
         }
     }
 
-    void Control::drawSprites(SpriteBatch* spriteBatch, const Rectangle& position)
+    void Control::drawImages(SpriteBatch* spriteBatch, const Rectangle& position)
     {
     }
 
@@ -420,4 +756,207 @@ namespace gameplay
 
         return NORMAL;
     }
+
+    // Implementation of AnimationHandler
+    unsigned int Control::getAnimationPropertyComponentCount(int propertyId) const
+    {
+        switch(propertyId)
+        {
+        case ANIMATE_POSITION:
+        case ANIMATE_SIZE:
+            return 2;
+
+        case ANIMATE_POSITION_X:
+        case ANIMATE_POSITION_Y:
+        case ANIMATE_SIZE_WIDTH:
+        case ANIMATE_SIZE_HEIGHT:
+        case ANIMATE_OPACITY:
+            return 1;
+
+        default:
+            return -1;
+        }
+    }
+
+    void Control::getAnimationPropertyValue(int propertyId, AnimationValue* value)
+    {
+        switch(propertyId)
+        {
+        case ANIMATE_POSITION:
+            value->setFloat(0, _position.x);
+            value->setFloat(1, _position.y);
+            break;
+        case ANIMATE_SIZE:
+            value->setFloat(0, _size.x);
+            value->setFloat(1, _size.y);
+            break;
+        case ANIMATE_POSITION_X:
+            value->setFloat(0, _position.x);
+            break;
+        case ANIMATE_POSITION_Y:
+            value->setFloat(0, _position.y);
+            break;
+        case ANIMATE_SIZE_WIDTH:
+            value->setFloat(0, _size.x);
+            break;
+        case ANIMATE_SIZE_HEIGHT:
+            value->setFloat(0, _size.y);
+            break;
+        case ANIMATE_OPACITY:
+        default:
+            break;
+        }
+    }
+
+    void Control::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
+    {
+        switch(propertyId)
+        {
+        case ANIMATE_POSITION:
+            applyAnimationValuePositionX(value->getFloat(0), blendWeight);
+            applyAnimationValuePositionY(value->getFloat(1), blendWeight);
+            break;
+        case ANIMATE_POSITION_X:
+            applyAnimationValuePositionX(value->getFloat(0), blendWeight);
+            break;
+        case ANIMATE_POSITION_Y:
+            applyAnimationValuePositionY(value->getFloat(0), blendWeight);
+            break;
+        case ANIMATE_SIZE:
+            applyAnimationValueSizeWidth(value->getFloat(0), blendWeight);
+            applyAnimationValueSizeHeight(value->getFloat(1), blendWeight);
+            break;
+        case ANIMATE_SIZE_WIDTH:
+            applyAnimationValueSizeWidth(value->getFloat(0), blendWeight);
+            break;
+        case ANIMATE_SIZE_HEIGHT:
+            applyAnimationValueSizeHeight(value->getFloat(0), blendWeight);
+            break;
+        case ANIMATE_OPACITY:
+            applyAnimationValueOpacity();
+        default:
+            break;
+        }
+    }
+
+    void Control::applyAnimationValuePositionX(float x, float blendWeight)
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_POSITION_X_BIT) != ANIMATION_POSITION_X_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_POSITION_X_BIT;
+        }
+        else
+        {
+            x = Curve::lerp(blendWeight, _position.x, x);
+        }
+        _position.x = x;
+        _dirty = true;
+    }
+    
+    void Control::applyAnimationValuePositionY(float y, float blendWeight)
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_POSITION_Y_BIT) != ANIMATION_POSITION_Y_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_POSITION_Y_BIT;
+        }
+        else
+        {
+            y = Curve::lerp(blendWeight, _position.y, y);
+        }
+        _position.y = y;
+        _dirty = true;
+    }
+    
+    void Control::applyAnimationValueSizeWidth(float width, float blendWeight)
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_SIZE_WIDTH_BIT) != ANIMATION_SIZE_WIDTH_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_SIZE_WIDTH_BIT;
+        }
+        else
+        {
+            width = Curve::lerp(blendWeight, _size.x, width);
+        }
+        _size.x = width;
+        _dirty = true;
+    }
+
+    void Control::applyAnimationValueSizeHeight(float height, float blendWeight)
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_SIZE_HEIGHT_BIT) != ANIMATION_SIZE_HEIGHT_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_SIZE_HEIGHT_BIT;
+        }
+        else
+        {
+            height = Curve::lerp(blendWeight, _size.y, height);
+        }
+        _size.y = height;
+        _dirty = true;
+    }
+
+    void Control::applyAnimationValueOpacity()
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
+        }
+        _dirty = true;
+    }
+    
+    Theme::Style::Overlay** Control::getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays)
+    {
+        unsigned int index = 0;
+        if ((overlayTypes & NORMAL) == NORMAL)
+        {
+            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_NORMAL);
+        }
+
+        if ((overlayTypes & FOCUS) == FOCUS)
+        {
+            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
+        }
+
+        if ((overlayTypes & ACTIVE) == ACTIVE)
+        {
+            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
+        }
+
+        if ((overlayTypes & DISABLED) == DISABLED)
+        {
+            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
+        }
+
+        return overlays;
+    }
+
+    Theme::Style::Overlay* Control::getOverlay(State state) const
+    {
+        switch(state)
+        {
+        case Control::NORMAL:
+            return _style->getOverlay(Theme::Style::OVERLAY_NORMAL);
+        case Control::FOCUS:
+            return _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
+        case Control::ACTIVE:
+            return _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
+        case Control::DISABLED:
+            return _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
+        default:
+            return NULL;
+        }
+    }
+
+    void Control::overrideStyle()
+    {
+        if (_styleOverridden)
+        {
+            return;
+        }
+
+        // Copy the style.
+        WARN_VARG("%d", sizeof(Theme::Style::Overlay));
+        _style = new Theme::Style(*_style);
+        _styleOverridden = true;
+    }
 }

+ 408 - 14
gameplay/src/Control.h

@@ -9,13 +9,18 @@
 #include "Touch.h"
 #include "Keyboard.h"
 
+/**
+ * Default duration of UI animations.
+ */
+#define DEFAULT_UI_ANIMATION_DURATION 200L
+
 namespace gameplay
 {
 
 /**
  * Base class for UI controls.
  */
-class Control : public Ref
+class Control : public Ref, public AnimationTarget
 {
     friend class Form;
     friend class Container;
@@ -29,12 +34,15 @@ public:
      */
     enum State
     {
-        NORMAL,
-        FOCUS,
-        ACTIVE,
-        DISABLED
+        NORMAL = 0x01,
+        FOCUS = 0x02,
+        ACTIVE = 0x04,
+        DISABLED = 0x08,
     };
 
+    // Only for setting control states
+    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
+
     class Listener
     {
     public:
@@ -50,6 +58,41 @@ public:
         virtual void controlEvent(Control* control, EventType evt) = 0;
     };
 
+    /**
+     * Position animation property. Data = x, y
+     */
+    static const int ANIMATE_POSITION = 1;
+
+    /**
+     * Position x animation property. Data = x
+     */
+    static const int ANIMATE_POSITION_X = 2;
+
+    /**
+     * Position y animation property. Data = y
+     */
+    static const int ANIMATE_POSITION_Y = 3;
+
+    /**
+     * Size animation property.  Data = width, height
+     */
+    static const int ANIMATE_SIZE = 4;
+
+    /**
+     * Size width animation property.  Data = width
+     */
+    static const int ANIMATE_SIZE_WIDTH = 5;
+
+    /**
+     * Size height animation property.  Data = height
+     */
+    static const int ANIMATE_SIZE_HEIGHT = 6;
+
+    /**
+     * Opacity property.  Data = opacity
+     */
+    static const int ANIMATE_OPACITY = 7;
+
     /**
      * Get this control's ID string.
      *
@@ -63,7 +106,7 @@ public:
      * @param x The x coordinate.
      * @param y The y coordinate.
      */
-    void setPosition(float x, float y);
+    void setPosition(float x, float y, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
 
     /**
      * Get the position of this control relative to its parent container.
@@ -78,7 +121,7 @@ public:
      * @param width The width.
      * @param height The height.
      */
-    void setSize(float width, float height);
+    void setSize(float width, float height, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
 
     /**
      * Get the desired size of this control, including its border and padding, before clipping.
@@ -87,6 +130,314 @@ public:
      */
     const Vector2& getSize() const;
 
+    // Themed properties.
+    
+    //void setBorder(const Theme::Border& border, unsigned char states = STATE_ALL);
+    /**
+     * Set the size of this control's border.
+     *
+     * @param top The height of the border's top side.
+     * @param bottom The height of the border's bottom side.
+     * @param left The width of the border's left side.
+     * @param right The width of the border's right side.
+     */
+    void setBorder(float top, float bottom, float left, float right, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the measurements of this control's border for a given state. 
+     *
+     * @return This control's border.
+     */
+    const Theme::Border& getBorder(State state = NORMAL) const;
+
+    /**
+     * Set the texture region of this control's skin.
+     *
+     * @param region The texture region, in pixels.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setSkinRegion(const Rectangle& region, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the texture region of this control's skin for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The texture region of this control's skin.
+     */
+    const Rectangle& getSkinRegion(State state = NORMAL) const;
+
+    /**
+     * Get the texture coordinates of an area of this control's skin for a given state.
+     *
+     * @param area The area of the skin to get the coordinates of.
+     * @param state The state to get this property from.
+     *
+     * @return The texture coordinates of an area of this control's skin.
+     */
+    const Theme::UVs& getSkinUVs(Theme::Skin::SkinArea area, State state = NORMAL) const;
+
+    /**
+     * Set the blend color of this control's skin.
+     *
+     * @param color The new blend color.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setSkinColor(const Vector4& color, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the blend color of this control's skin for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The blend color of this control's skin.
+     */
+    const Vector4& getSkinColor(State state = NORMAL) const;
+
+    /**
+     * Set this control's margin.
+     *
+     * @param top Height of top margin.
+     * @param bottom Height of bottom margin.
+     * @param left Width of left margin.
+     * @param right Width of right margin.
+     */
+    void setMargin(float top, float bottom, float left, float right);
+
+    /**
+     * Get this control's margin.
+     *
+     * @return This control's margin.
+     */
+    const Theme::Margin& getMargin() const;
+
+    /**
+     * Set this control's padding.
+     *
+     * @param top Height of top padding.
+     * @param bottom Height of bottom padding.
+     * @param left Width of left padding.
+     * @param right Width of right padding.
+     */
+    void setPadding(float top, float bottom, float left, float right);
+
+    /**
+     * Get this control's padding.
+     *
+     * @return This control's padding.
+     */
+    const Theme::Padding& getPadding() const;
+
+    /**
+     * Set the texture region of an image used by this control.
+     *
+     * @param id The ID of the image to modify.
+     * @param region The new texture region of the image.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setImageRegion(const char* id, const Rectangle& region, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the texture region of an image used by this control for a given state.
+     *
+     * @param id The ID of the image.
+     * @param state The state to get this property from.
+     *
+     * @return The texture region of the specified image.
+     */
+    const Rectangle& getImageRegion(const char* id, State state) const;
+
+    /**
+     * Set the blend color of an image used by this control.
+     *
+     * @param id The ID of the image to modify.
+     * @param color The new blend color of the image.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setImageColor(const char* id, const Vector4& color, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the blend color of an image used by this control for a given state.
+     *
+     * @param id The ID of the image.
+     * @param state The state to get this property from.
+     *
+     * @return The blend color of the specified image.
+     */
+    const Vector4& getImageColor(const char* id, State state) const;
+
+    /**
+     * Get the texture coordinates of an image used by this control for a given state.
+     *
+     * @param id The ID of the image.
+     * @param state The state to get this property from.
+     *
+     * @return The texture coordinates of the specified image.
+     */
+    const Theme::UVs& getImageUVs(const char* id, State state) const;
+
+    /**
+     * Set the texture region of this control's cursor.
+     *
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setCursorRegion(const Rectangle& region, unsigned char states);
+
+    /**
+     * Get the texture region of this control's cursor for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The texture region of this control's cursor.
+     */
+    const Rectangle& getCursorRegion(State state) const;
+
+    /**
+     * Set the blend color of this control's cursor.
+     *
+     * @param color The new blend color.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setCursorColor(const Vector4& color, unsigned char states);
+
+    /**
+     * Get the blend color of this control's cursor for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The blend color of this control's cursor.
+     */
+    const Vector4& getCursorColor(State state);
+    
+    /**
+     * Get the texture coordinates of this control's cursor for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The texture coordinates of this control's cursor.
+     */
+    const Theme::UVs& getCursorUVs(State state);
+
+    /**
+     * Set the font used by this control.
+     *
+     * @param font The new font to use.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setFont(Font* font, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the font used by this control for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return the font used by this control.
+     */
+    Font* getFont(State state = NORMAL) const;
+
+    /**
+     * Set this control's font size.
+     *
+     * @param size The new font size.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setFontSize(unsigned int size, unsigned char states = STATE_ALL);
+
+    /**
+     * Get this control's font size for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return This control's font size.
+     */
+    unsigned int getFontSize(State state = NORMAL) const;
+
+    /**
+     * Set this control's text color.
+     *
+     * @param color The new text color.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setTextColor(const Vector4& color, unsigned char states = STATE_ALL);
+
+    /**
+     * Get this control's text color for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return This control's text color.
+     */
+    const Vector4& getTextColor(State state = NORMAL) const;
+
+    /**
+     * Set this control's text alignment.
+     *
+     * @param alignment The new text alignment.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setTextAlignment(Font::Justify alignment, unsigned char states = STATE_ALL);
+
+    /**
+     * Get this control's text alignment for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return This control's text alignment for the given state.
+     */
+    Font::Justify getTextAlignment(State state = NORMAL) const;
+
+    /**
+     * Set whether text is drawn from right to left within this control.
+     *
+     * @param rightToLeft Whether text is drawn from right to left within this control.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setTextRightToLeft(bool rightToLeft, unsigned char states = STATE_ALL);
+
+    /**
+     * Get whether text is drawn from right to left within this control, for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return Whether text is drawn from right to left within this control, for the given state.
+     */
+    bool getTextRightToLeft(State state = NORMAL) const;
+
+    /**
+     * Set the opacity of this control.
+     *
+     * @param opacity The new opacity.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     * @param duration The duration to animate opacity by.
+     */
+    void setOpacity(float opacity, unsigned char states = STATE_ALL, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
+
+    /**
+     * Get the opacity of this control for a given state. 
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The opacity of this control for a given state.
+     */
+    float getOpacity(State state = NORMAL) const;
+
+    // TODO
+    // Controls must state the names of the images they use, for the purposes of a future UI editor.
+    //virtual std::vector<std::string> getImageNames() = 0;
+
+    // Control state.
     /**
      * Get the bounds of this control, relative to its parent container, after clipping.
      *
@@ -106,7 +457,7 @@ public:
      * its text and themed visual elements (CheckBox / RadioButton toggle etc.).
      *
      * Similarly set this on the width and/or height of a Container to tightly fit
-     * the Container around all its children.
+     * the Container around STATE_ALL its children.
      *
      * @param width Whether to automatically determine this Control's width.
      * @param height Whether to automatically determine this Control's height.
@@ -125,7 +476,7 @@ public:
      *
      * @return This control's current state.
      */
-    State getState();
+    State getState() const;
 
     /**
      * Disable this control.
@@ -185,6 +536,21 @@ public:
      */
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
+    /**
+     * @see AnimationTarget#getAnimationPropertyComponentCount
+     */
+    unsigned int getAnimationPropertyComponentCount(int propertyId) const;
+
+    /**
+     * @see AnimationTarget#getAnimationProperty
+     */
+    void getAnimationPropertyValue(int propertyId, AnimationValue* value);
+
+    /**
+     * @see AnimationTarget#setAnimationProperty
+     */
+    void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
+
 protected:
     Control();
     virtual ~Control();
@@ -230,6 +596,7 @@ protected:
      */
     virtual void update(const Rectangle& clip);
 
+private:
     /**
      * Draws the themed border and background of a control.
      *
@@ -238,13 +605,14 @@ protected:
      */
     virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
 
+protected:
     /**
-     * Draw the icons associated with this control.
+     * Draw the images associated with this control.
      *
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param clip The clipping rectangle of this control's parent container.
      */
-    virtual void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     /**
      * Draw this control's text.
@@ -254,7 +622,7 @@ protected:
     virtual void drawText(const Rectangle& clip);
 
     /**
-     * Initialize properties common to all Controls.
+     * Initialize properties common to STATE_ALL Controls.
      */
     virtual void init(Theme::Style* style, Properties* properties);
 
@@ -274,7 +642,7 @@ protected:
     static State getStateFromString(const char* state);
 
     /**
-     * Notify all listeners of a specific event.
+     * Notify STATE_ALL listeners of a specific event.
      */
     void notifyListeners(Listener::EventType eventType);
 
@@ -285,7 +653,7 @@ protected:
     Vector2 _position;      // Position, relative to parent container's clipping window.
     Vector2 _size;          // Desired size.  Will be clipped.
     Rectangle _bounds;      // The position and size of this control, relative to parent container's bounds, including border and padding, after clipping.
-    Rectangle _textBounds;  // The position and size of this control's content, before clipping.  Used for text alignment.
+    Rectangle _textBounds;  // The position and size of this control's text area, before clipping.  Used for text alignment.
     Rectangle _clip;        // Clipping window of this control's content, after clipping.
     bool _autoWidth;
     bool _autoHeight;
@@ -296,6 +664,32 @@ protected:
     ListenerMap* _listeners;
 
 private:
+    // Animation blending bits.
+    static const char ANIMATION_POSITION_X_BIT = 0x01;
+    static const char ANIMATION_POSITION_Y_BIT = 0x02;
+    static const char ANIMATION_SIZE_WIDTH_BIT = 0x04;
+    static const char ANIMATION_SIZE_HEIGHT_BIT = 0x08;
+    static const char ANIMATION_OPACITY_BIT = 0x10;
+
+    bool _styleOverridden;
+
+    void applyAnimationValuePositionX(float x, float blendWeight);
+    void applyAnimationValuePositionY(float y, float blendWeight);
+    void applyAnimationValueSizeWidth(float width, float blendWeight);
+    void applyAnimationValueSizeHeight(float height, float blendWeight);
+    void applyAnimationValueOpacity();
+
+    // Gets the overlays requested in the overlayTypes bitflag.
+    Theme::Style::Overlay** getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays);
+
+    /**
+     * Gets an overlay from a control state.
+     */
+    Theme::Style::Overlay* getOverlay(Control::State state) const;
+
+    // Ensures that this control has a copy of its style so that it can override it without affecting other controls.
+    void overrideStyle();
+
     Control(const Control& copy);
 };
 

+ 1 - 1
gameplay/src/Form.cpp

@@ -210,7 +210,7 @@ namespace gameplay
                 control->drawBorder(spriteBatch, clip);
 
                 // Add all themed foreground sprites (checkboxes etc.) to the same batch.
-                control->drawSprites(spriteBatch, clip);
+                control->drawImages(spriteBatch, clip);
             }
         }
         spriteBatch->end();

+ 4 - 1
gameplay/src/Label.cpp

@@ -73,9 +73,12 @@ namespace gameplay
         Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
         Font* font = overlay->getFont();
 
+        Vector4 textColor = overlay->getTextColor();
+        textColor.w *= overlay->getOpacity();
+
         // Draw the text.
         font->begin();
-        font->drawText(_text.c_str(), _textBounds, overlay->getTextColor(), overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft(), &_clip);
+        font->drawText(_text.c_str(), _textBounds, textColor, overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft(), &_clip);
         font->end();
 
         _dirty = false;

+ 56 - 37
gameplay/src/RadioButton.cpp

@@ -29,7 +29,7 @@ RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
     RadioButton* radioButton = new RadioButton();
     radioButton->init(style, properties);
 
-    properties->getVector2("iconSize", &radioButton->_iconSize);
+    properties->getVector2("imageSize", &radioButton->_imageSize);
 
     if (properties->getBool("selected"))
     {
@@ -48,21 +48,14 @@ RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
     return radioButton;
 }
 
-void RadioButton::setIconSize(float width, float height)
+void RadioButton::setImageSize(float width, float height)
 {
-    _iconSize.set(width, height);
+    _imageSize.set(width, height);
 }
 
-const Vector2& RadioButton::getIconSize() const
+const Vector2& RadioButton::getImageSize() const
 {
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    if (_iconSize.isZero() && icon)
-    {
-        return icon->getSize();
-    }
-
-    return _iconSize;
+    return _imageSize;
 }
 
 void RadioButton::addListener(Control::Listener* listener, int eventFlags)
@@ -121,42 +114,57 @@ void RadioButton::clearSelected(const std::string& groupId)
     }
 }
 
-void RadioButton::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     // Left, v-center.
-    // TODO: Set an alignment for icons.
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getRadioButtonIcon();
-    if (icon)
+    // TODO: Set an alignment for radio button images.
+    const Theme::Border border = getBorder(_state);
+    const Theme::Padding padding = _style->getPadding();
+    float opacity = getOpacity(_state);
+
+    if (_selected)
     {
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        if (containerRegion)
+        const Rectangle& selectedRegion = getImageRegion("selected", _state);
+        const Theme::UVs& selected = getImageUVs("selected", _state);
+        Vector4 selectedColor = getImageColor("selected", _state);
+        selectedColor.w *= opacity;
+
+        Vector2 size;
+        if (_imageSize.isZero())
         {
-            border = containerRegion->getBorder();
+            size.set(selectedRegion.width, selectedRegion.height);
         }
-        const Theme::Padding padding = _style->getPadding();
-
-        Vector2& size = _iconSize;
-        if (_iconSize.isZero())
+        else
         {
-            size = icon->getSize();
+            size.set(_imageSize);
         }
-        const Vector4 color = icon->getColor();
 
         Vector2 pos(clip.x + _position.x + border.left + padding.left,
             clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
-        if (_selected)
+        spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
+    }
+    else
+    {
+        const Rectangle& unselectedRegion = getImageRegion("unselected", _state);
+        const Theme::UVs& unselected = getImageUVs("unselected", _state);
+        Vector4 unselectedColor = getImageColor("unselected", _state);
+        unselectedColor.w *= opacity;
+
+        Vector2 size;
+        if (_imageSize.isZero())
         {
-            const Theme::UVs on = icon->getOnUVs();
-            spriteBatch->draw(pos.x, pos.y, size.x, size.y, on.u1, on.v1, on.u2, on.v2, color);
+            size.set(unselectedRegion.width, unselectedRegion.height);
         }
         else
         {
-            const Theme::UVs off = icon->getOffUVs();
-            spriteBatch->draw(pos.x, pos.y, size.x, size.y, off.u1, off.v1, off.u2, off.v2, color);
+            size.set(_imageSize);
         }
+
+        Vector2 pos(clip.x + _position.x + border.left + padding.left,
+            clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+
+        spriteBatch->draw(pos.x, pos.y, size.x, size.y, unselected.u1, unselected.v1, unselected.u2, unselected.v2, unselectedColor, _clip);
     }
 }
 
@@ -164,12 +172,23 @@ void RadioButton::update(const Rectangle& clip)
 {
     Control::update(clip);
 
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    Vector2& size = _iconSize;
-    if (_iconSize.isZero() && icon)
+    Vector2 size;
+    if (_imageSize.isZero())
+    {
+        if (_selected)
+        {
+            const Rectangle& selectedRegion = getImageRegion("selected", _state);
+            size.set(selectedRegion.width, selectedRegion.height);
+        }
+        else
+        {
+            const Rectangle& unselectedRegion = getImageRegion("unselected", _state);
+            size.set(unselectedRegion.width, unselectedRegion.height);
+        }
+    }
+    else
     {
-        size = icon->getSize();
+        size.set(_imageSize);
     }
     float iconWidth = size.x;
 

+ 4 - 4
gameplay/src/RadioButton.h

@@ -44,14 +44,14 @@ public:
      * @param width The width to draw the radio button icon.
      * @param height The height to draw the radio button icon.
      */
-    void setIconSize(float width, float height);
+    void setImageSize(float width, float height);
 
     /**
      * Get the size at which the radio button icon will be drawn.
      *
      * @return The size of the radio button icon.
      */
-    const Vector2& getIconSize() const;
+    const Vector2& getImageSize() const;
 
     /**
      * Add a listener to be notified of specific events affecting
@@ -83,14 +83,14 @@ protected:
 
     void update(const Rectangle& clip);
 
-    void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     // Clear the _selected flag of all radio buttons in the given group.
     static void clearSelected(const std::string& groupId);
 
     std::string _groupId;
     bool _selected;
-    Vector2 _iconSize;
+    Vector2 _imageSize;
 
 private:
     RadioButton(const RadioButton& copy);

+ 8 - 0
gameplay/src/Ref.cpp

@@ -18,6 +18,14 @@ Ref::Ref() :
 #endif
 }
 
+Ref::Ref(const Ref& copy) :
+    _refCount(1)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+    __record = trackRef(this);
+#endif
+}
+
 Ref::~Ref()
 {
 }

+ 2 - 0
gameplay/src/Ref.h

@@ -50,6 +50,8 @@ protected:
      */
     Ref();
 
+    Ref(const Ref& copy);
+
     /**
      * Destructor.
      */

+ 49 - 58
gameplay/src/Slider.cpp

@@ -95,22 +95,14 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
             y > 0 && y <= _bounds.height)
         {
             // 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();
+            const Theme::Border& border = getBorder(_state);
+            const Theme::Padding& padding = getPadding();
+            const Rectangle& minCapRegion = getImageRegion("minCap", _state);
+            const Rectangle& maxCapRegion = getImageRegion("maxCap", _state);
 
-            float markerPosition = ((float)x - maxCapSize.x - border.left - padding.left) /
-                                    (_bounds.width - border.left - border.right - padding.left - padding.right - minCapSize.x - maxCapSize.x);
+            float markerPosition = ((float)x - maxCapRegion.width - border.left - padding.left) /
+                (_bounds.width - border.left - border.right - padding.left - padding.right - minCapRegion.width - maxCapRegion.width);
+            
             if (markerPosition > 1.0f)
             {
                 markerPosition = 1.0f;
@@ -143,56 +135,55 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return Control::touchEvent(evt, x, y, contactIndex);
 }
 
-void Slider::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     // 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;
-        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        if (containerRegion)
-        {
-            border = containerRegion->getBorder();
-        }
-        Theme::Padding padding = _style->getPadding();
-
-        const Vector2 minCapSize = icon->getMinCapSize();
-        const Vector2 maxCapSize = icon->getMaxCapSize();
-        const Vector2 markerSize = icon->getMarkerSize();
-        const Vector2 trackSize = icon->getTrackSize();
-
-        const Theme::UVs minCap = icon->getMinCapUVs();
-        const Theme::UVs maxCap = icon->getMaxCapUVs();
-        const Theme::UVs marker = icon->getMarkerUVs();
-        const Theme::UVs track = icon->getTrackUVs();
-
-        const Vector4 color = icon->getColor();
-
-        // Draw order: track, caps, marker.
-        float midY = clip.y + _bounds.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f;
-        Vector2 pos(clip.x + _bounds.x + border.left + padding.left, midY - trackSize.y / 2.0f);
-        spriteBatch->draw(pos.x, pos.y, _bounds.width, trackSize.y, track.u1, track.v1, track.u2, track.v2, color);
-
-        pos.y = midY - minCapSize.y * 0.5f;
-        pos.x -= minCapSize.x * 0.5f;
-        spriteBatch->draw(pos.x, pos.y, minCapSize.x, minCapSize.y, minCap.u1, minCap.v1, minCap.u2, minCap.v2, color);
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
+
+    const Rectangle& minCapRegion = getImageRegion("minCap", _state);
+    const Rectangle& maxCapRegion = getImageRegion("maxCap", _state);
+    const Rectangle& markerRegion = getImageRegion("marker", _state);
+    const Rectangle& trackRegion = getImageRegion("track", _state);
+
+    const Theme::UVs minCap = getImageUVs("minCap", _state);
+    const Theme::UVs maxCap = getImageUVs("maxCap", _state);
+    const Theme::UVs marker = getImageUVs("marker", _state);
+    const Theme::UVs track = getImageUVs("track", _state);
+
+    Vector4 minCapColor = getImageColor("minCap", _state);
+    Vector4 maxCapColor = getImageColor("maxCap", _state);
+    Vector4 markerColor = getImageColor("marker", _state);
+    Vector4 trackColor = getImageColor("track", _state);
+
+    float opacity = getOpacity(_state);
+    minCapColor.w *= opacity;
+    maxCapColor.w *= opacity;
+    markerColor.w *= opacity;
+    trackColor.w *= opacity;
+
+    // Draw order: track, caps, marker.
+    float midY = clip.y + _bounds.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f;
+    Vector2 pos(clip.x + _bounds.x + border.left + padding.left, midY - trackRegion.height / 2.0f);
+    spriteBatch->draw(pos.x, pos.y, _bounds.width, trackRegion.height, track.u1, track.v1, track.u2, track.v2, trackColor);
+
+    pos.y = midY - minCapRegion.height * 0.5f;
+    pos.x -= minCapRegion.width * 0.5f;
+    spriteBatch->draw(pos.x, pos.y, minCapRegion.width, minCapRegion.height, minCap.u1, minCap.v1, minCap.u2, minCap.v2, minCapColor);
         
-        pos.x = clip.x + _bounds.x + _bounds.width - border.right - padding.right - maxCapSize.x * 0.5f;
-        spriteBatch->draw(pos.x, pos.y, maxCapSize.x, maxCapSize.y, maxCap.u1, maxCap.v1, maxCap.u2, maxCap.v2, color);
-
-        // Percent across.
-        float markerPosition = _value / (_max - _min);
-        markerPosition *= _bounds.width - border.left - padding.left - border.right - padding.right - minCapSize.x * 0.5f - maxCapSize.x * 0.5f - markerSize.x;
-        pos.x = clip.x + _bounds.x + border.left + padding.left + minCapSize.x * 0.5f + markerPosition;
-        pos.y = midY - markerSize.y / 2.0f;
-        spriteBatch->draw(pos.x, pos.y, markerSize.x, markerSize.y, marker.u1, marker.v1, marker.u2, marker.v2, color);
-    }
+    pos.x = clip.x + _bounds.x + _bounds.width - border.right - padding.right - maxCapRegion.width * 0.5f;
+    spriteBatch->draw(pos.x, pos.y, maxCapRegion.width, maxCapRegion.height, maxCap.u1, maxCap.v1, maxCap.u2, maxCap.v2, maxCapColor);
+
+    // Percent across.
+    float markerPosition = _value / (_max - _min);
+    markerPosition *= _bounds.width - border.left - padding.left - border.right - padding.right - minCapRegion.width * 0.5f - maxCapRegion.width * 0.5f - markerRegion.width;
+    pos.x = clip.x + _bounds.x + border.left + padding.left + minCapRegion.width * 0.5f + markerPosition;
+    pos.y = midY - markerRegion.height / 2.0f;
+    spriteBatch->draw(pos.x, pos.y, markerRegion.width, markerRegion.height, marker.u1, marker.v1, marker.u2, marker.v2, markerColor);
 }
 
 }

+ 1 - 1
gameplay/src/Slider.h

@@ -100,7 +100,7 @@ protected:
 
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
-    void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     float _min;
     float _max;

+ 9 - 44
gameplay/src/TextBox.cpp

@@ -31,14 +31,8 @@ int TextBox::getLastKeypress()
 
 void TextBox::setCursorLocation(int x, int y)
 {
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-    Theme::Border border;
-    if (containerRegion)
-    {
-        border = containerRegion->getBorder();
-    }
-    Theme::Padding padding = _style->getPadding();
+    Theme::Border border = getBorder(_state);
+    Theme::Padding padding = getPadding();
 
     _cursorLocation.set(x - border.left - padding.left + _clip.x,
                        y - border.top - padding.top + _clip.y);
@@ -251,26 +245,6 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 
 void TextBox::update(const Rectangle& clip)
 {
-    /*
-    Vector2 pos(clip.x + _position.x, clip.y + _position.y);
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Border border;
-    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-    if (containerRegion)
-    {
-        border = containerRegion->getBorder();
-    }
-    Theme::Padding padding = _style->getPadding();
-
-    // Set up the text viewport.
-    Font* font = overlay->getFont();
-    _clip.set(pos.x + border.left + padding.left,
-                  pos.y + border.top + padding.top,
-                  _size.x - border.left - padding.left - border.right - padding.right,
-                  _size.y - border.top - padding.top - border.bottom - padding.bottom - overlay->getFontSize());
-
-                  */
-
     Control::update(clip);
 
     // Get index into string and cursor location from the last recorded touch location.
@@ -283,28 +257,19 @@ void TextBox::update(const Rectangle& clip)
     }
 }
 
-void TextBox::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     if (_state == FOCUS)
     {
         // Draw the cursor at its current location.
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Theme::Cursor* cursor = overlay->getTextCursor();
-        if (cursor)
+        const Rectangle& region = getImageRegion("textCaret", _state);
+        if (!region.isEmpty())
         {
-            Theme::Border border;
-            Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-            if (containerRegion)
-            {
-                border = containerRegion->getBorder();
-            }
-            const Theme::Padding padding = _style->getPadding();
-            const Vector2 size = cursor->getSize();
-            const Vector4 color = cursor->getColor();
-            const Theme::UVs uvs = cursor->getUVs();
-            unsigned int fontSize = overlay->getFontSize();
+            const Vector4& color = getImageColor("textCaret", _state);
+            const Theme::UVs uvs = getImageUVs("textCaret", _state);
+            unsigned int fontSize = getFontSize(_state);
 
-            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 - (region.width / 2.0f), _cursorLocation.y, region.width, fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
         }
     }
 

+ 1 - 1
gameplay/src/TextBox.h

@@ -60,7 +60,7 @@ protected:
     void update(const Rectangle& clip);
 
     // Draw the cursor.
-    void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     Vector2 _cursorLocation;
     unsigned int textIndex;

+ 504 - 420
gameplay/src/Theme.cpp

@@ -1,7 +1,3 @@
-/*
- * Theme.cpp
- */
-
 #include "Base.h"
 #include "Theme.h"
 
@@ -26,28 +22,22 @@ namespace gameplay
             SAFE_DELETE(style);
         }
 
-        for (unsigned int i = 0, count = _cursors.size(); i < count; ++i)
+        for (unsigned int i = 0, count = _images.size(); i < count; ++i)
         {
-            Cursor* cursor = _cursors[i];
-            SAFE_RELEASE(cursor);
+            Image* image = _images[i];
+            SAFE_RELEASE(image);
         }
 
-        for (unsigned int i = 0, count = _icons.size(); i < count; ++i)
+        for (unsigned int i = 0, count = _imageLists.size(); i < count; ++i)
         {
-            Icon* icon = _icons[i];
-            SAFE_RELEASE(icon);
+            ImageList* imageList = _imageLists[i];
+            SAFE_RELEASE(imageList);
         }
 
-        for (unsigned int i = 0, count = _sliders.size(); i < count; ++i)
+        for (unsigned int i = 0, count = _skins.size(); i < count; ++i)
         {
-            SliderIcon* slider = _sliders[i];
-            SAFE_RELEASE(slider);
-        }
-
-        for (unsigned int i = 0, count = _containers.size(); i < count; ++i)
-        {
-            ContainerRegion* container = _containers[i];
-            SAFE_RELEASE(container);
+            Skin* skin = _skins[i];
+            SAFE_RELEASE(skin);
         }
 
         SAFE_DELETE(_spriteBatch);
@@ -104,80 +94,24 @@ namespace gameplay
         theme->_texture = Texture::create(textureFile, false);
         theme->_spriteBatch = SpriteBatch::create(theme->_texture);
 
+        float tw = 1.0f / theme->_texture->getWidth();
+        float th = 1.0f / theme->_texture->getHeight();
+
         Properties* space = themeProperties->getNextNamespace();
         while (space != NULL)
         {
             // First load all cursors, checkboxes etc. that can be referred to by styles.
             const char* spacename = space->getNamespace();
-            if (strcmp(spacename, "cursor") == 0)
-            {
-                Vector4 regionVector;                
-                space->getVector4("region", &regionVector);
-                const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
-
-                Vector4 color(1, 1, 1, 1);
-                if (space->exists("color"))
-                {
-                    space->getColor("color", &color);
-                }
-
-                Theme::Cursor* c = Theme::Cursor::create(space->getId(), *theme->_texture, region, color);
-                theme->_cursors.push_back(c);
-            }
-            else if (strcmp(spacename, "icon") == 0)
-            {
-                Vector2 offVec;
-                Vector2 onVec;
-                Vector2 size;
-                space->getVector2("offPosition", &offVec);
-                space->getVector2("onPosition", &onVec);
-                space->getVector2("size", &size);
-                
-                Vector4 color(1, 1, 1, 1);
-                if (space->exists("color"))
-                {
-                    space->getColor("color", &color);
-                }
-
-                Icon* icon = Icon::create(space->getId(), *theme->_texture, size, offVec, onVec, color);
-                theme->_icons.push_back(icon);
-            }
-            else if (strcmp(spacename, "slider") == 0)
+            
+            if (strcmp(spacename, "image") == 0)
             {
-                Vector4 minCapRegion;
-                Vector4 maxCapRegion;
-                Vector4 trackRegion;
-                Vector4 markerRegion;
-                space->getVector4("minCapRegion", &minCapRegion);
-                space->getVector4("maxCapRegion", &maxCapRegion);
-                space->getVector4("trackRegion", &trackRegion);
-                space->getVector4("markerRegion", &markerRegion);
-                
-                Vector4 color(1, 1, 1, 1);
-                if (space->exists("color"))
-                {
-                    space->getColor("color", &color);
-                }
-
-                SliderIcon* sliderIcon = SliderIcon::create(space->getId(), *theme->_texture, minCapRegion, maxCapRegion, markerRegion, trackRegion, color);
-                theme->_sliders.push_back(sliderIcon);
+                theme->_images.push_back(Image::create(tw, th, space, Vector4::one()));
             }
-            else if (strcmp(spacename, "cursor") == 0)
+            else if (strcmp(spacename, "imageList") == 0)
             {
-                Vector4 regionVector;
-                space->getVector4("region", &regionVector);
-                const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
-
-                Vector4 color(1, 1, 1, 1);
-                if (space->exists("color"))
-                {
-                    space->getColor("color", &color);
-                }
-
-                Cursor* cursor = Cursor::create(space->getId(), *theme->_texture, region, color);
-                theme->_cursors.push_back(cursor);
+                theme->_imageLists.push_back(ImageList::create(tw, th, space));
             }
-            else if (strcmp(spacename, "container") == 0)
+            else if (strcmp(spacename, "skin") == 0)
             {
                 Theme::Border border;
                 Properties* innerSpace = space->getNextNamespace();
@@ -203,8 +137,8 @@ namespace gameplay
                     space->getColor("color", &color);
                 }
 
-                ContainerRegion* container = ContainerRegion::create(space->getId(), *theme->_texture, region, border, color);
-                theme->_containers.push_back(container);
+                Skin* skin = Skin::create(space->getId(), tw, th, region, border, color);
+                theme->_skins.push_back(skin);
             }
 
             space = themeProperties->getNextNamespace();
@@ -231,7 +165,7 @@ namespace gameplay
                 while (innerSpace != NULL)
                 {
                     const char* innerSpacename = innerSpace->getNamespace();
-                    if (strcmp(innerSpacename, "normal") == 0)
+                    if (strcmp(innerSpacename, "stateNormal") == 0)
                     {
                         Vector4 textColor(0, 0, 0, 1);
                         if (innerSpace->exists("textColor"))
@@ -254,26 +188,27 @@ namespace gameplay
                         }
                         bool rightToLeft = innerSpace->getBool("rightToLeft");
 
-                        Icon* checkBoxIcon = NULL;
-                        Icon* radioButtonIcon = NULL;
-                        SliderIcon* sliderIcon = NULL;
-                        Cursor* textCursor = NULL;
-                        Cursor* mouseCursor = NULL;
-                        ContainerRegion* containerRegion = NULL;
-                        theme->lookUpSprites(innerSpace, &checkBoxIcon, &radioButtonIcon, &sliderIcon, &textCursor, &mouseCursor, &containerRegion);
+                        float opacity = 1.0f;
+                        if (innerSpace->exists("opacity"))
+                        {
+                            opacity = innerSpace->getFloat("opacity");
+                        }
+
+                        ImageList* imageList = NULL;
+                        Image* cursor = NULL;
+                        Skin* skin = NULL;
+                        theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
 
                         normal = Theme::Style::Overlay::create();
-                        normal->setContainerRegion(containerRegion);
-                        normal->setTextCursor(textCursor);
-                        normal->setMouseCursor(mouseCursor);
-                        normal->setCheckBoxIcon(checkBoxIcon);
-                        normal->setRadioButtonIcon(radioButtonIcon);
-                        normal->setSliderIcon(sliderIcon);
+                        normal->setSkin(skin);
+                        normal->setCursor(cursor);
+                        normal->setImageList(imageList);
                         normal->setTextColor(textColor);
                         normal->setFont(font);
                         normal->setFontSize(fontSize);
                         normal->setTextAlignment(alignment);
                         normal->setTextRightToLeft(rightToLeft);
+                        normal->setOpacity(opacity);
 
                         theme->_fonts.insert(font);
 
@@ -308,7 +243,7 @@ namespace gameplay
                         padding.left = innerSpace->getFloat("left");
                         padding.right = innerSpace->getFloat("right");
                     }
-                    else if (strcmp(innerSpacename, "normal") != 0)
+                    else if (strcmp(innerSpacename, "stateNormal") != 0)
                     {
                         // Either OVERLAY_FOCUS or OVERLAY_ACTIVE.
                         // If a property isn't specified, it inherits from OVERLAY_NORMAL.
@@ -360,94 +295,78 @@ namespace gameplay
                             rightToLeft = normal->getTextRightToLeft();
                         }
 
-
-                        Icon* checkBoxIcon = NULL;
-                        Icon* radioButtonIcon = NULL;
-                        SliderIcon* sliderIcon = NULL;
-                        Cursor* textCursor = NULL;
-                        Cursor* mouseCursor = NULL;
-                        ContainerRegion* containerRegion = NULL;
-                        theme->lookUpSprites(innerSpace, &checkBoxIcon, &radioButtonIcon, &sliderIcon, &textCursor, &mouseCursor, &containerRegion);
-
-                        if (!checkBoxIcon)
+                        float opacity;
+                        if (innerSpace->exists("opacity"))
                         {
-                            checkBoxIcon = normal->getCheckBoxIcon();
+                            opacity = innerSpace->getFloat("opacity");
                         }
-                        
-                        if (!radioButtonIcon)
-                        {
-                            radioButtonIcon = normal->getRadioButtonIcon();
-                        }
-                        
-                        if (!sliderIcon)
+                        else
                         {
-                            sliderIcon = normal->getSliderIcon();
+                            opacity = normal->getOpacity();
                         }
 
-                        if (!textCursor)
+                        ImageList* imageList = NULL;
+                        Image* cursor = NULL;
+                        Skin* skin = NULL;
+                        theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
+
+                        if (!imageList)
                         {
-                            textCursor = normal->getTextCursor();
+                            imageList = normal->getImageList();
                         }
 
-                        if (!mouseCursor)
+                        if (!cursor)
                         {
-                            mouseCursor = normal->getMouseCursor();
+                            cursor = normal->getCursor();
                         }
                         
-                        if (!containerRegion)
+                        if (!skin)
                         {
-                            containerRegion = normal->getContainerRegion();
+                            skin = normal->getSkin();
                         }
 
-                        if (strcmp(innerSpacename, "focus") == 0)
+                        if (strcmp(innerSpacename, "stateFocus") == 0)
                         {
                             focus = Theme::Style::Overlay::create();
-                            focus->setContainerRegion(containerRegion);
-                            focus->setCheckBoxIcon(checkBoxIcon);
-                            focus->setTextCursor(textCursor);
-                            focus->setMouseCursor(mouseCursor);
-                            focus->setCheckBoxIcon(checkBoxIcon);
-                            focus->setRadioButtonIcon(radioButtonIcon);
-                            focus->setSliderIcon(sliderIcon);
+                            focus->setSkin(skin);
+                            focus->setCursor(cursor);
+                            focus->setImageList(imageList);
                             focus->setTextColor(textColor);
                             focus->setFont(font);
                             focus->setFontSize(fontSize);
                             focus->setTextAlignment(alignment);
                             focus->setTextRightToLeft(rightToLeft);
+                            focus->setOpacity(opacity);
 
                             theme->_fonts.insert(font);
                         }
-                        else if (strcmp(innerSpacename, "active") == 0)
+                        else if (strcmp(innerSpacename, "stateActive") == 0)
                         {
                             active = Theme::Style::Overlay::create();
-                            active->setContainerRegion(containerRegion);
-                            active->setTextCursor(textCursor);
-                            active->setMouseCursor(mouseCursor);
-                            active->setCheckBoxIcon(checkBoxIcon);
-                            active->setRadioButtonIcon(radioButtonIcon);
-                            active->setSliderIcon(sliderIcon);
+                            active->setSkin(skin);
+                            active->setCursor(cursor);
+                            active->setImageList(imageList);
                             active->setTextColor(textColor);
                             active->setFont(font);
                             active->setFontSize(fontSize);
                             active->setTextAlignment(alignment);
                             active->setTextRightToLeft(rightToLeft);
+                            active->setOpacity(opacity);
 
                             theme->_fonts.insert(font);
                         }
-                        else if (strcmp(innerSpacename, "disabled") == 0)
+                        else if (strcmp(innerSpacename, "stateDisabled") == 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->setSkin(skin);
+                            disabled->setCursor(cursor);
+                            disabled->setImageList(imageList);
                             disabled->setTextColor(textColor);
                             disabled->setFont(font);
                             disabled->setFontSize(fontSize);
                             disabled->setTextAlignment(alignment);
                             disabled->setTextRightToLeft(rightToLeft);
+                            disabled->setOpacity(opacity);
 
                             theme->_fonts.insert(font);
                         }
@@ -474,7 +393,7 @@ namespace gameplay
                     disabled->addRef();
                 }
 
-                Theme::Style* s = new Theme::Style(space->getId(), margin, padding, normal, focus, active, disabled);
+                Theme::Style* s = new Theme::Style(space->getId(), tw, th, margin, padding, normal, focus, active, disabled);
                 theme->_styles.push_back(s);
             }
 
@@ -519,223 +438,222 @@ namespace gameplay
         return _spriteBatch;
     }
 
-    /***************
-     * Theme::Icon *
-     ***************/
-    Theme::Icon* Theme::Icon::create(const char* id, const Texture& texture, const Vector2& size,
-                                     const Vector2& offPosition, const Vector2& onPosition, const Vector4& color)
+    /**************
+     * Theme::UVs *
+     **************/
+    Theme::UVs::UVs()
+        : u1(0), v1(0), u2(0), v2(0)
     {
-        Icon* icon = new Icon(texture, size, offPosition, onPosition, color);
-
-        if (id)
-        {
-            icon->_id = id;
-        }
-
-        return icon;
     }
 
-    Theme::Icon::Icon(const Texture& texture, const Vector2& size,
-                      const Vector2& offPosition, const Vector2& onPosition, const Vector4& color)
-                      : _size(size), _color(color)
+    Theme::UVs::UVs(float u1, float v1, float u2, float v2)
+        : u1(u1), v1(v1), u2(u2), v2(v2)
     {
-        generateUVs(texture, offPosition.x, offPosition.y, size.x, size.y, &_off);
-        generateUVs(texture, onPosition.x, onPosition.y, size.x, size.y, &_on);
     }
 
-    Theme::Icon::~Icon()
+    const Theme::UVs& Theme::UVs::empty()
     {
+        static UVs empty(0, 0, 0, 0);
+        return empty;
     }
 
-    const char* Theme::Icon::getId() const
-    {
-        return _id.c_str();
-    }
-
-    const Vector2& Theme::Icon::getSize() const
+    /**********************
+     * Theme::SideRegions *
+     **********************/
+    const Theme::SideRegions& Theme::SideRegions::empty()
     {
-        return _size;
+        static SideRegions empty;
+        return empty;
     }
 
-    const Vector4& Theme::Icon::getColor() const
+    /****************
+     * Theme::Image *
+     ****************/
+    Theme::Image::Image(float tw, float th, const Rectangle& region, const Vector4& color)
+        : _region(region), _color(color)
     {
-        return _color;
+        generateUVs(tw, th, region.x, region.y, region.width, region.height, &_uvs);
     }
 
-    const Theme::UVs& Theme::Icon::getOffUVs() const
+    Theme::Image::~Image()
     {
-        return _off;
     }
 
-    const Theme::UVs& Theme::Icon::getOnUVs() const
+    Theme::Image* Theme::Image::create(float tw, float th, Properties* properties, const Vector4& defaultColor)
     {
-        return _on;
-    }
-
+        Vector4 regionVector;                
+        properties->getVector4("region", &regionVector);
+        const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
 
-    /*********************
-     * Theme::SliderIcon *
-     *********************/
-    Theme::SliderIcon* Theme::SliderIcon::create(const char* id, const Texture& texture, const Vector4& minCapRegion,
-            const Vector4& maxCapRegion, const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color)
-    {
-        SliderIcon* sliderIcon = new SliderIcon(texture, minCapRegion, maxCapRegion, markerRegion, trackRegion, color);
+        Vector4 color;
+        if (properties->exists("color"))
+        {
+            properties->getColor("color", &color);
+        }
+        else
+        {
+            color.set(defaultColor);
+        }
 
+        Image* image = new Image(tw, th, region, color);
+        const char* id = properties->getId();
         if (id)
         {
-            sliderIcon->_id = id;
+            image->_id = id;
         }
 
-        return sliderIcon;
+        return image;
     }
 
-    Theme::SliderIcon::SliderIcon(const Texture& texture, const Vector4& minCapRegion, const Vector4& maxCapRegion,
-                                  const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color)
-                                  : _color(color)
+    const char* Theme::Image::getId() const
     {
-        _minCapSize.set(minCapRegion.z, minCapRegion.w);
-        _maxCapSize.set(maxCapRegion.z, maxCapRegion.w);
-        _markerSize.set(markerRegion.z, markerRegion.w);
-        _trackSize.set(trackRegion.z, trackRegion.w);
-
-        generateUVs(texture, minCapRegion.x, minCapRegion.y, minCapRegion.z, minCapRegion.w, &_minCap);
-        generateUVs(texture, maxCapRegion.x, maxCapRegion.y, maxCapRegion.z, maxCapRegion.w, &_maxCap);
-        generateUVs(texture, markerRegion.x, markerRegion.y, markerRegion.z, markerRegion.w, &_marker);
-        generateUVs(texture, trackRegion.x, trackRegion.y, trackRegion.z, trackRegion.w, &_track);
+        return _id.c_str();
     }
 
-    Theme::SliderIcon::~SliderIcon()
+    const Theme::UVs& Theme::Image::getUVs() const
     {
+        return _uvs;
     }
 
-    const char* Theme::SliderIcon::getId() const
+    const Rectangle& Theme::Image::getRegion() const
     {
-        return _id.c_str();
+        return _region;
     }
 
-    const Theme::UVs& Theme::SliderIcon::getMinCapUVs() const
+    const Vector4& Theme::Image::getColor() const
     {
-        return _minCap;
+        return _color;
     }
 
-    const Theme::UVs& Theme::SliderIcon::getMaxCapUVs() const
+    /********************
+     * Theme::ImageList *
+     ********************/
+    Theme::ImageList::ImageList(const Vector4& color) : _color(color)
     {
-        return _maxCap;
-    }
-    
-    const Theme::UVs& Theme::SliderIcon::getMarkerUVs() const
-    {
-        return _marker;
-    }
-    
-    const Theme::UVs& Theme::SliderIcon::getTrackUVs() const
-    {
-        return _track;
     }
 
-    const Vector2& Theme::SliderIcon::getMinCapSize() const
+    Theme::ImageList::ImageList(const ImageList& copy)
     {
-        return _minCapSize;
-    }
+        _id = copy._id;
+        _color = copy._color;
 
-    const Vector2& Theme::SliderIcon::getMaxCapSize() const
-    {
-        return _maxCapSize;
+        std::vector<Image*>::const_iterator it;
+        for (it = copy._images.begin(); it != copy._images.end(); it++)
+        {
+            Image* image = *it;
+            _images.push_back(new Image(*image));
+        }
     }
 
-    const Vector2& Theme::SliderIcon::getMarkerSize() const
+    Theme::ImageList::~ImageList()
     {
-        return _markerSize;
+        std::vector<Image*>::const_iterator it;
+        for (it = _images.begin(); it != _images.end(); it++)
+        {
+            Image* image = *it;
+            SAFE_RELEASE(image);
+        }
     }
 
-    const Vector2& Theme::SliderIcon::getTrackSize() const
+    Theme::ImageList* Theme::ImageList::create(float tw, float th, Properties* properties)
     {
-        return _trackSize;
-    }
+        Vector4 color(1, 1, 1, 1);
+        if (properties->exists("color"))
+        {
+            properties->getColor("color", &color);
+        }
 
-    const Vector4& Theme::SliderIcon::getColor() const
-    {
-        return _color;
-    }
+        ImageList* imageList = new ImageList(color);
 
-    /*****************
-     * Theme::Cursor *
-     *****************/
-    Theme::Cursor* Theme::Cursor::create(const char* id, const Texture& texture, const Rectangle& region, const Vector4& color)
-    {
-        Cursor* cursor = new Cursor(texture, region, color);
-        
+        const char* id = properties->getId();
         if (id)
         {
-            cursor->_id = id;
+            imageList->_id = id;
+        }
+
+        Properties* space = properties->getNextNamespace();
+        while (space != NULL)
+        {
+            Image* image = Image::create(tw, th, space, color);
+            imageList->_images.push_back(image);
+            space = properties->getNextNamespace();
         }
 
-        return cursor;
+        return imageList;
     }
 
-    Theme::Cursor::Cursor(const Texture& texture, const Rectangle& region, const Vector4& color)
-        : _color(color)
+    const char* Theme::ImageList::getId() const
     {
-        _size.set(region.width, region.height);
-        generateUVs(texture, region.x, region.y, region.width, region.height, &_uvs);
+        return _id.c_str();
     }
 
-    Theme::Cursor::~Cursor()
+    Theme::Image* Theme::ImageList::getImage(const char* imageId) const
     {
+        std::vector<Image*>::const_iterator it;
+        for (it = _images.begin(); it != _images.end(); it++)
+        {
+            Image* image = *it;
+            if (strcmp(image->getId(), imageId) == 0)
+            {
+                return image;
+            }
+        }
+
+        return NULL;
     }
 
-    const char* Theme::Cursor::getId() const
+    /***************
+     * Theme::Skin *
+     ***************/
+    Theme::Skin* Theme::Skin::create(const char* id, float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color)
     {
-        return _id.data();
+        Skin* skin = new Skin(tw, th, region, border, color);
+
+        if (id)
+        {
+            skin->_id = id;
+        }
+
+        return skin;
     }
 
-    const Theme::UVs& Theme::Cursor::getUVs() const
+    Theme::Skin::Skin(float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color)
+        : _border(border), _color(color), _region(region)
     {
-        return _uvs;
+        setRegion(region, tw, th);
     }
 
-    const Vector2& Theme::Cursor::getSize() const
+    Theme::Skin::~Skin()
     {
-        return _size;
     }
 
-    const Vector4& Theme::Cursor::getColor() const
+    const char* Theme::Skin::getId() const
     {
-        return _color;
+        return _id.c_str();
     }
 
-    /**************************
-     * Theme::ContainerRegion *
-     **************************/
-    Theme::ContainerRegion* Theme::ContainerRegion::create(const char* id, const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color)
+    const Theme::Border& Theme::Skin::getBorder() const
     {
-        ContainerRegion* containerRegion = new ContainerRegion(texture, region, border, color);
-
-        if (id)
-        {
-            containerRegion->_id = id;
-        }
-
-        return containerRegion;
+        return _border;
     }
 
-    Theme::ContainerRegion::ContainerRegion(const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color)
-        : _border(border), _color(color)
+    const Rectangle& Theme::Skin::getRegion() const
     {
-        // Need to convert pixel coords to unit space by dividing by texture size.
-        float tw = 1.0f / (float)texture.getWidth();
-        float th = 1.0f / (float)texture.getHeight();
+        return _region;
+    }
 
+    void Theme::Skin::setRegion(const Rectangle& region, float tw, float th)
+    {
         // Can calculate all measurements in advance.
         float leftEdge = region.x * tw;
         float rightEdge = (region.x + region.width) * tw;
-        float leftBorder = (region.x + border.left) * tw;
-        float rightBorder = (region.x + region.width - border.right) * tw;
+        float leftBorder = (region.x + _border.left) * tw;
+        float rightBorder = (region.x + region.width - _border.right) * tw;
 
         float topEdge = 1.0f - (region.y * th);
         float bottomEdge = 1.0f - ((region.y + region.height) * th);
-        float topBorder = 1.0f - ((region.y + border.top) * th);
-        float bottomBorder = 1.0f - ((region.y + region.height - border.bottom) * th);
+        float topBorder = 1.0f - ((region.y + _border.top) * th);
+        float bottomBorder = 1.0f - ((region.y + region.height - _border.bottom) * th);
 
         // There are 9 sets of UVs to set.
         _uvs[TOP_LEFT].u1 = leftEdge;
@@ -784,26 +702,12 @@ namespace gameplay
         _uvs[BOTTOM_RIGHT].v2 = bottomEdge;
     }
 
-    Theme::ContainerRegion::~ContainerRegion()
-    {
-    }
-
-    const char* Theme::ContainerRegion::getId() const
-    {
-        return _id.c_str();
-    }
-
-    const Theme::Border& Theme::ContainerRegion::getBorder() const
-    {
-        return _border;
-    }
-
-    const Theme::UVs& Theme::ContainerRegion::getUVs(ContainerArea area) const
+    const Theme::UVs& Theme::Skin::getUVs(SkinArea area) const
     {
         return _uvs[area];
     }
 
-    const Vector4& Theme::ContainerRegion::getColor() const
+    const Vector4& Theme::Skin::getColor() const
     {
         return _color;
     }
@@ -811,9 +715,10 @@ namespace gameplay
     /****************
      * Theme::Style *
      ****************/
-    Theme::Style::Style(const char* id, const Theme::Margin& margin, const Theme::Padding& padding,
+    Theme::Style::Style(const char* id, float tw, float th,
+            const Theme::Margin& margin, const Theme::Padding& padding,
             Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled)
-        : _id(id), _margin(margin), _padding(padding)
+        : _id(id), _tw(tw), _th(th), _margin(margin), _padding(padding)
     {
         _overlays[OVERLAY_NORMAL] = normal;
         _overlays[OVERLAY_FOCUS] = focus;
@@ -821,6 +726,20 @@ namespace gameplay
         _overlays[OVERLAY_DISABLED] = disabled;
     }
 
+    Theme::Style::Style(const Style& copy)
+    {
+        _id = copy._id;
+        _margin = copy._margin;
+        _padding = copy._padding;
+        _tw = copy._tw;
+        _th = copy._th;
+
+        for (int i = 0; i < MAX_OVERLAYS; i++)
+        {
+            _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
+        }
+    }
+
     Theme::Style::~Style()
     {
         for (unsigned int i = 0; i < MAX_OVERLAYS; i++)
@@ -839,16 +758,32 @@ namespace gameplay
         return _overlays[overlayType];
     }
 
+    void Theme::Style::setMargin(float top, float bottom, float left, float right)
+    {
+        _margin.top = top;
+        _margin.bottom = bottom;
+        _margin.left = left;
+        _margin.right = right;
+    }
+
     const Theme::Margin& Theme::Style::getMargin() const
     {
         return _margin;
     }
 
+    void Theme::Style::setPadding(float top, float bottom, float left, float right)
+    {
+        _padding.top = top;
+        _padding.bottom = bottom;
+        _padding.left = left;
+        _padding.right = right;
+    }
+
     const Theme::Padding& Theme::Style::getPadding() const
     {
         return _padding;
     }
-
+    
     /*************************
      * Theme::Style::Overlay *
      *************************/
@@ -858,21 +793,123 @@ namespace gameplay
         return overlay;
     }
 
-    Theme::Style::Overlay::Overlay() : _container(NULL), _textCursor(NULL), _mouseCursor(NULL), _checkBoxIcon(NULL), _radioButtonIcon(NULL), _sliderIcon(NULL), _font(NULL)
+    Theme::Style::Overlay::Overlay() : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL)
     {
     }
 
+    Theme::Style::Overlay::Overlay(const Overlay& copy) : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL)
+    {
+        if (copy._skin)
+        {
+            _skin = new Skin(*copy._skin);
+        }
+        if (copy._cursor)
+        {
+            _cursor = new Image(*copy._cursor);
+        }
+        if (copy._imageList)
+        {
+            _imageList = new ImageList(*copy._imageList);
+        }
+
+        _font = copy._font;
+        _fontSize = copy._fontSize;
+        _alignment = copy._alignment;
+        _textRightToLeft = copy._textRightToLeft;
+        _textColor = Vector4(copy._textColor);
+        _opacity = copy._opacity;
+
+        if (_font)
+        {
+            _font->addRef();
+        }
+    }
+
     Theme::Style::Overlay::~Overlay()
     {
-        SAFE_RELEASE(_container);
-        SAFE_RELEASE(_checkBoxIcon);
-        SAFE_RELEASE(_radioButtonIcon);
-        SAFE_RELEASE(_sliderIcon);
-        SAFE_RELEASE(_mouseCursor);
-        SAFE_RELEASE(_textCursor);
+        SAFE_RELEASE(_skin);
+        SAFE_RELEASE(_imageList);
+        SAFE_RELEASE(_cursor);
         SAFE_RELEASE(_font);
     }
 
+    float Theme::Style::Overlay::getOpacity() const
+    {
+        return _opacity;
+    }
+
+    void Theme::Style::Overlay::setOpacity(float opacity)
+    {
+        _opacity = opacity;
+    }
+
+    void Theme::Style::Overlay::setBorder(float top, float bottom, float left, float right)
+    {
+        if (_skin)
+        {
+            _skin->_border.top = top;
+            _skin->_border.bottom = bottom;
+            _skin->_border.left = left;
+            _skin->_border.right = right;
+        }
+    }
+
+    const Theme::Border& Theme::Style::Overlay::getBorder() const
+    {
+        if (_skin)
+        {
+            return _skin->getBorder();
+        }
+        else
+        {
+            return Theme::Border::empty();
+        }
+    }
+
+    void Theme::Style::Overlay::setSkinColor(const Vector4& color)
+    {
+        if (_skin)
+        {
+            _skin->_color.set(color);
+        }
+    }
+
+    const Vector4& Theme::Style::Overlay::getSkinColor() const
+    {
+        if (_skin)
+        {
+            return _skin->getColor();
+        }
+
+        return Vector4::one();
+    }
+
+    void Theme::Style::Overlay::setSkinRegion(const Rectangle& region, float tw, float th)
+    {
+        assert(_skin);
+        _skin->setRegion(region, tw, th);
+    }
+
+    const Rectangle& Theme::Style::Overlay::getSkinRegion() const
+    {
+        if (_skin)
+        {
+            return _skin->getRegion();
+        }
+
+        return Rectangle::empty();
+    }
+
+    const Theme::UVs& Theme::Style::Overlay::getSkinUVs(Theme::Skin::SkinArea area) const
+    {
+        if (_skin)
+        {
+            return _skin->_uvs[area];
+        }
+
+        return UVs::empty();
+    }
+
     Font* Theme::Style::Overlay::getFont() const
     {
         return _font;
@@ -933,213 +970,260 @@ namespace gameplay
         _textColor = color;
     }
 
-    Theme::Cursor* Theme::Style::Overlay::getTextCursor() const
+    const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
     {
-        return _textCursor;
+        Image* image = _imageList->getImage(id);
+        if (image)
+        {
+            return image->getRegion();
+        }
+        else
+        {
+            return Rectangle::empty();
+        }
+    }
+    
+    void Theme::Style::Overlay::setImageRegion(const char* id, const Rectangle& region, float tw, float th)
+    {
+        Image* image = _imageList->getImage(id);
+        assert(image);
+        image->_region.set(region);
+        generateUVs(tw, th, region.x, region.y, region.width, region.height, &(image->_uvs));
     }
 
-    void Theme::Style::Overlay::setTextCursor(Theme::Cursor* cursor)
+    const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
     {
-        if (_textCursor != cursor)
+        Image* image = _imageList->getImage(id);
+        if (image)
         {
-            SAFE_RELEASE(_textCursor);
-
-            _textCursor = cursor;
-            
-            if (cursor)
-            {
-                cursor->addRef();
-            }
+            return image->getColor();
+        }
+        else
+        {
+            return Vector4::zero();
         }
     }
 
-    Theme::Cursor* Theme::Style::Overlay::getMouseCursor() const
+    void Theme::Style::Overlay::setImageColor(const char* id, const Vector4& color)
     {
-        return _mouseCursor;
+        Image* image = _imageList->getImage(id);
+        assert(image);
+        image->_color.set(color);
     }
 
-    void Theme::Style::Overlay::setMouseCursor(Theme::Cursor* cursor)
+    const Theme::UVs& Theme::Style::Overlay::getImageUVs(const char* id) const
     {
-        if (_mouseCursor != cursor)
+        Image* image = _imageList->getImage(id);
+        if (image)
         {
-            SAFE_RELEASE(_mouseCursor);
-
-            _mouseCursor = cursor;
-            
-            if (cursor)
-            {
-                cursor->addRef();
-            }
+            return image->getUVs();
+        }
+        else
+        {
+            return UVs::empty();
         }
     }
 
-    void Theme::Style::Overlay::setCheckBoxIcon(Icon* icon)
+    const Rectangle& Theme::Style::Overlay::getCursorRegion() const
     {
-        if (_checkBoxIcon != icon)
+        if (_cursor)
         {
-            SAFE_RELEASE(_checkBoxIcon);
+            return _cursor->getRegion();
+        }
+        else
+        {
+            return Rectangle::empty();
+        }
+    }
+    
+    void Theme::Style::Overlay::setCursorRegion(const Rectangle& region, float tw, float th)
+    {
+        assert(_cursor);
+        _cursor->_region.set(region);
+        generateUVs(tw, th, region.x, region.y, region.width, region.height, &(_cursor->_uvs));
+    }
 
-            _checkBoxIcon = icon;
-            
-            if (icon)
-            {
-                icon->addRef();
-            }
+    const Vector4& Theme::Style::Overlay::getCursorColor() const
+    {
+        if (_cursor)
+        {
+            return _cursor->getColor();
+        }
+        else
+        {
+            return Vector4::zero();
         }
     }
 
-    Theme::Icon* Theme::Style::Overlay::getCheckBoxIcon() const
+    void Theme::Style::Overlay::setCursorColor(const Vector4& color)
     {
-        return _checkBoxIcon;
+        assert(_cursor);
+        _cursor->_color.set(color);
     }
 
-    void Theme::Style::Overlay::setRadioButtonIcon(Icon* icon)
+    const Theme::UVs Theme::Style::Overlay::getCursorUVs() const
     {
-        if (_radioButtonIcon != icon)
+        if (_cursor)
+        {
+            return _cursor->getUVs();
+        }
+        else
         {
-            SAFE_RELEASE(_radioButtonIcon);
+            return UVs::empty();
+        }
+    }
 
-            _radioButtonIcon = icon;
+    void Theme::Style::Overlay::setSkin(Skin* skin)
+    {
+        if (_skin != skin)
+        {
+            SAFE_RELEASE(_skin);
+            _skin = skin;
 
-            if (icon)
+            if (skin)
             {
-                icon->addRef();
+                skin->addRef();
             }
         }
     }
 
-    Theme::Icon* Theme::Style::Overlay::getRadioButtonIcon() const
+    Theme::Skin* Theme::Style::Overlay::getSkin() const
     {
-        return _radioButtonIcon;
+        return _skin;
     }
 
-    void Theme::Style::Overlay::setSliderIcon(SliderIcon* slider)
+    void Theme::Style::Overlay::setCursor(Image* cursor)
     {
-        if (_sliderIcon != slider)
+        if (_cursor != cursor)
         {
-            SAFE_RELEASE(_sliderIcon);
+            SAFE_RELEASE(_cursor);
+            _cursor = cursor;
 
-            _sliderIcon = slider;
-
-            if (slider)
+            if (cursor)
             {
-                slider->addRef();
+                cursor->addRef();
             }
         }
     }
 
-    Theme::SliderIcon* Theme::Style::Overlay::getSliderIcon() const
+    Theme::Image* Theme::Style::Overlay::getCursor() const
     {
-        return _sliderIcon;
+        return _cursor;
     }
-
-    void Theme::Style::Overlay::setContainerRegion(ContainerRegion* container)
+            
+    void Theme::Style::Overlay::setImageList(ImageList* imageList)
     {
-        if (_container != container)
+        if (_imageList != imageList)
         {
-            SAFE_RELEASE(_container);
+            SAFE_RELEASE(_imageList);
+            _imageList = imageList;
 
-            _container = container;
-
-            if (container)
+            if (imageList)
             {
-                container->addRef();
+                imageList->addRef();
             }
         }
     }
-
-    Theme::ContainerRegion* Theme::Style::Overlay::getContainerRegion() const
+    
+    Theme::ImageList* Theme::Style::Overlay::getImageList() const
     {
-        return _container;
+        return _imageList;
     }
 
-    void Theme::generateUVs(const Texture& texture, float x, float y, float width, float height, UVs* uvs)
+    // Implementation of AnimationHandler
+    unsigned int Theme::Style::Overlay::getAnimationPropertyComponentCount(int propertyId) const
     {
-        float tw = 1.0f / texture.getWidth();
-        float th = 1.0f / texture.getHeight();
-
-        uvs->u1 = x * tw;
-        uvs->u2 = (x + width) * tw;
-        uvs->v1 = 1.0f - (y * th);
-        uvs->v2 = 1.0f - ((y + height) * th);
+        switch(propertyId)
+        {
+        case Theme::Style::Overlay::ANIMATE_OPACITY:
+            return 1;
+        default:
+            return -1;
+        }
     }
 
-    void Theme::lookUpSprites(const Properties* overlaySpace, Icon** checkBoxIcon, Icon** radioButtonIcon, SliderIcon** sliderIcon,
-                              Cursor** textCursor, Cursor** mouseCursor, ContainerRegion** containerRegion)
+    void Theme::Style::Overlay::getAnimationPropertyValue(int propertyId, AnimationValue* value)
     {
-        const char* checkBoxString = overlaySpace->getString("checkBox");
-        if (checkBoxString)
+        switch(propertyId)
         {
-            for (unsigned int i = 0; i < _icons.size(); i++)
-            {
-                if (strcmp(_icons[i]->getId(), checkBoxString) == 0)
-                {
-                    *checkBoxIcon = _icons[i];
-                    break;
-                }
-            }
+        case ANIMATE_OPACITY:
+            value->setFloat(0, _opacity);
+            break;
+        default:
+            break;
         }
+    }
 
-        const char* radioButtonString = overlaySpace->getString("radioButton");
-        if (radioButtonString)
+    void Theme::Style::Overlay::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
+    {
+        switch(propertyId)
         {
-            for (unsigned int i = 0; i < _icons.size(); i++)
+            case ANIMATE_OPACITY:
             {
-                if (strcmp(_icons[i]->getId(), radioButtonString) == 0)
+                float opacity = value->getFloat(0);
+                if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
                 {
-                    *radioButtonIcon = _icons[i];
-                    break;
+                    _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
                 }
-            }
-        }
-
-        const char* sliderString = overlaySpace->getString("slider");
-        if (sliderString)
-        {
-            for (unsigned int i = 0; i < _sliders.size(); ++i)
-            {
-                if (strcmp(_sliders[i]->getId(), sliderString) == 0)
+                else
                 {
-                    *sliderIcon = _sliders[i];
-                    break;
+                    opacity = Curve::lerp(blendWeight, _opacity, opacity);
                 }
+                _opacity = opacity;
+                break;
             }
+            default:
+                break;
         }
+    }
+    
+    /**
+     * Theme utility methods.
+     */
+    void Theme::generateUVs(float tw, float th, float x, float y, float width, float height, UVs* uvs)
+    {
+        uvs->u1 = x * tw;
+        uvs->u2 = (x + width) * tw;
+        uvs->v1 = 1.0f - (y * th);
+        uvs->v2 = 1.0f - ((y + height) * th);
+    }
 
-        const char* textCursorString = overlaySpace->getString("textCursor");
-        if (textCursorString)
+    void Theme::lookUpSprites(const Properties* overlaySpace, ImageList** imageList, Image** cursor, Skin** Skin)
+    {
+        const char* imageListString = overlaySpace->getString("imageList");
+        if (imageListString)
         {
-            for (unsigned int i = 0; i < _cursors.size(); ++i)
+            for (unsigned int i = 0; i < _imageLists.size(); ++i)
             {
-                if (strcmp(_cursors[i]->getId(), textCursorString) == 0)
+                if (strcmp(_imageLists[i]->getId(), imageListString) == 0)
                 {
-                    *textCursor = _cursors[i];
+                    *imageList = _imageLists[i];
                     break;
                 }
             }
         }
 
-        const char* mouseCursorString = overlaySpace->getString("mouseCursor");
-        if (mouseCursorString)
+        const char* cursorString = overlaySpace->getString("cursor");
+        if (cursorString)
         {
-            for (unsigned int i = 0; i < _cursors.size(); ++i)
+            for (unsigned int i = 0; i < _images.size(); ++i)
             {
-                if (strcmp(_cursors[i]->getId(), mouseCursorString) == 0)
+                if (strcmp(_images[i]->getId(), cursorString) == 0)
                 {
-                    *mouseCursor = _cursors[i];
+                    *cursor = _images[i];
                     break;
                 }
             }
         }
 
-        const char* containerString = overlaySpace->getString("container");
-        if (containerString)
+        const char* skinString = overlaySpace->getString("skin");
+        if (skinString)
         {
-            for (unsigned int i = 0; i < _containers.size(); ++i)
+            for (unsigned int i = 0; i < _skins.size(); ++i)
             {
-                if (strcmp(_containers[i]->getId(), containerString) == 0)
+                if (strcmp(_skins[i]->getId(), skinString) == 0)
                 {
-                    *containerRegion = _containers[i];
+                    *Skin = _skins[i];
                     break;
                 }
             }

+ 137 - 237
gameplay/src/Theme.h

@@ -18,7 +18,7 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
  * A theme is created and stored as part of a form and represents its appearance.
  * Once loaded, the appearance properties can be retrieved from their style IDs and set on other
  * UI controls.  A Theme has one property, 'texture', which points to an image containing
- * all the Icon, Cursor, Slider, and Container sprites used by the theme.  Each set of sprites within
+ * all the Icon, Cursor, Slider, and Skin sprites used by the theme.  Each set of sprites within
  * the texture is described in its own namespace.  The rest of the Theme consists of Style namespaces.
  * A Style describes the border, margin, and padding of a Control, what icons and cursors (if any) are
  * associated with a Control, and Font properties to apply to a Control's text.
@@ -54,7 +54,7 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
  *    }
  *   
  *    // Defines the border and background of a Control.
- *    container <containerID>
+ *    Skin <SkinID>
  *    {
  *        // The corners and edges of the given region will be used as border sprites.
  *        border
@@ -65,8 +65,8 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
  *            right   =   <int>   // Width of right border, right corners.
  *        }
  *       
- *        region  =   <x, y, width, height>   // Total container region including entire border.
- *        color   =   <#ffffffff>             // Tint to apply to container sprites.
+ *        region  =   <x, y, width, height>   // Total Skin region including entire border.
+ *        color   =   <#ffffffff>             // Tint to apply to Skin sprites.
  *    }
  *   
  *    style <styleID>
@@ -92,7 +92,7 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
  *        // This overlay is used when a control is enabled but not active or focused.
  *        normal
  *        {
- *            container   =   <containerID>               // Container to use for border and background sprites.
+ *            Skin   =   <SkinID>               // Skin to use for border and background sprites.
  *            checkBox    =   <iconID>                    // Icon to use for checkbox sprites.
  *            radioButton =   <iconID>                    // Icon to use for radioButton sprites.
  *            slider      =   <sliderID>                  // Slider to use for slider sprites.
@@ -120,8 +120,9 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
 class Theme: public Ref
 {
     friend class Control;
-    friend class Form;
     friend class Container;
+    friend class Form;
+    friend class Skin;
 
 public:
     class Style;
@@ -154,8 +155,14 @@ public:
     /**
      * Struct representing the UV coordinates of a rectangular image.
      */
-    typedef struct UVs
+    typedef class UVs
     {
+    public:
+        UVs();
+        UVs(float u1, float v1, float u2, float v2);
+
+        static const UVs& empty();
+
         float u1;
         float v1;
         float u2;
@@ -166,231 +173,76 @@ public:
      * Struct representing margin, border, and padding areas by
      * the width or height of each side.
      */
-    typedef struct padding
+    typedef class SideRegions
     {
+    public:
+        SideRegions() : top(0), bottom(0), left(0), right(0) {}
+
+        static const SideRegions& empty();
+
         float top;
         float bottom;
         float left;
         float right;
-
-        padding() : top(0), bottom(0), left(0), right(0) {}
     } Margin, Border, Padding;
 
-    /**
-     * An icon with two images representing "on" and "off" states.
-     */
-    class Icon : public Ref
+    class Image : public Ref
     {
         friend class Theme;
+        friend class Control;
 
     public:
-        /**
-         * Get this icon's ID.
-         *
-         * @return This icon's ID.
-         */
         const char* getId() const;
 
-        /**
-         * Get this icon's size.
-         *
-         * @return This icon's size.
-         */
-        const Vector2& getSize() const;
-        /**
-         * Get this icon's color.
-         *
-         * @return This icon's color.
-         */
-        const Vector4& getColor() const;
-
-        /**
-         * Get the UVs for this icon's off-state image.
-         *
-         * @return The UVs for this icon's off-state image.
-         */
-        const Theme::UVs& getOffUVs() const;
-
-        /**
-         * Get the UVs for this icon's on-state image.
-         *
-         * @return The UVs for this icon's on-state image.
-         */
-        const Theme::UVs& getOnUVs() const;
-
-    private:
-        Icon(const Texture& texture, const Vector2& size, const Vector2& offPosition, const Vector2& onPosition, const Vector4& color);
-        Icon(const Icon& copy);
-        ~Icon();
+        const UVs& getUVs() const;
 
-        static Icon* create(const char* id, const Texture& texture, const Vector2& size,
-                            const Vector2& offPosition, const Vector2& onPosition, const Vector4& color);
+        const Rectangle& getRegion() const;
 
-        std::string _id;
-        Vector2 _size;
-        Vector4 _color;
-        UVs _off;
-        UVs _on;
-    };
-
-    /**
-     * A set of four icons that make up a slider:
-     * two end-caps, a track, and a marker to be placed along the track.
-     */
-    class SliderIcon : public Ref
-    {
-        friend class Theme;
-
-    public:
-        /**
-         * Get this slider icon's ID.
-         *
-         * @return This slider icon's ID.
-         */
-        const char* getId() const;
-
-        /**
-         * Get the UVs for this slider's min-cap image.
-         *
-         * @return The UVs for this slider's min-cap image.
-         */
-        const Theme::UVs& getMinCapUVs() const;
-
-        /**
-         * Get the UVs for this slider's max-cap image.
-         *
-         * @return The UVs for this slider's max-cap image.
-         */
-        const Theme::UVs& getMaxCapUVs() const;
-
-        /**
-         * Get the UVs for this slider's marker image.
-         *
-         * @return The UVs for this slider's marker image.
-         */
-        const Theme::UVs& getMarkerUVs() const;
-
-        /**
-         * Get the UVs for this slider's track image.
-         *
-         * @return The UVs for this slider's track image.
-         */
-        const Theme::UVs& getTrackUVs() const;
-
-        /**
-         * Get this slider's min-cap size.
-         *
-         * @return This slider's min-cap size.
-         */
-        const Vector2& getMinCapSize() const;
-
-        /**
-         * Get this slider's max-cap size.
-         *
-         * @return This slider's max-cap size.
-         */
-        const Vector2& getMaxCapSize() const;
-
-        /**
-         * Get this slider's marker size.
-         *
-         * @return This slider's marker size.
-         */
-        const Vector2& getMarkerSize() const;
-
-        /**
-         * Get this slider's track size.
-         *
-         * @return This slider's track size.
-         */
-        const Vector2& getTrackSize() const;
-
-        /**
-         * Get this slider's color.
-         *
-         * @return This slider's color.
-         */
         const Vector4& getColor() const;
 
     private:
-        SliderIcon(const Texture& texture, const Vector4& minCapRegion, const Vector4& maxCapRegion,
-                   const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color);
-        SliderIcon(const SliderIcon& copy);
-        ~SliderIcon();
+        Image(float tw, float th, const Rectangle& region, const Vector4& color);
+        ~Image();
 
-        static SliderIcon* create(const char* id, const Texture& texture, const Vector4& minCapRegion, const Vector4& maxCapRegion,
-                                  const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color);
+        static Image* create(float tw, float th, Properties* properties, const Vector4& defaultColor);
 
         std::string _id;
-        UVs _minCap;
-        UVs _maxCap;
-        UVs _marker;
-        UVs _track;
-        Vector2 _minCapSize;
-        Vector2 _maxCapSize;
-        Vector2 _markerSize;
-        Vector2 _trackSize;
+        UVs _uvs;
+        Rectangle _region;
         Vector4 _color;
     };
-    
-    /**
-     * This class represents the appearance of a cursor.
-     */
-    class Cursor : public Ref
+
+    class ImageList : public Ref
     {
         friend class Theme;
+        friend class Control;
 
     public:
-       /**
-        * Returns the Id of this Cursor.
-        *
-        * @return This cursor's ID.
-        */
         const char* getId() const;
 
-       /**
-        * Gets a UV coordinates computed from the texture region.
-        *
-        * @return This cursor's UVs.
-        */
-        const Theme::UVs& getUVs() const;
+        Image* getImage(const char* imageId) const;
 
-        /**
-         * Gets this cursor's size.
-         *
-         * @return This cursor's size.
-         */
-        const Vector2& getSize() const;
-
-        /**
-         * Gets this cursor's color.
-         *
-         * @return This cursor's color.
-         */
-        const Vector4& getColor() const;
-    
     private:
-        Cursor(const Texture& texture, const Rectangle& region, const Vector4& color);
-        Cursor(const Cursor& copy);
-        ~Cursor();
+        ImageList(const Vector4& color);
+        ImageList(const ImageList& copy);
+        ~ImageList();
 
-        static Theme::Cursor* create(const char* id, const Texture& texture, const Rectangle& region, const Vector4& color);
+        static ImageList* create(float tw, float th, Properties* properties);
 
         std::string _id;
-        UVs _uvs;
-        Vector2 _size;
+        std::vector<Image*> _images;
         Vector4 _color;
     };
 
     /**
-     * A container region defines the border and background of a control.
+     * A skin defines the border and background of a control.
      */
-    class ContainerRegion : public Ref
+    class Skin : public Ref
     {
         friend class Theme;
 
     public:
-        enum ContainerArea
+        enum SkinArea
         {
             TOP_LEFT, TOP, TOP_RIGHT,
             LEFT, CENTER, RIGHT,
@@ -398,44 +250,50 @@ public:
         };
 
         /**
-         * Gets this container area's ID.
+         * Gets this skin's ID.
          *
-         * @return This container area's ID.
+         * @return This skin's ID.
          */
         const char* getId() const;
 
         /**
-         * Gets this container area's border.
+         * Gets this skin's border.
          *
-         * @return This container area's border.
+         * @return This skin's border.
          */
         const Theme::Border& getBorder() const;
 
+        const Rectangle& getRegion() const;
+
         /**
-         * Gets this container area's UVs.
+         * Gets this skin's UVs.
          *
-         * @return This container area's UVs.
+         * @return This skin's UVs.
          */
-        const Theme::UVs& getUVs(ContainerArea area) const;
+        const Theme::UVs& getUVs(SkinArea area) const;
 
         /**
-         * Gets this container area's color.
+         * Gets this skin's color.
          *
-         * @return This container area's color.
+         * @return This skin's color.
          */
         const Vector4& getColor() const;
 
     private:
-        ContainerRegion(const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color);
-        ContainerRegion(const ContainerRegion& copy);
-        ~ContainerRegion();
+        Skin(float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color);
+        //Skin(const Skin& copy);
+        ~Skin();
 
-        static ContainerRegion* create(const char* id, const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color);
+        static Skin* create(const char* id, float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color);
+
+        void setRegion(const Rectangle& region, float tw, float th);
     
         std::string _id;
         Theme::Border _border;
         UVs _uvs[MAX_OVERLAY_REGIONS];
         Vector4 _color;
+        Rectangle _region;
+        float _tw, _th;
     };
     
     /**
@@ -447,6 +305,7 @@ public:
     class Style
     {
         friend class Theme;
+        friend class Control;
 
     public:
         class Overlay;
@@ -496,17 +355,37 @@ public:
         /**
          * This class represents a control's overlay for one of the 3 modes: normal, focussed or active.
          */
-        class Overlay : public Ref
+        class Overlay : public Ref, public AnimationTarget
         {
             friend class Theme;
             friend class Style;
 
         public:
+            /**
+             * Animate this overlay's opacity property.  Data = opacity
+             */
+            static const int ANIMATE_OPACITY = 1;
+
            /**
             * Returns the Overlay type.
             */
             OverlayType getType();
 
+            float getOpacity() const;
+            void setOpacity(float opacity);
+
+            // setBorder(const Theme::Border& border) ??
+            void setBorder(float top, float bottom, float left, float right);
+            const Border& getBorder() const;
+
+            void setSkinColor(const Vector4& color);
+            const Vector4& getSkinColor() const;
+
+            void setSkinRegion(const Rectangle& region, float tw, float th);
+            const Rectangle& getSkinRegion() const;
+
+            const Theme::UVs& getSkinUVs(Theme::Skin::SkinArea area) const;
+
            /**
             * Gets a font associated with this overlay.
             */
@@ -527,28 +406,38 @@ public:
             void setTextRightToLeft(bool rightToLeft);
 
             const Vector4& getTextColor() const;
-            void setTextColor(const Vector4& color);
-            
-           /**
-            * Gets a cursor associated with this overlay.
-            */
-            void setTextCursor(Cursor* cursor);
-            Cursor* getTextCursor() const;
-            
-            void setMouseCursor(Cursor* cursor);
-            Cursor* getMouseCursor() const;
-            
-            void setCheckBoxIcon(Icon* icon);
-            Icon* getCheckBoxIcon() const;
+            void setTextColor(const Vector4& color); 
 
-            void setRadioButtonIcon(Icon* icon);
-            Icon* getRadioButtonIcon() const;
+            const Rectangle& getImageRegion(const char* id) const;
+            void setImageRegion(const char* id, const Rectangle& region, float tw, float th);
 
-            void setSliderIcon(SliderIcon* slider);
-            SliderIcon* getSliderIcon() const;
-            
-            void setContainerRegion(ContainerRegion* container);
-            ContainerRegion* getContainerRegion() const;
+            const Vector4& getImageColor(const char* id) const;
+            void setImageColor(const char* id, const Vector4& color);
+
+            const Theme::UVs& getImageUVs(const char* id) const;
+
+            const Rectangle& getCursorRegion() const;
+            void setCursorRegion(const Rectangle& region, float tw, float th);
+
+            const Vector4& getCursorColor() const;
+            void setCursorColor(const Vector4& color);
+
+            const Theme::UVs getCursorUVs() const;
+
+            /**
+             * @see AnimationTarget#getAnimationPropertyComponentCount
+             */
+            unsigned int getAnimationPropertyComponentCount(int propertyId) const;
+
+            /**
+             * @see AnimationTarget#getAnimationProperty
+             */
+            void getAnimationPropertyValue(int propertyId, AnimationValue* value);
+
+            /**
+             * @see AnimationTarget#setAnimationProperty
+             */
+            void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
         
         private:
             Overlay();
@@ -557,21 +446,32 @@ public:
 
             static Overlay* create();
 
-            ContainerRegion* _container;
-            Cursor* _textCursor;
-            Cursor* _mouseCursor;
-            Icon* _checkBoxIcon;
-            Icon* _radioButtonIcon;
-            SliderIcon* _sliderIcon;
+            void setSkin(Skin* Skin);
+            Skin* getSkin() const;
+
+            void setCursor(Image* cursor);
+            Image* getCursor() const;
+            
+            void setImageList(ImageList* imageList);
+            ImageList* getImageList() const;
+
+            static const char ANIMATION_OPACITY_BIT = 0x01;
+            void applyAnimationValueOpacity(float opacity, float blendWeight);
+
+            Skin* _skin;
+            Image* _cursor;
+            ImageList* _imageList;
             Font* _font;
             unsigned int _fontSize;
             Font::Justify _alignment;
             bool _textRightToLeft;
             Vector4 _textColor;
+            float _opacity;
         };
 
     private:
-        Style(const char* id, const Theme::Margin& margin, const Theme::Padding& padding,
+        Style(const char* id, float tw, float th,
+            const Theme::Margin& margin, const Theme::Padding& padding,
             Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled);
         Style(const Style& style);
         ~Style();
@@ -580,6 +480,8 @@ public:
         Margin _margin;
         Padding _padding;
         Overlay* _overlays[MAX_OVERLAYS];
+        float _tw;
+        float _th;
     };
 
 private:
@@ -598,18 +500,16 @@ private:
      */
     ~Theme();
 
-    static void generateUVs(const Texture& texture, float x, float y, float width, float height, UVs* uvs);
-    void lookUpSprites(const Properties* overlaySpace, Icon** checkBoxIcon, Icon** radioButtonIcon, SliderIcon** sliderIcon,
-                              Cursor** textCursor, Cursor** mouseCursor, ContainerRegion** containerRegion);
+    static void generateUVs(float tw, float th, float x, float y, float width, float height, UVs* uvs);
+    void lookUpSprites(const Properties* overlaySpace, ImageList** imageList, Image** mouseCursor, Skin** skin);
 
     std::string _path;
     Texture* _texture;
     SpriteBatch* _spriteBatch;
-    std::vector<Cursor*> _cursors;
     std::vector<Style*> _styles;
-    std::vector<Icon*> _icons;
-    std::vector<SliderIcon*> _sliders;
-    std::vector<ContainerRegion*> _containers;
+    std::vector<Image*> _images;
+    std::vector<ImageList*> _imageLists;
+    std::vector<Skin*> _skins;
     std::set<Font*> _fonts;
 };
 

+ 7 - 5
gameplay/src/VerticalLayout.cpp

@@ -45,12 +45,14 @@ namespace gameplay
     {
         // Need border, padding.
         Theme::Style* style = container->getStyle();
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = style->getOverlay(container->getOverlayType())->getContainerRegion();
-        if (containerRegion)
+        Theme::Border border = container->getBorder(container->getState());
+        /*
+        Theme::Skin* skin = style->getOverlay(container->getOverlayType())->getSkin();
+        if (skin)
         {
-            border = containerRegion->getBorder();
+            border = skin->getBorder();
         }
+        */
         Theme::Padding padding = style->getPadding();
 
         float yPosition = 0;
@@ -80,7 +82,7 @@ namespace gameplay
 
             yPosition += margin.top;
 
-            control->setPosition(0, yPosition);
+            control->setPosition(0, yPosition, 0L);
             if (control->isDirty())
             {
                 control->update(container->getClip());