File: | LWidgets.c |
Warning: | line 402, column 13 Value stored to 'y' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
16 | static int xBorder, xBorder2, xBorder3, xBorder4; |
17 | static int yBorder, yBorder2, yBorder3, yBorder4; |
18 | static int xInputOffset, yInputOffset, inputExpand; |
19 | static int caretOffset, caretWidth, caretHeight; |
20 | static int scrollbarWidth, dragPad, gridlineWidth, gridlineHeight; |
21 | static int hdrYOffset, hdrYPadding, rowYOffset, rowYPadding; |
22 | static int cellXOffset, cellXPadding, cellMinWidth; |
23 | static int flagXOffset, flagYOffset; |
24 | |
25 | void 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 | |
54 | void 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 | |
61 | void 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 | |
67 | void 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 | |
77 | void 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 | *#########################################################################################################################*/ |
87 | static 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 | |
95 | static 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 | |
113 | static 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 | |
131 | static 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 | |
151 | static 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 | |
173 | static 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 | |
178 | static 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 | }; |
184 | void 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 | |
194 | void 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 | *#########################################################################################################################*/ |
206 | CC_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 | |
215 | static 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 | |
243 | static 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 | |
260 | static 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 | |
274 | static 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 | |
292 | static 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 | |
318 | static 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 | |
343 | static TimeMS caretStart; |
344 | static cc_bool lastCaretShow; |
345 | static 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 | |
348 | static 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 | |
378 | static 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 | |
389 | static 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 | |
421 | static 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 | |
434 | static 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 | |
441 | static 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 | |
450 | static 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 | |
469 | static void LInput_KeyChar(void* widget, char c) { |
470 | struct LInput* w = (struct LInput*)widget; |
471 | LInput_Append(w, c); |
472 | } |
473 | |
474 | static 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 | |
480 | static cc_bool LInput_DefaultInputFilter(char c) { |
481 | return c >= ' ' && c <= '~' && c != '&'; |
482 | } |
483 | |
484 | static 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 | }; |
492 | void 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 */ |
509 | static CC_INLINEinline void LInput_ClampCaret(struct LInput* w) { |
510 | if (w->caretPos >= w->text.length) w->caretPos = -1; |
511 | } |
512 | |
513 | void 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 | |
529 | static 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 | |
542 | void 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 | |
548 | void 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 | |
558 | void 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 | |
574 | void 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 | |
585 | void 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 | *#########################################################################################################################*/ |
598 | static 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 | |
606 | static 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 | }; |
612 | void 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 | |
621 | void 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 | |
631 | void 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 )) |
641 | static 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 | |
647 | static 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 | }; |
653 | void 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 | *#########################################################################################################################*/ |
664 | static 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 | |
684 | static 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 | |
697 | static 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 | |
710 | static 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 | }; |
716 | void 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 | *#########################################################################################################################*/ |
729 | static 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 | |
735 | static void NameColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) { |
736 | args->text = row->name; |
737 | } |
738 | static int NameColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { |
739 | return String_Compare(&b->name, &a->name); |
740 | } |
741 | |
742 | static 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 | } |
745 | static int PlayersColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { |
746 | return b->players - a->players; |
747 | } |
748 | |
749 | static 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 | } |
762 | static int UptimeColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { |
763 | return b->uptime - a->uptime; |
764 | } |
765 | |
766 | static void SoftwareColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, int x, int y) { |
767 | args->text = row->software; |
768 | } |
769 | static int SoftwareColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { |
770 | return String_Compare(&b->software, &a->software); |
771 | } |
772 | |
773 | static 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 | |
782 | static int sortingCol = -1; |
783 | /* Works out top and height of the scrollbar */ |
784 | static 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 */ |
795 | static 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 */ |
803 | static 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 */ |
815 | static 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, <able_Get(index)(&FetchServersTask.servers[FetchServersTask.servers[index ]._order])->hash); |
821 | LTable_ShowSelected(w); |
822 | w->OnSelectedChanged(); |
823 | } |
824 | |
825 | /* Draws background behind column headers */ |
826 | static 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 */ |
838 | static 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 */ |
857 | static 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 */ |
882 | static 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 */ |
901 | static 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 */ |
908 | static 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 */ |
927 | static 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 */ |
962 | static 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 | |
978 | cc_bool LTable_HandlesKey(int key) { |
979 | return key == KEY_UP || key == KEY_DOWN || key == KEY_PAGEUP || key == KEY_PAGEDOWN; |
980 | } |
981 | |
982 | static 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 | |
1000 | static 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 | |
1029 | static 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(<able_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) */ |
1047 | static 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 */ |
1076 | static 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 | |
1092 | static 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 | |
1107 | static 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. */ |
1116 | static 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 | |
1123 | void 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 | |
1136 | static 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 | |
1145 | static 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 | }; |
1152 | void LTable_Init(struct LTable* w, struct FontDesc* rowFont) { |
1153 | int i; |
1154 | w->VTABLE = <able_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 | |
1164 | void 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 | |
1177 | void 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 | |
1196 | static 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 | |
1208 | static 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 | |
1226 | void 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 | |
1235 | void 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 |