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

Refactoring of the UI API.
Modifying scope of Theme's inner classes.
Replacing Control::_position and Control::_size with Control::_bounds; renaming existing _bounds attribute to _clipBounds.
Removing automated animations from controls.
Removing unimplemented features from headers (e.g. auto-sizing).
Adding comments.
Fixed some clipping issues.
Fixed slider calculations.

Adam Blake 13 жил өмнө
parent
commit
f7fa5c1bb6

+ 2 - 0
gameplay/gameplay.vcxproj

@@ -94,6 +94,7 @@
     <ClCompile Include="src\TextBox.cpp" />
     <ClCompile Include="src\Texture.cpp" />
     <ClCompile Include="src\Theme.cpp" />
+    <ClCompile Include="src\ThemeStyle.cpp" />
     <ClCompile Include="src\Transform.cpp" />
     <ClCompile Include="src\Vector2.cpp" />
     <ClCompile Include="src\Vector3.cpp" />
@@ -183,6 +184,7 @@
     <ClInclude Include="src\TextBox.h" />
     <ClInclude Include="src\Texture.h" />
     <ClInclude Include="src\Theme.h" />
+    <ClInclude Include="src\ThemeStyle.h" />
     <ClInclude Include="src\TimeListener.h" />
     <ClInclude Include="src\Touch.h" />
     <ClInclude Include="src\Transform.h" />

+ 6 - 0
gameplay/gameplay.vcxproj.filters

@@ -273,6 +273,9 @@
     <ClCompile Include="src\PhysicsCollisionShape.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\ThemeStyle.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -542,6 +545,9 @@
     <ClInclude Include="src\ScreenDisplayer.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\ThemeStyle.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">

+ 5 - 2
gameplay/src/Button.h

@@ -14,9 +14,9 @@ namespace gameplay
  *
  * The following properties are available for buttons:
  *
