libgui 2.01
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
GuiControllerView.cpp
1/****************************************************************************
2 * libgui
3 * Daryl Borth 2026
4 * GuiControllerView.cpp
5 ***************************************************************************/
6
7#include "Gui.h"
8#include <cmath>
9#include <algorithm>
10
11static PixelColor withAlpha(PixelColor c, uint8_t a)
12{
13 c.a = a;
14 return c;
15}
16
17// ---- the element
18
20 layout(&l), scale(s), leader(false), leaderX(0), leaderY(0), meshFeather(-1.0f), dirty(true), originX(0), originY(0), alpha(255)
21{
22 rebuild();
23}
24
25GuiControllerView::~GuiControllerView()
26{
27 clearLabels();
28}
29
30void GuiControllerView::clearLabels()
31{
32 for(size_t i = 0; i < labels.size(); i++)
33 delete labels[i];
34 labels.clear();
35 labelLit.clear();
36}
37
38void GuiControllerView::rebuild()
39{
40 // (a hair is taken off first: 400 * 0.6f is 240.000004 in floats and must not become 241)
41 width = (int)ceilf(layout->width * scale - 0.01f);
42 height = (int)ceilf(layout->height * scale - 0.01f);
43
44 spots.assign(layout->spots.size(), SpotCache());
45 for(size_t i = 0; i < spots.size(); i++)
46 for(int k = 0; k < 4; k++)
47 spots[i].built[k] = false;
48 stickX.assign(layout->spots.size(), 0.0f);
49 stickY.assign(layout->spots.size(), 0.0f);
50
51 // labels are text children centered on their control
52 clearLabels();
53 const GuiTheme& theme = GuiTheme::current();
54 for(size_t i = 0; i < layout->spots.size(); i++)
55 {
56 const GuiControlSpot& s = layout->spots[i];
57 GuiText * t = nullptr;
58 if(s.label)
59 {
60 t = new GuiText(s.label, (int)(15.0f * std::max(scale, 0.7f)), theme.textMuted);
61 t->setParent(this);
62 t->setAlignment(ALIGN_H::CENTRE, ALIGN_V::MIDDLE);
63 const float cx = (s.x + s.w * 0.5f) * scale - width * 0.5f;
64 const float cy = (s.y + s.h * 0.5f) * scale - height * 0.5f;
65 t->setPosition((int)floorf(cx + 0.5f), (int)floorf(cy + 0.5f));
66 }
67 labels.push_back(t);
68 labelLit.push_back(false);
69 }
70 dirty = true;
71}
72
74{
75 layout = &l;
76 highlighted.clear();
77 rebuild();
78}
79
81{
82 scale = s;
83 rebuild();
84}
85
87{
88 highlighted.clear();
89 if(id != 0)
90 highlighted.push_back(id);
91}
92
93void GuiControllerView::setHighlight(const std::vector<int>& ids)
94{
95 highlighted = ids;
96}
97
98void GuiControllerView::setLeader(bool on, float fromX, float fromY)
99{
100 leader = on;
101 leaderX = fromX;
102 leaderY = fromY;
103}
104
105void GuiControllerView::setStick(int id, float x, float y)
106{
107 for(size_t i = 0; i < layout->spots.size(); i++)
108 {
109 if(layout->spots[i].id == id && layout->spots[i].shape == CTL_SHAPE::STICK)
110 {
111 stickX[i] = x;
112 stickY[i] = y;
113 }
114 }
115}
116
117bool GuiControllerView::getSpotCentre(int id, float& x, float& y) const
118{
119 const GuiControlSpot * s = layout->find(id);
120 if(!s)
121 return false;
122 x = originX + (s->x + s->w * 0.5f) * scale;
123 y = originY + (s->y + s->h * 0.5f) * scale;
124 return true;
125}
126
127void GuiControllerView::emit(const GuiMesh& m)
128{
129 const std::vector<MeshVertex>& src = m.vertices();
130 for(size_t i = 0; i < src.size(); i++)
131 {
132 MeshVertex v = src[i];
133 v.x += originX;
134 v.y += originY;
135 if(alpha < 255)
136 v.c.a = (uint8_t)((v.c.a * alpha + 127) / 255);
137 scratch.push_back(v);
138 }
139}
140
142{
143 if(!this->isVisible())
144 return;
145
146 alpha = this->getAlpha();
147 if(alpha > 0)
148 {
149 VideoDriver * video = platform->getVideo();
150 const float uiScale = video->getUIScale();
151 const float feather = uiScale > 0.0f ? 1.0f / uiScale : 1.0f;
152 if(dirty || feather != meshFeather)
153 {
154 body = GuiMesh(feather);
155 GuiBuildControllerBody(body, *layout, scale);
156 for(size_t i = 0; i < spots.size(); i++)
157 for(int k = 0; k < 4; k++)
158 {
159 spots[i].mesh[k] = GuiMesh(feather);
160 spots[i].built[k] = false;
161 }
162 meshFeather = feather;
163 dirty = false;
164 }
165
166 originX = (float)this->getLeft();
167 originY = (float)this->getTop();
168 scratch.clear();
169 emit(body);
170
171 // resting controls first, highlighted ones on top so their glow is not painted over
172 for(int pass = 0; pass < 2; pass++)
173 {
174 for(size_t i = 0; i < layout->spots.size(); i++)
175 {
176 const GuiControlSpot& s = layout->spots[i];
177 if(s.id == 0)
178 continue;
179 const bool hl = std::find(highlighted.begin(), highlighted.end(), s.id) != highlighted.end();
180 if((pass == 1) != hl)
181 continue;
182 const bool isLit = lit && lit(s.id);
183 const int k = (isLit ? 1 : 0) + (hl ? 2 : 0);
184 if(!spots[i].built[k])
185 {
186 GuiBuildControllerSpot(spots[i].mesh[k], s, (CTL_STATE)k, scale);
187 spots[i].built[k] = true;
188 }
189 emit(spots[i].mesh[k]);
190
191 if(labels[i] && labelLit[i] != isLit)
192 {
193 labels[i]->setColor(isLit ? GuiTheme::current().textOnAccent : GuiTheme::current().textMuted);
194 labelLit[i] = isLit;
195 }
196 }
197 }
198
199 // stick caps move, so they are built each frame
200 for(size_t i = 0; i < layout->spots.size(); i++)
201 {
202 const GuiControlSpot& s = layout->spots[i];
203 if(s.shape != CTL_SHAPE::STICK)
204 continue;
205 GuiMesh cap(feather);
206 GuiBuildControllerStickCap(cap, s, stickX[i], stickY[i], lit && lit(s.id), scale);
207 emit(cap);
208 }
209
210 // a line from a list row to the highlighted control
211 if(leader && !highlighted.empty())
212 {
213 float tx, ty;
214 if(getSpotCentre(highlighted[0], tx, ty))
215 {
216 const GuiTheme& theme = GuiTheme::current();
217 GuiMesh line(feather);
218 line.addLine({ leaderX, leaderY }, { tx, ty }, 1.6f, GuiFill::solid(withAlpha(theme.accent, 200)));
219 GuiRect dot = { tx - 3.5f, ty - 3.5f, 7.0f, 7.0f };
220 line.addEllipse(dot, GuiFill::solid(theme.accent));
221 // the mesh is already in canvas coordinates: draw it as it is
222 const std::vector<MeshVertex>& src = line.vertices();
223 for(size_t i = 0; i < src.size(); i++)
224 {
225 MeshVertex v = src[i];
226 if(alpha < 255)
227 v.c.a = (uint8_t)((v.c.a * alpha + 127) / 255);
228 scratch.push_back(v);
229 }
230 }
231 }
232
233 if(!scratch.empty())
234 video->getImageRenderer()->drawMesh(scratch.data(), scratch.size());
235
236 for(size_t i = 0; i < labels.size(); i++)
237 if(labels[i])
238 labels[i]->draw();
239 }
240
241 this->updateEffects();
242}
Umbrella header for the platform-agnostic libgui core.
void setHighlight(int id)
Highlights one control (0 = none)
void setLeader(bool on, float fromX=0.0f, float fromY=0.0f)
Draws a line from a point (design-canvas coordinates, such as a list row's edge) to the first highlig...
void draw() override
Called constantly to redraw the element.
void setLayout(const GuiControllerLayout &layout)
Changes the controller drawn.
GuiControllerView(const GuiControllerLayout &layout, float scale=1.0f)
void setStick(int id, float x, float y)
Sets an analog stick's position (-1 to 1 each)
bool getSpotCentre(int id, float &x, float &y) const
Gets the center of a control in design-canvas coordinates (false if the layout has no such control)
void setScale(float scale)
Changes the size.
bool isVisible()
Checks whether or not the element is visible.
int getTop()
Gets the current topmost coordinate of the element Considers vertical alignment, y offset,...
int width
Element width.
Definition GuiElement.h:268
int height
Element height.
Definition GuiElement.h:269
void updateEffects()
Updates the element's effects (dynamic values) Called by Draw(), used for animation purposes.
int getLeft()
Gets the current leftmost coordinate of the element Considers horizontal alignment,...
void setParent(GuiElement *e)
Set the element's parent.
void setPosition(int x, int y)
Sets the element's position.
int getAlpha()
Gets the element's alpha value Considers alpha, alphaDyn, and the parent element's GetAlpha() value.
Accumulates shapes as a triangle list of MeshVertex.
void addLine(GuiPoint a, GuiPoint b, float width, const GuiFill &fill)
Adds a straight line with flat ends.
const std::vector< MeshVertex > & vertices() const
Gets the triangle list.
void addEllipse(const GuiRect &r, const GuiFill &fill, bool edgeFringe=true)
Adds a filled ellipse inscribed in a rectangle (a circle when w == h)
void setAlignment(ALIGN_H hor, ALIGN_V vert)
Sets the text alignment.
Definition GuiText.cpp:297
virtual void drawMesh(const MeshVertex *vertices, size_t count)=0
Draws a triangle list: three vertices per triangle, each with its own color.
Platform video backend owning the frame lifecycle (init/render/clear, screen size,...
Definition VideoDriver.h:28
virtual ImageRenderer * getImageRenderer()=0
virtual float getUIScale() const
Ratio of physical render-target pixels to design-canvas pixels (the width/height passed to init()).
Definition VideoDriver.h:58
void GuiBuildControllerSpot(GuiMesh &mesh, const GuiControlSpot &spot, CTL_STATE state, float scale)
Builds one control in one state (an analog stick's cap is separate, see GuiBuildControllerStickCap).
void GuiBuildControllerBody(GuiMesh &mesh, const GuiControllerLayout &layout, float scale)
Builds the resting drawing of a controller (silhouette, then every decorative control) in local coord...
void GuiBuildControllerStickCap(GuiMesh &mesh, const GuiControlSpot &spot, float x, float y, bool lit, float scale)
Builds an analog stick's cap, displaced by (x, y) (each -1 to 1) from the middle of its ring.
CTL_STATE
The look a control is drawn in.
@ STICK
An analog stick: an outer ring with a cap that can move.
One control on a controller drawing.
int id
Input bit this control stands for; 0 for decoration (never lit or highlighted).
float h
Bounding box.
const char * label
Text drawn on the control ("A", "ZL", "+"), or null.
A controller: its drawing size, silhouette and controls.
static GuiFill solid(PixelColor c)
A single flat color.
An axis-aligned rectangle (top-left origin) in design-canvas pixels.
Definition GuiGeometry.h:30
Colors, metrics and the style recipes built from them.
Definition GuiTheme.h:16
static GuiTheme & current()
The theme the rest of the library reads. Starts as the default dark theme.
Definition GuiTheme.cpp:35
PixelColor textMuted
Secondary text.
Definition GuiTheme.h:27
A single 2D vertex with its own color, in design-canvas coordinates.
Definition Mesh.h:27
float y
Y position (design-canvas pixels).
Definition Mesh.h:29
float x
X position (design-canvas pixels).
Definition Mesh.h:28
PixelColor c
Straight (non-premultiplied) RGBA color.
Definition Mesh.h:30
8-bit-per-channel RGBA color used by the video interfaces.
Definition Mesh.h:16
uint8_t a
Alpha component.
Definition Mesh.h:20