1 /*
2 	GWEN
3 	Copyright (c) 2010 Facepunch Studios
4 	See license in Gwen.h
5 */
6 
7 #include "Gwen/Gwen.h"
8 #include "Gwen/Controls/RichLabel.h"
9 #include "Gwen/Controls/Label.h"
10 #include "Gwen/Utility.h"
11 
12 using namespace Gwen;
13 using namespace Gwen::Controls;
14 
15 const unsigned char Type_Text = 0;
16 const unsigned char Type_Newline = 1;
17 
GWEN_CONTROL_CONSTRUCTOR(RichLabel)18 GWEN_CONTROL_CONSTRUCTOR(RichLabel)
19 {
20 	m_bNeedsRebuild = false;
21 }
22 
AddLineBreak()23 void RichLabel::AddLineBreak()
24 {
25 	DividedText t;
26 	t.type = Type_Newline;
27 
28 	m_TextBlocks.push_back(t);
29 }
30 
AddText(const Gwen::TextObject & text,Gwen::Color color,Gwen::Font * font)31 void RichLabel::AddText(const Gwen::TextObject& text, Gwen::Color color, Gwen::Font* font)
32 {
33 	if (text.m_Data.size() == 0) return;
34 
35 	Gwen::Utility::Strings::UnicodeList lst;
36 	Gwen::Utility::Strings::Split(text.GetUnicode(), L"\n", lst, false);
37 
38 	for (size_t i = 0; i < lst.size(); i++)
39 	{
40 		if (i > 0) AddLineBreak();
41 
42 		DividedText t;
43 		t.type = Type_Text;
44 		t.text = lst[i];
45 		t.color = color;
46 		t.font = font;
47 
48 		m_TextBlocks.push_back(t);
49 		m_bNeedsRebuild = true;
50 		Invalidate();
51 	}
52 }
53 
SizeToChildren(bool w,bool h)54 bool RichLabel::SizeToChildren(bool w, bool h)
55 {
56 	Rebuild();
57 	return BaseClass::SizeToChildren(w, h);
58 }
59 
SplitLabel(const Gwen::UnicodeString & text,Gwen::Font * pFont,const DividedText & txt,int & x,int & y,int & lineheight)60 void RichLabel::SplitLabel(const Gwen::UnicodeString& text, Gwen::Font* pFont, const DividedText& txt, int& x, int& y, int& lineheight)
61 {
62 	Gwen::Utility::Strings::UnicodeList lst;
63 	Gwen::Utility::Strings::Split(text, L" ", lst, true);
64 	if (lst.size() == 0) return;
65 
66 	int iSpaceLeft = Width() - x;
67 
68 	// Does the whole word fit in?
69 	{
70 		Gwen::Point StringSize = GetSkin()->GetRender()->MeasureText(pFont, text);
71 		if (iSpaceLeft > StringSize.x)
72 		{
73 			return CreateLabel(text, txt, x, y, lineheight, true);
74 		}
75 	}
76 
77 	// If the first word is bigger than the line, just give up.
78 	{
79 		Gwen::Point WordSize = GetSkin()->GetRender()->MeasureText(pFont, lst[0]);
80 		if (WordSize.x >= iSpaceLeft)
81 		{
82 			CreateLabel(lst[0], txt, x, y, lineheight, true);
83 			if (lst[0].size() >= text.size()) return;
84 
85 			Gwen::UnicodeString LeftOver = text.substr(lst[0].size() + 1);
86 			return SplitLabel(LeftOver, pFont, txt, x, y, lineheight);
87 		}
88 	}
89 
90 	Gwen::UnicodeString strNewString = L"";
91 	for (size_t i = 0; i < lst.size(); i++)
92 	{
93 		Gwen::Point WordSize = GetSkin()->GetRender()->MeasureText(pFont, strNewString + lst[i]);
94 		if (WordSize.x > iSpaceLeft)
95 		{
96 			CreateLabel(strNewString, txt, x, y, lineheight, true);
97 			x = 0;
98 			y += lineheight;
99 			break;
100 			;
101 		}
102 
103 		strNewString += lst[i];
104 	}
105 
106 	Gwen::UnicodeString LeftOver = text.substr(strNewString.size() + 1);
107 	return SplitLabel(LeftOver, pFont, txt, x, y, lineheight);
108 }
109 
CreateLabel(const Gwen::UnicodeString & text,const DividedText & txt,int & x,int & y,int & lineheight,bool NoSplit)110 void RichLabel::CreateLabel(const Gwen::UnicodeString& text, const DividedText& txt, int& x, int& y, int& lineheight, bool NoSplit)
111 {
112 	//
113 	// Use default font or is one set?
114 	//
115 	Gwen::Font* pFont = GetSkin()->GetDefaultFont();
116 	if (txt.font) pFont = txt.font;
117 
118 	//
119 	// This string is too long for us, split it up.
120 	//
121 	Gwen::Point p = GetSkin()->GetRender()->MeasureText(pFont, text);
122 
123 	if (lineheight == -1)
124 	{
125 		lineheight = p.y;
126 	}
127 
128 	if (!NoSplit)
129 	{
130 		if (x + p.x > Width())
131 		{
132 			return SplitLabel(text, pFont, txt, x, y, lineheight);
133 		}
134 	}
135 
136 	//
137 	// Wrap
138 	//
139 	if (x + p.x >= Width())
140 	{
141 		CreateNewline(x, y, lineheight);
142 	}
143 
144 	Gwen::Controls::Label* pLabel = new Gwen::Controls::Label(this);
145 	pLabel->SetText(x == 0 ? Gwen::Utility::Strings::TrimLeft<Gwen::UnicodeString>(text, L" ") : text);
146 	pLabel->SetTextColor(txt.color);
147 	pLabel->SetFont(pFont);
148 	pLabel->SizeToContents();
149 	pLabel->SetPos(x, y);
150 
151 	//lineheight = (lineheight + pLabel->Height()) / 2;
152 
153 	x += pLabel->Width();
154 
155 	if (x >= Width())
156 	{
157 		CreateNewline(x, y, lineheight);
158 	}
159 }
160 
CreateNewline(int & x,int & y,int & lineheight)161 void RichLabel::CreateNewline(int& x, int& y, int& lineheight)
162 {
163 	x = 0;
164 	y += lineheight;
165 }
166 
Rebuild()167 void RichLabel::Rebuild()
168 {
169 	RemoveAllChildren();
170 
171 	int x = 0;
172 	int y = 0;
173 	int lineheight = -1;
174 	for (DividedText::List::iterator it = m_TextBlocks.begin(); it != m_TextBlocks.end(); ++it)
175 	{
176 		if (it->type == Type_Newline)
177 		{
178 			CreateNewline(x, y, lineheight);
179 			continue;
180 		}
181 
182 		if (it->type == Type_Text)
183 		{
184 			CreateLabel((*it).text, *it, x, y, lineheight, false);
185 			continue;
186 		}
187 	}
188 
189 	m_bNeedsRebuild = false;
190 }
191 
OnBoundsChanged(Gwen::Rect oldBounds)192 void RichLabel::OnBoundsChanged(Gwen::Rect oldBounds)
193 {
194 	BaseClass::OnBoundsChanged(oldBounds);
195 
196 	Rebuild();
197 }
198 
Layout(Gwen::Skin::Base * skin)199 void RichLabel::Layout(Gwen::Skin::Base* skin)
200 {
201 	BaseClass::Layout(skin);
202 
203 	if (m_bNeedsRebuild)
204 	{
205 		Rebuild();
206 	}
207 }