Pārlūkot izejas kodu

Refactoring of Theme, especially the way data about texture regions (images used by controls) is stored.
Refactoring of the rest of the UI API to work with these changes.
Adding setters and getters on all themed properties of controls.
Supporting animation of control size, position, and opacity.

Adam Blake 13 gadi atpakaļ
vecāks
revīzija
f150ba812b

+ 1 - 1
gameplay/src/AnimationTarget.h

@@ -71,7 +71,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:
 

+ 69 - 42
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,33 +34,26 @@ 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);
-}
+}*/
 
 bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
@@ -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);
     }
 }
 

+ 8 - 6
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
@@ -61,7 +61,9 @@ public:
      * @param listener The listener to add.
      * @param eventFlags The events to listen for.
      */
-    virtual void addListener(Control::Listener* listener, int eventFlags);
+//    virtual void addListener(Control::Listener* listener, int eventFlags);
+
+  //  virtual void animationEvent(AnimationClip* clip, EventType type);
 
 protected:
     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;

+ 500 - 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)
+            
+            if (strcmp(spacename, "image") == 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);
+                theme->_images.push_back(Image::create(tw, th, space));
             }
-            else if (strcmp(spacename, "icon") == 0)
+            else if (strcmp(spacename, "imageList") == 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)
-            {
-                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->_imageLists.push_back(ImageList::create(tw, th, space));
             }
-            else 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);
-                }
-
-                Cursor* cursor = Cursor::create(space->getId(), *theme->_texture, region, color);
-                theme->_cursors.push_back(cursor);
-            }
-            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)
-                        {
-                            checkBoxIcon = normal->getCheckBoxIcon();
-                        }
-                        
-                        if (!radioButtonIcon)
+                        float opacity;
+                        if (innerSpace->exists("opacity"))
                         {
-                            radioButtonIcon = normal->getRadioButtonIcon();
+                            opacity = innerSpace->getFloat("opacity");
                         }
-                        
-                        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,218 @@ 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)
-    {
-        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 *
+     **************/
+    Theme::UVs::UVs()
+        : u1(0), v1(0), u2(0), v2(0)
     {
-        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()
+    Theme::UVs::UVs(float u1, float v1, float u2, float v2)
+        : u1(u1), v1(v1), u2(u2), v2(v2)
     {
     }
 
-    const char* Theme::Icon::getId() const
+    const Theme::UVs& Theme::UVs::empty()
     {
-        return _id.c_str();
+        static UVs empty(0, 0, 0, 0);
+        return empty;
     }
 
-    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)
     {
-        return _on;
-    }
+        Vector4 regionVector;                
+        properties->getVector4("region", &regionVector);
+        const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
 
+        Vector4 color(1, 1, 1, 1);
+        if (properties->exists("color"))
+        {
+            properties->getColor("color", &color);
+        }
 
-    /*********************
-     * 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);
-
+        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);
+            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 +698,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 +711,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 +722,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 (int i = 0; i < MAX_OVERLAYS; i++)
@@ -839,16 +754,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 +789,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 +966,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);
 
         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());