Browse Source

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

Next ablake
Sean Paul Taylor 13 years ago
parent
commit
62e50a081a

+ 16 - 4
gameplay/src/Control.cpp

@@ -64,7 +64,7 @@ void Control::initialize(Theme::Style* style, Properties* properties)
         size.x = properties->getFloat("width");
         size.y = properties->getFloat("height");
     }
-    _bounds.set(position.x, position.y, size.x, size.y);
+    setBounds(Rectangle(position.x, position.y, size.x, size.y));
 
     _state = Control::getState(properties->getString("state"));
 
@@ -139,7 +139,11 @@ void Control::setSize(float width, float height)
 
 void Control::setBounds(const Rectangle& bounds)
 {
-    _bounds.set(bounds);
+    if (bounds != _bounds)
+    {
+        _bounds.set(bounds);
+        _dirty = true;
+    }
 }
 
 const Rectangle& Control::getBounds() const
@@ -179,7 +183,11 @@ Control::Alignment Control::getAlignment() const
 
 void Control::setAutoWidth(bool autoWidth)
 {
-    _autoWidth = autoWidth;
+    if (_autoWidth != autoWidth)
+    {
+        _autoWidth = autoWidth;
+        _dirty = true;
+    }
 }
 
 bool Control::getAutoWidth() const
@@ -189,7 +197,11 @@ bool Control::getAutoWidth() const
 
 void Control::setAutoHeight(bool autoHeight)
 {
-    _autoHeight = autoHeight;
+    if (_autoHeight != autoHeight)
+    {
+        _autoHeight = autoHeight;
+        _dirty = true;
+    }
 }
 
 void Control::setOpacity(float opacity, unsigned char states)

+ 4 - 4
gameplay/src/Control.h

@@ -193,7 +193,7 @@ public:
      * @param width The width.
      * @param height The height.
      */
-    void setSize(float width, float height);
+    virtual void setSize(float width, float height);
 
     /**
      * Set the bounds of this control, relative to its parent container and including its
@@ -201,7 +201,7 @@ public:
      *
      * @param bounds The new bounds to set.
      */
-    void setBounds(const Rectangle& bounds);
+    virtual void setBounds(const Rectangle& bounds);
 
     /**
      * Get the bounds of this control, relative to its parent container and including its
@@ -258,7 +258,7 @@ public:
      *
      * @param autoWidth Whether to size this control to fit horizontally within its parent container.
      */
-    void setAutoWidth(bool autoWidth);
+    virtual void setAutoWidth(bool autoWidth);
 
     /**
      * Get whether this control's width is set to automatically adjust to
@@ -273,7 +273,7 @@ public:
      *
      * @param autoHeight Whether to size this control to fit vertically within its parent container.
      */
