GuiElement#textColor | string [optional] Normal state text color, defaults to white [1, 1, 1, 1]. Format: "[r] [g] [b] [a]" where each value is in the range of [0.0, 1.0]. The first three values represent red, green and blue color channels and the last is the alpha (translucency) value. When setting this property in guiProfiles.xml, preset values as defined at the top of that file may also be used. |
GuiElement#textSelectedColor | string [optional] Selected state color, works like #textColor. |
GuiElement#text2SelectedColor | string [optional] Selected state shadow color, works like #textColor. |
GuiElement#textHighlightedColor | string [optional] Highlighted state color, works like #textColor. |
GuiElement#text2HighlightedColor | string [optional] Highlighted state shadow color, works like #textColor. |
GuiElement#textDisabledColor | string [optional] Disabled state color, works like #textColor. |
GuiElement#text2DisabledColor | string [optional] Disabled state shadow color, works like #textColor. |
GuiElement#text2Color | string [optional] Normal state shadow color, works like #textColor. |
GuiElement#textOffset | string [optional] Pixel offset of text from element origin in reference resolution, defaults to [0, 0]. Format: "[x]px [y]px" |
GuiElement#textFocusedOffset | string [optional] Pixel offset of text when focused, works like #textOffset. |
GuiElement#text2Offset | string [optional] Pixel offset of text shadow, works like #textOffset. |
GuiElement#text2FocusedOffset | string [optional] Pixel offset of text shadow when focused, works like #textOffset. |
GuiElement#textSize | string [optional] Pixel size of text in reference resolution, defaults to 0.03 * reference height. Format: "[size]px" |
GuiElement#text2Size | string [optional] Pixel size of text shadow, works like #textSize. |
GuiElement#textMaxWidth | string [optional] Maximum pixel width of text in reference resolution after which it will be truncated. Default is no maximum. Format: "[width]px". Limits size of auto-width. |
GuiElement#textAutoWidth | boolean [optional] If set to true, the width of the elemement is updated to fit the text in its width. Useful for flow layouts |
GuiElement#textLayoutMode | string [optional] Set to overflow for no capping of the text (vertical overflow when wrapping, horizontal when not wrapping). Set to resize to automatically resize to fit the element. Set to clip to clip the size to fit the element |
GuiElement#textMinSize | string [optional] Minimum text size when resizing. Format: "[height]px". Default: 0.03 screen size. If this minimum is reacher the rest is clipped. |
GuiElement#textMaxNumLines | int [optional] Maximum number of lines when wrapping around to next line. Defaults to 1. |
GuiElement#textBold | bool [optional] If true, displays text in bold letters. |
GuiElement#textSelectedBold | bool [optional] If true, displays text in bold letters when selected. |
GuiElement#textHighlightedBold | bool [optional] If true, displays text in bold letters when highlighted. |
GuiElement#textUpperCase | bool [optional] If true, displays text in upper case letters. |
GuiElement#text2Bold | bool [optional] If true, displays text shadow in bold letters. |
GuiElement#text2SelectedBold | bool [optional] If true, displays text shadow in bold letters when selected. |
GuiElement#text2HighlightedBold | bool [optional] If true, displays text shadow in bold letters when highlighted. |
GuiElement#textLinesPerPage | int [optional] Number of text lines per page. |
GuiElement#textLineHeightScale | float [optional] Scale of line heights. |
GuiElement#textAlignment | string [optional] Text alignment within element borders, defaults to left alignment. Valid values are "right", "center" and "left" |
GuiElement#textVerticalAlignment | string [optional] Vertical text alignment within element borders, defaults to middle alignment. Valid values are "top", "middle", "bottom" |
GuiElement#ignoreDisabled | bool [optional] If true, will display normal state colors when disabled instead of disabled state colors. |
GuiElement#text | string [optional] Initial text value. Variable text names prefixed with "$l10n_" will be resolved to a text in the current language by the localization system. |
GuiElement#textConsole | string [optional] Initial text value override for console version. "$l10n_" prefix works here, as well. |
GuiElement#onTextChanged | callback [optional] onTextChangedCallback(self, text) Called when text is changed by setText(). Receives this element and the new text. |
366 | function TextElement:copyAttributes(src) |
367 | TextElement:superClass().copyAttributes(self, src) |
368 | self.text = src.text |
369 | self.format = src.format |
370 | self.locaKey = src.locaKey |
371 | self.value = src.value |
372 | self.formatDecimalPlaces = src.formatDecimalPlaces |
373 | self.sourceText = src.sourceText |
374 | |
375 | self.textColor = table.copy(src.textColor) |
376 | self.textSelectedColor = table.copy(src.textSelectedColor) |
377 | self.textHighlightedColor = table.copy(src.textHighlightedColor) |
378 | self.textDisabledColor = table.copy(src.textDisabledColor) |
379 | self.text2Color = table.copy(src.text2Color) |
380 | self.text2SelectedColor = table.copy(src.text2SelectedColor) |
381 | self.text2HighlightedColor = table.copy(src.text2HighlightedColor) |
382 | self.text2DisabledColor = table.copy(src.text2DisabledColor) |
383 | self.textSize = src.textSize |
384 | self.textOffset = table.copy(src.textOffset) |
385 | self.textFocusedOffset = table.copy(src.textFocusedOffset) |
386 | self.text2Size = src.text2Size |
387 | self.text2Offset = table.copy(src.text2Offset) |
388 | self.text2FocusedOffset = table.copy(src.text2FocusedOffset) |
389 | self.ignoreDisabled = src.ignoreDisabled |
390 | |
391 | self.textMaxWidth = src.textMaxWidth |
392 | self.textMinWidth = src.textMinWidth |
393 | self.textMaxNumLines = src.textMaxNumLines |
394 | self.textAutoWidth = src.textAutoWidth |
395 | self.textLayoutMode = src.textLayoutMode |
396 | self.textMinSize = src.textMinSize |
397 | |
398 | self.textBold = src.textBold |
399 | self.textSelectedBold = src.textSelectedBold |
400 | self.textHighlightedBold = src.textHighlightedBold |
401 | self.text2Bold = src.text2Bold |
402 | self.text2SelectedBold = src.text2SelectedBold |
403 | self.text2HighlightedBold = src.text2HighlightedBold |
404 | self.textUpperCase = src.textUpperCase |
405 | self.textLinesPerPage = src.textLinesPerPage |
406 | self.textAlignment = src.textAlignment |
407 | self.currentPage = src.currentPage |
408 | self.defaultTextSize = src.defaultTextSize |
409 | self.defaultText2Size = src.defaultText2Size |
410 | self.textLineHeightScale = src.textLineHeightScale |
411 | self.textVerticalAlignment = src.textVerticalAlignment |
412 | |
413 | self.onTextChangedCallback = src.onTextChangedCallback |
414 | end |
891 | function TextElement:draw(clipX1, clipY1, clipX2, clipY2) |
892 | if self:getDoRenderText() then |
893 | if self.text ~= nil and self.text ~= "" then |
894 | |
895 | if clipX1 ~= nil then |
896 | setTextClipArea(clipX1, clipY1, clipX2, clipY2) |
897 | end |
898 | |
899 | setTextAlignment(self.textAlignment) |
900 | |
901 | local maxWidth = self.absSize[1] |
902 | if self.textMaxWidth ~= nil then |
903 | maxWidth = self.textMaxWidth |
904 | end |
905 | |
906 | if self.textMaxNumLines > 1 then |
907 | setTextWrapWidth(maxWidth) |
908 | end |
909 | |
910 | setTextLineBounds((self.currentPage - 1) * self.textLinesPerPage, self.textLinesPerPage) |
911 | setTextLineHeightScale(self.textLineHeightScale) |
912 | |
913 | local text = self.text |
914 | |
915 | local bold = self.textBold or (self.textSelectedBold and self:getIsSelected() or (self.textHighlightedBold and self:getIsHighlighted())) |
916 | setTextBold(bold) |
917 | |
918 | local xPos, yPos = self:getTextPosition(text) |
919 | |
920 | -- The rendering engine works as follows: |
921 | -- The text size is from baseline to above the umlaut of a capital. This causes |
922 | -- very weird vertical alignment. We are going to adjust this by drawing our text |
923 | -- vertically based on xheight+ascending. The engine does not provide this info, |
924 | -- because we are drawing simple bitmap fonts. |
925 | -- and there is only 1 font, so instead these values are using pixel counting. |
926 | -- For fonts of size 20, we offset on Y for 2px. It is proportional so we scale it. |
927 | local baselineOffset = self.textSize * 0.1 |
928 | yPos = yPos + baselineOffset |
929 | |
930 | if self.text2Size > 0 then |
931 | local x2Offset, y2Offset = self:getText2Offset() |
932 | bold = self.text2Bold or (self.text2SelectedBold and self:getIsSelected()) or (self.text2HighlightedBold and self:getIsHighlighted()) |
933 | setTextBold(bold) |
934 | local r,g,b,a = unpack(self:getText2Color()) |
935 | setTextColor(r,g,b,a*self.alpha) |
936 | renderText(xPos + x2Offset, yPos + y2Offset, self.text2Size, text) |
937 | end |
938 | |
939 | local r,g,b,a = unpack(self:getTextColor()) |
940 | setTextColor(r,g,b,a*self.alpha) |
941 | |
942 | local xOffset, yOffset = self:getTextOffset() |
943 | renderText(xPos + xOffset, yPos + yOffset, self.textSize, text) |
944 | |
945 | -- TODO: apply engine vertical text alignment as soon as it's exposed to script |
946 | setTextBold(false) |
947 | setTextAlignment(RenderText.ALIGN_LEFT) |
948 | setTextLineHeightScale(RenderText.DEFAULT_LINE_HEIGHT_SCALE) |
949 | setTextColor(1, 1, 1, 1) |
950 | setTextLineBounds(0, 0) |
951 | setTextWrapWidth(0) |
952 | |
953 | if clipX1 ~= nil then |
954 | setTextClipArea(0, 0, 1, 1) |
955 | end |
956 | |
957 | if self.debugEnabled or g_uiDebugEnabled then |
958 | if self.textMaxWidth ~= nil then |
959 | local yPixel = 1 / g_screenHeight |
960 | setOverlayColor(GuiElement.debugOverlay, 0, 0, 0, 1) |
961 | |
962 | local x = xPos + xOffset |
963 | if self.textAlignment == RenderText.ALIGN_RIGHT then |
964 | x = x - self.textMaxWidth |
965 | elseif self.textAlignment == RenderText.ALIGN_CENTER then |
966 | x = x - self.textMaxWidth / 2 |
967 | end |
968 | |
969 | renderOverlay(GuiElement.debugOverlay, x, yPos + yOffset, self.textMaxWidth, yPixel) |
970 | end |
971 | |
972 | local width = self:getTextWidth() |
973 | local x = xPos + xOffset |
974 | if self.textAlignment == RenderText.ALIGN_RIGHT then |
975 | x = x - width |
976 | elseif self.textAlignment == RenderText.ALIGN_CENTER then |
977 | x = x - width * 0.5 |
978 | end |
979 | |
980 | -- Baseline |
981 | setOverlayColor(GuiElement.debugOverlay, 0, 1, 0, 1) |
982 | renderOverlay(GuiElement.debugOverlay, x, yPos + yOffset, width, 1 / g_screenHeight) |
983 | -- xHeight |
984 | setOverlayColor(GuiElement.debugOverlay, 1, 0.5, 0, 1) |
985 | renderOverlay(GuiElement.debugOverlay, x, yPos + yOffset + getTextHeight(self.textSize, text) * 0.5, width, 1 / g_screenHeight) |
986 | -- Ascending |
987 | setOverlayColor(GuiElement.debugOverlay, 0, 0, 1, 1) |
988 | renderOverlay(GuiElement.debugOverlay, x, yPos + yOffset + getTextHeight(self.textSize, text) * 0.75, width, 1 / g_screenHeight) |
989 | end |
990 | end |
991 | end |
992 | TextElement:superClass().draw(self, clipX1, clipY1, clipX2, clipY2) |
993 | end |
137 | function TextElement:loadFromXML(xmlFile, key) |
138 | TextElement:superClass().loadFromXML(self, xmlFile, key) |
139 | |
140 | local xmlFilename = getXMLFilename(xmlFile) |
141 | local modName, _ = Utils.getModNameAndBaseDirectory(xmlFilename) |
142 | if modName ~= nil then |
143 | self.customEnvironment = modName |
144 | end |
145 | |
146 | self.textColor = GuiUtils.getColorArray(getXMLString(xmlFile, key.."#textColor"), self.textColor) |
147 | self.textSelectedColor = GuiUtils.getColorArray(getXMLString(xmlFile, key.."#textSelectedColor"), self.textSelectedColor) |
148 | self.text2SelectedColor = GuiUtils.getColorArray(getXMLString(xmlFile, key.."#text2SelectedColor"), self.text2SelectedColor) |
149 | self.textHighlightedColor = GuiUtils.getColorArray(getXMLString(xmlFile, key.."#textHighlightedColor"), self.textHighlightedColor) |
150 | self.text2HighlightedColor = GuiUtils.getColorArray(getXMLString(xmlFile, key.."#text2HighlightedColor"), self.text2HighlightedColor) |
151 | self.textDisabledColor = GuiUtils.getColorArray(getXMLString(xmlFile, key.."#textDisabledColor"), self.textDisabledColor) |
152 | self.text2DisabledColor = GuiUtils.getColorArray(getXMLString(xmlFile, key.."#text2DisabledColor"), self.text2DisabledColor) |
153 | self.text2Color = GuiUtils.getColorArray(getXMLString(xmlFile, key.."#text2Color"), self.text2Color) |
154 | |
155 | self.textOffset = GuiUtils.getNormalizedValues(getXMLString(xmlFile, key.."#textOffset"), self.outputSize, self.textOffset) |
156 | self.textFocusedOffset = GuiUtils.getNormalizedValues(getXMLString(xmlFile, key.."#textFocusedOffset"), self.outputSize, self.textFocusedOffset) |
157 | self.text2Offset = GuiUtils.getNormalizedValues(getXMLString(xmlFile, key.."#text2Offset"), self.outputSize, self.text2Offset) |
158 | self.text2FocusedOffset = GuiUtils.getNormalizedValues(getXMLString(xmlFile, key.."#text2FocusedOffset"), self.outputSize, self.text2FocusedOffset) |
159 | self.textSize = unpack(GuiUtils.getNormalizedValues(getXMLString(xmlFile, key.."#textSize"), {self.outputSize[2]}, {self.textSize})) |
160 | self.text2Size = unpack(GuiUtils.getNormalizedValues(getXMLString(xmlFile, key.."#text2Size"), {self.outputSize[2]}, {self.text2Size})) |
161 | |
162 | self.textBold = Utils.getNoNil(getXMLBool(xmlFile, key.."#textBold"), self.textBold) |
163 | self.textSelectedBold = Utils.getNoNil(getXMLBool(xmlFile, key.."#textSelectedBold"), self.textSelectedBold) |
164 | self.textHighlightedBold = Utils.getNoNil(getXMLBool(xmlFile, key.."#textHighlightedBold"), self.textHighlightedBold) |
165 | self.textUpperCase = Utils.getNoNil(getXMLBool(xmlFile, key.."#textUpperCase"), self.textUpperCase) |
166 | self.text2Bold = Utils.getNoNil(getXMLBool(xmlFile, key.."#text2Bold"), self.text2Bold) |
167 | self.text2SelectedBold = Utils.getNoNil(getXMLBool(xmlFile, key.."#text2SelectedBold"), self.text2SelectedBold) |
168 | self.text2HighlightedBold = Utils.getNoNil(getXMLBool(xmlFile, key.."#text2HighlightedBold"), self.text2HighlightedBold) |
169 | self.textLinesPerPage = Utils.getNoNil(getXMLInt(xmlFile, key.."#textLinesPerPage"), self.textLinesPerPage) |
170 | self.textLineHeightScale = Utils.getNoNil(getXMLFloat(xmlFile, key.."#textLineHeightScale"), self.textLineHeightScale) |
171 | |
172 | self.defaultTextSize = self.textSize |
173 | self.defaultText2Size = self.text2Size |
174 | |
175 | self.textMaxWidth = unpack(GuiUtils.getNormalizedValues(getXMLString(xmlFile, key.."#textMaxWidth"), {self.outputSize[1]}, {self.textMaxWidth})) |
176 | self.textMinWidth = unpack(GuiUtils.getNormalizedValues(getXMLString(xmlFile, key.."#textMinWidth"), {self.outputSize[1]}, {self.textMinWidth})) |
177 | self.textMaxNumLines = Utils.getNoNil(getXMLInt(xmlFile, key.."#textMaxNumLines"), self.textMaxNumLines) |
178 | self.textAutoWidth = Utils.getNoNil(getXMLBool(xmlFile, key .."#textAutoWidth"), self.textAutoWidth) |
179 | self.textMinSize = unpack(GuiUtils.getNormalizedValues(getXMLString(xmlFile, key.."#textMinSize"), {self.outputSize[2]}, {self.textMinSize})) |
180 | |
181 | local wrapModeKey = getXMLString(xmlFile, key .. "#textLayoutMode") |
182 | if wrapModeKey ~= nil then |
183 | wrapModeKey = wrapModeKey:lower() |
184 | if wrapModeKey == "truncate" then |
185 | self.textLayoutMode = TextElement.LAYOUT_MODE.TRUNCATE |
186 | elseif wrapModeKey == "resize" then |
187 | self.textLayoutMode = TextElement.LAYOUT_MODE.RESIZE |
188 | elseif wrapModeKey == "overflow" then |
189 | self.textLayoutMode = TextElement.LAYOUT_MODE.OVERFLOW |
190 | end |
191 | end |
192 | |
193 | local textAlignment = getXMLString(xmlFile, key.."#textAlignment") |
194 | if textAlignment ~= nil then |
195 | textAlignment = textAlignment:lower() |
196 | if textAlignment == "right" then |
197 | self.textAlignment = RenderText.ALIGN_RIGHT |
198 | elseif textAlignment == "center" then |
199 | self.textAlignment = RenderText.ALIGN_CENTER |
200 | else |
201 | self.textAlignment = RenderText.ALIGN_LEFT |
202 | end |
203 | end |
204 | |
205 | local textVerticalAlignment = getXMLString(xmlFile, key .. "#textVerticalAlignment") or "" |
206 | local verticalAlignKey = string.upper(textVerticalAlignment) |
207 | self.textVerticalAlignment = TextElement.VERTICAL_ALIGNMENT[verticalAlignKey] or self.textVerticalAlignment |
208 | |
209 | self.ignoreDisabled = Utils.getNoNil(getXMLBool(xmlFile, key.."#ignoreDisabled"), self.ignoreDisabled) |
210 | |
211 | local text = getXMLString(xmlFile, key.."#text") |
212 | if Platform.isConsole then |
213 | local textConsole = getXMLString(xmlFile, key.."#textConsole") |
214 | if textConsole ~= nil then |
215 | text = textConsole |
216 | end |
217 | end |
218 | if text ~= nil then |
219 | local addColon = false |
220 | local length = text:len() |
221 | if text:sub(length, length + 1) == ":" then |
222 | text = text:sub(1, length - 1) |
223 | addColon = true |
224 | end |
225 | if text:sub(1,6) == "$l10n_" then |
226 | text = g_i18n:getText(text:sub(7), self.customEnvironment) |
227 | end |
228 | if addColon and text ~= "" then |
229 | text = text .. ":" |
230 | end |
231 | |
232 | self.sourceText = text |
233 | |
234 | -- Otherwise this overrides the formatting as XML is loaded after profiles |
235 | if self.format == TextElement.FORMAT.NONE then |
236 | self:setText(text, false, true) |
237 | end |
238 | end |
239 | |
240 | self.formatDecimalPlaces = math.max(Utils.getNoNil(getXMLInt(xmlFile, key.."#formatDecimalPlaces"), self.formatDecimalPlaces), 0) |
241 | local format = getXMLString(xmlFile, key.."#format") |
242 | if format ~= nil then |
243 | format = format:lower() |
244 | local f = TextElement.FORMAT.NONE |
245 | if format == "currency" then |
246 | f = TextElement.FORMAT.CURRENCY |
247 | elseif format == "accounting" then |
248 | f = TextElement.FORMAT.ACCOUNTING |
249 | elseif format == "temperature" then |
250 | f = TextElement.FORMAT.TEMPERATURE |
251 | elseif format == "number" then |
252 | f = TextElement.FORMAT.NUMBER |
253 | elseif format == "percentage" then |
254 | f = TextElement.FORMAT.PERCENTAGE |
255 | elseif format == "none" then |
256 | f = TextElement.FORMAT.NONE |
257 | end |
258 | self:setFormat(f) |
259 | end |
260 | |
261 | self:addCallback(xmlFile, key.."#onTextChanged", "onTextChangedCallback") |
262 | self:updateSize() |
263 | end |
267 | function TextElement:loadProfile(profile, applyProfile) |
268 | TextElement:superClass().loadProfile(self, profile, applyProfile) |
269 | |
270 | self.textColor = GuiUtils.getColorArray(profile:getValue("textColor"), self.textColor) |
271 | self.textSelectedColor = GuiUtils.getColorArray(profile:getValue("textSelectedColor"), self.textSelectedColor) |
272 | self.textHighlightedColor = GuiUtils.getColorArray(profile:getValue("textHighlightedColor"), self.textHighlightedColor) |
273 | self.textDisabledColor = GuiUtils.getColorArray(profile:getValue("textDisabledColor"), self.textDisabledColor) |
274 | self.text2Color = GuiUtils.getColorArray(profile:getValue("text2Color"), self.text2Color) |
275 | self.text2SelectedColor = GuiUtils.getColorArray(profile:getValue("text2SelectedColor"), self.text2SelectedColor) |
276 | self.text2HighlightedColor = GuiUtils.getColorArray(profile:getValue("text2HighlightedColor"), self.text2HighlightedColor) |
277 | self.text2DisabledColor = GuiUtils.getColorArray(profile:getValue("text2DisabledColor"), self.text2DisabledColor) |
278 | |
279 | self.textSize = unpack(GuiUtils.getNormalizedValues(profile:getValue("textSize"), {self.outputSize[2]}, {self.textSize})) |
280 | self.textOffset = GuiUtils.getNormalizedValues(profile:getValue("textOffset"), self.outputSize, self.textOffset) |
281 | self.textFocusedOffset = GuiUtils.getNormalizedValues(profile:getValue("textFocusedOffset"), self.outputSize, {self.textOffset[1], self.textOffset[2]}) |
282 | self.text2Size = unpack(GuiUtils.getNormalizedValues(profile:getValue("text2Size"), {self.outputSize[2]}, {self.text2Size})) |
283 | self.text2Offset = GuiUtils.getNormalizedValues(profile:getValue("text2Offset"), self.outputSize, self.text2Offset) |
284 | self.text2FocusedOffset = GuiUtils.getNormalizedValues(profile:getValue("text2FocusedOffset"), self.outputSize, {self.text2Offset[1], self.text2Offset[2]}) |
285 | |
286 | self.textBold = profile:getBool("textBold", self.textBold) |
287 | self.textSelectedBold = profile:getBool("textSelectedBold", self.textSelectedBold) |
288 | self.textHighlightedBold = profile:getBool("textHighlightedBold", self.textHighlightedBold) |
289 | self.text2Bold = profile:getBool("text2Bold", self.text2Bold) |
290 | self.text2SelectedBold = profile:getBool("text2SelectedBold", self.text2SelectedBold) |
291 | self.text2HighlightedBold = profile:getBool("text2HighlightedBold", self.text2HighlightedBold) |
292 | self.textUpperCase = profile:getBool("textUpperCase", self.textUpperCase) |
293 | self.textLinesPerPage = profile:getNumber("textLinesPerPage", self.textLinesPerPage) |
294 | |
295 | self.textLineHeightScale = profile:getNumber("textLineHeightScale", self.textLineHeightScale) |
296 | |
297 | self.textMaxWidth = unpack(GuiUtils.getNormalizedValues(profile:getValue("textMaxWidth"), {self.outputSize[1]}, {self.textMaxWidth})) |
298 | self.textMinWidth = unpack(GuiUtils.getNormalizedValues(profile:getValue("textMinWidth"), {self.outputSize[1]}, {self.textMinWidth})) |
299 | self.textMaxNumLines = profile:getNumber("textMaxNumLines", self.textMaxNumLines) |
300 | self.textAutoWidth = profile:getBool("textAutoWidth", self.textAutoWidth) |
301 | self.textMinSize = unpack(GuiUtils.getNormalizedValues(profile:getValue("textMinSize"), {self.outputSize[2]}, {self.textMinSize})) |
302 | |
303 | self.defaultTextSize = self.textSize |
304 | self.defaultText2Size = self.text2Size |
305 | |
306 | local wrapModeKey = profile:getValue("textLayoutMode") |
307 | if wrapModeKey ~= nil then |
308 | wrapModeKey = wrapModeKey:lower() |
309 | if wrapModeKey == "truncate" then |
310 | self.textLayoutMode = TextElement.LAYOUT_MODE.TRUNCATE |
311 | elseif wrapModeKey == "resize" then |
312 | self.textLayoutMode = TextElement.LAYOUT_MODE.RESIZE |
313 | elseif wrapModeKey == "overflow" then |
314 | self.textLayoutMode = TextElement.LAYOUT_MODE.OVERFLOW |
315 | end |
316 | end |
317 | |
318 | self.ignoreDisabled = profile:getBool("ignoreDisabled", self.ignoreDisabled) |
319 | |
320 | local textAlignment = profile:getValue("textAlignment") |
321 | if textAlignment ~= nil then |
322 | textAlignment = textAlignment:lower() |
323 | if textAlignment == "right" then |
324 | self.textAlignment = RenderText.ALIGN_RIGHT |
325 | elseif textAlignment == "center" then |
326 | self.textAlignment = RenderText.ALIGN_CENTER |
327 | else |
328 | self.textAlignment = RenderText.ALIGN_LEFT |
329 | end |
330 | end |
331 | |
332 | local textVerticalAlignment = profile:getValue("textVerticalAlignment", "") |
333 | local verticalAlignKey = string.upper(textVerticalAlignment) |
334 | self.textVerticalAlignment = TextElement.VERTICAL_ALIGNMENT[verticalAlignKey] or self.textVerticalAlignment |
335 | |
336 | self.formatDecimalPlaces = math.max(profile:getNumber("formatDecimalPlaces", self.formatDecimalPlaces), 0) |
337 | local format = profile:getValue("format") |
338 | |
339 | if format ~= nil then |
340 | format = format:lower() |
341 | local f = TextElement.FORMAT.NONE |
342 | if format == "currency" then |
343 | f = TextElement.FORMAT.CURRENCY |
344 | elseif format == "accounting" then |
345 | f = TextElement.FORMAT.ACCOUNTING |
346 | elseif format == "temperature" then |
347 | f = TextElement.FORMAT.TEMPERATURE |
348 | elseif format == "number" then |
349 | f = TextElement.FORMAT.NUMBER |
350 | elseif format == "percentage" then |
351 | f = TextElement.FORMAT.PERCENTAGE |
352 | elseif format == "none" then |
353 | f = TextElement.FORMAT.NONE |
354 | end |
355 | self:setFormat(f) |
356 | end |
357 | |
358 | if applyProfile then |
359 | self:applyTextAspectScale() |
360 | self:updateSize() |
361 | end |
362 | end |
81 | function TextElement.new(target, custom_mt) |
82 | if custom_mt == nil then |
83 | custom_mt = TextElement_mt |
84 | end |
85 | local self = GuiElement.new(target, custom_mt) |
86 | |
87 | self.textColor = {1, 1, 1, 1} |
88 | self.textDisabledColor = {0.5, 0.5, 0.5, 1} |
89 | self.textSelectedColor = {1, 1, 1, 1} |
90 | self.textHighlightedColor = {1, 1, 1, 1} |
91 | self.textOffset = {0,0} |
92 | self.textFocusedOffset = {0,0} |
93 | self.textSize = 0.03 |
94 | self.textBold = false |
95 | self.textSelectedBold = false |
96 | self.textHighlightedBold = false |
97 | self.text2Color = {1, 1, 1, 1} |
98 | self.text2DisabledColor = {1, 1, 1, 0} |
99 | self.text2SelectedColor = {0, 0, 0, 0.5} |
100 | self.text2HighlightedColor = {0, 0, 0, 0.5} |
101 | self.text2Offset = {0,0} |
102 | self.text2FocusedOffset = {0,0} |
103 | self.text2Size = 0 |
104 | self.text2Bold = false |
105 | self.text2SelectedBold = false |
106 | self.text2HighlightedBold = false |
107 | self.textUpperCase = false |
108 | self.textLinesPerPage = 0 |
109 | self.currentPage = 1 |
110 | self.defaultTextSize = self.textSize |
111 | self.defaultText2Size = self.text2Size |
112 | self.textLineHeightScale = RenderText.DEFAULT_LINE_HEIGHT_SCALE |
113 | self.text = "" |
114 | self.textAlignment = RenderText.ALIGN_CENTER |
115 | self.textVerticalAlignment = TextElement.VERTICAL_ALIGNMENT.MIDDLE |
116 | self.ignoreDisabled = false |
117 | |
118 | self.format = TextElement.FORMAT.NONE |
119 | self.locaKey = nil |
120 | self.value = nil |
121 | self.formatDecimalPlaces = 0 |
122 | |
123 | self.textMaxWidth = nil -- no limit, thus size limited |
124 | self.textMinWidth = 0 |
125 | self.textMaxNumLines = 1 -- just one line by default |
126 | self.textAutoWidth = false -- no auto sizing of the element |
127 | self.textLayoutMode = TextElement.LAYOUT_MODE.TRUNCATE -- hide any overflow |
128 | self.textMinSize = 0.01 |
129 | |
130 | self.sourceText = "" |
131 | |
132 | return self |
133 | end |
496 | function TextElement:setTextInternal(text, forceTextSize, skipCallback, doNotUpdateSize) |
497 | if text == nil then |
498 | text = "" |
499 | end |
500 | |
501 | text = tostring(text) |
502 | |
503 | -- Save the original text in case any text properties change |
504 | local textHasChanged = self.sourceText ~= text |
505 | if self.textUpperCase then |
506 | text = utf8ToUpper(text) |
507 | end |
508 | self.sourceText = text |
509 | |
510 | -- Reset to original size before resizing again |
511 | self.textSize = self.defaultTextSize |
512 | self.text2Size = self.defaultText2Size |
513 | |
514 | self:updateSize() |
515 | |
516 | local maxWidth = self.absSize[1] |
517 | if self.textMaxWidth ~= nil then |
518 | maxWidth = self.textMaxWidth |
519 | elseif self.textAutoWidth then |
520 | maxWidth = 1 |
521 | end |
522 | |
523 | local limitVerticalLines = false |
524 | |
525 | setTextBold(self.textBold) |
526 | |
527 | if self.textLayoutMode == TextElement.LAYOUT_MODE.RESIZE then |
528 | -- Wrap at max length |
529 | setTextWrapWidth(maxWidth) |
530 | |
531 | local textMaxNumLines = self.textMaxNumLines |
532 | -- Breaking is not possible on spaces |
533 | if text:find("[ -]") == nil then |
534 | textMaxNumLines = 1 |
535 | end |
536 | |
537 | -- Then find how long the max line would be |
538 | local lengthWithNoLineLimit = getTextLength(self.textSize, text, 99999) |
539 | |
540 | -- We want to fully fit inside, so our final length must be <= our max length |
541 | while getTextLength(self.textSize, text, textMaxNumLines) < lengthWithNoLineLimit do |
542 | self.textSize = self.textSize - self.defaultTextSize * 0.05 |
543 | self.text2Size = self.text2Size - self.defaultText2Size * 0.05 |
544 | |
545 | -- Limit size. Cut off any extra text |
546 | if self.textSize <= self.textMinSize then |
547 | -- Undo |
548 | self.textSize = self.textSize + self.defaultTextSize * 0.05 |
549 | self.text2Size = self.text2Size + self.defaultText2Size * 0.05 |
550 | |
551 | if textMaxNumLines == 1 then |
552 | text = Utils.limitTextToWidth(text, self.textSize, maxWidth, false, "...") |
553 | else |
554 | limitVerticalLines = true |
555 | end |
556 | |
557 | break |
558 | end |
559 | end |
560 | |
561 | setTextWrapWidth(0) |
562 | elseif self.textLayoutMode == TextElement.LAYOUT_MODE.OVERFLOW then -- luacheck: ignore |
563 | -- Note: with textMaxNumLines set, it overflows vertically instead of horizontally |
564 | elseif self.textLayoutMode == TextElement.LAYOUT_MODE.TRUNCATE then |
565 | -- We need to find a fitting amount of text for the width and max num lines |
566 | -- Engine tools don't provide max num lines so we do it by hand |
567 | |
568 | -- Fast case: use engine |
569 | if self.textMaxNumLines == 1 then |
570 | text = Utils.limitTextToWidth(text, self.textSize, maxWidth, false, "...") |
571 | else |
572 | limitVerticalLines = true |
573 | end |
574 | end |
575 | |
576 | if limitVerticalLines then |
577 | -- for limiting vertically with num lines and wrap |
578 | -- local leftover = nil |
579 | -- if self.textMaxNumLines ~= nil and self.textWrapWidth ~= nil then |
580 | -- setTextWrapWidth(self.textWrapWidth) |
581 | -- local l = getTextLength(self.textSize, text, self.textMaxNumLines) |
582 | -- setTextWrapWidth(0) |
583 | -- leftover = utf8Substr(text, l) |
584 | -- text = utf8Substr(text, 0, l) |
585 | -- end |
586 | -- Check for too-many first |
587 | setTextWrapWidth(maxWidth) |
588 | local _, numLines = getTextHeight(self.textSize, text) |
589 | |
590 | if numLines > self.textMaxNumLines then |
591 | while true do |
592 | _, numLines = getTextHeight(self.textSize, text) |
593 | if numLines > self.textMaxNumLines then |
594 | text = utf8Substr(text, 0, utf8Strlen(text) - 1) |
595 | else |
596 | break |
597 | end |
598 | end |
599 | |
600 | -- Add ellipsis |
601 | text = utf8Substr(text, 0, math.max(utf8Strlen(text) - 3, 0)) .. "..." |
602 | end |
603 | |
604 | setTextWrapWidth(0) |
605 | end |
606 | |
607 | setTextBold(false) |
608 | |
609 | self.text = text |
610 | |
611 | if textHasChanged and not skipCallback then |
612 | self:raiseCallback("onTextChangedCallback", self, self.text) |
613 | self:updateScaledWidth(1, 1) |
614 | end |
615 | |
616 | self:updateSize(forceTextSize) |
617 | |
618 | return "" |
619 | end |
1013 | function TextElement:updateSize(forceTextSize) |
1014 | -- Only update when auto width is enabled and max lines is 1 |
1015 | if (not self.textAutoWidth or self.textMaxNumLines ~= 1) and forceTextSize ~= true then |
1016 | return |
1017 | end |
1018 | |
1019 | local offset = self:getTextOffset() |
1020 | |
1021 | -- We need to manually apply aspect scaling because element size is not updated |
1022 | -- This is a bit of a hack... the aspect scaling at all is quite a hack. |
1023 | local textSize = self.textSize |
1024 | local xScale, yScale = self:getAspectScale() |
1025 | -- if not self.didApplyAspectScaling then |
1026 | -- textSize = textSize * yScale |
1027 | -- end |
1028 | |
1029 | -- Get width using the source text, as the element is supposed to fit all text (as |
1030 | -- textAutoWidth is enabled and max lines is 1) |
1031 | setTextBold(self.textBold) |
1032 | local textWidth = getTextWidth(textSize, self.sourceText) |
1033 | setTextBold(false) |
1034 | |
1035 | -- Limit element to max width |
1036 | if self.textMaxWidth ~= nil then |
1037 | textWidth = math.min(self.textMaxWidth, textWidth) |
1038 | end |
1039 | if self.textMinWidth ~= nil then |
1040 | textWidth = math.max(self.textMinWidth, textWidth) |
1041 | end |
1042 | |
1043 | local width = offset + textWidth |
1044 | if width ~= self.size[1] then |
1045 | local height = nil |
1046 | if self.size[2] == 0 then |
1047 | height = self.textSize |
1048 | end |
1049 | self:setSize(width, height) |
1050 | |
1051 | -- We need to manually apply aspect scaling because element size is not updated |
1052 | -- This is a bit of a hack... the aspect scaling at all is quite a hack. |
1053 | if not self.didApplyAspectScaling then |
1054 | local xScale, yScale = self:getAspectScale() |
1055 | width = width / xScale |
1056 | end |
1057 | |
1058 | self:setSize(width, height) -- do not overwrite height |
1059 | |
1060 | if self.parent ~= nil and self.parent.invalidateLayout ~= nil and self.parent.autoValidateLayout then |
1061 | self.parent:invalidateLayout() |
1062 | end |
1063 | end |
1064 | end |