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]