Explorar el Código

Adding TextBox; refactoring Theme API; cleaning up memory leaks.

Adam Blake hace 14 años
padre
commit
e3d061a1c5

+ 35 - 0
gameplay.sln

@@ -25,6 +25,23 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample03-character", "gamep
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gameplay-encoder", "gameplay-encoder\gameplay-encoder.vcxproj", "{9D69B743-4872-4DD1-8E30-0087C64298D7}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D3CA8AE2-3ED6-458B-963A-EDA671E6F51C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample05-particles", "gameplay-samples\sample05-particles\sample05-particles.vcxproj", "{217A796A-1B0F-4F09-ABF1-BC6E029D5C7D}"
+	ProjectSection(ProjectDependencies) = postProject
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test-text", "..\test-text\test-text.vcxproj", "{635ECFF5-9E27-4C0A-9FCF-A7A4FE161541}"
+	ProjectSection(ProjectDependencies) = postProject
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test-forms", "..\test-forms\test-forms.vcxproj", "{F0CF2418-536D-4C4F-ACB5-8BBC37B208A9}"
+	ProjectSection(ProjectDependencies) = postProject
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -68,6 +85,24 @@ Global
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.DebugMem|Win32.Build.0 = Debug|Win32
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.Release|Win32.ActiveCfg = Release|Win32
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.Release|Win32.Build.0 = Release|Win32
+		{217A796A-1B0F-4F09-ABF1-BC6E029D5C7D}.Debug|Win32.ActiveCfg = Debug|Win32
+		{217A796A-1B0F-4F09-ABF1-BC6E029D5C7D}.Debug|Win32.Build.0 = Debug|Win32
+		{217A796A-1B0F-4F09-ABF1-BC6E029D5C7D}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{217A796A-1B0F-4F09-ABF1-BC6E029D5C7D}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{217A796A-1B0F-4F09-ABF1-BC6E029D5C7D}.Release|Win32.ActiveCfg = Release|Win32
+		{217A796A-1B0F-4F09-ABF1-BC6E029D5C7D}.Release|Win32.Build.0 = Release|Win32
+		{635ECFF5-9E27-4C0A-9FCF-A7A4FE161541}.Debug|Win32.ActiveCfg = Debug|Win32
+		{635ECFF5-9E27-4C0A-9FCF-A7A4FE161541}.Debug|Win32.Build.0 = Debug|Win32
+		{635ECFF5-9E27-4C0A-9FCF-A7A4FE161541}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{635ECFF5-9E27-4C0A-9FCF-A7A4FE161541}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{635ECFF5-9E27-4C0A-9FCF-A7A4FE161541}.Release|Win32.ActiveCfg = Release|Win32
+		{635ECFF5-9E27-4C0A-9FCF-A7A4FE161541}.Release|Win32.Build.0 = Release|Win32
+		{F0CF2418-536D-4C4F-ACB5-8BBC37B208A9}.Debug|Win32.ActiveCfg = Debug|Win32
+		{F0CF2418-536D-4C4F-ACB5-8BBC37B208A9}.Debug|Win32.Build.0 = Debug|Win32
+		{F0CF2418-536D-4C4F-ACB5-8BBC37B208A9}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{F0CF2418-536D-4C4F-ACB5-8BBC37B208A9}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{F0CF2418-536D-4C4F-ACB5-8BBC37B208A9}.Release|Win32.ActiveCfg = Release|Win32
+		{F0CF2418-536D-4C4F-ACB5-8BBC37B208A9}.Release|Win32.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 12 - 1
gameplay/src/AbsoluteLayout.cpp

@@ -1,5 +1,7 @@
 #include "Base.h"
+#include "Control.h"
 #include "AbsoluteLayout.h"
+#include "Container.h"
 
 namespace gameplay
 {
@@ -26,8 +28,17 @@ namespace gameplay
         return Layout::LAYOUT_ABSOLUTE;
     }
 
-    void AbsoluteLayout::update(std::vector<Control*> controls, const Vector2& size, Theme::Style* containerStyle)
+    void AbsoluteLayout::update(const Container* container)
     {
         // An AbsoluteLayout does nothing to modify the layout of Controls.
+        std::vector<Control*> controls = container->getControls();
+        unsigned int controlsCount = controls.size();
+        for (unsigned int i = 0; i < controlsCount; i++)
+        {
+            if (controls[i]->isDirty())
+            {
+                controls[i]->update(container->getPosition());
+            }
+        }
     }
 }

+ 1 - 1
gameplay/src/AbsoluteLayout.h

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

+ 1 - 0
gameplay/src/Button.cpp

@@ -11,6 +11,7 @@ namespace gameplay
 
     Button::~Button()
     {
+        SAFE_DELETE(_callback);
     }
 
     Button* Button::create(Theme::Style* style, Properties* properties)

+ 23 - 12
gameplay/src/CheckBox.cpp

@@ -78,23 +78,29 @@ void CheckBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
     Theme::Icon* icon = overlay->getCheckBoxIcon();
     if (icon)
     {
-        Theme::Border border = _style->getBorder();
-        Theme::Padding padding = _style->getPadding();
+        const Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+        Theme::Border border;
+        if (containerRegion)
+        {
+                border = containerRegion->getBorder();
+        }
+        const Theme::Padding padding = _style->getPadding();
+
+        const Vector2 size = icon->getSize();
+        const Vector4 color = icon->getColor();
 
         Vector2 pos(position.x + _position.x + border.left + padding.left,
-            position.y + _position.y + (_size.y - border.bottom - padding.bottom) / 2.0f - icon->size.y / 2.0f);
+            position.y + _position.y + (_size.y - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
-        if (_state == STATE_ACTIVE)
+        if (_checked)
         {
-            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->active.u1, icon->active.v1, icon->active.u2, icon->active.v2, overlay->getBorderColor());
-        }
-        else if (_checked)
-        {
-            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->on.u1, icon->on.v1, icon->on.u2, icon->on.v2, overlay->getBorderColor());
+            const Theme::UVs on = icon->getOnUVs();
+            spriteBatch->draw(pos.x, pos.y, size.x, size.y, on.u1, on.v1, on.u2, on.v2, color);
         }
         else
         {
-            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->off.u1, icon->off.v1, icon->off.u2, icon->off.v2, overlay->getBorderColor());
+            const Theme::UVs off = icon->getOffUVs();
+            spriteBatch->draw(pos.x, pos.y, size.x, size.y, off.u1, off.v1, off.u2, off.v2, color);
         }
     }
 }
@@ -104,14 +110,19 @@ void CheckBox::drawText(const Vector2& position)
     // TODO: Batch all labels that use the same font.
     Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
     Theme::Icon* icon = overlay->getCheckBoxIcon();
-    Theme::Border border = _style->getBorder();
+    Theme::Border border;
+    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+    if (containerRegion)
+    {
+        border = overlay->getContainerRegion()->getBorder();
+    }
     Theme::Padding padding = _style->getPadding();
 
     // Set up the text viewport.
     float iconWidth = 0.0f;
     if (icon)
     {
-        iconWidth = icon->size.x;
+        iconWidth = icon->getSize().x;
     }
 
     Font* font = overlay->getFont();

+ 34 - 26
gameplay/src/Container.cpp

@@ -8,6 +8,7 @@
 #include "CheckBox.h"
 #include "RadioButton.h"
 #include "Slider.h"
