Sign in to follow this  
TheMightyDude

DrawText with whitespaces issue :(

Recommended Posts

Hello All I am using code based on the code thats in the DirectX9 User Interfaces book, and I have a problem. When I type in the TextBox Control the cursor moves left and right when enerting and removing text, however spaces doesn's make the cursor move if the last character is a space ("test ") is displayed as ("test"), I saw in another post that ID3DXFont::DrawText() when used to calculate the size of the rect doesn't count the whitespaces at the end of the text, and the reason for this was due to whitespaces don't get rendered or something on those lines. My problem is how would I get it to place the cursor to include the whitespaces at the end of the text? I thought of having m_endspaces var that gets incremented each time a whitespace is added, and reset it to 0 if a non-whitespace is added. Is this the right way to do it, or is their a more better way to do it? Any help would be great :) Anyway here is the 2 bits of code where i am having problems with:
// Code to calculate the String Width.
FLOAT CXTextBox::GetStringWidth(std::string String)
{
    RECT String_Info;
    ZeroMemory(&String_Info, sizeof(RECT)); 

    if(m_Font)
    {
        m_Font->DrawText(NULL, String.c_str(), String.length(), &String_Info, DT_CALCRECT, D3DCOLOR_XRGB(0,0,0));
    }
// .right seems to be 0 when String equals a whitespace :(
    return (float)String_Info.right;
}
// Code that handles the TextBox Rendering.
bool CXTextBox::OnRender()
{
    D3DXVECTOR2 Abs;
    RECT Rct;

    ZeroMemory(&Abs, sizeof(D3DXVECTOR2));

    GetAbsolutePosition(&Abs);

    Rct.left    = (long)Abs.x;
    Rct.top     = (long)Abs.y;
    Rct.right   = (long)Abs.x + GetWidth();
    Rct.bottom  = (long)Abs.y + GetHeight();
    m_Font->DrawText(NULL, m_Text.c_str(), m_Text.length(), &Rct, DT_LEFT , D3DCOLOR_XRGB(255,255,255));

    if(m_Focus)
    {
        if(m_CaretVisible)
        {
            D3DXVECTOR2 Absolute[2];
            ZeroMemory(Absolute, sizeof(D3DXVECTOR2) * 2);

            Absolute[0].x = Abs.x + m_CaretVector[0].x;
            Absolute[0].y = Abs.y + m_CaretVector[0].y+2;
            Absolute[1].x = Abs.x + m_CaretVector[0].x;
            Absolute[1].y = Abs.y + m_CaretVector[0].y +2+ (10 * 96/72);//GetHeight();

            m_CaretLine->Draw(Absolute, 2, D3DCOLOR_XRGB(255,255,255));
            m_CaretVisible = false;
        }
        else
        {
            m_CaretVisible = true;
        }
    }
    return true;
}
Thanks in andvance Paul Kirby [Edited by - TheMightyDude on May 9, 2008 10:33:27 AM]

Share this post


Link to post
Share on other sites
Thanks for the reply

Quote:
Original post by streamer
Check this one.
It is texture based font drawing, easy to implement, and faster than normal DrawText calls.
It draws spaces also [smile], so you will have no problems like this.


I took at the dx9 version of his code, run ok and displayed fine, however you cant compile their code due to half their header files are not included in their archives, that’s not too much of an issue, I will most probably be able to look through the code to see how they have done it (hopefully).

My code displays fine, but its where I use DrawText() with the DT_CALCRECT flag so that it returns the size of the RECT needed, and that returns a size of 0 for whitespaces.

Come to think of it FastFont wouldn't really solve my issue, its not the displaying that I am having problems with, its calculating the width of the string which is used to calculate where to put my flashing(blinking) cursor prompt (i.e. |).

I guess I could have a very small loop that searches the string in reverse for whitespaces and counts them and multiplies that by about 5, it then adds that to the RECT, this will only be done when I add, remove or modify anything in the string, so it shouldn't be much of an impact.

As I have already said, thanks for your reply, I now have another way to display text if needed :)

