Home
Code & Snippets
Tutorials
GUI Docs
Projects
Drawings & Sketches
Links
Bio & Resume

Kindly hosted by


Support This Project

GUI Tutorial : Part #4

Table of Contents

Welcome to the fourth tutorial in GUI series. This time we are going into some technical stuff and learn how to draw text on the screen
(NeHe Font Tutorial #13) and how to align/wrap text as it is done in Windows (or in a similar way).
So, let's start.

Static Control class

A static control class is a simple GUI element with the text rendered above - what else does it need? That's right, nothing .
So, let's see how it looks like:

class CGUIStatic : public CGUIElement
{
public:

	virtual int Parse(TiXmlNode *node, char *filename);

	virtual void OnDraw();
	virtual void OnDestroy();
	
	void DrawText();

	CBitmapFont *GetFont();
	void SetFont(CBitmapFont *font);

	char *GetText();
	void SetText(char *strtext);

	void SetTextAlign(int horiz = 0, int vert = 1);
	void GetTextAlign(bool vert = false);

	CGUIStatic();
	virtual ~CGUIStatic();
	
private:
	tVERTEX2d m_TextPos;
	bool m_bWrapText;
	CBitmapFont *m_pFont;
	char *m_strText;
	bool m_bEditable;

	// For horizontal: 0 - left, 1 - center, 2 - right
	// For vertical: 0 - top, 1 - center, 2 - bottom
	int m_iTextHAlign, 
	    m_iTextVAlign;

	int GetLineLength(int line_index);
	int GetLineCount();
	char *GetLine(int line, int char_count = -1);

//	int m_iCaretPosX, m_iCaretPosY;
};

Yep, it's that simple, although the actual text handling and drawing isn't.


Font Class

OK, font class. Any basic font has a name, position for the string, font type and some additional features.
So, here it is, the general font class, from which all other fonts derive from:

class CCustomFont
{
public:
	CCustomFont();
	virtual ~CCustomFont();
	
	char	m_strFilename[128];
	DWORD	m_dwFlags;
	int m_iTextPosX, m_iTextPosY;

	virtual int Create();
	virtual int Parse(TiXmlNode *this_node, char *filename);
	virtual void Draw(const  char *fmt, ...);
	virtual void DrawChar(float x, float y, char c);
	virtual void Destroy();

	void SetFontType(eFontType type);
	eFontType GetFontType();

private:
	eFontType m_FontType;
};

Well, pretty simple. Virtual functions for creating, loading(parsing), drawing and destruction give the
ability to override the default functionality of a font with new content.

The font class. From the NeHe's tutorial we are going to copy some functionality of a bitmap font.
So, simply saying, each character will be a textured quad. As someone said,
"the best thing is usually the simplest one". So, we will follow that path:

class CBitmapFont : public CCustomFont
{
public:

	unsigned int m_iNI;
	unsigned int m_iCharLengthX, m_iCharLengthY, m_iQuadLengthX, m_iQuadLengthY, m_iSpaceChar;
	tMaterial	*m_pFontMat;

	virtual int Parse(TiXmlNode *this_node, char *filename);
	virtual void Draw(const  char *fmt, ...);
	virtual void DrawChar(float x, float y, char c);

	int CreateBitmapFont(unsigned int normal_italic, unsigned int charx, unsigned int chary, unsigned int quadx,
unsigned int quady, unsigned int space); CBitmapFont(); virtual ~CBitmapFont(); private: int m_iFontBase; int m_iNumLists; };

Well, that's about it. The bitmap font should have Character dimensions (how big is a sub-region inside texture for each character) and OpenGL-specific size of the quad for each character it's going to be mapped on.


Static Control XML data details

OK, let's now take a look at the Static Control description. It looks pretty much like the Element, but with some additions:

<Static Rect="34,353,540,520" Font="bitmap_font.xml" Text="Testing Static Control..." 
BorderFile="default_border.xml" VAlign="Top" HAlign="Left"/>

The additional parameters are the horizontal & vertical align for the text, the bitmap XML config file, and the text. Pretty straightforward.


Font XML data details

Well, now on to the font XML. Here's the example:

<Font Type="Bitmap" CX="16" CY="16" NI="Normal" QuadSizeX="12" QuadSizeY="12" Spacing="0">
<Material RefID="16"/>
</Font>

Not so complicated. The font has the type by which we can identify it, character and quad dimensions, spacing and material/texture
reference to bitmap that has texture with characters.

Rendering text on-screen

Well, this is the hardest part. The text in static control can be aligned in 9 different ways - 3 horizontal and 3 vertical different positions - and
also it can be wrapped or not at the end. Well, let's take a look at how we do it. We add a function called DrawText() so that the Z order can be contained. For example, we render all children after we have drawn the parent element, but here we have to go this way:

  1. Hide children and draw element alone
  2. Draw text
  3. Draw all children
void CGUIStatic::OnDraw()
{
	// Hide children, and draw only this element
	CGUIElement::OnDraw();
	// Draw it's text
	DrawText();
	// Draw children after
}

OK, so let's take a look at DrawText() function:

void CGUIStatic::DrawText()
{
	if(!(m_strText == NULL || strlen(m_strText) == 0))
	{
		for (int i=0; i<GetLineCount(); i++)
		{
			char line[255] = "";
			
			int text_width = GetLineLength(i) * m_pFont->m_iQuadLengthX;
			
			if(text_width >=GetWidth())
				GetLine(line, i, GetWidth() / m_pFont->m_iQuadLengthX);
			else
				GetLine(line, i);

			int align_x_pixels = 0, align_y_pixels = 0;

			if(m_iTextHAlign == 0)
				align_x_pixels = 0;
			
			if(m_iTextHAlign == 1)
				align_x_pixels = (GetWidth()- (strlen(line) * m_pFont->m_iQuadLengthX)) / 2;
			
			if(m_iTextHAlign == 2)
				align_x_pixels = GetWidth() - (strlen(line) * m_pFont->m_iQuadLengthX);

			if(m_iTextVAlign == 1)
				align_y_pixels = (GetHeight() - (GetLineCount() * m_pFont->m_iQuadLengthY)) / 2;

			if(m_iTextVAlign == 2)
				align_y_pixels = GetHeight() - (GetLineCount() * m_pFont->m_iQuadLengthY);

			m_TextPos.x = GetRect().m_iLeft + align_x_pixels;
			m_TextPos.y = GetRect().m_iTop - (i+1)*m_pFont->m_iQuadLengthY - align_y_pixels;

			if((m_TextPos.y) > GetRect().m_iBottom)
			{
				m_pFont->m_iTextPosX = m_TextPos.x;
				m_pFont->m_iTextPosY = m_TextPos.y;
				m_pFont->Draw(line);
			}
		}
	}
}

Firstly, we check if we have any text to render and only after that we proceed to:

  1. Aquire line width.
  2. Wrap text at the end - e.g. don't draw it since we simply can't draw beyond the static control.
  3. Aquire alignment displacement (in OpenGL units).
  4. Assign total text displacement + alignment to text position.
  5. Ensuring that we don't render past bottom of control, draw the text line by line.

Text alignment

The text in static control can be aligned in 9 different ways : 3 x 3 - 3 vertical and 3 horizontal ways = 9 total ways. Well, the alignment is
the usual - (left, center, right) for horizontal and (top, center, bottom) for vertical alignment.

Well, that's about it! Now, let's take a look what our code will do for us and be proud of yourselves because
now we can easily render text! Soon enough we will have a textbox that we can edit it in!
Next tutorial we will go into buttons. Cyas there!