+#include "TextBox.h"
 
 namespace gameplay
 {
@@ -23,11 +24,18 @@ namespace gameplay
 
     Container::~Container()
     {
+        std::vector<Control*>::iterator it;
+        for (it = _controls.begin(); it < _controls.end(); it++)
+        {
+            SAFE_RELEASE((*it));
+        }
+
+        SAFE_RELEASE(_layout);
     }
 
     Container* Container::create(const char* id, Layout::Type type)
     {
-        Layout* layout;
+        Layout* layout = NULL;
         switch(type)
         {
         case Layout::LAYOUT_ABSOLUTE:
@@ -105,6 +113,10 @@ namespace gameplay
             {
                 control = Slider::create(controlStyle, controlSpace);
             }
+            else if (controlName == "TEXTBOX")
+            {
+                control = TextBox::create(controlStyle, controlSpace);
+            }
 
             // Add the new control to the form.
             if (control)
@@ -140,7 +152,6 @@ namespace gameplay
     unsigned int Container::addControl(Control* control)
     {
         _controls.push_back(control);
-        control->addRef();
 
         return _controls.size() - 1;
     }
@@ -203,28 +214,20 @@ namespace gameplay
         return NULL;
     }
 
-    void Container::update()
+    std::vector<Control*> Container::getControls() const
+    {
+        return _controls;
+    }
+
+    void Container::update(const Vector2& position)
     {
         // Should probably have sizeChanged() for this.
-        //if (isDirty())
+        if (isDirty())
         {
-            // Call update() on nested Containers.
-            std::vector<Control*>::const_iterator it;
-            for (it = _controls.begin(); it < _controls.end(); it++)
-            {
-                // Can't do this without enabling run-time type information!
-                //Container* container = dynamic_cast<Container*>(*it);
-
-                Control* control = *it;
-                if (control->isContainer())
-                {
-                    Container* container = static_cast<Container*>(control);
-                    container->update();
-                }
-            }
-            
-            _layout->update(_controls, _size, _style);
+            _layout->update(this);
         }
+
+        _dirty = false;
     }
 
     void Container::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
@@ -283,7 +286,7 @@ namespace gameplay
             const Vector2& size = control->getSize();
             const Vector2& position = control->getPosition();
             
-            if (control->getState() == Control::STATE_ACTIVE ||
+            if (control->getState() != Control::STATE_NORMAL ||
                 (x >= position.x &&
                  x <= position.x + size.x &&
                  y >= position.y &&
@@ -305,6 +308,16 @@ namespace gameplay
         }
     }
 
+    void Container::keyEvent(Keyboard::KeyEvent evt, int key)
+    {
+        std::vector<Control*>::const_iterator it;
+        for (it = _controls.begin(); it < _controls.end(); it++)
+        {
+            Control* control = *it;
+            control->keyEvent(evt, key);
+        }
+    }
+
     Layout::Type Container::getLayoutType(const char* layoutString)
     {
         std::string layoutName(layoutString);
@@ -327,9 +340,4 @@ namespace gameplay
             return Layout::LAYOUT_ABSOLUTE;
         }
     }
-
-    bool Container::isContainer()
-    {
-        return true;
-    }
 }

+ 7 - 5
gameplay/src/Container.h

@@ -57,19 +57,21 @@ public:
      */
     Control* getControl(const char* id) const;
 
+    std::vector<Control*> getControls() const;
+
     /**
      * Updates the position of each Control within this Container
      * according to the Container's Layout.
      */
-    void update();
+    virtual void update(const Vector2& position);
 
     //void draw(Theme* theme, const Vector2& position);
-    void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
-    void drawText(const Vector2& position);
+    virtual void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
+    virtual void drawText(const Vector2& position);
 
-    void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+    virtual void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
-    bool isContainer();
+    virtual void keyEvent(Keyboard::KeyEvent evt, int key);
 
 protected:
     Container();

+ 24 - 28
gameplay/src/Control.cpp

@@ -83,7 +83,7 @@ namespace gameplay
         _state = STATE_NORMAL;
     }
 