-    void setAutoHeight(bool autoHeight);
+    virtual void setAutoHeight(bool autoHeight);
 
     /**
      * Get whether this control's height is set to automatically adjust to

+ 300 - 400
gameplay/src/Font.cpp

@@ -173,310 +173,365 @@ void Font::begin()
 {
     _batch->begin();
 }
-/*
-Font::TextBatch* Font::getTextBatch(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify,
-    bool wrap, bool rightToLeft, const Rectangle* clip)
-{
 
-}
-*/
-void Font::drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft)
+Font::Text* Font::createText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify,
+    bool wrap, bool rightToLeft, const Rectangle* clip)
 {
     if (size == 0)
         size = _size;
     float scale = (float)size / _size;
-    const char* cursor = NULL;
+    const int length = strlen(text);
+    int yPos = area.y;
+    const float areaHeight = area.height - size;
+    std::vector<int> xPositions;
+    std::vector<unsigned int> lineLengths;
 
-    if (rightToLeft)
+    getMeasurementInfo(text, area, size, justify, wrap, rightToLeft, &xPositions, &yPos, &lineLengths);
+
+    Text* batch = new Text(text);
+
+    int xPos = area.x;
+    std::vector<int>::const_iterator xPositionsIt = xPositions.begin();
+    if (xPositionsIt != xPositions.end())
     {
-        cursor = text;
+        xPos = *xPositionsIt++;
     }
 
-    int xPos = x, yPos = y;
-    bool done = false;
+    const char* 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 (!done)
+    while (token[0] != 0)
     {
-        int length;
-        int startIndex;
-        int iteration;
-        if (rightToLeft)
+        // Handle delimiters until next token.
+        if (!handleDelimiters(&token, size, iteration, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
         {
-            char delimiter = cursor[0];
-            while (!done &&
-                   (delimiter == ' ' ||
-                   delimiter == '\t' ||
-                   delimiter == '\r' ||
-                   delimiter == '\n' ||
-                   delimiter == 0))
-            {
-                switch (delimiter)
-                {
-                case ' ':
-                    xPos += size >> 1;
-                    break;
-                case '\r':
-                case '\n':
-                    yPos += size;
-                    xPos = x;
-                    break;
-                case '\t':
-                    xPos += (size >> 1)*4;
-                    break;
-                case 0:
-                    done = true;
-                    break;
-                }
-
-                if (!done)
-                {
-                    ++cursor;
-                    delimiter = cursor[0];
-                }
-            }
+            break;
+        }
 
-            length = strcspn(cursor, "\r\n");
-            startIndex = length - 1;
+        bool truncated = false;
+        unsigned int tokenLength;
+        unsigned int tokenWidth;
+        unsigned int startIndex;
+        if (rightToLeft)
+        {
+            tokenLength = getReversedTokenLength(token, text);
+            currentLineLength += tokenLength;
+            token -= (tokenLength - 1);
+            tokenWidth = getTokenWidth(token, tokenLength, size, scale);
             iteration = -1;
+            startIndex = tokenLength - 1;
         }
         else
         {
-            length = strlen(text);
-            startIndex = 0;
+            tokenLength = strcspn(token, " \r\n\t");
+            tokenWidth = getTokenWidth(token, tokenLength, size, scale);
             iteration = 1;
+            startIndex = 0;
         }
 
-        for (int i = startIndex; i < length && i >= 0; i += iteration)
+        // Wrap if necessary.
+        if (wrap &&
+            xPos + (int)tokenWidth > area.x + area.width ||
+            (rightToLeft && currentLineLength > lineLength))
         {
-            char c = 0;
-            if (rightToLeft)
+            yPos += (int)size;
+            currentLineLength = tokenLength;
+
+            if (xPositionsIt != xPositions.end())
             {
-                c = cursor[i];
+                xPos = *xPositionsIt++;
             }
             else
             {
-                c = text[i];
-            }
-
-            // Draw this character.
-            switch (c)
-            {
-            case ' ':
-                xPos += size >> 1;
-                break;
-            case '\r':
-            case '\n':
-                yPos += size;
-                xPos = x;
-                break;
-            case '\t':
-                xPos += (size >> 1)*4;
-                break;
-            default:
-                int index = c - 32; // HACK for ASCII
-                if (index >= 0 && index < (int)_glyphCount)
-                {
-                    Glyph& g = _glyphs[index];
-                    _batch->draw(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
-                    xPos += floor(g.width * scale + (float)(size >> 3));
-                    break;
-                }
+                xPos = area.x;
             }
         }
 
-        if (rightToLeft)
+        bool draw = true;
+        if (yPos < area.y)
         {
-            cursor += length;   
+            // Skip drawing until line break or wrap.
+            draw = false;
         }
-        else
+        else if (yPos > area.y + areaHeight)
         {
-            done = true;
+            // Truncate below area's vertical limit.
+            break;
         }
-    }
-}
-
-void Font::drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify, bool wrap, bool rightToLeft, const Rectangle* clip)
-{
-    if (size == 0)
-        size = _size;
-    float scale = (float)size / _size;
-    const char* token = text;
-    const int length = strlen(text);
-    int yPos = area.y;
-    const float areaHeight = area.height - size;
-
-    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;
-    }
-
-    // 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)
+        for (int i = startIndex; i < (int)tokenLength && i >= 0; i += iteration)
         {
-            // Go a word at a time.
-            bool reachedEOF = false;
-            unsigned int lineLength = 0;
-            while (token[0] != 0)
+            char c = token[i];
+            int glyphIndex = c - 32; // HACK for ASCII
+        
+            if (glyphIndex >= 0 && glyphIndex < (int)_glyphCount)
             {
-                unsigned int tokenWidth = 0;
+                Glyph& g = _glyphs[glyphIndex];
 
-                // Handle delimiters until next token.
-                char delimiter = token[0];
-                while (delimiter == ' ' ||
-                       delimiter == '\t' ||
-                       delimiter == '\r' ||
-                       delimiter == '\n' ||
-                       delimiter == 0)
+                if (xPos + (int)(g.width*scale) > area.x + area.width)
                 {
-                    switch (delimiter)
+                    // Truncate this line and go on to the next one.
+                    truncated = true;
+                    break;
+                }
+                else if (xPos >= area.x)
+                {
+                    // Draw this character.
+                    if (draw)
                     {
-                        case ' ':
-                            delimWidth += size >> 1;
-                            lineLength++;
-                            break;
-                        case '\r':
-                        case '\n':
-                            yPos += size;
+                        if (clip)
+                        {
+                            _batch->addSprite(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color, *clip, &batch->_vertices[batch->_vertexCount]);
+                        }
+                        else
+                        {
+                            _batch->addSprite(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color, &batch->_vertices[batch->_vertexCount]);
+                        }
 
-                            if (lineWidth > 0)
+                        if (batch->_vertexCount == 0)
+                        {
+                            // Simply copy values directly into the start of the index array
+                            //memcpy(_indicesPtr, indices, indexCount * sizeof(unsigned short));
+                            batch->_indices[batch->_vertexCount] = batch->_vertexCount++;
+                            batch->_indices[batch->_vertexCount] = batch->_vertexCount++;
+                            batch->_indices[batch->_vertexCount] = batch->_vertexCount++;
+                            batch->_indices[batch->_vertexCount] = batch->_vertexCount++;
+                            batch->_indexCount += 4;
+                        }
+                        else
+                        {
+                            // Create a degenerate triangle to connect separate triangle strips
+                            // by duplicating the previous and next vertices.
+                            batch->_indices[batch->_indexCount] = batch->_indices[batch->_indexCount - 1];
+                            batch->_indices[batch->_indexCount + 1] = batch->_vertexCount;
+            
+                            // Loop through all indices and insert them, their their value offset by
+                            // 'vertexCount' so that they are relative to the first newly insertted vertex
+                            for (unsigned int i = 0; i < 4; ++i)
                             {
-                                addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+                                batch->_indices[batch->_indexCount + 2 + i] = i + batch->_vertexCount;
                             }
 
-                            lineWidth = 0;
-                            lineLength = 0;
-                            delimWidth = 0;
-                            break;
-                        case '\t':
-                            delimWidth += (size >> 1)*4;
-                            lineLength++;
-                            break;
-                        case 0:
-                            reachedEOF = true;
-                            break;
-                    }
+                            batch->_indexCount += 6;
+                            batch->_vertexCount += 4;
+                        }
 
-                    if (reachedEOF)
-                    {
-                        break;
                     }
-
-                    token++;
-                    delimiter = token[0];
                 }
+                xPos += (int)(g.width)*scale + (size >> 3);
+            }
+        }
 
-                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)
+        if (!truncated)
+        {
+            if (rightToLeft)
+            {
+                if (token == lineStart)
                 {
-                    yPos += (int)size;
-
-                    // Push position of current line.
-                    if (lineLength)
+                    token += lineLength;
+                    
+                    // Now handle delimiters going forwards.
+                    if (!handleDelimiters(&token, size, 1, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
                     {
-                        addLineInfo(area, lineWidth, lineLength-1, hAlign, &xPositions, &lineLengths, rightToLeft);
+                        break;
                     }
-                    else
+
+                    if (lineLengthsIt != lineLengths.end())
                     {
-                        addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+                        lineLength = *lineLengthsIt++;
                     }
-
-                    // Move token to the next line.
-                    lineWidth = 0;
-                    lineLength = 0;
-                    delimWidth = 0;
+                    lineStart = token;
+                    token += lineLength-1;
                 }
                 else
                 {
-                    lineWidth += delimWidth;
-                    delimWidth = 0;
+                    token--;
                 }
-
-                lineWidth += tokenWidth;
-                lineLength += tokenLength;
-                token += tokenLength;
             }
-
-            // Final calculation of vertical position.
-            int textHeight = yPos - area.y;
-            int vWhiteSpace = areaHeight - textHeight;
-            if (vAlign == ALIGN_VCENTER)
+            else
             {
-                yPos = area.y + vWhiteSpace / 2;
+                token += tokenLength;
             }
-            else if (vAlign == ALIGN_BOTTOM)
+        }
+        else
+        {
+            if (rightToLeft)
             {
-                yPos = area.y + vWhiteSpace;
+                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");
 
-            // Calculation of final horizontal position.
-            addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+                if (tokenLength > 0)
+                {                
+                    // Get first token of next line.
+                    token += tokenLength;
+                }
+            }
         }
-        else
+    }
+
+    return batch;
+}
+
+void Font::drawText(Text* textBatch)
+{
+    _batch->draw(textBatch->_vertices, textBatch->_vertexCount, textBatch->_indices, textBatch->_indexCount);
+}
+
+void Font::drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft)
+{
+    if (size == 0)
+        size = _size;
+    float scale = (float)size / _size;
+    const char* cursor = NULL;
+
+    if (rightToLeft)
+    {
+        cursor = text;
+    }
+
+    int xPos = x, yPos = y;
+    bool done = false;
+
+    while (!done)
+    {
+        int length;
+        int startIndex;
+        int iteration;
+        if (rightToLeft)
         {
-            // Go a line at a time.
-            while (token[0] != 0)
+            char delimiter = cursor[0];
+            while (!done &&
+                   (delimiter == ' ' ||
+                   delimiter == '\t' ||
+                   delimiter == '\r' ||
+                   delimiter == '\n' ||
+                   delimiter == 0))
             {
-                char delimiter = token[0];
-                while (delimiter == '\n')
+                switch (delimiter)
                 {
-                    yPos += (int)size;
-                    ++token;
-                    delimiter = token[0];
+                case ' ':
+                    xPos += size >> 1;
+                    break;
+                case '\r':
+                case '\n':
+                    yPos += size;
+                    xPos = x;
+                    break;
+                case '\t':
+                    xPos += (size >> 1)*4;
+                    break;
+                case 0:
+                    done = true;
+                    break;
                 }
 
-                unsigned int tokenLength = strcspn(token, "\n");
-                if (tokenLength == 0)
+                if (!done)
                 {
-                    tokenLength = strlen(token);
+                    ++cursor;
+                    delimiter = cursor[0];
                 }
+            }
 
-                int lineWidth = getTokenWidth(token, tokenLength, size, scale);
-                addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+            length = strcspn(cursor, "\r\n");
+            startIndex = length - 1;
+            iteration = -1;
+        }
+        else
+        {
+            length = strlen(text);
+            startIndex = 0;
+            iteration = 1;
+        }
 
-                token += tokenLength;
+        for (int i = startIndex; i < length && i >= 0; i += iteration)
+        {
+            char c = 0;
+            if (rightToLeft)
+            {
+                c = cursor[i];
             }
-
-            int textHeight = yPos - area.y;
-            int vWhiteSpace = areaHeight - textHeight;
-            if (vAlign == ALIGN_VCENTER)
+            else
             {
-                yPos = area.y + vWhiteSpace / 2;
+                c = text[i];
             }
-            else if (vAlign == ALIGN_BOTTOM)
+
+            // Draw this character.
+            switch (c)
             {
-                yPos = area.y + vWhiteSpace;
+            case ' ':
+                xPos += size >> 1;
+                break;
+            case '\r':
+            case '\n':
+                yPos += size;
+                xPos = x;
+                break;
+            case '\t':
+                xPos += (size >> 1)*4;
+                break;
+            default:
+                int index = c - 32; // HACK for ASCII
+                if (index >= 0 && index < (int)_glyphCount)
+                {
+                    Glyph& g = _glyphs[index];
+                    _batch->draw(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
+                    xPos += floor(g.width * scale + (float)(size >> 3));
+                    break;
+                }
             }
         }
 
-        if (vAlign == ALIGN_TOP)
+        if (rightToLeft)
+        {
+            cursor += length;   
+        }
+        else
         {
-            yPos = area.y;
+            done = true;
         }
     }
+}
+
+void Font::drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify, bool wrap, bool rightToLeft, const Rectangle* clip)
+{
+    if (size == 0)
+        size = _size;
+    float scale = (float)size / _size;
+    const int length = strlen(text);
+    int yPos = area.y;
+    const float areaHeight = area.height - size;
+    std::vector<int> xPositions;
+    std::vector<unsigned int> lineLengths;
+
+    getMeasurementInfo(text, area, size, justify, wrap, rightToLeft, &xPositions, &yPos, &lineLengths);
 
     // Now we have the info we need in order to render.
     int xPos = area.x;
@@ -486,8 +541,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         xPos = *xPositionsIt++;
     }
 
