Skip to main content

History: ColoredTextAreaOverlayElement

Source of version: 3 (current)

Copy to clipboard
            {maketoc}

!!Introduction
This article presents a class that extends the ''TextAreaOverlayElement'' allowing for different colors to be applied to each character in the string individually. The behaviour was designed to mimic the Quake 3 coloring scheme. This should be very easy to integrate into existing code since the interface is virtually identical to the ''TextAreaOverlayElement'' interface. For example, consider the following code:

{CODE(wrap="1", colors="c++")} TextAreaOverlayElement *pTextArea =
     (TextAreaOverlayElement*)OverlayManager.createOverlayElement("TextArea", "MyTextArea");
 pTextArea->setCaption("Some plain text");
 ...
{CODE}
Simply needs to be changed to:

{CODE(wrap="1", colors="c++")} TextAreaOverlayElement *pTextArea =
     (TextAreaOverlayElement*)OverlayManager.createOverlayElement("ColoredTextArea", "MyTextArea");
 pTextArea->setCaption("Some ^1red^7 text");
 ...
{CODE}
Note that ''"^1"'' and ''"^7"'' in the string are color codes. Those familiar with Quake 3 will recognise this. The color that corresponds to each code can be found in the ''ColoredTextAreaOverlayElement::GetColor'' function, they are the same as Quake 3 but can easily be changed.

Before this code will work however, the new overlay must be registered using the ''ColoredTextAreaOverlayElementFactory''. Here is some sample code on how to do that:

__In header file__
{CODE(wrap="1", colors="c++")} ColoredTextAreaOverlayElementFactory* pColoredTextAreaOverlayElementFactory;
{CODE}
__Initialisation code__
{CODE(wrap="1", colors="c++")} // Register the overlay element
 m_pColoredTextAreaOverlayElementFactory = new ColoredTextAreaOverlayElementFactory();
 OverlayManager::getSingleton().addOverlayElementFactory(pColoredTextAreaOverlayElementFactory);
{CODE}
__Deinitialisation code__
{CODE(wrap="1", colors="c++")} // This must be called before the ogre root is destroyed or crashes may occur on exit
 delete pColoredTextAreaOverlayElementFactory
{CODE}
Having done this you should also be able to use this class in your overlay scripts (although I haven't tested that).

Similarly to the ''TextAreaOverlayElement'' class, a gradient can be applied from the top to the bottom. Instead of specifying an absolute color, a value can be specified instead with the following functions:

{CODE(wrap="1", colors="c++")}void setValueBottom(float Value);
void setValueTop(float Value);{CODE}

This is essentially the value component of the HSV color.

Here is a screenshot of the class in use (see text at the bottom left):

{img src="img/wiki_up/ColoredTextOverlays.png" alt="ColoredTextOverlays.png"}

!!Important
Keep in mind that this is not thread safe! We had much trouble to figure out why it was broken: "e used ''updateColours()'' from a different thread, never do that!

!!Code
__ColoredTextAreaOverlayElement.h__

{CODE(wrap="1", colors="c++")} #pragma once
 
 class ColoredTextAreaOverlayElement : public TextAreaOverlayElement
 {
 public:
     ColoredTextAreaOverlayElement(const String& name);
     ~ColoredTextAreaOverlayElement(void);
 
     void setValueBottom(float Value);
     void setValueTop(float Value);
     void setCaption(const DisplayString& text);
     static DisplayString StripColors(const DisplayString& text);
     static ColourValue GetColor(unsigned char ID, float Value = 1.0f);
 
 protected:
     void updateColours(void);
 
     vector<unsigned char> m_Colors;
     float m_ValueTop;
     float m_ValueBottom;
 };
{CODE}
__ColoredTextAreaOverlayElement.cpp__
{CODE(wrap="1", colors="c++")} #include "ColoredTextAreaOverlayElement.h"
 
 #define POS_TEX_BINDING 0
 #define COLOUR_BINDING 1
 
 ColoredTextAreaOverlayElement::ColoredTextAreaOverlayElement(const String& name)
 : TextAreaOverlayElement(name)
 , m_ValueTop(1.0f)
 , m_ValueBottom(0.8f)
 {
 }
 
 ColoredTextAreaOverlayElement::~ColoredTextAreaOverlayElement(void)
 {
 }
 
 void ColoredTextAreaOverlayElement::setValueBottom(float Value)
 {
     m_ValueTop = Value;
     mColoursChanged = true;
 }
 
 void ColoredTextAreaOverlayElement::setValueTop(float Value)
 {
     m_ValueBottom = Value;
     mColoursChanged = true;
 }
 
 ColourValue ColoredTextAreaOverlayElement::GetColor(unsigned char ID, float Value)
 {
     switch (ID)
     {
     case 0:
         return ColourValue(0, 0, 0);    // Black
     case 1:
         return ColourValue(Value, 0, 0);    // Red
     case 2:
         return ColourValue(0, Value, 0);    // Green
     case 3:
         return ColourValue(Value, Value, 0);    // Yellow
     case 4:
         return ColourValue(0, 0, Value);    // Blue
     case 5:
         return ColourValue(0, Value, Value);    // Cyan
     case 6:
         return ColourValue(Value, 0, Value);    // Magenta
     case 7:
         return ColourValue(Value, Value, Value);    // White
     }
     return ColourValue::Black;
 }
 
 DisplayString ColoredTextAreaOverlayElement::StripColors(const DisplayString& text)
 {
     DisplayString StrippedText;
     int i;
     for (i = 0; i < (int)text.size()-1; ++i)
     {
         if (text[i] == '^' &&
             text[i+1] >= '0' && text[i+1] <= '7')    // This is a color code, ignore it
         {
             ++i;
         }
         else
         {
             StrippedText.append(1, text[i]);
         }
     }
     // One last character to add because loop went to size()-1
     if (i < (int)text.size())
         StrippedText.append(1, text[i]);
     return StrippedText;
 }
 
 void ColoredTextAreaOverlayElement::setCaption(const DisplayString& text)
 {
     m_Colors.resize(text.size(), 7);
     int i, iNumColorCodes = 0, iNumSpaces = 0;
     for (i = 0; i < (int)text.size()-1; ++i)
     {
         if (text[i] == ' ' || text[i] == '\n')
         {
             // Spaces and newlines are skipped when rendering and as such can't have a color
             ++iNumSpaces;
         }
         else if (text[i] == '^' &&
             text[i+1] >= '0' && text[i+1] <= '7')    // This is a color code
         {
             // Fill the color array starting from this point to the end with the new color code
             // adjustments need to made because color codes will be removed and spaces are not counted
             fill(m_Colors.begin()+i-(2*iNumColorCodes)-iNumSpaces, m_Colors.end(), text[i+1]-'0');
             ++i;
             ++iNumColorCodes;
         }
     }
     // Set the caption using the base class, but strip the color codes from it first
     TextAreaOverlayElement::setCaption(StripColors(text));
 }
 
 void ColoredTextAreaOverlayElement::updateColours(void)
 {
     // Convert to system-specific
     RGBA topColour, bottomColour;
     // Set default to white
     Root::getSingleton().convertColourValue(ColourValue::White, &topColour);
     Root::getSingleton().convertColourValue(ColourValue::White, &bottomColour);
 
     HardwareVertexBufferSharedPtr vbuf = 
         mRenderOp.vertexData->vertexBufferBinding->getBuffer(COLOUR_BINDING);
 
     RGBA* pDest = static_cast<RGBA*>(
         vbuf->lock(HardwareBuffer::HBL_DISCARD) );
 
     for (size_t i = 0; i < mAllocSize; ++i)
     {
         if (i < m_Colors.size())
         {
             Root::getSingleton().convertColourValue(GetColor(m_Colors[i], m_ValueTop), &topColour);
             Root::getSingleton().convertColourValue(GetColor(m_Colors[i], m_ValueBottom), &bottomColour);
         }
 
         // First tri (top, bottom, top)
         *pDest++ = topColour;
         *pDest++ = bottomColour;
         *pDest++ = topColour;
         // Second tri (top, bottom, bottom)
         *pDest++ = topColour;
         *pDest++ = bottomColour;
         *pDest++ = bottomColour;
     }
     vbuf->unlock();
 }{CODE}

__ColoredTextAreaOverlayElementFactory.h__
{CODE(wrap="1", colors="c++")} #pragma once
 
 #include <OgreOverlayElementFactory.h>
 #include "ColoredTextAreaOverlayElement.h"
 
 /** Factory for creating TextAreaOverlayElement instances. */
 class ColoredTextAreaOverlayElementFactory : public OverlayElementFactory
 {
 public:
     /** See OverlayElementFactory */
     OverlayElement* createOverlayElement(const String& instanceName)
     {
         return new ColoredTextAreaOverlayElement(instanceName);
     }
     /** See OverlayElementFactory */
     const String& getTypeName() const
     {
         static String name = "ColoredTextArea";
         return name;
     }
 };{CODE}

!!Updated version
A slightly updated version can be found there:
* [http://rigsofrods.svn.sourceforge.net/viewvc/rigsofrods/trunk/build/main/source/ColoredTextAreaOverlayElement.cpp]
* [http://rigsofrods.svn.sourceforge.net/viewvc/rigsofrods/trunk/build/main/source/ColoredTextAreaOverlayElement.h]
* [http://rigsofrods.svn.sourceforge.net/viewvc/rigsofrods/trunk/build/main/source/ColoredTextAreaOverlayElementFactory.h]