-    Theme::Style::OverlayType Control::getOverlayType()
+    Theme::Style::OverlayType Control::getOverlayType() const
     {
         switch (_state)
         {
@@ -103,31 +103,38 @@ namespace gameplay
         // Empty stub to be implemented by Button and its descendents.
     }
 
+    void Control::keyEvent(Keyboard::KeyEvent evt, int key)
+    {
+    }
+
+    void Control::update(const Vector2& position)
+    {
+    }
+
     void Control::drawBorder(SpriteBatch* spriteBatch, const Vector2& position)
     {
         Vector2 pos(position.x + _position.x, position.y + _position.y);
 
-        // Get the overlay for this control's current state.
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-
-        if (overlay && !(overlay->getRegion().isEmpty()))
+        // Get the border and background images for this control's current state.
+        Theme::ContainerRegion* containerRegion = _style->getOverlay(getOverlayType())->getContainerRegion();
+        if (containerRegion)
         {
             // Get the UVs.
             Theme::UVs topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight;
-            topLeft = overlay->getUVs(Theme::Style::Overlay::TOP_LEFT);
-            top = overlay->getUVs(Theme::Style::Overlay::TOP);
-            topRight = overlay->getUVs(Theme::Style::Overlay::TOP_RIGHT);
-            left = overlay->getUVs(Theme::Style::Overlay::LEFT);
-            center = overlay->getUVs(Theme::Style::Overlay::CENTER);
-            right = overlay->getUVs(Theme::Style::Overlay::RIGHT);
-            bottomLeft = overlay->getUVs(Theme::Style::Overlay::BOTTOM_LEFT);
-            bottom = overlay->getUVs(Theme::Style::Overlay::BOTTOM);
-            bottomRight = overlay->getUVs(Theme::Style::Overlay::BOTTOM_RIGHT);
+            topLeft = containerRegion->getUVs(Theme::ContainerRegion::TOP_LEFT);
+            top = containerRegion->getUVs(Theme::ContainerRegion::TOP);
+            topRight = containerRegion->getUVs(Theme::ContainerRegion::TOP_RIGHT);
+            left = containerRegion->getUVs(Theme::ContainerRegion::LEFT);
+            center = containerRegion->getUVs(Theme::ContainerRegion::CENTER);
+            right = containerRegion->getUVs(Theme::ContainerRegion::RIGHT);
+            bottomLeft = containerRegion->getUVs(Theme::ContainerRegion::BOTTOM_LEFT);
+            bottom = containerRegion->getUVs(Theme::ContainerRegion::BOTTOM);
+            bottomRight = containerRegion->getUVs(Theme::ContainerRegion::BOTTOM_RIGHT);
 
             // Calculate screen-space positions.
-            Theme::Border border = _style->getBorder();
+            Theme::Border border = containerRegion->getBorder();
             Theme::Padding padding = _style->getPadding();
-            Vector4 borderColor = overlay->getBorderColor();
+            Vector4 borderColor = containerRegion->getColor();
 
             float midWidth = _size.x - border.left - border.right;
             float midHeight = _size.y - border.top - border.bottom;
@@ -170,17 +177,6 @@ namespace gameplay
 
     bool Control::isDirty()
     {
-        if (_dirty)
-        {
-            _dirty = false;
-            return true;
-        }
-
-        return false;
-    }
-
-    bool Control::isContainer()
-    {
-        return false;
+        return _dirty;
     }
 }

+ 6 - 3
gameplay/src/Control.h

@@ -7,6 +7,7 @@
 #include "SpriteBatch.h"
 #include "Theme.h"
 #include "Touch.h"
+#include "Keyboard.h"
 
 namespace gameplay
 {
@@ -82,13 +83,17 @@ public:
     void disable();
     void enable();
 
-    Theme::Style::OverlayType getOverlayType();
+    Theme::Style::OverlayType getOverlayType() const;
 
     /**
      * Defaults to empty stub.
      */
     virtual void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    virtual void keyEvent(Keyboard::KeyEvent evt, int key);
+
+    virtual void update(const Vector2& position);
+
     /**
      * Draws the themed border and background of a control.
      */
@@ -107,8 +112,6 @@ public:
 
     void themeChanged();
 
-    virtual bool isContainer();
-
 protected:
     Control();
     Control(const Control& copy);

+ 397 - 4
gameplay/src/Font.cpp

@@ -997,6 +997,389 @@ void Font::measureText(const char* text, const Rectangle& viewport, unsigned int
     }
 }
 
+unsigned int Font::getIndexAtLocation(const char* text, const Rectangle& area, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
+                                      Justify justify, bool wrap, bool rightToLeft)
+{
+    unsigned int charIndex = 0;
+
+    // Essentially need to measure text until we reach inLocation.
+    float scale = (float)size / _size;
+    const char* token = text;
+    const int length = strlen(text);
+    int yPos = area.y;
+
+    Justify vAlign = static_cast<Justify>(justify & 0xF0);
+    if (vAlign == 0)
+    {
+        vAlign = ALIGN_TOP;
+    }
+
+    Justify hAlign = static_cast<Justify>(justify & 0x0F);
+    if (hAlign == 0)
+    {
+        hAlign = ALIGN_LEFT;
+    }
+
+    token = text;
+
+    // For alignments other than top-left, need to calculate the y position to begin drawing from
+    // and the starting x position of each line.  For right-to-left text, need to determine
+    // the number of characters on each line.
+    std::vector<int> xPositions;
+    std::vector<unsigned int> lineLengths;
+    if (vAlign != ALIGN_TOP || hAlign != ALIGN_LEFT || rightToLeft)
+    {
+        int lineWidth = 0;
+        int delimWidth = 0;
+
+        if (wrap)
+        {
+            // Go a word at a time.
+            bool reachedEOF = false;
+            unsigned int lineLength = 0;
+            while (token[0] != 0)
+            {
+                unsigned int tokenWidth = 0;
+
+                // Handle delimiters until next token.
+                char delimiter = token[0];
+                while (delimiter == ' ' ||
+                       delimiter == '\t' ||
+                       delimiter == '\r' ||
+                       delimiter == '\n' ||
+                       delimiter == 0)
+                {
+                    switch (delimiter)
+                    {
+                        case ' ':
+                            delimWidth += size>>1;
+                            lineLength++;
+                            break;
+                        case '\r':
+                        case '\n':
+                            yPos += size;
+
+                            if (lineWidth > 0)
+                            {
+                                addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+                            }
+
+                            lineWidth = 0;
+                            lineLength = 0;
+                            delimWidth = 0;
+                            break;
+                        case '\t':
+                            delimWidth += (size>>1)*4;
+                            lineLength++;
+                            break;
+                        case 0:
+                            reachedEOF = true;
+                            break;
+                    }
+
+                    if (reachedEOF)
+                    {
+                        break;
+                    }
+
+                    token++;
+                    delimiter = token[0];
+                }
+
+                if (reachedEOF || token == NULL)
+                {
+                    break;
+                }
+
+                unsigned int tokenLength = strcspn(token, " \r\n\t");
+                tokenWidth += getTokenWidth(token, tokenLength, size, scale);
+
+                // Wrap if necessary.
+                if (lineWidth + tokenWidth + delimWidth > area.width)
+                {
+                    yPos += size;
+
+                    // Push position of current line.
+                    if (lineLength)
+                    {
+                        addLineInfo(area, lineWidth, lineLength-1, hAlign, &xPositions, &lineLengths, rightToLeft);
+                    }
+                    else
+                    {
+                        addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+                    }
+
+                    // Move token to the next line.
+                    lineWidth = 0;
+                    lineLength = 0;
+                    delimWidth = 0;
+                }
+                else
+                {
+                    lineWidth += delimWidth;
+                    delimWidth = 0;
+                }
+
+                lineWidth += tokenWidth;
+                lineLength += tokenLength;
+                token += tokenLength;
+            }
+
+            // Final calculation of vertical position.
+            int textHeight = yPos - area.y;
+            int vWhiteSpace = area.height - textHeight;
+            if (vAlign == ALIGN_VCENTER)
+            {
+                yPos = area.y + vWhiteSpace / 2;
+            }
+            else if (vAlign == ALIGN_BOTTOM)
+            {
+                yPos = area.y + vWhiteSpace;
+            }
+
+            // Calculation of final horizontal position.
+            addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+        }
+        else
+        {
+            // Go a line at a time.
+            while (token[0] != 0)
+            {
+                char delimiter = token[0];
+                while (delimiter == '\n')
+                {
+                    yPos += size;
+                    ++token;
+                    delimiter = token[0];
+                }
+
+                unsigned int tokenLength = strcspn(token, "\n");
+                if (tokenLength == 0)
+                {
+                    tokenLength = strlen(token);
+                }
+
+                int lineWidth = getTokenWidth(token, tokenLength, size, scale);
+                addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+
+                token += tokenLength;
+            }
+
+            int textHeight = yPos - area.y;
+            int vWhiteSpace = area.height - textHeight;
+            if (vAlign == ALIGN_VCENTER)
+            {
+                yPos = area.y + vWhiteSpace / 2;
+            }
+            else if (vAlign == ALIGN_BOTTOM)
+            {
+                yPos = area.y + vWhiteSpace;
+            }
+        }
+
+        if (vAlign == ALIGN_TOP)
+        {
+            yPos = area.y;
+        }
+    }
+
+    // Now we have the info we need in order to render.
+    int xPos = area.x;
+    std::vector<int>::const_iterator xPositionsIt = xPositions.begin();
+    if (xPositionsIt != xPositions.end())
+    {
+        xPos = *xPositionsIt++;
+    }
+
+    token = text;
+    
+    int iteration = 1;
+    unsigned int lineLength;
+    unsigned int currentLineLength = 0;
+    const char* lineStart;
+    std::vector<unsigned int>::const_iterator lineLengthsIt;
+    if (rightToLeft)
+    {
+        lineStart = token;
+        lineLengthsIt = lineLengths.begin();
+        lineLength = *lineLengthsIt++;
+        token += lineLength - 1;
+        iteration = -1;
+    }
+
+    while (token[0] != 0)
+    {
+        // Handle delimiters until next token.
+        unsigned int delimLength = 0;
+        int result = handleDelimiters(&token, size, iteration, area.x, &xPos, &yPos, &delimLength, &xPositionsIt, xPositions.end(), &inLocation);
+        charIndex += delimLength;
+        currentLineLength += delimLength;
+        if (result == 0)
+        {
+            break;
+        }
+        else if (result == 2)
+        {
+            outLocation->x = xPos;
+            outLocation->y = yPos;
+            return charIndex;
+        }
+
+        if (inLocation.x >= xPos && inLocation.x < xPos + (size>>3) &&
+            inLocation.y >= yPos && inLocation.y < yPos + size)
+        {
+            outLocation->x = xPos;
+            outLocation->y = yPos;
+            return charIndex;
+        }
+
+        bool truncated = false;
+        unsigned int tokenLength;
+        unsigned int tokenWidth;
+        unsigned int startIndex;
+        if (rightToLeft)
+        {
+            tokenLength = getReversedTokenLength(token, text);
+            currentLineLength += tokenLength;
+            charIndex += tokenLength;
+            token -= (tokenLength - 1);
+            tokenWidth = getTokenWidth(token, tokenLength, size, scale);
+            iteration = -1;
+            startIndex = tokenLength - 1;
+        }
+        else
+        {
+            tokenLength = strcspn(token, " \r\n\t");
+            tokenWidth = getTokenWidth(token, tokenLength, size, scale);
+            iteration = 1;
+            startIndex = 0;
+        }
+
+        // Wrap if necessary.
+        if (wrap &&
+            xPos + tokenWidth > area.x + area.width ||
+            (rightToLeft && currentLineLength > lineLength))
+        {
+            yPos += size;
+            currentLineLength = tokenLength;
+
+            if (xPositionsIt != xPositions.end())
+            {
+                xPos = *xPositionsIt++;
+            }
+            else
+            {
+                xPos = area.x;
+            }
+        }
+
+        if (yPos > area.y + area.height)
+        {
+            // Truncate below area's vertical limit.
+            break;
+        }
+
+        for (int i = startIndex; i < (int)tokenLength && i >= 0; i += iteration)
+        {
+            char c = token[i];
+            int glyphIndex = c - 32; // HACK for ASCII
+        
+            if (glyphIndex >= 0 && glyphIndex < (int)_glyphCount)
+            {
+                Glyph& g = _glyphs[glyphIndex];
+
+                if (xPos + (int)(g.width*scale) > area.x + area.width)
+                {
+                    // Truncate this line and go on to the next one.
+                    truncated = true;
+                    break;
+                }
+
+                // Check against inLocation.
+                if (inLocation.x >= xPos && inLocation.x < xPos + g.width*scale + (size>>3) &&
+                    inLocation.y >= yPos && inLocation.y < yPos + size)
+                {
+                    outLocation->x = xPos;
+                    outLocation->y = yPos;
+                    return charIndex;
+                }
+
+                xPos += g.width*scale + (size>>3);
+                charIndex++;
+            }
+        }
+
+        if (!truncated)
+        {
+            if (rightToLeft)
+            {
+                if (token == lineStart)
+                {
+                    token += lineLength;
+                    
+                    // Now handle delimiters going forwards.
+                    if (!handleDelimiters(&token, size, 1, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
+                    {
+                        break;
+                    }
+                    charIndex += currentLineLength;
+
+                    if (lineLengthsIt != lineLengths.end())
+                    {
+                        lineLength = *lineLengthsIt++;
+                    }
+                    lineStart = token;
+                    token += lineLength-1;
+                    charIndex += tokenLength;
+                }
+                else
+                {
+                    token--;
+                }
+            }
+            else
+            {
+                token += tokenLength;
+            }
+        }
+        else
+        {
+            if (rightToLeft)
+            {
+                token = lineStart + lineLength;
+                
+                if (!handleDelimiters(&token, size, 1, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
+                {
+                    break;
+                }
+
+                if (lineLengthsIt != lineLengths.end())
+                {
+                    lineLength = *lineLengthsIt++;
+                }
+                lineStart = token;
+                token += lineLength-1;
+            }
+            else
+            {
+                // Skip the rest of this line.
+                unsigned int tokenLength = strcspn(token, "\n");
+
+                if (tokenLength > 0)
+                {                
+                    // Get first token of next line.
+                    token += tokenLength;
+                    charIndex += tokenLength;
+                }
+            }
+        }
+    }
+
+    outLocation->x = xPos;
+    outLocation->y = yPos;
+    return charIndex;
+}
+
 unsigned int Font::getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale)
 {
     // Calculate width of word or line.
@@ -1047,8 +1430,8 @@ unsigned int Font::getReversedTokenLength(const char* token, const char* bufStar
     return length;
 }
 
-bool Font::handleDelimiters(const char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
-                      std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd)
+int Font::handleDelimiters(const char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
+                      std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd, const Vector2* stopAtPosition)
 {
     char delimiter = *token[0];
     bool nextLine = true;
@@ -1058,6 +1441,14 @@ bool Font::handleDelimiters(const char** token, const unsigned int size, const i
             delimiter == '\n' ||
             delimiter == 0)
     {
+        if (stopAtPosition &&
+            stopAtPosition->x >= *xPos && stopAtPosition->x < *xPos + (size>>1) &&
+            stopAtPosition->y >= *yPos && stopAtPosition->y < *yPos + size)
+        {
+            // Success + stopAtPosition was reached.
+            return 2;
+        }
+
         switch (delimiter)
         {
             case ' ':
@@ -1089,14 +1480,16 @@ bool Font::handleDelimiters(const char** token, const unsigned int size, const i
                 (*lineLength)++;
                 break;
             case 0:
-                return false;
+                // EOF reached.
+                return 0;
         }
 
         *token += iteration;
         delimiter = *token[0];
     }
 
-    return true;
+    // Success.
+    return 1;
 }
 
 void Font::addLineInfo(const Rectangle& area, int lineWidth, int lineLength, Justify hAlign,

+ 7 - 3
gameplay/src/Font.h

@@ -175,6 +175,10 @@ public:
     void measureText(const char* text, const Rectangle& clip, unsigned int size, Rectangle* out,
                      Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool ignoreClip = false);
 
+    // Get an index into a string corresponding to the character nearest the given location within the clip region.
+    unsigned int getIndexAtLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
+                                    Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
+
     SpriteBatch* getSpriteBatch();
 
     static Justify getJustifyFromString(const char* justify);
@@ -201,9 +205,9 @@ private:
     unsigned int getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale);
     unsigned int getReversedTokenLength(const char* token, const char* bufStart);
 
-    // Returns false if EOF was reached, true otherwise.
-    bool handleDelimiters(const char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
-                          std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd);
+    // Returns 0 if EOF was reached, 1 if delimiters were handles correctly, and 2 if the stopAtPosition was reached while handling delimiters.
+    int handleDelimiters(const char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
+                          std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd, const Vector2* stopAtPosition = NULL);
     void addLineInfo(const Rectangle& area, int lineWidth, int lineLength, Justify hAlign,
                      std::vector<int>* xPositions, std::vector<unsigned int>* lineLengths, bool rightToLeft);
 

+ 20 - 3
gameplay/src/Form.cpp

@@ -74,6 +74,8 @@ namespace gameplay
         // Add all the controls to the form.
         form->addControls(form->getTheme(), formProperties);
 
+        SAFE_DELETE(properties);
+
         return form;
     }
 
@@ -161,6 +163,11 @@ namespace gameplay
         _viewport = new Viewport(0, 0, _size.x, _size.y);
     }
 
+    void Form::update()
+    {
+        Container::update(Vector2::zero());
+    }
+
     void Form::draw()
     {
         // If this Form has a Node then it's a 3D Form.  The contents will be rendered
@@ -183,11 +190,11 @@ namespace gameplay
                 _frameBuffer->bind();
                 _viewport->bind();
 
-                // Clear form background color.
                 Game* game = Game::getInstance();
-                game->clear(Game::CLEAR_COLOR, _style->getOverlay(getOverlayType())->getBorderColor(), 1.0f, 0);
 
-                //draw(_theme, _position);
+                // Clear form background color.
+                //game->clear(Game::CLEAR_COLOR, _style->getOverlay(getOverlayType())->getBorderColor(), 1.0f, 0);
+
                 draw(_theme->getSpriteBatch(), _position);
                 FrameBuffer::bindDefault();
 
@@ -314,4 +321,14 @@ namespace gameplay
             }
         }
     }
+
+    void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
+    {
+        std::vector<Form*>::const_iterator it;
+        for (it = __forms.begin(); it < __forms.end(); it++)
+        {
+            Form* form = *it;
+            form->keyEvent(evt, key);
+        }
+    }
 }

+ 4 - 0
gameplay/src/Form.h

@@ -7,6 +7,7 @@
 #include "Node.h"
 #include "FrameBuffer.h"
 #include "Touch.h"
+#include "Keyboard.h"
 
 namespace gameplay
 {
@@ -40,6 +41,7 @@ public:
 
     void setNode(Node* node);
 
+    void update();
     void draw();
 
 private:
@@ -53,6 +55,8 @@ private:
 
     static void touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    static void keyEventInternal(Keyboard::KeyEvent evt, int key);
+
     Theme* _theme;              // The Theme applied to this Form.
     Model* _quad;               // Quad for rendering this Form in world-space.
     Node* _node;                // Node for transforming this Form in world-space.

+ 20 - 10
gameplay/src/Label.cpp

@@ -7,7 +7,6 @@ namespace gameplay
 
     Label::Label() : _text("")
     {
-
     }
 
     Label::Label(const Label& copy)
@@ -60,25 +59,36 @@ namespace gameplay
         return _text.c_str();
     }
 
-    void Label::drawText(const Vector2& position)
+    void Label::update(const Vector2& position)
     {
-        // TODO: Batch all labels that use the same font.
         Vector2 pos(position.x + _position.x, position.y + _position.y);
         Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Theme::Border border = _style->getBorder();
+        
+        Theme::Border border;
+        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+        if (containerRegion)
+        {
+            border = overlay->getContainerRegion()->getBorder();
+        }
         Theme::Padding padding = _style->getPadding();
 
         // Set up the text viewport.
         Font* font = overlay->getFont();
-        Rectangle viewport(pos.x + border.left + padding.left,
-                           pos.y + border.top + padding.top,
-                           _size.x - border.left - padding.left - border.right - padding.right,
-                           _size.y - border.top - padding.top - border.bottom - padding.bottom - font->getSize());
+        _viewport.set(pos.x + border.left + padding.left,
+                      pos.y + border.top + padding.top,
+                      _size.x - border.left - padding.left - border.right - padding.right,
+                      _size.y - border.top - padding.top - border.bottom - padding.bottom - font->getSize());
+    }
+
+    void Label::drawText(const Vector2& position)
+    {
+        // TODO: Batch all labels that use the same font.
+        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+        Font* font = overlay->getFont();
 
         // Draw the text.
-        
         font->begin();
-        font->drawText(_text.c_str(), viewport, overlay->getTextColor(), overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+        font->drawText(_text.c_str(), _viewport, overlay->getTextColor(), overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
         font->end();
 
         _dirty = false;

+ 3 - 0
gameplay/src/Label.h

@@ -16,6 +16,8 @@ public:
     void setText(const char* text);
     const char* getText();
 
+    void update(const Vector2& position);
+
     void drawText(const Vector2& position);
 
 protected:
@@ -25,6 +27,7 @@ protected:
     virtual ~Label();
 
     std::string _text;
+    Rectangle _viewport;
 };
 
 }

+ 3 - 2
gameplay/src/Layout.h

@@ -2,11 +2,12 @@
 #define LAYOUT_H_
 
 #include "Ref.h"
-#include "Control.h"
 
 namespace gameplay
 {
 
+class Container;
+
 class Layout : public Ref
 {
 public:
@@ -19,7 +20,7 @@ public:
 
     virtual Type getType() = 0;
 
-    virtual void update(std::vector<Control*> controls, const Vector2& size, Theme::Style* containerStyle) = 0;
+    virtual void update(const Container* container) = 0;
 
 protected:
     //Layout();

+ 3 - 0
gameplay/src/Platform.h

@@ -2,6 +2,7 @@
 #define PLATFORM_H_
 
 #include "Touch.h"
+#include "Keyboard.h"
 
 namespace gameplay
 {
@@ -99,6 +100,8 @@ public:
 
     static void touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    static void keyEventInternal(Keyboard::KeyEvent evt, int key);
+
 private:
 
     /**

+ 6 - 0
gameplay/src/PlatformQNX.cpp

@@ -886,6 +886,12 @@ void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned
     Form::touchEventInternal(evt, x, y, contactIndex);
 }
 
+void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Form::keyEventInternal(evt, key);
+}
+
 }
 
 #endif

+ 16 - 4
gameplay/src/PlatformWin32.cpp

@@ -45,6 +45,8 @@ static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
         return gameplay::Keyboard::KEY_RETURN;
     case VK_CAPITAL:
         return gameplay::Keyboard::KEY_CAPS_LOCK;
+    case VK_SHIFT:
+        return gameplay::Keyboard::KEY_SHIFT;
     case VK_LSHIFT:
         return gameplay::Keyboard::KEY_LEFT_SHIFT;
     case VK_RSHIFT:
@@ -250,6 +252,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     static int lx, ly;
 
     static bool shiftDown = false;
+    static bool capsOn = false;
 
     switch (msg)
     {
@@ -327,17 +330,20 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         break;
 
     case WM_KEYDOWN:
-        if (wParam == VK_LSHIFT || wParam == VK_RSHIFT)
+        if (wParam == VK_SHIFT || wParam == VK_LSHIFT || wParam == VK_RSHIFT)
             shiftDown = true;
 
-        gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown));
+        if (wParam == VK_CAPITAL)
+            capsOn = !capsOn;
+
+        gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown ^ capsOn));
         break;
 
     case WM_KEYUP:
-        if (wParam == VK_LSHIFT || wParam == VK_RSHIFT)
+        if (wParam == VK_SHIFT || wParam == VK_LSHIFT || wParam == VK_RSHIFT)
             shiftDown = false;
 
-        gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_RELEASE, getKey(wParam, shiftDown));
+        gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_RELEASE, getKey(wParam, shiftDown ^ capsOn));
         break;
 
     case WM_SETFOCUS:
@@ -592,6 +598,12 @@ void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned
     Form::touchEventInternal(evt, x, y, contactIndex);
 }
 
+void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Form::keyEventInternal(evt, key);
+}
+
 }
 
 #endif

+ 28 - 15
gameplay/src/RadioButton.cpp

@@ -6,7 +6,7 @@ namespace gameplay
 
 static std::vector<RadioButton*> __radioButtons;
 
-RadioButton::RadioButton() : _groupId(NULL), _selected(false)
+RadioButton::RadioButton() : _selected(false)
 {
 }
 
@@ -62,6 +62,7 @@ void RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int c
     case Touch::TOUCH_PRESS:
         {
             _state = Control::STATE_ACTIVE;
+            _dirty = true;
         }
         break;
     case Touch::TOUCH_RELEASE:
@@ -79,19 +80,20 @@ void RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int c
                     _selected = true;
                 }
                 setState(Control::STATE_NORMAL);
+
             }
         }
         break;
     }
 }
 
-void RadioButton::clearSelected(const char* groupId)
+void RadioButton::clearSelected(const std::string& groupId)
 {
     std::vector<RadioButton*>::const_iterator it;
     for (it = __radioButtons.begin(); it < __radioButtons.end(); it++)
     {
         RadioButton* radioButton = *it;
-        if (strcmp(groupId, radioButton->_groupId) == 0)
+        if (groupId == radioButton->_groupId)
         {
             radioButton->_selected = false;
         }
@@ -106,23 +108,29 @@ void RadioButton::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
     Theme::Icon* icon = overlay->getRadioButtonIcon();
     if (icon)
     {
-        Theme::Border border = _style->getBorder();
-        Theme::Padding padding = _style->getPadding();
+        Theme::Border border;
+        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+        if (containerRegion)
+        {
+            border = containerRegion->getBorder();
+        }
+        const Theme::Padding padding = _style->getPadding();
+
+        const Vector2 size = icon->getSize();
+        const Vector4 color = icon->getColor();
 
         Vector2 pos(position.x + _position.x + border.left + padding.left,
-            position.y + _position.y + (_size.y - border.bottom - padding.bottom) / 2.0f - icon->size.y / 2.0f);
+            position.y + _position.y + (_size.y - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
-        if (_state == STATE_ACTIVE)
+        if (_selected)
         {
-            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->active.u1, icon->active.v1, icon->active.u2, icon->active.v2, overlay->getBorderColor());
-        }
-        else if (_selected)
-        {
-            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->on.u1, icon->on.v1, icon->on.u2, icon->on.v2, overlay->getBorderColor());
+            const Theme::UVs on = icon->getOnUVs();
+            spriteBatch->draw(pos.x, pos.y, size.x, size.y, on.u1, on.v1, on.u2, on.v2, color);
         }
         else
         {
-            spriteBatch->draw(pos.x, pos.y, icon->size.x, icon->size.y, icon->off.u1, icon->off.v1, icon->off.u2, icon->off.v2, overlay->getBorderColor());
+            const Theme::UVs off = icon->getOffUVs();
+            spriteBatch->draw(pos.x, pos.y, size.x, size.y, off.u1, off.v1, off.u2, off.v2, color);
         }
     }
 }
@@ -132,14 +140,19 @@ void RadioButton::drawText(const Vector2& position)
     // TODO: Batch all labels that use the same font.
     Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
     Theme::Icon* icon = overlay->getRadioButtonIcon();
-    Theme::Border border = _style->getBorder();
+    Theme::Border border;
+    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+    if (containerRegion)
+    {
+        border = containerRegion->getBorder();
+    }
     Theme::Padding padding = _style->getPadding();
 
     // Set up the text viewport.
     float iconWidth = 0.0f;
     if (icon)
     {
-        iconWidth = icon->size.x;
+        iconWidth = icon->getSize().x;
     }
 
     Font* font = overlay->getFont();

+ 2 - 2
gameplay/src/RadioButton.h

@@ -26,9 +26,9 @@ private:
     RadioButton(const RadioButton& copy);
 
     // Clear the _selected flag of all RadioButtons in the given group.
-    static void clearSelected(const char* groupId);
+    static void clearSelected(const std::string& groupId);
 
-    const char* _groupId;
+    std::string _groupId;
     bool _selected;
 };
 

+ 41 - 16
gameplay/src/Slider.cpp

@@ -63,14 +63,22 @@ void Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         y > 0 && y <= _size.y)
     {
         // Horizontal case.
-        Theme::Border border = _style->getBorder();
+        Theme::Border border;
+        Theme::ContainerRegion* containerRegion = _style->getOverlay(getOverlayType())->getContainerRegion();
+        if (containerRegion)
+        {
+            border = containerRegion->getBorder();
+        }
         Theme::Padding padding = _style->getPadding();
 
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Theme::SliderIcon* icon = overlay->getSliderIcon();
+        const Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+        const Theme::SliderIcon* icon = overlay->getSliderIcon();
 
-        float markerPosition = ((float)x - icon->rightCapSize.x - border.left - padding.left) /
-                               (_size.x - border.left - border.right - padding.left - padding.right - icon->leftCapSize.x - icon->rightCapSize.x);
+        const Vector2 minCapSize = icon->getMinCapSize();
+        const Vector2 maxCapSize = icon->getMaxCapSize();
+
+        float markerPosition = ((float)x - maxCapSize.x - border.left - padding.left) /
+                               (_size.x - border.left - border.right - padding.left - padding.right - minCapSize.x - maxCapSize.x);
         if (markerPosition > 1.0f)
         {
             markerPosition = 1.0f;
@@ -102,26 +110,43 @@ void Slider::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
     Theme::SliderIcon* icon = overlay->getSliderIcon();
     if (icon)
     {
-        Theme::Border border = _style->getBorder();
+        Theme::Border border;
+        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+        if (containerRegion)
+        {
+            border = containerRegion->getBorder();
+        }
         Theme::Padding padding = _style->getPadding();
 
+        const Vector2 minCapSize = icon->getMinCapSize();
+        const Vector2 maxCapSize = icon->getMaxCapSize();
+        const Vector2 markerSize = icon->getMarkerSize();
+        const Vector2 trackSize = icon->getTrackSize();
+
+        const Theme::UVs minCap = icon->getMinCapUVs();
+        const Theme::UVs maxCap = icon->getMaxCapUVs();
+        const Theme::UVs marker = icon->getMarkerUVs();
+        const Theme::UVs track = icon->getTrackUVs();
+
+        const Vector4 color = icon->getColor();
+
         // Draw order: track, caps, marker.
         float midY = position.y + _position.y + (_size.y - border.bottom - padding.bottom) / 2.0f;
-        Vector2 pos(position.x + _position.x + border.left + padding.left, midY - icon->trackSize.y / 2.0f);
-        spriteBatch->draw(pos.x, pos.y, _size.x, icon->trackSize.y, icon->track.u1, icon->track.v1, icon->track.u2, icon->track.v2, overlay->getBorderColor());
+        Vector2 pos(position.x + _position.x + border.left + padding.left, midY - trackSize.y / 2.0f);
+        spriteBatch->draw(pos.x, pos.y, _size.x, trackSize.y, track.u1, track.v1, track.u2, track.v2, color);
 
-        pos.y = midY - icon->leftCapSize.y / 2.0f;
-        spriteBatch->draw(pos.x, pos.y, icon->leftCapSize.x, icon->leftCapSize.y, icon->leftCap.u1, icon->leftCap.v1, icon->leftCap.u2, icon->leftCap.v2, overlay->getBorderColor());
+        pos.y = midY - minCapSize.y / 2.0f;
+        spriteBatch->draw(pos.x, pos.y, minCapSize.x, minCapSize.y, minCap.u1, minCap.v1, minCap.u2, minCap.v2, color);
         
-        pos.x = position.x + _position.x + _size.x - border.left - padding.left - icon->rightCapSize.x;
-        spriteBatch->draw(pos.x, pos.y, icon->rightCapSize.x, icon->rightCapSize.y, icon->rightCap.u1, icon->rightCap.v1, icon->rightCap.u2, icon->rightCap.v2, overlay->getBorderColor());
+        pos.x = position.x + _position.x + _size.x - border.left - padding.left - maxCapSize.x;
+        spriteBatch->draw(pos.x, pos.y, maxCapSize.x, maxCapSize.y, maxCap.u1, maxCap.v1, maxCap.u2, maxCap.v2, color);
 
         // Percent across.
         float markerPosition = _value / (_max - _min);
-        markerPosition *= _size.x - border.left - padding.left - border.right - padding.right - icon->leftCapSize.x - icon->rightCapSize.x - icon->markerSize.x;
-        pos.x = position.x + _position.x + border.left + padding.left + icon->leftCapSize.x + markerPosition;
-        pos.y = midY - icon->markerSize.y / 2.0f;
-        spriteBatch->draw(pos.x, pos.y, icon->markerSize.x, icon->markerSize.y, icon->marker.u1, icon->marker.v1, icon->marker.u2, icon->marker.v2, overlay->getBorderColor());
+        markerPosition *= _size.x - border.left - padding.left - border.right - padding.right - minCapSize.x - maxCapSize.x - markerSize.x;
+        pos.x = position.x + _position.x + border.left + padding.left +  minCapSize.x + markerPosition;
+        pos.y = midY - markerSize.y / 2.0f;
+        spriteBatch->draw(pos.x, pos.y, markerSize.x, markerSize.y, marker.u1, marker.v1, marker.u2, marker.v2, color);
     }
 }
 

+ 5 - 0
gameplay/src/SpriteBatch.cpp

@@ -100,6 +100,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
         else
         {
             effect = __spriteEffect;
+            __spriteEffect->addRef();
         }
     }
 
@@ -123,6 +124,10 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
 
     // Wrap the effect in a material
     Material* material = Material::create(effect); // +ref effect
+    if (effect == __spriteEffect)
+    {
+        effect->release();
+    }
 
     // Set initial material state
     material->getStateBlock()->setBlend(true);

+ 222 - 0
gameplay/src/TextBox.cpp

@@ -0,0 +1,222 @@
+#include "TextBox.h"
+
+namespace gameplay
+{
+
+static std::vector<TextBox*> __textBoxes;
+
+TextBox::TextBox()
+{
+}
+
+TextBox::TextBox(const TextBox& copy)
+{
+}
+
+TextBox::~TextBox()
+{
+}
+
+TextBox* TextBox::create(Theme::Style* style, Properties* properties)
+{
+    TextBox* textBox = new TextBox();
+    textBox->_style = style;
+    textBox->_id = properties->getId();
+    properties->getVector2("position", &textBox->_position);
+    properties->getVector2("size", &textBox->_size);
+    textBox->_text = properties->getString("text");
+
+    __textBoxes.push_back(textBox);
+    return textBox;
+}
+
+TextBox* TextBox::getTextBox(const char* id)
+{
+    std::vector<TextBox*>::const_iterator it;
+    for (it = __textBoxes.begin(); it < __textBoxes.end(); it++)
+    {
+        TextBox* t = *it;
+        if (strcmp(id, t->getID()) == 0)
+        {
+            return t;
+        }
+    }
+
+    return NULL;
+}
+
+void TextBox::setTouchLocation(int x, int y)
+{
+    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+    Theme::Border border;
+    if (containerRegion)
+    {
+        border = containerRegion->getBorder();
+    }
+    Theme::Padding padding = _style->getPadding();
+
+    _touchLocation.set(x - border.left - padding.left + _viewport.x,
+                       y - border.top - padding.top + _viewport.y);
+}
+
+void TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{   
+    if (_state != STATE_DISABLED)
+    {
+        switch (evt)
+        {
+        case Touch::TOUCH_PRESS: 
+            if (_state == STATE_NORMAL)
+            {
+                _state = STATE_ACTIVE;
+            }
+            break;
+        case Touch::TOUCH_MOVE:
+            if (_state == STATE_FOCUS &&
+                x > 0 && x <= _size.x &&
+                y > 0 && y <= _size.y)
+            {
+                setTouchLocation(x, y);
+                _dirty = true;
+            }
+            break;
+        case Touch::TOUCH_RELEASE:
+            if (x > 0 && x <= _size.x &&
+                y > 0 && y <= _size.y)
+            {
+                setTouchLocation(x, y);
+                _state = STATE_FOCUS;
+                _dirty = true;
+            }
+            else
+            {
+                _state = STATE_NORMAL;
+            }
+            break;
+        }
+    }
+}
+
+void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
+{
+    if (_state == STATE_FOCUS)
+    {
+        switch (evt)
+        {
+        case Keyboard::KEY_PRESS:
+            break;
+        case Keyboard::KEY_RELEASE:
+            Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+            Font* font = overlay->getFont();
+
+            // _text.insert / replace / remove depending on key.
+            switch (key)
+            {
+            case Keyboard::KEY_BACKSPACE:
+                break;
+            case Keyboard::KEY_DELETE:
+                break;
+            //case Keyboard::KEY_TAB:
+                // May be handled by default?
+                // May want to insert 4 spaces instead of \t.
+                // If this were the web we'd want to switch focus to the next Control...
+                //break;
+            case Keyboard::KEY_HOME:
+                // Move cursor to beginning of line.
+                break;
+            case Keyboard::KEY_END:
+                // Move cursor to end of line.
+                break;
+            case Keyboard::KEY_PG_DOWN:
+                break;
+            case Keyboard::KEY_PG_UP:
+                break;
+            default:
+                // Insert character into string.
+                _textIndex = font->getIndexAtLocation(_text.c_str(), _viewport, overlay->getFontSize(), _touchLocation, &_touchLocation,
+                    overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+
+                _text.insert(_textIndex, 1, (char)key);
+                
+                // Move cursor forward.
+                unsigned int w, h;
+                font->measureText((char*)&key, overlay->getFontSize(), &w, &h);
+                ++_textIndex;
+                _touchLocation.x += w;
+                //_cursorLocation.x += w;
+                
+                // Wrap cursor if necessary.
+                if (_touchLocation.x >= _viewport.x + _viewport.width)
+                {
+                    //_cursorLocation.x = w;
+                    _touchLocation.x = w;
+                    //_cursorLocation.y += h;
+                    _touchLocation.x += h;
+                }
+
+                _dirty = true;
+
+                break;
+            }
+            break;
+        }
+    }
+}
+
+void TextBox::update(const Vector2& position)
+{
+    Vector2 pos(position.x + _position.x, position.y + _position.y);
+    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+    Theme::Border border;
+    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+    if (containerRegion)
+    {
+        border = containerRegion->getBorder();
+    }
+    Theme::Padding padding = _style->getPadding();
+
+    // Set up the text viewport.
+    Font* font = overlay->getFont();
+    _viewport.set(pos.x + border.left + padding.left,
+                  pos.y + border.top + padding.top,
+                  _size.x - border.left - padding.left - border.right - padding.right,
+                  _size.y - border.top - padding.top - border.bottom - padding.bottom - font->getSize());
+
+    // Get index into string and cursor location from the last recorded touch location.
+    if (_state == STATE_FOCUS)
+    {
+        _textIndex = font->getIndexAtLocation(_text.c_str(), _viewport, overlay->getFontSize(), _touchLocation, &_touchLocation,
+            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+    }
+
+    _dirty = false;
+}
+
+void TextBox::drawSprites(SpriteBatch* spriteBatch, const Vector2& position)
+{
+    if (_state == STATE_FOCUS)
+    {
+        // Draw the cursor at its current location.
+        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
+        Theme::Cursor* cursor = overlay->getTextCursor();
+        if (cursor)
+        {
+            Theme::Border border;
+            Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
+            if (containerRegion)
+            {
+                border = containerRegion->getBorder();
+            }
+            const Theme::Padding padding = _style->getPadding();
+            const Vector2 size = cursor->getSize();
+            const Vector4 color = cursor->getColor();
+            const Theme::UVs uvs = cursor->getUVs();
+            unsigned int fontSize = overlay->getFontSize();
+
+            spriteBatch->draw(_touchLocation.x - (size.x / 2.0f), _touchLocation.y, size.x, fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
+        }
+    }
+}
+
+}

+ 41 - 0
gameplay/src/TextBox.h

@@ -0,0 +1,41 @@
+#ifndef TEXTBOX_H_
+#define TEXTBOX_H_
+
+#include "Base.h"
+#include "Label.h"
+#include "Theme.h"
+#include "Keyboard.h"
+
+namespace gameplay
+{
+
+class TextBox : public Label
+{
+public:
+    static TextBox* create(Theme::Style* style, Properties* properties);
+    static TextBox* getTextBox(const char* id);
+
+    void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+    void keyEvent(Keyboard::KeyEvent evt, int key);
+
+    void update(const Vector2& position);
+
+    // Draw the cursor.
+    void drawSprites(SpriteBatch* spriteBatch, const Vector2& position);
+
+private:
+    TextBox();
+    TextBox(const TextBox& copy);
+    ~TextBox();
+
+    void setTouchLocation(int x, int y);
+
+    Vector2 _touchLocation;
+    Vector2 _cursorLocation;
+    unsigned int _textIndex;
+};
+
+}
+
+#endif

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 430 - 290
gameplay/src/Theme.cpp


+ 151 - 116
gameplay/src/Theme.h

@@ -2,8 +2,8 @@
  * Theme.h
  */
 
-# ifndef THEME_H_
-# define THEME_H_
+#ifndef THEME_H_
+#define THEME_H_
 
 #include "Base.h"
 #include "Ref.h"
@@ -19,7 +19,7 @@ namespace gameplay
 #define MAX_OVERLAY_REGIONS 9
 
 /**
- * This class represents the apperance of an UI form described in a 
+ * This class represents the appearance of an UI form described in a 
  * theme file. A theme file, at its simplest, contains a source texture atlas,
  * the texture coordinates of each control in its different mode. Once loaded,
  * the appearance properties can be retrieved using a style id and set on a UI control.
@@ -44,82 +44,158 @@ public:
         float bottom;
         float left;
         float right;
+
+        padding() : top(0), bottom(0), left(0), right(0) {}
     } Margin, Border, Padding;
 
     class Icon : public Ref
     {
     public:
-        std::string id;
-        UVs off;
-        UVs on;
-        UVs active;
-        Vector2 size;
+        static Icon* create(const char* id, const Texture& texture, const Vector2& size,
+                            const Vector2& offPosition, const Vector2& onPosition, const Vector4& color);
+
+        const char* getId() const;
+        const Vector2& getSize() const;
+        const Vector4& getColor() const;
+        const Theme::UVs& getOffUVs() const;
+        const Theme::UVs& getOnUVs() const;
+
+    private:
+        Icon(const Texture& texture, const Vector2& size, const Vector2& offPosition, const Vector2& onPosition, const Vector4& color);
+        Icon(const Icon& copy);
+        ~Icon();
+
+        std::string _id;
+        Vector2 _size;
+        Vector4 _color;
+        UVs _off;
+        UVs _on;
     };
 
     class SliderIcon : public Ref
     {
     public:
-        std::string id;
-        UVs leftCap;
-        UVs rightCap;
-        UVs track;
-        UVs marker;
-        Vector2 leftCapSize;
-        Vector2 rightCapSize;
-        Vector2 trackSize;
-        Vector2 markerSize;
-    };
+        static SliderIcon* create(const char* id, const Texture& texture, const Vector4& minCapRegion, const Vector4& maxCapRegion,
+                                  const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color);
 
-    /**
-     * 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.
-     *
-     * @param name Name of the style (as specified in the Theme file).
-     *
-     * @return instance of the Style.
-     */
-    Theme::Style* getStyle(const char* styleName) const;
+        const char* getId() const;
+        const Theme::UVs& getMinCapUVs() const;
+        const Theme::UVs& getMaxCapUVs() const;
+        const Theme::UVs& getMarkerUVs() const;
+        const Theme::UVs& getTrackUVs() const;
+        const Vector2& getMinCapSize() const;
+        const Vector2& getMaxCapSize() const;
+        const Vector2& getMarkerSize() const;
+        const Vector2& getTrackSize() const;
+        const Vector4& getColor() const;
 
-    SpriteBatch* getSpriteBatch() const;
+    private:
+        SliderIcon(const Texture& texture, const Vector4& minCapRegion, const Vector4& maxCapRegion,
+                   const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color);
+        SliderIcon(const SliderIcon& copy);
+        ~SliderIcon();
 
+        std::string _id;
+        UVs _minCap;
+        UVs _maxCap;
+        UVs _marker;
+        UVs _track;
+        Vector2 _minCapSize;
+        Vector2 _maxCapSize;
+        Vector2 _markerSize;
+        Vector2 _trackSize;
+        Vector4 _color;
+    };
+    
     /**
-     * This class represents the apperance of a cursor.
+     * This class represents the appearance of a cursor.
      */
-    class Cursor
+    class Cursor : public Ref
     {
     public:
-        Cursor(const char* id, const Rectangle& region, const Vector4& color);
+        static Theme::Cursor* create(const char* id, const Texture& texture, const Rectangle& region, const Vector4& color);
 
        /**
         * Returns the Id of this Cursor.
         */
         const char* getId() const;
 
-       /**
-        * Gets a texture region in the texture atlas for a cursor.
-        */
-        const Rectangle& getRegion() const;
-
        /**
         * Gets a UV coordinates computed from the texture region.
         */
         const Theme::UVs& getUVs() const;
+
+        const Vector2& getSize() const;
+
+        const Vector4& getColor() const;
     
     private:
+        Cursor(const Texture& texture, const Rectangle& region, const Vector4& color);
+        Cursor(const Cursor& copy);
+        ~Cursor();
 
         std::string _id;
-        Rectangle _region;
         UVs _uvs;
+        Vector2 _size;
+        Vector4 _color;
+    };
+
+    class ContainerRegion : public Ref
+    {
+    public:
+        static ContainerRegion* create(const char* id, const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color);
+
+        enum ContainerArea
+        {
+            TOP_LEFT, TOP, TOP_RIGHT,
+            LEFT, CENTER, RIGHT,
+            BOTTOM_LEFT, BOTTOM, BOTTOM_RIGHT
+        };
+
+        const char* getId() const;
+        const Theme::Border& getBorder() const;
+        const Theme::UVs& getUVs(ContainerArea area) const;
+        const Vector4& getColor() const;
+        
+        /**
+         * Set the size of this ContainerRegion's border.
+         *
+         * When auto-size is set on width and/or height:
+         * Space added to the calculated (tightly bound) width and height.
+         *
+         */
+        //void setBorder(float top, float bottom, float left, float right);
+
+    private:
+        ContainerRegion(const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color);
+        ContainerRegion(const ContainerRegion& copy);
+        ~ContainerRegion();
+    
+        std::string _id;
+        Theme::Border _border;
+        UVs _uvs[MAX_OVERLAY_REGIONS];
         Vector4 _color;
     };
+
+    /**
+     * 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.
+     *
+     * @param id ID of the style (as specified in the Theme file).
+     *
+     * @return Instance of the Style.
+     */
+    Theme::Style* getStyle(const char* id) const;
+
+    SpriteBatch* getSpriteBatch() const;
     
     /**
      * This class represents the apperance of a control's style.
@@ -136,7 +212,7 @@ public:
             OVERLAY_ACTIVE
         };
 
-        Style(const char* id, const Theme::Margin& margin, const Theme::Border& border, const Theme::Padding& padding,
+        Style(const char* id, const Theme::Margin& margin, const Theme::Padding& padding,
             Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active);
 
         ~Style();
@@ -151,11 +227,6 @@ public:
          */
         Theme::Style::Overlay* getOverlay(OverlayType overlayType) const;
 
-        /**
-         * Gets the Border region of this style.
-         */
-        const Theme::Border& getBorder() const;
-
         /**
          * Gets the Padding region of this style.
          */
@@ -166,15 +237,6 @@ public:
          */
         const Theme::Margin& getMargin() const;
 
-        /**
-         * Set the size of this Style's border.
-         *
-         * When auto-size is set on width and/or height:
-         * Space added to the calculated (tightly bound) width and height.
-         *
-         */
-        void setBorder(float top, float bottom, float left, float right);
-
         /**
          * Set this size of this Style's padding.
          *
@@ -195,39 +257,12 @@ public:
         class Overlay : public Ref
         {
         public:
-            Overlay();
-            Overlay(OverlayType type);
-
-            enum OverlayArea
-            {
-                TOP_LEFT, TOP, TOP_RIGHT,
-                LEFT, CENTER, RIGHT,
-                BOTTOM_LEFT, BOTTOM, BOTTOM_RIGHT
-            };
-
-           /**
-            * Destructor.
-            */
-            ~Overlay();
+            static Overlay* create();
 
            /**
             * Returns the Overlay type.
             */
             OverlayType getType();
-            
-            /**
-            * Gets a texture region in the texture atlas that the overlay uses.
-            */
-            const Rectangle& getRegion() const;
-
-            void setRegion(const Rectangle& region);
-
-            // Calculates and sets UV coordinates based on the current region and given border and texture size.
-            void calculateUVs(const Theme::Border& border, unsigned int textureWidth, unsigned int textureHeight);
-
-            const Theme::UVs& getUVs(OverlayArea area);
-
-            void setUVs(OverlayArea area, const Theme::UVs& uvs);
 
            /**
             * Gets a font associated with this overlay.
@@ -248,40 +283,38 @@ public:
             bool getTextRightToLeft() const;
             void setTextRightToLeft(bool rightToLeft);
 
+            const Vector4& getTextColor() const;
+            void setTextColor(const Vector4& color);
+            
            /**
             * Gets a cursor associated with this overlay.
             */
-            Cursor* getCursor() const;
-
-            void setCursor(Cursor* cursor);
-
-            const Vector4& getTextColor() const;
-
-            void setTextColor(const Vector4& color);
-
-            const Vector4& getBorderColor() const;
-
-            void setBorderColor(const Vector4& color);
+            void setTextCursor(Cursor* cursor);
+            Cursor* getTextCursor() const;
+            
+            void setMouseCursor(Cursor* cursor);
+            Cursor* getMouseCursor() const;
             
             void setCheckBoxIcon(Icon* icon);
-
-            Icon* getCheckBoxIcon();
+            Icon* getCheckBoxIcon() const;
 
             void setRadioButtonIcon(Icon* icon);
-
-            Icon* getRadioButtonIcon();
+            Icon* getRadioButtonIcon() const;
 
             void setSliderIcon(SliderIcon* slider);
-
-            SliderIcon* getSliderIcon();
-
-            void addRef();
+            SliderIcon* getSliderIcon() const;
+            
+            void setContainerRegion(ContainerRegion* container);
+            ContainerRegion* getContainerRegion() const;
         
         private:
-            OverlayType _type;
-            Rectangle _region;
-            UVs _uvs[MAX_OVERLAY_REGIONS];
-            Cursor* _cursor;
+            Overlay();
+            Overlay(const Overlay& copy);
+            ~Overlay();
+
+            ContainerRegion* _container;
+            Cursor* _textCursor;
+            Cursor* _mouseCursor;
             Icon* _checkBoxIcon;
             Icon* _radioButtonIcon;
             SliderIcon* _sliderIcon;
@@ -290,10 +323,10 @@ public:
             Font::Justify _alignment;
             bool _textRightToLeft;
             Vector4 _textColor;
-            Vector4 _borderColor;
         };
 
     private:
+        Style(const Style& style);
         
         std::string _id;
         Border _border;
@@ -318,16 +351,18 @@ private:
      */
     ~Theme();
 
-    static void generateUVs(float x, float y, float width, float height, float textureWidth, float textureHeight, UVs* uvs);
+    static void generateUVs(const Texture& texture, float x, float y, float width, float height, UVs* uvs);
+    void lookUpSprites(const Properties* overlaySpace, Icon** checkBoxIcon, Icon** radioButtonIcon, SliderIcon** sliderIcon,
+                              Cursor** textCursor, Cursor** mouseCursor, ContainerRegion** containerRegion);
 
     std::string _path;
     Texture* _texture;
     SpriteBatch* _spriteBatch;
-    std::vector<Cursor *> _cursors;
-    std::vector<Style *> _styles;
-    std::vector<Font *> _fonts;
+    std::vector<Cursor*> _cursors;
+    std::vector<Style*> _styles;
     std::vector<Icon*> _icons;
     std::vector<SliderIcon*> _sliders;
+    std::vector<ContainerRegion*> _containers;
 };
 
 }

+ 16 - 3
gameplay/src/VerticalLayout.cpp

@@ -31,10 +31,22 @@ namespace gameplay
         return Layout::LAYOUT_VERTICAL;
     }
 
-    void VerticalLayout::update(std::vector<Control*> controls, const Vector2& size, Theme::Style* containerStyle)
+    void VerticalLayout::update(const Container* container)
     {
-        float yPosition = containerStyle->getBorder().top + containerStyle->getPadding().top;
-        float xPosition = containerStyle->getBorder().left + containerStyle->getPadding().left;
+        // Need border, padding.
+        Theme::Style* style = container->getStyle();
+        Theme::Border border;
+        Theme::ContainerRegion* containerRegion = style->getOverlay(container->getOverlayType())->getContainerRegion();
+        if (containerRegion)
+        {
+            border = containerRegion->getBorder();
+        }
+        Theme::Padding padding = style->getPadding();
+
+        float yPosition = border.top + padding.top;
+        float xPosition = border.left + padding.left;
+
+        std::vector<Control*> controls = container->getControls();
 
         unsigned int i, end, iter;
         if (_bottomToTop)
@@ -59,6 +71,7 @@ namespace gameplay
             yPosition += margin.top;
 
             control->setPosition(xPosition, yPosition);
+            control->update(container->getPosition());
 
             yPosition += size.y + margin.bottom;
 

+ 2 - 1
gameplay/src/VerticalLayout.h

@@ -2,6 +2,7 @@
 #define VERTICALLAYOUT_H_
 
 #include "Layout.h"
+#include "Container.h"
 
 namespace gameplay
 {
@@ -15,7 +16,7 @@ public:
 
     Layout::Type getType();
 
-    void update(std::vector<Control*> controls, const Vector2& size, Theme::Style* containerStyle);
+    void update(const Container* container);
 
 private:
     VerticalLayout();

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio