Chesto 0.9
A declarative and element-based library for creating GUIs on homebrew'd consoles
Button.cpp
1#include "Button.hpp"
2#include <iostream>
3
4CST_Color Button::colors[2] = {
5 { 0x00, 0x00, 0x00, 0xff }, // light
6 { 0xff, 0xff, 0xff, 0xff }, // dark
7};
8
9Button::Button(std::string message, int button, bool dark, int size, int width)
10 : physical(button)
11 , dark(dark)
12 , icon(getControllerButtonImageForPlatform(button, !dark, false))
13 , text(message, (size / SCALER), &colors[dark])
14{
15
16 super::append(&text);
17 super::append(&icon);
18
19 // on initialization, store the last gamepad info
20 myLastSeenGamepad = "";
21
22 icon.resize(text.height*1.5, text.height*1.5);
23
24 if (shouldRenderGlossy()) {
25 icon.hide();
26 }
27
28 fixedWidth = width;
29
30 updateBounds();
31
32 this->touchable = true;
33 this->hasBackground = true;
34
35 // protect "stack" children
36 text.isProtected = icon.isProtected = true;
37
38 if (dark)
39 {
40 backgroundColor = RootDisplay::mainDisplay->backgroundColor;
41 backgroundColor.r += 0x25/255.0;
42 backgroundColor.g += 0x25/255.0;
43 backgroundColor.b += 0x25/255.0;
44 // backgroundColor.r = fmin(backgroundColor.r, 1.0);
45 }
46 else
47 backgroundColor = (rgb){ 0xee/255.0, 0xee/255.0, 0xee/255.0 };
48}
49
50void Button::updateBounds()
51{
52 int PADDING = 10 / SCALER;
53
54 int bWidth = PADDING * 0.5 * (icon.width != 0); // gap space between button
55
56 if (shouldRenderGlossy()) {
57 icon.position(0, 0); // the icon is the overlay, same size as the button
58 } else {
59 text.position(PADDING * 2 + bWidth + icon.width, PADDING);
60 icon.position(PADDING * 1.7, PADDING + (text.height - icon.height) / 2);
61 }
62
63 this->width = (fixedWidth > 0) ? fixedWidth : text.width + PADDING * 4 + bWidth + (icon.hidden ? 0 : icon.width);
64 this->height = text.height + PADDING * 2;
65
66 if (shouldRenderGlossy()) {
67 text.position(width / 2 - text.width / 2, PADDING); // y is already centered
68 }
69}
70
71void Button::updateText(const char* inc_text)
72{
73 this->text.setText(inc_text);
74 this->text.update();
75 updateBounds();
76}
77
78std::string Button::getControllerButtonImageForPlatform(int button, bool isGray, bool isOutline)
79{
80 // grab the current gamepad info
81 GamepadInfo& gamepad = InputEvents::getLastGamepadInfo();
82
83 // find the button index in the gamepad.buttons array
84 // TODO: use a hashmap instead of an array
85 if (gamepad.buttons != nullptr) {
86 for (int i = 0; i < TOTAL_BUTTONS; i++)
87 {
88 if (gamepad.buttons[i] == button)
89 {
90 auto outlineSuffix = isOutline ? "_outline" : "";
91 auto graySuffix = isGray ? "_gray" : "";
92 auto retVal = RAMFS "res/controllers/buttons/" + gamepad.prefix + "_" + gamepad.names[i] + outlineSuffix + graySuffix + ".svg";
93 return retVal;
94 }
95 }
96 }
97 // if we didn't find it, return an empty string
98 printf("Button %d not found in gamepad, returning empty string\n", button);
99 return "";
100}
101
103{
104 if (hidden) return;
105
106 CST_SetDrawBlend(RootDisplay::renderer, true);
107
108 this->recalcPosition(parent);
109
110 if (shouldRenderGlossy()) {
111 // draw a glossy button
112 auto r = backgroundColor.r * 0xff;
113 auto g = backgroundColor.g * 0xff;
114 auto b = backgroundColor.b * 0xff;
115
116 CST_roundedBoxRGBA(
117 RootDisplay::renderer,
118 xAbs, yAbs, xAbs + width, yAbs + height, cornerRadius,
119 r, g, b, 0xff
120 );
121 // slightly darker overlay for the second half of the button
122 auto darkenOffset = dark ? 0x10 : 0x00; // use a deeper color for dark buttons
123 CST_roundedBoxRGBA(
124 RootDisplay::renderer,
125 xAbs, yAbs + height / 2, xAbs + width, yAbs + height, cornerRadius,
126 0x00, 0x00, 0x00, 0x15 + darkenOffset
127 );
128 // inner white outline around the whole thing
129 auto p = 0.5;
130 CST_roundedRectangleRGBA(
131 RootDisplay::renderer,
132 xAbs+2*p, yAbs+2*p, xAbs + width - p, yAbs + height - p, cornerRadius,
133 0xff, 0xff, 0xff, 0x40
134 );
135 // thicker outer gray outline
136 CST_roundedRectangleRGBA(
137 RootDisplay::renderer,
138 xAbs, yAbs, xAbs + width, yAbs + height, cornerRadius,
139 0x00, 0x00, 0x00, 0x80
140 );
141 }
142
143 // render text/icon and bg if needed
145
146 CST_SetDrawBlend(RootDisplay::renderer, false);
147}
148
150{
151 if (event->isKeyDown() && this->physical != 0 && event->held(this->physical))
152 {
153 // invoke our action, since we saw a physical button press that matches!
154 this->action();
155 return true;
156 }
157
158 bool ret = false;
159
160 // if the last gamepad is different from the current one, update the button image
161 if (myLastSeenGamepad != InputEvents::lastGamepadKey)
162 {
163 if (shouldRenderGlossy()) {
164 icon.hide();
165 hasBackground = false; // we'll handle our own two-toned and outlined background drawing
166 cornerRadius = fmin(25, height / 4); // bound the corner radius to the height of our darker highlight
167 } else {
168 auto newPath = getControllerButtonImageForPlatform(this->physical, !dark, false);
169 icon.unhide();
170 hasBackground = true;
171 icon.loadPath(newPath);
172 icon.resize(text.height*1.5, text.height*1.5);
173 }
174 myLastSeenGamepad = InputEvents::lastGamepadKey;
175 }
176
177 return super::process(event) || ret;
178}
179
180const std::string Button::getText()
181{
182 return this->text.text;
183}
184
185bool Button::shouldRenderGlossy()
186{
187#ifdef WII_MOCK
188 return true;
189#endif
190
191 return InputEvents::lastGamepadKey == "Wii Remote" || InputEvents::lastGamepadKey == "Wii Remote and Nunchuk";
192}
bool process(InputEvents *event)
process any input that is received for this element
Definition: Button.cpp:149
void render(Element *parent)
display the current state of the display
Definition: Button.cpp:102
virtual bool process(InputEvents *event)
process any input that is received for this element
Definition: Element.cpp:17
bool hidden
whether this element should skip rendering or not
Definition: Element.hpp:107
std::function< void()> action
the action to call (from binded callback) on touch or button selection https://stackoverflow....
Definition: Element.hpp:60
virtual void render(Element *parent)
display the current state of the display
Definition: Element.cpp:60
void position(int x, int y)
position the element
Definition: Element.cpp:206
int width
width and height of this element (must be manually set, isn't usually calculated (but is in some case...
Definition: Element.hpp:120
Element * parent
the parent element (can sometimes be null if it isn't set)
Definition: Element.hpp:104
bool held(int buttons)
whether or not a button is pressed during this cycle
void update(bool forceUpdate=false)
update TextElement with changes
Definition: TextElement.cpp:90
void resize(int w, int h)
Resizes the texture.
Definition: Texture.cpp:182
void loadPath(std::string &path, bool forceReload=false)
update and load or reload the texture
Definition: Texture.cpp:231