Bug Summary

File:LWidgets.c
Warning:line 402, column 13
Value stored to 'y' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple amd64-unknown-openbsd6.8 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name LWidgets.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mthread-model posix -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/10.0.1 -I /usr/X11R6/include -I /usr/local/include -fdebug-compilation-dir /home/ben/Projects/ClassiCube/src -ferror-limit 19 -fmessage-length 0 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -fobjc-runtime=gnustep -fdiagnostics-show-option -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/ClassiCube/src/scan/2020-12-10-180422-33404-1 -x c LWidgets.c
1#include "LWidgets.h"
2#ifndef CC_BUILD_WEB
3#include "String.h"
4#include "Gui.h"
5#include "Drawer2D.h"
6#include "Launcher.h"
7#include "ExtMath.h"
8#include "Window.h"
9#include "Funcs.h"
10#include "LWeb.h"
11#include "Platform.h"
12#include "LScreens.h"
13#include "Input.h"
14#include "Utils.h"
15
16static int xBorder, xBorder2, xBorder3, xBorder4;
17static int yBorder, yBorder2, yBorder3, yBorder4;
18static int xInputOffset, yInputOffset, inputExpand;
19static int caretOffset, caretWidth, caretHeight;
20static int scrollbarWidth, dragPad, gridlineWidth, gridlineHeight;
21static int hdrYOffset, hdrYPadding, rowYOffset, rowYPadding;
22static int cellXOffset, cellXPadding, cellMinWidth;
23static int flagXOffset, flagYOffset;
24
25void LWidget_CalcOffsets(void) {
26 xBorder = Display_ScaleX(1); xBorder2 = xBorder * 2; xBorder3 = xBorder * 3; xBorder4 = xBorder * 4;
27 yBorder = Display_ScaleY(1); yBorder2 = yBorder * 2; yBorder3 = yBorder * 3; yBorder4 = yBorder * 4;
28
29 xInputOffset = Display_ScaleX(5);
30 yInputOffset = Display_ScaleY(2);
31 inputExpand = Display_ScaleX(20);
32
33 caretOffset = Display_ScaleY(5);
34 caretWidth = Display_ScaleX(10);
35 caretHeight = Display_ScaleY(2);
36
37 scrollbarWidth = Display_ScaleX(10);
38 dragPad = Display_ScaleX(8);
39 gridlineWidth = Display_ScaleX(2);
40 gridlineHeight = Display_ScaleY(2);
41
42 hdrYOffset = Display_ScaleY(3);
43 hdrYPadding = Display_ScaleY(5);
44 rowYOffset = Display_ScaleY(3);
45 rowYPadding = Display_ScaleY(1);
46
47 cellXOffset = Display_ScaleX(6);
48 cellXPadding = Display_ScaleX(5);
49 cellMinWidth = Display_ScaleX(20);
50 flagXOffset = Display_ScaleX(2);
51 flagYOffset = Display_ScaleY(6);
52}
53
54void LWidget_SetLocation(void* widget, cc_uint8 horAnchor, cc_uint8 verAnchor, int xOffset, int yOffset) {
55 struct LWidget* w = (struct LWidget*)widget;
56 w->horAnchor = horAnchor; w->verAnchor = verAnchor;
57 w->xOffset = xOffset; w->yOffset = yOffset;
58 LWidget_CalcPosition(widget);
59}
60
61void LWidget_CalcPosition(void* widget) {
62 struct LWidget* w = (struct LWidget*)widget;
63 w->x = Gui_CalcPos(w->horAnchor, Display_ScaleX(w->xOffset), w->width, WindowInfo.Width);
64 w->y = Gui_CalcPos(w->verAnchor, Display_ScaleY(w->yOffset), w->height, WindowInfo.Height);
65}
66
67void LWidget_Draw(void* widget) {
68 struct LWidget* w = (struct LWidget*)widget;
69 w->last.X = w->x; w->last.Width = w->width;
70 w->last.Y = w->y; w->last.Height = w->height;
71
72 if (w->hidden) return;
73 w->VTABLE->Draw(w);
74 Launcher_MarkDirty(w->x, w->y, w->width, w->height);
75}
76
77void LWidget_Redraw(void* widget) {
78 struct LWidget* w = (struct LWidget*)widget;
79 Launcher_ResetArea(w->last.X, w->last.Y, w->last.Width, w->last.Height);
80 LWidget_Draw(w);
81}
82
83
84/*########################################################################################################################*
85*------------------------------------------------------ButtonWidget-------------------------------------------------------*
86*#########################################################################################################################*/
87static BitmapCol LButton_Expand(BitmapCol a, int amount) {
88 int r, g, b;
89 r = BitmapCol_R(a)((cc_uint8)(a >> 16)) + amount; Math_Clamp(r, 0, 255)r = r < (0) ? (0) : r; r = r > (255) ? (255) : r;;
90 g = BitmapCol_G(a)((cc_uint8)(a >> 8)) + amount; Math_Clamp(g, 0, 255)g = g < (0) ? (0) : g; g = g > (255) ? (255) : g;;
91 b = BitmapCol_B(a)((cc_uint8)(a >> 0)) + amount; Math_Clamp(b, 0, 255)b = b < (0) ? (0) : b; b = b > (255) ? (255) : b;;
92 return BitmapCol_Make(r, g, b, 255)(((cc_uint8)(r) << 16) | ((cc_uint8)(g) << 8) | (
(cc_uint8)(b) << 0) | ((cc_uint8)(255) << 24))
;
93}
94
95static void LButton_DrawBackground(struct LButton* w) {
96 BitmapCol activeCol = BitmapCol_Make(126, 136, 191, 255)(((cc_uint8)(126) << 16) | ((cc_uint8)(136) << 8)
| ((cc_uint8)(191) << 0) | ((cc_uint8)(255) << 24
))
;
97 BitmapCol inactiveCol = BitmapCol_Make(111, 111, 111, 255)(((cc_uint8)(111) << 16) | ((cc_uint8)(111) << 8)
| ((cc_uint8)(111) << 0) | ((cc_uint8)(255) << 24
))
;
98 BitmapCol col;
99
100 if (Launcher_ClassicBackground) {
101 col = w->hovered ? activeCol : inactiveCol;
102 Gradient_Noise(&Launcher_Framebuffer, col, 8,
103 w->x + xBorder, w->y + yBorder,
104 w->width - xBorder2, w->height - yBorder2);
105 } else {
106 col = w->hovered ? Launcher_ButtonForeActiveCol : Launcher_ButtonForeCol;
107 Gradient_Vertical(&Launcher_Framebuffer, LButton_Expand(col, 8), LButton_Expand(col, -8),
108 w->x + xBorder, w->y + yBorder,
109 w->width - xBorder2, w->height - yBorder2);
110 }
111}
112
113static void LButton_DrawBorder(struct LButton* w) {
114 BitmapCol black = BitmapCol_Make(0, 0, 0, 255)(((cc_uint8)(0) << 16) | ((cc_uint8)(0) << 8) | (
(cc_uint8)(0) << 0) | ((cc_uint8)(255) << 24))
;
115 BitmapCol backCol = Launcher_ClassicBackground ? black : Launcher_ButtonBorderCol;
116
117 Drawer2D_Clear(&Launcher_Framebuffer, backCol,
118 w->x + xBorder, w->y,
119 w->width - xBorder2, yBorder);
120 Drawer2D_Clear(&Launcher_Framebuffer, backCol,
121 w->x + xBorder, w->y + w->height - yBorder,
122 w->width - xBorder2, yBorder);
123 Drawer2D_Clear(&Launcher_Framebuffer, backCol,
124 w->x, w->y + yBorder,
125 xBorder, w->height - yBorder2);
126 Drawer2D_Clear(&Launcher_Framebuffer, backCol,
127 w->x + w->width - xBorder, w->y + yBorder,
128 xBorder, w->height - yBorder2);
129}
130
131static void LButton_DrawHighlight(struct LButton* w) {
132 BitmapCol activeCol = BitmapCol_Make(189, 198, 255, 255)(((cc_uint8)(189) << 16) | ((cc_uint8)(198) << 8)
| ((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 24
))
;
133 BitmapCol inactiveCol = BitmapCol_Make(168, 168, 168, 255)(((cc_uint8)(168) << 16) | ((cc_uint8)(168) << 8)
| ((cc_uint8)(168) << 0) | ((cc_uint8)(255) << 24
))
;
134 BitmapCol highlightCol;
135
136 if (Launcher_ClassicBackground) {
137 highlightCol = w->hovered ? activeCol : inactiveCol;
138 Drawer2D_Clear(&Launcher_Framebuffer, highlightCol,
139 w->x + xBorder2, w->y + yBorder,
140 w->width - xBorder4, yBorder);
141 Drawer2D_Clear(&Launcher_Framebuffer, highlightCol,
142 w->x + xBorder, w->y + yBorder2,
143 xBorder, w->height - yBorder4);
144 } else if (!w->hovered) {
145 Drawer2D_Clear(&Launcher_Framebuffer, Launcher_ButtonHighlightCol,
146 w->x + xBorder2, w->y + yBorder,
147 w->width - xBorder4, yBorder);
148 }
149}
150
151static void LButton_Draw(void* widget) {
152 struct LButton* w = (struct LButton*)widget;
153 struct DrawTextArgs args;
154 int xOffset, yOffset;
155 if (w->hidden) return;
156
157 xOffset = w->width - w->_textWidth;
158 yOffset = w->height - w->_textHeight;
159 DrawTextArgs_Make(&args, &w->text, &Launcher_TitleFont, true1);
160
161 LButton_DrawBackground(w);
162 LButton_DrawBorder(w);
163 LButton_DrawHighlight(w);
164
165 if (!w->hovered) Drawer2D_Cols['f'] = Drawer2D_Cols['7'];
166 Drawer2D_DrawText(&Launcher_Framebuffer, &args,
167 w->x + xOffset / 2, w->y + yOffset / 2);
168
169 if (!w->hovered) Drawer2D_Cols['f'] = Drawer2D_Cols['F'];
170 Launcher_MarkDirty(w->x, w->y, w->width, w->height);
171}
172
173static void LButton_Hover(void* w, int x, int y, cc_bool wasOver) {
174 /* only need to redraw when changing from unhovered to hovered */
175 if (!wasOver) LWidget_Draw(w);
176}
177
178static const struct LWidgetVTABLE lbutton_VTABLE = {
179 LButton_Draw, NULL((void*)0),
180 NULL((void*)0), NULL((void*)0), /* Key */
181 LButton_Hover, LWidget_Draw, /* Hover */
182 NULL((void*)0), NULL((void*)0) /* Select */
183};
184void LButton_Init(struct LScreen* s, struct LButton* w, int width, int height, const char* text) {
185 w->VTABLE = &lbutton_VTABLE;
186 w->tabSelectable = true1;
187 w->width = Display_ScaleX(width);
188 w->height = Display_ScaleY(height);
189
190 LButton_SetConst(w, text);
191 s->widgets[s->numWidgets++] = (struct LWidget*)w;
192}
193
194void LButton_SetConst(struct LButton* w, const char* text) {
195 struct DrawTextArgs args;
196 w->text = String_FromReadonly(text);
197 DrawTextArgs_Make(&args, &w->text, &Launcher_TitleFont, true1);
198 w->_textWidth = Drawer2D_TextWidth(&args);
199 w->_textHeight = Drawer2D_TextHeight(&args);
200}
201
202
203/*########################################################################################################################*
204*------------------------------------------------------InputWidget--------------------------------------------------------*
205*#########################################################################################################################*/
206CC_NOINLINE__attribute__((noinline)) static void LInput_GetText(struct LInput* w, cc_string* text) {
207 int i;
208 if (w->type != KEYBOARD_TYPE_PASSWORD) { *text = w->text; return; }
209
210 for (i = 0; i < w->text.length; i++) {
211 String_Append(text, '*');
212 }
213}
214
215static void LInput_DrawOuterBorder(struct LInput* w) {
216 BitmapCol col = BitmapCol_Make(97, 81, 110, 255)(((cc_uint8)(97) << 16) | ((cc_uint8)(81) << 8) |
((cc_uint8)(110) << 0) | ((cc_uint8)(255) << 24)
)
;
217
218 if (w->selected) {
219 Drawer2D_Clear(&Launcher_Framebuffer, col,
220 w->x, w->y,
221 w->width, yBorder);
222 Drawer2D_Clear(&Launcher_Framebuffer, col,
223 w->x, w->y + w->height - yBorder,
224 w->width, yBorder);
225 Drawer2D_Clear(&Launcher_Framebuffer, col,
226 w->x, w->y,
227 xBorder, w->height);
228 Drawer2D_Clear(&Launcher_Framebuffer, col,
229 w->x + w->width - xBorder, w->y,
230 xBorder, w->height);
231 } else {
232 Launcher_ResetArea(w->x, w->y,
233 w->width, yBorder);
234 Launcher_ResetArea(w->x, w->y + w->height - yBorder,
235 w->width, yBorder);
236 Launcher_ResetArea(w->x, w->y,
237 xBorder, w->height);
238 Launcher_ResetArea(w->x + w->width - xBorder, w->y,
239 xBorder, w->height);
240 }
241}
242
243static void LInput_DrawInnerBorder(struct LInput* w) {
244 BitmapCol col = BitmapCol_Make(165, 142, 168, 255)(((cc_uint8)(165) << 16) | ((cc_uint8)(142) << 8)
| ((cc_uint8)(168) << 0) | ((cc_uint8)(255) << 24
))
;
245
246 Drawer2D_Clear(&Launcher_Framebuffer, col,
247 w->x + xBorder, w->y + yBorder,
248 w->width - xBorder2, yBorder);
249 Drawer2D_Clear(&Launcher_Framebuffer, col,
250 w->x + xBorder, w->y + w->height - yBorder2,
251 w->width - xBorder2, yBorder);
252 Drawer2D_Clear(&Launcher_Framebuffer, col,
253 w->x + xBorder, w->y + yBorder,
254 xBorder, w->height - yBorder2);
255 Drawer2D_Clear(&Launcher_Framebuffer, col,
256 w->x + w->width - xBorder2, w->y + yBorder,
257 xBorder, w->height - yBorder2);
258}
259
260static void LInput_BlendBoxTop(struct LInput* w) {
261 BitmapCol col = BitmapCol_Make(0, 0, 0, 255)(((cc_uint8)(0) << 16) | ((cc_uint8)(0) << 8) | (
(cc_uint8)(0) << 0) | ((cc_uint8)(255) << 24))
;
262
263 Gradient_Blend(&Launcher_Framebuffer, col, 75,
264 w->x + xBorder, w->y + yBorder,
265 w->width - xBorder2, yBorder);
266 Gradient_Blend(&Launcher_Framebuffer, col, 50,
267 w->x + xBorder, w->y + yBorder2,
268 w->width - xBorder2, yBorder);
269 Gradient_Blend(&Launcher_Framebuffer, col, 25,
270 w->x + xBorder, w->y + yBorder3,
271 w->width - xBorder2, yBorder);
272}
273
274static void LInput_DrawText(struct LInput* w, struct DrawTextArgs* args) {
275 int y, hintHeight;
276
277 if (w->text.length || !w->hintText) {
278 y = w->y + (w->height - w->_textHeight) / 2;
279 Drawer2D_DrawText(&Launcher_Framebuffer, args,
280 w->x + xInputOffset, y + yInputOffset);
281 } else {
282 args->text = String_FromReadonly(w->hintText);
283 args->font = &Launcher_HintFont;
284
285 hintHeight = Drawer2D_TextHeight(args);
286 y = w->y + (w->height - hintHeight) / 2;
287 Drawer2D_DrawText(&Launcher_Framebuffer, args,
288 w->x + xInputOffset, y);
289 }
290}
291
292static void LInput_Draw(void* widget) {
293 struct LInput* w = (struct LInput*)widget;
294 cc_string text; char textBuffer[STRING_SIZE64];
295 struct DrawTextArgs args;
296 int textWidth;
297
298 String_InitArray(text, textBuffer)text.buffer = textBuffer; text.length = 0; text.capacity = sizeof
(textBuffer);
;
299 LInput_GetText(w, &text);
300 DrawTextArgs_Make(&args, &text, &Launcher_TextFont, false0);
301
302 textWidth = Drawer2D_TextWidth(&args);
303 w->width = max(w->minWidth, textWidth + inputExpand)((w->minWidth) > (textWidth + inputExpand) ? (w->minWidth
) : (textWidth + inputExpand))
;
304 w->_textHeight = Drawer2D_TextHeight(&args);
305
306 LInput_DrawOuterBorder(w);
307 LInput_DrawInnerBorder(w);
308 Drawer2D_Clear(&Launcher_Framebuffer, BITMAPCOL_WHITE(((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 8)
| ((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 24
))
,
309 w->x + xBorder2, w->y + yBorder2,
310 w->width - xBorder4, w->height - yBorder4);
311 LInput_BlendBoxTop(w);
312
313 Drawer2D_Cols['f'] = Drawer2D_Cols['0'];
314 LInput_DrawText(w, &args);
315 Drawer2D_Cols['f'] = Drawer2D_Cols['F'];
316}
317
318static Rect2D LInput_MeasureCaret(struct LInput* w) {
319 cc_string text; char textBuffer[STRING_SIZE64];
320 struct DrawTextArgs args;
321 Rect2D r;
322
323 String_InitArray(text, textBuffer)text.buffer = textBuffer; text.length = 0; text.capacity = sizeof
(textBuffer);
;
324 LInput_GetText(w, &text);
325 DrawTextArgs_Make(&args, &text, &Launcher_TextFont, true1);
326
327 r.X = w->x + xInputOffset;
328 r.Y = w->y + w->height - caretOffset; r.Height = caretHeight;
329
330 if (w->caretPos == -1) {
331 r.X += Drawer2D_TextWidth(&args);
332 r.Width = caretWidth;
333 } else {
334 args.text = String_UNSAFE_Substring(&text, 0, w->caretPos);
335 r.X += Drawer2D_TextWidth(&args);
336
337 args.text = String_UNSAFE_Substring(&text, w->caretPos, 1);
338 r.Width = Drawer2D_TextWidth(&args);
339 }
340 return r;
341}
342
343static TimeMS caretStart;
344static cc_bool lastCaretShow;
345static Rect2D lastCaretRec;
346#define Rect2D_Equals(a, b)a.X == b.X && a.Y == b.Y && a.Width == b.Width
&& a.Height == b.Height
a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height
347
348static void LInput_TickCaret(void* widget) {
349 struct LInput* w = (struct LInput*)widget;
350 int elapsed;
351 cc_bool caretShow;
352 Rect2D r;
353
354 if (!caretStart) return;
355 elapsed = (int)(DateTime_CurrentUTC_MS() - caretStart);
356
357 caretShow = (elapsed % 1000) < 500;
358 if (caretShow == lastCaretShow) return;
359 lastCaretShow = caretShow;
360
361 LInput_Draw(w);
362 r = LInput_MeasureCaret(w);
363
364 if (caretShow) {
365 Drawer2D_Clear(&Launcher_Framebuffer, BITMAPCOL_BLACK(((cc_uint8)(0) << 16) | ((cc_uint8)(0) << 8) | (
(cc_uint8)(0) << 0) | ((cc_uint8)(255) << 24))
,
366 r.X, r.Y, r.Width, r.Height);
367 }
368
369 if (Rect2D_Equals(r, lastCaretRec)r.X == lastCaretRec.X && r.Y == lastCaretRec.Y &&
r.Width == lastCaretRec.Width && r.Height == lastCaretRec
.Height
) {
370 /* Fast path, caret is blinking in same spot */
371 Launcher_MarkDirty(r.X, r.Y, r.Width, r.Height);
372 } else {
373 Launcher_MarkDirty(w->x, w->y, w->width, w->height);
374 }
375 lastCaretRec = r;
376}
377
378static void LInput_AdvanceCaretPos(struct LInput* w, cc_bool forwards) {
379 if (forwards && w->caretPos == -1) return;
380 if (!forwards && w->caretPos == 0) return;
381 if (w->caretPos == -1 && !forwards) /* caret after text */
382 w->caretPos = w->text.length;
383
384 w->caretPos += (forwards ? 1 : -1);
385 if (w->caretPos < 0 || w->caretPos >= w->text.length) w->caretPos = -1;
386 LWidget_Redraw(w);
387}
388
389static void LInput_MoveCaretToCursor(struct LInput* w) {
390 cc_string text; char textBuffer[STRING_SIZE64];
391 struct DrawTextArgs args;
392 int i, charX, charWidth;
393 int x = Mouse_X, y = Mouse_Y;
394
395 /* Input widget may have been selected by pressing tab */
396 /* In which case cursor is completely outside, so ignore */
397 if (!Gui_Contains(w->x, w->y, w->width, w->height, x, y)) return;
398 lastCaretShow = false0;
399
400 String_InitArray(text, textBuffer)text.buffer = textBuffer; text.length = 0; text.capacity = sizeof
(textBuffer);
;
401 LInput_GetText(w, &text);
402 x -= w->x; y -= w->y;
Value stored to 'y' is never read
403
404 DrawTextArgs_Make(&args, &text, &Launcher_TextFont, true1);
405 if (x >= Drawer2D_TextWidth(&args)) {
406 w->caretPos = -1; return;
407 }
408
409 for (i = 0; i < text.length; i++) {
410 args.text = String_UNSAFE_Substring(&text, 0, i);
411 charX = Drawer2D_TextWidth(&args);
412
413 args.text = String_UNSAFE_Substring(&text, i, 1);
414 charWidth = Drawer2D_TextWidth(&args);
415 if (x >= charX && x < charX + charWidth) {
416 w->caretPos = i; return;
417 }
418 }
419}
420
421static void LInput_Select(void* widget, cc_bool wasSelected) {
422 struct LInput* w = (struct LInput*)widget;
423 caretStart = DateTime_CurrentUTC_MS();
424 LInput_MoveCaretToCursor(w);
425 /* TODO: Only draw outer border */
426 if (wasSelected) return;
427 LWidget_Draw(widget);
428 Window_OpenKeyboard(&w->text, w->type);
429}
430
431static void LInput_Unselect(void* widget) {
432 caretStart = 0;
433 /* TODO: Only draw outer border */
434 LWidget_Draw(widget);
435 Window_CloseKeyboard();
436}
437
438static void LInput_CopyFromClipboard(cc_string* text, void* widget) {
439 struct LInput* w = (struct LInput*)widget;
440 String_UNSAFE_TrimStart(text);
441 String_UNSAFE_TrimEnd(text);
442
443 if (w->ClipboardFilter) w->ClipboardFilter(text);
444 LInput_AppendString(w, text);
445}
446
447static void LInput_KeyDown(void* widget, int key, cc_bool was) {
448 struct LInput* w = (struct LInput*)widget;
449 if (key == KEY_BACKSPACE) {
450 LInput_Backspace(w);
451 } else if (key == KEY_DELETE) {
452 LInput_Delete(w);
453 } else if (key == 'C' && Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) {
454 if (w->text.length) Clipboard_SetText(&w->text);
455 } else if (key == 'V' && Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) {
456 Clipboard_RequestText(LInput_CopyFromClipboard, w);
457 } else if (key == KEY_ESCAPE) {
458 LInput_Clear(w);
459 } else if (key == KEY_LEFT) {
460 LInput_AdvanceCaretPos(w, false0);
461 } else if (key == KEY_RIGHT) {
462 LInput_AdvanceCaretPos(w, true1);
463 }
464}
465
466static void LInput_KeyChar(void* widget, char c) {
467 struct LInput* w = (struct LInput*)widget;
468 LInput_Append(w, c);
469}
470
471static void LInput_TextChanged(void* widget, const cc_string* str) {
472 struct LInput* w = (struct LInput*)widget;
473 LInput_SetText(w, str);
474 if (w->TextChanged) w->TextChanged(w);
475}
476
477static cc_bool LInput_DefaultInputFilter(char c) {
478 return c >= ' ' && c <= '~' && c != '&';
479}
480
481static const struct LWidgetVTABLE linput_VTABLE = {
482 LInput_Draw, LInput_TickCaret,
483 LInput_KeyDown, LInput_KeyChar, /* Key */
484 NULL((void*)0), NULL((void*)0), /* Hover */
485 /* TODO: Don't redraw whole thing, just the outer border */
486 LInput_Select, LInput_Unselect, /* Select */
487 NULL((void*)0), LInput_TextChanged /* TextChanged */
488};
489void LInput_Init(struct LScreen* s, struct LInput* w, int width, const char* hintText) {
490 w->VTABLE = &linput_VTABLE;
491 w->tabSelectable = true1;
492 w->TextFilter = LInput_DefaultInputFilter;
493 String_InitArray(w->text, w->_textBuffer)w->text.buffer = w->_textBuffer; w->text.length = 0;
w->text.capacity = sizeof(w->_textBuffer);
;
494
495 w->width = Display_ScaleX(width);
496 w->height = Display_ScaleY(30);
497 w->minWidth = w->width;
498 LWidget_CalcPosition(w);
499
500 w->hintText = hintText;
501 w->caretPos = -1;
502 s->widgets[s->numWidgets++] = (struct LWidget*)w;
503}
504
505/* If caret position is now beyond end of text, resets to -1 */
506static CC_INLINEinline void LInput_ClampCaret(struct LInput* w) {
507 if (w->caretPos >= w->text.length) w->caretPos = -1;
508}
509
510void LInput_SetText(struct LInput* w, const cc_string* text_) {
511 cc_string text; char textBuffer[STRING_SIZE64];
512 struct DrawTextArgs args;
513 int textWidth;
514
515 String_Copy(&w->text, text_);
516 String_InitArray(text, textBuffer)text.buffer = textBuffer; text.length = 0; text.capacity = sizeof
(textBuffer);
;
517 LInput_GetText(w, &text);
518 DrawTextArgs_Make(&args, &text, &Launcher_TextFont, true1);
519
520 textWidth = Drawer2D_TextWidth(&args);
521 w->width = max(w->minWidth, textWidth + inputExpand)((w->minWidth) > (textWidth + inputExpand) ? (w->minWidth
) : (textWidth + inputExpand))
;
522 w->_textHeight = Drawer2D_TextHeight(&args);
523 LInput_ClampCaret(w);
524}
525
526static CC_NOINLINE__attribute__((noinline)) cc_bool LInput_AppendRaw(struct LInput* w, char c) {
527 if (w->TextFilter(c) && w->text.length < w->text.capacity) {
528 if (w->caretPos == -1) {
529 String_Append(&w->text, c);
530 } else {
531 String_InsertAt(&w->text, w->caretPos, c);
532 w->caretPos++;
533 }
534 return true1;
535 }
536 return false0;
537}
538
539void LInput_Append(struct LInput* w, char c) {
540 cc_bool appended = LInput_AppendRaw(w, c);
541 if (appended && w->TextChanged) w->TextChanged(w);
542 if (appended) LWidget_Redraw(w);
543}
544
545void LInput_AppendString(struct LInput* w, const cc_string* str) {
546 int i, appended = 0;
547 for (i = 0; i < str->length; i++) {
548 if (LInput_AppendRaw(w, str->buffer[i])) appended++;
549 }
550
551 if (appended && w->TextChanged) w->TextChanged(w);
552 if (appended) LWidget_Redraw(w);
553}
554
555void LInput_Backspace(struct LInput* w) {
556 if (!w->text.length || w->caretPos == 0) return;
557
558 if (w->caretPos == -1) {
559 String_DeleteAt(&w->text, w->text.length - 1);
560 } else {
561 String_DeleteAt(&w->text, w->caretPos - 1);
562 w->caretPos--;
563 if (w->caretPos == -1) w->caretPos = 0;
564 }
565
566 if (w->TextChanged) w->TextChanged(w);
567 LInput_ClampCaret(w);
568 LWidget_Redraw(w);
569}
570
571void LInput_Delete(struct LInput* w) {
572 if (!w->text.length || w->caretPos == -1) return;
573
574 String_DeleteAt(&w->text, w->caretPos);
575 if (w->caretPos == -1) w->caretPos = 0;
576
577 if (w->TextChanged) w->TextChanged(w);
578 LInput_ClampCaret(w);
579 LWidget_Redraw(w);
580}
581
582void LInput_Clear(struct LInput* w) {
583 if (!w->text.length) return;
584 w->text.length = 0;
585
586 if (w->TextChanged) w->TextChanged(w);
587 w->caretPos = -1;
588 LWidget_Redraw(w);
589}
590
591
592/*########################################################################################################################*
593*------------------------------------------------------LabelWidget--------------------------------------------------------*
594*#########################################################################################################################*/
595static void LLabel_Draw(void* widget) {
596 struct LLabel* w = (struct LLabel*)widget;
597 struct DrawTextArgs args;
598
599 DrawTextArgs_Make(&args, &w->text, w->font, true1);
600 Drawer2D_DrawText(&Launcher_Framebuffer, &args, w->x, w->y);
601}
602
603static const struct LWidgetVTABLE llabel_VTABLE = {
604 LLabel_Draw, NULL((void*)0),
605 NULL((void*)0), NULL((void*)0), /* Key */
606 NULL((void*)0), NULL((void*)0), /* Hover */
607 NULL((void*)0), NULL((void*)0) /* Select */
608};
609void LLabel_Init(struct LScreen* s, struct LLabel* w, const char* text) {
610 w->VTABLE = &llabel_VTABLE;
611 w->font = &Launcher_TextFont;
612
613 String_InitArray(w->text, w->_textBuffer)w->text.buffer = w->_textBuffer; w->text.length = 0;
w->text.capacity = sizeof(w->_textBuffer);
;
614 LLabel_SetConst(w, text);
615 s->widgets[s->numWidgets++] = (struct LWidget*)w;
616}
617
618void LLabel_SetText(struct LLabel* w, const cc_string* text) {
619 struct DrawTextArgs args;
620 String_Copy(&w->text, text);
621
622 DrawTextArgs_Make(&args, &w->text, w->font, true1);
623 w->width = Drawer2D_TextWidth(&args);
624 w->height = Drawer2D_TextHeight(&args);
625 LWidget_CalcPosition(w);
626}
627
628void LLabel_SetConst(struct LLabel* w, const char* text) {
629 cc_string str = String_FromReadonly(text);
630 LLabel_SetText(w, &str);
631}
632
633
634/*########################################################################################################################*
635*-------------------------------------------------------BoxWidget---------------------------------------------------------*
636*#########################################################################################################################*/
637#define CLASSIC_LINE_COL(((cc_uint8)(128) << 16) | ((cc_uint8)(128) << 8)
| ((cc_uint8)(128) << 0) | ((cc_uint8)(255) << 24
))
BitmapCol_Make(128,128,128, 255)(((cc_uint8)(128) << 16) | ((cc_uint8)(128) << 8)
| ((cc_uint8)(128) << 0) | ((cc_uint8)(255) << 24
))
638static void LLine_Draw(void* widget) {
639 struct LLine* w = (struct LLine*)widget;
640 BitmapCol col = Launcher_ClassicBackground ? CLASSIC_LINE_COL(((cc_uint8)(128) << 16) | ((cc_uint8)(128) << 8)
| ((cc_uint8)(128) << 0) | ((cc_uint8)(255) << 24
))
: Launcher_ButtonBorderCol;
641 Gradient_Blend(&Launcher_Framebuffer, col, 128, w->x, w->y, w->width, w->height);
642}
643
644static const struct LWidgetVTABLE lline_VTABLE = {
645 LLine_Draw, NULL((void*)0),
646 NULL((void*)0), NULL((void*)0), /* Key */
647 NULL((void*)0), NULL((void*)0), /* Hover */
648 NULL((void*)0), NULL((void*)0) /* Select */
649};
650void LLine_Init(struct LScreen* s, struct LLine* w, int width) {
651 w->VTABLE = &lline_VTABLE;
652 w->width = Display_ScaleX(width);
653 w->height = Display_ScaleY(2);
654 s->widgets[s->numWidgets++] = (struct LWidget*)w;
655}
656
657
658/*########################################################################################################################*
659*------------------------------------------------------SliderWidget-------------------------------------------------------*
660*#########################################################################################################################*/
661static void LSlider_DrawBoxBounds(struct LSlider* w) {
662 BitmapCol boundsTop = BitmapCol_Make(119, 100, 132, 255)(((cc_uint8)(119) << 16) | ((cc_uint8)(100) << 8)
| ((cc_uint8)(132) << 0) | ((cc_uint8)(255) << 24
))
;
663 BitmapCol boundsBottom = BitmapCol_Make(150, 130, 165, 255)(((cc_uint8)(150) << 16) | ((cc_uint8)(130) << 8)
| ((cc_uint8)(165) << 0) | ((cc_uint8)(255) << 24
))
;
664
665 /* TODO: Check these are actually right */
666 Drawer2D_Clear(&Launcher_Framebuffer, boundsTop,
667 w->x, w->y,
668 w->width, yBorder);
669 Drawer2D_Clear(&Launcher_Framebuffer, boundsBottom,
670 w->x, w->y + w->height - yBorder,
671 w->width, yBorder);
672
673 Gradient_Vertical(&Launcher_Framebuffer, boundsTop, boundsBottom,
674 w->x, w->y,
675 xBorder, w->height);
676 Gradient_Vertical(&Launcher_Framebuffer, boundsTop, boundsBottom,
677 w->x + w->width - xBorder, w->y,
678 xBorder, w->height);
679}
680
681static void LSlider_DrawBox(struct LSlider* w) {
682 BitmapCol progTop = BitmapCol_Make(220, 204, 233, 255)(((cc_uint8)(220) << 16) | ((cc_uint8)(204) << 8)
| ((cc_uint8)(233) << 0) | ((cc_uint8)(255) << 24
))
;
683 BitmapCol progBottom = BitmapCol_Make(207, 181, 216, 255)(((cc_uint8)(207) << 16) | ((cc_uint8)(181) << 8)
| ((cc_uint8)(216) << 0) | ((cc_uint8)(255) << 24
))
;
684 int halfHeight = (w->height - yBorder2) / 2;
685
686 Gradient_Vertical(&Launcher_Framebuffer, progTop, progBottom,
687 w->x + xBorder, w->y + yBorder,
688 w->width - xBorder2, halfHeight);
689 Gradient_Vertical(&Launcher_Framebuffer, progBottom, progTop,
690 w->x + xBorder, w->y + yBorder + halfHeight,
691 w->width - xBorder2, halfHeight);
692}
693
694static void LSlider_Draw(void* widget) {
695 struct LSlider* w = (struct LSlider*)widget;
696 int curWidth;
697
698 LSlider_DrawBoxBounds(w);
699 LSlider_DrawBox(w);
700
701 curWidth = (int)((w->width - xBorder2) * w->value / w->maxValue);
702 Drawer2D_Clear(&Launcher_Framebuffer, w->col,
703 w->x + xBorder, w->y + yBorder,
704 curWidth, w->height - yBorder2);
705}
706
707static const struct LWidgetVTABLE lslider_VTABLE = {
708 LSlider_Draw, NULL((void*)0),
709 NULL((void*)0), NULL((void*)0), /* Key */
710 NULL((void*)0), NULL((void*)0), /* Hover */
711 NULL((void*)0), NULL((void*)0) /* Select */
712};
713void LSlider_Init(struct LScreen* s, struct LSlider* w, int width, int height, BitmapCol col) {
714 w->VTABLE = &lslider_VTABLE;
715 w->width = Display_ScaleX(width);
716 w->height = Display_ScaleY(height);
717 w->maxValue = 100;
718 w->col = col;
719 s->widgets[s->numWidgets++] = (struct LWidget*)w;
720}
721
722
723/*########################################################################################################################*
724*------------------------------------------------------TableWidget--------------------------------------------------------*
725*#########################################################################################################################*/
726static void FlagColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
727 struct Bitmap* bmp = Flags_Get(row);
728 if (!bmp) return;
729 Drawer2D_BmpCopy(&Launcher_Framebuffer, x + flagXOffset, y + flagYOffset, bmp);
730}
731
732static void NameColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
733 args->text = row->name;
734}
735static int NameColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
736 return String_Compare(&b->name, &a->name);
737}
738
739static void PlayersColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
740 String_Format2(&args->text, "%i/%i", &row->players, &row->maxPlayers);
741}
742static int PlayersColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
743 return b->players - a->players;
744}
745
746static void UptimeColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
747 int uptime = row->uptime;
748 char unit = 's';
749
750 if (uptime >= SECS_PER_DAY(60 * 60 * 24) * 7) {
751 uptime /= SECS_PER_DAY(60 * 60 * 24); unit = 'd';
752 } else if (uptime >= SECS_PER_HOUR(60 * 60)) {
753 uptime /= SECS_PER_HOUR(60 * 60); unit = 'h';
754 } else if (uptime >= SECS_PER_MIN60) {
755 uptime /= SECS_PER_MIN60; unit = 'm';
756 }
757 String_Format2(&args->text, "%i%r", &uptime, &unit);
758}
759static int UptimeColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
760 return b->uptime - a->uptime;
761}
762
763static void SoftwareColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
764 args->text = row->software;
765}
766static int SoftwareColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
767 return String_Compare(&b->software, &a->software);
768}
769
770static struct LTableColumn tableColumns[5] = {
771 { "", 15, FlagColumn_Draw, NULL((void*)0), false0, false0 },
772 { "Name", 328, NameColumn_Draw, NameColumn_Sort, true1, true1 },
773 { "Players", 73, PlayersColumn_Draw, PlayersColumn_Sort, true1, true1 },
774 { "Uptime", 73, UptimeColumn_Draw, UptimeColumn_Sort, true1, true1 },
775 { "Software", 143, SoftwareColumn_Draw, SoftwareColumn_Sort, false0, true1 }
776};
777
778
779static int sortingCol = -1;
780/* Works out top and height of the scrollbar */
781static void LTable_GetScrollbarCoords(struct LTable* w, int* y, int* height) {
782 float scale;
783 if (!w->rowsCount) { *y = 0; *height = 0; return; }
784
785 scale = w->height / (float)w->rowsCount;
786 *y = Math_Ceil(w->topRow * scale);
787 *height = Math_Ceil(w->visibleRows * scale);
788 *height = min(*y + *height, w->height)((*y + *height) < (w->height) ? (*y + *height) : (w->
height))
- *y;
789}
790
791/* Ensures top/first visible row index lies within table */
792static void LTable_ClampTopRow(struct LTable* w) {
793 if (w->topRow > w->rowsCount - w->visibleRows) {
794 w->topRow = w->rowsCount - w->visibleRows;
795 }
796 if (w->topRow < 0) w->topRow = 0;
797}
798
799/* Returns index of selected row in currently visible rows */
800static int LTable_GetSelectedIndex(struct LTable* w) {
801 struct ServerInfo* entry;
802 int row;
803
804 for (row = 0; row < w->rowsCount; row++) {
805 entry = LTable_Get(row)(&FetchServersTask.servers[FetchServersTask.servers[row].
_order])
;
806 if (String_CaselessEquals(w->selectedHash, &entry->hash)) return row;
807 }
808 return -1;
809}
810
811/* Sets selected row to given row, scrolling table if needed */
812static void LTable_SetSelectedTo(struct LTable* w, int index) {
813 if (!w->rowsCount) return;
814 if (index >= w->rowsCount) index = w->rowsCount - 1;
815 if (index < 0) index = 0;
816
817 String_Copy(w->selectedHash, &LTable_Get(index)(&FetchServersTask.servers[FetchServersTask.servers[index
]._order])
->hash);
818 LTable_ShowSelected(w);
819 w->OnSelectedChanged();
820}
821
822/* Draws background behind column headers */
823static void LTable_DrawHeaderBackground(struct LTable* w) {
824 BitmapCol gridCol = BitmapCol_Make(20, 20, 10, 255)(((cc_uint8)(20) << 16) | ((cc_uint8)(20) << 8) |
((cc_uint8)(10) << 0) | ((cc_uint8)(255) << 24))
;
825
826 if (!Launcher_ClassicBackground) {
827 Drawer2D_Clear(&Launcher_Framebuffer, gridCol,
828 w->x, w->y, w->width, w->hdrHeight);
829 } else {
830 Launcher_ResetArea(w->x, w->y, w->width, w->hdrHeight);
831 }
832}
833
834/* Works out the background colour of the given row */
835static BitmapCol LTable_RowCol(struct LTable* w, struct ServerInfo* row) {
836 BitmapCol gridCol = BitmapCol_Make( 20, 20, 10, 255)(((cc_uint8)(20) << 16) | ((cc_uint8)(20) << 8) |
((cc_uint8)(10) << 0) | ((cc_uint8)(255) << 24))
;
837 BitmapCol featSelCol = BitmapCol_Make( 50, 53, 0, 255)(((cc_uint8)(50) << 16) | ((cc_uint8)(53) << 8) |
((cc_uint8)(0) << 0) | ((cc_uint8)(255) << 24))
;
838 BitmapCol featuredCol = BitmapCol_Make(101, 107, 0, 255)(((cc_uint8)(101) << 16) | ((cc_uint8)(107) << 8)
| ((cc_uint8)(0) << 0) | ((cc_uint8)(255) << 24)
)
;
839 BitmapCol selectedCol = BitmapCol_Make( 40, 40, 40, 255)(((cc_uint8)(40) << 16) | ((cc_uint8)(40) << 8) |
((cc_uint8)(40) << 0) | ((cc_uint8)(255) << 24))
;
840 cc_bool selected;
841
842 if (row) {
843 selected = String_Equals(&row->hash, w->selectedHash);
844 if (row->featured) {
845 return selected ? featSelCol : featuredCol;
846 } else if (selected) {
847 return selectedCol;
848 }
849 }
850 return Launcher_ClassicBackground ? 0 : gridCol;
851}
852
853/* Draws background behind each row in the table */
854static void LTable_DrawRowsBackground(struct LTable* w) {
855 struct ServerInfo* entry;
856 BitmapCol col;
857 int y, height, row;
858
859 y = w->rowsBegY;
860 for (row = w->topRow; ; row++, y += w->rowHeight) {
861 entry = row < w->rowsCount ? LTable_Get(row)(&FetchServersTask.servers[FetchServersTask.servers[row].
_order])
: NULL((void*)0);
862 col = LTable_RowCol(w, entry);
863
864 /* last row may get chopped off */
865 height = min(y + w->rowHeight, w->rowsEndY)((y + w->rowHeight) < (w->rowsEndY) ? (y + w->rowHeight
) : (w->rowsEndY))
- y;
866 /* hit the end of the table */
867 if (height < 0) break;
868
869 if (col) {
870 Drawer2D_Clear(&Launcher_Framebuffer, col,
871 w->x, y, w->width, height);
872 } else {
873 Launcher_ResetArea(w->x, y, w->width, height);
874 }
875 }
876}
877
878/* Draws a gridline below column headers and gridlines after each column */
879static void LTable_DrawGridlines(struct LTable* w) {
880 int i, x;
881 if (Launcher_ClassicBackground) return;
882
883 x = w->x;
884 Drawer2D_Clear(&Launcher_Framebuffer, Launcher_BackgroundCol,
885 x, w->y + w->hdrHeight, w->width, gridlineHeight);
886
887 for (i = 0; i < w->numColumns; i++) {
888 x += w->columns[i].width;
889 if (!w->columns[i].hasGridline) continue;
890
891 Drawer2D_Clear(&Launcher_Framebuffer, Launcher_BackgroundCol,
892 x, w->y, gridlineWidth, w->height);
893 x += gridlineWidth;
894 }
895}
896
897/* Draws the entire background of the table */
898static void LTable_DrawBackground(struct LTable* w) {
899 LTable_DrawHeaderBackground(w);
900 LTable_DrawRowsBackground(w);
901 LTable_DrawGridlines(w);
902}
903
904/* Draws title of each column at top of the table */
905static void LTable_DrawHeaders(struct LTable* w) {
906 struct DrawTextArgs args;
907 int i, x, y;
908
909 DrawTextArgs_MakeEmpty(&args, &Launcher_TextFont, true1);
910 x = w->x; y = w->y;
911
912 for (i = 0; i < w->numColumns; i++) {
913 args.text = String_FromReadonly(w->columns[i].name);
914 Drawer2D_DrawClippedText(&Launcher_Framebuffer, &args,
915 x + cellXOffset, y + hdrYOffset,
916 w->columns[i].width - cellXPadding);
917
918 x += w->columns[i].width;
919 if (w->columns[i].hasGridline) x += gridlineWidth;
920 }
921}
922
923/* Draws contents of the currently visible rows in the table */
924static void LTable_DrawRows(struct LTable* w) {
925 cc_string str; char strBuffer[STRING_SIZE64];
926 struct ServerInfo* entry;
927 struct DrawTextArgs args;
928 int i, x, y, row, end;
929
930 String_InitArray(str, strBuffer)str.buffer = strBuffer; str.length = 0; str.capacity = sizeof
(strBuffer);
;
931 DrawTextArgs_Make(&args, &str, w->rowFont, true1);
932 y = w->rowsBegY;
933 end = w->topRow + w->visibleRows;
934
935 for (row = w->topRow; row < end; row++, y += w->rowHeight) {
936 x = w->x;
937
938 if (row >= w->rowsCount) break;
939 if (y + w->rowHeight > w->rowsEndY) break;
940 entry = LTable_Get(row)(&FetchServersTask.servers[FetchServersTask.servers[row].
_order])
;
941
942 for (i = 0; i < w->numColumns; i++) {
943 args.text = str;
944 w->columns[i].DrawRow(entry, &args, x, y);
945
946 if (args.text.length) {
947 Drawer2D_DrawClippedText(&Launcher_Framebuffer, &args,
948 x + cellXOffset, y + rowYOffset,
949 w->columns[i].width - cellXPadding);
950 }
951
952 x += w->columns[i].width;
953 if (w->columns[i].hasGridline) x += gridlineWidth;
954 }
955 }
956}
957
958/* Draws scrollbar on the right edge of the table */
959static void LTable_DrawScrollbar(struct LTable* w) {
960 BitmapCol classicBack = BitmapCol_Make( 80, 80, 80, 255)(((cc_uint8)(80) << 16) | ((cc_uint8)(80) << 8) |
((cc_uint8)(80) << 0) | ((cc_uint8)(255) << 24))
;
961 BitmapCol classicScroll = BitmapCol_Make(160, 160, 160, 255)(((cc_uint8)(160) << 16) | ((cc_uint8)(160) << 8)
| ((cc_uint8)(160) << 0) | ((cc_uint8)(255) << 24
))
;
962 BitmapCol backCol = Launcher_ClassicBackground ? classicBack : Launcher_ButtonBorderCol;
963 BitmapCol scrollCol = Launcher_ClassicBackground ? classicScroll : Launcher_ButtonForeActiveCol;
964
965 int x, y, height;
966 x = w->x + w->width - scrollbarWidth;
967 LTable_GetScrollbarCoords(w, &y, &height);
968
969 Drawer2D_Clear(&Launcher_Framebuffer, backCol,
970 x, w->y, scrollbarWidth, w->height);
971 Drawer2D_Clear(&Launcher_Framebuffer, scrollCol,
972 x, w->y + y, scrollbarWidth, height);
973}
974
975cc_bool LTable_HandlesKey(int key) {
976 return key == KEY_UP || key == KEY_DOWN || key == KEY_PAGEUP || key == KEY_PAGEDOWN;
977}
978
979static void LTable_KeyDown(void* widget, int key, cc_bool was) {
980 struct LTable* w = (struct LTable*)widget;
981 int index = LTable_GetSelectedIndex(w);
982
983 if (key == KEY_UP) {
984 index--;
985 } else if (key == KEY_DOWN) {
986 index++;
987 } else if (key == KEY_PAGEUP) {
988 index -= w->visibleRows;
989 } else if (key == KEY_PAGEDOWN) {
990 index += w->visibleRows;
991 } else { return; }
992
993 w->_lastRow = -1;
994 LTable_SetSelectedTo(w, index);
995}
996
997static void LTable_MouseMove(void* widget, int deltaX, int deltaY, cc_bool wasOver) {
998 struct LTable* w = (struct LTable*)widget;
999 int x = Mouse_X - w->x, y = Mouse_Y - w->y, col;
1000
1001 if (w->draggingScrollbar) {
1002 float scale = w->height / (float)w->rowsCount;
1003 int row = (int)((y - w->mouseOffset) / scale);
1004 /* avoid expensive redraw when possible */
1005 if (w->topRow == row) return;
1006
1007 w->topRow = row;
1008 LTable_ClampTopRow(w);
1009 LWidget_Draw(w);
1010 } else if (w->draggingColumn >= 0) {
1011 if (!deltaX || x >= w->x + w->width - cellMinWidth) return;
1012 col = w->draggingColumn;
1013
1014 w->columns[col].width += deltaX;
1015 Math_Clamp(w->columns[col].width, cellMinWidth, w->width - cellMinWidth)w->columns[col].width = w->columns[col].width < (cellMinWidth
) ? (cellMinWidth) : w->columns[col].width; w->columns[
col].width = w->columns[col].width > (w->width - cellMinWidth
) ? (w->width - cellMinWidth) : w->columns[col].width;
;
1016 LWidget_Draw(w);
1017 }
1018}
1019
1020static void LTable_RowsClick(struct LTable* w) {
1021 int mouseY = Mouse_Y - w->rowsBegY;
1022 int row = w->topRow + mouseY / w->rowHeight;
1023 TimeMS now;
1024
1025 LTable_SetSelectedTo(w, row);
1026 now = DateTime_CurrentUTC_MS();
1027
1028 /* double click on row to join */
1029 if (w->_lastClick + 1000 >= now && row == w->_lastRow) {
1030 Launcher_ConnectToServer(&LTable_Get(row)(&FetchServersTask.servers[FetchServersTask.servers[row].
_order])
->hash);
1031 }
1032
1033 w->_lastRow = LTable_GetSelectedIndex(w);
1034 w->_lastClick = now;
1035}
1036
1037/* Handles clicking on column headers (either resizes a column or sort rows) */
1038static void LTable_HeadersClick(struct LTable* w) {
1039 int x, i, mouseX = Mouse_X;
1040
1041 for (i = 0, x = w->x; i < w->numColumns; i++) {
1042 /* clicked on gridline, begin dragging */
1043 if (mouseX >= (x - dragPad) && mouseX < (x + dragPad) && w->columns[i].interactable) {
1044 w->draggingColumn = i - 1;
1045 return;
1046 }
1047
1048 x += w->columns[i].width;
1049 if (w->columns[i].hasGridline) x += gridlineWidth;
1050 }
1051
1052 for (i = 0, x = w->x; i < w->numColumns; i++) {
1053 if (mouseX >= x && mouseX < (x + w->columns[i].width) && w->columns[i].interactable) {
1054 sortingCol = i;
1055 w->columns[i].invertSort = !w->columns[i].invertSort;
1056 LTable_Sort(w);
1057 return;
1058 }
1059
1060 x += w->columns[i].width;
1061 if (w->columns[i].hasGridline) x += gridlineWidth;
1062 }
1063}
1064
1065/* Handles clicking on the scrollbar on right edge of table */
1066static void LTable_ScrollbarClick(struct LTable* w) {
1067 int y, height, mouseY = Mouse_Y - w->y;
1068 LTable_GetScrollbarCoords(w, &y, &height);
1069
1070 if (mouseY < y) {
1071 w->topRow -= w->visibleRows;
1072 } else if (mouseY >= y + height) {
1073 w->topRow += w->visibleRows;
1074 } else {
1075 w->mouseOffset = mouseY - y;
1076 }
1077
1078 w->draggingScrollbar = true1;
1079 LTable_ClampTopRow(w);
1080}
1081
1082static void LTable_MouseDown(void* widget, cc_bool wasSelected) {
1083 struct LTable* w = (struct LTable*)widget;
1084
1085 if (Mouse_X >= WindowInfo.Width - scrollbarWidth) {
1086 LTable_ScrollbarClick(w);
1087 w->_lastRow = -1;
1088 } else if (Mouse_Y < w->rowsBegY) {
1089 LTable_HeadersClick(w);
1090 w->_lastRow = -1;
1091 } else {
1092 LTable_RowsClick(w);
1093 }
1094 LWidget_Draw(w);
1095}
1096
1097static void LTable_MouseWheel(void* widget, float delta) {
1098 struct LTable* w = (struct LTable*)widget;
1099 w->topRow -= Utils_AccumulateWheelDelta(&w->_wheelAcc, delta);
1100 LTable_ClampTopRow(w);
1101 LWidget_Draw(w);
1102 w->_lastRow = -1;
1103}
1104
1105/* Stops an in-progress dragging of resizing column. */
1106static void LTable_StopDragging(void* widget) {
1107 struct LTable* w = (struct LTable*)widget;
1108 w->draggingColumn = -1;
1109 w->draggingScrollbar = false0;
1110 w->mouseOffset = 0;
1111}
1112
1113void LTable_Reposition(struct LTable* w) {
1114 int rowsHeight;
1115 w->hdrHeight = Drawer2D_FontHeight(&Launcher_TextFont, true1) + hdrYPadding;
1116 w->rowHeight = Drawer2D_FontHeight(w->rowFont, true1) + rowYPadding;
1117
1118 w->rowsBegY = w->y + w->hdrHeight + gridlineHeight;
1119 w->rowsEndY = w->y + w->height;
1120 rowsHeight = w->height - (w->rowsBegY - w->y);
1121
1122 w->visibleRows = rowsHeight / w->rowHeight;
1123 LTable_ClampTopRow(w);
1124}
1125
1126static void LTable_Draw(void* widget) {
1127 struct LTable* w = (struct LTable*)widget;
1128 LTable_DrawBackground(w);
1129 LTable_DrawHeaders(w);
1130 LTable_DrawRows(w);
1131 LTable_DrawScrollbar(w);
1132 Launcher_MarkAllDirty();
1133}
1134
1135static const struct LWidgetVTABLE ltable_VTABLE = {
1136 LTable_Draw, NULL((void*)0),
1137 LTable_KeyDown, NULL((void*)0), /* Key */
1138 LTable_MouseMove, NULL((void*)0), /* Hover */
1139 LTable_MouseDown, LTable_StopDragging, /* Select */
1140 LTable_MouseWheel, /* Wheel */
1141};
1142void LTable_Init(struct LTable* w, struct FontDesc* rowFont) {
1143 int i;
1144 w->VTABLE = &ltable_VTABLE;
1145 w->columns = tableColumns;
1146 w->numColumns = Array_Elems(tableColumns)(sizeof(tableColumns) / sizeof(tableColumns[0]));
1147 w->rowFont = rowFont;
1148
1149 for (i = 0; i < w->numColumns; i++) {
1150 w->columns[i].width = Display_ScaleX(w->columns[i].width);
1151 }
1152}
1153
1154void LTable_Reset(struct LTable* w) {
1155 LTable_StopDragging(w);
1156 LTable_Reposition(w);
1157
1158 w->topRow = 0;
1159 w->rowsCount = 0;
1160 sortingCol = -1;
1161 w->_wheelAcc = 0.0f;
1162
1163 w->selectedHash->length = 0;
1164 w->filter->length = 0;
1165}
1166
1167void LTable_ApplyFilter(struct LTable* w) {
1168 int i, j, count;
1169
1170 count = FetchServersTask.numServers;
1171 for (i = 0, j = 0; i < count; i++) {
1172 if (String_CaselessContains(&Servers_Get(i)(&FetchServersTask.servers[FetchServersTask.orders[i]])->name, w->filter)) {
1173 FetchServersTask.servers[j++]._order = FetchServersTask.orders[i];
1174 }
1175 }
1176
1177 w->rowsCount = j;
1178 for (; j < count; j++) {
1179 FetchServersTask.servers[j]._order = -100000;
1180 }
1181
1182 w->_lastRow = -1;
1183 LTable_ClampTopRow(w);
1184}
1185
1186static int LTable_SortOrder(const struct ServerInfo* a, const struct ServerInfo* b) {
1187 int order;
1188 if (sortingCol >= 0) {
1189 order = tableColumns[sortingCol].SortOrder(a, b);
1190 return tableColumns[sortingCol].invertSort ? -order : order;
1191 }
1192
1193 /* Default sort order. (most active server, then by highest uptime) */
1194 if (a->players != b->players) return a->players - b->players;
1195 return a->uptime - b->uptime;
1196}
1197
1198static void LTable_QuickSort(int left, int right) {
1199 cc_uint16* keys = FetchServersTask.orders; cc_uint16 key;
1200
1201 while (left < right) {
1202 int i = left, j = right;
1203 struct ServerInfo* mid = Servers_Get((i + j) >> 1)(&FetchServersTask.servers[FetchServersTask.orders[(i + j
) >> 1]])
;
1204
1205 /* partition the list */
1206 while (i <= j) {
1207 while (LTable_SortOrder(mid, Servers_Get(i)(&FetchServersTask.servers[FetchServersTask.orders[i]])) < 0) i++;
1208 while (LTable_SortOrder(mid, Servers_Get(j)(&FetchServersTask.servers[FetchServersTask.orders[j]])) > 0) j--;
1209 QuickSort_Swap_Maybe()if (i <= j) { key = keys[i]; keys[i] = keys[j]; keys[j] = key
; i++; j--;}
;
1210 }
1211 /* recurse into the smaller subset */
1212 QuickSort_Recurse(LTable_QuickSort)if (j - left <= right - i) { if (left < j) { LTable_QuickSort
(left, j); } left = i;} else { if (i < right) { LTable_QuickSort
(i, right); } right = j;}
1213 }
1214}
1215
1216void LTable_Sort(struct LTable* w) {
1217 if (!FetchServersTask.numServers) return;
1218 FetchServersTask_ResetOrder();
1219 LTable_QuickSort(0, FetchServersTask.numServers - 1);
1220
1221 LTable_ApplyFilter(w);
1222 LTable_ShowSelected(w);
1223}
1224
1225void LTable_ShowSelected(struct LTable* w) {
1226 int i = LTable_GetSelectedIndex(w);
1227 if (i == -1) return;
1228
1229 if (i >= w->topRow + w->visibleRows) {
1230 w->topRow = i - (w->visibleRows - 1);
1231 }
1232 if (i < w->topRow) w->topRow = i;
1233 LTable_ClampTopRow(w);
1234}
1235#endif