Bug Summary

File:Widgets.c
Warning:line 2018, column 3
2nd function call argument is an uninitialized value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple amd64-unknown-openbsd6.8 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name Widgets.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mthread-model posix -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/10.0.1 -I /usr/X11R6/include -I /usr/local/include -fdebug-compilation-dir /home/ben/Projects/ClassiCube/src -ferror-limit 19 -fmessage-length 0 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -fobjc-runtime=gnustep -fdiagnostics-show-option -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/ClassiCube/src/scan/2021-01-09-173753-31878-1 -x c Widgets.c

Widgets.c

1#include "Widgets.h"
2#include "Graphics.h"
3#include "Drawer2D.h"
4#include "ExtMath.h"
5#include "Funcs.h"
6#include "Window.h"
7#include "Inventory.h"
8#include "IsometricDrawer.h"
9#include "Utils.h"
10#include "Model.h"
11#include "Screens.h"
12#include "Platform.h"
13#include "Server.h"
14#include "Event.h"
15#include "Chat.h"
16#include "Game.h"
17#include "Logger.h"
18#include "Bitmap.h"
19#include "Block.h"
20#include "Input.h"
21
22#define Widget_UV(u1,v1, u2,v2)u1/256.0f,v1/256.0f,u2/256.0f,v2/256.0f Tex_UV(u1/256.0f,v1/256.0f, u2/256.0f,v2/256.0f)u1/256.0f,v1/256.0f,u2/256.0f,v2/256.0f
23static void Widget_NullFunc(void* widget) { }
24static int Widget_Pointer(void* elem, int id, int x, int y) { return false0; }
25static void Widget_InputUp(void* elem, int key) { }
26static int Widget_InputDown(void* elem, int key) { return false0; }
27static void Widget_PointerUp(void* elem, int id, int x, int y) { }
28static int Widget_PointerMove(void* elem, int id, int x, int y) { return false0; }
29static int Widget_MouseScroll(void* elem, float delta) { return false0; }
30
31/*########################################################################################################################*
32*-------------------------------------------------------TextWidget--------------------------------------------------------*
33*#########################################################################################################################*/
34static void TextWidget_Render(void* widget, double delta) {
35 struct TextWidget* w = (struct TextWidget*)widget;
36 if (w->tex.ID) Texture_RenderShaded(&w->tex, w->col);
37}
38
39static void TextWidget_Free(void* widget) {
40 struct TextWidget* w = (struct TextWidget*)widget;
41 Gfx_DeleteTexture(&w->tex.ID);
42}
43
44static void TextWidget_Reposition(void* widget) {
45 struct TextWidget* w = (struct TextWidget*)widget;
46 Widget_CalcPosition(w);
47 w->tex.X = w->x; w->tex.Y = w->y;
48}
49
50static void TextWidget_BuildMesh(void* widget, struct VertexTextured** vertices) {
51 struct TextWidget* w = (struct TextWidget*)widget;
52 Gfx_Make2DQuad(&w->tex, w->col, vertices);
53}
54
55static int TextWidget_Render2(void* widget, int offset) {
56 struct TextWidget* w = (struct TextWidget*)widget;
57 if (w->tex.ID) {
58 Gfx_BindTexture(w->tex.ID);
59 Gfx_DrawVb_IndexedTris_Range(4, offset);
60 }
61 return offset + 4;
62}
63
64static const struct WidgetVTABLE TextWidget_VTABLE = {
65 TextWidget_Render, TextWidget_Free, TextWidget_Reposition,
66 Widget_InputDown, Widget_InputUp, Widget_MouseScroll,
67 Widget_Pointer, Widget_PointerUp, Widget_PointerMove,
68 TextWidget_BuildMesh, TextWidget_Render2
69};
70void TextWidget_Init(struct TextWidget* w) {
71 Widget_Reset(w);
72 w->VTABLE = &TextWidget_VTABLE;
73 w->col = PACKEDCOL_WHITE(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 24
))
;
74}
75
76void TextWidget_Set(struct TextWidget* w, const cc_string* text, struct FontDesc* font) {
77 struct DrawTextArgs args;
78 Gfx_DeleteTexture(&w->tex.ID);
79
80 if (Drawer2D_IsEmptyText(text)) {
81 w->tex.Width = 0;
82 w->tex.Height = Drawer2D_FontHeight(font, true1);
83 } else {
84 DrawTextArgs_Make(&args, text, font, true1);
85 Drawer2D_MakeTextTexture(&w->tex, &args);
86 }
87
88 w->width = w->tex.Width; w->height = w->tex.Height;
89 Widget_Layout(w)(w)->VTABLE->Reposition(w);
90}
91
92void TextWidget_SetConst(struct TextWidget* w, const char* text, struct FontDesc* font) {
93 cc_string str = String_FromReadonly(text);
94 TextWidget_Set(w, &str, font);
95}
96
97
98/*########################################################################################################################*
99*------------------------------------------------------ButtonWidget-------------------------------------------------------*
100*#########################################################################################################################*/
101#define BUTTON_uWIDTH(200.0f / 256.0f) (200.0f / 256.0f)
102
103static struct Texture btnShadowTex = { 0, Tex_Rect(0,0, 0,0)0,0,0,0, Widget_UV(0,66, 200,86)0/256.0f,66/256.0f,200/256.0f,86/256.0f };
104static struct Texture btnSelectedTex = { 0, Tex_Rect(0,0, 0,0)0,0,0,0, Widget_UV(0,86, 200,106)0/256.0f,86/256.0f,200/256.0f,106/256.0f };
105static struct Texture btnDisabledTex = { 0, Tex_Rect(0,0, 0,0)0,0,0,0, Widget_UV(0,46, 200,66)0/256.0f,46/256.0f,200/256.0f,66/256.0f };
106
107static void ButtonWidget_Free(void* widget) {
108 struct ButtonWidget* w = (struct ButtonWidget*)widget;
109 Gfx_DeleteTexture(&w->tex.ID);
110}
111
112static void ButtonWidget_Reposition(void* widget) {
113 struct ButtonWidget* w = (struct ButtonWidget*)widget;
114 w->width = max(w->tex.Width, w->minWidth)((w->tex.Width) > (w->minWidth) ? (w->tex.Width) :
(w->minWidth))
;
115 w->height = max(w->tex.Height, w->minHeight)((w->tex.Height) > (w->minHeight) ? (w->tex.Height
) : (w->minHeight))
;
116
117 Widget_CalcPosition(w);
118 w->tex.X = w->x + (w->width / 2 - w->tex.Width / 2);
119 w->tex.Y = w->y + (w->height / 2 - w->tex.Height / 2);
120}
121
122static void ButtonWidget_Render(void* widget, double delta) {
123 PackedCol normCol = PackedCol_Make(224, 224, 224, 255)(((cc_uint8)(224) << 0) | ((cc_uint8)(224) << 8) |
((cc_uint8)(224) << 16) | ((cc_uint8)(255) << 24
))
;
124 PackedCol activeCol = PackedCol_Make(255, 255, 160, 255)(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(160) << 16) | ((cc_uint8)(255) << 24
))
;
125 PackedCol disabledCol = PackedCol_Make(160, 160, 160, 255)(((cc_uint8)(160) << 0) | ((cc_uint8)(160) << 8) |
((cc_uint8)(160) << 16) | ((cc_uint8)(255) << 24
))
;
126 PackedCol col;
127
128 struct ButtonWidget* w = (struct ButtonWidget*)widget;
129 struct Texture back;
130 float scale;
131
132 back = w->active ? btnSelectedTex : btnShadowTex;
133 if (w->disabled) back = btnDisabledTex;
134
135 back.ID = Gui.ClassicTexture ? Gui.GuiClassicTex : Gui.GuiTex;
136 back.X = w->x; back.Width = w->width;
137 back.Y = w->y; back.Height = w->height;
138
139 /* TODO: Does this 400 need to take DPI into account */
140 if (w->width >= 400) {
141 /* Button can be drawn normally */
142 Texture_Render(&back);
143 } else {
144 /* Split button down the middle */
145 scale = (w->width / 400.0f) * 0.5f;
146 Gfx_BindTexture(back.ID); /* avoid bind twice */
147
148 back.Width = (w->width / 2);
149 back.uv.U1 = 0.0f; back.uv.U2 = BUTTON_uWIDTH(200.0f / 256.0f) * scale;
150 Gfx_Draw2DTexture(&back, w->col);
151
152 back.X += (w->width / 2);
153 back.uv.U1 = BUTTON_uWIDTH(200.0f / 256.0f) * (1.0f - scale); back.uv.U2 = BUTTON_uWIDTH(200.0f / 256.0f);
154 Gfx_Draw2DTexture(&back, w->col);
155 }
156
157 if (!w->tex.ID) return;
158 col = w->disabled ? disabledCol : (w->active ? activeCol : normCol);
159 Texture_RenderShaded(&w->tex, col);
160}
161
162static void ButtonWidget_BuildMesh(void* widget, struct VertexTextured** vertices) {
163 PackedCol normCol = PackedCol_Make(224, 224, 224, 255)(((cc_uint8)(224) << 0) | ((cc_uint8)(224) << 8) |
((cc_uint8)(224) << 16) | ((cc_uint8)(255) << 24
))
;
164 PackedCol activeCol = PackedCol_Make(255, 255, 160, 255)(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(160) << 16) | ((cc_uint8)(255) << 24
))
;
165 PackedCol disabledCol = PackedCol_Make(160, 160, 160, 255)(((cc_uint8)(160) << 0) | ((cc_uint8)(160) << 8) |
((cc_uint8)(160) << 16) | ((cc_uint8)(255) << 24
))
;
166 PackedCol col;
167
168 struct ButtonWidget* w = (struct ButtonWidget*)widget;
169 struct Texture back;
170 float scale;
171
172 back = w->active ? btnSelectedTex : btnShadowTex;
173 if (w->disabled) back = btnDisabledTex;
174 back.X = w->x; back.Width = w->width;
175 back.Y = w->y; back.Height = w->height;
176
177 /* TODO: Does this 400 need to take DPI into account */
178 if (w->width >= 400) {
179 /* Button can be drawn normally */
180 Gfx_Make2DQuad(&back, w->col, vertices);
181 *vertices += 4; /* always use up 8 vertices for body */
182 } else {
183 /* Split button down the middle */
184 scale = (w->width / 400.0f) * 0.5f;
185
186 back.Width = (w->width / 2);
187 back.uv.U1 = 0.0f; back.uv.U2 = BUTTON_uWIDTH(200.0f / 256.0f) * scale;
188 Gfx_Make2DQuad(&back, w->col, vertices);
189
190 back.X += (w->width / 2);
191 back.uv.U1 = BUTTON_uWIDTH(200.0f / 256.0f) * (1.0f - scale); back.uv.U2 = BUTTON_uWIDTH(200.0f / 256.0f);
192 Gfx_Make2DQuad(&back, w->col, vertices);
193 }
194
195 col = w->disabled ? disabledCol : (w->active ? activeCol : normCol);
196 Gfx_Make2DQuad(&w->tex, col, vertices);
197}
198
199static int ButtonWidget_Render2(void* widget, int offset) {
200 struct ButtonWidget* w = (struct ButtonWidget*)widget;
201 Gfx_BindTexture(Gui.ClassicTexture ? Gui.GuiClassicTex : Gui.GuiTex);
202 /* TODO: Does this 400 need to take DPI into account */
203 Gfx_DrawVb_IndexedTris_Range(w->width >= 400 ? 4 : 8, offset);
204
205 if (w->tex.ID) {
206 Gfx_BindTexture(w->tex.ID);
207 Gfx_DrawVb_IndexedTris_Range(4, offset + 8);
208 }
209 return offset + 12;
210}
211
212static const struct WidgetVTABLE ButtonWidget_VTABLE = {
213 ButtonWidget_Render, ButtonWidget_Free, ButtonWidget_Reposition,
214 Widget_InputDown, Widget_InputUp, Widget_MouseScroll,
215 Widget_Pointer, Widget_PointerUp, Widget_PointerMove,
216 ButtonWidget_BuildMesh, ButtonWidget_Render2
217};
218void ButtonWidget_Make(struct ButtonWidget* w, int minWidth, Widget_LeftClick onClick, cc_uint8 horAnchor, cc_uint8 verAnchor, int xOffset, int yOffset) {
219 ButtonWidget_Init(w, minWidth, onClick);
220 Widget_SetLocation(w, horAnchor, verAnchor, xOffset, yOffset);
221}
222
223void ButtonWidget_Init(struct ButtonWidget* w, int minWidth, Widget_LeftClick onClick) {
224 Widget_Reset(w);
225 w->VTABLE = &ButtonWidget_VTABLE;
226 w->col = PACKEDCOL_WHITE(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 24
))
;
227 w->optName = NULL((void*)0);
228 w->minWidth = Display_ScaleX(minWidth);
229 w->minHeight = Display_ScaleY(40);
230 w->MenuClick = onClick;
231}
232
233void ButtonWidget_Set(struct ButtonWidget* w, const cc_string* text, struct FontDesc* font) {
234 struct DrawTextArgs args;
235 Gfx_DeleteTexture(&w->tex.ID);
236
237 if (Drawer2D_IsEmptyText(text)) {
238 w->tex.Width = 0;
239 w->tex.Height = Drawer2D_FontHeight(font, true1);
240 } else {
241 DrawTextArgs_Make(&args, text, font, true1);
242 Drawer2D_MakeTextTexture(&w->tex, &args);
243 }
244 Widget_Layout(w)(w)->VTABLE->Reposition(w);
245}
246
247void ButtonWidget_SetConst(struct ButtonWidget* w, const char* text, struct FontDesc* font) {
248 cc_string str = String_FromReadonly(text);
249 ButtonWidget_Set(w, &str, font);
250}
251
252
253/*########################################################################################################################*
254*-----------------------------------------------------ScrollbarWidget-----------------------------------------------------*
255*#########################################################################################################################*/
256#define SCROLL_BACK_COL(((cc_uint8)(10) << 0) | ((cc_uint8)(10) << 8) | (
(cc_uint8)(10) << 16) | ((cc_uint8)(220) << 24))
PackedCol_Make( 10, 10, 10, 220)(((cc_uint8)(10) << 0) | ((cc_uint8)(10) << 8) | (
(cc_uint8)(10) << 16) | ((cc_uint8)(220) << 24))
257#define SCROLL_BAR_COL(((cc_uint8)(100) << 0) | ((cc_uint8)(100) << 8) |
((cc_uint8)(100) << 16) | ((cc_uint8)(220) << 24
))
PackedCol_Make(100, 100, 100, 220)(((cc_uint8)(100) << 0) | ((cc_uint8)(100) << 8) |
((cc_uint8)(100) << 16) | ((cc_uint8)(220) << 24
))
258#define SCROLL_HOVER_COL(((cc_uint8)(122) << 0) | ((cc_uint8)(122) << 8) |
((cc_uint8)(122) << 16) | ((cc_uint8)(220) << 24
))
PackedCol_Make(122, 122, 122, 220)(((cc_uint8)(122) << 0) | ((cc_uint8)(122) << 8) |
((cc_uint8)(122) << 16) | ((cc_uint8)(220) << 24
))
259
260static void ScrollbarWidget_ClampTopRow(struct ScrollbarWidget* w) {
261 int maxTop = w->rowsTotal - w->rowsVisible;
262 if (w->topRow >= maxTop) w->topRow = maxTop;
263 if (w->topRow < 0) w->topRow = 0;
264}
265
266static float ScrollbarWidget_GetScale(struct ScrollbarWidget* w) {
267 float rows = (float)w->rowsTotal;
268 return (w->height - w->borderY * 2) / rows;
269}
270
271static void ScrollbarWidget_GetScrollbarCoords(struct ScrollbarWidget* w, int* y, int* height) {
272 float scale = ScrollbarWidget_GetScale(w);
273 *y = Math_Ceil(w->topRow * scale) + w->borderY;
274 *height = Math_Ceil(w->rowsVisible * scale);
275 *height = min(*y + *height, w->height - w->borderY)((*y + *height) < (w->height - w->borderY) ? (*y + *
height) : (w->height - w->borderY))
- *y;
276}
277
278static void ScrollbarWidget_Render(void* widget, double delta) {
279 struct ScrollbarWidget* w = (struct ScrollbarWidget*)widget;
280 int x, y, width, height;
281 PackedCol barCol;
282 cc_bool hovered;
283
284 x = w->x; width = w->width;
285 Gfx_Draw2DFlat(x, w->y, width, w->height, SCROLL_BACK_COL(((cc_uint8)(10) << 0) | ((cc_uint8)(10) << 8) | (
(cc_uint8)(10) << 16) | ((cc_uint8)(220) << 24))
);
286
287 ScrollbarWidget_GetScrollbarCoords(w, &y, &height);
288 x += w->borderX; y += w->y;
289 width -= w->borderX * 2;
290
291 hovered = Gui_ContainsPointers(x, y, width, height);
292 barCol = hovered ? SCROLL_HOVER_COL(((cc_uint8)(122) << 0) | ((cc_uint8)(122) << 8) |
((cc_uint8)(122) << 16) | ((cc_uint8)(220) << 24
))
: SCROLL_BAR_COL(((cc_uint8)(100) << 0) | ((cc_uint8)(100) << 8) |
((cc_uint8)(100) << 16) | ((cc_uint8)(220) << 24
))
;
293 Gfx_Draw2DFlat(x, y, width, height, barCol);
294
295 if (height < 20) return;
296 x += w->nubsWidth; y += (height / 2);
297 width -= w->nubsWidth * 2;
298
299 Gfx_Draw2DFlat(x, y + w->offsets[0], width, w->borderY, SCROLL_BACK_COL(((cc_uint8)(10) << 0) | ((cc_uint8)(10) << 8) | (
(cc_uint8)(10) << 16) | ((cc_uint8)(220) << 24))
);
300 Gfx_Draw2DFlat(x, y + w->offsets[1], width, w->borderY, SCROLL_BACK_COL(((cc_uint8)(10) << 0) | ((cc_uint8)(10) << 8) | (
(cc_uint8)(10) << 16) | ((cc_uint8)(220) << 24))
);
301 Gfx_Draw2DFlat(x, y + w->offsets[2], width, w->borderY, SCROLL_BACK_COL(((cc_uint8)(10) << 0) | ((cc_uint8)(10) << 8) | (
(cc_uint8)(10) << 16) | ((cc_uint8)(220) << 24))
);
302}
303
304static int ScrollbarWidget_PointerDown(void* widget, int id, int x, int y) {
305 struct ScrollbarWidget* w = (struct ScrollbarWidget*)widget;
306 int posY, height;
307
308 if (w->draggingId == id) return TOUCH_TYPE_GUI1;
309 if (x < w->x || x >= w->x + w->width + w->padding) return false0;
310 /* only intercept pointer that's dragging scrollbar */
311 if (w->draggingId) return false0;
312
313 y -= w->y;
314 ScrollbarWidget_GetScrollbarCoords(w, &posY, &height);
315
316 if (y < posY) {
317 w->topRow -= w->rowsVisible;
318 } else if (y >= posY + height) {
319 w->topRow += w->rowsVisible;
320 } else {
321 w->draggingId = id;
322 w->dragOffset = y - posY;
323 }
324 ScrollbarWidget_ClampTopRow(w);
325 return TOUCH_TYPE_GUI1;
326}
327
328static void ScrollbarWidget_PointerUp(void* widget, int id, int x, int y) {
329 struct ScrollbarWidget* w = (struct ScrollbarWidget*)widget;
330 if (w->draggingId != id) return;
331 w->draggingId = 0;
332 w->dragOffset = 0;
333}
334
335static int ScrollbarWidget_MouseScroll(void* widget, float delta) {
336 struct ScrollbarWidget* w = (struct ScrollbarWidget*)widget;
337 int steps = Utils_AccumulateWheelDelta(&w->scrollingAcc, delta);
338
339 w->topRow -= steps;
340 ScrollbarWidget_ClampTopRow(w);
341 return true1;
342}
343
344static int ScrollbarWidget_PointerMove(void* widget, int id, int x, int y) {
345 struct ScrollbarWidget* w = (struct ScrollbarWidget*)widget;
346 float scale;
347
348 if (w->draggingId == id) {
349 y -= w->y;
350 scale = ScrollbarWidget_GetScale(w);
351 w->topRow = (int)((y - w->dragOffset) / scale);
352 ScrollbarWidget_ClampTopRow(w);
353 return true1;
354 }
355 return false0;
356}
357
358static const struct WidgetVTABLE ScrollbarWidget_VTABLE = {
359 ScrollbarWidget_Render, Widget_NullFunc, Widget_CalcPosition,
360 Widget_InputDown, Widget_InputUp, ScrollbarWidget_MouseScroll,
361 ScrollbarWidget_PointerDown, ScrollbarWidget_PointerUp, ScrollbarWidget_PointerMove
362};
363void ScrollbarWidget_Create(struct ScrollbarWidget* w) {
364 Widget_Reset(w);
365 w->VTABLE = &ScrollbarWidget_VTABLE;
366 w->width = Display_ScaleX(22);
367 w->borderX = Display_ScaleX(2);
368 w->borderY = Display_ScaleY(2);
369 w->nubsWidth = Display_ScaleX(3);
370
371 w->offsets[0] = Display_ScaleY(-1 - 4);
372 w->offsets[1] = Display_ScaleY(-1);
373 w->offsets[2] = Display_ScaleY(-1 + 4);
374
375 w->rowsTotal = 0;
376 w->rowsVisible = 0;
377 w->topRow = 0;
378 w->scrollingAcc = 0.0f;
379 w->draggingId = 0;
380 w->dragOffset = 0;
381
382 /* It's easy to accidentally touch a bit to the right of the */
383 /* scrollbar with your finger, so just add some padding */
384 if (!Input_TouchMode0) return;
385 w->padding = Display_ScaleX(15);
386}
387
388
389/*########################################################################################################################*
390*------------------------------------------------------HotbarWidget-------------------------------------------------------*
391*#########################################################################################################################*/
392#define HotbarWidget_TileX(w, idx)(int)(w->x + w->slotXOffset + w->slotWidth * (idx)) (int)(w->x + w->slotXOffset + w->slotWidth * (idx))
393
394static void HotbarWidget_RenderHotbarOutline(struct HotbarWidget* w) {
395 PackedCol white = PACKEDCOL_WHITE(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 24
))
;
396 GfxResourceID tex;
397 int x;
398
399 tex = Gui.ClassicTexture ? Gui.GuiClassicTex : Gui.GuiTex;
400 w->backTex.ID = tex;
401 Texture_Render(&w->backTex);
402
403 x = HotbarWidget_TileX(w, Inventory.SelectedIndex)(int)(w->x + w->slotXOffset + w->slotWidth * (Inventory
.SelectedIndex))
;
404 w->selTex.ID = tex;
405 w->selTex.X = (int)(x - w->selWidth / 2);
406 Gfx_Draw2DTexture(&w->selTex, white);
407}
408
409static void HotbarWidget_RenderHotbarBlocks(struct HotbarWidget* w) {
410 /* TODO: Should hotbar use its own VB? */
411 struct VertexTextured vertices[INVENTORY_BLOCKS_PER_HOTBAR9 * ISOMETRICDRAWER_MAXVERTICES16];
412 float scale;
413 int i, x, y;
414
415 IsometricDrawer_BeginBatch(vertices, Models.Vb);
416 scale = w->elemSize / 2.0f;
417
418 for (i = 0; i < INVENTORY_BLOCKS_PER_HOTBAR9; i++) {
419 x = HotbarWidget_TileX(w, i)(int)(w->x + w->slotXOffset + w->slotWidth * (i));
420 y = w->y + (w->height / 2);
421
422#ifdef CC_BUILD_TOUCH
423 if (i == HOTBAR_MAX_INDEX(9 - 1) && Input_TouchMode0) continue;
424#endif
425 IsometricDrawer_DrawBatch(Inventory_Get(i)(Inventory.Table[Inventory.Offset + (i)]), scale, x, y);
426 }
427 IsometricDrawer_EndBatch();
428}
429
430static int HotbarWidget_ScrolledIndex(struct HotbarWidget* w, float delta, int index, int dir) {
431 int steps = Utils_AccumulateWheelDelta(&w->scrollAcc, delta);
432 index += (dir * steps) % INVENTORY_BLOCKS_PER_HOTBAR9;
433
434 if (index < 0) index += INVENTORY_BLOCKS_PER_HOTBAR9;
435 if (index >= INVENTORY_BLOCKS_PER_HOTBAR9) {
436 index -= INVENTORY_BLOCKS_PER_HOTBAR9;
437 }
438 return index;
439}
440
441static void HotbarWidget_Reposition(void* widget) {
442 struct HotbarWidget* w = (struct HotbarWidget*)widget;
443 float scaleX = w->scale * DisplayInfo.ScaleX;
444 float scaleY = w->scale * DisplayInfo.ScaleY;
445 int y;
446
447 w->width = (int)(182 * scaleX);
448 w->height = Math_Floor(22.0f * scaleY);
449 Widget_CalcPosition(w);
450
451 w->selWidth = (float)Math_Ceil(24.0f * scaleX);
452 w->elemSize = 13.5f * scaleX;
453 w->slotXOffset = 11.1f * scaleX;
454 w->slotWidth = 20.0f * scaleX;
455
456 Tex_SetRect(w->backTex, w->x,w->y, w->width,w->height)w->backTex.X = w->x; w->backTex.Y = w->y; w->backTex
.Width = w->width; w->backTex.Height = w->height;
;
457 Tex_SetUV(w->backTex, 0,0, 182/256.0f,22/256.0f)w->backTex.uv.U1 = 0; w->backTex.uv.V1 = 0; w->backTex
.uv.U2 = 182/256.0f; w->backTex.uv.V2 = 22/256.0f;
;
458
459 y = w->y + (w->height - (int)(23.0f * scaleY));
460 Tex_SetRect(w->selTex, 0,y, (int)w->selWidth,w->height)w->selTex.X = 0; w->selTex.Y = y; w->selTex.Width = (
int)w->selWidth; w->selTex.Height = w->height;
;
461 Tex_SetUV(w->selTex, 0,22/256.0f, 24/256.0f,44/256.0f)w->selTex.uv.U1 = 0; w->selTex.uv.V1 = 22/256.0f; w->
selTex.uv.U2 = 24/256.0f; w->selTex.uv.V2 = 44/256.0f;
;
462}
463
464static void HotbarWidget_Render(void* widget, double delta) {
465 struct HotbarWidget* w = (struct HotbarWidget*)widget;
466 HotbarWidget_RenderHotbarOutline(w);
467 HotbarWidget_RenderHotbarBlocks(w);
468
469#ifdef CC_BUILD_TOUCH
470 if (!Input_TouchMode0) return;
471 w->ellipsisTex.X = HotbarWidget_TileX(w, HOTBAR_MAX_INDEX)(int)(w->x + w->slotXOffset + w->slotWidth * ((9 - 1
)))
- w->ellipsisTex.Width / 2;
472 w->ellipsisTex.Y = w->y + (w->height / 2) - w->ellipsisTex.Height / 2;
473 Texture_Render(&w->ellipsisTex);
474#endif
475}
476
477static int HotbarWidget_KeyDown(void* widget, int key) {
478 struct HotbarWidget* w = (struct HotbarWidget*)widget;
479 int index;
480 if (key < '1' || key > '9') return false0;
481
482 index = key - '1';
483 if (KeyBind_IsPressed(KEYBIND_HOTBAR_SWITCH)) {
484 /* Pick from first to ninth row */
485 Inventory_SetHotbarIndex(index);
486 w->altHandled = true1;
487 } else {
488 Inventory_SetSelectedIndex(index);
489 }
490 return true1;
491}
492
493static void HotbarWidget_InputUp(void* widget, int key) {
494 struct HotbarWidget* w = (struct HotbarWidget*)widget;
495 /* Need to handle these cases:
496 a) user presses alt then number
497 b) user presses alt
498 We only do case b) if case a) did not happen */
499 if (key != KeyBinds[KEYBIND_HOTBAR_SWITCH]) return;
500 if (w->altHandled) { w->altHandled = false0; return; } /* handled already */
501
502 /* Don't switch hotbar when alt+tabbing to another window */
503 if (WindowInfo.Focused) Inventory_SwitchHotbar();
504}
505
506static int HotbarWidget_PointerDown(void* widget, int id, int x, int y) {
507 struct HotbarWidget* w = (struct HotbarWidget*)widget;
508 int width, height;
509 int i, cellX, cellY;
510
511 if (!Widget_Contains(w, x, y)) return false0;
512 width = (int)w->slotWidth;
513 height = w->height;
514
515 for (i = 0; i < INVENTORY_BLOCKS_PER_HOTBAR9; i++) {
516 cellX = (int)(w->x + width * i);
517 cellY = w->y;
518 if (!Gui_Contains(cellX, cellY, width, height, x, y)) continue;
519
520#ifdef CC_BUILD_TOUCH
521 if (i == HOTBAR_MAX_INDEX(9 - 1) && Input_TouchMode0) {
522 InventoryScreen_Show(); return TOUCH_TYPE_GUI1;
523 }
524#endif
525 Inventory_SetSelectedIndex(i);
526 return TOUCH_TYPE_GUI1;
527 }
528 return false0;
529}
530
531static int HotbarWidget_MouseScroll(void* widget, float delta) {
532 struct HotbarWidget* w = (struct HotbarWidget*)widget;
533 int index;
534
535 if (KeyBind_IsPressed(KEYBIND_HOTBAR_SWITCH)) {
536 index = Inventory.Offset / INVENTORY_BLOCKS_PER_HOTBAR9;
537 index = HotbarWidget_ScrolledIndex(w, delta, index, 1);
538 Inventory_SetHotbarIndex(index);
539 w->altHandled = true1;
540 } else {
541 index = HotbarWidget_ScrolledIndex(w, delta, Inventory.SelectedIndex, -1);
542 Inventory_SetSelectedIndex(index);
543 }
544 return true1;
545}
546
547static void HotbarWidget_Free(void* widget) {
548#ifdef CC_BUILD_TOUCH
549 struct HotbarWidget* w = (struct HotbarWidget*)widget;
550 if (!Input_TouchMode0) return;
551
552 Gfx_DeleteTexture(&w->ellipsisTex.ID);
553#endif
554}
555
556static const struct WidgetVTABLE HotbarWidget_VTABLE = {
557 HotbarWidget_Render, HotbarWidget_Free, HotbarWidget_Reposition,
558 HotbarWidget_KeyDown, HotbarWidget_InputUp, HotbarWidget_MouseScroll,
559 HotbarWidget_PointerDown, Widget_PointerUp, Widget_PointerMove
560};
561void HotbarWidget_Create(struct HotbarWidget* w) {
562 Widget_Reset(w);
563 w->VTABLE = &HotbarWidget_VTABLE;
564 w->horAnchor = ANCHOR_CENTRE;
565 w->verAnchor = ANCHOR_MAX;
566 w->scale = 1;
567}
568
569void HotbarWidget_SetFont(struct HotbarWidget* w, struct FontDesc* font) {
570#ifdef CC_BUILD_TOUCH
571 static const cc_string dots = String_FromConst("..."){ "...", (sizeof("...") - 1), (sizeof("...") - 1)};
572 struct DrawTextArgs args;
573 if (!Input_TouchMode0) return;
574
575 DrawTextArgs_Make(&args, &dots, font, true1);
576 Drawer2D_MakeTextTexture(&w->ellipsisTex, &args);
577#endif
578}
579
580
581/*########################################################################################################################*
582*-------------------------------------------------------TableWidget-------------------------------------------------------*
583*#########################################################################################################################*/
584static int Table_X(struct TableWidget* w) { return w->x - w->paddingX; }
585static int Table_Y(struct TableWidget* w) { return w->y - w->paddingTopY; }
586static int Table_Width(struct TableWidget* w) {
587 return w->blocksPerRow * w->cellSizeX + w->paddingX * 2;
588}
589static int Table_Height(struct TableWidget* w) {
590 return w->rowsVisible * w->cellSizeY + w->paddingTopY + w->paddingMaxY;
591}
592
593#define TABLE_MAX_VERTICES(8 * 10 * 16) (8 * 10 * ISOMETRICDRAWER_MAXVERTICES16)
594
595static cc_bool TableWidget_GetCoords(struct TableWidget* w, int i, int* cellX, int* cellY) {
596 int x, y;
597 x = i % w->blocksPerRow;
598 y = i / w->blocksPerRow - w->scroll.topRow;
599
600 *cellX = w->x + w->cellSizeX * x;
601 *cellY = w->y + w->cellSizeY * y + 3;
602 return y >= 0 && y < w->rowsVisible;
603}
604
605static void TableWidget_MoveCursorToSelected(struct TableWidget* w) {
606 int x, y, idx;
607 if (w->selectedIndex == -1) return;
608
609 idx = w->selectedIndex;
610 TableWidget_GetCoords(w, idx, &x, &y);
611
612 x += w->cellSizeX / 2; y += w->cellSizeY / 2;
613 Cursor_SetPosition(x, y);
614}
615
616static void TableWidget_MakeBlockDesc(cc_string* desc, BlockID block) {
617 cc_string name;
618 int block_ = block;
619 if (Game_PureClassic(Game_ClassicMode && !Game_ClassicHacks)) { String_AppendConst(desc, "Select block"); return; }
620 if (block == BLOCK_AIR) return;
621
622 name = Block_UNSAFE_GetName(block);
623 String_AppendString(desc, &name);
624 if (Game_ClassicMode) return;
625
626 String_Format1(desc, " (ID %i&f", &block_);
627 if (!Blocks.CanPlace[block]) { String_AppendConst(desc, ", place &cNo&f"); }
628 if (!Blocks.CanDelete[block]) { String_AppendConst(desc, ", delete &cNo&f"); }
629 String_Append(desc, ')');
630}
631
632static void TableWidget_UpdateDescTexPos(struct TableWidget* w) {
633 w->descTex.X = w->x + w->width / 2 - w->descTex.Width / 2;
634 w->descTex.Y = w->y - w->descTex.Height - 5;
635}
636
637static void TableWidget_RecreateDescTex(struct TableWidget* w) {
638 if (w->selectedIndex == w->lastCreatedIndex) return;
639 if (w->blocksCount == 0) return;
640 w->lastCreatedIndex = w->selectedIndex;
641
642 Gfx_DeleteTexture(&w->descTex.ID);
643 if (w->selectedIndex == -1) return;
644 TableWidget_MakeDescTex(w, w->blocks[w->selectedIndex]);
645}
646
647void TableWidget_MakeDescTex(struct TableWidget* w, BlockID block) {
648 cc_string desc; char descBuffer[STRING_SIZE64 * 2];
649 struct DrawTextArgs args;
650
651 Gfx_DeleteTexture(&w->descTex.ID);
652 String_InitArray(desc, descBuffer)desc.buffer = descBuffer; desc.length = 0; desc.capacity = sizeof
(descBuffer);
;
653 TableWidget_MakeBlockDesc(&desc, block);
654 if (!desc.length) return;
655
656 DrawTextArgs_Make(&args, &desc, w->font, true1);
657 Drawer2D_MakeTextTexture(&w->descTex, &args);
658 TableWidget_UpdateDescTexPos(w);
659}
660
661static cc_bool TableWidget_RowEmpty(struct TableWidget* w, int start) {
662 int i, end = min(start + w->blocksPerRow, Array_Elems(Inventory.Map))((start + w->blocksPerRow) < ((sizeof(Inventory.Map) / sizeof
(Inventory.Map[0]))) ? (start + w->blocksPerRow) : ((sizeof
(Inventory.Map) / sizeof(Inventory.Map[0]))))
;
663
664 for (i = start; i < end; i++) {
665 if (Inventory.Map[i] != BLOCK_AIR) return false0;
666 }
667 return true1;
668}
669
670void TableWidget_RecreateBlocks(struct TableWidget* w) {
671 int i, max = Game_UseCPEBlocks ? BLOCK_COUNT : BLOCK_ORIGINAL_COUNT;
672 BlockID block;
673 w->blocksCount = 0;
674
675 for (i = 0; i < Array_Elems(Inventory.Map)(sizeof(Inventory.Map) / sizeof(Inventory.Map[0])); ) {
676 if ((i % w->blocksPerRow) == 0 && TableWidget_RowEmpty(w, i)) {
677 i += w->blocksPerRow; continue;
678 }
679
680 block = Inventory.Map[i];
681 if (block < max) { w->blocks[w->blocksCount++] = block; }
682 i++;
683 }
684
685 w->rowsTotal = Math_CeilDiv(w->blocksCount, w->blocksPerRow);
686 Widget_Layout(w)(w)->VTABLE->Reposition(w);
687}
688
689static void TableWidget_Render(void* widget, double delta) {
690 struct TableWidget* w = (struct TableWidget*)widget;
691 struct VertexTextured vertices[TABLE_MAX_VERTICES(8 * 10 * 16)];
692 int cellSizeX, cellSizeY, size;
693 float off;
694 int i, x, y;
695
696 /* These were sourced by taking a screenshot of vanilla */
697 /* Then using paint to extract the colour components */
698 /* Then using wolfram alpha to solve the glblendfunc equation */
699 PackedCol topBackCol = PackedCol_Make( 34, 34, 34, 168)(((cc_uint8)(34) << 0) | ((cc_uint8)(34) << 8) | (
(cc_uint8)(34) << 16) | ((cc_uint8)(168) << 24))
;
700 PackedCol bottomBackCol = PackedCol_Make( 57, 57, 104, 202)(((cc_uint8)(57) << 0) | ((cc_uint8)(57) << 8) | (
(cc_uint8)(104) << 16) | ((cc_uint8)(202) << 24))
;
701 PackedCol topSelCol = PackedCol_Make(255, 255, 255, 142)(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(142) << 24
))
;
702 PackedCol bottomSelCol = PackedCol_Make(255, 255, 255, 192)(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(192) << 24
))
;
703
704 Gfx_Draw2DGradient(Table_X(w), Table_Y(w),
705 Table_Width(w), Table_Height(w), topBackCol, bottomBackCol);
706
707 if (w->rowsVisible < w->rowsTotal) {
708 Elem_Render(&w->scroll, delta)(&w->scroll)->VTABLE->Render(&w->scroll, delta
)
;
709 }
710
711 cellSizeX = w->cellSizeX;
712 cellSizeY = w->cellSizeY;
713 if (w->selectedIndex != -1 && Game_ClassicMode && w->blocks[w->selectedIndex] != BLOCK_AIR) {
714 TableWidget_GetCoords(w, w->selectedIndex, &x, &y);
715
716 /* TODO: Need two size arguments, in case X/Y dpi differs */
717 off = cellSizeX * 0.1f;
718 size = (int)(cellSizeX + off * 2);
719 Gfx_Draw2DGradient((int)(x - off), (int)(y - off),
720 size, size, topSelCol, bottomSelCol);
721 }
722 Gfx_SetTexturing(true1);
723 Gfx_SetVertexFormat(VERTEX_FORMAT_TEXTURED);
724
725 IsometricDrawer_BeginBatch(vertices, w->vb);
726 for (i = 0; i < w->blocksCount; i++) {
727 if (!TableWidget_GetCoords(w, i, &x, &y)) continue;
728
729 /* We want to always draw the selected block on top of others */
730 /* TODO: Need two size arguments, in case X/Y dpi differs */
731 if (i == w->selectedIndex) continue;
732 IsometricDrawer_DrawBatch(w->blocks[i], cellSizeX * 0.7f / 2.0f,
733 x + cellSizeX / 2, y + cellSizeY / 2);
734 }
735
736 i = w->selectedIndex;
737 if (i != -1) {
738 TableWidget_GetCoords(w, i, &x, &y);
739
740 IsometricDrawer_DrawBatch(w->blocks[i],
741 (cellSizeX + w->selBlockExpand) * 0.7f / 2.0f,
742 x + cellSizeX / 2, y + cellSizeY / 2);
743 }
744 IsometricDrawer_EndBatch();
745
746 if (w->descTex.ID) { Texture_Render(&w->descTex); }
747 Gfx_SetTexturing(false0);
748}
749
750static void TableWidget_Free(void* widget) {
751 struct TableWidget* w = (struct TableWidget*)widget;
752 Gfx_DeleteDynamicVbGfx_DeleteVb(&w->vb);
753 Gfx_DeleteTexture(&w->descTex.ID);
754 w->lastCreatedIndex = -1000;
755}
756
757void TableWidget_Recreate(struct TableWidget* w) {
758 Elem_Free(w)(w)->VTABLE->Free(w);
759 Gfx_RecreateDynamicVb(&w->vb, VERTEX_FORMAT_TEXTURED, TABLE_MAX_VERTICES(8 * 10 * 16));
760 TableWidget_RecreateDescTex(w);
761}
762
763static void TableWidget_Reposition(void* widget) {
764 struct TableWidget* w = (struct TableWidget*)widget;
765 float scale = w->scale;
766 int cellSize;
767
768 cellSize = (int)(50 * Math_SqrtF(scale)__builtin_sqrtf(scale));
769 w->cellSizeX = Display_ScaleX(cellSize);
770 w->cellSizeY = Display_ScaleY(cellSize);
771
772 w->selBlockExpand = 25.0f * Math_SqrtF(scale)__builtin_sqrtf(scale);
773 w->rowsVisible = min(8, w->rowsTotal)((8) < (w->rowsTotal) ? (8) : (w->rowsTotal)); /* 8 rows max */
774
775 do {
776 w->width = w->cellSizeX * w->blocksPerRow;
777 w->height = w->cellSizeY * w->rowsVisible;
778 Widget_CalcPosition(w);
779 TableWidget_UpdateDescTexPos(w);
780
781 /* Does the table fit on screen? */
782 if (Game_ClassicMode || Table_Y(w) >= 0) break;
783 w->rowsVisible--;
784 } while (w->rowsVisible > 1);
785
786 w->scroll.x = Table_X(w) + Table_Width(w);
787 w->scroll.y = Table_Y(w);
788 w->scroll.height = Table_Height(w);
789 w->scroll.rowsTotal = w->rowsTotal;
790 w->scroll.rowsVisible = w->rowsVisible;
791}
792
793static void TableWidget_ScrollRelative(struct TableWidget* w, int delta) {
794 int start = w->selectedIndex, index = start;
795 index += delta;
796 if (index < 0) index -= delta;
797 if (index >= w->blocksCount) index -= delta;
798 w->selectedIndex = index;
799
800 /* adjust scrollbar by number of rows moved up/down */
801 w->scroll.topRow += (index / w->blocksPerRow) - (start / w->blocksPerRow);
802 ScrollbarWidget_ClampTopRow(&w->scroll);
803
804 TableWidget_RecreateDescTex(w);
805 TableWidget_MoveCursorToSelected(w);
806}
807
808static int TableWidget_PointerDown(void* widget, int id, int x, int y) {
809 struct TableWidget* w = (struct TableWidget*)widget;
810 w->pendingClose = false0;
811
812 if (Elem_HandlesPointerDown(&w->scroll, id, x, y)(&w->scroll)->VTABLE->HandlesPointerDown(&w->
scroll, id, x, y)
) {
813 return TOUCH_TYPE_GUI1;
814 } else if (w->selectedIndex != -1 && w->blocks[w->selectedIndex] != BLOCK_AIR) {
815 Inventory_SetSelectedBlock(w->blocks[w->selectedIndex]);
816 w->pendingClose = true1;
817 return TOUCH_TYPE_GUI1;
818 } else if (Gui_Contains(Table_X(w), Table_Y(w), Table_Width(w), Table_Height(w), x, y)) {
819 return TOUCH_TYPE_GUI1;
820 }
821 return false0;
822}
823
824static void TableWidget_PointerUp(void* widget, int id, int x, int y) {
825 struct TableWidget* w = (struct TableWidget*)widget;
826 Elem_OnPointerUp(&w->scroll, id, x, y)(&w->scroll)->VTABLE->OnPointerUp(&w->scroll
, id, x, y)
;
827}
828
829static int TableWidget_MouseScroll(void* widget, float delta) {
830 struct TableWidget* w = (struct TableWidget*)widget;
831 int origTopRow, index;
832
833 cc_bool bounds = Gui_ContainsPointers(Table_X(w), Table_Y(w),
834 Table_Width(w) + w->scroll.width, Table_Height(w));
835 if (!bounds) return false0;
836
837 origTopRow = w->scroll.topRow;
838 Elem_HandlesMouseScroll(&w->scroll, delta)(&w->scroll)->VTABLE->HandlesMouseScroll(&w->
scroll, delta)
;
839 if (w->selectedIndex == -1) return true1;
840
841 index = w->selectedIndex;
842 index += (w->scroll.topRow - origTopRow) * w->blocksPerRow;
843 if (index >= w->blocksCount) index = -1;
844
845 w->selectedIndex = index;
846 TableWidget_RecreateDescTex(w);
847 return true1;
848}
849
850static int TableWidget_PointerMove(void* widget, int id, int x, int y) {
851 struct TableWidget* w = (struct TableWidget*)widget;
852 int cellSizeX, cellSizeY, maxHeight;
853 int i, cellX, cellY;
854
855 if (Elem_HandlesPointerMove(&w->scroll, id, x, y)(&w->scroll)->VTABLE->HandlesPointerMove(&w->
scroll, id, x, y)
) return true1;
856 if (w->lastX == x && w->lastY == y) return true1;
857 w->lastX = x; w->lastY = y;
858
859 w->selectedIndex = -1;
860 cellSizeX = w->cellSizeX;
861 cellSizeY = w->cellSizeY;
862 maxHeight = cellSizeY * w->rowsVisible;
863
864 if (Gui_Contains(w->x, w->y + 3, w->width, maxHeight - 3 * 2, x, y)) {
865 for (i = 0; i < w->blocksCount; i++) {
866 TableWidget_GetCoords(w, i, &cellX, &cellY);
867
868 if (Gui_Contains(cellX, cellY, cellSizeX, cellSizeY, x, y)) {
869 w->selectedIndex = i;
870 break;
871 }
872 }
873 }
874 TableWidget_RecreateDescTex(w);
875 return true1;
876}
877
878static int TableWidget_KeyDown(void* widget, int key) {
879 struct TableWidget* w = (struct TableWidget*)widget;
880 if (w->selectedIndex == -1) return false0;
881
882 if (key == KEY_LEFT || key == KEY_KP4) {
883 TableWidget_ScrollRelative(w, -1);
884 } else if (key == KEY_RIGHT || key == KEY_KP6) {
885 TableWidget_ScrollRelative(w, 1);
886 } else if (key == KEY_UP || key == KEY_KP8) {
887 TableWidget_ScrollRelative(w, -w->blocksPerRow);
888 } else if (key == KEY_DOWN || key == KEY_KP2) {
889 TableWidget_ScrollRelative(w, w->blocksPerRow);
890 } else {
891 return false0;
892 }
893 return true1;
894}
895
896static const struct WidgetVTABLE TableWidget_VTABLE = {
897 TableWidget_Render, TableWidget_Free, TableWidget_Reposition,
898 TableWidget_KeyDown, Widget_InputUp, TableWidget_MouseScroll,
899 TableWidget_PointerDown, TableWidget_PointerUp, TableWidget_PointerMove
900};
901void TableWidget_Create(struct TableWidget* w) {
902 Widget_Reset(w);
903 w->VTABLE = &TableWidget_VTABLE;
904 w->lastCreatedIndex = -1000;
905 ScrollbarWidget_Create(&w->scroll);
906
907 w->horAnchor = ANCHOR_CENTRE;
908 w->verAnchor = ANCHOR_CENTRE;
909 w->lastX = -20; w->lastY = -20;
910 w->scale = 1;
911
912 w->paddingX = Display_ScaleX(15);
913 w->paddingTopY = Display_ScaleY(15 + 20);
914 w->paddingMaxY = Display_ScaleX(15);
915}
916
917void TableWidget_SetBlockTo(struct TableWidget* w, BlockID block) {
918 int i;
919 w->selectedIndex = -1;
920
921 for (i = 0; i < w->blocksCount; i++) {
922 if (w->blocks[i] == block) w->selectedIndex = i;
923 }
924 /* When holding air, inventory should open at middle */
925 if (block == BLOCK_AIR) w->selectedIndex = -1;
926
927 w->scroll.topRow = w->selectedIndex / w->blocksPerRow;
928 w->scroll.topRow -= (w->rowsVisible - 1);
929 ScrollbarWidget_ClampTopRow(&w->scroll);
930 TableWidget_MoveCursorToSelected(w);
931 TableWidget_RecreateDescTex(w);
932}
933
934void TableWidget_OnInventoryChanged(struct TableWidget* w) {
935 TableWidget_RecreateBlocks(w);
936 if (w->selectedIndex >= w->blocksCount) {
937 w->selectedIndex = w->blocksCount - 1;
938 }
939 w->lastX = -1; w->lastY = -1;
940
941 w->scroll.topRow = w->selectedIndex / w->blocksPerRow;
942 ScrollbarWidget_ClampTopRow(&w->scroll);
943 TableWidget_RecreateDescTex(w);
944}
945
946
947/*########################################################################################################################*
948*-------------------------------------------------------InputWidget-------------------------------------------------------*
949*#########################################################################################################################*/
950static void InputWidget_Reset(struct InputWidget* w) {
951 Widget_Reset(w);
952 w->caretPos = -1;
953 w->caretOffset = Display_ScaleY(2);
954 w->OnTextChanged = NULL((void*)0);
955}
956
957static void InputWidget_FormatLine(struct InputWidget* w, int i, cc_string* line) {
958 cc_string src = w->lines[i];
959 if (!w->convertPercents) { String_AppendString(line, &src); return; }
960
961 for (i = 0; i < src.length; i++) {
962 char c = src.buffer[i];
963 if (c == '%' && Drawer2D_ValidColCodeAt(&src, i + 1)) { c = '&'; }
964 String_Append(line, c);
965 }
966}
967
968static void InputWidget_CalculateLineSizes(struct InputWidget* w) {
969 cc_string line; char lineBuffer[STRING_SIZE64];
970 struct DrawTextArgs args;
971 int y;
972
973 for (y = 0; y < INPUTWIDGET_MAX_LINES3; y++) {
974 w->lineWidths[y] = 0;
975 }
976 w->lineWidths[0] = w->prefixWidth;
977 DrawTextArgs_MakeEmpty(&args, w->font, true1);
978
979 String_InitArray(line, lineBuffer)line.buffer = lineBuffer; line.length = 0; line.capacity = sizeof
(lineBuffer);
;
980 for (y = 0; y < w->GetMaxLines(); y++) {
981 line.length = 0;
982 InputWidget_FormatLine(w, y, &line);
983
984 args.text = line;
985 w->lineWidths[y] += Drawer2D_TextWidth(&args);
986 }
987}
988
989static char InputWidget_GetLastCol(struct InputWidget* w, int x, int y) {
990 cc_string line; char lineBuffer[STRING_SIZE64];
991 char col;
992 String_InitArray(line, lineBuffer)line.buffer = lineBuffer; line.length = 0; line.capacity = sizeof
(lineBuffer);
;
993
994 for (; y >= 0; y--) {
995 line.length = 0;
996 InputWidget_FormatLine(w, y, &line);
997
998 col = Drawer2D_LastCol(&line, x);
999 if (col) return col;
1000 if (y > 0) { x = w->lines[y - 1].length; }
1001 }
1002 return '\0';
1003}
1004
1005static void InputWidget_UpdateCaret(struct InputWidget* w) {
1006 static const cc_string caret = String_FromConst("_"){ "_", (sizeof("_") - 1), (sizeof("_") - 1)};
1007 BitmapCol col;
1008 cc_string line; char lineBuffer[STRING_SIZE64];
1009 struct DrawTextArgs args;
1010 int maxChars, lineWidth;
1011 char colCode;
1012
1013 if (!w->caretTex.ID) {
1014 DrawTextArgs_Make(&args, &caret, w->font, true1);
1015 Drawer2D_MakeTextTexture(&w->caretTex, &args);
1016 w->caretWidth = (cc_uint16)((w->caretTex.Width * 3) / 4);
1017 }
1018
1019 maxChars = w->GetMaxLines() * INPUTWIDGET_LEN64;
1020 if (w->caretPos >= maxChars) w->caretPos = -1;
1021 WordWrap_GetCoords(w->caretPos, w->lines, w->GetMaxLines(), &w->caretX, &w->caretY);
1022
1023 DrawTextArgs_MakeEmpty(&args, w->font, false0);
1024 w->caretAccumulator = 0;
1025 w->caretTex.Width = w->caretWidth;
1026
1027 /* Caret is at last character on line */
1028 if (w->caretX == INPUTWIDGET_LEN64) {
1029 lineWidth = w->lineWidths[w->caretY];
1030 } else {
1031 String_InitArray(line, lineBuffer)line.buffer = lineBuffer; line.length = 0; line.capacity = sizeof
(lineBuffer);
;
1032 InputWidget_FormatLine(w, w->caretY, &line);
1033
1034 args.text = String_UNSAFE_Substring(&line, 0, w->caretX);
1035 lineWidth = Drawer2D_TextWidth(&args);
1036 if (w->caretY == 0) lineWidth += w->prefixWidth;
1037
1038 if (w->caretX < line.length) {
1039 args.text = String_UNSAFE_Substring(&line, w->caretX, 1);
1040 args.useShadow = true1;
1041 w->caretTex.Width = Drawer2D_TextWidth(&args);
1042 }
1043 }
1044
1045 w->caretTex.X = w->x + w->padding + lineWidth;
1046 w->caretTex.Y = (w->inputTex.Y + w->caretOffset) + w->caretY * w->lineHeight;
1047 colCode = InputWidget_GetLastCol(w, w->caretX, w->caretY);
1048
1049 if (colCode) {
1050 col = Drawer2D_GetCol(colCode)Drawer2D_Cols[(cc_uint8)colCode];
1051 /* Component order might be different to BitmapCol */
1052 w->caretCol = PackedCol_Make(BitmapCol_R(col), BitmapCol_G(col),(((cc_uint8)(((cc_uint8)(col >> 16))) << 0) | ((cc_uint8
)(((cc_uint8)(col >> 8))) << 8) | ((cc_uint8)(((cc_uint8
)(col >> 0))) << 16) | ((cc_uint8)(((cc_uint8)(col
>> 24))) << 24))
1053 BitmapCol_B(col), BitmapCol_A(col))(((cc_uint8)(((cc_uint8)(col >> 16))) << 0) | ((cc_uint8
)(((cc_uint8)(col >> 8))) << 8) | ((cc_uint8)(((cc_uint8
)(col >> 0))) << 16) | ((cc_uint8)(((cc_uint8)(col
>> 24))) << 24))
;
1054 } else {
1055 w->caretCol = PackedCol_Scale(PACKEDCOL_WHITE(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 24
))
, 0.8f);
1056 }
1057}
1058
1059static void InputWidget_RenderCaret(struct InputWidget* w, double delta) {
1060 float second;
1061 if (!w->showCaret) return;
1062 w->caretAccumulator += delta;
1063
1064 second = Math_Mod1((float)w->caretAccumulator);
1065 if (second < 0.5f) Texture_RenderShaded(&w->caretTex, w->caretCol);
1066}
1067
1068static void InputWidget_OnPressedEnter(void* widget) {
1069 struct InputWidget* w = (struct InputWidget*)widget;
1070 InputWidget_Clear(w);
1071 w->height = w->lineHeight;
1072 /* TODO get rid of this awful hack.. */
1073 Widget_Layout(w)(w)->VTABLE->Reposition(w);
1074}
1075
1076void InputWidget_Clear(struct InputWidget* w) {
1077 int i;
1078 w->text.length = 0;
1079
1080 for (i = 0; i < Array_Elems(w->lines)(sizeof(w->lines) / sizeof(w->lines[0])); i++) {
1081 w->lines[i] = String_Empty;
1082 }
1083
1084 w->caretPos = -1;
1085 Gfx_DeleteTexture(&w->inputTex.ID);
1086 /* TODO: Maybe call w->OnTextChanged */
1087}
1088
1089static cc_bool InputWidget_AllowedChar(void* widget, char c) {
1090 return Server.SupportsFullCP437 || (Convert_CP437ToUnicode(c) == c);
1091}
1092
1093static void InputWidget_AppendChar(struct InputWidget* w, char c) {
1094 if (w->caretPos == -1) {
1095 String_InsertAt(&w->text, w->text.length, c);
1096 } else {
1097 String_InsertAt(&w->text, w->caretPos, c);
1098 w->caretPos++;
1099 if (w->caretPos >= w->text.length) { w->caretPos = -1; }
1100 }
1101}
1102
1103static cc_bool InputWidget_TryAppendChar(struct InputWidget* w, char c) {
1104 int maxChars = w->GetMaxLines() * INPUTWIDGET_LEN64;
1105 if (w->text.length >= maxChars) return false0;
1106 if (!w->AllowedChar(w, c)) return false0;
1107
1108 InputWidget_AppendChar(w, c);
1109 return true1;
1110}
1111
1112static int InputWidget_DoAppendText(struct InputWidget* w, const cc_string* text) {
1113 int i, appended = 0;
1114 for (i = 0; i < text->length; i++) {
1115 if (InputWidget_TryAppendChar(w, text->buffer[i])) appended++;
1116 }
1117 return appended;
1118}
1119
1120void InputWidget_AppendText(struct InputWidget* w, const cc_string* text) {
1121 int appended = InputWidget_DoAppendText(w, text);
1122 if (appended) InputWidget_UpdateText(w);
1123}
1124
1125void InputWidget_Append(struct InputWidget* w, char c) {
1126 if (!InputWidget_TryAppendChar(w, c)) return;
1127 InputWidget_UpdateText(w);
1128}
1129
1130static void InputWidget_DeleteChar(struct InputWidget* w) {
1131 if (!w->text.length) return;
1132
1133 if (w->caretPos == -1) {
1134 String_DeleteAt(&w->text, w->text.length - 1);
1135 } else if (w->caretPos > 0) {
1136 w->caretPos--;
1137 String_DeleteAt(&w->text, w->caretPos);
1138 }
1139}
1140
1141static void InputWidget_BackspaceKey(struct InputWidget* w) {
1142 int i, len;
1143
1144 if (Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) {
1145 if (w->caretPos == -1) { w->caretPos = w->text.length - 1; }
1146 len = WordWrap_GetBackLength(&w->text, w->caretPos);
1147 if (!len) return;
1148
1149 w->caretPos -= len;
1150 if (w->caretPos < 0) { w->caretPos = 0; }
1151
1152 for (i = 0; i <= len; i++) {
1153 String_DeleteAt(&w->text, w->caretPos);
1154 }
1155
1156 if (w->caretPos >= w->text.length) { w->caretPos = -1; }
1157 if (w->caretPos == -1 && w->text.length > 0) {
1158 String_InsertAt(&w->text, w->text.length, ' ');
1159 } else if (w->caretPos >= 0 && w->text.buffer[w->caretPos] != ' ') {
1160 String_InsertAt(&w->text, w->caretPos, ' ');
1161 }
1162 InputWidget_UpdateText(w);
1163 } else if (w->text.length > 0 && w->caretPos != 0) {
1164 InputWidget_DeleteChar(w);
1165 InputWidget_UpdateText(w);
1166 }
1167}
1168
1169static void InputWidget_DeleteKey(struct InputWidget* w) {
1170 if (w->text.length > 0 && w->caretPos != -1) {
1171 String_DeleteAt(&w->text, w->caretPos);
1172 if (w->caretPos >= w->text.length) { w->caretPos = -1; }
1173 InputWidget_UpdateText(w);
1174 }
1175}
1176
1177static void InputWidget_LeftKey(struct InputWidget* w) {
1178 if (Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) {
1179 if (w->caretPos == -1) { w->caretPos = w->text.length - 1; }
1180 w->caretPos -= WordWrap_GetBackLength(&w->text, w->caretPos);
1181 InputWidget_UpdateCaret(w);
1182 return;
1183 }
1184
1185 if (w->text.length > 0) {
1186 if (w->caretPos == -1) { w->caretPos = w->text.length; }
1187 w->caretPos--;
1188 if (w->caretPos < 0) { w->caretPos = 0; }
1189 InputWidget_UpdateCaret(w);
1190 }
1191}
1192
1193static void InputWidget_RightKey(struct InputWidget* w) {
1194 if (Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) {
1195 w->caretPos += WordWrap_GetForwardLength(&w->text, w->caretPos);
1196 if (w->caretPos >= w->text.length) { w->caretPos = -1; }
1197 InputWidget_UpdateCaret(w);
1198 return;
1199 }
1200
1201 if (w->text.length > 0 && w->caretPos != -1) {
1202 w->caretPos++;
1203 if (w->caretPos >= w->text.length) { w->caretPos = -1; }
1204 InputWidget_UpdateCaret(w);
1205 }
1206}
1207
1208static void InputWidget_HomeKey(struct InputWidget* w) {
1209 if (!w->text.length) return;
1210 w->caretPos = 0;
1211 InputWidget_UpdateCaret(w);
1212}
1213
1214static void InputWidget_EndKey(struct InputWidget* w) {
1215 w->caretPos = -1;
1216 InputWidget_UpdateCaret(w);
1217}
1218
1219static void InputWidget_CopyFromClipboard(cc_string* text, void* w) {
1220 InputWidget_AppendText((struct InputWidget*)w, text);
1221}
1222
1223static cc_bool InputWidget_OtherKey(struct InputWidget* w, int key) {
1224 int maxChars = w->GetMaxLines() * INPUTWIDGET_LEN64;
1225 if (!Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) return false0;
1226
1227 if (key == 'V' && w->text.length < maxChars) {
1228 Clipboard_RequestText(InputWidget_CopyFromClipboard, w);
1229 return true1;
1230 } else if (key == 'C') {
1231 if (!w->text.length) return true1;
1232 Clipboard_SetText(&w->text);
1233 return true1;
1234 }
1235 return false0;
1236}
1237
1238void InputWidget_UpdateText(struct InputWidget* w) {
1239 int lines = w->GetMaxLines();
1240 if (lines > 1) {
1241 WordWrap_Do(&w->text, w->lines, lines, INPUTWIDGET_LEN64);
1242 } else {
1243 w->lines[0] = w->text;
1244 }
1245
1246 Gfx_DeleteTexture(&w->inputTex.ID);
1247 InputWidget_CalculateLineSizes(w);
1248 w->RemakeTexture(w);
1249 InputWidget_UpdateCaret(w);
1250 Window_SetKeyboardText(&w->text);
1251 if (w->OnTextChanged) w->OnTextChanged(w);
1252}
1253
1254void InputWidget_SetText(struct InputWidget* w, const cc_string* str) {
1255 InputWidget_Clear(w);
1256 InputWidget_DoAppendText(w, str);
1257 InputWidget_UpdateText(w);
1258}
1259
1260static void InputWidget_Free(void* widget) {
1261 struct InputWidget* w = (struct InputWidget*)widget;
1262 Gfx_DeleteTexture(&w->inputTex.ID);
1263 Gfx_DeleteTexture(&w->caretTex.ID);
1264}
1265
1266static void InputWidget_Reposition(void* widget) {
1267 struct InputWidget* w = (struct InputWidget*)widget;
1268 int oldX = w->x, oldY = w->y;
1269 Widget_CalcPosition(w);
1270
1271 w->caretTex.X += w->x - oldX; w->caretTex.Y += w->y - oldY;
1272 w->inputTex.X += w->x - oldX; w->inputTex.Y += w->y - oldY;
1273}
1274
1275static int InputWidget_KeyDown(void* widget, int key) {
1276 struct InputWidget* w = (struct InputWidget*)widget;
1277 if (key == KEY_LEFT) {
1278 InputWidget_LeftKey(w);
1279 } else if (key == KEY_RIGHT) {
1280 InputWidget_RightKey(w);
1281 } else if (key == KEY_BACKSPACE) {
1282 InputWidget_BackspaceKey(w);
1283 } else if (key == KEY_DELETE) {
1284 InputWidget_DeleteKey(w);
1285 } else if (key == KEY_HOME) {
1286 InputWidget_HomeKey(w);
1287 } else if (key == KEY_END) {
1288 InputWidget_EndKey(w);
1289 } else if (!InputWidget_OtherKey(w, key)) {
1290 return false0;
1291 }
1292 return true1;
1293}
1294
1295static int InputWidget_PointerDown(void* widget, int id, int x, int y) {
1296 cc_string line; char lineBuffer[STRING_SIZE64];
1297 struct InputWidget* w = (struct InputWidget*)widget;
1298 struct DrawTextArgs args;
1299 int cx, cy, offset = 0;
1300 int charX, charWidth, charHeight;
1301
1302 x -= w->inputTex.X; y -= w->inputTex.Y;
1303 DrawTextArgs_MakeEmpty(&args, w->font, true1);
1304 charHeight = w->lineHeight;
1305 String_InitArray(line, lineBuffer)line.buffer = lineBuffer; line.length = 0; line.capacity = sizeof
(lineBuffer);
;
1306
1307 for (cy = 0; cy < w->GetMaxLines(); cy++) {
1308 line.length = 0;
1309 InputWidget_FormatLine(w, cy, &line);
1310 if (!line.length) continue;
1311
1312 for (cx = 0; cx < line.length; cx++) {
1313 args.text = String_UNSAFE_Substring(&line, 0, cx);
1314 charX = Drawer2D_TextWidth(&args);
1315 if (cy == 0) charX += w->prefixWidth;
1316
1317 args.text = String_UNSAFE_Substring(&line, cx, 1);
1318 charWidth = Drawer2D_TextWidth(&args);
1319
1320 if (Gui_Contains(charX, cy * charHeight, charWidth, charHeight, x, y)) {
1321 w->caretPos = offset + cx;
1322 InputWidget_UpdateCaret(w);
1323 return TOUCH_TYPE_GUI1;
1324 }
1325 }
1326 offset += line.length;
1327 }
1328
1329 w->caretPos = -1;
1330 InputWidget_UpdateCaret(w);
1331 return TOUCH_TYPE_GUI1;
1332}
1333
1334
1335/*########################################################################################################################*
1336*-----------------------------------------------------MenuInputDesc-------------------------------------------------------*
1337*#########################################################################################################################*/
1338static void Hex_Range(struct MenuInputDesc* d, cc_string* range) {
1339 String_AppendConst(range, "&7(#000000 - #FFFFFF)");
1340}
1341
1342static cc_bool Hex_ValidChar(struct MenuInputDesc* d, char c) {
1343 return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
1344}
1345
1346static cc_bool Hex_ValidString(struct MenuInputDesc* d, const cc_string* s) {
1347 return s->length <= 6;
1348}
1349
1350static cc_bool Hex_ValidValue(struct MenuInputDesc* d, const cc_string* s) {
1351 cc_uint8 rgb[3];
1352 return PackedCol_TryParseHex(s, rgb);
1353}
1354
1355static void Hex_Default(struct MenuInputDesc* d, cc_string* value) {
1356 PackedCol_ToHex(value, d->meta.h.Default);
1357}
1358
1359const struct MenuInputVTABLE HexInput_VTABLE = {
1360 Hex_Range, Hex_ValidChar, Hex_ValidString, Hex_ValidValue, Hex_Default
1361};
1362
1363static void Int_Range(struct MenuInputDesc* d, cc_string* range) {
1364 String_Format2(range, "&7(%i - %i)", &d->meta.i.Min, &d->meta.i.Max);
1365}
1366
1367static cc_bool Int_ValidChar(struct MenuInputDesc* d, char c) {
1368 return (c >= '0' && c <= '9') || c == '-';
1369}
1370
1371static cc_bool Int_ValidString(struct MenuInputDesc* d, const cc_string* s) {
1372 int value;
1373 if (s->length == 1 && s->buffer[0] == '-') return true1; /* input is just a minus sign */
1374 return Convert_ParseInt(s, &value);
1375}
1376
1377static cc_bool Int_ValidValue(struct MenuInputDesc* d, const cc_string* s) {
1378 int value, min = d->meta.i.Min, max = d->meta.i.Max;
1379 return Convert_ParseInt(s, &value) && min <= value && value <= max;
1380}
1381
1382static void Int_Default(struct MenuInputDesc* d, cc_string* value) {
1383 String_AppendInt(value, d->meta.i.Default);
1384}
1385
1386const struct MenuInputVTABLE IntInput_VTABLE = {
1387 Int_Range, Int_ValidChar, Int_ValidString, Int_ValidValue, Int_Default
1388};
1389
1390static void Seed_Range(struct MenuInputDesc* d, cc_string* range) {
1391 String_AppendConst(range, "&7(an integer)");
1392}
1393static void Seed_NoDefault(struct MenuInputDesc* d, cc_string* value) { }
1394
1395const struct MenuInputVTABLE SeedInput_VTABLE = {
1396 Seed_Range, Int_ValidChar, Int_ValidString, Int_ValidValue, Seed_NoDefault
1397};
1398
1399static void Float_Range(struct MenuInputDesc* d, cc_string* range) {
1400 String_Format2(range, "&7(%f2 - %f2)", &d->meta.f.Min, &d->meta.f.Max);
1401}
1402
1403static cc_bool Float_ValidChar(struct MenuInputDesc* d, char c) {
1404 return (c >= '0' && c <= '9') || c == '-' || c == '.' || c == ',';
1405}
1406
1407static cc_bool Float_ValidString(struct MenuInputDesc* d, const cc_string* s) {
1408 float value;
1409 if (s->length == 1 && Float_ValidChar(d, s->buffer[0])) return true1;
1410 return Convert_ParseFloat(s, &value);
1411}
1412
1413static cc_bool Float_ValidValue(struct MenuInputDesc* d, const cc_string* s) {
1414 float value, min = d->meta.f.Min, max = d->meta.f.Max;
1415 return Convert_ParseFloat(s, &value) && min <= value && value <= max;
1416}
1417
1418static void Float_Default(struct MenuInputDesc* d, cc_string* value) {
1419 String_AppendFloat(value, d->meta.f.Default, 3);
1420}
1421
1422const struct MenuInputVTABLE FloatInput_VTABLE = {
1423 Float_Range, Float_ValidChar, Float_ValidString, Float_ValidValue, Float_Default
1424};
1425
1426static void Path_Range(struct MenuInputDesc* d, cc_string* range) {
1427 String_AppendConst(range, "&7(Enter name)");
1428}
1429
1430static cc_bool Path_ValidChar(struct MenuInputDesc* d, char c) {
1431 return !(c == '/' || c == '\\' || c == '?' || c == '*' || c == ':'
1432 || c == '<' || c == '>' || c == '|' || c == '"' || c == '.');
1433}
1434static cc_bool Path_ValidString(struct MenuInputDesc* d, const cc_string* s) { return true1; }
1435
1436const struct MenuInputVTABLE PathInput_VTABLE = {
1437 Path_Range, Path_ValidChar, Path_ValidString, Path_ValidString, Seed_NoDefault
1438};
1439
1440static void String_Range(struct MenuInputDesc* d, cc_string* range) {
1441 String_AppendConst(range, "&7(Enter text)");
1442}
1443
1444static cc_bool String_ValidChar(struct MenuInputDesc* d, char c) {
1445 return c != '&';
1446}
1447
1448static cc_bool String_ValidString(struct MenuInputDesc* d, const cc_string* s) {
1449 return s->length <= STRING_SIZE64;
1450}
1451
1452const struct MenuInputVTABLE StringInput_VTABLE = {
1453 String_Range, String_ValidChar, String_ValidString, String_ValidString, Seed_NoDefault
1454};
1455
1456
1457/*########################################################################################################################*
1458*-----------------------------------------------------TextInputWidget-----------------------------------------------------*
1459*#########################################################################################################################*/
1460static void TextInputWidget_Render(void* widget, double delta) {
1461 struct InputWidget* w = (struct InputWidget*)widget;
1462 Texture_Render(&w->inputTex);
1463 InputWidget_RenderCaret(w, delta);
1464}
1465
1466static void TextInputWidget_BuildMesh(void* widget, struct VertexTextured** vertices) {
1467 struct InputWidget* w = (struct InputWidget*)widget;
1468 Gfx_Make2DQuad(&w->inputTex, PACKEDCOL_WHITE(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 24
))
, vertices);
1469 Gfx_Make2DQuad(&w->caretTex, w->caretCol, vertices);
1470}
1471
1472static int TextInputWidget_Render2(void* widget, int offset) {
1473 struct InputWidget* w = (struct InputWidget*)widget;
1474 Gfx_BindTexture(w->inputTex.ID);
1475 Gfx_DrawVb_IndexedTris_Range(4, offset);
1476 offset += 4;
1477
1478 if (w->showCaret && Math_Mod1((float)w->caretAccumulator) < 0.5f) {
1479 Gfx_BindTexture(w->caretTex.ID);
1480 Gfx_DrawVb_IndexedTris_Range(4, offset);
1481 }
1482 return offset + 4;
1483}
1484
1485static void TextInputWidget_RemakeTexture(void* widget) {
1486 cc_string range; char rangeBuffer[STRING_SIZE64];
1487 struct TextInputWidget* w = (struct TextInputWidget*)widget;
1488 PackedCol backCol = PackedCol_Make(30, 30, 30, 200)(((cc_uint8)(30) << 0) | ((cc_uint8)(30) << 8) | (
(cc_uint8)(30) << 16) | ((cc_uint8)(200) << 24))
;
1489 struct MenuInputDesc* desc;
1490 struct DrawTextArgs args;
1491 struct Texture* tex;
1492 int textWidth, lineHeight;
1493 int width, height, hintX, y;
1494 struct Bitmap bmp;
1495
1496 DrawTextArgs_Make(&args, &w->base.text, w->base.font, false0);
1497 textWidth = Drawer2D_TextWidth(&args);
1498 lineHeight = w->base.lineHeight;
1499 w->base.caretAccumulator = 0.0;
1500
1501 String_InitArray(range, rangeBuffer)range.buffer = rangeBuffer; range.length = 0; range.capacity =
sizeof(rangeBuffer);
;
1502 desc = &w->desc;
1503 desc->VTABLE->GetRange(desc, &range);
1504
1505 width = max(textWidth, w->minWidth)((textWidth) > (w->minWidth) ? (textWidth) : (w->minWidth
))
; w->base.width = width;
1506 height = max(lineHeight, w->minHeight)((lineHeight) > (w->minHeight) ? (lineHeight) : (w->
minHeight))
; w->base.height = height;
1507
1508 Bitmap_AllocateClearedPow2(&bmp, width, height);
1509 {
1510 /* Centre text vertically */
1511 y = 0;
1512 if (lineHeight < height) { y = height / 2 - lineHeight / 2; }
1513 w->base.caretOffset = 2 + y;
1514
1515 Drawer2D_Clear(&bmp, backCol, 0, 0, width, height);
1516 Drawer2D_DrawText(&bmp, &args, w->base.padding, y);
1517
1518 args.text = range;
1519 hintX = width - Drawer2D_TextWidth(&args);
1520 /* Draw hint text right-aligned if it won't overlap input text */
1521 if (textWidth + 3 < hintX) {
1522 Drawer2D_DrawText(&bmp, &args, hintX, y);
1523 }
1524 }
1525
1526 tex = &w->base.inputTex;
1527 Drawer2D_MakeTexture(tex, &bmp, width, height);
1528 Mem_Free(bmp.scan0);
1529
1530 Widget_Layout(&w->base)(&w->base)->VTABLE->Reposition(&w->base);
1531 tex->X = w->base.x; tex->Y = w->base.y;
1532}
1533
1534static cc_bool TextInputWidget_AllowedChar(void* widget, char c) {
1535 struct InputWidget* w = (struct InputWidget*)widget;
1536 struct MenuInputDesc* desc;
1537 int maxChars;
1538 cc_bool valid;
1539
1540 if (c == '&') return false0;
1541 desc = &((struct TextInputWidget*)w)->desc;
1542
1543 if (!desc->VTABLE->IsValidChar(desc, c)) return false0;
1544 maxChars = w->GetMaxLines() * INPUTWIDGET_LEN64;
1545 if (w->text.length == maxChars) return false0;
1546
1547 /* See if the new string is in valid format */
1548 InputWidget_AppendChar(w, c);
1549 valid = desc->VTABLE->IsValidString(desc, &w->text);
1550 InputWidget_DeleteChar(w);
1551 return valid;
1552}
1553
1554static int TextInputWidget_GetMaxLines(void) { return 1; }
1555static const struct WidgetVTABLE TextInputWidget_VTABLE = {
1556 TextInputWidget_Render, InputWidget_Free, InputWidget_Reposition,
1557 InputWidget_KeyDown, Widget_InputUp, Widget_MouseScroll,
1558 InputWidget_PointerDown, Widget_PointerUp, Widget_PointerMove,
1559 TextInputWidget_BuildMesh, TextInputWidget_Render2
1560};
1561void TextInputWidget_Create(struct TextInputWidget* w, int width, const cc_string* text, struct MenuInputDesc* desc) {
1562 InputWidget_Reset(&w->base);
1563 w->base.VTABLE = &TextInputWidget_VTABLE;
1564
1565 w->minWidth = Display_ScaleX(width);
1566 w->minHeight = Display_ScaleY(30);
1567 w->desc = *desc;
1568
1569 w->base.convertPercents = false0;
1570 w->base.padding = 3;
1571 w->base.showCaret = true1;
1572
1573 w->base.GetMaxLines = TextInputWidget_GetMaxLines;
1574 w->base.RemakeTexture = TextInputWidget_RemakeTexture;
1575 w->base.OnPressedEnter = InputWidget_OnPressedEnter;
1576 w->base.AllowedChar = TextInputWidget_AllowedChar;
1577
1578 String_InitArray(w->base.text, w->_textBuffer)w->base.text.buffer = w->_textBuffer; w->base.text.length
= 0; w->base.text.capacity = sizeof(w->_textBuffer);
;
1579 String_Copy(&w->base.text, text);
1580}
1581
1582void TextInputWidget_SetFont(struct TextInputWidget* w, struct FontDesc* font) {
1583 w->base.font = font;
1584 w->base.lineHeight = Drawer2D_FontHeight(font, false0);
1585 InputWidget_UpdateText(&w->base);
1586}
1587
1588
1589/*########################################################################################################################*
1590*-----------------------------------------------------ChatInputWidget-----------------------------------------------------*
1591*#########################################################################################################################*/
1592static const cc_string chatInputPrefix = String_FromConst("> "){ "> ", (sizeof("> ") - 1), (sizeof("> ") - 1)};
1593
1594static void ChatInputWidget_RemakeTexture(void* widget) {
1595 cc_string line; char lineBuffer[STRING_SIZE64 + 2];
1596 struct InputWidget* w = (struct InputWidget*)widget;
1597 struct DrawTextArgs args;
1598 int width = 0, height = 0;
1599 struct Bitmap bmp;
1600 char lastCol;
1601 int i, x, y;
1602
1603 for (i = 0; i < w->GetMaxLines(); i++) {
1604 if (!w->lines[i].length) break;
1605 height += w->lineHeight;
1606 width = max(width, w->lineWidths[i])((width) > (w->lineWidths[i]) ? (width) : (w->lineWidths
[i]))
;
1607 }
1608
1609 if (!width) width = w->prefixWidth;
1610 if (!height) height = w->lineHeight;
1611 Bitmap_AllocateClearedPow2(&bmp, width, height);
1612
1613 DrawTextArgs_Make(&args, &chatInputPrefix, w->font, true1);
1614 Drawer2D_DrawText(&bmp, &args, 0, 0);
1615
1616 String_InitArray(line, lineBuffer)line.buffer = lineBuffer; line.length = 0; line.capacity = sizeof
(lineBuffer);
;
1617 for (i = 0, y = 0; i < Array_Elems(w->lines)(sizeof(w->lines) / sizeof(w->lines[0])); i++) {
1618 if (!w->lines[i].length) break;
1619 line.length = 0;
1620
1621 /* Colour code continues in next line */
1622 lastCol = InputWidget_GetLastCol(w, 0, i);
1623 if (!Drawer2D_IsWhiteCol(lastCol)) {
1624 String_Append(&line, '&'); String_Append(&line, lastCol);
1625 }
1626 /* Convert % to & for colour codes */
1627 InputWidget_FormatLine(w, i, &line);
1628 args.text = line;
1629
1630 x = i == 0 ? w->prefixWidth : 0;
1631 Drawer2D_DrawText(&bmp, &args, x, y);
1632 y += w->lineHeight;
1633 }
1634
1635 Drawer2D_MakeTexture(&w->inputTex, &bmp, width, height);
1636 Mem_Free(bmp.scan0);
1637 w->caretAccumulator = 0;
1638
1639 w->width = width;
1640 w->height = height;
1641 Widget_Layout(w)(w)->VTABLE->Reposition(w);
1642 w->inputTex.X = w->x + w->padding;
1643 w->inputTex.Y = w->y;
1644}
1645
1646static void ChatInputWidget_Render(void* widget, double delta) {
1647 struct InputWidget* w = (struct InputWidget*)widget;
1648 PackedCol backCol = PackedCol_Make(0, 0, 0, 127)(((cc_uint8)(0) << 0) | ((cc_uint8)(0) << 8) | ((
cc_uint8)(0) << 16) | ((cc_uint8)(127) << 24))
;
1649 int x = w->x, y = w->y;
1650 cc_bool caretAtEnd;
1651 int i, width;
1652
1653 Gfx_SetTexturing(false0);
1654 for (i = 0; i < INPUTWIDGET_MAX_LINES3; i++) {
1655 if (i > 0 && !w->lines[i].length) break;
1656
1657 caretAtEnd = (w->caretY == i) && (w->caretX == INPUTWIDGET_LEN64 || w->caretPos == -1);
1658 width = w->lineWidths[i] + (caretAtEnd ? w->caretTex.Width : 0);
1659 /* Cover whole window width to match original classic behaviour */
1660 if (Gui.ClassicChat) { width = max(width, WindowInfo.Width - x * 4)((width) > (WindowInfo.Width - x * 4) ? (width) : (WindowInfo
.Width - x * 4))
; }
1661
1662 Gfx_Draw2DFlat(x, y, width + w->padding * 2, w->lineHeight, backCol);
1663 y += w->lineHeight;
1664 }
1665
1666 Gfx_SetTexturing(true1);
1667 Texture_Render(&w->inputTex);
1668 InputWidget_RenderCaret(w, delta);
1669}
1670
1671static void ChatInputWidget_OnPressedEnter(void* widget) {
1672 struct ChatInputWidget* w = (struct ChatInputWidget*)widget;
1673 /* Don't want trailing spaces in output message */
1674 cc_string text = w->base.text;
1675 String_UNSAFE_TrimEnd(&text);
1676 if (text.length) { Chat_Send(&text, true1); }
1677
1678 w->origStr.length = 0;
1679 w->typingLogPos = Chat_InputLog.count; /* Index of newest entry + 1. */
1680
1681 Chat_AddOf(&String_Empty, MSG_TYPE_CLIENTSTATUS_2);
1682 InputWidget_OnPressedEnter(widget);
1683}
1684
1685static void ChatInputWidget_UpKey(struct InputWidget* w) {
1686 struct ChatInputWidget* W = (struct ChatInputWidget*)w;
1687 cc_string prevInput;
1688 int pos;
1689
1690 if (Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) {
1691 pos = w->caretPos == -1 ? w->text.length : w->caretPos;
1692 if (pos < INPUTWIDGET_LEN64) return;
1693
1694 w->caretPos = pos - INPUTWIDGET_LEN64;
1695 InputWidget_UpdateCaret(w);
1696 return;
1697 }
1698
1699 if (W->typingLogPos == Chat_InputLog.count) {
1700 String_Copy(&W->origStr, &w->text);
1701 }
1702
1703 if (!Chat_InputLog.count) return;
1704 W->typingLogPos--;
1705 w->text.length = 0;
1706
1707 if (W->typingLogPos < 0) W->typingLogPos = 0;
1708 prevInput = StringsBuffer_UNSAFE_Get(&Chat_InputLog, W->typingLogPos);
1709 String_AppendString(&w->text, &prevInput);
1710
1711 w->caretPos = -1;
1712 InputWidget_UpdateText(w);
1713}
1714
1715static void ChatInputWidget_DownKey(struct InputWidget* w) {
1716 struct ChatInputWidget* W = (struct ChatInputWidget*)w;
1717 cc_string prevInput;
1718
1719 if (Key_IsActionPressed()(Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])) {
1720 if (w->caretPos == -1) return;
1721
1722 w->caretPos += INPUTWIDGET_LEN64;
1723 if (w->caretPos >= w->text.length) { w->caretPos = -1; }
1724 InputWidget_UpdateCaret(w);
1725 return;
1726 }
1727
1728 if (!Chat_InputLog.count) return;
1729 W->typingLogPos++;
1730 w->text.length = 0;
1731
1732 if (W->typingLogPos >= Chat_InputLog.count) {
1733 W->typingLogPos = Chat_InputLog.count;
1734 String_AppendString(&w->text, &W->origStr);
1735 } else {
1736 prevInput = StringsBuffer_UNSAFE_Get(&Chat_InputLog, W->typingLogPos);
1737 String_AppendString(&w->text, &prevInput);
1738 }
1739
1740 w->caretPos = -1;
1741 InputWidget_UpdateText(w);
1742}
1743
1744static cc_bool ChatInputWidget_IsNameChar(char c) {
1745 return c == '_' || c == '.' || (c >= '0' && c <= '9')
1746 || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
1747}
1748
1749static void ChatInputWidget_TabKey(struct InputWidget* w) {
1750 cc_string str; char strBuffer[STRING_SIZE64];
1751 EntityID matches[TABLIST_MAX_NAMES256];
1752 cc_string part, name;
1753 int beg, end, len;
1754 int i, j, numMatches;
1755 char* buffer;
1756
1757 end = w->caretPos == -1 ? w->text.length - 1 : w->caretPos;
1758 beg = end;
1759 buffer = w->text.buffer;
1760
1761 /* e.g. if player typed "hi Nam", backtrack to "N" */
1762 while (beg >= 0 && ChatInputWidget_IsNameChar(buffer[beg])) beg--;
1763 beg++;
1764 if (end < 0 || beg > end) return;
1765
1766 part = String_UNSAFE_Substring(&w->text, beg, (end + 1) - beg);
1767 Chat_AddOf(&String_Empty, MSG_TYPE_CLIENTSTATUS_2);
1768 numMatches = 0;
1769
1770 for (i = 0; i < TABLIST_MAX_NAMES256; i++) {
1771 if (!TabList.NameOffsets[i]) continue;
1772
1773 name = TabList_UNSAFE_GetPlayer(i)StringsBuffer_UNSAFE_Get(&TabList._buffer, TabList.NameOffsets
[i] - 3);
;
1774 if (!String_CaselessContains(&name, &part)) continue;
1775 matches[numMatches++] = (EntityID)i;
1776 }
1777
1778 if (numMatches == 1) {
1779 if (w->caretPos == -1) end++;
1780 len = end - beg;
1781
1782 /* Following on from above example, delete 'N','a','m' */
1783 /* Then insert the e.g. matching 'nAME1' player name */
1784 for (j = 0; j < len; j++) {
1785 String_DeleteAt(&w->text, beg);
1786 }
1787
1788 if (w->caretPos != -1) w->caretPos -= len;
1789 name = TabList_UNSAFE_GetPlayer(matches[0])StringsBuffer_UNSAFE_Get(&TabList._buffer, TabList.NameOffsets
[matches[0]] - 3);
;
1790 InputWidget_AppendText(w, &name);
1791 } else if (numMatches > 1) {
1792 String_InitArray(str, strBuffer)str.buffer = strBuffer; str.length = 0; str.capacity = sizeof
(strBuffer);
;
1793 String_Format1(&str, "&e%i matching names: ", &numMatches);
1794
1795 for (i = 0; i < numMatches; i++) {
1796 name = TabList_UNSAFE_GetPlayer(matches[i])StringsBuffer_UNSAFE_Get(&TabList._buffer, TabList.NameOffsets
[matches[i]] - 3);
;
1797 if ((str.length + name.length + 1) > STRING_SIZE64) break;
1798
1799 String_AppendString(&str, &name);
1800 String_Append(&str, ' ');
1801 }
1802 Chat_AddOf(&str, MSG_TYPE_CLIENTSTATUS_2);
1803 }
1804}
1805
1806static int ChatInputWidget_KeyDown(void* widget, int key) {
1807 struct InputWidget* w = (struct InputWidget*)widget;
1808 if (key == KEY_TAB) { ChatInputWidget_TabKey(w); return true1; }
1809 if (key == KEY_UP) { ChatInputWidget_UpKey(w); return true1; }
1810 if (key == KEY_DOWN) { ChatInputWidget_DownKey(w); return true1; }
1811 return InputWidget_KeyDown(w, key);
1812}
1813
1814static int ChatInputWidget_GetMaxLines(void) {
1815 return !Game_ClassicMode && Server.SupportsPartialMessages ? INPUTWIDGET_MAX_LINES3 : 1;
1816}
1817
1818static const struct WidgetVTABLE ChatInputWidget_VTABLE = {
1819 ChatInputWidget_Render, InputWidget_Free, InputWidget_Reposition,
1820 ChatInputWidget_KeyDown, Widget_InputUp, Widget_MouseScroll,
1821 InputWidget_PointerDown, Widget_PointerUp, Widget_PointerMove
1822};
1823void ChatInputWidget_Create(struct ChatInputWidget* w) {
1824 InputWidget_Reset(&w->base);
1825 w->typingLogPos = Chat_InputLog.count; /* Index of newest entry + 1. */
1826 w->base.VTABLE = &ChatInputWidget_VTABLE;
1827
1828 w->base.convertPercents = !Game_ClassicMode;
1829 w->base.showCaret = true1;
1830 w->base.padding = 5;
1831 w->base.GetMaxLines = ChatInputWidget_GetMaxLines;
1832 w->base.RemakeTexture = ChatInputWidget_RemakeTexture;
1833 w->base.OnPressedEnter = ChatInputWidget_OnPressedEnter;
1834 w->base.AllowedChar = InputWidget_AllowedChar;
1835
1836 String_InitArray(w->base.text, w->_textBuffer)w->base.text.buffer = w->_textBuffer; w->base.text.length
= 0; w->base.text.capacity = sizeof(w->_textBuffer);
;
1837 String_InitArray(w->origStr, w->_origBuffer)w->origStr.buffer = w->_origBuffer; w->origStr.length
= 0; w->origStr.capacity = sizeof(w->_origBuffer);
;
1838}
1839
1840void ChatInputWidget_SetFont(struct ChatInputWidget* w, struct FontDesc* font) {
1841 struct DrawTextArgs args;
1842 DrawTextArgs_Make(&args, &chatInputPrefix, font, true1);
1843
1844 w->base.font = font;
1845 w->base.prefixWidth = Drawer2D_TextWidth(&args);
1846 w->base.lineHeight = Drawer2D_TextHeight(&args);
1847 Gfx_DeleteTexture(&w->base.caretTex.ID);
1848}
1849
1850
1851/*########################################################################################################################*
1852*-----------------------------------------------------TextGroupWidget-----------------------------------------------------*
1853*#########################################################################################################################*/
1854void TextGroupWidget_ShiftUp(struct TextGroupWidget* w) {
1855 int last, i;
1856 Gfx_DeleteTexture(&w->textures[0].ID);
1857 last = w->lines - 1;
1858
1859 for (i = 0; i < last; i++) {
1860 w->textures[i] = w->textures[i + 1];
1861 }
1862 w->textures[last].ID = 0; /* Gfx_DeleteTexture() called by TextGroupWidget_Redraw otherwise */
1863 TextGroupWidget_Redraw(w, last);
1864}
1865
1866void TextGroupWidget_ShiftDown(struct TextGroupWidget* w) {
1867 int last, i;
1868 last = w->lines - 1;
1869 Gfx_DeleteTexture(&w->textures[last].ID);
1870
1871 for (i = last; i > 0; i--) {
1872 w->textures[i] = w->textures[i - 1];
1873 }
1874 w->textures[0].ID = 0; /* Gfx_DeleteTexture() called by TextGroupWidget_Redraw otherwise */
1875 TextGroupWidget_Redraw(w, 0);
1876}
1877
1878int TextGroupWidget_UsedHeight(struct TextGroupWidget* w) {
1879 struct Texture* textures = w->textures;
1880 int i, height = 0;
1881
1882 for (i = 0; i < w->lines; i++) {
1883 if (textures[i].ID) break;
1884 }
1885 for (; i < w->lines; i++) {
1886 height += textures[i].Height;
1887 }
1888 return height;
1889}
1890
1891static void TextGroupWidget_Reposition(void* widget) {
1892 struct TextGroupWidget* w = (struct TextGroupWidget*)widget;
1893 struct Texture* textures = w->textures;
1894 int i, y, width = 0, height = 0;
1895
1896 /* Work out how big the text group is now */
1897 for (i = 0; i < w->lines; i++) {
1898 width = max(width, textures[i].Width)((width) > (textures[i].Width) ? (width) : (textures[i].Width
))
;
1899 height += textures[i].Height;
1900 }
1901
1902 w->width = width; w->height = height;
1903 Widget_CalcPosition(w);
1904
1905 for (i = 0, y = w->y; i < w->lines; i++) {
1906 textures[i].X = Gui_CalcPos(w->horAnchor, w->xOffset, textures[i].Width, WindowInfo.Width);
1907 textures[i].Y = y;
1908 y += textures[i].Height;
1909 }
1910}
1911
1912struct Portion { short Beg, Len, LineBeg, LineLen; };
1913#define TEXTGROUPWIDGET_HTTP_LEN7 7 /* length of http:// */
1914#define TEXTGROUPWIDGET_URL0x8000 0x8000
1915#define TEXTGROUPWIDGET_PACKED_LEN0x7FFF 0x7FFF
1916
1917static int TextGroupWidget_NextUrl(char* chars, int charsLen, int i) {
1918 int start, left;
1919
1920 for (; i < charsLen; i++) {
1921 if (!(chars[i] == 'h' || chars[i] == '&')) continue;
1922 left = charsLen - i;
1923 if (left < TEXTGROUPWIDGET_HTTP_LEN7) return charsLen;
1924
1925 /* colour codes at start of URL */
1926 start = i;
1927 while (left >= 2 && chars[i] == '&') { left -= 2; i += 2; }
1928 if (left < TEXTGROUPWIDGET_HTTP_LEN7) continue;
1929
1930 /* Starts with "http" */
1931 if (chars[i] != 'h' || chars[i + 1] != 't' || chars[i + 2] != 't' || chars[i + 3] != 'p') continue;
1932 left -= 4; i += 4;
1933
1934 /* And then with "s://" or "://" */
1935 if (chars[i] == 's') { left--; i++; }
1936 if (left >= 3 && chars[i] == ':' && chars[i + 1] == '/' && chars[i + 2] == '/') return start;
1937 }
1938 return charsLen;
1939}
1940
1941static int TextGroupWidget_UrlEnd(char* chars, int charsLen, int* begs, int begsLen, int i) {
1942 int start = i, j;
1943 int next, left;
1944 cc_bool isBeg;
1945
1946 for (; i < charsLen && chars[i] != ' '; i++) {
1947 /* Is this character the start of a line */
1948 isBeg = false0;
1949 for (j = 0; j < begsLen; j++) {
1950 if (i == begs[j]) { isBeg = true1; break; }
1951 }
1952
1953 /* Definitely not a multilined URL */
1954 if (!isBeg || i == start) continue;
1955 if (chars[i] != '>') break;
1956
1957 /* Does this line start with "> ", making it a multiline */
1958 next = i + 1; left = charsLen - next;
1959 while (left >= 2 && chars[next] == '&') { left -= 2; next += 2; }
1960 if (left == 0 || chars[next] != ' ') break;
1961
1962 i = next;
1963 }
1964 return i;
1965}
1966
1967static void TextGroupWidget_Output(struct Portion bit, int lineBeg, int lineEnd, struct Portion** portions) {
1968 struct Portion* cur;
1969 int overBy, underBy;
1970 if (bit.Beg >= lineEnd || !bit.Len) return;
1971
1972 bit.LineBeg = bit.Beg;
1973 bit.LineLen = bit.Len & TEXTGROUPWIDGET_PACKED_LEN0x7FFF;
1974
1975 /* Adjust this portion to be within this line */
1976 if (bit.Beg >= lineBeg) {
1977 } else if (bit.Beg + bit.LineLen > lineBeg) {
1978 /* Adjust start of portion to be within this line */
1979 underBy = lineBeg - bit.Beg;
1980 bit.LineBeg += underBy; bit.LineLen -= underBy;
1981 } else { return; }
1982
1983 /* Limit length of portion to be within this line */
1984 overBy = (bit.LineBeg + bit.LineLen) - lineEnd;
1985 if (overBy > 0) bit.LineLen -= overBy;
1986
1987 bit.LineBeg -= lineBeg;
1988 if (!bit.LineLen) return;
1989
1990 cur = *portions; *cur++ = bit; *portions = cur;
1991}
1992
1993static int TextGroupWidget_Reduce(struct TextGroupWidget* w, char* chars, int target, struct Portion* portions) {
1994 struct Portion* start = portions;
1995 int begs[TEXTGROUPWIDGET_MAX_LINES30];
1996 int ends[TEXTGROUPWIDGET_MAX_LINES30];
1997 struct Portion bit;
1998 cc_string line;
1999 int nextStart, i, total = 0, end;
2000
2001 for (i = 0; i < w->lines; i++) {
29
Assuming 'i' is >= field 'lines'
30
Loop condition is false. Execution continues on line 2011
2002 line = TextGroupWidget_UNSAFE_Get(w, i);
2003 begs[i] = -1; ends[i] = -1;
2004 if (!line.length) continue;
2005
2006 begs[i] = total;
2007 Mem_Copy(&chars[total], line.buffer, line.length);
2008 total += line.length; ends[i] = total;
2009 }
2010
2011 end = 0;
2012 for (;;) {
31
Loop condition is true. Entering loop body
2013 nextStart = TextGroupWidget_NextUrl(chars, total, end);
2014
2015 /* add normal portion between urls */
2016 bit.Beg = end;
2017 bit.Len = nextStart - end;
2018 TextGroupWidget_Output(bit, begs[target], ends[target], &portions);
32
2nd function call argument is an uninitialized value
2019
2020 if (nextStart == total) break;
2021 end = TextGroupWidget_UrlEnd(chars, total, begs, w->lines, nextStart);
2022
2023 /* add this url portion */
2024 bit.Beg = nextStart;
2025 bit.Len = (end - nextStart) | TEXTGROUPWIDGET_URL0x8000;
2026 TextGroupWidget_Output(bit, begs[target], ends[target], &portions);
2027 }
2028 return (int)(portions - start);
2029}
2030
2031static void TextGroupWidget_FormatUrl(cc_string* text, const cc_string* url) {
2032 char* dst;
2033 int i;
2034 String_AppendColorless(text, url);
2035
2036 /* Delete "> " multiline chars from URLs */
2037 dst = text->buffer;
2038 for (i = text->length - 2; i >= 0; i--) {
2039 if (dst[i] != '>' || dst[i + 1] != ' ') continue;
2040
2041 String_DeleteAt(text, i + 1);
2042 String_DeleteAt(text, i);
2043 }
2044}
2045
2046static cc_bool TextGroupWidget_GetUrl(struct TextGroupWidget* w, cc_string* text, int index, int mouseX) {
2047 char chars[TEXTGROUPWIDGET_MAX_LINES30 * TEXTGROUPWIDGET_LEN(64 + (64 / 2))];
2048 struct Portion portions[2 * (TEXTGROUPWIDGET_LEN(64 + (64 / 2)) / TEXTGROUPWIDGET_HTTP_LEN7)];
2049 struct Portion bit;
2050 struct DrawTextArgs args = { 0 };
2051 cc_string line, url;
2052 int portionsCount;
2053 int i, x, width;
2054
2055 mouseX -= w->textures[index].X;
2056 args.useShadow = true1;
2057 line = TextGroupWidget_UNSAFE_Get(w, index);
2058
2059 if (Game_ClassicMode) return false0;
2060 portionsCount = TextGroupWidget_Reduce(w, chars, index, portions);
2061
2062 for (i = 0, x = 0; i < portionsCount; i++) {
2063 bit = portions[i];
2064 args.text = String_UNSAFE_Substring(&line, bit.LineBeg, bit.LineLen);
2065 args.font = w->font;
2066
2067 width = Drawer2D_TextWidth(&args);
2068 if ((bit.Len & TEXTGROUPWIDGET_URL0x8000) && mouseX >= x && mouseX < x + width) {
2069 bit.Len &= TEXTGROUPWIDGET_PACKED_LEN0x7FFF;
2070 url = String_Init(&chars[bit.Beg], bit.Len, bit.Len);
2071
2072 TextGroupWidget_FormatUrl(text, &url);
2073 return true1;
2074 }
2075 x += width;
2076 }
2077 return false0;
2078}
2079
2080int TextGroupWidget_GetSelected(struct TextGroupWidget* w, cc_string* text, int x, int y) {
2081 struct Texture tex;
2082 cc_string line;
2083 int i;
2084
2085 for (i = 0; i < w->lines; i++) {
2086 if (!w->textures[i].ID) continue;
2087 tex = w->textures[i];
2088 if (!Gui_Contains(tex.X, tex.Y, tex.Width, tex.Height, x, y)) continue;
2089
2090 if (!TextGroupWidget_GetUrl(w, text, i, x)) {
2091 line = TextGroupWidget_UNSAFE_Get(w, i);
2092 String_AppendString(text, &line);
2093 }
2094 return i;
2095 }
2096 return -1;
2097}
2098
2099static cc_bool TextGroupWidget_MightHaveUrls(struct TextGroupWidget* w) {
2100 cc_string line;
2101 int i;
2102
2103 for (i = 0; i < w->lines; i++) {
2104 line = TextGroupWidget_UNSAFE_Get(w, i);
2105 if (String_IndexOf(&line, '/')String_IndexOfAt(&line, 0, '/') >= 0) return true1;
2106 }
2107 return false0;
2108}
2109
2110static void TextGroupWidget_DrawAdvanced(struct TextGroupWidget* w, struct Texture* tex, struct DrawTextArgs* args, int index, const cc_string* text) {
2111 char chars[TEXTGROUPWIDGET_MAX_LINES30 * TEXTGROUPWIDGET_LEN(64 + (64 / 2))];
2112 struct Portion portions[2 * (TEXTGROUPWIDGET_LEN(64 + (64 / 2)) / TEXTGROUPWIDGET_HTTP_LEN7)];
2113 struct Portion bit;
2114 int width, height;
2115 int partWidths[Array_Elems(portions)(sizeof(portions) / sizeof(portions[0]))];
2116 struct Bitmap bmp;
2117 int portionsCount;
2118 int i, x, ul;
2119
2120 width = 0;
2121 height = Drawer2D_TextHeight(args);
2122 portionsCount = TextGroupWidget_Reduce(w, chars, index, portions);
27
Passing the value 0 via 3rd parameter 'target'
28
Calling 'TextGroupWidget_Reduce'
2123
2124 for (i = 0; i < portionsCount; i++) {
2125 bit = portions[i];
2126 args->text = String_UNSAFE_Substring(text, bit.LineBeg, bit.LineLen);
2127
2128 partWidths[i] = Drawer2D_TextWidth(args);
2129 width += partWidths[i];
2130 }
2131
2132 Bitmap_AllocateClearedPow2(&bmp, width, height);
2133 {
2134 x = 0;
2135 for (i = 0; i < portionsCount; i++) {
2136 bit = portions[i];
2137 ul = (bit.Len & TEXTGROUPWIDGET_URL0x8000);
2138 args->text = String_UNSAFE_Substring(text, bit.LineBeg, bit.LineLen);
2139
2140 if (ul) args->font->flags |= FONT_FLAGS_UNDERLINE;
2141 Drawer2D_DrawText(&bmp, args, x, 0);
2142 if (ul) args->font->flags &= ~FONT_FLAGS_UNDERLINE;
2143
2144 x += partWidths[i];
2145 }
2146 Drawer2D_MakeTexture(tex, &bmp, width, height);
2147 }
2148 Mem_Free(bmp.scan0);
2149}
2150
2151void TextGroupWidget_RedrawAll(struct TextGroupWidget* w) {
2152 int i;
2153 for (i = 0; i < w->lines; i++) { TextGroupWidget_Redraw(w, i); }
2154}
2155
2156void TextGroupWidget_Redraw(struct TextGroupWidget* w, int index) {
2157 cc_string text;
2158 struct DrawTextArgs args;
2159 struct Texture tex = { 0 };
2160 Gfx_DeleteTexture(&w->textures[index].ID);
2161
2162 text = TextGroupWidget_UNSAFE_Get(w, index);
17
Calling 'TextGroupWidget_UNSAFE_Get'
19
Returning from 'TextGroupWidget_UNSAFE_Get'
2163 if (!Drawer2D_IsEmptyText(&text)) {
20
Assuming the condition is true
21
Taking true branch
2164 DrawTextArgs_Make(&args, &text, w->font, true1);
2165
2166 if (w->underlineUrls && TextGroupWidget_MightHaveUrls(w)) {
22
Assuming field 'underlineUrls' is not equal to 0
23
Assuming the condition is true
24
Taking true branch
2167 TextGroupWidget_DrawAdvanced(w, &tex, &args, index, &text);
25
Passing the value 0 via 4th parameter 'index'
26
Calling 'TextGroupWidget_DrawAdvanced'
2168 } else {
2169 Drawer2D_MakeTextTexture(&tex, &args);
2170 }
2171 Drawer2D_ReducePadding_Tex(&tex, w->font->size, 3);
2172 } else {
2173 tex.Height = w->collapsible[index] ? 0 : w->defaultHeight;
2174 }
2175
2176 tex.X = Gui_CalcPos(w->horAnchor, w->xOffset, tex.Width, WindowInfo.Width);
2177 w->textures[index] = tex;
2178 Widget_Layout(w)(w)->VTABLE->Reposition(w);
2179}
2180
2181void TextGroupWidget_RedrawAllWithCol(struct TextGroupWidget* group, char col) {
2182 cc_string line;
2183 int i, j;
2184
2185 for (i = 0; i < group->lines; i++) {
1
The value 0 is assigned to 'i'
2
Assuming 'i' is < field 'lines'
3
Loop condition is true. Entering loop body
2186 line = TextGroupWidget_UNSAFE_Get(group, i);
4
Calling 'TextGroupWidget_UNSAFE_Get'
7
Returning from 'TextGroupWidget_UNSAFE_Get'
2187 if (!line.length) continue;
8
Assuming field 'length' is not equal to 0
9
Taking false branch
2188
2189 for (j = 0; j < line.length - 1; j++) {
10
Assuming the condition is true
11
Loop condition is true. Entering loop body
2190 if (line.buffer[j] == '&' && line.buffer[j + 1] == col) {
12
Assuming the condition is true
13
Assuming the condition is true
14
Taking true branch
2191 TextGroupWidget_Redraw(group, i);
15
Passing the value 0 via 2nd parameter 'index'
16
Calling 'TextGroupWidget_Redraw'
2192 break;
2193 }
2194 }
2195 }
2196}
2197
2198
2199void TextGroupWidget_SetFont(struct TextGroupWidget* w, struct FontDesc* font) {
2200 int i, height;
2201
2202 height = Drawer2D_FontHeight(font, true1);
2203 Drawer2D_ReducePadding_Height(&height, font->size, 3);
2204 w->defaultHeight = height;
2205
2206 for (i = 0; i < w->lines; i++) {
2207 w->textures[i].Height = w->collapsible[i] ? 0 : height;
2208 }
2209 w->font = font;
2210 Widget_Layout(w)(w)->VTABLE->Reposition(w);
2211}
2212
2213static void TextGroupWidget_Render(void* widget, double delta) {
2214 struct TextGroupWidget* w = (struct TextGroupWidget*)widget;
2215 struct Texture* textures = w->textures;
2216 int i;
2217
2218 for (i = 0; i < w->lines; i++) {
2219 if (!textures[i].ID) continue;
2220 Texture_Render(&textures[i]);
2221 }
2222}
2223
2224static void TextGroupWidget_Free(void* widget) {
2225 struct TextGroupWidget* w = (struct TextGroupWidget*)widget;
2226 int i;
2227
2228 for (i = 0; i < w->lines; i++) {
2229 Gfx_DeleteTexture(&w->textures[i].ID);
2230 }
2231}
2232
2233static const struct WidgetVTABLE TextGroupWidget_VTABLE = {
2234 TextGroupWidget_Render, TextGroupWidget_Free, TextGroupWidget_Reposition,
2235 Widget_InputDown, Widget_InputUp, Widget_MouseScroll,
2236 Widget_Pointer, Widget_PointerUp, Widget_PointerMove
2237};
2238void TextGroupWidget_Create(struct TextGroupWidget* w, int lines, struct Texture* textures, TextGroupWidget_Get getLine) {
2239 Widget_Reset(w);
2240 w->VTABLE = &TextGroupWidget_VTABLE;
2241 w->lines = lines;
2242 w->textures = textures;
2243 w->GetLine = getLine;
2244}
2245
2246
2247/*########################################################################################################################*
2248*---------------------------------------------------SpecialInputWidget----------------------------------------------------*
2249*#########################################################################################################################*/
2250static void SpecialInputWidget_UpdateColString(struct SpecialInputWidget* w) {
2251 int i;
2252 String_InitArray(w->colString, w->_colBuffer)w->colString.buffer = w->_colBuffer; w->colString.length
= 0; w->colString.capacity = sizeof(w->_colBuffer);
;
2253
2254 for (i = 0; i < DRAWER2D_MAX_COLS256; i++) {
2255 if (i >= 'A' && i <= 'F') continue;
2256 if (!BitmapCol_A(Drawer2D_Cols[i])((cc_uint8)(Drawer2D_Cols[i] >> 24))) continue;
2257
2258 String_Append(&w->colString, '&'); String_Append(&w->colString, (char)i);
2259 String_Append(&w->colString, '%'); String_Append(&w->colString, (char)i);
2260 }
2261}
2262
2263static cc_bool SpecialInputWidget_IntersectsTitle(struct SpecialInputWidget* w, int x, int y) {
2264 int i, width, titleX = 0;
2265
2266 for (i = 0; i < Array_Elems(w->tabs)(sizeof(w->tabs) / sizeof(w->tabs[0])); i++) {
2267 width = w->tabs[i].titleWidth;
2268 if (Gui_Contains(titleX, 0, width, w->titleHeight, x, y)) {
2269 w->selectedIndex = i;
2270 return true1;
2271 }
2272 titleX += width;
2273 }
2274 return false0;
2275}
2276
2277static void SpecialInputWidget_IntersectsBody(struct SpecialInputWidget* w, int x, int y) {
2278 struct SpecialInputTab e = w->tabs[w->selectedIndex];
2279 cc_string str;
2280 int i;
2281
2282 y -= w->titleHeight;
2283 x /= w->elementWidth; y /= w->elementHeight;
2284
2285 i = (x + y * e.itemsPerRow) * e.charsPerItem;
2286 if (i >= e.contents.length) return;
2287
2288 /* TODO: need to insert characters that don't affect w->caretPos index, adjust w->caretPos colour */
2289 str = String_Init(&e.contents.buffer[i], e.charsPerItem, 0);
2290
2291 /* TODO: Not be so hacky */
2292 if (w->selectedIndex == 0) str.length = 2;
2293 InputWidget_AppendText(w->target, &str);
2294}
2295
2296static void SpecialInputTab_Init(struct SpecialInputTab* tab, STRING_REF cc_string* title, int itemsPerRow, int charsPerItem, STRING_REF cc_string* contents) {
2297 tab->title = *title;
2298 tab->titleWidth = 0;
2299 tab->contents = *contents;
2300 tab->itemsPerRow = itemsPerRow;
2301 tab->charsPerItem = charsPerItem;
2302}
2303
2304static void SpecialInputWidget_InitTabs(struct SpecialInputWidget* w) {
2305 static cc_string title_cols = String_FromConst("Colours"){ "Colours", (sizeof("Colours") - 1), (sizeof("Colours") - 1)
}
;
2306 static cc_string title_math = String_FromConst("Math"){ "Math", (sizeof("Math") - 1), (sizeof("Math") - 1)};
2307 static cc_string tab_math = String_FromConst("\x9F\xAB\xAC\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xFB\xFC\xFD"){ "\x9F\xAB\xAC\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xFB\xFC\xFD"
, (sizeof("\x9F\xAB\xAC\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xFB\xFC\xFD"
) - 1), (sizeof("\x9F\xAB\xAC\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xFB\xFC\xFD"
) - 1)}
;
2308 static cc_string title_line = String_FromConst("Line/Box"){ "Line/Box", (sizeof("Line/Box") - 1), (sizeof("Line/Box") -
1)}
;
2309 static cc_string tab_line = String_FromConst("\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFE"){ "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFE"
, (sizeof("\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFE"
) - 1), (sizeof("\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFE"
) - 1)}
;
2310 static cc_string title_letters = String_FromConst("Letters"){ "Letters", (sizeof("Letters") - 1), (sizeof("Letters") - 1)
}
;
2311 static cc_string tab_letters = String_FromConst("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\xA0\xA1\xA2\xA3\xA4\xA5"){ "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\xA0\xA1\xA2\xA3\xA4\xA5"
, (sizeof("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\xA0\xA1\xA2\xA3\xA4\xA5"
) - 1), (sizeof("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\xA0\xA1\xA2\xA3\xA4\xA5"
) - 1)}
;
2312 static cc_string title_other = String_FromConst("Other"){ "Other", (sizeof("Other") - 1), (sizeof("Other") - 1)};
2313 static cc_string tab_other = String_FromConst("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F\x9B\x9C\x9D\x9E\xA6\xA7\xA8\xA9\xAA\xAD\xAE\xAF\xF9\xFA"){ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F\x9B\x9C\x9D\x9E\xA6\xA7\xA8\xA9\xAA\xAD\xAE\xAF\xF9\xFA"
, (sizeof("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F\x9B\x9C\x9D\x9E\xA6\xA7\xA8\xA9\xAA\xAD\xAE\xAF\xF9\xFA"
) - 1), (sizeof("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F\x9B\x9C\x9D\x9E\xA6\xA7\xA8\xA9\xAA\xAD\xAE\xAF\xF9\xFA"
) - 1)}
;
2314
2315 SpecialInputWidget_UpdateColString(w);
2316 SpecialInputTab_Init(&w->tabs[0], &title_cols, 10, 4, &w->colString);
2317 SpecialInputTab_Init(&w->tabs[1], &title_math, 16, 1, &tab_math);
2318 SpecialInputTab_Init(&w->tabs[2], &title_line, 17, 1, &tab_line);
2319 SpecialInputTab_Init(&w->tabs[3], &title_letters, 17, 1, &tab_letters);
2320 SpecialInputTab_Init(&w->tabs[4], &title_other, 16, 1, &tab_other);
2321}
2322
2323#define SPECIAL_TITLE_SPACING10 10
2324#define SPECIAL_CONTENT_SPACING5 5
2325static int SpecialInputWidget_MeasureTitles(struct SpecialInputWidget* w) {
2326 struct DrawTextArgs args;
2327 int i, width = 0;
2328
2329 DrawTextArgs_MakeEmpty(&args, w->font, false0);
2330 for (i = 0; i < Array_Elems(w->tabs)(sizeof(w->tabs) / sizeof(w->tabs[0])); i++) {
2331 args.text = w->tabs[i].title;
2332
2333 w->tabs[i].titleWidth = Drawer2D_TextWidth(&args) + SPECIAL_TITLE_SPACING10;
2334 width += w->tabs[i].titleWidth;
2335 }
2336
2337 w->titleHeight = Drawer2D_TextHeight(&args);
2338 return width;
2339}
2340
2341static void SpecialInputWidget_DrawTitles(struct SpecialInputWidget* w, struct Bitmap* bmp) {
2342 BitmapCol col_selected = BitmapCol_Make(30, 30, 30, 200)(((cc_uint8)(30) << 16) | ((cc_uint8)(30) << 8) |
((cc_uint8)(30) << 0) | ((cc_uint8)(200) << 24))
;
2343 BitmapCol col_inactive = BitmapCol_Make( 0, 0, 0, 127)(((cc_uint8)(0) << 16) | ((cc_uint8)(0) << 8) | (
(cc_uint8)(0) << 0) | ((cc_uint8)(127) << 24))
;
2344 BitmapCol col;
2345 struct DrawTextArgs args;
2346 int i, width, x = 0;
2347
2348 DrawTextArgs_MakeEmpty(&args, w->font, false0);
2349 for (i = 0; i < Array_Elems(w->tabs)(sizeof(w->tabs) / sizeof(w->tabs[0])); i++) {
2350 args.text = w->tabs[i].title;
2351 col = i == w->selectedIndex ? col_selected : col_inactive;
2352 width = w->tabs[i].titleWidth;
2353
2354 Drawer2D_Clear(bmp, col, x, 0, width, w->titleHeight);
2355 Drawer2D_DrawText(bmp, &args, x + SPECIAL_TITLE_SPACING10 / 2, 0);
2356 x += width;
2357 }
2358}
2359
2360static int SpecialInputWidget_MeasureContent(struct SpecialInputWidget* w, struct SpecialInputTab* tab) {
2361 struct DrawTextArgs args;
2362 int textWidth, textHeight;
2363 int i, maxWidth = 0;
2364
2365 DrawTextArgs_MakeEmpty(&args, w->font, false0);
2366 args.text.length = tab->charsPerItem;
2367 textHeight = Drawer2D_TextHeight(&args);
2368
2369 for (i = 0; i < tab->contents.length; i += tab->charsPerItem) {
2370 args.text.buffer = &tab->contents.buffer[i];
2371 textWidth = Drawer2D_TextWidth(&args);
2372 maxWidth = max(maxWidth, textWidth)((maxWidth) > (textWidth) ? (maxWidth) : (textWidth));
2373 }
2374
2375 w->elementWidth = maxWidth + SPECIAL_CONTENT_SPACING5;
2376 w->elementHeight = textHeight + SPECIAL_CONTENT_SPACING5;
2377 return w->elementWidth * tab->itemsPerRow;
2378}
2379
2380static int SpecialInputWidget_ContentHeight(struct SpecialInputWidget* w, struct SpecialInputTab* tab) {
2381 int rows = Math_CeilDiv(tab->contents.length / tab->charsPerItem, tab->itemsPerRow);
2382 return w->elementHeight * rows;
2383}
2384
2385static void SpecialInputWidget_DrawContent(struct SpecialInputWidget* w, struct SpecialInputTab* tab, struct Bitmap* bmp, int yOffset) {
2386 struct DrawTextArgs args;
2387 int i, x, y, item;
2388
2389 int wrap = tab->itemsPerRow;
2390 DrawTextArgs_MakeEmpty(&args, w->font, false0);
2391 args.text.length = tab->charsPerItem;
2392
2393 for (i = 0; i < tab->contents.length; i += tab->charsPerItem) {
2394 args.text.buffer = &tab->contents.buffer[i];
2395 item = i / tab->charsPerItem;
2396
2397 x = (item % wrap) * w->elementWidth;
2398 y = (item / wrap) * w->elementHeight + yOffset;
2399 Drawer2D_DrawText(bmp, &args, x, y);
2400 }
2401}
2402
2403static void SpecialInputWidget_Make(struct SpecialInputWidget* w, struct SpecialInputTab* tab) {
2404 BitmapCol col = BitmapCol_Make(30, 30, 30, 200)(((cc_uint8)(30) << 16) | ((cc_uint8)(30) << 8) |
((cc_uint8)(30) << 0) | ((cc_uint8)(200) << 24))
;
2405 int titlesWidth, titlesHeight;
2406 int contentWidth, contentHeight;
2407 int width, height;
2408 struct Bitmap bmp;
2409
2410 titlesWidth = SpecialInputWidget_MeasureTitles(w);
2411 titlesHeight = w->titleHeight;
2412 contentWidth = SpecialInputWidget_MeasureContent(w, tab);
2413 contentHeight = SpecialInputWidget_ContentHeight(w, tab);
2414
2415 width = max(titlesWidth, contentWidth)((titlesWidth) > (contentWidth) ? (titlesWidth) : (contentWidth
))
;
2416 height = titlesHeight + contentHeight;
2417 Gfx_DeleteTexture(&w->tex.ID);
2418
2419 Bitmap_AllocateClearedPow2(&bmp, width, height);
2420 {
2421 SpecialInputWidget_DrawTitles(w, &bmp);
2422 Drawer2D_Clear(&bmp, col, 0, titlesHeight, width, contentHeight);
2423 SpecialInputWidget_DrawContent(w, tab, &bmp, titlesHeight);
2424 }
2425 Drawer2D_MakeTexture(&w->tex, &bmp, width, height);
2426 Mem_Free(bmp.scan0);
2427}
2428
2429void SpecialInputWidget_Redraw(struct SpecialInputWidget* w) {
2430 SpecialInputWidget_Make(w, &w->tabs[w->selectedIndex]);
2431 w->pendingRedraw = false0;
2432 Widget_Layout(w)(w)->VTABLE->Reposition(w);
2433}
2434
2435static void SpecialInputWidget_Render(void* widget, double delta) {
2436 struct SpecialInputWidget* w = (struct SpecialInputWidget*)widget;
2437 Texture_Render(&w->tex);
2438}
2439
2440static void SpecialInputWidget_Free(void* widget) {
2441 struct SpecialInputWidget* w = (struct SpecialInputWidget*)widget;
2442 Gfx_DeleteTexture(&w->tex.ID);
2443}
2444
2445static void SpecialInputWidget_Reposition(void* widget) {
2446 struct SpecialInputWidget* w = (struct SpecialInputWidget*)widget;
2447 w->width = w->tex.Width;
2448 w->height = w->active ? w->tex.Height : 0;
2449 Widget_CalcPosition(w);
2450 w->tex.X = w->x; w->tex.Y = w->y;
2451}
2452
2453static int SpecialInputWidget_PointerDown(void* widget, int id, int x, int y) {
2454 struct SpecialInputWidget* w = (struct SpecialInputWidget*)widget;
2455 x -= w->x; y -= w->y;
2456
2457 if (SpecialInputWidget_IntersectsTitle(w, x, y)) {
2458 SpecialInputWidget_Redraw(w);
2459 } else {
2460 SpecialInputWidget_IntersectsBody(w, x, y);
2461 }
2462 return TOUCH_TYPE_GUI1;
2463}
2464
2465void SpecialInputWidget_UpdateCols(struct SpecialInputWidget* w) {
2466 SpecialInputWidget_UpdateColString(w);
2467 w->tabs[0].contents = w->colString;
2468 if (w->selectedIndex != 0) return;
2469
2470 /* defer updating colours tab until visible */
2471 if (!w->active) { w->pendingRedraw = true1; return; }
2472 SpecialInputWidget_Redraw(w);
2473}
2474
2475void SpecialInputWidget_SetActive(struct SpecialInputWidget* w, cc_bool active) {
2476 w->active = active;
2477 if (active && w->pendingRedraw) SpecialInputWidget_Redraw(w);
2478 Widget_Layout(w)(w)->VTABLE->Reposition(w);
2479}
2480
2481static const struct WidgetVTABLE SpecialInputWidget_VTABLE = {
2482 SpecialInputWidget_Render, SpecialInputWidget_Free, SpecialInputWidget_Reposition,
2483 Widget_InputDown, Widget_InputUp, Widget_MouseScroll,
2484 SpecialInputWidget_PointerDown, Widget_PointerUp, Widget_PointerMove
2485};
2486void SpecialInputWidget_Create(struct SpecialInputWidget* w, struct FontDesc* font, struct InputWidget* target) {
2487 Widget_Reset(w);
2488 w->VTABLE = &SpecialInputWidget_VTABLE;
2489 w->verAnchor = ANCHOR_MAX;
2490 w->font = font;
2491 w->target = target;
2492 SpecialInputWidget_InitTabs(w);
2493}
2494
2495
2496/*########################################################################################################################*
2497*----------------------------------------------------ThumbstickWidget-----------------------------------------------------*
2498*#########################################################################################################################*/
2499#ifdef CC_BUILD_TOUCH
2500#define DIR_YMAX (1 << 0)
2501#define DIR_YMIN (1 << 1)
2502#define DIR_XMAX (1 << 2)
2503#define DIR_XMIN (1 << 3)
2504
2505static void ThumbstickWidget_Rotate(void* widget, struct VertexTextured** vertices, int offset) {
2506 struct ThumbstickWidget* w = (struct ThumbstickWidget*)widget;
2507 struct VertexTextured* ptr;
2508 int i, x, y;
2509
2510 ptr = *vertices - 4;
2511 for (i = 0; i < 4; i++) {
2512 int x = ptr[i].X - w->x;
2513 int y = ptr[i].Y - w->y;
2514 ptr[i].X = -y + w->x + offset;
2515 ptr[i].Y = x + w->y;
2516 }
2517}
2518
2519static void ThumbstickWidget_BuildGroup(void* widget, struct Texture* tex, struct VertexTextured** vertices) {
2520 struct ThumbstickWidget* w = (struct ThumbstickWidget*)widget;
2521 float tmp;
2522 tex->Y = w->y + w->height / 2;
2523 Gfx_Make2DQuad(tex, PACKEDCOL_WHITE(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 24
))
, vertices);
2524
2525 tex->Y = w->y;
2526 tmp = tex->uv.V1; tex->uv.V1 = tex->uv.V2; tex->uv.V2 = tmp;
2527 Gfx_Make2DQuad(tex, PACKEDCOL_WHITE(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 24
))
, vertices);
2528
2529 Gfx_Make2DQuad(tex, PACKEDCOL_WHITE(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 24
))
, vertices);
2530 ThumbstickWidget_Rotate(widget, vertices, w->width);
2531
2532 tmp = tex->uv.V1; tex->uv.V1 = tex->uv.V2; tex->uv.V2 = tmp;
2533 Gfx_Make2DQuad(tex, PACKEDCOL_WHITE(((cc_uint8)(255) << 0) | ((cc_uint8)(255) << 8) |
((cc_uint8)(255) << 16) | ((cc_uint8)(255) << 24
))
, vertices);
2534 ThumbstickWidget_Rotate(widget, vertices, w->width / 2);
2535}
2536
2537static void ThumbstickWidget_BuildMesh(void* widget, struct VertexTextured** vertices) {
2538 struct ThumbstickWidget* w = (struct ThumbstickWidget*)widget;
2539 struct Texture tex;
2540
2541 tex.X = w->x;
2542 tex.Width = w->width; tex.Height = w->height / 2;
2543 tex.uv.U1 = 0.0f; tex.uv.U2 = 1.0f;
2544
2545 tex.uv.V1 = 0.0f; tex.uv.V2 = 0.5f;
2546 ThumbstickWidget_BuildGroup(widget, &tex, vertices);
2547 tex.uv.V1 = 0.5f; tex.uv.V2 = 1.0f;
2548 ThumbstickWidget_BuildGroup(widget, &tex, vertices);
2549}
2550
2551static int ThumbstickWidget_CalcDirs(struct ThumbstickWidget* w) {
2552 int i, dx, dy, dirs = 0;
2553 double angle;
2554
2555 for (i = 0; i < INPUT_MAX_POINTERS1; i++) {
2556 if (!(w->active & (1 << i))) continue;
2557
2558 dx = Pointers[i].x - (w->x + w->width / 2);
2559 dy = Pointers[i].y - (w->y + w->height / 2);
2560 angle = Math_Atan2(dx, dy) * MATH_RAD2DEG(180.0f / 3.1415926535897931f);
2561
2562 /* 4 quadrants diagonally, but slightly expanded for overlap*/
2563 if (angle >= 30 && angle <= 150) dirs |= DIR_YMAX;
2564 if (angle >= -60 && angle <= 60) dirs |= DIR_XMAX;
2565 if (angle >= -150 && angle <= -30) dirs |= DIR_YMIN;
2566 if (angle < -120 || angle > 120) dirs |= DIR_XMIN;
2567 }
2568 return dirs;
2569}
2570
2571static int ThumbstickWidget_Render2(void* widget, int offset) {
2572 struct ThumbstickWidget* w = (struct ThumbstickWidget*)widget;
2573 int i, base, flags = ThumbstickWidget_CalcDirs(w);
2574
2575 if (Gui.TouchTex) {
2576 Gfx_BindTexture(Gui.TouchTex);
2577 for (i = 0; i < 4; i++) {
2578 base = (flags & (1 << i)) ? 0 : THUMBSTICKWIDGET_PER;
2579 Gfx_DrawVb_IndexedTris_Range(4, offset + base + (i * 4));
2580 }
2581 }
2582 return offset + THUMBSTICKWIDGET_MAX;
2583}
2584
2585static void ThumbstickWidget_Reposition(void* widget) {
2586 struct ThumbstickWidget* w = (struct ThumbstickWidget*)widget;
2587 w->width = Display_ScaleX(128 * w->scale);
2588 w->height = Display_ScaleY(128 * w->scale);
2589 Widget_CalcPosition(w);
2590}
2591
2592static const struct WidgetVTABLE ThumbstickWidget_VTABLE = {
2593 NULL((void*)0), Screen_NullFunc, ThumbstickWidget_Reposition,
2594 Widget_InputDown, Widget_InputUp, Widget_MouseScroll,
2595 Widget_Pointer, Widget_PointerUp, Widget_PointerMove,
2596 ThumbstickWidget_BuildMesh, ThumbstickWidget_Render2
2597};
2598void ThumbstickWidget_Init(struct ThumbstickWidget* w) {
2599 Widget_Reset(w);
2600 w->VTABLE = &ThumbstickWidget_VTABLE;
2601 w->scale = 1;
2602}
2603
2604void ThumbstickWidget_GetMovement(struct ThumbstickWidget* w, float* xMoving, float* zMoving) {
2605 int dirs = ThumbstickWidget_CalcDirs(w);
2606 if (dirs & DIR_XMIN) *xMoving -= 1;
2607 if (dirs & DIR_XMAX) *xMoving += 1;
2608 if (dirs & DIR_YMIN) *zMoving -= 1;
2609 if (dirs & DIR_YMAX) *zMoving += 1;
2610}
2611#endif

