Page 1 of 3

TextArea GUI Element [v1.0]

Posted: Mon May 30, 2011 10:09 am
by RdR
Hello all,

I've created a textarea class which I want to share with you if someone needs something like it.

Features:
- Multiple colors on 1 line
- Padding
- Line spacing/offset
- Adjustable size
- TextArea border ( adjustable size )
- Wrapping
- Long word chop
- Left / Right / Center aligned
- Background texture
- Multiple fonts in textarea (1 font per line)
- Image support
- (Auto) Scrolling
- Batching text to minimize draw calls
- Use RTT to improve performance
- Line lifetime (auto removed when expired )

Examples:
Image
Image

Image

Image

More examples can be found in an other project of mine TANK@WAR: http://irrlicht.sourceforge.net/phpBB2/ ... hp?t=44174

Source:
http://dev.ferket.net/irrlicht/TextArea.zip
IrrExt mirror

Example use:

Code: Select all

 
// Create a new textarea
TextArea* textArea = new TextArea(Environment, Environment->getRootGUIElement());
textArea->setDimension(370, 135);
textArea->setMaxLines(6);
textArea->setAlignment(TextArea::LEFT);
textArea->setPosition(irr::core::vector2di(5, 200));
textArea->drop(); // Always drop custom GUI Elements
 
 
// Create new line
Line* line = new Line();
 
irr::core::stringw msg = L"";
msg = "Lorum ipsum";
line->addString(msg, irr::video::SColor(255, 0, 0, 0));
 
msg = "dolor";
line->addString(msg, irr::video::SColor(255, 0, 255, 0));
 
msg = "amut";
line->addString(msg, irr::video::SColor(255, 125, 255, 0));
 
// Add image to line
irr::video::ITexture* icon = Environment->getVideoDriver()->getTexture("./media/icons/icon.png");
line->addImage(icon);
 
// Add new line to chatBox
textArea->addLine(line);
 
// Create another line with different font
line = new Line(Environment->getFont("./media/fonts/arial.xml"));
 
msg = "Lorum ipsum";
line->addString(msg, irr::video::SColor(255, 0, 0, 0));
 
// Add new line to chatBox
textArea->addLine(line);
 
To use the scrolling feature make you have to get the EMIE_MOUSE_WHEEL event:

Code: Select all

 
// Basic example
 
float mouseWheel;
 
bool OnEvent(const SEvent& event) {
        if (event.EventType == EET_MOUSE_INPUT_EVENT && event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
                mouseWheel += event.MouseInput.Wheel;
        }
        return false;
}
 
// Put this in your handle method
if (mouseWheel > 0 || mouseWheel < 0) {
        textArea->changeStartIndex((irr::s32) mouseWheel);
}
 
Note:
Make sure you add the source of Irrlicht to your includes as well (source/Irrlicht)
Else you will get an error like: TextArea.cpp:24:22: fatal error: CGUIFont.h: No such file or directory compilation terminated.

