libgui 3.00
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
GuiOptionBrowser.cpp
1/****************************************************************************
2 * libgui
3 *
4 * Daryl Borth 2009-2026
5 * GuiOptionBrowser.cpp
6 ***************************************************************************/
7
8#include "libgui/Gui.h"
9#include "GuiOptionBrowser.h"
10#include "../filelist.h"
11
12GuiOptionBrowser::GuiOptionBrowser(int w, int h, OptionList * l)
13{
14 width = w;
15 height = h;
16 options = l;
17 selectable = true;
18 listOffset = this->findMenuItem(-1, 1);
19 listChanged = true; // trigger an initial list update
20 selectedItem = 0;
21 focus = 0; // allow focus
22
23 trigA = new GuiTrigger;
24 trigA->setPrimaryTrigger();
25
26 bgOptions = new GuiImageData(bg_options_png);
27 bgOptionsImg = new GuiImage(bgOptions);
28 bgOptionsImg->setParent(this);
29 bgOptionsImg->setAlignment(ALIGN_H::LEFT, ALIGN_V::MIDDLE);
30
31 bgOptionsEntry = new GuiImageData(bg_options_entry_png);
32
33 scrollbar = new GuiImageData(scrollbar_png);
34 scrollbarImg = new GuiImage(scrollbar);
35 scrollbarImg->setParent(this);
36 scrollbarImg->setAlignment(ALIGN_H::RIGHT, ALIGN_V::TOP);
37 scrollbarImg->setPosition(0, 30);
38
39 arrowDown = new GuiImageData(scrollbar_arrowdown_png);
40 arrowDownImg = new GuiImage(arrowDown);
41 arrowDownOver = new GuiImageData(scrollbar_arrowdown_over_png);
42 arrowDownOverImg = new GuiImage(arrowDownOver);
43 arrowUp = new GuiImageData(scrollbar_arrowup_png);
44 arrowUpImg = new GuiImage(arrowUp);
45 arrowUpOver = new GuiImageData(scrollbar_arrowup_over_png);
46 arrowUpOverImg = new GuiImage(arrowUpOver);
47
48 arrowUpBtn = new GuiButton(arrowUpImg->getWidth(), arrowUpImg->getHeight());
49 arrowUpBtn->setParent(this);
50 arrowUpBtn->setImage(arrowUpImg);
51 arrowUpBtn->setImageOver(arrowUpOverImg);
52 arrowUpBtn->setAlignment(ALIGN_H::RIGHT, ALIGN_V::TOP);
53 arrowUpBtn->setSelectable(false);
54 arrowUpBtn->setTrigger(trigA);
55
56 arrowDownBtn = new GuiButton(arrowDownImg->getWidth(), arrowDownImg->getHeight());
57 arrowDownBtn->setParent(this);
58 arrowDownBtn->setImage(arrowDownImg);
59 arrowDownBtn->setImageOver(arrowDownOverImg);
60 arrowDownBtn->setAlignment(ALIGN_H::RIGHT, ALIGN_V::BOTTOM);
61 arrowDownBtn->setSelectable(false);
62 arrowDownBtn->setTrigger(trigA);
63
64 for(int i=0; i<OPTION_PAGESIZE; i++)
65 {
66 optionTxt[i] = new GuiText(nullptr, 18, (PixelColor){0, 0, 0, 0xff});
67 optionTxt[i]->setAlignment(ALIGN_H::LEFT, ALIGN_V::MIDDLE);
68 optionTxt[i]->setPosition(8,4);
69 optionTxt[i]->setMaxWidth(235);
70
71 optionVal[i] = new GuiText(nullptr, 18, (PixelColor){0, 0, 0, 0xff});
72 optionVal[i]->setAlignment(ALIGN_H::LEFT, ALIGN_V::MIDDLE);
73 optionVal[i]->setPosition(250,4);
74 optionVal[i]->setMaxWidth(324);
75
76 optionBg[i] = new GuiImage(bgOptionsEntry);
77
78 optionIndex[i] = -1; // no option until the first update fills the row
79 optionBtn[i] = new GuiButton(512,30);
80 optionBtn[i]->setParent(this);
81 optionBtn[i]->setLabel(optionTxt[i], 0);
82 optionBtn[i]->setLabel(optionVal[i], 1);
83 optionBtn[i]->setImageOver(optionBg[i]);
84 optionBtn[i]->setPosition(2,30*i+3);
85 optionBtn[i]->setTrigger(trigA);
86 optionBtn[i]->setSoundOver(nullptr);
87 }
88}
89
90GuiOptionBrowser::~GuiOptionBrowser()
91{
92 delete arrowUpBtn;
93 delete arrowDownBtn;
94
95 delete bgOptionsImg;
96 delete scrollbarImg;
97 delete arrowDownImg;
98 delete arrowDownOverImg;
99 delete arrowUpImg;
100 delete arrowUpOverImg;
101
102 delete bgOptions;
103 delete bgOptionsEntry;
104 delete scrollbar;
105 delete arrowDown;
106 delete arrowDownOver;
107 delete arrowUp;
108 delete arrowUpOver;
109
110 delete trigA;
111
112 for(int i=0; i<OPTION_PAGESIZE; i++)
113 {
114 delete optionTxt[i];
115 delete optionVal[i];
116 delete optionBg[i];
117 delete optionBtn[i];
118 }
119}
120
122{
123 for(int i=0; i<OPTION_PAGESIZE; i++)
124 optionTxt[i]->setPosition(x,0);
125}
126
128{
129 for(int i=0; i<OPTION_PAGESIZE; i++)
130 optionVal[i]->setPosition(x,4);
131}
132
134{
135 focus = f;
136
137 for(int i=0; i<OPTION_PAGESIZE; i++)
138 optionBtn[i]->resetState();
139
140 if(f == 1)
141 optionBtn[selectedItem]->setState(STATE::SELECTED);
142}
143
145{
146 if(state != STATE::DISABLED)
147 {
148 state = STATE::DEFAULT;
149 stateChan = -1;
150 }
151
152 for(int i=0; i<OPTION_PAGESIZE; i++)
153 {
154 optionBtn[i]->resetState();
155 }
156}
157
159{
160 int found = -1;
161 for(int i=0; i<OPTION_PAGESIZE; i++)
162 {
163 if(optionBtn[i]->getState() == STATE::CLICKED)
164 {
165 optionBtn[i]->setState(STATE::SELECTED);
166 found = optionIndex[i];
167 break;
168 }
169 }
170 return found;
171}
172
173/****************************************************************************
174 * FindMenuItem
175 *
176 * Help function to find the next visible menu item on the list
177 ***************************************************************************/
178
179int GuiOptionBrowser::findMenuItem(int currentItem, int direction)
180{
181 int nextItem = currentItem + direction;
182
183 if(nextItem < 0 || nextItem >= options->length)
184 return -1;
185
186 if(strlen(options->name[nextItem]) > 0)
187 return nextItem;
188 else
189 return findMenuItem(nextItem, direction);
190}
191
196{
197 if(!this->isVisible())
198 return;
199
200 bgOptionsImg->draw();
201
202 int next = listOffset;
203
204 for(int i=0; i<OPTION_PAGESIZE; ++i)
205 {
206 if(next >= 0)
207 {
208 optionBtn[i]->draw();
209 next = this->findMenuItem(next, 1);
210 }
211 else
212 break;
213 }
214
215 scrollbarImg->draw();
216 arrowUpBtn->draw();
217 arrowDownBtn->draw();
218
219 this->updateEffects();
220}
221
223{
224 listChanged = true;
225}
226
228{
229 int next = listOffset;
230
231 for(int i=0; i<OPTION_PAGESIZE; i++)
232 {
233 if(next >= 0)
234 {
235 optionBtn[i]->resetText();
236 next = this->findMenuItem(next, 1);
237 }
238 else
239 break;
240 }
241}
242
244{
245 if(state == STATE::DISABLED || !controller)
246 return;
247
248 int next, prev;
249
250 arrowUpBtn->update(controller);
251 arrowDownBtn->update(controller);
252
253 next = listOffset;
254
255 if(listChanged)
256 {
257 listChanged = false;
258 for(int i=0; i<OPTION_PAGESIZE; ++i)
259 {
260 if(next >= 0)
261 {
262 if(optionBtn[i]->getState() == STATE::DISABLED)
263 {
264 optionBtn[i]->setVisible(true);
265 optionBtn[i]->setState(STATE::DEFAULT);
266 }
267
268 optionTxt[i]->setText(options->name[next]);
269 optionVal[i]->setText(options->value[next]);
270 optionIndex[i] = next;
271 next = this->findMenuItem(next, 1);
272 }
273 else
274 {
275 optionBtn[i]->setVisible(false);
276 optionBtn[i]->setState(STATE::DISABLED);
277 }
278 }
279 }
280
281 auto pad = controller->getPadData();
282 int currentChan = controller->getChannel();
283 // A InputController with no live signal this frame (e.g. a persistent
284 // but currently-disconnected controller slot) must not be able to claim
285 // or evict a selection just by taking its turn through this loop -- only
286 // a channel that's genuinely doing something (pointing or pressing) gets
287 // a say in who owns the selected slot.
288 bool channelActive = pad.validPointer || pad.buttons_d != 0 || pad.buttons_h != 0;
289
290 for(int i=0; i<OPTION_PAGESIZE; ++i)
291 {
292 if(i != selectedItem && optionBtn[i]->getState() == STATE::SELECTED)
293 optionBtn[i]->resetState();
294 else if(focus && channelActive && i == selectedItem && optionBtn[i]->getState() == STATE::DEFAULT)
295 optionBtn[selectedItem]->setState(STATE::SELECTED, currentChan);
296 else if(focus && channelActive && i == selectedItem && optionBtn[i]->getState() == STATE::SELECTED &&
297 optionBtn[i]->getStateChan() != -1 && optionBtn[i]->getStateChan() != currentChan)
298 // Slot is already SELECTED but carries a stale channel from an earlier
299 // selection (e.g. a reused slot after paging, or a preselected item that
300 // was never actually hovered by the real channel yet). Without this,
301 // currentChan == stateChan never holds and clicks are silently ignored
302 // until the item happens to be navigated away from and back.
303 optionBtn[i]->resetState();
304
305 // Present a "no channel" (-1) identity to any item the cursor isn't
306 // currently over. Without this, a stale stateChan left on a reused
307 // list slot (e.g. after paging/navigating) can permanently block
308 // clicks from the real channel until the item is re-hovered.
309 if(pad.validPointer && !optionBtn[i]->isInside(pad.cursor_x, pad.cursor_y))
310 controller->setChannel(-1);
311
312 optionBtn[i]->update(controller);
313 controller->setChannel(currentChan);
314
315 if(optionBtn[i]->getState() == STATE::SELECTED)
316 selectedItem = i;
317
318 if(selectedItem == i) {
319 optionTxt[i]->setScroll(SCROLL::HORIZONTAL);
320 optionVal[i]->setScroll(SCROLL::HORIZONTAL);
321 }
322 else
323 {
324 optionTxt[i]->setScroll(SCROLL::NONE);
325 optionVal[i]->setScroll(SCROLL::NONE);
326 }
327 }
328
329 // pad/joystick navigation
330 if(!focus)
331 return; // skip navigation
332
333 if(controller->down() || arrowDownBtn->getState() == STATE::CLICKED)
334 {
335 next = this->findMenuItem(optionIndex[selectedItem], 1);
336
337 if(next >= 0)
338 {
339 if(selectedItem == OPTION_PAGESIZE-1)
340 {
341 // move list down by 1
342 listOffset = this->findMenuItem(listOffset, 1);
343 listChanged = true;
344 }
345 else if(optionBtn[selectedItem+1]->isVisible())
346 {
347 optionBtn[selectedItem]->resetState();
348 optionBtn[selectedItem+1]->setState(STATE::SELECTED, controller->getChannel());
349 ++selectedItem;
350 }
351 }
352 arrowDownBtn->resetState();
353 }
354 else if(controller->up() || arrowUpBtn->getState() == STATE::CLICKED)
355 {
356 prev = this->findMenuItem(optionIndex[selectedItem], -1);
357
358 if(prev >= 0)
359 {
360 if(selectedItem == 0)
361 {
362 // move list up by 1
363 listOffset = prev;
364 listChanged = true;
365 }
366 else
367 {
368 optionBtn[selectedItem]->resetState();
369 optionBtn[selectedItem-1]->setState(STATE::SELECTED, controller->getChannel());
370 --selectedItem;
371 }
372 }
373 arrowUpBtn->resetState();
374 }
375
376 if(updateCB)
377 updateCB(this);
378}
Umbrella header for the platform-agnostic libgui core.
Display, manage, and manipulate buttons in the GUI.
Definition GuiButton.h:13
bool isVisible()
Checks whether or not the element is visible.
int getStateChan()
Gets the controller channel that last changed the element's state.
bool selectable
Whether or not this element selectable (can change to SELECTED state)
Definition GuiElement.h:298
STATE getState()
Gets the element's current state.
int stateChan
Which controller channel is responsible for the last change in state.
Definition GuiElement.h:297
int width
Element width.
Definition GuiElement.h:273
UpdateCallback updateCB
Callback function to call when this element is updated.
Definition GuiElement.h:269
int height
Element height.
Definition GuiElement.h:274
void updateEffects()
Updates the element's effects (dynamic values) Called by Draw(), used for animation purposes.
int focus
Element focus (-1 = focus disabled, 0 = not focused, 1 = focused)
Definition GuiElement.h:272
STATE state
Element state (DEFAULT, SELECTED, CLICKED, DISABLED)
Definition GuiElement.h:296
void setPosition(int x, int y)
Sets the element's position.
Decodes compressed image data (PNG) into a platform-native texture created from it.
Display, manage, and manipulate images in the GUI.
Definition GuiImage.h:18
int findMenuItem(int currentItem, int direction)
Finds the next non-blank option index from currentItem, skipping blank-named entries used as separato...
void resetState()
Resets the element's state to STATE::DEFAULT.
GuiOptionBrowser(int w, int h, OptionList *l)
void setCol1Position(int x)
Sets the x offset of the name column.
void draw() override
Draw the button on screen.
void resetText()
Called when the language has changed, to obtain new text values for all text elements.
void setFocus(int f)
Sets the element's focus.
void update(InputController *c)
Called constantly to allow the element to respond to the current input data.
GuiText * optionVal[OPTION_PAGESIZE]
One value label per visible row of the current page.
void setCol2Position(int x)
Sets the x offset of the value column.
void triggerUpdate()
Forces the visible page to refresh from the underlying options list.
Display, manage, and manipulate text in the GUI.
Definition GuiText.h:10
void setAlignment(ALIGN_H hor, ALIGN_V vert)
Sets the text alignment.
Definition GuiText.cpp:307
Input-condition matcher attached to a GuiElement (via setSimpleTrigger() etc.) and evaluated against ...
Definition GuiTrigger.h:34
void setPrimaryTrigger(int ch=-1)
Semantic Triggers.
Logical controller for one input channel.
8-bit-per-channel RGBA color used by the video interfaces.
Definition Mesh.h:16