- * button <Button ID>
+ * button <buttonID>
  * {
- *      style       = <Style ID>
+ *      style       = <styleID>
  *      position    = <x, y>
  *      size        = <width, height>
  *      text        = <string>
@@ -27,6 +27,7 @@ class Button : public Label
     friend class Container;
 
 protected:
+
     /**
      * Create a button with a given style and properties.
      *
@@ -52,9 +53,11 @@ protected:
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
     Button();
+
     virtual ~Button();
 
 private:
+
     Button(const Button& copy);
 };
 

+ 7 - 7
gameplay/src/CheckBox.cpp

@@ -68,8 +68,8 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
         {
             if (_state == Control::ACTIVE)
             {
-                if (x > 0 && x <= _bounds.width &&
-                    y > 0 && y <= _bounds.height)
+                if (x > 0 && x <= _clipBounds.width &&
+                    y > 0 && y <= _clipBounds.height)
                 {
                     _checked = !_checked;
                     notifyListeners(Control::Listener::VALUE_CHANGED);
@@ -123,7 +123,7 @@ void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     // Left, v-center.
     // TODO: Set an alignment for icons.
     const Theme::Border border = getBorder(_state);
-    const Theme::Padding padding = _style->getPadding();
+    const Theme::Padding padding = getPadding();
     float opacity = getOpacity(_state);
 
     if (_checked)
@@ -143,8 +143,8 @@ void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& 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);
+        Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
+            clip.y + _bounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
         spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
     }
@@ -165,8 +165,8 @@ void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& 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);
+        Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
+            clip.y + _bounds.y + (_clipBounds.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);
     }

+ 2 - 2
gameplay/src/CheckBox.h

@@ -14,7 +14,7 @@ namespace gameplay
  *
  * The following properties are available for checkboxes:
  *
- * checkBox <CheckBox ID>
+ * checkBox <checkBoxID>
  * {
  *      style       = <Style ID>
  *      position    = <x, y>
@@ -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 //, public AnimationClip::Listener
+class CheckBox : public Button
 {
     friend class Container;
 

+ 1 - 1
gameplay/src/Container.cpp

@@ -295,7 +295,7 @@ namespace gameplay
                 continue;
             }
 
-            const Rectangle& bounds = control->getBounds();
+            const Rectangle& bounds = control->getClipBounds();
             if (control->getState() != Control::NORMAL ||
                 (evt == Touch::TOUCH_PRESS &&
                  x >= xPos + bounds.x &&

+ 9 - 4
gameplay/src/Container.h

@@ -20,10 +20,11 @@ namespace gameplay
  *      position = <x, y>               // Position of the container on-screen, measured in pixels.
  *      size     = <width, height>      // Size of the container, measured in pixels.
  *   
- *      // All the controls within this container.
- *      container{}
- *      label{}
- *      textBox{}
+ *      // All the nested controls within this container.
+ *      container { }
+
+ *      label { }
+ *      textBox { }
  *      button{}
  *      checkBox{}
  *      radioButton{}
@@ -33,6 +34,7 @@ namespace gameplay
 class Container : public Control
 {
 public:
+
     /**
      * Get this container's layout.
      *
@@ -103,7 +105,9 @@ public:
     std::vector<Control*> getControls() const;
 
 protected:
+
     Container();
+
     virtual ~Container();
 
     /**
@@ -207,6 +211,7 @@ protected:
     std::vector<Control*> _controls;    // List of controls within this container.
 
 private:
+
     Container(const Container& copy);
 };
 

+ 101 - 133
gameplay/src/Control.cpp

@@ -5,8 +5,8 @@
 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), _styleOverridden(false)
+        : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _clip(Rectangle::empty()),
+            _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false)
     {
     }
 
@@ -18,7 +18,7 @@ namespace gameplay
     {
         if (_listeners)
         {
-            for (ListenerMap::const_iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
+            for (std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
             {
                 std::list<Listener*>* list = itr->second;
                 SAFE_DELETE(list);
@@ -36,8 +36,11 @@ namespace gameplay
     {
         _style = style;
 
-        properties->getVector2("position", &_position);
-        properties->getVector2("size", &_size);
+        Vector2 position;
+        Vector2 size;
+        properties->getVector2("position", &position);
+        properties->getVector2("size", &size);
+        _bounds.set(position.x, position.y, size.x, size.y);
 
         _state = Control::getStateFromString(properties->getString("state"));
 
@@ -51,87 +54,59 @@ namespace gameplay
         return _id.c_str();
     }
 
-    void Control::setPosition(float x, float y, unsigned long duration)
+    void Control::setPosition(float x, float 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);
-        }
+        _bounds.x = x;
+        _bounds.y = y;
+        _dirty = true;
+    }
 
+    void Control::setSize(float width, float height)
+    {
+        _bounds.width = width;
+        _bounds.height = height;
         _dirty = true;
     }
 
-    const Vector2& Control::getPosition() const
+    void Control::setBounds(const Rectangle& bounds)
     {
-        return _position;
+        _bounds.set(bounds);
     }
 
-    void Control::setSize(float width, float height, unsigned long duration)
+    const Rectangle& Control::getBounds() const
     {
-        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);
-        }
+        return _bounds;
+    }
 
-        _dirty = true;
+    float Control::getX() const
+    {
+        return _bounds.x;
     }
 
-    const Vector2& Control::getSize() const
+    float Control::getY() const
     {
-        return _size;
+        return _bounds.y;
     }
 
-    void Control::setOpacity(float opacity, unsigned char states, unsigned long duration)
+    float Control::getWidth() const
+    {
+        return _bounds.width;
+    }
+
+    float Control::getHeight() const
+    {
+        return _bounds.height;
+    }
+
+    void Control::setOpacity(float opacity, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 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();
+            overlays[i]->setOpacity(opacity);
         }
         
         _dirty = true;
@@ -145,10 +120,10 @@ namespace gameplay
     void Control::setBorder(float top, float bottom, float left, float right, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setBorder(top, bottom, left, right);
         }
@@ -164,10 +139,10 @@ namespace gameplay
     void Control::setSkinRegion(const Rectangle& region, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setSkinRegion(region, _style->_tw, _style->_th);
         }
@@ -188,10 +163,10 @@ namespace gameplay
     void Control::setSkinColor(const Vector4& color, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setSkinColor(color);
         }
@@ -229,10 +204,10 @@ namespace gameplay
     void Control::setImageRegion(const char* id, const Rectangle& region, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setImageRegion(id, region, _style->_tw, _style->_th);
         }
@@ -248,10 +223,10 @@ namespace gameplay
     void Control::setImageColor(const char* id, const Vector4& color, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setImageColor(id, color);
         }
@@ -272,10 +247,10 @@ namespace gameplay
     void Control::setCursorRegion(const Rectangle& region, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setCursorRegion(region, _style->_tw, _style->_th);
         }
@@ -291,10 +266,10 @@ namespace gameplay
     void Control::setCursorColor(const Vector4& color, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setCursorColor(color);
         }
@@ -315,10 +290,10 @@ namespace gameplay
     void Control::setFont(Font* font, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setFont(font);
         }
@@ -334,10 +309,10 @@ namespace gameplay
     void Control::setFontSize(unsigned int fontSize, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setFontSize(fontSize);
         }
@@ -353,10 +328,10 @@ namespace gameplay
     void Control::setTextColor(const Vector4& color, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setTextColor(color);
         }
@@ -372,10 +347,10 @@ namespace gameplay
     void Control::setTextAlignment(Font::Justify alignment, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setTextAlignment(alignment);
         }
@@ -391,10 +366,10 @@ namespace gameplay
     void Control::setTextRightToLeft(bool rightToLeft, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setTextRightToLeft(rightToLeft);
         }
@@ -407,9 +382,9 @@ namespace gameplay
         return getOverlay(state)->getTextRightToLeft();
     }
 
-    const Rectangle& Control::getBounds() const
+    const Rectangle& Control::getClipBounds() const
     {
-        return _bounds;
+        return _clipBounds;
     }
 
     const Rectangle& Control::getClip() const
@@ -417,13 +392,6 @@ namespace gameplay
         return _clip;
     }
 
-    void Control::setAutoSize(bool width, bool height)
-    {
-        _autoWidth = width;
-        _autoHeight = height;
-        _dirty = true;
-    }
-
     void Control::setStyle(Theme::Style* style)
     {
         if (style != _style)
@@ -529,7 +497,7 @@ namespace gameplay
             _listeners = new std::map<Listener::EventType, std::list<Listener*>*>();
         }
 
-        ListenerMap::const_iterator itr = _listeners->find(eventType);
+        std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
         if (itr == _listeners->end())
         {
             _listeners->insert(std::make_pair(eventType, new std::list<Listener*>()));
@@ -558,8 +526,8 @@ namespace gameplay
             notifyListeners(Listener::RELEASE);
 
             // Only trigger Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
-            if (x > 0 && x <= _bounds.width &&
-                y > 0 && y <= _bounds.height)
+            if (x > 0 && x <= _clipBounds.width &&
+                y > 0 && y <= _clipBounds.height)
             {
                 notifyListeners(Listener::CLICK);
             }
@@ -577,7 +545,7 @@ namespace gameplay
     {
         if (_listeners)
         {
-            ListenerMap::const_iterator itr = _listeners->find(eventType);
+            std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
             if (itr != _listeners->end())
             {
                 std::list<Listener*>* listenerList = itr->second;
@@ -592,10 +560,10 @@ namespace gameplay
     void Control::update(const Rectangle& clip)
     {
         // Calculate the bounds.
-        float x = clip.x + _position.x;
-        float y = clip.y + _position.y;
-        float width = _size.x;
-        float height = _size.y;
+        float x = clip.x + _bounds.x;
+        float y = clip.y + _bounds.y;
+        float width = _bounds.width;
+        float height = _bounds.height;
 
         float clipX2 = clip.x + clip.width;
         float x2 = x + width;
@@ -611,7 +579,7 @@ namespace gameplay
             height = clipY2 - y;
         }
 
-        _bounds.set(_position.x, _position.y, width, height);
+        _clipBounds.set(_bounds.x, _bounds.y, width, height);
 
         // Calculate the clipping viewport.
         const Theme::Border& border = getBorder(_state);
@@ -619,8 +587,8 @@ namespace gameplay
 
         x +=  border.left + padding.left;
         y +=  border.top + padding.top;
-        width = _size.x - border.left - padding.left - border.right - padding.right;
-        height = _size.y - border.top - padding.top - border.bottom - padding.bottom;
+        width = _bounds.width - border.left - padding.left - border.right - padding.right;
+        height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
 
         _textBounds.set(x, y, width, height);
 
@@ -653,7 +621,7 @@ namespace gameplay
 
     void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
     {
-        Vector2 pos(clip.x + _position.x, clip.y + _position.y);
+        Vector2 pos(clip.x + _bounds.x, clip.y + _bounds.y);
 
         // Get the border and background images for this control's current state.
         //Theme::UVs topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight;
@@ -673,18 +641,18 @@ namespace gameplay
         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 midWidth = _bounds.width - border.left - border.right;
+        float midHeight = _bounds.height - 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;
+        float rightX = pos.x + _bounds.width - border.right;
+        float bottomY = pos.y + _bounds.height - 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);
+            spriteBatch->draw(pos.x, pos.y, _bounds.width, _bounds.height, center.u1, center.v1, center.u2, center.v2, skinColor, clip);
         }
         else
         {
@@ -697,7 +665,7 @@ namespace gameplay
             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,
+                spriteBatch->draw(pos.x + border.left, pos.y + border.top, _bounds.width - border.left - border.right, _bounds.height - 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);
@@ -781,24 +749,24 @@ namespace gameplay
         switch(propertyId)
         {
         case ANIMATE_POSITION:
-            value->setFloat(0, _position.x);
-            value->setFloat(1, _position.y);
+            value->setFloat(0, _bounds.x);
+            value->setFloat(1, _bounds.y);
             break;
         case ANIMATE_SIZE:
-            value->setFloat(0, _size.x);
-            value->setFloat(1, _size.y);
+            value->setFloat(0, _clipBounds.width);
+            value->setFloat(1, _clipBounds.height);
             break;
         case ANIMATE_POSITION_X:
-            value->setFloat(0, _position.x);
+            value->setFloat(0, _bounds.x);
             break;
         case ANIMATE_POSITION_Y:
-            value->setFloat(0, _position.y);
+            value->setFloat(0, _bounds.y);
             break;
         case ANIMATE_SIZE_WIDTH:
-            value->setFloat(0, _size.x);
+            value->setFloat(0, _clipBounds.width);
             break;
         case ANIMATE_SIZE_HEIGHT:
-            value->setFloat(0, _size.y);
+            value->setFloat(0, _clipBounds.height);
             break;
         case ANIMATE_OPACITY:
         default:
@@ -845,9 +813,9 @@ namespace gameplay
         }
         else
         {
-            x = Curve::lerp(blendWeight, _position.x, x);
+            x = Curve::lerp(blendWeight, _bounds.x, x);
         }
-        _position.x = x;
+        _bounds.x = x;
         _dirty = true;
     }
     
@@ -859,9 +827,9 @@ namespace gameplay
         }
         else
         {
-            y = Curve::lerp(blendWeight, _position.y, y);
+            y = Curve::lerp(blendWeight, _bounds.y, y);
         }
-        _position.y = y;
+        _bounds.y = y;
         _dirty = true;
     }
     
@@ -873,9 +841,9 @@ namespace gameplay
         }
         else
         {
-            width = Curve::lerp(blendWeight, _size.x, width);
+            width = Curve::lerp(blendWeight, _clipBounds.width, width);
         }
-        _size.x = width;
+        _clipBounds.width = width;
         _dirty = true;
     }
 
@@ -887,9 +855,9 @@ namespace gameplay
         }
         else
         {
-            height = Curve::lerp(blendWeight, _size.y, height);
+            height = Curve::lerp(blendWeight, _clipBounds.height, height);
         }
-        _size.y = height;
+        _clipBounds.height = height;
         _dirty = true;
     }
 

+ 93 - 41
gameplay/src/Control.h

@@ -6,14 +6,10 @@
 #include "Vector2.h"
 #include "SpriteBatch.h"
 #include "Theme.h"
+#include "ThemeStyle.h"
 #include "Touch.h"
 #include "Keyboard.h"
 
-/**
- * Default duration of UI animations.
- */
-#define DEFAULT_UI_ANIMATION_DURATION 200L
-
 namespace gameplay
 {
 
@@ -34,13 +30,31 @@ public:
      */
     enum State
     {
+        /**
+         * State of an enabled but inactive control.
+         */
         NORMAL = 0x01,
+
+        /**
+         * State of a control currently in focus.
+         */
         FOCUS = 0x02,
+
+        /**
+         * State of a control that is currently being acted on,
+         * e.g. through touch or mouse-click events.
+         */
         ACTIVE = 0x04,
+
+        /**
+         * State of a control that has been disabled.
+         */
         DISABLED = 0x08,
     };
 
-    // Only for setting control states
+    /**
+     * A constant used for setting themed attributes on all control states simultaneously.
+     */
     static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
 
     class Listener
@@ -48,13 +62,40 @@ public:
     public:
         enum EventType
         {
+            /**
+             * Mouse-down or touch-press event.
+             */
             PRESS           = 0x01,
+
+            /**
+             * Mouse-up or touch-release event.
+             */
             RELEASE         = 0x02,
+
+            /**
+             * Event triggered after consecutive PRESS and RELEASE events take place
+             * within the bounds of a control.
+             */
             CLICK           = 0x04,
+
+            /**
+             * Event triggered when the value of a slider, check box, or radio button
+             * changes.
+             */
             VALUE_CHANGED   = 0x08,
+
+            /**
+             * Event triggered when the contents of a text box are modified.
+             */
             TEXT_CHANGED    = 0x10
         };
 
+        /**
+         * Method called by controls when an event is triggered.
+         * 
+         * @param control The control triggering the event.
+         * @param evt The event triggered.
+         */
         virtual void controlEvent(Control* control, EventType evt) = 0;
     };
 
@@ -106,33 +147,61 @@ public:
      * @param x The x coordinate.
      * @param y The y coordinate.
      */
-    void setPosition(float x, float y, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
+    void setPosition(float x, float y);
 
     /**
-     * Get the position of this control relative to its parent container.
+     * Set the desired size of this control, including its border and padding, before clipping.
      *
-     * @return The position of this control relative to its parent container.
+     * @param width The width.
+     * @param height The height.
      */
-    const Vector2& getPosition() const;
+    void setSize(float width, float height);
 
     /**
-     * Set the desired size of this control, including its border and padding, before clipping.
+     * Set the bounds of this control, relative to its parent container and including its
+     * border and padding, before clipping.
      *
-     * @param width The width.
-     * @param height The height.
+     * @param bounds The new bounds to set.
      */
-    void setSize(float width, float height, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
+    void setBounds(const Rectangle& bounds);
 
     /**
-     * Get the desired size of this control, including its border and padding, before clipping.
+     * Get the bounds of this control, relative to its parent container and including its
+     * border and padding, before clipping.
      *
-     * @return The size of this control.
+     * @return The bounds of this control.
      */
-    const Vector2& getSize() const;
+    const Rectangle& getBounds() const;
 
-    // Themed properties.
+    /**
+     * Get the x coordinate of this control's bounds.
+     *
+     * @return The x coordinate of this control's bounds.
+     */
+    float getX() const;
     
-    //void setBorder(const Theme::Border& border, unsigned char states = STATE_ALL);
+    /**
+     * Get the y coordinate of this control's bounds.
+     *
+     * @return The y coordinate of this control's bounds.
+     */
+    float getY() const;
+
+    /**
+     * Get the width of this control's bounds.
+     *
+     * @return The width of this control's bounds.
+     */
+    float getWidth() const;
+
+    /**
+     * Get the height of this control's bounds.
+     *
+     * @return The height of this control's bounds.
+     */
+    float getHeight() const;
+
+    // Themed properties.
     /**
      * Set the size of this control's border.
      *
@@ -422,7 +491,7 @@ public:
      *               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);
+    void setOpacity(float opacity, unsigned char states = STATE_ALL);
 
     /**
      * Get the opacity of this control for a given state. 
@@ -437,13 +506,12 @@ public:
     // 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.
      *
      * @return The bounds of this control.
      */
-    const Rectangle& getBounds() const;
+    const Rectangle& getClipBounds() const;
 
     /**
      * Get the content area of this control, in screen coordinates, after clipping.
@@ -452,18 +520,6 @@ public:
      */
     const Rectangle& getClip() const;
 
-    /**
-     * Set width and/or height to auto-size to size a Control to tightly fit
-     * 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 STATE_ALL its children.
-     *
-     * @param width Whether to automatically determine this Control's width.
-     * @param height Whether to automatically determine this Control's height.
-     */
-    void setAutoSize(bool width, bool height);
-
     /**
      * Change this control's state.
      *
@@ -650,18 +706,14 @@ protected:
 
     std::string _id;
     State _state;           // Determines overlay used during draw().
-    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 _bounds;      // Position, relative to parent container's clipping window, and desired size.
+    Rectangle _clipBounds;  // 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 text area, before clipping.  Used for text alignment.
     Rectangle _clip;        // Clipping window of this control's content, after clipping.
-    bool _autoWidth;
-    bool _autoHeight;
     bool _dirty;
     bool _consumeTouchEvents;
     Theme::Style* _style;
-    typedef std::map<Listener::EventType, std::list<Listener*>*> ListenerMap;
-    ListenerMap* _listeners;
+    std::map<Listener::EventType, std::list<Listener*>*>* _listeners;
 
 private:
     // Animation blending bits.

+ 8 - 9
gameplay/src/Form.cpp

@@ -136,9 +136,9 @@ namespace gameplay
         if (_node && !_quad)
         {
             // Set this Form up to be 3D by initializing a quad, projection matrix and viewport.
-            setQuad(0.0f, 0.0f, _size.x, _size.y);
+            setQuad(0.0f, 0.0f, _bounds.width, _bounds.height);
 
-            Matrix::createOrthographicOffCenter(0, _size.x, _size.y, 0, 0, 1, &_projectionMatrix);
+            Matrix::createOrthographicOffCenter(0, _bounds.width, _bounds.height, 0, 0, 1, &_projectionMatrix);
             _theme->setProjectionMatrix(_projectionMatrix);
             
             _node->setModel(_quad);
@@ -147,7 +147,7 @@ namespace gameplay
 
     void Form::update()
     {
-        Container::update(Rectangle(0, 0, _size.x, _size.y));
+        Container::update(Rectangle(0, 0, _bounds.width, _bounds.height));
     }
 
     void Form::draw()
@@ -171,7 +171,7 @@ namespace gameplay
                 Game* game = Game::getInstance();
                 Rectangle prevViewport = game->getViewport();
                 
-                game->setViewport(Rectangle(_position.x, _position.y, _size.x, _size.y));
+                game->setViewport(Rectangle(_bounds.x, _bounds.y, _bounds.width, _bounds.height));
 
                 draw(_theme->getSpriteBatch(), _clip);
 
@@ -199,7 +199,7 @@ namespace gameplay
 
         // Draw the form's border and background.
         // We don't pass the form's position to itself or it will be applied twice!
-        Control::drawBorder(spriteBatch, Rectangle(0, 0, _size.x, _size.y));
+        Control::drawBorder(spriteBatch, Rectangle(0, 0, _bounds.width, _bounds.height));
 
         // Draw each control's border and background.
         for (it = _controls.begin(); it < _controls.end(); it++)
@@ -244,7 +244,6 @@ namespace gameplay
         // Set the common render state block for the material
         RenderState::StateBlock* stateBlock = _theme->getSpriteBatch()->getStateBlock();
         stateBlock->setDepthWrite(true);
-        //material->setStateBlock(_theme->getSpriteBatch()->getStateBlock());
         material->setStateBlock(stateBlock);
 
         // Bind the WorldViewProjection matrix
@@ -259,7 +258,7 @@ namespace gameplay
         // Use the FrameBuffer to texture the quad.
         if (!_frameBuffer->getRenderTarget())
         {
-            RenderTarget* rt = RenderTarget::create(_id.c_str(), _size.x, _size.y);
+            RenderTarget* rt = RenderTarget::create(_id.c_str(), _bounds.width, _bounds.height);
             _frameBuffer->setRenderTarget(rt);
             SAFE_RELEASE(rt);
         }
@@ -327,7 +326,7 @@ namespace gameplay
                             m.transformPoint(&point);
 
                             // Pass the touch event on.
-                            const Rectangle& bounds = form->getBounds();
+                            const Rectangle& bounds = form->getClipBounds();
                             if (form->getState() == Control::FOCUS ||
                                 (evt == Touch::TOUCH_PRESS &&
                                  point.x >= bounds.x &&
@@ -346,7 +345,7 @@ namespace gameplay
                 else
                 {
                     // Simply compare with the form's bounds.
-                    const Rectangle& bounds = form->getBounds();
+                    const Rectangle& bounds = form->getClipBounds();
                     if (form->getState() == Control::FOCUS ||
                         (evt == Touch::TOUCH_PRESS &&
                          x >= bounds.x &&

+ 4 - 7
gameplay/src/Label.cpp

@@ -69,16 +69,13 @@ namespace gameplay
         if (_text.size() <= 0)
             return;
 
-        // TODO: Batch all labels that use the same font.
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Font* font = overlay->getFont();
-
-        Vector4 textColor = overlay->getTextColor();
-        textColor.w *= overlay->getOpacity();
+        Font* font = getFont(_state);
+        Vector4 textColor = getTextColor(_state);
+        textColor.w *= getOpacity(_state);
 
         // Draw the text.
         font->begin();
-        font->drawText(_text.c_str(), _textBounds, textColor, overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft(), &_clip);
+        font->drawText(_text.c_str(), _textBounds, textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_clip);
         font->end();
 
         _dirty = false;

+ 13 - 7
gameplay/src/RadioButton.cpp

@@ -48,6 +48,11 @@ RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
     return radioButton;
 }
 
+bool RadioButton::isSelected() const
+{
+    return _selected;
+}
+
 void RadioButton::setImageSize(float width, float height)
 {
     _imageSize.set(width, height);
@@ -82,8 +87,8 @@ bool RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int c
         {
             if (_state == Control::ACTIVE)
             {
-                if (x > 0 && x <= _bounds.width &&
-                    y > 0 && y <= _bounds.height)
+                if (x > 0 && x <= _clipBounds.width &&
+                    y > 0 && y <= _clipBounds.height)
                 {
                     if (!_selected)
                     {
@@ -110,6 +115,7 @@ void RadioButton::clearSelected(const std::string& groupId)
         {
             radioButton->_selected = false;
             radioButton->_dirty = true;
+            radioButton->notifyListeners(Listener::VALUE_CHANGED);
         }
     }
 }
@@ -119,7 +125,7 @@ void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     // Left, v-center.
     // TODO: Set an alignment for radio button images.
     const Theme::Border border = getBorder(_state);
-    const Theme::Padding padding = _style->getPadding();
+    const Theme::Padding padding = getPadding();
     float opacity = getOpacity(_state);
 
     if (_selected)
@@ -139,8 +145,8 @@ void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& 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);
+        Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
+            clip.y + _bounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
         spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
     }
@@ -161,8 +167,8 @@ void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& 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);
+        Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
+            clip.y + _bounds.y + (_clipBounds.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);
     }

+ 1 - 1
gameplay/src/RadioButton.h

@@ -36,7 +36,7 @@ public:
      *
      * @return Whether this radio button is currently selected.
      */
-    bool isSelected();
+    bool isSelected() const;
 
     /**
      * Set the size to draw the radio button icon.

+ 10 - 10
gameplay/src/Slider.cpp

@@ -91,8 +91,8 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     case Touch::TOUCH_MOVE:
     case Touch::TOUCH_RELEASE:
         if (_state == ACTIVE &&
-            x > 0 && x <= _bounds.width &&
-            y > 0 && y <= _bounds.height)
+            x > 0 && x <= _clipBounds.width &&
+            y > 0 && y <= _clipBounds.height)
         {
             // Horizontal case.
             const Theme::Border& border = getBorder(_state);
@@ -101,7 +101,7 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
             const Rectangle& maxCapRegion = getImageRegion("maxCap", _state);
 
             float markerPosition = ((float)x - maxCapRegion.width - border.left - padding.left) /
-                (_bounds.width - border.left - border.right - padding.left - padding.right - minCapRegion.width - maxCapRegion.width);
+                (_clipBounds.width - border.left - border.right - padding.left - padding.right - minCapRegion.width - maxCapRegion.width);
             
             if (markerPosition > 1.0f)
             {
@@ -167,21 +167,21 @@ void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     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, _clip);
+    float midY = clip.y + _clipBounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f;
+    Vector2 pos(clip.x + _clipBounds.x + border.left + padding.left, midY - trackRegion.height / 2.0f);
+    spriteBatch->draw(pos.x, pos.y, _clipBounds.width, trackRegion.height, track.u1, track.v1, track.u2, track.v2, trackColor, _clip);
 
     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, _clip);
         
-    pos.x = clip.x + _bounds.x + _bounds.width - border.right - padding.right - maxCapRegion.width * 0.5f;
+    pos.x = clip.x + _clipBounds.x + _clipBounds.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, _clip);
 
     // Percent across.
-    float markerPosition = (_value + _max) / (_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;
+    float markerPosition = (_value - _min) / (_max - _min);
+    markerPosition *= _clipBounds.width - border.left - padding.left - border.right - padding.right - minCapRegion.width * 0.5f - maxCapRegion.width * 0.5f - markerRegion.width;
+    pos.x = clip.x + _clipBounds.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, _clip);
 }

+ 52 - 42
gameplay/src/TextBox.cpp

@@ -66,8 +66,8 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             _dirty = true;
             return _consumeTouchEvents;
         }
-        else if (!(x > 0 && x <= _bounds.width &&
-                    y > 0 && y <= _bounds.height))
+        else if (!(x > 0 && x <= _clipBounds.width &&
+                    y > 0 && y <= _clipBounds.height))
         {
             _state = NORMAL;
             Game::getInstance()->displayKeyboard(false);
@@ -77,8 +77,8 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         break;
     case Touch::TOUCH_MOVE:
         if (_state == FOCUS &&
-            x > 0 && x <= _bounds.width &&
-            y > 0 && y <= _bounds.height)
+            x > 0 && x <= _clipBounds.width &&
+            y > 0 && y <= _clipBounds.height)
         {
             setCursorLocation(x, y);
             _dirty = true;
@@ -86,8 +86,8 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         }
         break;
     case Touch::TOUCH_RELEASE:
-        if (x > 0 && x <= _bounds.width &&
-            y > 0 && y <= _bounds.height)
+        if (x > 0 && x <= _clipBounds.width &&
+            y > 0 && y <= _clipBounds.height)
         {
             setCursorLocation(x, y);
             _state = FOCUS;
@@ -126,66 +126,71 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     }
                     case Keyboard::KEY_DELETE:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
-                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
 
+                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
+                            textAlignment, true, rightToLeft);
                         _text.erase(textIndex, 1);
                         font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         notifyListeners(Listener::TEXT_CHANGED);
                         break;
                     }
                     case Keyboard::KEY_LEFT_ARROW:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
-                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
 
+                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
+                            textAlignment, true, rightToLeft);
                         font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex - 1,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         break;
                     }
                     case Keyboard::KEY_RIGHT_ARROW:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
-                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
 
+                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
+                            textAlignment, true, rightToLeft);
                         font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex + 1,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         break;
                     }
                     case Keyboard::KEY_UP_ARROW:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
 
                         _cursorLocation.y -= fontSize;
                         font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         break;
                     }
                     case Keyboard::KEY_DOWN_ARROW:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
 
                         _cursorLocation.y += fontSize;
                         font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         break;
                     }
@@ -195,11 +200,13 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 
             case Keyboard::KEY_CHAR:
             {
-                Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                Font* font = overlay->getFont();
-                unsigned int fontSize = overlay->getFontSize();
+                Font* font = getFont(_state);
+                unsigned int fontSize = getFontSize(_state);
+                Font::Justify textAlignment = getTextAlignment(_state);
+                bool rightToLeft = getTextRightToLeft(_state);
+
                 unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                    overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                    textAlignment, true, rightToLeft);
 
                 switch (key)
                 {
@@ -210,7 +217,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                             --textIndex;
                             _text.erase(textIndex, 1);
                             font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex,
-                                overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                                textAlignment, true, rightToLeft);
 
                             _dirty = true;
                         }
@@ -226,7 +233,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 
                         // Get new location of cursor.
                         font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex + 1,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                            textAlignment, true, rightToLeft);
                 
                         _dirty = true;
                         break;
@@ -250,10 +257,13 @@ void TextBox::update(const Rectangle& clip)
     // Get index into string and cursor location from the last recorded touch location.
     if (_state == FOCUS)
     {
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Font* font = overlay->getFont();
-        font->getIndexAtLocation(_text.c_str(), _clip, overlay->getFontSize(), _cursorLocation, &_cursorLocation,
-            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+        Font* font = getFont(_state);
+        unsigned int fontSize = getFontSize(_state);
+        Font::Justify textAlignment = getTextAlignment(_state);
+        bool rightToLeft = getTextRightToLeft(_state);
+
+        font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
+            textAlignment, true, rightToLeft);
     }
 }
 

+ 2 - 466
gameplay/src/Theme.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "Theme.h"
+#include "ThemeStyle.h"
 
 namespace gameplay
 {
@@ -9,7 +10,7 @@ namespace gameplay
     {
     }
 
-    Theme::Theme(const Theme* theme)
+    Theme::Theme(const Theme& theme)
     {
     }
 
@@ -711,471 +712,6 @@ namespace gameplay
     {
         return _color;
     }
-
-    /****************
-     * Theme::Style *
-     ****************/
-    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), _tw(tw), _th(th), _margin(margin), _padding(padding)
-    {
-        _overlays[OVERLAY_NORMAL] = normal;
-        _overlays[OVERLAY_FOCUS] = focus;
-        _overlays[OVERLAY_ACTIVE] = active;
-        _overlays[OVERLAY_DISABLED] = disabled;
-    }
-
-    Theme::Style::Style(const Style& copy)
-    {
-        _id = copy._id;
-        _margin = copy._margin;
-        _padding = copy._padding;
-        _tw = copy._tw;
-        _th = copy._th;
-
-        for (int i = 0; i < MAX_OVERLAYS; i++)
-        {
-            _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
-        }
-    }
-
-    Theme::Style::~Style()
-    {
-        for (unsigned int i = 0; i < MAX_OVERLAYS; i++)
-        {
-            SAFE_RELEASE(_overlays[i]);
-        }
-    }
-    
-    const char* Theme::Style::getId() const
-    {
-        return _id.data();
-    }
-
-    Theme::Style::Overlay* Theme::Style::getOverlay(OverlayType overlayType) const
-    {
-        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 *
-     *************************/
-    Theme::Style::Overlay* Theme::Style::Overlay::create()
-    {
-        Overlay* overlay = new Overlay();
-        return overlay;
-    }
-
-    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(_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;
-    }
-
-    void Theme::Style::Overlay::setFont(Font* font)
-    {
-        if (_font != font)
-        {
-            SAFE_RELEASE(_font);
-
-            _font = font;
-
-            if (_font)
-            {
-                _font->addRef();
-            }
-        }
-    }
-
-    unsigned int Theme::Style::Overlay::getFontSize() const
-    {
-        return _fontSize;
-    }
-
-    void Theme::Style::Overlay::setFontSize(unsigned int fontSize)
-    {
-        _fontSize = fontSize;
-    }
-
-    Font::Justify Theme::Style::Overlay::getTextAlignment() const
-    {
-        return _alignment;
-    }
-
-    void Theme::Style::Overlay::setTextAlignment(Font::Justify alignment)
-    {
-        _alignment = alignment;
-    }
-
-    bool Theme::Style::Overlay::getTextRightToLeft() const
-    {
-        return _textRightToLeft;
-    }
-
-    void Theme::Style::Overlay::setTextRightToLeft(bool rightToLeft)
-    {
-        _textRightToLeft = rightToLeft;
-    }
-
-    const Vector4& Theme::Style::Overlay::getTextColor() const
-    {
-        return _textColor;
-    }
-
-    void Theme::Style::Overlay::setTextColor(const Vector4& color)
-    {
-        _textColor = color;
-    }
-
-    const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
-    {
-        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));
-    }
-
-    const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
-    {
-        Image* image = _imageList->getImage(id);
-        if (image)
-        {
-            return image->getColor();
-        }
-        else
-        {
-            return Vector4::zero();
-        }
-    }
-
-    void Theme::Style::Overlay::setImageColor(const char* id, const Vector4& color)
-    {
-        Image* image = _imageList->getImage(id);
-        assert(image);
-        image->_color.set(color);
-    }
-
-    const Theme::UVs& Theme::Style::Overlay::getImageUVs(const char* id) const
-    {
-        Image* image = _imageList->getImage(id);
-        if (image)
-        {
-            return image->getUVs();
-        }
-        else
-        {
-            return UVs::empty();
-        }
-    }
-
-    const Rectangle& Theme::Style::Overlay::getCursorRegion() const
-    {
-        if (_cursor)
-        {
-            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));
-    }
-
-    const Vector4& Theme::Style::Overlay::getCursorColor() const
-    {
-        if (_cursor)
-        {
-            return _cursor->getColor();
-        }
-        else
-        {
-            return Vector4::zero();
-        }
-    }
-
-    void Theme::Style::Overlay::setCursorColor(const Vector4& color)
-    {
-        assert(_cursor);
-        _cursor->_color.set(color);
-    }
-
-    const Theme::UVs& Theme::Style::Overlay::getCursorUVs() const
-    {
-        if (_cursor)
-        {
-            return _cursor->getUVs();
-        }
-        else
-        {
-            return UVs::empty();
-        }
-    }
-
-    void Theme::Style::Overlay::setSkin(Skin* skin)
-    {
-        if (_skin != skin)
-        {
-            SAFE_RELEASE(_skin);
-            _skin = skin;
-
-            if (skin)
-            {
-                skin->addRef();
-            }
-        }
-    }
-
-    Theme::Skin* Theme::Style::Overlay::getSkin() const
-    {
-        return _skin;
-    }
-
-    void Theme::Style::Overlay::setCursor(Image* cursor)
-    {
-        if (_cursor != cursor)
-        {
-            SAFE_RELEASE(_cursor);
-            _cursor = cursor;
-
-            if (cursor)
-            {
-                cursor->addRef();
-            }
-        }
-    }
-
-    Theme::Image* Theme::Style::Overlay::getCursor() const
-    {
-        return _cursor;
-    }
-            
-    void Theme::Style::Overlay::setImageList(ImageList* imageList)
-    {
-        if (_imageList != imageList)
-        {
-            SAFE_RELEASE(_imageList);
-            _imageList = imageList;
-
-            if (imageList)
-            {
-                imageList->addRef();
-            }
-        }
-    }
-    
-    Theme::ImageList* Theme::Style::Overlay::getImageList() const
-    {
-        return _imageList;
-    }
-
-    // Implementation of AnimationHandler
-    unsigned int Theme::Style::Overlay::getAnimationPropertyComponentCount(int propertyId) const
-    {
-        switch(propertyId)
-        {
-        case Theme::Style::Overlay::ANIMATE_OPACITY:
-            return 1;
-        default:
-            return -1;
-        }
-    }
-
-    void Theme::Style::Overlay::getAnimationPropertyValue(int propertyId, AnimationValue* value)
-    {
-        switch(propertyId)
-        {
-        case ANIMATE_OPACITY:
-            value->setFloat(0, _opacity);
-            break;
-        default:
-            break;
-        }
-    }
-
-    void Theme::Style::Overlay::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
-    {
-        switch(propertyId)
-        {
-            case ANIMATE_OPACITY:
-            {
-                float opacity = value->getFloat(0);
-                if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
-                {
-                    _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
-                }
-                else
-                {
-                    opacity = Curve::lerp(blendWeight, _opacity, opacity);
-                }
-                _opacity = opacity;
-                break;
-            }
-            default:
-                break;
-        }
-    }
     
     /**
      * Theme utility methods.

+ 51 - 224
gameplay/src/Theme.h

@@ -11,9 +11,6 @@
 namespace gameplay
 {
 
-static const unsigned int MAX_OVERLAYS = 4;
-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
@@ -54,7 +51,7 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
  *    }
  *   
  *    // Defines the border and background of a Control.
- *    Skin <SkinID>
+ *    Skin <skinID>
  *    {
  *        // The corners and edges of the given region will be used as border sprites.
  *        border
@@ -125,40 +122,23 @@ class Theme: public Ref
     friend class Skin;
 
 public:
-    class Style;
-    class Cursor;
-
-private:
-    /**
-     * Creates an instance of a Theme from a theme file.
-     *
-     * @param path Path to a theme file.
-     *
-     * @return A new Theme.
-     */
-    static Theme* create(const char* path);
 
     /**
-     * Returns style with the given name.
+     * Class representing a set of themed attributes that can be
+     * assigned to a control.
      *
-     * @param id ID of the style (as specified in the Theme file).
-     *
-     * @return Instance of the Style.
+     * Defined in "ThemeStyle.h"
      */
