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/2021-01-09-173753-31878-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 idx, 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, int idx) {
390 cc_string text; char textBuffer[STRING_SIZE64];
391 int x = Pointers[idx].x, y = Pointers[idx].y;
392 struct DrawTextArgs args;
393 int i, charX, charWidth;
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, int idx, cc_bool wasSelected) {
422 struct LInput* w = (struct LInput*)widget;
423 struct OpenKeyboardArgs args;
424 caretStart = DateTime_CurrentUTC_MS();
425 LInput_MoveCaretToCursor(w, idx);
426 /* TODO: Only draw outer border */
427 if (wasSelected) return;
428
429 LWidget_Draw(widget);
430 OpenKeyboardArgs_Init(&args, &w->text, w->type);
431 Window_OpenKeyboard(&args);
432}
433
434static void LInput_Unselect(void* widget, int idx) {
435 caretStart = 0;
436 /* TODO: Only draw outer border */
437 LWidget_Draw(widget);
438 Window_CloseKeyboard();
439}
440
441static void LInput_CopyFromClipboard(cc_string* text, void* widget) {
442 struct LInput* w = (struct LInput*)widget;
443 String_UNSAFE_TrimStart(text);
444 String_UNSAFE_TrimEnd(text);
445
446 if (w->ClipboardFilter) w->ClipboardFilter(text);
447 LInput_AppendString(w, text);
448}
449
450static void LInput_KeyDown(void* widget, int key, cc_bool was) {
451 struct LInput* w = (struct LInput*)widget;
452 if (key == KEY_BACKSPACE) {
453 LInput_Backspace(w);
454 } else if (key == KEY_DELETE) {
455 LInput_Delete(w);
456 } else if (key == 'C' && Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) {
457 if (w->text.length) Clipboard_SetText(&w->text);
458 } else if (key == 'V' && Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) {
459 Clipboard_RequestText(LInput_CopyFromClipboard, w);
460 } else if (key == KEY_ESCAPE) {
461 LInput_Clear(w);
462 } else if (key == KEY_LEFT) {
463 LInput_AdvanceCaretPos(w, false0);
464 } else if (key == KEY_RIGHT) {
465 LInput_AdvanceCaretPos(w, true1);
466 }
467}
468
469static void LInput_KeyChar(void* widget, char c) {
470 struct LInput* w = (struct LInput*)widget;
471 LInput_Append(w, c);
472}
473
474static void LInput_TextChanged(void* widget, const cc_string* str) {
475 struct LInput* w = (struct LInput*)widget;
476 LInput_SetText(w, str);
477 if (w->TextChanged) w->TextChanged(w);
478}
479
480static cc_bool LInput_DefaultInputFilter(char c) {
481 return c >= ' ' && c <= '~' && c != '&';
482}
483
484static const struct LWidgetVTABLE linput_VTABLE = {
485 LInput_Draw, LInput_TickCaret,
486 LInput_KeyDown, LInput_KeyChar, /* Key */
487 NULL((void*)0), NULL((void*)0), /* Hover */
488 /* TODO: Don't redraw whole thing, just the outer border */
489 LInput_Select, LInput_Unselect, /* Select */
490 NULL((void*)0), LInput_TextChanged /* TextChanged */
491};
492void LInput_Init(struct LScreen* s, struct LInput* w, int width, const char* hintText) {
493 w->VTABLE = &linput_VTABLE;
494 w->tabSelectable = true1;
495 w->TextFilter = LInput_DefaultInputFilter;
496 String_InitArray(w->text, w->_textBuffer)w->text.buffer = w->_textBuffer; w->text.length = 0;
w->text.capacity = sizeof(w->_textBuffer);
;
497
498 w->width = Display_ScaleX(width);
499 w->height = Display_ScaleY(30);
500 w->minWidth = w->width;
501 LWidget_CalcPosition(w);
502
503 w->hintText = hintText;
504 w->caretPos = -1;
505 s->widgets[s->numWidgets++] = (struct LWidget*)w;
506}
507
508/* If caret position is now beyond end of text, resets to -1 */
509static CC_INLINEinline void LInput_ClampCaret(struct LInput* w) {
510 if (w->caretPos >= w->text.length) w->caretPos = -1;
511}
512
513void LInput_SetText(struct LInput* w, const cc_string* text_) {
514 cc_string text; char textBuffer[STRING_SIZE64];
515 struct DrawTextArgs args;
516 int textWidth;
517
518 String_Copy(&w->text, text_);
519 String_InitArray(text, textBuffer)text.buffer = textBuffer; text.length = 0; text.capacity = sizeof
(textBuffer);
;
520 LInput_GetText(w, &text);
521 DrawTextArgs_Make(&args, &text, &Launcher_TextFont, true1);
522
523 textWidth = Drawer2D_TextWidth(&args);
524 w->width = max(w->minWidth, textWidth + inputExpand)((w->minWidth) > (textWidth + inputExpand) ? (w->minWidth
) : (textWidth + inputExpand))
;
525 w->_textHeight = Drawer2D_TextHeight(&args);
526 LInput_ClampCaret(w);
527}
528
529static CC_NOINLINE__attribute__((noinline)) cc_bool LInput_AppendRaw(struct LInput* w, char c) {
530 if (w->TextFilter(c) && w->text.length < w->text.capacity) {
531 if (w->caretPos == -1) {
532 String_Append(&w->text, c);
533 } else {
534 String_InsertAt(&w->text, w->caretPos, c);
535 w->caretPos++;
536 }
537 return true1;
538 }
539 return false0;
540}
541
542void LInput_Append(struct LInput* w, char c) {
543 cc_bool appended = LInput_AppendRaw(w, c);
544 if (appended && w->TextChanged) w->TextChanged(w);
545 if (appended) LWidget_Redraw(w);
546}
547
548void LInput_AppendString(struct LInput* w, const cc_string* str) {
549 int i, appended = 0;
550 for (i = 0; i < str->length; i++) {
551 if (LInput_AppendRaw(w, str->buffer[i])) appended++;
552 }
553
554 if (appended && w->TextChanged) w->TextChanged(w);
555 if (appended) LWidget_Redraw(w);
556}
557
558void LInput_Backspace(struct LInput* w) {
559 if (!w->text.length || w->caretPos == 0) return;
560
561 if (w->caretPos == -1) {
562 String_DeleteAt(&w->text, w->text.length - 1);
563 } else {
564 String_DeleteAt(&w->text, w->caretPos - 1);
565 w->caretPos--;
566 if (w->caretPos == -1) w->caretPos = 0;
567 }
568
569 if (w->TextChanged) w->TextChanged(w);
570 LInput_ClampCaret(w);
571 LWidget_Redraw(w);
572}
573
574void LInput_Delete(struct LInput* w) {
575 if (!w->text.length || w->caretPos == -1) return;
576
577 String_DeleteAt(&w->text, w->caretPos);
578 if (w->caretPos == -1) w->caretPos = 0;
579
580 if (w->TextChanged) w->TextChanged(w);
581 LInput_ClampCaret(w);
582 LWidget_Redraw(w);
583}
584
585void LInput_Clear(struct LInput* w) {
586 if (!w->text.length) return;
587 w->text.length = 0;
588
589 if (w->TextChanged) w->TextChanged(w);
590 w->caretPos = -1;
591 LWidget_Redraw(w);
592}
593
594
595/*########################################################################################################################*
596*------------------------------------------------------LabelWidget--------------------------------------------------------*
597*#########################################################################################################################*/
598static void LLabel_Draw(void* widget) {
599 struct LLabel* w = (struct LLabel*)widget;
600 struct DrawTextArgs args;
601
602 DrawTextArgs_Make(&args, &w->text, w->font, true1);
603 Drawer2D_DrawText(&Launcher_Framebuffer, &args, w->x, w->y);
604}
605
606static const struct LWidgetVTABLE llabel_VTABLE = {
607 LLabel_Draw, NULL((void*)0),
608 NULL((void*)0), NULL((void*)0), /* Key */
609 NULL((void*)0), NULL((void*)0), /* Hover */
610 NULL((void*)0), NULL((void*)0) /* Select */
611};
612void LLabel_Init(struct LScreen* s, struct LLabel* w, const char* text) {
613 w->VTABLE = &llabel_VTABLE;
614 w->font = &Launcher_TextFont;
615
616 String_InitArray(w->text, w->_textBuffer)w->text.buffer = w->_textBuffer; w->text.length = 0;
w->text.capacity = sizeof(w->_textBuffer);
;
617 LLabel_SetConst(w, text);
618 s->widgets[s->numWidgets++] = (struct LWidget*)w;
619}
620
621void LLabel_SetText(struct LLabel* w, const cc_string* text) {
622 struct DrawTextArgs args;
623 String_Copy(&w->text, text);
624
625 DrawTextArgs_Make(&args, &w->text, w->font, true1);
626 w->width = Drawer2D_TextWidth(&args);
627 w->height = Drawer2D_TextHeight(&args);
628 LWidget_CalcPosition(w);
629}
630
631void LLabel_SetConst(struct LLabel* w, const char* text) {
632 cc_string str = String_FromReadonly(text);
633 LLabel_SetText(w, &str);
634}
635
636
637/*########################################################################################################################*
638*-------------------------------------------------------BoxWidget---------------------------------------------------------*
639*#########################################################################################################################*/
640#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
))
641static void LLine_Draw(void* widget) {
642 struct LLine* w = (struct LLine*)widget;
643 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;
644 Gradient_Blend(&Launcher_Framebuffer, col, 128, w->x, w->y, w->width, w->height);
645}
646
647static const struct LWidgetVTABLE lline_VTABLE = {
648 LLine_Draw, NULL((void*)0),
649 NULL((void*)0), NULL((void*)0), /* Key */
650 NULL((void*)0), NULL((void*)0), /* Hover */
651 NULL((void*)0), NULL((void*)0) /* Select */
652};
653void LLine_Init(struct LScreen* s, struct LLine* w, int width) {
654 w->VTABLE = &lline_VTABLE;
655 w->width = Display_ScaleX(width);
656 w->height = Display_ScaleY(2);
657 s->widgets[s->numWidgets++] = (struct LWidget*)w;
658}
659
660
661/*########################################################################################################################*
662*------------------------------------------------------SliderWidget-------------------------------------------------------*
663*#########################################################################################################################*/
664static void LSlider_DrawBoxBounds(struct LSlider* w) {
665 BitmapCol boundsTop = BitmapCol_Make(119, 100, 132, 255)(((cc_uint8)(119) << 16) | ((cc_uint8)(100) << 8)
| ((cc_uint8)(132) << 0) | ((cc_uint8)(255) << 24
))
;
666 BitmapCol boundsBottom = BitmapCol_Make(150, 130, 165, 255)(((cc_uint8)(150) << 16) | ((cc_uint8)(130) << 8)
| ((cc_uint8)(165) << 0) | ((cc_uint8)(255) << 24
))
;
667
668 /* TODO: Check these are actually right */
669 Drawer2D_Clear(&Launcher_Framebuffer, boundsTop,
670 w->x, w->y,
671 w->width, yBorder);
672 Drawer2D_Clear(&Launcher_Framebuffer, boundsBottom,
673 w->x, w->y + w->height - yBorder,
674 w->width, yBorder);
675
676 Gradient_Vertical(&Launcher_Framebuffer, boundsTop, boundsBottom,
677 w->x, w->y,
678 xBorder, w->height);
679 Gradient_Vertical(&Launcher_Framebuffer, boundsTop, boundsBottom,
680 w->x + w->width - xBorder, w->y,
681 xBorder, w->height);
682}
683
684static void LSlider_DrawBox(struct LSlider* w) {
685 BitmapCol progTop = BitmapCol_Make(220, 204, 233, 255)(((cc_uint8)(220) << 16) | ((cc_uint8)(204) << 8)
| ((cc_uint8)(233) << 0) | ((cc_uint8)(255) << 24
))
;
686 BitmapCol progBottom = BitmapCol_Make(207, 181, 216, 255)(((cc_uint8)(207) << 16) | ((cc_uint8)(181) << 8)
| ((cc_uint8)(216) << 0) | ((cc_uint8)(255) << 24
))
;
687 int halfHeight = (w->height - yBorder2) / 2;
688
689 Gradient_Vertical(&Launcher_Framebuffer, progTop, progBottom,
690 w->x + xBorder, w->y + yBorder,
691 w->width - xBorder2, halfHeight);
692 Gradient_Vertical(&Launcher_Framebuffer, progBottom, progTop,
693 w->x + xBorder, w->y + yBorder + halfHeight,
694 w->width - xBorder2, halfHeight);
695}
696
697static void LSlider_Draw(void* widget) {
698 struct LSlider* w = (struct LSlider*)widget;
699 int curWidth;
700
701 LSlider_DrawBoxBounds(w);
702 LSlider_DrawBox(w);
703
704 curWidth = (int)((w->width - xBorder2) * w->value / w->maxValue);
705 Drawer2D_Clear(&Launcher_Framebuffer, w->col,
706 w->x + xBorder, w->y + yBorder,
707 curWidth, w->height - yBorder2);
708}
709
710static const struct LWidgetVTABLE lslider_VTABLE = {
711 LSlider_Draw, NULL((void*)0),
712 NULL((void*)0), NULL((void*)0), /* Key */
713 NULL((void*)0), NULL((void*)0), /* Hover */
714 NULL((void*)0), NULL((void*)0) /* Select */
715};
716void LSlider_Init(struct LScreen* s, struct LSlider* w, int width, int height, BitmapCol col) {
717 w->VTABLE = &lslider_VTABLE;
718 w->width = Display_ScaleX(width);
719 w->height = Display_ScaleY(height);
720 w->maxValue = 100;
721 w->col = col;
722 s->widgets[s->numWidgets++] = (struct LWidget*)w;
723}
724
725
726/*########################################################################################################################*
727*------------------------------------------------------TableWidget--------------------------------------------------------*
728*#########################################################################################################################*/
729static void FlagColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
730 struct Bitmap* bmp = Flags_Get(row);
731 if (!bmp) return;
732 Drawer2D_BmpCopy(&Launcher_Framebuffer, x + flagXOffset, y + flagYOffset, bmp);
733}
734
735static void NameColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
736 args->text = row->name;
737}
738static int NameColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
739 return String_Compare(&b->name, &a->name);
740}
741
742static void PlayersColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
743 String_Format2(&args->text, "%i/%i", &row->players, &row->maxPlayers);
744}
745static int PlayersColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
746 return b->players - a->players;
747}
748
749static void UptimeColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
750 int uptime = row->uptime;
751 char unit = 's';
752
753 if (uptime >= SECS_PER_DAY(60 * 60 * 24) * 7) {
754 uptime /= SECS_PER_DAY(60 * 60 * 24); unit = 'd';
755 } else if (uptime >= SECS_PER_HOUR(60 * 60)) {
756 uptime /= SECS_PER_HOUR(60 * 60); unit = 'h';
757 } else if (uptime >= SECS_PER_MIN60) {
758 uptime /= SECS_PER_MIN60; unit = 'm';
759 }
760 String_Format2(&args->text, "%i%r", &uptime, &unit);
761}
762static int UptimeColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
763 return b->uptime - a->uptime;
764}
765
766static void SoftwareColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) {
767 args->text = row->software;
768}
769static int SoftwareColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) {
770 return String_Compare(&b->software, &a->software);
771}
772
773static struct LTableColumn tableColumns[5] = {
774 { "", 15, FlagColumn_Draw, NULL((void*)0), false0, false0 },
775 { "Name", 328, NameColumn_Draw, NameColumn_Sort, true1, true1 },
776 { "Players", 73, PlayersColumn_Draw, PlayersColumn_Sort, true1, true1 },
777 { "Uptime", 73, UptimeColumn_Draw, UptimeColumn_Sort, true1, true1 },
778 { "Software", 143, SoftwareColumn_Draw, SoftwareColumn_Sort, false0, true1 }
779};
780
781
782static int sortingCol = -1;
783/* Works out top and height of the scrollbar */
784static void LTable_GetScrollbarCoords(struct LTable* w, int* y, int* height) {
785 float scale;
786 if (!w->rowsCount) { *y = 0; *height = 0; return; }
787
788 scale = w->height / (float)w->rowsCount;
789 *y = Math_Ceil(w->topRow * scale);
790 *height = Math_Ceil(w->visibleRows * scale);
791 *height = min(*y + *height, w->height)((*y + *height) < (w->height) ? (*y + *height) : (w->
height))
- *y;
792}
793
794/* Ensures top/first visible row index lies within table */
795static void LTable_ClampTopRow(struct LTable* w) {
796 if (w->topRow > w->rowsCount - w->visibleRows) {
797 w->topRow = w->rowsCount - w->visibleRows;
798 }
799 if (w->topRow < 0) w->topRow = 0;
800}
801
802/* Returns index of selected row in currently visible rows */
803static int LTable_GetSelectedIndex(struct LTable* w) {
804 struct ServerInfo* entry;
805 int row;
806
807 for (row = 0; row < w->rowsCount; row++) {
808 entry = LTable_Get(row)(&FetchServersTask.servers[FetchServersTask.servers[row].
_order])
;
809 if (String_CaselessEquals(w->selectedHash, &entry->hash)) return row;
810 }
811 return -1;
812}
813
814/* Sets selected row to given row, scrolling table if needed */
815static void LTable_SetSelectedTo(struct LTable* w, int index) {
816 if (!w->rowsCount) return;
817 if (index >= w->rowsCount) index = w->rowsCount - 1;
818 if (index < 0) index = 0;
819
820 String_Copy(w->selectedHash, &LTable_Get(index)(&FetchServersTask.servers[FetchServersTask.servers[index
]._order])
->hash);
821 LTable_ShowSelected(w);
822 w->OnSelectedChanged();
823}
824
825/* Draws background behind column headers */
826static void LTable_DrawHeaderBackground(struct LTable* w) {
827 BitmapCol gridCol = BitmapCol_Make(20, 20, 10, 255)(((cc_uint8)(20) << 16) | ((cc_uint8)(20) << 8) |
((cc_uint8)(10) << 0) | ((cc_uint8)(255) << 24))
;
828
829 if (!Launcher_ClassicBackground) {
830 Drawer2D_Clear(&Launcher_Framebuffer, gridCol,
831 w->x, w->y, w->width, w->hdrHeight);
832 } else {
833 Launcher_ResetArea(w->x, w->y, w->width, w->hdrHeight);
834 }
835}
836
837/* Works out the background colour of the given row */
838static BitmapCol LTable_RowCol(struct LTable* w, struct ServerInfo* row) {
839 BitmapCol gridCol = BitmapCol_Make( 20, 20, 10, 255)(((cc_uint8)(20) << 16) | ((cc_uint8)(20) << 8) |
((cc_uint8)(10) << 0) | ((cc_uint8)(255) << 24))
;
840 BitmapCol featSelCol = BitmapCol_Make( 50, 53, 0, 255)(((cc_uint8)(50) << 16) | ((cc_uint8)(53) << 8) |
((cc_uint8)(0) << 0) | ((cc_uint8)(255) << 24))
;
841 BitmapCol featuredCol = BitmapCol_Make(101, 107, 0, 255)(((cc_uint8)(101) << 16) | ((cc_uint8)(107) << 8)
| ((cc_uint8)(0) << 0) | ((cc_uint8)(255) << 24)
)
;
842 BitmapCol selectedCol = BitmapCol_Make( 40, 40, 40, 255)(((cc_uint8)(40) << 16) | ((cc_uint8)(40) << 8) |
((cc_uint8)(40) << 0) | ((cc_uint8)(255) << 24))
;
843 cc_bool selected;
844
845 if (row) {
846 selected = String_Equals(&row->hash, w->selectedHash);
847 if (row->featured) {
848 return selected ? featSelCol : featuredCol;
849 } else if (selected) {
850 return selectedCol;
851 }
852 }
853 return Launcher_ClassicBackground ? 0 : gridCol;
854}
855
856/* Draws background behind each row in the table */
857static void LTable_DrawRowsBackground(struct LTable* w) {
858 struct ServerInfo* entry;
859 BitmapCol col;
860 int y, height, row;
861
862 y = w->rowsBegY;
863 for (row = w->topRow; ; row++, y += w->rowHeight) {
864 entry = row < w->rowsCount ? LTable_Get(row)(&FetchServersTask.servers[FetchServersTask.servers[row].
_order])
: NULL((void*)0);
865 col = LTable_RowCol(w, entry);
866
867 /* last row may get chopped off */
868 height = min(y + w->rowHeight, w->rowsEndY)((y + w->rowHeight) < (w->rowsEndY) ? (y + w->rowHeight
) : (w->rowsEndY))
- y;
869 /* hit the end of the table */
870 if (height < 0) break;
871
872 if (col) {
873 Drawer2D_Clear(&Launcher_Framebuffer, col,
874 w->x, y, w->width, height);
875 } else {
876 Launcher_ResetArea(w->x, y, w->width, height);
877 }
878 }
879}
880
881/* Draws a gridline below column headers and gridlines after each column */
882static void LTable_DrawGridlines(struct LTable* w) {
883 int i, x;
884 if (Launcher_ClassicBackground) return;
885
886 x = w->x;
887 Drawer2D_Clear(&Launcher_Framebuffer, Launcher_BackgroundCol,
888 x, w->y + w->hdrHeight, w->width, gridlineHeight);
889
890 for (i = 0; i < w->numColumns; i++) {
891 x += w->columns[i].width;
892 if (!w->columns[i].hasGridline) continue;
893
894 Drawer2D_Clear(&Launcher_Framebuffer, Launcher_BackgroundCol,
895 x, w->y, gridlineWidth, w->height);
896 x += gridlineWidth;
897 }
898}
899
900/* Draws the entire background of the table */
901static void LTable_DrawBackground(struct LTable* w) {
902 LTable_DrawHeaderBackground(w);
903 LTable_DrawRowsBackground(w);
904 LTable_DrawGridlines(w);
905}
906
907/* Draws title of each column at top of the table */
908static void LTable_DrawHeaders(struct LTable* w) {
909 struct DrawTextArgs args;
910 int i, x, y;
911
912 DrawTextArgs_MakeEmpty(&args, &Launcher_TextFont, true1);
913 x = w->x; y = w->y;
914
915 for (i = 0; i < w->numColumns; i++) {
916 args.text = String_FromReadonly(w->columns[i].name);
917 Drawer2D_DrawClippedText(&Launcher_Framebuffer, &args,
918 x + cellXOffset, y + hdrYOffset,
919 w->columns[i].width - cellXPadding);
920
921 x += w->columns[i].width;
922 if (w->columns[i].hasGridline) x += gridlineWidth;
923 }
924}
925
926/* Draws contents of the currently visible rows in the table */
927static void LTable_DrawRows(struct LTable* w) {
928 cc_string str; char strBuffer[STRING_SIZE64];
929 struct ServerInfo* entry;
930 struct DrawTextArgs args;
931 int i, x, y, row, end;
932
933 String_InitArray(str, strBuffer)str.buffer = strBuffer; str.length = 0; str.capacity = sizeof
(strBuffer);
;
934 DrawTextArgs_Make(&args, &str, w->rowFont, true1);
935 y = w->rowsBegY;
936 end = w->topRow + w->visibleRows;
937
938 for (row = w->topRow; row < end; row++, y += w->rowHeight) {
939 x = w->x;
940
941 if (row >= w->rowsCount) break;
942 if (y + w->rowHeight > w->rowsEndY) break;
943 entry = LTable_Get(row)(&FetchServersTask.servers[FetchServersTask.servers[row].
_order])
;
944
945 for (i = 0; i < w->numColumns; i++) {
946 args.text = str;
947 w->columns[i].DrawRow(entry, &args, x, y);
948
949 if (args.text.length) {
950 Drawer2D_DrawClippedText(&Launcher_Framebuffer, &args,
951 x + cellXOffset, y + rowYOffset,
952 w->columns[i].width - cellXPadding);
953 }
954
955 x += w->columns[i].width;
956 if (w->columns[i].hasGridline) x += gridlineWidth;
957 }
958 }
959}
960
961/* Draws scrollbar on the right edge of the table */
962static void LTable_DrawScrollbar(struct LTable* w) {
963 BitmapCol classicBack = BitmapCol_Make( 80, 80, 80, 255)(((cc_uint8)(80) << 16) | ((cc_uint8)(80) << 8) |
((cc_uint8)(80) << 0) | ((cc_uint8)(255) << 24))
;
964 BitmapCol classicScroll = BitmapCol_Make(160, 160, 160, 255)(((cc_uint8)(160) << 16) | ((cc_uint8)(160) << 8)
| ((cc_uint8)(160) << 0) | ((cc_uint8)(255) << 24
))
;
965 BitmapCol backCol = Launcher_ClassicBackground ? classicBack : Launcher_ButtonBorderCol;
966 BitmapCol scrollCol = Launcher_ClassicBackground ? classicScroll : Launcher_ButtonForeActiveCol;
967
968 int x, y, height;
969 x = w->x + w->width - scrollbarWidth;
970 LTable_GetScrollbarCoords(w, &y, &height);
971
972 Drawer2D_Clear(&Launcher_Framebuffer, backCol,
973 x, w->y, scrollbarWidth, w->height);
974 Drawer2D_Clear(&Launcher_Framebuffer, scrollCol,
975 x, w->y + y, scrollbarWidth, height);
976}
977
978cc_bool LTable_HandlesKey(int key) {
979 return key == KEY_UP || key == KEY_DOWN || key == KEY_PAGEUP || key == KEY_PAGEDOWN;
980}
981
982static void LTable_KeyDown(void* widget, int key, cc_bool was) {
983 struct LTable* w = (struct LTable*)widget;
984 int index = LTable_GetSelectedIndex(w);
985
986 if (key == KEY_UP) {
987 index--;
988 } else if (key == KEY_DOWN) {
989 index++;
990 } else if (key == KEY_PAGEUP) {
991 index -= w->visibleRows;
992 } else if (key == KEY_PAGEDOWN) {
993 index += w->visibleRows;
994 } else { return; }
995
996 w->_lastRow = -1;
997 LTable_SetSelectedTo(w, index);
998}
999
1000static void LTable_MouseMove(void* widget, int idx, cc_bool wasOver) {
1001 struct LTable* w = (struct LTable*)widget;
1002 int x = Pointers[idx].x - w->x, y = Pointers[idx].y - w->y;
1003 int i, col, width, maxW;
1004
1005 if (w->draggingScrollbar) {
1006 float scale = w->height / (float)w->rowsCount;
1007 int row = (int)((y - w->dragYOffset) / scale);
1008 /* avoid expensive redraw when possible */
1009 if (w->topRow == row) return;
1010
1011 w->topRow = row;
1012 LTable_ClampTopRow(w);
1013 LWidget_Draw(w);
1014 } else if (w->draggingColumn >= 0) {
1015 col = w->draggingColumn;
1016 width = x - w->dragXStart;
1017
1018 /* Ensure this column doesn't expand past right side of table */
1019 maxW = w->width;
1020 for (i = 0; i < col; i++) maxW -= w->columns[i].width;
1021
1022 Math_Clamp(width, cellMinWidth, maxW - cellMinWidth)width = width < (cellMinWidth) ? (cellMinWidth) : width; width
= width > (maxW - cellMinWidth) ? (maxW - cellMinWidth) :
width;
;
1023 if (width == w->columns[col].width) return;
1024 w->columns[col].width = width;
1025 LWidget_Draw(w);
1026 }
1027}
1028
1029static void LTable_RowsClick(struct LTable* w, int idx) {
1030 int mouseY = Pointers[idx].y - w->rowsBegY;
1031 int row = w->topRow + mouseY / w->rowHeight;
1032 cc_uint64 now;
1033
1034 LTable_SetSelectedTo(w, row);
1035 now = Stopwatch_Measure();
1036
1037 /* double click on row to join */
1038 if (Stopwatch_ElapsedMS(w->_lastClick, now) < 1000 && row == w->_lastRow) {
1039 Launcher_ConnectToServer(&LTable_Get(row)(&FetchServersTask.servers[FetchServersTask.servers[row].
_order])
->hash);
1040 }
1041
1042 w->_lastRow = LTable_GetSelectedIndex(w);
1043 w->_lastClick = now;
1044}
1045
1046/* Handles clicking on column headers (either resizes a column or sort rows) */
1047static void LTable_HeadersClick(struct LTable* w, int idx) {
1048 int x, i, mouseX = Pointers[idx].x;
1049
1050 for (i = 0, x = w->x; i < w->numColumns; i++) {
1051 /* clicked on gridline, begin dragging */
1052 if (mouseX >= (x - dragPad) && mouseX < (x + dragPad) && w->columns[i].interactable) {
1053 w->draggingColumn = i - 1;
1054 w->dragXStart = (mouseX - w->x) - w->columns[i - 1].width;
1055 return;
1056 }
1057
1058 x += w->columns[i].width;
1059 if (w->columns[i].hasGridline) x += gridlineWidth;
1060 }
1061
1062 for (i = 0, x = w->x; i < w->numColumns; i++) {
1063 if (mouseX >= x && mouseX < (x + w->columns[i].width) && w->columns[i].interactable) {
1064 sortingCol = i;
1065 w->columns[i].invertSort = !w->columns[i].invertSort;
1066 LTable_Sort(w);
1067 return;
1068 }
1069
1070 x += w->columns[i].width;
1071 if (w->columns[i].hasGridline) x += gridlineWidth;
1072 }
1073}
1074
1075/* Handles clicking on the scrollbar on right edge of table */
1076static void LTable_ScrollbarClick(struct LTable* w, int idx) {
1077 int y, height, mouseY = Pointers[idx].y - w->y;
1078 LTable_GetScrollbarCoords(w, &y, &height);
1079
1080 if (mouseY < y) {
1081 w->topRow -= w->visibleRows;
1082 } else if (mouseY >= y + height) {
1083 w->topRow += w->visibleRows;
1084 } else {
1085 w->dragYOffset = mouseY - y;
1086 }
1087
1088 w->draggingScrollbar = true1;
1089 LTable_ClampTopRow(w);
1090}
1091
1092static void LTable_MouseDown(void* widget, int idx, cc_bool wasSelected) {
1093 struct LTable* w = (struct LTable*)widget;
1094
1095 if (Pointers[idx].x >= WindowInfo.Width - scrollbarWidth) {
1096 LTable_ScrollbarClick(w, idx);
1097 w->_lastRow = -1;
1098 } else if (Pointers[idx].y < w->rowsBegY) {
1099 LTable_HeadersClick(w, idx);
1100 w->_lastRow = -1;
1101 } else {
1102 LTable_RowsClick(w, idx);
1103 }
1104 LWidget_Draw(w);
1105}
1106
1107static void LTable_MouseWheel(void* widget, float delta) {
1108 struct LTable* w = (struct LTable*)widget;
1109 w->topRow -= Utils_AccumulateWheelDelta(&w->_wheelAcc, delta);
1110 LTable_ClampTopRow(w);
1111 LWidget_Draw(w);
1112 w->_lastRow = -1;
1113}
1114
1115/* Stops an in-progress dragging of resizing column. */
1116static void LTable_StopDragging(void* widget, int idx) {
1117 struct LTable* w = (struct LTable*)widget;
1118 w->draggingColumn = -1;
1119 w->draggingScrollbar = false0;
1120 w->dragYOffset = 0;
1121}
1122
1123void LTable_Reposition(struct LTable* w) {
1124 int rowsHeight;
1125 w->hdrHeight = Drawer2D_FontHeight(&Launcher_TextFont, true1) + hdrYPadding;
1126 w->rowHeight = Drawer2D_FontHeight(w->rowFont, true1) + rowYPadding;
1127
1128 w->rowsBegY = w->y + w->hdrHeight + gridlineHeight;
1129 w->rowsEndY = w->y + w->height;
1130 rowsHeight = w->height - (w->rowsBegY - w->y);
1131
1132 w->visibleRows = rowsHeight / w->rowHeight;
1133 LTable_ClampTopRow(w);
1134}
1135
1136static void LTable_Draw(void* widget) {
1137 struct LTable* w = (struct LTable*)widget;
1138 LTable_DrawBackground(w);
1139 LTable_DrawHeaders(w);
1140 LTable_DrawRows(w);
1141 LTable_DrawScrollbar(w);
1142 Launcher_MarkAllDirty();
1143}
1144
1145static const struct LWidgetVTABLE ltable_VTABLE = {
1146 LTable_Draw, NULL((void*)0),
1147 LTable_KeyDown, NULL((void*)0), /* Key */
1148 LTable_MouseMove, NULL((void*)0), /* Hover */
1149 LTable_MouseDown, LTable_StopDragging, /* Select */
1150 LTable_MouseWheel, /* Wheel */
1151};
1152void LTable_Init(struct LTable* w, struct FontDesc* rowFont) {
1153 int i;
1154 w->VTABLE = &ltable_VTABLE;
1155 w->columns = tableColumns;
1156 w->numColumns = Array_Elems(tableColumns)(sizeof(tableColumns) / sizeof(tableColumns[0]));
1157 w->rowFont = rowFont;
1158
1159 for (i = 0; i < w->numColumns; i++) {
1160 w->columns[i].width = Display_ScaleX(w->columns[i].width);
1161 }
1162}
1163
1164void LTable_Reset(struct LTable* w) {
1165 LTable_StopDragging(w, 0);
1166 LTable_Reposition(w);
1167
1168 w->topRow = 0;
1169 w->rowsCount = 0;
1170 sortingCol = -1;
1171 w->_wheelAcc = 0.0f;
1172
1173 w->selectedHash->length = 0;
1174 w->filter->length = 0;
1175}
1176
1177void LTable_ApplyFilter(struct LTable* w) {
1178 int i, j, count;
1179
1180 count = FetchServersTask.numServers;
1181 for (i = 0, j = 0; i < count; i++) {
1182 if (String_CaselessContains(&Servers_Get(i)(&FetchServersTask.servers[FetchServersTask.orders[i]])->name, w->filter)) {
1183 FetchServersTask.servers[j++]._order = FetchServersTask.orders[i];
1184 }
1185 }
1186
1187 w->rowsCount = j;
1188 for (; j < count; j++) {
1189 FetchServersTask.servers[j]._order = -100000;
1190 }
1191
1192 w->_lastRow = -1;
1193 LTable_ClampTopRow(w);
1194}
1195
1196static int LTable_SortOrder(const struct ServerInfo* a, const struct ServerInfo* b) {
1197 int order;
1198 if (sortingCol >= 0) {
1199 order = tableColumns[sortingCol].SortOrder(a, b);
1200 return tableColumns[sortingCol].invertSort ? -order : order;
1201 }
1202
1203 /* Default sort order. (most active server, then by highest uptime) */
1204 if (a->players != b->players) return a->players - b->players;
1205 return a->uptime - b->uptime;
1206}
1207
1208static void LTable_QuickSort(int left, int right) {
1209 cc_uint16* keys = FetchServersTask.orders; cc_uint16 key;
1210
1211 while (left < right) {
1212 int i = left, j = right;
1213 struct ServerInfo* mid = Servers_Get((i + j) >> 1)(&FetchServersTask.servers[FetchServersTask.orders[(i + j
) >> 1]])
;
1214
1215 /* partition the list */
1216 while (i <= j) {
1217 while (LTable_SortOrder(mid, Servers_Get(i)(&FetchServersTask.servers[FetchServersTask.orders[i]])) < 0) i++;
1218 while (LTable_SortOrder(mid, Servers_Get(j)(&FetchServersTask.servers[FetchServersTask.orders[j]])) > 0) j--;
1219 QuickSort_Swap_Maybe()if (i <= j) { key = keys[i]; keys[i] = keys[j]; keys[j] = key
; i++; j--;}
;
1220 }
1221 /* recurse into the smaller subset */
1222 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;}
1223 }
1224}
1225
1226void LTable_Sort(struct LTable* w) {
1227 if (!FetchServersTask.numServers) return;
1228 FetchServersTask_ResetOrder();
1229 LTable_QuickSort(0, FetchServersTask.numServers - 1);
1230
1231 LTable_ApplyFilter(w);
1232 LTable_ShowSelected(w);
1233}
1234
1235void LTable_ShowSelected(struct LTable* w) {
1236 int i = LTable_GetSelectedIndex(w);
1237 if (i == -1) return;
1238
1239 if (i >= w->topRow + w->visibleRows) {
1240 w->topRow = i - (w->visibleRows - 1);
1241 }
1242 if (i < w->topRow) w->topRow = i;
1243 LTable_ClampTopRow(w);
1244}
1245#endif