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 }