-    token = text;
-    
+    const char* token = text;
     int iteration = 1;
     unsigned int lineLength;
     unsigned int currentLineLength = 0;
@@ -1213,187 +1267,14 @@ int Font::getIndexOrLocation(const char* text, const Rectangle& area, unsigned i
 
     // 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;
     const float areaHeight = area.height - size;
-
-    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 = areaHeight - 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 = areaHeight - 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;
-        }
-    }
+    getMeasurementInfo(text, area, size, justify, wrap, rightToLeft, &xPositions, &yPos, &lineLengths);
 
-    // 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())
@@ -1401,7 +1282,7 @@ int Font::getIndexOrLocation(const char* text, const Rectangle& area, unsigned i
         xPos = *xPositionsIt++;
     }
 
-    token = text;
+    const char* token = text;
     
     int iteration = 1;
     unsigned int lineLength;
@@ -1833,4 +1714,23 @@ Font::Justify Font::getJustify(const char* justify)
     return Font::ALIGN_TOP_LEFT;
 }
 
+Font::Text::Text(const char* text) : _vertexCount(0), _indexCount(0)
+{
+    _text = text;
+    const int length = strlen(text);
+    _vertices = new SpriteBatch::SpriteVertex[length * 4];
+    _indices = new unsigned short[((length - 1) * 6) + 4];
+}
+
+Font::Text::~Text()
+{
+    SAFE_DELETE_ARRAY(_vertices);
+    SAFE_DELETE_ARRAY(_indices);
+}
+
+const char* Font::Text::getText()
+{
+    return _text.c_str();
+}
+
 }

