libgui 2.01
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 "Gui.h"
9
10GuiOptionBrowser::GuiOptionBrowser(int w, int h, OptionList * l)
11{
12 width = w;
13 height = h;
14 options = l;
15 selectable = true;
16 listOffset = this->findMenuItem(-1, 1);
17 listChanged = true; // trigger an initial list update
18 selectedItem = 0;
19 focus = 0; // allow focus
20
21 trigA = new GuiTrigger;
22 trigA->setPrimaryTrigger();
23
24 btnSoundOver = new GuiSound(button_over_pcm, button_over_pcm_size, SOUND::PCM);
25 btnSoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, SOUND::PCM);
26
27 bgOptions = new GuiImageData(bg_options_png);
28 bgOptionsImg = new GuiImage(bgOptions);
29 bgOptionsImg->setParent(this);
30 bgOptionsImg->setAlignment(ALIGN_H::LEFT, ALIGN_V::MIDDLE);
31
32 bgOptionsEntry = new GuiImageData(bg_options_entry_png);
33
34 scrollbar = new GuiImageData(scrollbar_png);
35 scrollbarImg = new GuiImage(scrollbar);
36 scrollbarImg->setParent(this);
37 scrollbarImg->setAlignment(ALIGN_H::RIGHT, ALIGN_V::TOP);
38 scrollbarImg->setPosition(0, 30);
39
40 arrowDown = new GuiImageData(scrollbar_arrowdown_png);
41 arrowDownImg = new GuiImage(arrowDown);
42 arrowDownOver = new GuiImageData(scrollbar_arrowdown_over_png);
43 arrowDownOverImg = new GuiImage(arrowDownOver);
44 arrowUp = new GuiImageData(scrollbar_arrowup_png);
45 arrowUpImg = new GuiImage(arrowUp);
46 arrowUpOver = new GuiImageData(scrollbar_arrowup_over_png);
47 arrowUpOverImg = new GuiImage(arrowUpOver);
48
49 arrowUpBtn = new GuiButton(arrowUpImg->getWidth(), arrowUpImg->getHeight());
50 arrowUpBtn->setParent(this);
51 arrowUpBtn->setImage(arrowUpImg);
52 arrowUpBtn->setImageOver(arrowUpOverImg);
53 arrowUpBtn->setAlignment(ALIGN_H::RIGHT, ALIGN_V::TOP);
54 arrowUpBtn->setSelectable(false);
55 arrowUpBtn->setTrigger(trigA);
56 arrowUpBtn->setSoundOver(btnSoundOver);
57 arrowUpBtn->setSoundClick(btnSoundClick);
58
59 arrowDownBtn = new GuiButton(arrowDownImg->getWidth(), arrowDownImg->getHeight());
60 arrowDownBtn->setParent(this);
61 arrowDownBtn->setImage(arrowDownImg);
62 arrowDownBtn->setImageOver(arrowDownOverImg);
63 arrowDownBtn->setAlignment(ALIGN_H::RIGHT, ALIGN_V::BOTTOM);
64 arrowDownBtn->setSelectable(false);
65 arrowDownBtn->setTrigger(trigA);
66 arrowDownBtn->setSoundOver(btnSoundOver);
67 arrowDownBtn->setSoundClick(btnSoundClick);
68
69 for(int i=0; i<OPTION_PAGESIZE; i++)
70 {
71 optionTxt[i] = new GuiText(nullptr, 18, (PixelColor){0, 0, 0, 0xff});
72 optionTxt[i]->setAlignment(ALIGN_H::LEFT, ALIGN_V::MIDDLE);
73 optionTxt[i]->setPosition(8,4);
74 optionTxt[i]->setMaxWidth(235);
75
76 optionVal[i] = new GuiText(nullptr, 18, (PixelColor){0, 0, 0, 0xff});
77 optionVal[i]->setAlignment(ALIGN_H::LEFT, ALIGN_V::MIDDLE);
78 optionVal[i]->setPosition(250,4);
79 optionVal[i]->setMaxWidth(324);
80
81 optionBg[i] = new GuiImage(bgOptionsEntry);
82
83 optionBtn[i] = new GuiButton(512,30);
84 optionBtn[i]->setParent(this);
85 optionBtn[i]->setLabel(optionTxt[i], 0);
86 optionBtn[i]->setLabel(optionVal[i], 1);
87 optionBtn[i]->setImageOver(optionBg[i]);
88 optionBtn[i]->setPosition(2,30*i+3);
89 optionBtn[i]->setTrigger(trigA);
90 optionBtn[i]->setSoundClick(btnSoundClick);
91 }
92}
93
94GuiOptionBrowser::~GuiOptionBrowser()
95{
96 delete arrowUpBtn;
97 delete arrowDownBtn;
98
99 delete bgOptionsImg;
100 delete scrollbarImg;
101 delete arrowDownImg;
102 delete arrowDownOverImg;
103 delete arrowUpImg;
104 delete arrowUpOverImg;
105
106 delete bgOptions;
107 delete bgOptionsEntry;
108 delete scrollbar;
109 delete arrowDown;
110 delete arrowDownOver;
111 delete arrowUp;
112 delete arrowUpOver;
113
114 delete trigA;
115 delete btnSoundOver;
116 delete btnSoundClick;
117
118 for(int i=0; i<OPTION_PAGESIZE; i++)
119 {
120 delete optionTxt[i];
121 delete optionVal[i];
122 delete optionBg[i];
123 delete optionBtn[i];
124 }
125}
126
128{
129 for(int i=0; i<OPTION_PAGESIZE; i++)
130 optionTxt[i]->setPosition(x,0);
131}
132
134{
135 for(int i=0; i<OPTION_PAGESIZE; i++)
136 optionVal[i]->setPosition(x,4);
137}
138
140{
141 focus = f;
142
143 for(int i=0; i<OPTION_PAGESIZE; i++)
144 optionBtn[i]->resetState();
145
146 if(f == 1)
147 optionBtn[selectedItem]->setState(STATE::SELECTED);
148}
149
151{
152 if(state != STATE::DISABLED)
153 {
154 state = STATE::DEFAULT;
155 stateChan = -1;
156 }
157
158 for(int i=0; i<OPTION_PAGESIZE; i++)
159 {
160 optionBtn[i]->resetState();
161 }
162}
163
165{
166 int found = -1;
167 for(int i=0; i<OPTION_PAGESIZE; i++)
168 {
169 if(optionBtn[i]->getState() == STATE::CLICKED)
170 {
171 optionBtn[i]->setState(STATE::SELECTED);
172 found = optionIndex[i];
173 break;
174 }
175 }
176 return found;
177}
178
179/****************************************************************************
180 * FindMenuItem
181 *
182 * Help function to find the next visible menu item on the list
183 ***************************************************************************/
184
185int GuiOptionBrowser::findMenuItem(int currentItem, int direction)
186{
187 int nextItem = currentItem + direction;
188
189 if(nextItem < 0 || nextItem >= options->length)
190 return -1;
191
192 if(strlen(options->name[nextItem]) > 0)
193 return nextItem;
194 else
195 return findMenuItem(nextItem, direction);
196}
197
202{
203 if(!this->isVisible())
204 return;
205
206 bgOptionsImg->draw();
207
208 int next = listOffset;
209
210 for(int i=0; i<OPTION_PAGESIZE; ++i)
211 {
212 if(next >= 0)
213 {
214 optionBtn[i]->draw();
215 next = this->findMenuItem(next, 1);
216 }
217 else
218 break;
219 }
220
221 scrollbarImg->draw();
222 arrowUpBtn->draw();
223 arrowDownBtn->draw();
224
225 this->updateEffects();
226}
227
229{
230 listChanged = true;
231}
232
234{
235 int next = listOffset;
236
237 for(int i=0; i<OPTION_PAGESIZE; i++)
238 {
239 if(next >= 0)
240 {
241 optionBtn[i]->resetText();
242 next = this->findMenuItem(next, 1);
243 }
244 else
245 break;
246 }
247}
248
250{
251 if(state == STATE::DISABLED || !controller)
252 return;
253
254 int next, prev;
255
256 arrowUpBtn->update(controller);
257 arrowDownBtn->update(controller);
258
259 next = listOffset;
260
261 if(listChanged)
262 {
263 listChanged = false;
264 for(int i=0; i<OPTION_PAGESIZE; ++i)
265 {
266 if(next >= 0)
267 {
268 if(optionBtn[i]->getState() == STATE::DISABLED)
269 {
270 optionBtn[i]->setVisible(true);
271 optionBtn[i]->setState(STATE::DEFAULT);
272 }
273
274 optionTxt[i]->setText(options->name[next]);
275 optionVal[i]->setText(options->value[next]);
276 optionIndex[i] = next;
277 next = this->findMenuItem(next, 1);
278 }
279 else
280 {
281 optionBtn[i]->setVisible(false);
282 optionBtn[i]->setState(STATE::DISABLED);
283 }
284 }
285 }
286
287 auto pad = controller->getPadData();
288 int currentChan = controller->getChannel();
289 // A InputController with no live signal this frame (e.g. a persistent
290 // but currently-disconnected controller slot) must not be able to claim
291 // or evict a selection just by taking its turn through this loop -- only
292 // a channel that's genuinely doing something (pointing or pressing) gets
293 // a say in who owns the selected slot.
294 bool channelActive = pad.validPointer || pad.buttons_d != 0 || pad.buttons_h != 0;
295
296 for(int i=0; i<OPTION_PAGESIZE; ++i)
297 {
298 if(i != selectedItem && optionBtn[i]->getState() == STATE::SELECTED)
299 optionBtn[i]->resetState();
300 else if(focus && channelActive && i == selectedItem && optionBtn[i]->getState() == STATE::DEFAULT)
301 optionBtn[selectedItem]->setState(STATE::SELECTED, currentChan);
302 else if(focus && channelActive && i == selectedItem && optionBtn[i]->getState() == STATE::SELECTED &&
303 optionBtn[i]->getStateChan() != -1 && optionBtn[i]->getStateChan() != currentChan)
304 // Slot is already SELECTED but carries a stale channel from an earlier
305 // selection (e.g. a reused slot after paging, or a preselected item that
306 // was never actually hovered by the real channel yet). Without this,
307 // currentChan == stateChan never holds and clicks are silently ignored
308 // until the item happens to be navigated away from and back.
309 optionBtn[i]->resetState();
310
311 // Present a "no channel" (-1) identity to any item the cursor isn't
312 // currently over. Without this, a stale stateChan left on a reused
313 // list slot (e.g. after paging/navigating) can permanently block
314 // clicks from the real channel until the item is re-hovered.
315 if(pad.validPointer && !optionBtn[i]->isInside(pad.cursor_x, pad.cursor_y))
316 controller->setChannel(-1);
317
318 optionBtn[i]->update(controller);
319 controller->setChannel(currentChan);
320
321 if(optionBtn[i]->getState() == STATE::SELECTED)
322 selectedItem = i;
323
324 if(selectedItem == i) {
325 optionTxt[i]->setScroll(SCROLL::HORIZONTAL);
326 optionVal[i]->setScroll(SCROLL::HORIZONTAL);
327 }
328 else
329 {
330 optionTxt[i]->setScroll(SCROLL::NONE);
331 optionVal[i]->setScroll(SCROLL::NONE);
332 }
333 }
334
335 // pad/joystick navigation
336 if(!focus)
337 return; // skip navigation
338
339 if(controller->down() || arrowDownBtn->getState() == STATE::CLICKED)
340 {
341 next = this->findMenuItem(optionIndex[selectedItem], 1);
342
343 if(next >= 0)
344 {
345 if(selectedItem == OPTION_PAGESIZE-1)
346 {
347 // move list down by 1
348 listOffset = this->findMenuItem(listOffset, 1);
349 listChanged = true;
350 }
351 else if(optionBtn[selectedItem+1]->isVisible())
352 {
353 optionBtn[selectedItem]->resetState();
354 optionBtn[selectedItem+1]->setState(STATE::SELECTED, controller->getChannel());
355 ++selectedItem;
356 }
357 }
358 arrowDownBtn->resetState();
359 }
360 else if(controller->up() || arrowUpBtn->getState() == STATE::CLICKED)
361 {
362 prev = this->findMenuItem(optionIndex[selectedItem], -1);
363
364 if(prev >= 0)
365 {
366 if(selectedItem == 0)
367 {
368 // move list up by 1
369 listOffset = prev;
370 listChanged = true;
371 }
372 else
373 {
374 optionBtn[selectedItem]->resetState();
375 optionBtn[selectedItem-1]->setState(STATE::SELECTED, controller->getChannel());
376 --selectedItem;
377 }
378 }
379 arrowUpBtn->resetState();
380 }
381
382 if(updateCB)
383 updateCB(this);
384}
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:293
STATE getState()
Gets the element's current state.
int stateChan
Which controller channel is responsible for the last change in state.
Definition GuiElement.h:292
int width
Element width.
Definition GuiElement.h:268
UpdateCallback updateCB
Callback function to call when this element is updated.
Definition GuiElement.h:264
int height
Element height.
Definition GuiElement.h:269
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:267
STATE state
Element state (DEFAULT, SELECTED, CLICKED, DISABLED)
Definition GuiElement.h:291
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.
Sound conversion and playback.
Definition GuiSound.h:27
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:297
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