-    Theme::Style* getStyle(const char* id) const;
-
-    void setProjectionMatrix(const Matrix& matrix);
-
-    SpriteBatch* getSpriteBatch() const;
+    class Style;
+    friend class Style;
 
-public:
     /**
      * Struct representing the UV coordinates of a rectangular image.
      */
-    typedef class UVs
+    struct UVs
     {
-    public:
         UVs();
+
         UVs(float u1, float v1, float u2, float v2);
 
         static const UVs& empty();
@@ -167,15 +147,14 @@ public:
         float v1;
         float u2;
         float v2;
-    } UVs;
+    };
 
     /**
      * Struct representing margin, border, and padding areas by
      * the width or height of each side.
      */
-    typedef class SideRegions
+    typedef struct SideRegions
     {
-    public:
         SideRegions() : top(0), bottom(0), left(0), right(0) {}
 
         static const SideRegions& empty();
@@ -186,12 +165,20 @@ public:
         float right;
     } Margin, Border, Padding;
 
+private:
+
+    /**
+     * Class representing an image within the theme's texture atlas.
+     * An image has a region and a blend color in addition to an ID.
+     * UV coordinates are calculated from the region and can be retrieved.
+     */
     class Image : public Ref
     {
         friend class Theme;
         friend class Control;
 
     public:
+
         const char* getId() const;
 
         const UVs& getUVs() const;
@@ -201,7 +188,9 @@ public:
         const Vector4& getColor() const;
 
     private:
+
         Image(float tw, float th, const Rectangle& region, const Vector4& color);
+
         ~Image();
 
         static Image* create(float tw, float th, Properties* properties, const Vector4& defaultColor);
@@ -212,19 +201,28 @@ public:
         Vector4 _color;
     };
 
