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