./Widgets.h

1#ifndef CC_WIDGETS_H
2#define CC_WIDGETS_H
3#include "Gui.h"
4#include "BlockID.h"
5#include "Constants.h"
6#include "Entity.h"
7/* Contains all 2D widget implementations.
8 Copyright 2014-2021 ClassiCube | Licensed under BSD-3
9*/
10struct FontDesc;
11
12/* A text label. */
13struct TextWidget {
14 Widget_Bodyconst struct WidgetVTABLE* VTABLE; int x, y, width, height; cc_bool
active; cc_bool disabled; cc_uint8 horAnchor, verAnchor; int
xOffset, yOffset; Widget_LeftClick MenuClick;
15 struct Texture tex;
16 PackedCol col;
17};
18#define TEXTWIDGET_MAX4 4
19
20/* Initialises a text widget. */
21CC_NOINLINE__attribute__((noinline)) void TextWidget_Init(struct TextWidget* w);
22/* Draws the given text into a texture, then updates the position and size of this widget. */
23CC_NOINLINE__attribute__((noinline)) void TextWidget_Set(struct TextWidget* w, const cc_string* text, struct FontDesc* font);
24/* Shorthand for TextWidget_Set using String_FromReadonly */
25CC_NOINLINE__attribute__((noinline)) void TextWidget_SetConst(struct TextWidget* w, const char* text, struct FontDesc* font);
26
27
28typedef void (*Button_Get)(cc_string* raw);
29typedef void (*Button_Set)(const cc_string* raw);
30/* A labelled button that can be clicked on. */
31struct ButtonWidget {
32 Widget_Bodyconst struct WidgetVTABLE* VTABLE; int x, y, width, height; cc_bool
active; cc_bool disabled; cc_uint8 horAnchor, verAnchor; int
xOffset, yOffset; Widget_LeftClick MenuClick;
33 struct Texture tex;
34 PackedCol col;
35 int minWidth, minHeight;
36 const char* optName;
37 Button_Get GetValue;
38 Button_Set SetValue;
39};
40#define BUTTONWIDGET_MAX12 12
41
42/* Initialises a button widget. */
43CC_NOINLINE__attribute__((noinline)) void ButtonWidget_Make(struct ButtonWidget* w, int minWidth, Widget_LeftClick onClick,
44 cc_uint8 horAnchor, cc_uint8 verAnchor, int xOffset, int yOffset);
45/* Initialises a button widget. */
46CC_NOINLINE__attribute__((noinline)) void ButtonWidget_Init(struct ButtonWidget* w, int minWidth, Widget_LeftClick onClick);
47/* Draws the given text into a texture, then updates the position and size of this widget. */
48CC_NOINLINE__attribute__((noinline)) void ButtonWidget_Set(struct ButtonWidget* w, const cc_string* text, struct FontDesc* font);
49/* Shorthand for ButtonWidget_Set using String_FromReadonly */
50CC_NOINLINE__attribute__((noinline)) void ButtonWidget_SetConst(struct ButtonWidget* w, const char* text, struct FontDesc* font);
51
52/* Clickable and draggable scrollbar. */
53struct ScrollbarWidget {
54 Widget_Bodyconst struct WidgetVTABLE* VTABLE; int x, y, width, height; cc_bool
active; cc_bool disabled; cc_uint8 horAnchor, verAnchor; int
xOffset, yOffset; Widget_LeftClick MenuClick;
55 int topRow, rowsTotal, rowsVisible;
56 float scrollingAcc;
57 int dragOffset;
58 int draggingId, padding;
59 int borderX, borderY;
60 int nubsWidth, offsets[3];
61};
62/* Resets state of the given scrollbar widget to default. */
63CC_NOINLINE__attribute__((noinline)) void ScrollbarWidget_Create(struct ScrollbarWidget* w);
64
65/* A row of blocks with a background. */
66struct HotbarWidget {
67 Widget_Bodyconst struct WidgetVTABLE* VTABLE; int x, y, width, height; cc_bool
active; cc_bool disabled; cc_uint8 horAnchor, verAnchor; int
xOffset, yOffset; Widget_LeftClick MenuClick;
68 struct Texture selTex, backTex;
69 float slotWidth, selWidth;
70 float slotXOffset, elemSize;
71 float scrollAcc, scale;
72 cc_bool altHandled;
73 struct Texture ellipsisTex;
74};
75/* Resets state of the given hotbar widget to default. */
76CC_NOINLINE__attribute__((noinline)) void HotbarWidget_Create(struct HotbarWidget* w);
77CC_NOINLINE__attribute__((noinline)) void HotbarWidget_SetFont(struct HotbarWidget* w, struct FontDesc* font);
78
79/* A table of blocks. */
80struct TableWidget {
81 Widget_Bodyconst struct WidgetVTABLE* VTABLE; int x, y, width, height; cc_bool
active; cc_bool disabled; cc_uint8 horAnchor, verAnchor; int
xOffset, yOffset; Widget_LeftClick MenuClick;
82 int blocksCount, blocksPerRow;
83 int rowsTotal, rowsVisible;
84 int lastCreatedIndex;
85 struct FontDesc* font;
86 int selectedIndex, cellSizeX, cellSizeY;
87 float selBlockExpand;
88 GfxResourceID vb;
89 cc_bool pendingClose;
90 float scale;
91
92 BlockID blocks[BLOCK_COUNT];
93 struct ScrollbarWidget scroll;
94 struct Texture descTex;
95 int lastX, lastY, paddingX;
96 int paddingTopY, paddingMaxY;
97};
98
99CC_NOINLINE__attribute__((noinline)) void TableWidget_Create(struct TableWidget* w);
100/* Sets the selected block in the table to the given block. */
101/* Also adjusts scrollbar and moves cursor to be over the given block. */
102CC_NOINLINE__attribute__((noinline)) void TableWidget_SetBlockTo(struct TableWidget* w, BlockID block);
103CC_NOINLINE__attribute__((noinline)) void TableWidget_RecreateBlocks(struct TableWidget* w);
104CC_NOINLINE__attribute__((noinline)) void TableWidget_OnInventoryChanged(struct TableWidget* w);
105CC_NOINLINE__attribute__((noinline)) void TableWidget_MakeDescTex(struct TableWidget* w, BlockID block);
106CC_NOINLINE__attribute__((noinline)) void TableWidget_Recreate(struct TableWidget* w);
107
108
109#define INPUTWIDGET_MAX_LINES3 3
110#define INPUTWIDGET_LEN64 STRING_SIZE64
111struct InputWidget {
112 Widget_Bodyconst struct WidgetVTABLE* VTABLE; int x, y, width, height; cc_bool
active; cc_bool disabled; cc_uint8 horAnchor, verAnchor; int
xOffset, yOffset; Widget_LeftClick MenuClick;
113 struct FontDesc* font;
114 int (*GetMaxLines)(void);
115 void (*RemakeTexture)(void* elem); /* Remakes the raw texture containing all the chat lines. Also updates dimensions. */
116 void (*OnPressedEnter)(void* elem); /* Invoked when the user presses enter. */
117 cc_bool (*AllowedChar)(void* elem, char c);
118 void (*OnTextChanged)(void* elem); /* Callback invoked whenever text changes. */
119
120 cc_string text; /* The actual raw text */
121 cc_string lines[INPUTWIDGET_MAX_LINES3]; /* text of each line after word wrapping */
122 int lineWidths[INPUTWIDGET_MAX_LINES3]; /* Width of each line in pixels */
123 int lineHeight; /* Height of a line in pixels */
124 struct Texture inputTex;
125 int prefixWidth;
126 cc_bool convertPercents;
127
128 cc_uint8 padding;
129 cc_bool showCaret;
130 int caretWidth;
131 int caretX, caretY; /* Coordinates of caret in lines */
132 int caretPos; /* Position of caret, -1 for at end of string */
133 int caretOffset;
134 PackedCol caretCol;
135 struct Texture caretTex;
136 double caretAccumulator;
137};
138
139/* Removes all characters and then deletes the input texture. */
140CC_NOINLINE__attribute__((noinline)) void InputWidget_Clear(struct InputWidget* w);
141/* Tries appending all characters from the given string, then update the input texture. */
142CC_NOINLINE__attribute__((noinline)) void InputWidget_AppendText(struct InputWidget* w, const cc_string* text);
143/* Tries appending the given character, then updates the input texture. */
144CC_NOINLINE__attribute__((noinline)) void InputWidget_Append(struct InputWidget* w, char c);
145/* Redraws text and recalculates associated state. */
146/* Also calls Window_SetKeyboardText with the text in the input widget. */
147/* This way native text input state stays synchronised with the input widget. */
148/* (e.g. may only accept numerical input, so 'c' gets stripped from str) */
149CC_NOINLINE__attribute__((noinline)) void InputWidget_UpdateText(struct InputWidget* w);
150/* Shorthand for InputWidget_Clear followed by InputWidget_AppendText, */
151/* then calls Window_SetKeyboardText with the text in the input widget. */
152/* This way native text input state stays synchronised with the input widget. */
153/* (e.g. may only accept numerical input, so 'c' gets stripped from str) */
154CC_NOINLINE__attribute__((noinline)) void InputWidget_SetText(struct InputWidget* w, const cc_string* str);
155
156
157struct MenuInputDesc;
158struct MenuInputVTABLE {
159 /* Returns a description of the range of valid values (e.g. "0 - 100") */
160 void (*GetRange)(struct MenuInputDesc* d, cc_string* range);
161 /* Whether the given character is acceptable for this input */
162 cc_bool (*IsValidChar)(struct MenuInputDesc* d, char c);
163 /* Whether the characters of the given string are acceptable for this input */
164 /* e.g. for an integer, '-' is only valid for the first character */
165 cc_bool (*IsValidString)(struct MenuInputDesc* d, const cc_string* s);
166 /* Whether the characters of the given string produce a valid value */
167 cc_bool (*IsValidValue)(struct MenuInputDesc* d, const cc_string* s);
168 /* Gets the default value for this input. */
169 void (*GetDefault)(struct MenuInputDesc* d, cc_string* value);
170};
171
172struct MenuInputDesc {
173 const struct MenuInputVTABLE* VTABLE;
174 union {
175 struct { const char* const* Names; int Count; } e;
176 struct { int Min, Max, Default; } i;
177 struct { float Min, Max, Default; } f;
178 struct { PackedCol Default; } h;
179 } meta;
180};
181
182extern const struct MenuInputVTABLE HexInput_VTABLE;
183extern const struct MenuInputVTABLE IntInput_VTABLE;
184extern const struct MenuInputVTABLE SeedInput_VTABLE;
185extern const struct MenuInputVTABLE FloatInput_VTABLE;
186extern const struct MenuInputVTABLE PathInput_VTABLE;
187extern const struct MenuInputVTABLE StringInput_VTABLE;
188
189#define MenuInput_Hex(v, def)v.VTABLE = &HexInput_VTABLE; v.meta.h.Default = def; v.VTABLE = &HexInput_VTABLE; v.meta.h.Default = def;
190#define MenuInput_Int(v, lo, hi, def)v.VTABLE = &IntInput_VTABLE; v.meta.i.Min = lo; v.meta.i.
Max = hi; v.meta.i.Default = def;
v.VTABLE = &IntInput_VTABLE; v.meta.i.Min = lo; v.meta.i.Max = hi; v.meta.i.Default = def;
191#define MenuInput_Seed(v)v.VTABLE = &SeedInput_VTABLE; v.meta.i.Min = ((cc_int32)-
2147483647L - (cc_int32)1L); v.meta.i.Max = ((cc_int32)2147483647L
);
v.VTABLE = &SeedInput_VTABLE; v.meta.i.Min = Int32_MinValue((cc_int32)-2147483647L - (cc_int32)1L); v.meta.i.Max = Int32_MaxValue((cc_int32)2147483647L);
192#define MenuInput_Float(v, lo, hi, def)v.VTABLE = &FloatInput_VTABLE; v.meta.f.Min = lo; v.meta.
f.Max = hi; v.meta.f.Default = def;
v.VTABLE = &FloatInput_VTABLE; v.meta.f.Min = lo; v.meta.f.Max = hi; v.meta.f.Default = def;
193#define MenuInput_Path(v)v.VTABLE = &PathInput_VTABLE; v.VTABLE = &PathInput_VTABLE;
194#define MenuInput_Enum(v, names, count)v.VTABLE = ((void*)0); v.meta.e.Names = names; v.meta.e.Count
= count;
v.VTABLE = NULL((void*)0); v.meta.e.Names = names; v.meta.e.Count = count;
195#define MenuInput_String(v)v.VTABLE = &StringInput_VTABLE; v.VTABLE = &StringInput_VTABLE;
196
197struct TextInputWidget {
198 struct InputWidget base;
199 int minWidth, minHeight;
200 struct MenuInputDesc desc;
201 char _textBuffer[INPUTWIDGET_LEN64];
202};
203#define MENUINPUTWIDGET_MAX8 8
204
205CC_NOINLINE__attribute__((noinline)) void TextInputWidget_Create(struct TextInputWidget* w, int width, const cc_string* text, struct MenuInputDesc* d);
206/* Sets the font used, then redraws the input widget. */
207CC_NOINLINE__attribute__((noinline)) void TextInputWidget_SetFont(struct TextInputWidget* w, struct FontDesc* font);
208
209
210struct ChatInputWidget {
211 struct InputWidget base;
212 int typingLogPos;
213 cc_string origStr;
214 char _textBuffer[INPUTWIDGET_MAX_LINES3 * INPUTWIDGET_LEN64];
215 char _origBuffer[INPUTWIDGET_MAX_LINES3 * INPUTWIDGET_LEN64];
216};
217
218CC_NOINLINE__attribute__((noinline)) void ChatInputWidget_Create(struct ChatInputWidget* w);
219CC_NOINLINE__attribute__((noinline)) void ChatInputWidget_SetFont(struct ChatInputWidget* w, struct FontDesc* font);
220
221
222/* Retrieves the text for the i'th line in the group */
223typedef cc_string (*TextGroupWidget_Get)(int i);
224#define TEXTGROUPWIDGET_MAX_LINES30 30
225#define TEXTGROUPWIDGET_LEN(64 + (64 / 2)) (STRING_SIZE64 + (STRING_SIZE64 / 2))
226
227/* A group of text labels. */
228struct TextGroupWidget {
229 Widget_Bodyconst struct WidgetVTABLE* VTABLE; int x, y, width, height; cc_bool
active; cc_bool disabled; cc_uint8 horAnchor, verAnchor; int
xOffset, yOffset; Widget_LeftClick MenuClick;
230 int lines, defaultHeight;
231 struct FontDesc* font;
232 /* Whether a line has zero height when that line has no text in it. */
233 cc_bool collapsible[TEXTGROUPWIDGET_MAX_LINES30];
234 cc_bool underlineUrls;
235 struct Texture* textures;
236 TextGroupWidget_Get GetLine;
237};
238
239CC_NOINLINE__attribute__((noinline)) void TextGroupWidget_Create(struct TextGroupWidget* w, int lines, struct Texture* textures, TextGroupWidget_Get getLine);
240CC_NOINLINE__attribute__((noinline)) void TextGroupWidget_SetFont(struct TextGroupWidget* w, struct FontDesc* font);
241/* Deletes first line, then moves all other lines upwards, then redraws last line. */
242/* NOTE: GetLine must also adjust the lines it returns for this to behave properly. */
243CC_NOINLINE__attribute__((noinline)) void TextGroupWidget_ShiftUp(struct TextGroupWidget* w);
244/* Deletes last line, then moves all other lines downwards, then redraws first line. */
245/* NOTE: GetLine must also adjust the lines it returns for this to behave properly. */
246CC_NOINLINE__attribute__((noinline)) void TextGroupWidget_ShiftDown(struct TextGroupWidget* w);
247/* Returns height of lines, except for the first 0 or more empty lines. */
248CC_NOINLINE__attribute__((noinline)) int TextGroupWidget_UsedHeight(struct TextGroupWidget* w);
249/* Returns either the URL or the line underneath the given coordinates. */
250CC_NOINLINE__attribute__((noinline)) int TextGroupWidget_GetSelected(struct TextGroupWidget* w, cc_string* text, int mouseX, int mouseY);
251/* Redraws the given line, updating the texture and Y position of other lines. */
252CC_NOINLINE__attribute__((noinline)) void TextGroupWidget_Redraw(struct TextGroupWidget* w, int index);
253/* Calls TextGroupWidget_Redraw for all lines */
254CC_NOINLINE__attribute__((noinline)) void TextGroupWidget_RedrawAll(struct TextGroupWidget* w);
255/* Calls TextGroupWidget_Redraw for all lines which have the given colour code. */
256/* Typically only called in response to the ChatEvents.ColCodeChanged event. */
257CC_NOINLINE__attribute__((noinline)) void TextGroupWidget_RedrawAllWithCol(struct TextGroupWidget* w, char col);
258/* Gets the text for the i'th line. */
259static cc_string TextGroupWidget_UNSAFE_Get(struct TextGroupWidget* w, int i) { return w->GetLine(i); }
5
Returning without writing to 'w->underlineUrls', which participates in a condition later
6
Returning value, which participates in a condition later
18
Returning without writing to 'w->underlineUrls', which participates in a condition later
260
261
262typedef void (*SpecialInputAppendFunc)(void* userData, char c);
263struct SpecialInputTab {
264 int itemsPerRow, charsPerItem, titleWidth;
265 cc_string title, contents;
266};
267
268struct SpecialInputWidget {
269 Widget_Bodyconst struct WidgetVTABLE* VTABLE; int x, y, width, height; cc_bool
active; cc_bool disabled; cc_uint8 horAnchor, verAnchor; int
xOffset, yOffset; Widget_LeftClick MenuClick;
270 int elementWidth, elementHeight;
271 int selectedIndex;
272 cc_bool pendingRedraw;
273 struct InputWidget* target;
274 struct Texture tex;
275 struct FontDesc* font;
276 int titleHeight;
277 struct SpecialInputTab tabs[5];
278 cc_string colString;
279 char _colBuffer[DRAWER2D_MAX_COLS256 * 4];
280};
281
282CC_NOINLINE__attribute__((noinline)) void SpecialInputWidget_Create(struct SpecialInputWidget* w, struct FontDesc* font, struct InputWidget* target);
283CC_NOINLINE__attribute__((noinline)) void SpecialInputWidget_Redraw(struct SpecialInputWidget* w);
284CC_NOINLINE__attribute__((noinline)) void SpecialInputWidget_UpdateCols(struct SpecialInputWidget* w);
285CC_NOINLINE__attribute__((noinline)) void SpecialInputWidget_SetActive(struct SpecialInputWidget* w, cc_bool active);
286
287#ifdef CC_BUILD_TOUCH
288struct ThumbstickWidget { Widget_Bodyconst struct WidgetVTABLE* VTABLE; int x, y, width, height; cc_bool
active; cc_bool disabled; cc_uint8 horAnchor, verAnchor; int
xOffset, yOffset; Widget_LeftClick MenuClick;
; float scale; };
289#define THUMBSTICKWIDGET_PER (4 * 4)
290#define THUMBSTICKWIDGET_MAX (THUMBSTICKWIDGET_PER * 2)
291
292void ThumbstickWidget_Init(struct ThumbstickWidget* w);
293void ThumbstickWidget_GetMovement(struct ThumbstickWidget* w, float* xMoving, float* zMoving);
294#endif
295#endif