Posted: Mon May 30, 2011 10:53 am
by shadowslair
Hello. I`ve made sth similar some time ago, but your looks better. Thanks for sharing. :wink:

Posted: Mon May 30, 2011 4:14 pm
by Ovan
Thanks for sharing :)
but i think this is not optimized, you use std::vector but why not irr::core::array ? ...
and I think this he need to PreRender the text and always update if it changed, not always update on render

Posted: Tue May 31, 2011 5:13 am
by REDDemon
Yup it can be optimized but that's not a difficult task. Good job :)

Posted: Wed Jun 01, 2011 12:32 am
by RdR
Ovan wrote:Thanks for sharing :)
but i think this is not optimized, you use std::vector but why not irr::core::array ? ...
and I think this he need to PreRender the text and always update if it changed, not always update on render
What do you mean by PreRender? I'm calculating the visible line only when new lines are added or the dimension of the textarea changes.

You mean something like using static text and only updating when a new line is added? (or changes in dimension)
REDDemon wrote:Yup it can be optimized but that's not a difficult task. Good job :)
Thanks, what are your suggestions for optimization?

Posted: Wed Jun 01, 2011 7:31 am
by REDDemon
RdR wrote:Thanks, what are your suggestions for optimization?
first of all there are some methods that can be setted as const:)

Code: Select all

irr::u32 TextArea::getNumberOfLines();
irr::core::recti getDimension();
the following methods can be added in two flavors, const and not-const. The compiler will select the best:

Code: Select all

std::vector<Line*> TextArea::getLines();
drawing method:
I personally like how do you compute all the things in the draw method. But there is also an alternative (idk if it is reallygood).

Actually you are calling all the drawings methods.
like this:

Code: Select all

for (irr::u32 i = 0; i < strings.size(); i++)
{
    irr::core::stringw str = strings.at(i); //!

    stringDimension = font->getDimension(str.c_str());
    lastDimension.LowerRightCorner.X += stringDimension.Width;

    font->draw(str.c_str(), lastDimension, colors.at(i), false, false);
    lastDimension.UpperLeftCorner.X = lastDimension.LowerRightCorner.X;
}
look at the "//!". You are just copying data, that can be copied directly to the draw() function skipping a step. The compiler can detect this redondance as not. If not you are just spending cycles re-copying data.

You can for example access data from an array when iterating, and precompute that array every time a string change

Code: Select all

font->draw(somethingelsehere, lastDimension, colors.at(i), false, false);
There are similiar things like that that can be done. I will add two test cases with original code and modified and I'll check i there is really a perfomance increase just because I want to waste some time :).

Posted: Wed Jun 01, 2011 8:16 am
by hybrid
Calling font->draw for single characters is also pretty cycle wasting, and will reduce render performance to a bare minimum. You have to call this method for larger portions of the text.

Posted: Wed Jun 01, 2011 10:19 am
by REDDemon
O_O I dont think he is calling it for every character, but only for whole strings.

EDIT: gotta what you mean. I don't see any easy way for doing that.

Posted: Wed Jun 01, 2011 11:20 am
by hybrid
Check the draw2dImageBatch method. That's what is actually happening in font->draw.

Posted: Wed Jun 01, 2011 11:29 am
by RdR
hybrid wrote:Check the draw2dImageBatch method. That's what is actually happening in font->draw.
But isn't that used by all text drawing methods?

Posted: Wed Jun 01, 2011 11:39 am
by REDDemon
RdR wrote:
hybrid wrote:Check the draw2dImageBatch method. That's what is actually happening in font->draw.
But isn't that used by all text drawing methods?
Actually if you draw 5 different textes with the same colour they are not batched togheter with your method. so instead of 1 draw calls there will be 5 draw calls.

Every character is batched in the same text every times irrlicht call a draw font method. But you can batch more texts if you know they can be batched.

I have the feeling that the whole text on the screen can be drawed with only 1 draw call (if color is holded by vertex data). but changin the text maybe is a bit slow if it is all batched togheter. So un-batched text is not fast as light but in general is more flexible

Re: Textarea (Chatbox etc)

Posted: Sun Aug 21, 2011 1:46 pm
by RdR
I added some extra features:

- Multiple font support (1 font per line)
- Image support
- Scrolling

And some fixes
How to use it, see code in first post.

Source:
http://dev.ferket.net/irrlicht/TextArea.zip

Image

Image

Re: Textarea (Chatbox etc)

Posted: Mon Aug 22, 2011 7:28 pm
by Ovan
hello juste post a sample code to replace text by image

smiley varible is std::map<const char*, const char*>

Code: Select all

 
void ClientEngine::initMap()
{
    smiley[":)"] = "smile.png";
    smiley[";)"] = "wink.png";
    smiley[":("] = "sad.png";
    smiley[":o"] = "surprise.png";
}
const char* ClientEngine::findFirst(irr::core::stringw text, int pos, int &find)
{
    int out = -1;
    const char *findKey = "no";
    const char *tnew = 0;
    for(std::map<const char*, const char*>::iterator i = smiley.begin(); i != smiley.end(); ++i)
    {
        int current = text.find((*i).first, pos);
        if(current > -1)
        {
            if(current < (out == -1 ? text.size() : out))
            {
                out = current;
                findKey = (*i).first;
                tnew = (*i).second;
            }
        }
    }
    printf("[%s = %s]\n", findKey, tnew);
    find = out;
    return tnew;
}
engine::addon::Line* ClientEngine::replace(engine::addon::Line *l)
{
    int pictureIndex = 0;
    engine::addon::Line *lnew = new engine::addon::Line(l->getFont());
    printf("-----------------------------------\n");
    for(int it = 0; it != l->getStrings().size(); it++)
    {
        irr::core::stringw text = l->getStrings().at(it);
        if(text == IMAGE_PLACEHOLDER)
        {
            lnew->addImage(l->getImages().at(pictureIndex));
            if(pictureIndex < l->getImages().size()) pictureIndex++;
        }
        else
        {
            int pos = 0, zln = 0;
            const char *tnew = findFirst(text, pos, zln);
            if(zln < 0) lnew->addString(text.subString(0, text.size()), l->getColors().at(it));
            while(zln != -1)
            {
                tnew = findFirst(text, pos, zln);
                lnew->addString(text.subString(pos, zln-pos), l->getColors().at(it));
                lnew->addImage(driver->getTexture(tnew));
                pos = zln+2; // smiley key size ( ":)" size = 2 )
            }
        }
    }
    delete l;
    return lnew;
}
use:

Code: Select all

 
                engine::addon::Line *tmp = new engine::addon::Line();
                tmp->addString(your_text, your_color);
                history->addLine(replace(tmp));
 

Re: Textarea (Chatbox etc)

Posted: Mon Aug 22, 2011 8:13 pm
by RdR
Ovan wrote:hello juste post a sample code to replace text by image

smiley varible is std::map<const char*, const char*>

Code: Select all

 
void ClientEngine::initMap()
{
    smiley[":)"] = "smile.png";
    smiley[";)"] = "wink.png";
    smiley[":("] = "sad.png";
    smiley[":o"] = "surprise.png";
}
const char* ClientEngine::findFirst(irr::core::stringw text, int pos, int &find)
{
    int out = -1;
    const char *findKey = "no";
    const char *tnew = 0;
    for(std::map<const char*, const char*>::iterator i = smiley.begin(); i != smiley.end(); ++i)
    {
        int current = text.find((*i).first, pos);
        if(current > -1)
        {
            if(current < (out == -1 ? text.size() : out))
            {
                out = current;
                findKey = (*i).first;
                tnew = (*i).second;
            }
        }
    }
    printf("[%s = %s]\n", findKey, tnew);
    find = out;
    return tnew;
}
engine::addon::Line* ClientEngine::replace(engine::addon::Line *l)
{
    int pictureIndex = 0;
    engine::addon::Line *lnew = new engine::addon::Line(l->getFont());
    printf("-----------------------------------\n");
    for(int it = 0; it != l->getStrings().size(); it++)
    {
        irr::core::stringw text = l->getStrings().at(it);
        if(text == IMAGE_PLACEHOLDER)
        {
            lnew->addImage(l->getImages().at(pictureIndex));
            if(pictureIndex < l->getImages().size()) pictureIndex++;
        }
        else
        {
            int pos = 0, zln = 0;
            const char *tnew = findFirst(text, pos, zln);
            if(zln < 0) lnew->addString(text.subString(0, text.size()), l->getColors().at(it));
            while(zln != -1)
            {
                tnew = findFirst(text, pos, zln);
                lnew->addString(text.subString(pos, zln-pos), l->getColors().at(it));
                lnew->addImage(driver->getTexture(tnew));
                pos = zln+2; // smiley key size ( ":)" size = 2 )
            }
        }
    }
    delete l;
    return lnew;
}
use:

Code: Select all

 
                engine::addon::Line *tmp = new engine::addon::Line();
                tmp->addString(your_text, your_color);
                history->addLine(replace(tmp));
 
Thats a nice addon, thanks for sharing!

Re: Textarea (Chatbox etc)

Posted: Mon Jan 23, 2012 3:31 pm
by RdR
Updated the textarea.
Batching the text with the same font and color now, so draw() calls are minimized.
In my test project it gained from ~200 FPS to ~800 FPS :D

Image

Also fixt the wrapping for right & center alignment
And some minor bugs.

So I recommend everyone using this to upgrade to latest version.

Image