libgui 2.01
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
GuiFileBrowser.cpp
1/****************************************************************************
2 * libgui
3 * Daryl Borth 2009-2026
4 * GuiFileBrowser.cpp
5 ***************************************************************************/
6
7#include "Gui.h"
8#include "../filebrowser.h"
9
11{
12 width = w;
13 height = h;
14 numEntries = 0;
15 selectedItem = 0;
16 selectable = true;
17 listChanged = true; // trigger an initial list update
18 focus = 0; // allow focus
19
20 trigA = new GuiTrigger;
21 trigA->setPrimaryTrigger();
22
23 trigHeldA = new GuiTrigger;
24 trigHeldA->setHeldTrigger(-1, INPUT_BTN_A);
25
26 btnSoundOver = new GuiSound(button_over_pcm, button_over_pcm_size, SOUND::PCM);
27 btnSoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, SOUND::PCM);
28
29 bgFileSelection = new GuiImageData(bg_file_selection_png);
30 bgFileSelectionImg = new GuiImage(bgFileSelection);
31 bgFileSelectionImg->setParent(this);
32 bgFileSelectionImg->setAlignment(ALIGN_H::LEFT, ALIGN_V::MIDDLE);
33
34 bgFileSelectionEntry = new GuiImageData(bg_file_selection_entry_png);
35 fileFolder = new GuiImageData(folder_png);
36
37 scrollbar = new GuiImageData(scrollbar_png);
38 scrollbarImg = new GuiImage(scrollbar);
39 scrollbarImg->setParent(this);
40 scrollbarImg->setAlignment(ALIGN_H::RIGHT, ALIGN_V::TOP);
41 scrollbarImg->setPosition(0, 30);
42
43 arrowDown = new GuiImageData(scrollbar_arrowdown_png);
44 arrowDownImg = new GuiImage(arrowDown);
45 arrowDownOver = new GuiImageData(scrollbar_arrowdown_over_png);
46 arrowDownOverImg = new GuiImage(arrowDownOver);
47 arrowUp = new GuiImageData(scrollbar_arrowup_png);
48 arrowUpImg = new GuiImage(arrowUp);
49 arrowUpOver = new GuiImageData(scrollbar_arrowup_over_png);
50 arrowUpOverImg = new GuiImage(arrowUpOver);
51 scrollbarBox = new GuiImageData(scrollbar_box_png);
52 scrollbarBoxImg = new GuiImage(scrollbarBox);
53 scrollbarBoxOver = new GuiImageData(scrollbar_box_over_png);
54 scrollbarBoxOverImg = new GuiImage(scrollbarBoxOver);
55
56 arrowUpBtn = new GuiButton(arrowUpImg->getWidth(), arrowUpImg->getHeight());
57 arrowUpBtn->setParent(this);
58 arrowUpBtn->setImage(arrowUpImg);
59 arrowUpBtn->setImageOver(arrowUpOverImg);
60 arrowUpBtn->setAlignment(ALIGN_H::RIGHT, ALIGN_V::TOP);
61 arrowUpBtn->setPosition(0, -2);
62 arrowUpBtn->setSelectable(false);
63 arrowUpBtn->setClickable(false);
64 arrowUpBtn->setHoldable(true);
65 arrowUpBtn->setTrigger(trigHeldA);
66 arrowUpBtn->setSoundOver(btnSoundOver);
67 arrowUpBtn->setSoundClick(btnSoundClick);
68
69 arrowDownBtn = new GuiButton(arrowDownImg->getWidth(), arrowDownImg->getHeight());
70 arrowDownBtn->setParent(this);
71 arrowDownBtn->setImage(arrowDownImg);
72 arrowDownBtn->setImageOver(arrowDownOverImg);
73 arrowDownBtn->setAlignment(ALIGN_H::RIGHT, ALIGN_V::BOTTOM);
74 arrowDownBtn->setSelectable(false);
75 arrowDownBtn->setClickable(false);
76 arrowDownBtn->setHoldable(true);
77 arrowDownBtn->setTrigger(trigHeldA);
78 arrowDownBtn->setSoundOver(btnSoundOver);
79 arrowDownBtn->setSoundClick(btnSoundClick);
80
81 scrollbarBoxBtn = new GuiButton(scrollbarBoxImg->getWidth(), scrollbarBoxImg->getHeight());
82 scrollbarBoxBtn->setParent(this);
83 scrollbarBoxBtn->setImage(scrollbarBoxImg);
84 scrollbarBoxBtn->setImageOver(scrollbarBoxOverImg);
85 scrollbarBoxBtn->setAlignment(ALIGN_H::RIGHT, ALIGN_V::TOP);
86 scrollbarBoxBtn->setMinY(0);
87 scrollbarBoxBtn->setMaxY(130);
88 scrollbarBoxBtn->setSelectable(false);
89 scrollbarBoxBtn->setClickable(false);
90 scrollbarBoxBtn->setHoldable(true);
91 scrollbarBoxBtn->setTrigger(trigHeldA);
92
93 for(int i=0; i<FILE_PAGESIZE; ++i)
94 {
95 fileListText[i] = new GuiText(nullptr, 18, (PixelColor){0, 0, 0, 0xff});
96 fileListText[i]->setAlignment(ALIGN_H::LEFT, ALIGN_V::MIDDLE);
97 fileListText[i]->setMaxWidth(512);
98
99 fileListBg[i] = new GuiImage(bgFileSelectionEntry);
100 fileListFolder[i] = new GuiImage(fileFolder);
101
102 fileList[i] = new GuiButton(512, 30);
103 fileList[i]->setParent(this);
104 fileList[i]->setLabel(fileListText[i]);
105 fileList[i]->setImageOver(fileListBg[i]);
106 fileList[i]->setPosition(2,30*i+3);
107 fileList[i]->setTrigger(trigA);
108 fileList[i]->setSoundClick(btnSoundClick);
109 }
110}
111
112GuiFileBrowser::~GuiFileBrowser()
113{
114 delete arrowUpBtn;
115 delete arrowDownBtn;
116 delete scrollbarBoxBtn;
117
118 delete bgFileSelectionImg;
119 delete scrollbarImg;
120 delete arrowDownImg;
121 delete arrowDownOverImg;
122 delete arrowUpImg;
123 delete arrowUpOverImg;
124 delete scrollbarBoxImg;
125 delete scrollbarBoxOverImg;
126
127 delete bgFileSelection;
128 delete bgFileSelectionEntry;
129 delete fileFolder;
130 delete scrollbar;
131 delete arrowDown;
132 delete arrowDownOver;
133 delete arrowUp;
134 delete arrowUpOver;
135 delete scrollbarBox;
136 delete scrollbarBoxOver;
137
138 delete btnSoundOver;
139 delete btnSoundClick;
140 delete trigHeldA;
141 delete trigA;
142
143 for(int i=0; i<FILE_PAGESIZE; i++)
144 {
145 delete fileListText[i];
146 delete fileList[i];
147 delete fileListBg[i];
148 delete fileListFolder[i];
149 }
150}
151
153{
154 focus = f;
155
156 for(int i=0; i<FILE_PAGESIZE; i++)
157 fileList[i]->resetState();
158
159 if(f == 1)
160 fileList[selectedItem]->setState(STATE::SELECTED);
161}
162
164{
165 state = STATE::DEFAULT;
166 stateChan = -1;
167 selectedItem = 0;
168
169 for(int i=0; i<FILE_PAGESIZE; i++)
170 {
171 fileList[i]->resetState();
172 }
173}
174
176{
177 int newIndex = browser.selIndex-browser.pageIndex;
178
179 if(newIndex >= FILE_PAGESIZE)
180 newIndex = FILE_PAGESIZE-1;
181 else if(newIndex < 0)
182 newIndex = 0;
183
184 selectedItem = newIndex;
185 listChanged = true;
186}
187
192{
193 if(!this->isVisible())
194 return;
195
196 bgFileSelectionImg->draw();
197
198 for(uint32_t i=0; i<FILE_PAGESIZE; ++i)
199 {
200 fileList[i]->draw();
201 }
202
203 scrollbarImg->draw();
204 arrowUpBtn->draw();
205 arrowDownBtn->draw();
206 scrollbarBoxBtn->draw();
207
208 this->updateEffects();
209}
210
212{
213 if(state == STATE::DISABLED || !controller)
214 return;
215
216 int position = 0;
217 int positionWiimote = 0;
218
219 arrowUpBtn->update(controller);
220 arrowDownBtn->update(controller);
221 scrollbarBoxBtn->update(controller);
222
223 auto pad = controller->getPadData();
224 int currentChan = controller->getChannel();
225 // A InputController with no live signal this frame (e.g. a persistent
226 // but currently-disconnected controller slot) must not be able to claim
227 // or evict a selection just by taking its turn through this loop -- only
228 // a channel that's genuinely doing something (pointing or pressing) gets
229 // a say in who owns the selected slot.
230 bool channelActive = pad.validPointer || pad.buttons_d != 0 || pad.buttons_h != 0;
231
232 // move the file listing to respond to wiimote cursor movement
233 if(scrollbarBoxBtn->getState() == STATE::HELD &&
234 scrollbarBoxBtn->getStateChan() == currentChan &&
235 pad.validPointer &&
236 browser.numEntries > FILE_PAGESIZE)
237 {
238 scrollbarBoxBtn->setPosition(0,0);
239 positionWiimote = pad.cursor_y - 60 - scrollbarBoxBtn->getTop();
240
241 if(positionWiimote < scrollbarBoxBtn->getMinY())
242 positionWiimote = scrollbarBoxBtn->getMinY();
243 else if(positionWiimote > scrollbarBoxBtn->getMaxY())
244 positionWiimote = scrollbarBoxBtn->getMaxY();
245
246 browser.pageIndex = (positionWiimote * browser.numEntries)/130.0 - selectedItem;
247
248 if(browser.pageIndex <= 0)
249 {
250 browser.pageIndex = 0;
251 }
252 else if(browser.pageIndex+FILE_PAGESIZE >= browser.numEntries)
253 {
254 browser.pageIndex = browser.numEntries-FILE_PAGESIZE;
255 }
256 listChanged = true;
257 focus = false;
258 }
259
260 // Evaluate simulated D-Pad actions via on-screen buttons
261 bool simulateDown = arrowDownBtn->getState() == STATE::HELD && arrowDownBtn->getStateChan() == currentChan;
262 bool simulateUp = arrowUpBtn->getState() == STATE::HELD && arrowUpBtn->getStateChan() == currentChan;
263
264 if(simulateDown)
265 {
266 if(!this->isFocused())
267 ((GuiWindow *)this->getParent())->changeFocus(this);
268 }
269 else if(simulateUp)
270 {
271 if(!this->isFocused())
272 ((GuiWindow *)this->getParent())->changeFocus(this);
273 }
274
275 // pad/joystick navigation
276 if(!focus)
277 {
278 goto endNavigation; // skip navigation
279 listChanged = false;
280 }
281
282 if(controller->right())
283 {
284 if(browser.pageIndex < browser.numEntries && browser.numEntries > FILE_PAGESIZE)
285 {
286 browser.pageIndex += FILE_PAGESIZE;
287 if(browser.pageIndex+FILE_PAGESIZE >= browser.numEntries)
288 browser.pageIndex = browser.numEntries-FILE_PAGESIZE;
289 listChanged = true;
290 }
291 }
292 else if(controller->left())
293 {
294 if(browser.pageIndex > 0)
295 {
296 browser.pageIndex -= FILE_PAGESIZE;
297 if(browser.pageIndex < 0)
298 browser.pageIndex = 0;
299 listChanged = true;
300 }
301 }
302 else if(controller->down() || simulateDown)
303 {
304 if(browser.pageIndex + selectedItem + 1 < browser.numEntries)
305 {
306 if(selectedItem == FILE_PAGESIZE-1)
307 {
308 // move list down by 1
309 ++browser.pageIndex;
310 listChanged = true;
311 }
312 else if(fileList[selectedItem+1]->isVisible())
313 {
314 fileList[selectedItem]->resetState();
315 fileList[++selectedItem]->setState(STATE::SELECTED, currentChan);
316 }
317 }
318 }
319 else if(controller->up() || simulateUp)
320 {
321 if(selectedItem == 0 && browser.pageIndex + selectedItem > 0)
322 {
323 // move list up by 1
324 --browser.pageIndex;
325 listChanged = true;
326 }
327 else if(selectedItem > 0)
328 {
329 fileList[selectedItem]->resetState();
330 fileList[--selectedItem]->setState(STATE::SELECTED, currentChan);
331 }
332 }
333
334 endNavigation:
335
336 for(int i=0; i<FILE_PAGESIZE; ++i)
337 {
338 if(listChanged || numEntries != browser.numEntries)
339 {
340 if(browser.pageIndex+i < browser.numEntries)
341 {
342 if(fileList[i]->getState() == STATE::DISABLED)
343 fileList[i]->setState(STATE::DEFAULT);
344
345 fileList[i]->setVisible(true);
346
347 fileListText[i]->setText(browserList[browser.pageIndex+i].displayname);
348
349 if(browserList[browser.pageIndex+i].isdir) // directory
350 {
351 fileList[i]->setIcon(fileListFolder[i]);
352 fileListText[i]->setPosition(30,4);
353 }
354 else
355 {
356 fileList[i]->setIcon(nullptr);
357 fileListText[i]->setPosition(10,4);
358 }
359 }
360 else
361 {
362 fileList[i]->setVisible(false);
363 fileList[i]->setState(STATE::DISABLED);
364 }
365 }
366
367 if(i != selectedItem && fileList[i]->getState() == STATE::SELECTED)
368 fileList[i]->resetState();
369 else if(focus && channelActive && i == selectedItem && fileList[i]->getState() == STATE::DEFAULT)
370 fileList[selectedItem]->setState(STATE::SELECTED, currentChan);
371 else if(focus && channelActive && i == selectedItem && fileList[i]->getState() == STATE::SELECTED &&
372 fileList[i]->getStateChan() != -1 && fileList[i]->getStateChan() != currentChan)
373 // Slot is already SELECTED but carries a stale channel from an earlier
374 // selection (e.g. a reused slot after paging, or a preselected item that
375 // was never actually hovered by the real channel yet). Without this,
376 // currentChan == stateChan never holds and clicks are silently ignored
377 // until the item happens to be navigated away from and back.
378 fileList[i]->resetState();
379
380 // Present a "no channel" (-1) identity to any item the cursor isn't
381 // currently over. Without this, a stale stateChan left on a reused
382 // list slot (e.g. after paging/navigating) can permanently block
383 // clicks from the real channel until the item is re-hovered.
384 if(pad.validPointer && !fileList[i]->isInside(pad.cursor_x, pad.cursor_y))
385 controller->setChannel(-1);
386
387 fileList[i]->update(controller);
388
389 controller->setChannel(currentChan);
390
391 if(fileList[i]->getState() == STATE::SELECTED)
392 {
393 selectedItem = i;
394 browser.selIndex = browser.pageIndex + i;
395 }
396
397 if(selectedItem == i)
398 fileListText[i]->setScroll(SCROLL::HORIZONTAL);
399 else
400 fileListText[i]->setScroll(SCROLL::NONE);
401 }
402
403 // update the location of the scroll box based on the position in the file list
404 if(positionWiimote > 0)
405 {
406 position = positionWiimote; // follow wiimote cursor
407 scrollbarBoxBtn->setPosition(0,position+36);
408 }
409 else if(listChanged || numEntries != browser.numEntries)
410 {
411 if(float((browser.pageIndex<<1))/(float(FILE_PAGESIZE)) < 1.0)
412 {
413 position = 0;
414 }
415 else if(browser.pageIndex+FILE_PAGESIZE >= browser.numEntries)
416 {
417 position = 130;
418 }
419 else
420 {
421 position = 130 * (browser.pageIndex + FILE_PAGESIZE/2) / (float)browser.numEntries;
422 }
423 scrollbarBoxBtn->setPosition(0,position+36);
424 }
425
426 listChanged = false;
427 numEntries = browser.numEntries;
428
429 if(updateCB)
430 updateCB(this);
431}
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 isFocused()
Checks whether the element is in focus.
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.
GuiElement * getParent()
Gets the element's parent.
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
int getMinY()
Gets the minimum y offset of the element.
GuiButton * fileList[FILE_PAGESIZE]
One button per visible row of the current page.
void update(InputController *c)
Called constantly to allow the element to respond to the current input data.
void resetState()
Resets the element's state to STATE::DEFAULT.
void setFocus(int f)
Sets the element's focus.
GuiFileBrowser(int w, int h)
void triggerUpdate()
Forces the visible page to refresh from the underlying browser data, eg.
void draw() override
Draw the button on screen.
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
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.
void setHeldTrigger(int ch, uint32_t buttonMask)
Sets a held trigger.
Allows GuiElements to be grouped together into a "window".
Definition GuiWindow.h:11
Logical controller for one input channel.
8-bit-per-channel RGBA color used by the video interfaces.
Definition Mesh.h:16