+ 47 - 5
gameplay/src/Font.h

@@ -76,11 +76,37 @@ public:
         float uvs[4];
     };
 
-    class TextBatch
+    /**
+     * Vertex coordinates, UVs and indices can be computed and stored in a Text object.
+     * For static text labels that do not change frequently, this means these computations
+     * need not be performed every frame.
+     */
+    class Text
     {
+        friend class Font;
+
     public:
+        /**
+         * Constructor.
+         */
+        Text(const char* text);
+
+        /**
+         * Destructor.
+         */
+        ~Text();
+
+        /**
+         * Get the string that will be drawn from this Text object.
+         */
+        const char* getText();
+
+    private:
+        std::string _text;
         SpriteBatch::SpriteVertex* _vertices;
+        unsigned short* _indices;
         unsigned int _vertexCount;
+        unsigned int _indexCount;
     };
 
     /**
@@ -133,15 +159,31 @@ public:
     void end();
 
     /**
-     * Compute vertex coordinates and UVs for a given string.
+     * Create a Text object from a given string.
+     * Vertex coordinates, UVs and indices will be computed and stored in the Text object.
+     * For static text labels that do not change frequently, this means these computations
+     * need not be performed every frame.
+     *
+     * @param text The text to draw.
+     * @param area The viewport area to draw within.  Text will be clipped outside this rectangle.
+     * @param color The color of text.
+     * @param size The size to draw text (0 for default size).
+     * @param justify Justification of text within the viewport.
+     * @param wrap Wraps text to fit within the width of the viewport if true.
+     * @param rightToLeft Whether to draw text from right to left.
+     * @param clip A region to clip text within after applying justification to the viewport area.
+     *
+     * @return A Text object.
      */
