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