1 #include "components/TextComponent.h"
2
3 #include "utils/StringUtil.h"
4 #include "Log.h"
5 #include "Settings.h"
6
TextComponent(Window * window)7 TextComponent::TextComponent(Window* window) : GuiComponent(window),
8 mFont(Font::get(FONT_SIZE_MEDIUM)), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true),
9 mHorizontalAlignment(ALIGN_LEFT), mVerticalAlignment(ALIGN_CENTER), mLineSpacing(1.5f), mBgColor(0),
10 mRenderBackground(false)
11 {
12 }
13
TextComponent(Window * window,const std::string & text,const std::shared_ptr<Font> & font,unsigned int color,Alignment align,Vector3f pos,Vector2f size,unsigned int bgcolor)14 TextComponent::TextComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color, Alignment align,
15 Vector3f pos, Vector2f size, unsigned int bgcolor) : GuiComponent(window),
16 mFont(NULL), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true),
17 mHorizontalAlignment(align), mVerticalAlignment(ALIGN_CENTER), mLineSpacing(1.5f), mBgColor(0),
18 mRenderBackground(false)
19 {
20 setFont(font);
21 setColor(color);
22 setBackgroundColor(bgcolor);
23 setText(text);
24 setPosition(pos);
25 setSize(size);
26 }
27
onSizeChanged()28 void TextComponent::onSizeChanged()
29 {
30 mAutoCalcExtent = Vector2i((getSize().x() == 0), (getSize().y() == 0));
31 onTextChanged();
32 }
33
setFont(const std::shared_ptr<Font> & font)34 void TextComponent::setFont(const std::shared_ptr<Font>& font)
35 {
36 mFont = font;
37 onTextChanged();
38 }
39
40 // Set the color of the font/text
setColor(unsigned int color)41 void TextComponent::setColor(unsigned int color)
42 {
43 mColor = color;
44 mColorOpacity = mColor & 0x000000FF;
45 onColorChanged();
46 }
47
48 // Set the color of the background box
setBackgroundColor(unsigned int color)49 void TextComponent::setBackgroundColor(unsigned int color)
50 {
51 mBgColor = color;
52 mBgColorOpacity = mBgColor & 0x000000FF;
53 }
54
setRenderBackground(bool render)55 void TextComponent::setRenderBackground(bool render)
56 {
57 mRenderBackground = render;
58 }
59
60 // Scale the opacity
setOpacity(unsigned char opacity)61 void TextComponent::setOpacity(unsigned char opacity)
62 {
63 // This method is mostly called to do fading in-out of the Text component element.
64 // Therefore, we assume here that opacity is a fractional value (expressed as an int 0-255),
65 // of the opacity originally set with setColor() or setBackgroundColor().
66
67 unsigned char o = (unsigned char)((float)opacity / 255.f * (float) mColorOpacity);
68 mColor = (mColor & 0xFFFFFF00) | (unsigned char) o;
69
70 unsigned char bgo = (unsigned char)((float)opacity / 255.f * (float)mBgColorOpacity);
71 mBgColor = (mBgColor & 0xFFFFFF00) | (unsigned char)bgo;
72
73 onColorChanged();
74
75 GuiComponent::setOpacity(opacity);
76 }
77
getOpacity() const78 unsigned char TextComponent::getOpacity() const
79 {
80 return mColor & 0x000000FF;
81 }
82
setText(const std::string & text)83 void TextComponent::setText(const std::string& text)
84 {
85 mText = text;
86 onTextChanged();
87 }
88
setUppercase(bool uppercase)89 void TextComponent::setUppercase(bool uppercase)
90 {
91 mUppercase = uppercase;
92 onTextChanged();
93 }
94
render(const Transform4x4f & parentTrans)95 void TextComponent::render(const Transform4x4f& parentTrans)
96 {
97 if (!isVisible())
98 return;
99
100 Transform4x4f trans = parentTrans * getTransform();
101
102 if (mRenderBackground)
103 {
104 Renderer::setMatrix(trans);
105 Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), mBgColor, mBgColor);
106 }
107
108 if(mTextCache)
109 {
110 const Vector2f& textSize = mTextCache->metrics.size;
111 float yOff = 0;
112 switch(mVerticalAlignment)
113 {
114 case ALIGN_TOP:
115 yOff = 0;
116 break;
117 case ALIGN_BOTTOM:
118 yOff = (getSize().y() - textSize.y());
119 break;
120 case ALIGN_CENTER:
121 yOff = (getSize().y() - textSize.y()) / 2.0f;
122 break;
123 }
124 Vector3f off(0, yOff, 0);
125
126 if(Settings::getInstance()->getBool("DebugText"))
127 {
128 // draw the "textbox" area, what we are aligned within
129 Renderer::setMatrix(trans);
130 Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033);
131 }
132
133 trans.translate(off);
134 Renderer::setMatrix(trans);
135
136 // draw the text area, where the text actually is going
137 if(Settings::getInstance()->getBool("DebugText"))
138 {
139 switch(mHorizontalAlignment)
140 {
141 case ALIGN_LEFT:
142 Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
143 break;
144 case ALIGN_CENTER:
145 Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
146 break;
147 case ALIGN_RIGHT:
148 Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
149 break;
150 }
151 }
152 mFont->renderTextCache(mTextCache.get());
153 }
154 }
155
calculateExtent()156 void TextComponent::calculateExtent()
157 {
158 if(mAutoCalcExtent.x())
159 {
160 mSize = mFont->sizeText(mUppercase ? Utils::String::toUpper(mText) : mText, mLineSpacing);
161 }else{
162 if(mAutoCalcExtent.y())
163 {
164 mSize[1] = mFont->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText) : mText, getSize().x(), mLineSpacing).y();
165 }
166 }
167 }
168
onTextChanged()169 void TextComponent::onTextChanged()
170 {
171 calculateExtent();
172
173 if(!mFont || mText.empty())
174 {
175 mTextCache.reset();
176 return;
177 }
178
179 std::string text = mUppercase ? Utils::String::toUpper(mText) : mText;
180
181 std::shared_ptr<Font> f = mFont;
182 const bool isMultiline = (mSize.y() == 0 || mSize.y() > f->getHeight()*1.2f);
183
184 bool addAbbrev = false;
185 if(!isMultiline)
186 {
187 size_t newline = text.find('\n');
188 text = text.substr(0, newline); // single line of text - stop at the first newline since it'll mess everything up
189 addAbbrev = newline != std::string::npos;
190 }
191
192 Vector2f size = f->sizeText(text);
193 if(!isMultiline && mSize.x() && text.size() && (size.x() > mSize.x() || addAbbrev))
194 {
195 // abbreviate text
196 const std::string abbrev = "...";
197 Vector2f abbrevSize = f->sizeText(abbrev);
198
199 while(text.size() && size.x() + abbrevSize.x() > mSize.x())
200 {
201 size_t newSize = Utils::String::prevCursor(text, text.size());
202 text.erase(newSize, text.size() - newSize);
203 size = f->sizeText(text);
204 }
205
206 text.append(abbrev);
207
208 mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(text, Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(), mHorizontalAlignment, mLineSpacing));
209 }else{
210 mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(f->wrapText(text, mSize.x()), Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(), mHorizontalAlignment, mLineSpacing));
211 }
212 }
213
onColorChanged()214 void TextComponent::onColorChanged()
215 {
216 if(mTextCache)
217 {
218 mTextCache->setColor(mColor);
219 }
220 }
221
setHorizontalAlignment(Alignment align)222 void TextComponent::setHorizontalAlignment(Alignment align)
223 {
224 mHorizontalAlignment = align;
225 onTextChanged();
226 }
227
setVerticalAlignment(Alignment align)228 void TextComponent::setVerticalAlignment(Alignment align)
229 {
230 mVerticalAlignment = align;
231 }
232
setLineSpacing(float spacing)233 void TextComponent::setLineSpacing(float spacing)
234 {
235 mLineSpacing = spacing;
236 onTextChanged();
237 }
238
setValue(const std::string & value)239 void TextComponent::setValue(const std::string& value)
240 {
241 setText(value);
242 }
243
getValue() const244 std::string TextComponent::getValue() const
245 {
246 return mText;
247 }
248
applyTheme(const std::shared_ptr<ThemeData> & theme,const std::string & view,const std::string & element,unsigned int properties)249 void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
250 {
251 GuiComponent::applyTheme(theme, view, element, properties);
252
253 using namespace ThemeFlags;
254
255 const ThemeData::ThemeElement* elem = theme->getElement(view, element, "text");
256 if(!elem)
257 return;
258
259 if (properties & COLOR && elem->has("color"))
260 setColor(elem->get<unsigned int>("color"));
261
262 setRenderBackground(false);
263 if (properties & COLOR && elem->has("backgroundColor")) {
264 setBackgroundColor(elem->get<unsigned int>("backgroundColor"));
265 setRenderBackground(true);
266 }
267
268 if(properties & ALIGNMENT && elem->has("alignment"))
269 {
270 std::string str = elem->get<std::string>("alignment");
271 if(str == "left")
272 setHorizontalAlignment(ALIGN_LEFT);
273 else if(str == "center")
274 setHorizontalAlignment(ALIGN_CENTER);
275 else if(str == "right")
276 setHorizontalAlignment(ALIGN_RIGHT);
277 else
278 LOG(LogError) << "Unknown text alignment string: " << str;
279 }
280
281 if(properties & TEXT && elem->has("text"))
282 setText(elem->get<std::string>("text"));
283
284 if(properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
285 setUppercase(elem->get<bool>("forceUppercase"));
286
287 if(properties & LINE_SPACING && elem->has("lineSpacing"))
288 setLineSpacing(elem->get<float>("lineSpacing"));
289
290 setFont(Font::getFromTheme(elem, properties, mFont));
291 }
292