-    TextBatch* getTextBatch(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0,
+    Text* createText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0,
         Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
 
     /**
-     * Draw a string from a precomputed StringBatch.
+     * Draw a string from a precomputed Text object.
+     *
+     * @param text The text to draw.
      */
-    void drawTextBatch(TextBatch* textBatch);
+    void drawText(Text* text);
 
     /**
      * Draws the specified text in a solid color, with a scaling factor.

+ 92 - 36
gameplay/src/Form.cpp

@@ -93,45 +93,9 @@ Form* Form::create(const char* url)
     const char* styleName = formProperties->getString("style");
     form->initialize(theme->getStyle(styleName), formProperties);
 
-    if (form->_autoWidth)
-    {
-        form->_bounds.width = Game::getInstance()->getWidth();
-    }
-
-    if (form->_autoHeight)
-    {
-        form->_bounds.height = Game::getInstance()->getHeight();
-    }
-
     // Add all the controls to the form.
     form->addControls(theme, formProperties);
 
-        
-    // Width and height must be powers of two to create a texture.
-    int w = (int)form->_bounds.width;
-    int h = (int)form->_bounds.height;
-
-    if (!((w & (w - 1)) == 0))
-    {
-        w = nextHighestPowerOfTwo(w);
-    }
-
-    if (!((h & (h - 1)) == 0))
-    {
-        h = nextHighestPowerOfTwo(h);
-    }
-
-    form->_u2 = form->_bounds.width / (float)w;
-    form->_v1 = form->_bounds.height / (float)h;
-
-    // Create the frame buffer.
-    form->_frameBuffer = FrameBuffer::create(form->_id.c_str());
-    RenderTarget* rt = RenderTarget::create(form->_id.c_str(), w, h);
-    GP_ASSERT(rt);
-    form->_frameBuffer->setRenderTarget(rt);
-    SAFE_RELEASE(rt);
-
-    Matrix::createOrthographicOffCenter(0, form->_bounds.width, form->_bounds.height, 0, 0, 1, &form->_projectionMatrix);
     Game* game = Game::getInstance();
     Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
 
@@ -158,6 +122,98 @@ Form* Form::getForm(const char* id)
     return NULL;
 }
 
+void Form::setSize(float width, float height)
+{
+    if (_autoWidth)
+    {
+        width = Game::getInstance()->getWidth();
+    }
+
+    if (_autoHeight)
+    {
+        height = Game::getInstance()->getHeight();
+    }
+
+    if (width != _bounds.width || height != _bounds.height)
+    {
+        // Width and height must be powers of two to create a texture.
+        int w = width;
+        int h = height;
+
+        if (!((w & (w - 1)) == 0))
+        {
+            w = nextHighestPowerOfTwo(w);
+        }
+
+        if (!((h & (h - 1)) == 0))
+        {
+            h = nextHighestPowerOfTwo(h);
+        }
+
+        _u2 = width / (float)w;
+        _v1 = height / (float)h;
+
+        // Create framebuffer if necessary.
+        if (!_frameBuffer)
+        {
+            _frameBuffer = FrameBuffer::create(_id.c_str());
+            GP_ASSERT(_frameBuffer);
+        }
+     
+        // Re-create render target.
+        RenderTarget* rt = RenderTarget::create(_id.c_str(), w, h);
+        GP_ASSERT(rt);
+        _frameBuffer->setRenderTarget(rt);
+        SAFE_RELEASE(rt);
+
+        // Re-create projection matrix.
+        Matrix::createOrthographicOffCenter(0, width, height, 0, 0, 1, &_projectionMatrix);
+
+        // Re-create sprite batch.
+        SAFE_DELETE(_spriteBatch);
+        _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
+        GP_ASSERT(_spriteBatch);
+
+        _bounds.width = width;
+        _bounds.height = height;
+        _dirty = true;
+    }
+}
+
+void Form::setBounds(const Rectangle& bounds)
+{
+    setPosition(bounds.x, bounds.y);
+    setSize(bounds.width, bounds.height);
+}
+
+void Form::setAutoWidth(bool autoWidth)
+{
+    if (_autoWidth != autoWidth)
+    {
+        _autoWidth = autoWidth;
+        _dirty = true;
+
+        if (_autoWidth)
+        {
+            setSize(_bounds.width, Game::getInstance()->getWidth());
+        }
+    }
+}
+
+void Form::setAutoHeight(bool autoHeight)
+{
+    if (_autoHeight != autoHeight)
+    {
+        _autoHeight = autoHeight;
+        _dirty = true;
+
+        if (_autoHeight)
+        {
+            setSize(_bounds.width, Game::getInstance()->getHeight());
+        }
+    }
+}
+
 void Form::setQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4)
 {
     Mesh* mesh = Mesh::createQuad(p1, p2, p3, p4);

+ 29 - 0
gameplay/src/Form.h

@@ -67,6 +67,35 @@ public:
      */
     static Form* getForm(const char* id);
 
+    /**
+     * Set the desired size of this form.
+     *
+     * @param width The width.
+     * @param height The height.
+     */
+    virtual void setSize(float width, float height);
+
+    /**
+     * Set the bounds of this form.
+     *
+     * @param bounds The new bounds to set.
+     */
+    virtual void setBounds(const Rectangle& bounds);
+
+    /**
+     * Set this form's width to that of the display.
+     *
+     * @param autoWidth Whether to set this form's width to that of the display.
+     */
+    virtual void setAutoWidth(bool autoWidth);
+
+    /**
+     * Set this form's height to that of the display.
+     *
+     * @param autoHeight Whether to set this form's height to that of the display.
+     */
+    virtual void setAutoHeight(bool autoHeight);
+
     /**
      * Create a 3D quad to texture with this Form.
      *

+ 90 - 48
gameplay/src/SpriteBatch.cpp

@@ -345,61 +345,45 @@ void SpriteBatch::draw(float x, float y, float width, float height, float u1, fl
 
 void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip)
 {
-    // Clip the rectangle given by { x, y, width, height } into clip.
-    // We need to scale the uvs accordingly as we do this.
-
-    // First check to see if we need to draw at all.
-    if (x + width < clip.x || x > clip.x + clip.width ||
-        y + height < clip.y || y > clip.y + clip.height)
-    {
-        return;
-    }
+    // Only draw if at least part of the sprite is within the clip region.
+    if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
+        draw(x, y, 0, width, height, u1, v1, u2, v2, color);
+}
 
-    const float uvWidth = u2 - u1;
-    const float uvHeight = v2 - v1;
+void SpriteBatch::addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, SpriteBatch::SpriteVertex* vertices)
+{
+    GP_ASSERT(vertices);
 
-    // Moving x to the right.
-    if (x < clip.x)
-    {
-        const float percent = (clip.x - x) / width;
-        const float dx = clip.x - x;
-        x = clip.x;
-        width -= dx;
-        u1 += uvWidth * percent;
-    }
+    const float x2 = x + width;
+    const float y2 = y + height;
+    ADD_SPRITE_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
+}
 
-    // Moving y down.
-    if (y < clip.y)
-    {
-        const float percent = (clip.y - y) / height;
-        const float dy = clip.y - y;
-        y = clip.y;
-        height -= dy;
-        v1 += uvHeight * percent;
-    }
+void SpriteBatch::addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip, SpriteBatch::SpriteVertex* vertices)
+{
+    GP_ASSERT(vertices);
 
-    // Moving width to the left.
-    const float clipX2 = clip.x + clip.width;
-    float x2 = x + width;
-    if (x2 > clipX2)
+    // Only add a sprite if at least part of the sprite is within the clip region.
+    if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
     {
-        const float percent = (x2 - clipX2) / width;
-        width = clipX2 - x;
-        u2 -= uvWidth * percent;
+        const float x2 = x + width;
+        const float y2 = y + height;
+        ADD_SPRITE_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
+        ADD_SPRITE_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
+        ADD_SPRITE_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
+        ADD_SPRITE_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
     }
+}
 
-    // Moving height up.
-    const float clipY2 = clip.y + clip.height;
-    float y2 = y + height;
-    if (y2 > clipY2)
-    {
-        const float percent = (y2 - clipY2) / height;
-        height = clipY2 - y;
-        v2 -= uvHeight * percent;
-    }
+void SpriteBatch::draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
+{
+    GP_ASSERT(vertices);
+    GP_ASSERT(indices);
 
-    // Now we can perform a normal draw call.
-    draw(x, y, 0, width, height, u1, v1, u2, v2, color);
+    _batch->add(vertices, vertexCount, indices, indexCount);
 }
 
 void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, bool positionIsCenter)
@@ -437,7 +421,7 @@ RenderState::StateBlock* SpriteBatch::getStateBlock() const
     return _batch->getMaterial()->getStateBlock();
 }
 
-Material* SpriteBatch::getMaterial()
+Material* SpriteBatch::getMaterial() const
 {
     return _batch->getMaterial();
 }
@@ -456,4 +440,62 @@ const Matrix& SpriteBatch::getOrthoMatrix() const
     return _projectionMatrix;
 }
 
+bool SpriteBatch::clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2)
+{
+    // Clip the rectangle given by { x, y, width, height } into clip.
+    // We need to scale the uvs accordingly as we do this.
+
+    // First check to see if we need to draw at all.
+    if (x + width < clip.x || x > clip.x + clip.width ||
+        y + height < clip.y || y > clip.y + clip.height)
+    {
+        return false;
+    }
+
+    const float uvWidth = u2 - u1;
+    const float uvHeight = v2 - v1;
+
+    // Moving x to the right.
+    if (x < clip.x)
+    {
+        const float percent = (clip.x - x) / width;
+        const float dx = clip.x - x;
+        x = clip.x;
+        width -= dx;
+        u1 += uvWidth * percent;
+    }
+
+    // Moving y down.
+    if (y < clip.y)
+    {
+        const float percent = (clip.y - y) / height;
+        const float dy = clip.y - y;
+        y = clip.y;
+        height -= dy;
+        v1 += uvHeight * percent;
+    }
+
+    // Moving width to the left.
+    const float clipX2 = clip.x + clip.width;
+    float x2 = x + width;
+    if (x2 > clipX2)
+    {
+        const float percent = (x2 - clipX2) / width;
+        width = clipX2 - x;
+        u2 -= uvWidth * percent;
+    }
+
+    // Moving height up.
+    const float clipY2 = clip.y + clip.height;
+    float y2 = y + height;
+    if (y2 > clipY2)
+    {
+        const float percent = (y2 - clipY2) / height;
+        height = clipY2 - y;
+        v2 -= uvHeight * percent;
+    }
+
+    return true;
+}
+
 }

+ 50 - 1
gameplay/src/SpriteBatch.h

@@ -247,6 +247,48 @@ public:
      */
     void draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip);
 
