libgui 2.01
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
GuiWindow.cpp
1/****************************************************************************
2 * libgui
3 * Daryl Borth 2009-2026
4 * GuiWindow.cpp
5 ***************************************************************************/
6
7#include "Gui.h"
8
10{
11 width = 0;
12 height = 0;
13 focus = 0; // allow focus
14}
15
17{
18 width = w;
19 height = h;
20 focus = 0; // allow focus
21}
22
24{
25 // Orphan all children if the window is destroyed first to prevent dangling pointer
26 for (GuiElement* e : _elements) {
27 e->setParent(nullptr);
28 }
29}
30
32{
33 if (e == nullptr)
34 return;
35
36 remove(e);
37 _elements.push_back(e);
38 e->setParent(this);
39}
40
42{
43 if (e == nullptr) return;
44 append(e);
45 e->setRemoveOnDestroy(true);
46}
47
48void GuiWindow::insert(GuiElement* e, uint32_t index)
49{
50 if (e == nullptr || index > (_elements.size() - 1))
51 return;
52
53 remove(e);
54 _elements.insert(_elements.begin()+index, e);
55 e->setParent(this);
56}
57
59{
60 if (e == nullptr)
61 return;
62
63 uint32_t elemSize = _elements.size();
64 for (uint32_t i = 0; i < elemSize; ++i)
65 {
66 if(e == _elements.at(i))
67 {
68 e->setParent(nullptr);
69 _elements.erase(_elements.begin()+i);
70 break;
71 }
72 }
73}
74
76{
77 uint32_t elemSize = _elements.size();
78 for (uint32_t i = 0; i < elemSize; ++i)
79 {
80 _elements.at(i)->setParent(nullptr);
81 }
82 _elements.clear();
83}
84
86{
87 if (e == nullptr)
88 return false;
89
90 uint32_t elemSize = _elements.size();
91 for (uint32_t i = 0; i < elemSize; ++i)
92 if(e == _elements.at(i))
93 return true;
94 return false;
95}
96
98{
99 if (index >= _elements.size())
100 return nullptr;
101 return _elements.at(index);
102}
103
105{
106 return _elements.size();
107}
108
110{
111 if(_elements.size() == 0 || !this->isVisible())
112 return;
113
114 uint32_t elemSize = _elements.size();
115 for (uint32_t i = 0; i < elemSize; ++i)
116 {
117 _elements.at(i)->draw();
118 }
119
120 this->updateEffects();
121
122 if(parentElement && state == STATE::DISABLED)
123 platform->getVideo()->getImageRenderer()->drawRectangle(0,0,platform->getVideo()->getScreenWidth(), platform->getVideo()->getScreenHeight(), (PixelColor){0xbe, 0xca, 0xd5, 0x70});
124}
125
127{
128 if(state != STATE::DISABLED)
129 state = STATE::DEFAULT;
130
131 uint32_t elemSize = _elements.size();
132 for (uint32_t i = 0; i < elemSize; ++i)
133 {
134 _elements.at(i)->resetState();
135 }
136}
137
139{
140 state = s;
141
142 uint32_t elemSize = _elements.size();
143 for (uint32_t i = 0; i < elemSize; ++i)
144 {
145 _elements.at(i)->setState(s, c);
146 }
147}
148
150{
151 visible = v;
152
153 uint32_t elemSize = _elements.size();
154 for (uint32_t i = 0; i < elemSize; ++i)
155 {
156 _elements.at(i)->setVisible(v);
157 }
158}
159
161{
162 focus = f;
163
164 if(f == 1)
165 this->moveSelectionVert(1);
166 else
167 this->resetState();
168}
169
171{
172 if(parentElement)
173 return; // this is only intended for the main window
174
175 uint32_t elemSize = _elements.size();
176 for (uint32_t i = 0; i < elemSize; ++i)
177 {
178 if(e == _elements.at(i))
179 _elements.at(i)->setFocus(1);
180 else if(_elements.at(i)->isFocused() == 1)
181 _elements.at(i)->setFocus(0);
182 }
183}
184
186{
187 if(parentElement)
188 return; // this is only intended for the main window
189
190 int found = -1;
191 int newfocus = -1;
192 int i;
193
194 int elemSize = _elements.size();
195
196 // look for currently in focus element
197 for (i = 0; i < elemSize; ++i)
198 {
199 if(_elements.at(i)->isFocused() == 1)
200 {
201 found = i;
202 break;
203 }
204 }
205
206 // element with focus not found, try to give focus
207 if(found == -1)
208 {
209 for (i = 0; i < elemSize; ++i)
210 {
211 if(_elements.at(i)->isFocused() == 0 && _elements.at(i)->getState() != STATE::DISABLED) // focus is possible (but not set)
212 {
213 _elements.at(i)->setFocus(1); // give this element focus
214 break;
215 }
216 }
217 }
218
219 // change focus
220 else if(controller->isSecondaryPressed())
221 {
222 for (i = found; i < elemSize; ++i)
223 {
224 if(_elements.at(i)->isFocused() == 0 && _elements.at(i)->getState() != STATE::DISABLED) // focus is possible (but not set)
225 {
226 newfocus = i;
227 _elements.at(i)->setFocus(1); // give this element focus
228 _elements.at(found)->setFocus(0); // disable focus on other element
229 break;
230 }
231 }
232
233 if(newfocus == -1)
234 {
235 for (i = 0; i < found; ++i)
236 {
237 if(_elements.at(i)->isFocused() == 0 && _elements.at(i)->getState() != STATE::DISABLED) // focus is possible (but not set)
238 {
239 _elements.at(i)->setFocus(1); // give this element focus
240 _elements.at(found)->setFocus(0); // disable focus on other element
241 break;
242 }
243 }
244 }
245 }
246}
247
249{
250 // find selected element
251 int found = -1;
252 uint32_t elemSize = _elements.size();
253 for (uint32_t i = 0; i < elemSize; ++i)
254 {
255 if(_elements.at(i)->getState() == STATE::SELECTED)
256 {
257 found = int(i);
258 break;
259 }
260 }
261 return found;
262}
263
264// set element to left/right as selected
265// there's probably a more clever way to do this, but this way works
266bool GuiWindow::collectNavigation(std::vector<GuiElement*>& out)
267{
268 bool nested = false;
269 for (GuiElement* e : _elements)
270 {
271 size_t before = out.size();
272 e->collectNavigable(out);
273 if(!(out.size() == before + 1 && out.back() == e))
274 nested = true; // the element contributed its children, not itself
275 }
276 return nested;
277}
278
279void GuiWindow::moveSelectionIn(std::vector<GuiElement*>& list, NAV_DIR d)
280{
281 int current = -1;
282 std::vector<GuiNavItem> items(list.size());
283 for (size_t i = 0; i < list.size(); ++i)
284 {
285 if(current < 0 && list[i]->getState() == STATE::SELECTED)
286 current = int(i);
287 items[i].r.x = (float)list[i]->getLeft();
288 items[i].r.y = (float)list[i]->getTop();
289 items[i].r.w = (float)list[i]->getWidth();
290 items[i].r.h = (float)list[i]->getHeight();
291 items[i].selectable = list[i]->isSelectable() && list[i]->isVisible() && list[i]->getState() != STATE::DISABLED;
292 }
293
294 int next = GuiNavigate(items, current, d);
295 if(next >= 0 && next != current)
296 {
297 list[next]->setState(STATE::SELECTED);
298 if(current >= 0)
299 list[current]->resetState();
300 }
301}
302
304{
305 std::vector<GuiElement*> nav;
306 if(this->collectNavigation(nav))
307 {
308 this->moveSelectionIn(nav, dir > 0 ? NAV_DIR::RIGHT : NAV_DIR::LEFT);
309 return;
310 }
311
312 int found = -1;
313 uint16_t left = 0;
314 uint16_t top = 0;
315 uint32_t i;
316 uint32_t elemSize = _elements.size();
317
318 int selected = this->getSelected();
319
320 if(selected >= 0)
321 {
322 left = _elements.at(selected)->getLeft();
323 top = _elements.at(selected)->getTop();
324 }
325
326
327 // look for a button on the same row, to the left/right
328 for (i = 0; i < elemSize; ++i)
329 {
330 if(_elements.at(i)->isSelectable())
331 {
332 if(_elements.at(i)->getLeft()*dir > left*dir && _elements.at(i)->getTop() == top)
333 {
334 if(found == -1)
335 found = int(i);
336 else if(_elements.at(i)->getLeft()*dir < _elements.at(found)->getLeft()*dir)
337 found = int(i); // this is a better match
338 }
339 }
340 }
341 if(found >= 0)
342 goto matchfound;
343
344 // match still not found, let's try the first button in the next row
345 for (i = 0; i < elemSize; ++i)
346 {
347 if(_elements.at(i)->isSelectable())
348 {
349 if(_elements.at(i)->getTop()*dir > top*dir)
350 {
351 if(found == -1)
352 found = i;
353 else if(_elements.at(i)->getTop()*dir < _elements.at(found)->getTop()*dir)
354 found = i; // this is a better match
355 else if(_elements.at(i)->getTop()*dir == _elements.at(found)->getTop()*dir
356 &&
357 _elements.at(i)->getLeft()*dir < _elements.at(found)->getLeft()*dir)
358 found = i; // this is a better match
359 }
360 }
361 }
362
363 // match found
364 matchfound:
365 if(found >= 0)
366 {
367 _elements.at(found)->setState(STATE::SELECTED);
368 if(selected >= 0)
369 _elements.at(selected)->resetState();
370 }
371}
372
374{
375 std::vector<GuiElement*> nav;
376 if(this->collectNavigation(nav))
377 {
378 this->moveSelectionIn(nav, dir > 0 ? NAV_DIR::DOWN : NAV_DIR::UP);
379 return;
380 }
381
382 int found = -1;
383 uint16_t left = 0;
384 uint16_t top = 0;
385
386 int selected = this->getSelected();
387
388 if(selected >= 0)
389 {
390 left = _elements.at(selected)->getLeft();
391 top = _elements.at(selected)->getTop();
392 }
393
394 // look for a button above/below, with the least horizontal difference
395 uint32_t elemSize = _elements.size();
396 for (uint32_t i = 0; i < elemSize; ++i)
397 {
398 if(_elements.at(i)->isSelectable())
399 {
400 if(_elements.at(i)->getTop()*dir > top*dir)
401 {
402 if(found == -1)
403 found = i;
404 else if(_elements.at(i)->getTop()*dir < _elements.at(found)->getTop()*dir)
405 found = i; // this is a better match
406 else if(_elements.at(i)->getTop()*dir == _elements.at(found)->getTop()*dir
407 &&
408 abs(_elements.at(i)->getLeft() - left) <
409 abs(_elements.at(found)->getLeft() - left))
410 found = i;
411 }
412 }
413 }
414 if(found >= 0)
415 goto matchfound;
416
417 // match found
418 matchfound:
419 if(found >= 0)
420 {
421 _elements.at(found)->setState(STATE::SELECTED);
422 if(selected >= 0)
423 _elements.at(selected)->resetState();
424 }
425}
426
428{
429 uint32_t elemSize = _elements.size();
430 for (uint32_t i = 0; i < elemSize; i++)
431 {
432 _elements.at(i)->resetText();
433 }
434}
435
437{
438 if(_elements.size() == 0 || (state == STATE::DISABLED && parentElement))
439 return;
440
441 uint32_t elemSize = _elements.size();
442 for (uint32_t i = 0; i < elemSize; ++i)
443 {
444 _elements.at(i)->update(controller);
445 }
446
447 this->toggleFocus(controller);
448
449 if(focus) // only send actions to this window if it's in focus
450 {
451 // pad/joystick navigation
452 if(controller->right())
453 this->moveSelectionHor(1);
454 else if(controller->left())
455 this->moveSelectionHor(-1);
456 else if(controller->down())
457 this->moveSelectionVert(1);
458 else if(controller->up())
459 this->moveSelectionVert(-1);
460 }
461
462 if(updateCB)
463 updateCB(this);
464}
Umbrella header for the platform-agnostic libgui core.
int getTop()
Gets the current topmost coordinate of the element Considers vertical alignment, y offset,...
void setRemoveOnDestroy(bool autoRemove)
Whether to notify parent on destruction to allow removal.
Definition GuiElement.h:76
STATE getState()
Gets the element's current state.
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
GuiElement * parentElement
Parent element.
Definition GuiElement.h:265
void updateEffects()
Updates the element's effects (dynamic values) Called by Draw(), used for animation purposes.
int getWidth()
Gets the current width of the element.
GuiElement()
Constructor.
Definition GuiElement.cpp:9
bool visible
Visibility of the element.
Definition GuiElement.h:296
int getLeft()
Gets the current leftmost coordinate of the element Considers horizontal alignment,...
void setParent(GuiElement *e)
Set 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 getHeight()
Gets the height of the element.
void moveSelectionIn(std::vector< GuiElement * > &list, NAV_DIR d)
Moves the selection within a collected list by on-screen geometry.
uint32_t getSize()
Returns the size of the list of elements.
void toggleFocus(InputController *c)
Changes window focus to the next focusable window or element If no element is in focus,...
bool collectNavigation(std::vector< GuiElement * > &out)
Gathers the elements selection can move between, looking inside layout containers.
void append(GuiElement *e)
Appends a GuiElement to the GuiWindow.
Definition GuiWindow.cpp:31
bool find(GuiElement *e)
Looks for the specified GuiElement.
Definition GuiWindow.cpp:85
void setFocus(int f)
Sets the window focus.
void setVisible(bool v)
Sets the visibility of the window.
void moveSelectionVert(int d)
Moves the selected element to the element above or below.
void resetState()
Resets the window's state to STATE::DEFAULT.
GuiElement * getGuiElementAt(uint32_t index) const
Returns the GuiElement at the specified index.
Definition GuiWindow.cpp:97
void appendWithAutoRemove(GuiElement *e)
Appends a GuiElement to the GuiWindow, and instructs the GuiElement to inform its parent on destructi...
Definition GuiWindow.cpp:41
void removeAll()
Removes all GuiElements.
Definition GuiWindow.cpp:75
GuiWindow()
Constructor.
Definition GuiWindow.cpp:9
int getSelected()
Gets the index of the GuiElement inside the window that is currently selected.
void draw()
Draws all the elements in this GuiWindow.
void setState(STATE s, int c=-1)
Sets the window state, propagated to every child element.
~GuiWindow()
Destructor.
Definition GuiWindow.cpp:23
void resetText()
Resets the text for all contained elements.
void update(InputController *c)
Updates the window and all elements contains within Allows the GuiWindow and all elements to respond ...
void insert(GuiElement *e, uint32_t i)
Inserts a GuiElement into the GuiWindow at the specified index.
Definition GuiWindow.cpp:48
void remove(GuiElement *e)
Removes the specified GuiElement from the GuiWindow.
Definition GuiWindow.cpp:58
std::vector< GuiElement * > _elements
Contains all elements within the GuiWindow.
Definition GuiWindow.h:93
void changeFocus(GuiElement *e)
Change the focus to the specified element This is intended for the primary GuiWindow only.
void moveSelectionHor(int d)
Moves the selected element to the element to the left or right.
Logical controller for one input channel.
STATE
Interaction state of a GuiElement (default, selected, clicked, held, disabled).
Definition GuiElement.h:36
NAV_DIR
Directions for moving the selection.
int GuiNavigate(const std::vector< GuiNavItem > &items, int current, NAV_DIR dir)
Picks which item the selection moves to.
8-bit-per-channel RGBA color used by the video interfaces.
Definition Mesh.h:16