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