GuiElement#wrap | bool [optional] If false, values will not cycle when the end or start of the value list is reached. |
GuiElement#buttonRLChange | bool [optional] If true, requires controller shoulder buttons as input to change the value. |
GuiElement#texts | string [optional] Selectable options, defaults to empty list. Format: "[text1]|[text2]|[text3]". If the prefix "$l10n_" is used in an option text, it is resolved as a variable by the localization system to the corresponding text in the current language. |
GuiElement#onClick | callback [optional] onClick(index, element) Called when one of the change buttons is clicked / activated. Receives the current option index and this element. |
GuiElement#onFocus | callback [optional] onFocus(element) Called when this element receives focus. Receives this element as an argument. |
GuiElement#onLeave | callback [optional] onLeave(element) Called when this element loses focus. Receives this element as an argument. |
157 | function MultiTextOptionElement:addElement(element) |
158 | MultiTextOptionElement:superClass().addElement(self, element) |
159 | |
160 | if element.name == "ignore" then |
161 | -- do nothing with this element |
162 | return |
163 | end |
164 | |
165 | if self.namedComponents then |
166 | if element.name == "left" then |
167 | -- left |
168 | self.leftButtonElement = element |
169 | self.leftButtonElement.forceHighlight = true |
170 | element:setHandleFocus(false) |
171 | element.target = self |
172 | element:setCallback("onClickCallback", "onLeftButtonClicked") |
173 | self:setDisabled(self.disabled) |
174 | elseif element.name == "right" then |
175 | -- right button |
176 | self.rightButtonElement = element |
177 | self.rightButtonElement.forceHighlight = true |
178 | element.target = self |
179 | element:setHandleFocus(false) |
180 | element:setCallback("onClickCallback", "onRightButtonClicked") |
181 | self:setDisabled(self.disabled) |
182 | elseif element.name == "label" then |
183 | self.labelElement = element |
184 | elseif element.name == "text" then |
185 | if element:isa(TextElement) then |
186 | self.textElement = element |
187 | self:updateContentElement() |
188 | end |
189 | elseif element.name == "icon" then |
190 | self.iconElement = element |
191 | self:updateContentElement() |
192 | elseif element.name == "gradient" then |
193 | if element:isa(BitmapElement) then |
194 | table.insert(self.gradientElements, element) |
195 | end |
196 | end |
197 | |
198 | else |
199 | -- TODO: this is super easy to get wrong when writing configurations from scratch. Make it smarter, e.g. using names to resolve collaborators. |
200 | local numElements = #self.elements |
201 | if numElements == 1 then |
202 | -- left |
203 | self.leftButtonElement = element |
204 | self.leftButtonElement.forceHighlight = true |
205 | element:setHandleFocus(false) |
206 | element.target = self |
207 | element:setCallback("onClickCallback", "onLeftButtonClicked") |
208 | self:setDisabled(self.disabled) |
209 | elseif numElements == 2 then |
210 | -- right button |
211 | self.rightButtonElement = element |
212 | self.rightButtonElement.forceHighlight = true |
213 | element.target = self |
214 | element:setHandleFocus(false) |
215 | element:setCallback("onClickCallback", "onRightButtonClicked") |
216 | self:setDisabled(self.disabled) |
217 | elseif numElements == 3 then |
218 | if element:isa(TextElement) then |
219 | self.textElement = element |
220 | self:updateContentElement() |
221 | else |
222 | self.iconElement = element |
223 | self:updateContentElement() |
224 | end |
225 | elseif numElements == 4 then |
226 | self.labelElement = element |
227 | elseif numElements == 5 then |
228 | -- alternative text element |
229 | if element:isa(TextElement) then |
230 | self.textElement = element |
231 | self:updateContentElement() |
232 | end |
233 | elseif numElements == 6 or numElements == 7 then |
234 | if self.textElement ~= nil and self.textElement ~= nil then |
235 | if element:isa(BitmapElement) then |
236 | table.insert(self.gradientElements, element) |
237 | end |
238 | end |
239 | end |
240 | end |
241 | end |
111 | function MultiTextOptionElement:copyAttributes(src) |
112 | MultiTextOptionElement:superClass().copyAttributes(self, src) |
113 | |
114 | self.isChecked = src.isChecked |
115 | self.buttonLRChange = src.buttonLRChange |
116 | self.state = src.state |
117 | self.wrap = src.wrap |
118 | self.scrollDelayDuration = src.scrollDelayDuration |
119 | self.canChangeState = src.canChangeState |
120 | self.namedComponents = src.namedComponents |
121 | |
122 | self.onClickCallback = src.onClickCallback |
123 | self.onLeaveCallback = src.onLeaveCallback |
124 | self.onFocusCallback = src.onFocusCallback |
125 | for _, text in pairs(src.texts) do |
126 | self:addText(text) |
127 | end |
128 | |
129 | GuiMixin.cloneMixin(IndexChangeSubjectMixin, src, self) |
130 | GuiMixin.cloneMixin(PlaySampleMixin, src, self) |
131 | end |
64 | function MultiTextOptionElement:loadFromXML(xmlFile, key) |
65 | MultiTextOptionElement:superClass().loadFromXML(self, xmlFile, key) |
66 | |
67 | self:addCallback(xmlFile, key.."#onClick", "onClickCallback") |
68 | self:addCallback(xmlFile, key.."#onFocus", "onFocusCallback") |
69 | self:addCallback(xmlFile, key.."#onLeave", "onLeaveCallback") |
70 | |
71 | self.wrap = Utils.getNoNil(getXMLBool(xmlFile, key.."#wrap"), self.wrap) |
72 | self.buttonLRChange = Utils.getNoNil(getXMLBool(xmlFile, key.."#buttonLRChange"), self.buttonLRChange) |
73 | self.scrollDelayDuration = Utils.getNoNil(getXMLInt(xmlFile, key .. "#scrollDelayDuration"), self.scrollDelayDuration) |
74 | self.namedComponents = Utils.getNoNil(getXMLBool(xmlFile, key .. "#namedComponents"), self.namedComponents) |
75 | |
76 | local text = getXMLString(xmlFile, key.."#texts") |
77 | if text ~= nil then |
78 | local texts = text:split("|") |
79 | for _, textPart in pairs(texts) do |
80 | if textPart:sub(1,6) == "$l10n_" then |
81 | textPart = g_i18n:getText(textPart:sub(7)) |
82 | end |
83 | table.insert(self.texts, textPart) |
84 | end |
85 | end |
86 | end |
90 | function MultiTextOptionElement:loadProfile(profile, applyProfile) |
91 | MultiTextOptionElement:superClass().loadProfile(self, profile, applyProfile) |
92 | |
93 | self.wrap = profile:getBool("wrap", self.wrap) |
94 | self.buttonLRChange = profile:getBool("buttonLRChange", self.buttonLRChange) |
95 | self.scrollDelayDuration = profile:getValue("scrollDelayDuration", self.scrollDelayDuration) |
96 | |
97 | local text = profile:getValue("texts") |
98 | if text ~= nil then |
99 | local texts = text:split("|") |
100 | for _, textPart in pairs(texts) do |
101 | if textPart:sub(1,6) == "$l10n_" then |
102 | textPart = g_i18n:getText(textPart:sub(7)) |
103 | end |
104 | table.insert(self.texts, textPart) |
105 | end |
106 | end |
107 | end |
296 | function MultiTextOptionElement:mouseEvent(posX, posY, isDown, isUp, button, eventUsed) |
297 | if self:getIsActive() then |
298 | if MultiTextOptionElement:superClass().mouseEvent(self, posX, posY, isDown, isUp, button, eventUsed) then |
299 | eventUsed = true |
300 | end |
301 | |
302 | if not eventUsed and not self.forceHighlight and GuiUtils.checkOverlayOverlap(posX, posY, self.absPosition[1], self.absPosition[2], self.absSize[1], self.absSize[2], nil) then |
303 | if not self.mouseEntered and not self.focusActive then |
304 | FocusManager:setHighlight(self) |
305 | self.mouseEntered = true |
306 | end |
307 | else |
308 | if self.mouseEntered and not self.focusActive then |
309 | FocusManager:unsetHighlight(self) |
310 | self.mouseEntered = false |
311 | end |
312 | end |
313 | end |
314 | return eventUsed |
315 | end |
29 | function MultiTextOptionElement.new(target, custom_mt) |
30 | local self = GuiElement.new(target, custom_mt or MultiTextOptionElement_mt) |
31 | self:include(IndexChangeSubjectMixin) -- add index change subject mixin for observers |
32 | self:include(PlaySampleMixin) -- add sound playing |
33 | |
34 | self.isChecked = false |
35 | self.mouseEntered = false |
36 | self.buttonLRChange = false |
37 | self.canChangeState = true |
38 | |
39 | self.state = 1 |
40 | self.wrap = true |
41 | self.texts = {} |
42 | |
43 | self.scrollDelayDuration = FocusManager.FIRST_LOCK |
44 | self.leftDelayTime = 0 |
45 | self.rightDelayTime = 0 |
46 | |
47 | self.forceHighlight = false |
48 | |
49 | self.leftButtonElement = nil |
50 | self.rightButtonElement = nil |
51 | self.textElement = nil |
52 | self.labelElement = nil |
53 | self.iconElement = nil |
54 | |
55 | self.namedComponents = false |
56 | |
57 | self.gradientElements = {} |
58 | |
59 | return self |
60 | end |
428 | function MultiTextOptionElement:onLeftButtonClicked(steps, noFocus) |
429 | if self:getCanChangeState() then |
430 | if steps == nil then steps = 1 end |
431 | if steps ~= nil and type(steps) ~= "number" then steps = 1 end |
432 | for _ = 1, steps do |
433 | if self.wrap then |
434 | self.state = self.state - 1 |
435 | if self.state < 1 then |
436 | self.state = table.getn(self.texts) |
437 | end |
438 | else |
439 | self.state = math.max(self.state-1, 1) |
440 | end |
441 | end |
442 | |
443 | self:playSample(GuiSoundPlayer.SOUND_SAMPLES.CLICK) |
444 | |
445 | self:setSoundSuppressed(true) |
446 | FocusManager:setFocus(self) |
447 | self:setSoundSuppressed(false) |
448 | |
449 | self:updateContentElement() |
450 | self:raiseClickCallback(true) |
451 | self:notifyIndexChange(self.state, #self.texts) |
452 | |
453 | if (noFocus == nil or not noFocus) then |
454 | if self.leftButtonElement ~= nil then |
455 | self.leftButtonElement:onFocusEnter() |
456 | end |
457 | if self.rightButtonElement ~= nil then |
458 | self.rightButtonElement:onFocusEnter() |
459 | end |
460 | end |
461 | end |
462 | end |
386 | function MultiTextOptionElement:onRightButtonClicked(steps, noFocus) |
387 | if self:getCanChangeState() then |
388 | if steps == nil then steps = 1 end |
389 | if steps ~= nil and type(steps) ~= "number" then steps = 1 end |
390 | for _ = 1, steps do |
391 | if self.wrap then |
392 | self.state = self.state + 1 |
393 | if self.state > #self.texts then |
394 | self.state = 1 |
395 | end |
396 | else |
397 | self.state = math.min(self.state+1, #self.texts) |
398 | end |
399 | end |
400 | |
401 | self:playSample(GuiSoundPlayer.SOUND_SAMPLES.CLICK) |
402 | |
403 | self:setSoundSuppressed(true) |
404 | FocusManager:setFocus(self) |
405 | self:setSoundSuppressed(false) |
406 | |
407 | self:updateContentElement() |
408 | self:raiseClickCallback(false) |
409 | self:notifyIndexChange(self.state, #self.texts) |
410 | |
411 | if (noFocus == nil or not noFocus) then |
412 | if self.leftButtonElement ~= nil then |
413 | self.leftButtonElement:onFocusEnter() |
414 | end |
415 | if self.rightButtonElement ~= nil then |
416 | self.rightButtonElement:onFocusEnter() |
417 | end |
418 | end |
419 | end |
420 | end |
541 | function MultiTextOptionElement:updateContentElement() |
542 | local value = self.texts[self.state] |
543 | local isFilename = value ~= nil and (value:lower():contains(".png") or value:lower():contains(".dds")) |
544 | |
545 | local useIcon = false |
546 | if self.iconElement ~= nil then |
547 | if value ~= nil then |
548 | if isFilename then |
549 | self.iconElement:setImageFilename(value) |
550 | self.iconElement:setVisible(true) |
551 | for i=1, #self.gradientElements do |
552 | self.gradientElements[i]:setVisible(false) |
553 | end |
554 | useIcon = true |
555 | end |
556 | end |
557 | |
558 | if not useIcon then |
559 | self.iconElement:setVisible(false) |
560 | for i=1, #self.gradientElements do |
561 | self.gradientElements[i]:setVisible(true) |
562 | end |
563 | end |
564 | end |
565 | |
566 | if self.textElement ~= nil then |
567 | if not useIcon and value ~= nil and not isFilename then |
568 | self.textElement:setText(value) |
569 | else |
570 | self.textElement:setText("") |
571 | end |
572 | end |
573 | end |