+    /**
+     * Adds a single sprite to a SpriteVertex array.
+     * 
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param width The sprite width.
+     * @param height The sprite height
+     * @param u1 Texture coordinate.
+     * @param v1 Texture coordinate.
+     * @param u2 Texture coordinate.
+     * @param v2 Texture coordinate.
+     * @param color The color to tint the sprite. Use white for no tint.
+     * @param clip The clip rectangle.
+     */
+    void addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, SpriteBatch::SpriteVertex* vertices);
+
+    /**
+     * Adds a single sprite to a SpriteVertex array, clipped within a rectangle.
+     * 
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param width The sprite width.
+     * @param height The sprite height
+     * @param u1 Texture coordinate.
+     * @param v1 Texture coordinate.
+     * @param u2 Texture coordinate.
+     * @param v2 Texture coordinate.
+     * @param color The color to tint the sprite. Use white for no tint.
+     * @param clip The clip rectangle.
+     */
+    void addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip, SpriteBatch::SpriteVertex* vertices);
+
+    /**
+     * Draws an array of vertices.
+     *
+     * @param vertices The vertices to draw.
+     * @param vertexCount The number of vertices within the vertex array.
+     * @param indices The vertex indices.
+     * @param indexCount The number of indices within the index array.
+     */
+    void draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount);
+
     /**
      * Draws a single sprite.
      * 
@@ -288,7 +330,7 @@ public:
      * 
      * @return The material.
      */
-    Material* getMaterial();
+    Material* getMaterial() const;
 
     /**
      * Sets a custom projection matrix to use with the sprite batch.
@@ -318,6 +360,13 @@ private:
 
     const Matrix& getOrthoMatrix() const;
 
+    /**
+     * Clip position and size to fit within clip region.
+     *
+     * @return true if any part of sprite intersects with the clip region and therefore needs drawing, false otherwise.
+     */
+    bool clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2);
+
     MeshBatch* _batch;
     bool _customEffect;
     float _textureWidthRatio;