Here is how it looks so far:
Engine_20080509_01

The TextBox is what I am working on atm, and thats just below the Button, and I haven't added any borders around it yet either, I just wanted to sort my issue out first :)

Thanks
paul Kirby

Share this post


Link to post
Share on other sites
Thanks for your reply.

Quote:
Original post by Evil Steve
Have you tried using GetTextExtentPoint32 to calculate the required size instead? I'm not sure if that ignores trailing whitespace too, but it might not - should be quick to check anyway.


Well it started to look like it solved it, however that only worked when using repeated letters like "AAAAAAAAAAAAAAAAAAAA" but when using something like "The little brown fox jumped over the fence" it all goes wrong. :(

It seems that GetTextExtentPoint32() doesnt scale with the size of the text (FontSize). :(
I.e its the same no matter the font size.

Oh yeah, it did count the whitespaces.

So it looks like I am going to have to count the amount of whitespaces that are together at the end of the string and mutliply that with the widthScale.

I will also need to use DrawText() to find the widthScale of '.' <- closest size to a whitespace and then add that to the width returned by DrawText() and I should in theroy get the correct width.

There must be a way to do this, because other game that have chat in their games must of had this issue to resolve.

Man id wish M$ would fix this :(

Share this post


Link to post
Share on other sites
Quote:
Original post by TheMightyDude
Well it started to look like it solved it, however that only worked when using repeated letters like "AAAAAAAAAAAAAAAAAAAA" but when using something like "The little brown fox jumped over the fence" it all goes wrong. :(

It seems that GetTextExtentPoint32() doesnt scale with the size of the text (FontSize). :(
I.e its the same no matter the font size.
Weird... From my text rendering code (Slightly modified):

char ch = ' '; // Whatever character to get the width of
// m_hDC is the HDC containing the font

// Get the size of the glyph blackbox
GLYPHMETRICS glyphMetrics;
MAT2 mat;
memset(&mat, 0, sizeof(mat));
mat.eM11.value = 1;
mat.eM22.value = 1;
GetGlyphOutline(m_hDC, ch, GGO_METRICS, &glyphMetrics, 0, NULL, &mat);
// Width is now glyphMetrics.gmCellIncX


You could give that a go. I know for a fact that works correctly.

Quote:
Original post by TheMightyDude
There must be a way to do this, because other game that have chat in their games must of had this issue to resolve.
Most commercial games don't use ID3DXFont, they have their own text renderer.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Quote:
Original post by TheMightyDude
Well it started to look like it solved it, however that only worked when using repeated letters like "AAAAAAAAAAAAAAAAAAAA" but when using something like "The little brown fox jumped over the fence" it all goes wrong. :(

It seems that GetTextExtentPoint32() doesnt scale with the size of the text (FontSize). :(
I.e its the same no matter the font size.
Weird... From my text rendering code (Slightly modified):
*** Source Snippet Removed ***You could give that a go. I know for a fact that works correctly.

Quote:
Original post by TheMightyDude
There must be a way to do this, because other game that have chat in their games must of had this issue to resolve.
Most commercial games don't use ID3DXFont, they have their own text renderer.


Well it must be just me then :(
glyphMetrics.gmCellIncX = -13108 thats not right :(

Also GetGlyphOutline() is returning -1 which is GDI_ERROR isn't it?
So it's failing for some reason :(

Share this post


Link to post
Share on other sites
Yeah, this is an insanely annoying issue and makes me want to throw away the DrawText function for texture-based text rendering.

Wait until you get into multiple line textboxes and caret placing. Oh that will drive you mad.

I think what I ended up doing was declaring a single Textbox control object (not sure if you are using C# or not). I then use the GetPositionFromCharIndex method of the textbox after setting its properties to the same as my textbox control. It works good but still quirky at times.

At this point, after all of the UI work I've done, if I could go back and make another decision, I'd go with texture-based text rendering. More control and better results.

-E

Share this post


Link to post
Share on other sites
Quote:
Original post by TheMightyDude
Well it must be just me then :(
glyphMetrics.gmCellIncX = -13108 thats not right :(

Also GetGlyphOutline() is returning -1 which is GDI_ERROR isn't it?
So it's failing for some reason :(
Typical, that's the bit I cut out [smile]
It returns GDI_ERROR if the font isn't a truetype font. Here's the unmodified code:

// Get the size of the glyph blackbox
GLYPHMETRICS glyphMetrics;
MAT2 mat;
memset(&mat, 0, sizeof(mat));
mat.eM11.value = 1;
mat.eM22.value = 1;
if(GetGlyphOutline(m_hDC, ch, GGO_METRICS, &glyphMetrics, 0, NULL, &mat) == GDI_ERROR)
{
// This is probably not a truetype font - approximate sizes
SIZE sizeGlyph;
if(!GetTextExtentPoint32W(m_hDC, &ch, 1, &sizeGlyph))
{
bRet = false;
break;
}

memset(&glyphMetrics, 0, sizeof(glyphMetrics));
glyphMetrics.gmBlackBoxX = sizeGlyph.cx;
glyphMetrics.gmBlackBoxY = sizeGlyph.cy;
glyphMetrics.gmptGlyphOrigin.x = 0;
glyphMetrics.gmptGlyphOrigin.y = m_nAscent;
glyphMetrics.gmCellIncX = (short)sizeGlyph.cx;
}

See if that's any more use...

Share this post


Link to post
Share on other sites
Quote:
Original post by EnochDagor
Yeah, this is an insanely annoying issue and makes me want to throw away the DrawText function for texture-based text rendering.

Wait until you get into multiple line textboxes and caret placing. Oh that will drive you mad.

I think what I ended up doing was declaring a single Textbox control object (not sure if you are using C# or not). I then use the GetPositionFromCharIndex method of the textbox after setting its properties to the same as my textbox control. It works good but still quirky at times.

At this point, after all of the UI work I've done, if I could go back and make another decision, I'd go with texture-based text rendering. More control and better results.

-E


lol, don't scare me :P
Well i haven't got that far yet, though i am now dreding it :(

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Quote:
Original post by TheMightyDude
Well it must be just me then :(
glyphMetrics.gmCellIncX = -13108 thats not right :(

Also GetGlyphOutline() is returning -1 which is GDI_ERROR isn't it?
So it's failing for some reason :(
Typical, that's the bit I cut out [smile]
It returns GDI_ERROR if the font isn't a truetype font. Here's the unmodified code:
*** Source Snippet Removed ***See if that's any more use...


Well i haven't tried that yet, but I know the following seems to work fine.
I wasn't too happy with the for loop but since this is only called when adding or removing text it shouldn't be too much of an issue.

I will try your code in a while.

Thanks for all your help so far :)

FLOAT CXTextBox::GetStringWidth(std::string String)
{
RECT String_Info;
ZeroMemory(&String_Info, sizeof(RECT));
int sizeofws = 0;
int found = 0;

if(m_Font)
{

// Get the size of "." <-- closest to a whitespace :)
m_Font->DrawText(NULL, ".", 1, &String_Info, DT_CALCRECT, D3DCOLOR_XRGB(0,0,0));
sizeofws = (int)String_Info.right;

// Now get the size of the string.
m_Font->DrawText(NULL, String.c_str(), String.length(), &String_Info, DT_CALCRECT, D3DCOLOR_XRGB(0,0,0));

// Check for any whitespaces at the end of the string.
if(String[String.length()-1] == 0x20)
{
for(int i=String.length()-1; i>=0; i--)
{
if(String[i] == 0x20)
{
// Found a whitespace.
found++;
}
else
{
// Ok, found a non whitespace at the end just before a whitespace.
// So I better break.
break;
}
}
}

if (found>0)
{
// if found >0 calculate the extra width to add.
String_Info.right += (int)(found * sizeofws);
}
}
return (float)String_Info.right;
}


Share this post


Link to post
Share on other sites
Hehe, that code looks vaguely familiar. I did something like it once. The for loop isn't too big of a deal. :)

And don't get down about the multi-line box. :) Wasn't my intention. Just giving you fair heads up. ;-)

Share this post


Link to post
Share on other sites
Quote:
Original post by EnochDagor
Hehe, that code looks vaguely familiar. I did something like it once. The for loop isn't too big of a deal. :)

Heh, it's very basic and was what I wanted to use a while ago, but wanted to get DrawText() way fixed.
No matter, it works for now with different font sizes :)

Quote:
Original post by EnochDagor
And don't get down about the multi-line box. :) Wasn't my intention. Just giving you fair heads up. ;-)


Na, it's ok :P
In most cases I proberbly won't need to use multiline TextBoxes anyway, so theres no problem there :P
Mindyou it would be nice to have that feature though, if I did have it then i could do something like I have now.

atm it uses the following code:

FLOAT CXTextBox::GetStringHeight(std::string String)
{
RECT String_Info;
ZeroMemory(&String_Info, sizeof(RECT));

if(m_Font)
{
m_Font->DrawText(NULL, String.c_str(), String.length(), &String_Info, DT_CALCRECT, D3DCOLOR_XRGB(0,0,0));
}
return (float)String_Info.bottom;
}



The only problem that I know that I will have would be if the second line only had a whitespace in it, but I could use something like what i used in GetStringWidth() so that shouldn't be much of an issue :)
Well one hopes :P

Share this post


Link to post
Share on other sites
Actually, I had problems with DrawText in a multi-line situation mainly with caret placement. The placement of text is not an issue. But one such problem was the Space at the end of a line causing the calculation of the caret to be weird.

I think I had to do something... figure out the line I was on, measure just that string. Sounds simple enough, eh? But in the end I had a lot of issues with it. It was a long time ago and I am having a hard time remembering it.

Anyway, good luck! :D

Share this post


Link to post
Share on other sites
Quote:
Original post by EnochDagor
Actually, I had problems with DrawText in a multi-line situation mainly with caret placement. The placement of text is not an issue. But one such problem was the Space at the end of a line causing the calculation of the caret to be weird.

Yeah, I just tried it and couldn't get the cursor on the next line, So I see the issue, most prob play with it later.

Quote:
Original post by EnochDagor
I think I had to do something... figure out the line I was on, measure just that string. Sounds simple enough, eh? But in the end I had a lot of issues with it. It was a long time ago and I am having a hard time remembering it.

Yeah, maybe able to work out the height of the strings RECT and divide by the lines and you have the height of the cursor, you then workout the width of each lines and work out when you should be on the next line and place it there, well something like that :P

Anyway, when exactly would you use a multi-line text box in a game?
I can't think of any at the moment.

Quote:
Original post by EnochDagor
Anyway, good luck! :D

Thanks.

Share this post


Link to post
Share on other sites
Quote:
Anyway, when exactly would you use a multi-line text box in a game?


You would use one in a description box. Thinking of an example... ever play Everquest or Everquest 2? When you are in your Personal screen... there's a place to edit the details of your character. It is a textbox with multi-line... freeform and all. That is an example of a multi-line textbox in a game. :)

-E

Share this post


Link to post
Share on other sites
You can use std::vector of single lines to represent multiline edit box, drawing them one under another,
or you can have some control character/string that will break the lines, for you to know when to write in new line. Something like < br > in HTML.

Share this post


Link to post
Share on other sites
I'm using this to compute the width of a space :

RECT TempRect = {0,0,0,0};
if (0 == pFont->DrawText(NULL, "a", 3, &TempRect, DT_CALCRECT, 0))
; // <- error handling here
int Awidth = TempRect.right;
if (0 == pFont->DrawText(NULL, "a a", 3, &TempRect, DT_CALCRECT, 0))
; // <- error handling here
int SpaceWidth = TempRect.right - 2 * Awidth;




This works for me but it might not suit your needs

Share this post


Link to post
Share on other sites
Quote:
Original post by rolkA
I'm using this to compute the width of a space :

*** Source Snippet Removed ***

This works for me but it might not suit your needs


Unfortunately he needs something different, where the space is on the end of string: "a "

You can normally check the space you need to draw the string, then check is there is a whitespace on the beginning and on the end. You need two variables for beginning and for the end. Then multiply variables with number of spaces.
beginning_space *= 10;
end_space *= 10;

Start of draw is x + beginning_space , end x + beginning_space + width + end_space.


Share this post


Link to post
Share on other sites
Quote:
Original post by rolkA
I'm using this to compute the width of a space :

*** Source Snippet Removed ***

This works for me but it might not suit your needs


Yeah I tried an 'a' and '|' etc, but found '.' seemed the closest to the width of a space, which was why I used that. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by streamer
Quote:
Original post by rolkA
I'm using this to compute the width of a space :

*** Source Snippet Removed ***

This works for me but it might not suit your needs


Unfortunately he needs something different, where the space is on the end of string: "a "

You can normally check the space you need to draw the string, then check is there is a whitespace on the beginning and on the end. You need two variables for beginning and for the end. Then multiply variables with number of spaces.
beginning_space *= 10;
end_space *= 10;

Start of draw is x + beginning_space , end x + beginning_space + width + end_space.


Well tbh, you don't need to check for begging spaces, just check for the spaces at the end.
Spaces at the start are in fact at the end if you don’t have non spaces after them.
DrawText() does return the width of the string which has spaces if there is a non space after them.

Share this post


Link to post
Share on other sites
Quote:
Original post by EnochDagor
Quote:
Anyway, when exactly would you use a multi-line text box in a game?


You would use one in a description box. Thinking of an example... ever play Everquest or Everquest 2? When you are in your Personal screen... there's a place to edit the details of your character. It is a textbox with multi-line... freeform and all. That is an example of a multi-line textbox in a game. :)

-E


Hmm, never really thought about that :P
As to Everquest and Everquest2 etc, no I haven't played them.

However i did find one game that uses a multi-line TextBox, WoW uses one for the user to enter in MACROS...
So I guess I will need to fix this. :P

Share this post


Link to post
Share on other sites
Thanks for all the help so far :)

Just to clarify things a little.
I am not having problems displaying text in a GUI::TextBox() Control, whether it be Single or Multi line they both display fine.

I was however having issues detecting the width of the entire string when it had spaces (whitespaces) at the end, this has been resolved.

The next issue I have which i have not looked into as yet, is calculating where the blinking cursor should be on the multi-line GUI::TextBox() Control.

I have been using VK_RETURN on the TextBox as you would with a normal TextBox which works fine. :)
However I think I will need to add a flag to the control which defines whether its a single or multi line control, due to you use the Enter/Return (VK_RETURN) to go onto the next line.
I will use bool m_multiline; for this, and when it detects a VK_RETURN and m_mutliline is true, it will then tell the control that it now has one extra line and so on.

I haven't thought too much into this yet, but in theory it should work. :)

Thanks in advance.
Paul Kirby

Share this post


Link to post
Share on other sites
Quote:
Original post by TheMightyDude
I have been using VK_RETURN on the TextBox as you would with a normal TextBox which works fine. :)
However I think I will need to add a flag to the control which defines whether its a single or multi line control, due to you use the Enter/Return (VK_RETURN) to go onto the next line.
I will use bool m_multiline; for this, and when it detects a VK_RETURN and m_mutliline is true, it will then tell the control that it now has one extra line and so on.

Thanks in advance.
Paul Kirby


As I said somewhere above, store it in std::vector class.

You will have these:

class EditBox
{
int x,y; // x & y position of cursor
std::vector < std::string > data; // text data
};



so number of rows you will have is data.size(), after VK_RETURN execute

data.push_back("");

which will add new row into text array.
After that point, when you write something you need to change data[ y ] member
in program (if y is 0 change first row, 1 second etc )

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this