Fading Transparent GuiTextCtrl
by Rubes · 10/08/2007 (10:22 am) · 9 comments
Download Code File
During development, I found I had the need to use a GuiTextCtrl to display a simple text string, but I wanted the text to be able to fade in from transparency and back out again, on command. The only way I could figure out how to do this in script was to modify the alpha value of the GuiTextCtrl's profile, but this was staggeringly slow. An engine solution was needed.
Fortunately, someone had already done something very similar to this. Jeff Parry's excellent "Fading transparent bitmap control" resource does this with bitmap images, so the code was already essentially there. His resource did not provide script functions to trigger the fading in or fading out process, so I modified it to include this functionality.
I thought originally to create a new GuiControl for this purpose, but instead I just modify the GuiTextCtrl itself to allow fading if desired. If fading is not desired, it will work the same as a normal GuiTextCtrl, and this is set as the default behavior.
To use this, just download the GuiTextCtrl.h and .cc files in the archive and drop them into your project, replacing the old GuiTextCtrl.h and .cc files. NOTE: As usual, you should test this out on a clean install of TGE 1.5.x before messing with your own project! You have been duly warned.
Below is the code for GuiTextCtrl.h, with the modifications for this resource shown in bold:
Below are the changes to GuiTextCtrl.cc. In GuiTextCtrl::GuiTextCtrl(), the lines in bold are added (note that the default for fadeCtrl is false, which means the default behavior of the GuiTextCtrl is to not perform fading in or out):
Later, in GuiTextCtrl::InitPersistFields():
The main change is to GuiTextCtrl::onRender(). The entire function is replaced with the code below:
Finally, these last methods are added at the end of the file to trigger the fadein or fadeout, and to provide script hooks for these functions:
That's all there is to it. To use the new GuiTextCtrl, use it the same way as you would any other GuiTextCtrl. If you want it to be able to fade in and out, set its fadeCtrl variable to true, and then specify desired values for fadeinTime and fadeoutTime, as below:
To initiate the fading in or out, just call the script function beginFadein or beginFadeout, as below for the GuiTextCtrl defined above:
That's all there is to it...let me know if there are any issues with the code.
Note: If you want to use the fade functions (by setting the control's fadeCtrl variable to true), the control assumes it starts from a fully transparent starting point. If you want the control to start already visible (fully faded in), then set the variables startFadein and fadeinDone to true. Then use the fadeout/fadein functions as usual.
During development, I found I had the need to use a GuiTextCtrl to display a simple text string, but I wanted the text to be able to fade in from transparency and back out again, on command. The only way I could figure out how to do this in script was to modify the alpha value of the GuiTextCtrl's profile, but this was staggeringly slow. An engine solution was needed.
Fortunately, someone had already done something very similar to this. Jeff Parry's excellent "Fading transparent bitmap control" resource does this with bitmap images, so the code was already essentially there. His resource did not provide script functions to trigger the fading in or fading out process, so I modified it to include this functionality.
I thought originally to create a new GuiControl for this purpose, but instead I just modify the GuiTextCtrl itself to allow fading if desired. If fading is not desired, it will work the same as a normal GuiTextCtrl, and this is set as the default behavior.
To use this, just download the GuiTextCtrl.h and .cc files in the archive and drop them into your project, replacing the old GuiTextCtrl.h and .cc files. NOTE: As usual, you should test this out on a clean install of TGE 1.5.x before messing with your own project! You have been duly warned.
Below is the code for GuiTextCtrl.h, with the modifications for this resource shown in bold:
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//
// Modifications for text fading by Michael Rubin (MAR)
//-----------------------------------------------------------------------------
#ifndef _GUITEXTCTRL_H_
#define _GUITEXTCTRL_H_
#ifndef _GFONT_H_
#include "dgl/gFont.h"
#endif
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GuiTextCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
public:
enum Constants { MAX_STRING_LENGTH = 1024 };
[b] // MAR: added for text fadein/fadeout
bool startFadein;
bool fadeinDone;
U32 fadeinTime;
U32 fadeinStartTime;
bool startFadeout;
bool fadeoutDone;
U32 fadeoutTime;
U32 fadeoutStartTime;
bool fadeCtrl;
// MAR: end[/b]
protected:
StringTableEntry mInitialText;
StringTableEntry mInitialTextID;
UTF8 mText[MAX_STRING_LENGTH + 1];
S32 mMaxStrLen; // max string len, must be less then or equal to 255
Resource<GFont> mFont;
public:
//creation methods
DECLARE_CONOBJECT(GuiTextCtrl);
GuiTextCtrl();
static void initPersistFields();
//Parental methods
bool onAdd();
virtual bool onWake();
virtual void onSleep();
//text methods
virtual void setText(const char *txt = NULL);
virtual void setTextID(S32 id);
virtual void setTextID(const char *id);
const char *getText() { return (const char*)mText; }
// Text Property Accessors
static bool setText(void* obj, const char* data) { static_cast<GuiTextCtrl*>(obj)->setText(data); return true; }
static const char* getTextProperty(void* obj, const char* data) { return static_cast<GuiTextCtrl*>(obj)->getText(); }
void inspectPostApply();
//rendering methods
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
void displayText( S32 xOffset, S32 yOffset );
//Console methods
const char *getScriptValue();
void setScriptValue(const char *value);
[b] // MAR: added for text fadein/fadeout
bool GuiTextCtrl::beginFadein();
bool GuiTextCtrl::beginFadeout();
// MAR: end [/b]
};
#endif //_GUI_TEXT_CONTROL_H_Below are the changes to GuiTextCtrl.cc. In GuiTextCtrl::GuiTextCtrl(), the lines in bold are added (note that the default for fadeCtrl is false, which means the default behavior of the GuiTextCtrl is to not perform fading in or out):
GuiTextCtrl::GuiTextCtrl()
{
//default fonts
mInitialText = StringTable->insert("");
mInitialTextID = StringTable->insert("");
mText[0] = '[[6101dd77d23be]]';
mMaxStrLen = GuiTextCtrl::MAX_STRING_LENGTH;
[b] // MAR: added for text fadein/fadeout
startFadein = false;
fadeinDone = false;
fadeinTime = 1000;
fadeinStartTime = 0;
startFadeout = false;
fadeoutDone = false;
fadeoutTime = 1000;
fadeoutStartTime = 0;
fadeCtrl = false; // distinguishes between controls that should or should not fade
// MAR: end[/b]
}Later, in GuiTextCtrl::InitPersistFields():
void GuiTextCtrl::initPersistFields()
{
Parent::initPersistFields();
//addField( "text", TypeCaseString, Offset( mInitialText, GuiTextCtrl ) );
addProtectedField("text", TypeCaseString, Offset(mInitialText, GuiTextCtrl), setText, getTextProperty, "");
addField( "textID", TypeString, Offset( mInitialTextID, GuiTextCtrl ) );
addField( "maxLength", TypeS32, Offset( mMaxStrLen, GuiTextCtrl ) );
[b] // MAR: added for text fadein/fadeout
addField("startFadein", TypeBool, Offset(startFadein, GuiTextCtrl));
addField("fadeinDone", TypeBool, Offset(fadeinDone, GuiTextCtrl));
addField("fadeinTime", TypeS32, Offset(fadeinTime, GuiTextCtrl));
addField("startFadeout", TypeBool, Offset(startFadeout, GuiTextCtrl));
addField("fadeoutDone", TypeBool, Offset(fadeoutDone, GuiTextCtrl));
addField("fadeoutTime", TypeS32, Offset(fadeoutTime, GuiTextCtrl));
addField("fadeCtrl", TypeBool, Offset(fadeCtrl, GuiTextCtrl));
// MAR: end[/b]
}The main change is to GuiTextCtrl::onRender(). The entire function is replaced with the code below:
[b]void GuiTextCtrl::onRender(Point2I offset, const RectI &updateRect)
{
// MAR: this code modified to allow text fadein/fadeout
U32 current = Platform::getRealMilliseconds();
U32 elapsed;
U32 alpha;
if (!fadeCtrl) {
// we're not supposed to fade in or out, so just render us normally
dglSetBitmapModulation( mProfile->mFontColor );
renderJustifiedText(offset, mBounds.extent, (char*)mText);
//render the child controls
renderChildControls(offset, updateRect);
return;
}
if (!startFadein) {
// haven't started the fadein sequence yet, so should not be visible
ColorI color(mProfile->mFontColor.red, mProfile->mFontColor.green, mProfile->mFontColor.blue, 0);
dglSetBitmapModulation(color);
renderJustifiedText(offset, mBounds.extent, (char*)mText);
renderChildControls(offset, updateRect);
return;
}
if (!startFadeout) {
// we've started the fadein sequence, but we haven't started the fadeout sequence yet
if (fadeinDone) {
// we've already finished fading in, so just display at full alpha
ColorI color(mProfile->mFontColor.red, mProfile->mFontColor.green, mProfile->mFontColor.blue, 255);
dglSetBitmapModulation(color);
renderJustifiedText(offset, mBounds.extent, (char*)mText);
renderChildControls(offset, updateRect);
return;
}
else {
// we're still fading in
elapsed = current - fadeinStartTime;
alpha = 255 * (F32(elapsed) / F32(fadeinTime));
if (alpha > 255) {
alpha = 255;
}
if (elapsed > fadeinTime) {
fadeinDone = true;
}
ColorI color(mProfile->mFontColor.red, mProfile->mFontColor.green, mProfile->mFontColor.blue, alpha);
dglSetBitmapModulation(color);
renderJustifiedText(offset, mBounds.extent, (char*)mText);
renderChildControls(offset, updateRect);
return;
}
}
else {
// we've started the fadeout sequence
if (fadeoutDone) {
// we're already finished fading out, so not much else left to do
startFadein = false;
startFadeout = false;
fadeinDone = false;
fadeoutDone = false;
ColorI color(mProfile->mFontColor.red, mProfile->mFontColor.green, mProfile->mFontColor.blue, 0);
dglSetBitmapModulation(color);
renderJustifiedText(offset, mBounds.extent, (char*)mText);
renderChildControls(offset, updateRect);
return;
}
else {
// we're still fading out
elapsed = current - fadeoutStartTime;
alpha = 255 * (F32(elapsed) / F32(fadeoutTime));
if (alpha > 255) {
alpha = 255;
}
if (elapsed > fadeoutTime) {
fadeoutDone = true;
}
ColorI color(mProfile->mFontColor.red, mProfile->mFontColor.green, mProfile->mFontColor.blue, 255-alpha);
dglSetBitmapModulation(color);
renderJustifiedText(offset, mBounds.extent, (char*)mText);
renderChildControls(offset, updateRect);
return;
}
}
// MAR: end
}[/b]Finally, these last methods are added at the end of the file to trigger the fadein or fadeout, and to provide script hooks for these functions:
[b]//-----------------------------------------------------------------------------
// MAR: this code added for text fadein/fadeout
bool GuiTextCtrl::beginFadein()
{
fadeinStartTime = Platform::getRealMilliseconds();
startFadein = true;
}
bool GuiTextCtrl::beginFadeout()
{
fadeoutStartTime = Platform::getRealMilliseconds();
startFadeout = true;
}
ConsoleMethod( GuiTextCtrl, beginFadein, void, 2, 2, ""
"Start the fade-in sequence.")
{
object->beginFadein();
}
ConsoleMethod( GuiTextCtrl, beginFadeout, void, 2, 2, ""
"Start the fade-out sequence.")
{
object->beginFadeout();
}
// MAR: end
//-----------------------------------------------------------------------------[/b]That's all there is to it. To use the new GuiTextCtrl, use it the same way as you would any other GuiTextCtrl. If you want it to be able to fade in and out, set its fadeCtrl variable to true, and then specify desired values for fadeinTime and fadeoutTime, as below:
new GuiTextCtrl(IntroCredit) {
canSaveDynamicFields = "0";
Profile = "IntroCreditTextProfile";
HorizSizing = "right";
VertSizing = "top";
Position = "30 315";
Extent = "119 62";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
hovertime = "1000";
text = "";
maxLength = "255";
[b] fadeCtrl = true;
fadeinTime = 2000;
fadeoutTime = 2000;[/b]
};To initiate the fading in or out, just call the script function beginFadein or beginFadeout, as below for the GuiTextCtrl defined above:
[b] IntroCredit.beginFadein();[/b]
That's all there is to it...let me know if there are any issues with the code.
Note: If you want to use the fade functions (by setting the control's fadeCtrl variable to true), the control assumes it starts from a fully transparent starting point. If you want the control to start already visible (fully faded in), then set the variables startFadein and fadeinDone to true. Then use the fadeout/fadein functions as usual.
#2
So you could either add those return true; statements to each, or just make them void's.
10/08/2007 (1:46 pm)
I did recently note that the GuiTextCtrl::beginFadein() and GuiTextCtrl::beginFadeout() routines were defined to return bool's. XCode on the Mac ignores the fact that there is no return true; statement at the end of the function, but VS2005 on the PC doesn't like it.So you could either add those return true; statements to each, or just make them void's.
#3
10/08/2007 (5:02 pm)
Excelent work! Thanks :)
#4
10/17/2007 (7:03 am)
I think it would be more useful to just have functions like text.fadeIn(500); text.fadeOut(500); So every textCtrl could be fade in/out easilly by script.
#5
The way I was approaching it, I didn't have many reasons to change a control's fadeinTime or fadeoutTime after it is created, so I just set them at the time of creation. But that doesn't mean you can't change them in script afterward.
10/17/2007 (7:40 am)
@Diego: that's a good idea, although the way it's currently set up, you can do it in script -- it just takes one extra line. All you would need to do (using the example GuiTextCtrl above) is to do:IntroCredit.fadeinTime = 500; IntroCredit.beginFadein();
The way I was approaching it, I didn't have many reasons to change a control's fadeinTime or fadeoutTime after it is created, so I just set them at the time of creation. But that doesn't mean you can't change them in script afterward.
#6
10/17/2007 (8:21 am)
Yeah, the way you did is prety good, i just preffer the way that torque already do with the StaticShapes startFade(int fadeTime, int delayTime, bool fadeOut); with just one method you can do whatever you want, and dont need to have a lot of variables exposed.
#7
02/04/2008 (8:06 am)
Do you think it will need a big change to work in TGEA?
#8
02/04/2008 (8:20 am)
I don't have any experience or knowledge of TGEA, so I can't comment.
#9
08/18/2008 (5:37 pm)
has anyone implemented this into GuiMLTextCtrl? 
Torque Owner Martin Schultz