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