Bug Summary

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