+    /**
+     * Class representing a collection of theme images.  An image list
+     * can be assigned to each overlay of a style, and controls
+     * using the style can then retrieve images by ID in order to draw themselves.
+     */
     class ImageList : public Ref
     {
         friend class Theme;
         friend class Control;
 
     public:
+
         const char* getId() const;
 
         Image* getImage(const char* imageId) const;
 
     private:
+
         ImageList(const Vector4& color);
+
         ImageList(const ImageList& copy);
+
         ~ImageList();
 
         static ImageList* create(float tw, float th, Properties* properties);
@@ -242,6 +240,7 @@ public:
         friend class Theme;
 
     public:
+
         enum SkinArea
         {
             TOP_LEFT, TOP, TOP_RIGHT,
@@ -280,8 +279,9 @@ public:
         const Vector4& getColor() const;
 
     private:
+
         Skin(float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color);
-        //Skin(const Skin& copy);
+        
         ~Skin();
 
         static Skin* create(const char* id, float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color);
@@ -290,217 +290,44 @@ public:
     
         std::string _id;
         Theme::Border _border;
-        UVs _uvs[MAX_OVERLAY_REGIONS];
+        UVs _uvs[9];
         Vector4 _color;
         Rectangle _region;
         float _tw, _th;
     };
-    
-    /**
-     * This class represents the appearance of a control.  A style can have padding and margin values,
-     * as well as overlays for each of the control's states.  Each overlay in turn can reference
-     * the above classes to determine the border, background, cursor, and icon settings to use for
-     * a particular state.
-     */
-    class Style
-    {
-        friend class Theme;
-        friend class Control;
-
-    public:
-        class Overlay;
-
-        enum OverlayType
-        {
-            OVERLAY_NORMAL,
-            OVERLAY_FOCUS,
-            OVERLAY_ACTIVE,
-            OVERLAY_DISABLED
-        };
-
-        /**
-         * Returns the Id of this Style.
-         */
-        const char* getId() const;
-
-        /**
-         * Gets an overlay from the overlay type.
-         */
-        Theme::Style::Overlay* getOverlay(OverlayType overlayType) const;
-
-        /**
-         * Gets the Padding region of this style.
-         */
-        const Theme::Padding& getPadding() const;
 
-        /**
-         * Gets the Margin region of this style.
-         */
-        const Theme::Margin& getMargin() const;
-
-        /**
-         * Set this size of this Style's padding.
-         *
-         * Padding is the space between a Control's content (all icons and text) and its border.
-         */
-        void setPadding(float top, float bottom, float left, float right);
-
-        /**
-         * Set the size of this Style's margin.
-         *
-         * The margin is used by Layouts other than AbsoluteLayout to put space between Controls.
-         */
-        void setMargin(float top, float bottom, float left, float right);
-       
-        /**
-         * This class represents a control's overlay for one of the 3 modes: normal, focussed or active.
-         */
-        class Overlay : 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.
-            */
-            Font* getFont() const;
-
-            void setFont(Font* font);
-
-            // Font size.
-            unsigned int getFontSize() const;
-            void setFontSize(unsigned int fontSize);
-
-            // Alignment.
-            Font::Justify getTextAlignment() const;
-            void setTextAlignment(Font::Justify alignment);
-            
-            // Text direction.
-            bool getTextRightToLeft() const;
-            void setTextRightToLeft(bool rightToLeft);
-
-            const Vector4& getTextColor() const;
-            void setTextColor(const Vector4& color); 
-
-            const Rectangle& getImageRegion(const char* id) const;
-            void setImageRegion(const char* id, const Rectangle& region, float tw, float th);
-
-            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();
-            Overlay(const Overlay& copy);
-            ~Overlay();
-
-            static Overlay* create();
-
-            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, 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();
-        
-        std::string _id;
-        Margin _margin;
-        Padding _padding;
-        Overlay* _overlays[MAX_OVERLAYS];
-        float _tw;
-        float _th;
-    };
-
-private:
     /**
      * Constructor.
      */
     Theme();
 
     /**
-     * Copy Constructor.
+     * Constructor.
      */
-    Theme(const Theme* theme);
+    Theme(const Theme& theme);
 
     /**
      * Destructor.
      */
     ~Theme();
 
+    /**
+     * Creates an instance of a Theme from a theme file.
+     *
+     * @param path Path to a theme file.
+     *
+     * @return A new Theme.
+     */
+    static Theme* create(const char* path);
+
+    Theme::Style* getStyle(const char* id) const;
+
+    void setProjectionMatrix(const Matrix& matrix);
+
+    SpriteBatch* getSpriteBatch() const;
+
     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;

+ 471 - 0
gameplay/src/ThemeStyle.cpp

@@ -0,0 +1,471 @@
+#include "ThemeStyle.h"
+
+namespace gameplay
+{
+
+/****************
+ * Theme::Style *
+ ****************/
+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), _tw(tw), _th(th), _margin(margin), _padding(padding)
+{
+    _overlays[OVERLAY_NORMAL] = normal;
+    _overlays[OVERLAY_FOCUS] = focus;
+    _overlays[OVERLAY_ACTIVE] = active;
+    _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 < OVERLAY_MAX; i++)
+    {
+        _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
+    }
+}
+
+Theme::Style::~Style()
+{
+    for (unsigned int i = 0; i < OVERLAY_MAX; i++)
+    {
+        SAFE_RELEASE(_overlays[i]);
+    }
+}
+    
+const char* Theme::Style::getId() const
+{
+    return _id.data();
+}
+
+Theme::Style::Overlay* Theme::Style::getOverlay(OverlayType overlayType) const
+{
+    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 *
+    *************************/
+Theme::Style::Overlay* Theme::Style::Overlay::create()
+{
+    Overlay* overlay = new Overlay();
+    return overlay;
+}
+
+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(_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;
+}
+
+void Theme::Style::Overlay::setFont(Font* font)
+{
+    if (_font != font)
+    {
+        SAFE_RELEASE(_font);
+
+        _font = font;
+
+        if (_font)
+        {
+            _font->addRef();
+        }
+    }
+}
+
+unsigned int Theme::Style::Overlay::getFontSize() const
+{
+    return _fontSize;
+}
+
+void Theme::Style::Overlay::setFontSize(unsigned int fontSize)
+{
+    _fontSize = fontSize;
+}
+
+Font::Justify Theme::Style::Overlay::getTextAlignment() const
+{
+    return _alignment;
+}
+
+void Theme::Style::Overlay::setTextAlignment(Font::Justify alignment)
+{
+    _alignment = alignment;
+}
+
+bool Theme::Style::Overlay::getTextRightToLeft() const
+{
+    return _textRightToLeft;
+}
+
+void Theme::Style::Overlay::setTextRightToLeft(bool rightToLeft)
+{
+    _textRightToLeft = rightToLeft;
+}
+
+const Vector4& Theme::Style::Overlay::getTextColor() const
+{
+    return _textColor;
+}
+
+void Theme::Style::Overlay::setTextColor(const Vector4& color)
+{
+    _textColor = color;
+}
+
+const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
+{
+    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));
+}
+
+const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
+{
+    Image* image = _imageList->getImage(id);
+    if (image)
+    {
+        return image->getColor();
+    }
+    else
+    {
+        return Vector4::zero();
+    }
+}
+
+void Theme::Style::Overlay::setImageColor(const char* id, const Vector4& color)
+{
+    Image* image = _imageList->getImage(id);
+    assert(image);
+    image->_color.set(color);
+}
+
+const Theme::UVs& Theme::Style::Overlay::getImageUVs(const char* id) const
+{
+    Image* image = _imageList->getImage(id);
+    if (image)
+    {
+        return image->getUVs();
+    }
+    else
+    {
+        return UVs::empty();
+    }
+}
+
+const Rectangle& Theme::Style::Overlay::getCursorRegion() const
+{
+    if (_cursor)
+    {
+        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));
+}
+
+const Vector4& Theme::Style::Overlay::getCursorColor() const
+{
+    if (_cursor)
+    {
+        return _cursor->getColor();
+    }
+    else
+    {
+        return Vector4::zero();
+    }
+}
+
+void Theme::Style::Overlay::setCursorColor(const Vector4& color)
+{
+    assert(_cursor);
+    _cursor->_color.set(color);
+}
+
+const Theme::UVs& Theme::Style::Overlay::getCursorUVs() const
+{
+    if (_cursor)
+    {
+        return _cursor->getUVs();
+    }
+    else
+    {
+        return UVs::empty();
+    }
+}
+
+void Theme::Style::Overlay::setSkin(Skin* skin)
+{
+    if (_skin != skin)
+    {
+        SAFE_RELEASE(_skin);
+        _skin = skin;
+
+        if (skin)
+        {
+            skin->addRef();
+        }
+    }
+}
+
+Theme::Skin* Theme::Style::Overlay::getSkin() const
+{
+    return _skin;
+}
+
+void Theme::Style::Overlay::setCursor(Image* cursor)
+{
+    if (_cursor != cursor)
+    {
+        SAFE_RELEASE(_cursor);
+        _cursor = cursor;
+
+        if (cursor)
+        {
+            cursor->addRef();
+        }
+    }
+}
+
+Theme::Image* Theme::Style::Overlay::getCursor() const
+{
+    return _cursor;
+}
+            
+void Theme::Style::Overlay::setImageList(ImageList* imageList)
+{
+    if (_imageList != imageList)
+    {
+        SAFE_RELEASE(_imageList);
+        _imageList = imageList;
+
+        if (imageList)
+        {
+            imageList->addRef();
+        }
+    }
+}
+    
+Theme::ImageList* Theme::Style::Overlay::getImageList() const
+{
+    return _imageList;
+}
+
+// Implementation of AnimationHandler
+unsigned int Theme::Style::Overlay::getAnimationPropertyComponentCount(int propertyId) const
+{
+    switch(propertyId)
+    {
+    case Theme::Style::Overlay::ANIMATE_OPACITY:
+        return 1;
+    default:
+        return -1;
+    }
+}
+
+void Theme::Style::Overlay::getAnimationPropertyValue(int propertyId, AnimationValue* value)
+{
+    switch(propertyId)
+    {
+    case ANIMATE_OPACITY:
+        value->setFloat(0, _opacity);
+        break;
+    default:
+        break;
+    }
+}
+
+void Theme::Style::Overlay::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
+{
+    switch(propertyId)
+    {
+        case ANIMATE_OPACITY:
+        {
+            float opacity = value->getFloat(0);
+            if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
+            {
+                _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
+            }
+            else
+            {
+                opacity = Curve::lerp(blendWeight, _opacity, opacity);
+            }
+            _opacity = opacity;
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+}

+ 225 - 0
gameplay/src/ThemeStyle.h

@@ -0,0 +1,225 @@
+#ifndef THEMESTYLE_H_
+#define THEMESTYLE_H_
+
+#include "Base.h"
+#include "Ref.h"
+#include "Font.h"
+#include "Rectangle.h"
+#include "Texture.h"
+#include "Properties.h"
+#include "Theme.h"
+
+namespace gameplay
+{
+
+/**
+ * This class represents the appearance of a control.  A style can have padding and margin values,
+ * as well as overlays for each of the control's states.  Each overlay in turn can reference
+ * the above classes to determine the border, background, cursor, and icon settings to use for
+ * a particular state.
+ */
+class Theme::Style
+{
+    friend class Theme;
+    friend class Control;
+
+private:
+
+    /**
+     * A style has one overlay for each possible control state.
+     */
+    enum OverlayType
+    {
+        OVERLAY_NORMAL,
+        OVERLAY_FOCUS,
+        OVERLAY_ACTIVE,
+        OVERLAY_DISABLED,
+        OVERLAY_MAX
+    };
+
+    /**
+     * This class represents a control's overlay for one of its states.
+     */
+    class Overlay : public Ref, public AnimationTarget
+    {
+        friend class Theme;
+        friend class Theme::Style;
+        friend class Control;
+
+    private:
+
+        static const int ANIMATE_OPACITY = 1;
+        static const char ANIMATION_OPACITY_BIT = 0x01;
+
+        Overlay();
+            
+        Overlay(const Overlay& copy);
+            
+        ~Overlay();
+
+        static Overlay* create();
+
+        OverlayType getType();
+
+        float getOpacity() const;
+
+        void setOpacity(float opacity);
+
+        void setBorder(float top, float bottom, float left, float right);
+
+        const Theme::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;
+
+        Font* getFont() const;
+
+        void setFont(Font* font);
+
+        unsigned int getFontSize() const;
+
+        void setFontSize(unsigned int fontSize);
+
+        Font::Justify getTextAlignment() const;
+
+        void setTextAlignment(Font::Justify alignment);
+            
+        bool getTextRightToLeft() const;
+
+        void setTextRightToLeft(bool rightToLeft);
+
+        const Vector4& getTextColor() const;
+
+        void setTextColor(const Vector4& color); 
+
+        const Rectangle& getImageRegion(const char* id) const;
+
+        void setImageRegion(const char* id, const Rectangle& region, float tw, float th);
+
+        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);
+       
+        void setSkin(Theme::Skin* Skin);
+
+        Theme::Skin* getSkin() const;
+
+        void setCursor(Theme::Image* cursor);
+            
+        Theme::Image* getCursor() const;
+            
+        void setImageList(Theme::ImageList* imageList);
+            
+        Theme::ImageList* getImageList() const;
+
+        void applyAnimationValueOpacity(float opacity, float blendWeight);
+
+        Skin* _skin;
+        Theme::Image* _cursor;
+        Theme::ImageList* _imageList;
+        Font* _font;
+        unsigned int _fontSize;
+        Font::Justify _alignment;
+        bool _textRightToLeft;
+        Vector4 _textColor;
+        float _opacity;
+    };
+
+    /**
+     * Returns the Id of this Style.
+     */
+    const char* getId() const;
+
+    /**
+     * Gets an overlay from the overlay type.
+     */
+    Overlay* getOverlay(OverlayType overlayType) const;
+
+    /**
+     * Gets the Padding region of this style.
+     */
+    const Theme::Padding& getPadding() const;
+
+    /**
+     * Gets the Margin region of this style.
+     */
+    const Theme::Margin& getMargin() const;
+
+    /**
+     * Set this size of this Style's padding.
+     *
+     * Padding is the space between a Control's content (all icons and text) and its border.
+     */
+    void setPadding(float top, float bottom, float left, float right);
+
+    /**
+     * Set the size of this Style's margin.
+     *
+     * The margin is used by Layouts other than AbsoluteLayout to put space between Controls.
+     */
+    void setMargin(float top, float bottom, float left, float right);
+
+private:
+
+    /**
+     * Constructor.
+     */
+    Style(const char* id, float tw, float th,
+          const Theme::Margin& margin, const Theme::Padding& padding,
+          Overlay* normal, Overlay* focus, Overlay* active, Overlay* disabled);
+
+    /**
+     * Constructor.
+     */
+    Style(const Style& style);
+
+    /**
+     * Destructor.
+     */
+    ~Style();
+        
+    std::string _id;
+    float _tw;
+    float _th;
+    Theme::Margin _margin;
+    Theme::Padding _padding;
+    Overlay* _overlays[OVERLAY_MAX];
+};
+
+}
+
+#endif

+ 6 - 5
gameplay/src/VerticalLayout.cpp

@@ -44,9 +44,10 @@ namespace gameplay
     void VerticalLayout::update(const Container* container)
     {
         // Need border, padding.
-        Theme::Style* style = container->getStyle();
+        //Theme::Style* style = container->getStyle();
         Theme::Border border = container->getBorder(container->getState());
-        Theme::Padding padding = style->getPadding();
+        //Theme::Padding padding = style->getPadding();
+        Theme::Padding padding = container->getPadding();
 
         float yPosition = 0;
 
@@ -70,12 +71,12 @@ namespace gameplay
         {
             Control* control = controls.at(i);
 
-            const Rectangle& bounds = control->getBounds();
-            const Theme::Margin& margin = control->getStyle()->getMargin();
+            const Rectangle& bounds = control->getClipBounds();
+            const Theme::Margin& margin = control->getMargin();
 
             yPosition += margin.top;
 
-            control->setPosition(0, yPosition, 0L);
+            control->setPosition(0, yPosition);
             if (control->isDirty())
             {
                 control->update(container->getClip());