1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9
10 #include "Widget.h"
11
12 #include "Window.h"
13
14 #include <algorithm>
15 #include <cmath>
16 #include <openrct2/Context.h>
17 #include <openrct2/Input.h>
18 #include <openrct2/drawing/Drawing.h>
19 #include <openrct2/localisation/Localisation.h>
20 #include <openrct2/sprites.h>
21 #include <openrct2/util/Util.h>
22
23 static void WidgetFrameDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
24 static void WidgetResizeDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
25 static void WidgetButtonDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
26 static void WidgetTabDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
27 static void WidgetFlatButtonDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
28 static void WidgetTextButton(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
29 static void WidgetTextCentred(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
30 static void WidgetText(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
31 static void WidgetTextInset(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
32 static void WidgetTextBoxDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
33 static void WidgetGroupboxDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
34 static void WidgetCaptionDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
35 static void WidgetCheckboxDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
36 static void WidgetCloseboxDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
37 static void WidgetScrollDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
38 static void WidgetHScrollbarDraw(
39 rct_drawpixelinfo* dpi, const rct_scroll& scroll, int32_t l, int32_t t, int32_t r, int32_t b, int32_t colour);
40 static void WidgetVScrollbarDraw(
41 rct_drawpixelinfo* dpi, const rct_scroll& scroll, int32_t l, int32_t t, int32_t r, int32_t b, int32_t colour);
42 static void WidgetDrawImage(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex);
43
44 /**
45 *
46 * rct2: 0x006EB2A8
47 */
WidgetDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)48 void WidgetDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
49 {
50 const auto& widget = w->widgets[widgetIndex];
51 switch (widget.type)
52 {
53 case WindowWidgetType::Frame:
54 WidgetFrameDraw(dpi, w, widgetIndex);
55 break;
56 case WindowWidgetType::Resize:
57 WidgetResizeDraw(dpi, w, widgetIndex);
58 break;
59 case WindowWidgetType::ImgBtn:
60 WidgetButtonDraw(dpi, w, widgetIndex);
61 break;
62 case WindowWidgetType::ColourBtn:
63 case WindowWidgetType::TrnBtn:
64 case WindowWidgetType::Tab:
65 WidgetTabDraw(dpi, w, widgetIndex);
66 break;
67 case WindowWidgetType::FlatBtn:
68 WidgetFlatButtonDraw(dpi, w, widgetIndex);
69 break;
70 case WindowWidgetType::Button:
71 case WindowWidgetType::TableHeader:
72 WidgetTextButton(dpi, w, widgetIndex);
73 break;
74 case WindowWidgetType::LabelCentred:
75 WidgetTextCentred(dpi, w, widgetIndex);
76 break;
77 case WindowWidgetType::Label:
78 WidgetText(dpi, w, widgetIndex);
79 break;
80 case WindowWidgetType::Spinner:
81 case WindowWidgetType::DropdownMenu:
82 case WindowWidgetType::Viewport:
83 WidgetTextInset(dpi, w, widgetIndex);
84 break;
85 case WindowWidgetType::Groupbox:
86 WidgetGroupboxDraw(dpi, w, widgetIndex);
87 break;
88 case WindowWidgetType::Caption:
89 WidgetCaptionDraw(dpi, w, widgetIndex);
90 break;
91 case WindowWidgetType::CloseBox:
92 WidgetCloseboxDraw(dpi, w, widgetIndex);
93 break;
94 case WindowWidgetType::Scroll:
95 WidgetScrollDraw(dpi, w, widgetIndex);
96 break;
97 case WindowWidgetType::Checkbox:
98 WidgetCheckboxDraw(dpi, w, widgetIndex);
99 break;
100 case WindowWidgetType::TextBox:
101 WidgetTextBoxDraw(dpi, w, widgetIndex);
102 break;
103 default:
104 break;
105 }
106 }
107
108 /**
109 *
110 * rct2: 0x006EB6CE
111 */
WidgetFrameDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)112 static void WidgetFrameDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
113 {
114 // Get the widget
115 const auto& widget = w->widgets[widgetIndex];
116
117 // Resolve the absolute ltrb
118 auto leftTop = w->windowPos + ScreenCoordsXY{ widget.left, widget.top };
119 int32_t r = w->windowPos.x + widget.right;
120 int32_t b = w->windowPos.y + widget.bottom;
121
122 //
123 uint8_t press = ((w->flags & WF_10) ? INSET_RECT_FLAG_FILL_MID_LIGHT : 0);
124
125 // Get the colour
126 uint8_t colour = w->colours[widget.colour];
127
128 // Draw the frame
129 gfx_fill_rect_inset(dpi, { leftTop, { r, b } }, colour, press);
130
131 // Check if the window can be resized
132 if (!(w->flags & WF_RESIZABLE))
133 return;
134 if (w->min_width == w->max_width && w->min_height == w->max_height)
135 return;
136
137 // Draw the resize sprite at the bottom right corner
138 leftTop = w->windowPos + ScreenCoordsXY{ widget.right - 18, widget.bottom - 18 };
139 gfx_draw_sprite(dpi, ImageId(SPR_RESIZE, colour & 0x7F), leftTop);
140 }
141
142 /**
143 *
144 * rct2: 0x006EB765
145 */
WidgetResizeDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)146 static void WidgetResizeDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
147 {
148 // Get the widget
149 const auto& widget = w->widgets[widgetIndex];
150
151 // Resolve the absolute ltrb
152 auto leftTop = w->windowPos + ScreenCoordsXY{ widget.left, widget.top };
153 int32_t r = w->windowPos.x + widget.right;
154 int32_t b = w->windowPos.y + widget.bottom;
155
156 // Get the colour
157 uint8_t colour = w->colours[widget.colour];
158
159 // Draw the panel
160 gfx_fill_rect_inset(dpi, { leftTop, { r, b } }, colour, 0);
161
162 // Check if the window can be resized
163 if (!(w->flags & WF_RESIZABLE))
164 return;
165 if (w->min_width == w->max_width && w->min_height == w->max_height)
166 return;
167
168 // Draw the resize sprite at the bottom right corner
169 leftTop = w->windowPos + ScreenCoordsXY{ widget.right - 18, widget.bottom - 18 };
170 gfx_draw_sprite(dpi, ImageId(SPR_RESIZE, colour & 0x7F), leftTop);
171 }
172
173 /**
174 *
175 * rct2: 0x006EB8E5
176 */
WidgetButtonDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)177 static void WidgetButtonDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
178 {
179 // Get the widget
180 const auto& widget = w->widgets[widgetIndex];
181
182 // Resolve the absolute ltrb
183 ScreenRect rect{ w->windowPos + ScreenCoordsXY{ widget.left, widget.top },
184 w->windowPos + ScreenCoordsXY{ widget.right, widget.bottom } };
185
186 // Check if the button is pressed down
187 uint8_t press = WidgetIsPressed(w, widgetIndex) || WidgetIsActiveTool(w, widgetIndex) ? INSET_RECT_FLAG_BORDER_INSET : 0;
188
189 // Get the colour
190 uint8_t colour = w->colours[widget.colour];
191
192 if (static_cast<int32_t>(widget.image) == -2)
193 {
194 // Draw border with no fill
195 gfx_fill_rect_inset(dpi, rect, colour, press | INSET_RECT_FLAG_FILL_NONE);
196 return;
197 }
198
199 // Draw the border with fill
200 gfx_fill_rect_inset(dpi, rect, colour, press);
201
202 WidgetDrawImage(dpi, w, widgetIndex);
203 }
204
205 /**
206 *
207 * rct2: 0x006EB806
208 */
WidgetTabDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)209 static void WidgetTabDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
210 {
211 // Get the widget
212 auto& widget = w->widgets[widgetIndex];
213
214 if (widget.type != WindowWidgetType::Tab && static_cast<int32_t>(widget.image) == -1)
215 return;
216
217 if (widget.type == WindowWidgetType::Tab)
218 {
219 if (WidgetIsDisabled(w, widgetIndex))
220 return;
221
222 if (widget.image == static_cast<uint32_t>(SPR_NONE))
223 {
224 // Set standard tab sprite to use.
225 widget.image = IMAGE_TYPE_REMAP | SPR_TAB;
226 }
227 }
228
229 // Draw widgets that aren't explicitly disabled.
230 if (!WidgetIsDisabled(w, widgetIndex))
231 {
232 WidgetDrawImage(dpi, w, widgetIndex);
233 return;
234 }
235
236 if (widget.type != WindowWidgetType::TrnBtn)
237 {
238 WidgetDrawImage(dpi, w, widgetIndex);
239 return;
240 }
241
242 // Resolve the absolute ltrb
243 auto leftTop = w->windowPos + ScreenCoordsXY{ widget.left, widget.top };
244
245 // Get the colour and disabled image
246 uint8_t colour = w->colours[widget.colour] & 0x7F;
247 uint32_t image = widget.image + 2;
248
249 // Draw disabled image
250 gfx_draw_sprite(dpi, ImageId(image, colour), leftTop);
251 }
252
253 /**
254 *
255 * rct2: 0x006EB861
256 */
WidgetFlatButtonDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)257 static void WidgetFlatButtonDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
258 {
259 if (!WidgetIsDisabled(w, widgetIndex) && WidgetIsHighlighted(w, widgetIndex))
260 {
261 WidgetButtonDraw(dpi, w, widgetIndex);
262 return;
263 }
264
265 // Get the widget
266 const auto& widget = w->widgets[widgetIndex];
267
268 // Resolve the absolute ltrb
269 ScreenRect rect{ w->windowPos + ScreenCoordsXY{ widget.left, widget.top },
270 w->windowPos + ScreenCoordsXY{ widget.right, widget.bottom } };
271
272 // Get the colour
273 uint8_t colour = w->colours[widget.colour];
274
275 // Check if the button is pressed down
276 if (WidgetIsPressed(w, widgetIndex) || WidgetIsActiveTool(w, widgetIndex))
277 {
278 if (static_cast<int32_t>(widget.image) == -2)
279 {
280 // Draw border with no fill
281 gfx_fill_rect_inset(dpi, rect, colour, INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_NONE);
282 return;
283 }
284
285 // Draw the border with fill
286 gfx_fill_rect_inset(dpi, rect, colour, INSET_RECT_FLAG_BORDER_INSET);
287 }
288
289 // Draw image
290 WidgetDrawImage(dpi, w, widgetIndex);
291 }
292
293 /**
294 *
295 * rct2: 0x006EBBEB
296 */
WidgetTextButton(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)297 static void WidgetTextButton(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
298 {
299 // Get the widget
300 const auto& widget = w->widgets[widgetIndex];
301
302 // Resolve the absolute ltrb
303 ScreenRect rect{ w->windowPos + ScreenCoordsXY{ widget.left, widget.top },
304 w->windowPos + ScreenCoordsXY{ widget.right, widget.bottom } };
305
306 // Get the colour
307 uint8_t colour = w->colours[widget.colour];
308
309 // Border
310 uint8_t press = WidgetIsPressed(w, widgetIndex) || WidgetIsActiveTool(w, widgetIndex) ? INSET_RECT_FLAG_BORDER_INSET : 0;
311 gfx_fill_rect_inset(dpi, rect, colour, press);
312
313 // Button caption
314 if (widget.type != WindowWidgetType::TableHeader)
315 {
316 WidgetTextCentred(dpi, w, widgetIndex);
317 }
318 else
319 {
320 WidgetText(dpi, w, widgetIndex);
321 }
322 }
323
324 /**
325 *
326 * rct2: 0x006EBC41
327 */
WidgetTextCentred(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)328 static void WidgetTextCentred(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
329 {
330 // Get the widget
331 const auto& widget = w->widgets[widgetIndex];
332
333 if (widget.text == STR_NONE)
334 return;
335
336 // Get the colour
337 colour_t colour = w->colours[widget.colour];
338 colour &= ~(COLOUR_FLAG_TRANSLUCENT);
339 if (WidgetIsDisabled(w, widgetIndex))
340 colour |= COLOUR_FLAG_INSET;
341
342 // Resolve the absolute ltrb
343 auto topLeft = w->windowPos + ScreenCoordsXY{ widget.left, 0 };
344 int32_t r = w->windowPos.x + widget.right;
345
346 if (widget.type == WindowWidgetType::Button || widget.type == WindowWidgetType::TableHeader)
347 topLeft.y += widget.textTop();
348 else
349 topLeft.y += widget.top;
350
351 auto stringId = widget.text;
352 auto ft = Formatter::Common();
353 if (widget.flags & WIDGET_FLAGS::TEXT_IS_STRING)
354 {
355 stringId = STR_STRING;
356 ft.Add<utf8*>(widget.string);
357 }
358
359 ScreenCoordsXY coords = { (topLeft.x + r + 1) / 2 - 1, topLeft.y };
360 if (widget.type == WindowWidgetType::LabelCentred)
361 {
362 DrawTextWrapped(dpi, coords, widget.width() - 2, stringId, ft, { colour, TextAlignment::CENTRE });
363 }
364 else
365 {
366 DrawTextEllipsised(dpi, coords, widget.width() - 2, stringId, ft, { colour, TextAlignment::CENTRE });
367 }
368 }
369
370 /**
371 *
372 * rct2: 0x006EBD52
373 */
WidgetText(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)374 static void WidgetText(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
375 {
376 // Get the widget
377 const auto& widget = w->widgets[widgetIndex];
378
379 if (widget.text == STR_NONE || widget.text == STR_VIEWPORT)
380 return;
381
382 // Get the colour
383 uint8_t colour = w->colours[widget.colour];
384 if (WidgetIsDisabled(w, widgetIndex))
385 colour |= COLOUR_FLAG_INSET;
386
387 // Resolve the absolute ltrb
388 int32_t l = w->windowPos.x + widget.left;
389 int32_t r = w->windowPos.x + widget.right;
390 int32_t t;
391
392 if (widget.type == WindowWidgetType::Button || widget.type == WindowWidgetType::DropdownMenu
393 || widget.type == WindowWidgetType::Spinner || widget.type == WindowWidgetType::TableHeader)
394 {
395 t = w->windowPos.y + widget.textTop();
396 }
397 else
398 t = w->windowPos.y + widget.top;
399
400 auto stringId = widget.text;
401 auto ft = Formatter::Common();
402 if (widget.flags & WIDGET_FLAGS::TEXT_IS_STRING)
403 {
404 stringId = STR_STRING;
405 ft.Add<utf8*>(widget.string);
406 }
407
408 ScreenCoordsXY coords = { l + 1, t };
409 if (widget.type == WindowWidgetType::LabelCentred)
410 {
411 DrawTextWrapped(dpi, coords, r - l, stringId, ft, { colour, TextAlignment::CENTRE });
412 }
413 else
414 {
415 DrawTextEllipsised(dpi, coords, r - l, stringId, ft, colour);
416 }
417 }
418
419 /**
420 *
421 * rct2: 0x006EBD1F
422 */
WidgetTextInset(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)423 static void WidgetTextInset(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
424 {
425 // Get the widget
426 const auto& widget = w->widgets[widgetIndex];
427
428 // Resolve the absolute ltrb
429 ScreenRect rect{ w->windowPos + ScreenCoordsXY{ widget.left, widget.top },
430 w->windowPos + ScreenCoordsXY{ widget.right, widget.bottom } };
431
432 // Get the colour
433 uint8_t colour = w->colours[widget.colour];
434
435 gfx_fill_rect_inset(dpi, rect, colour, INSET_RECT_F_60);
436 WidgetText(dpi, w, widgetIndex);
437 }
438
widget_get_stringid_and_args(const rct_widget & widget)439 static std::pair<rct_string_id, void*> widget_get_stringid_and_args(const rct_widget& widget)
440 {
441 auto stringId = widget.text;
442 void* formatArgs = gCommonFormatArgs;
443 if (widget.flags & WIDGET_FLAGS::TEXT_IS_STRING)
444 {
445 if (widget.string == nullptr || widget.string[0] == '\0')
446 {
447 stringId = STR_NONE;
448 formatArgs = nullptr;
449 }
450 else
451 {
452 stringId = STR_STRING;
453 formatArgs = const_cast<void*>(reinterpret_cast<const void*>(&widget.string));
454 }
455 }
456 return std::make_pair(stringId, formatArgs);
457 }
458
459 /**
460 *
461 * rct2: 0x006EB535
462 */
WidgetGroupboxDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)463 static void WidgetGroupboxDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
464 {
465 // Get the widget
466 const auto& widget = w->widgets[widgetIndex];
467
468 // Resolve the absolute ltrb
469 auto l = w->windowPos.x + widget.left + 5;
470 auto t = w->windowPos.y + widget.top;
471 auto textRight = l;
472
473 // Text
474 auto [stringId, formatArgs] = widget_get_stringid_and_args(widget);
475 if (stringId != STR_NONE)
476 {
477 uint8_t colour = w->colours[widget.colour] & 0x7F;
478 if (WidgetIsDisabled(w, widgetIndex))
479 colour |= COLOUR_FLAG_INSET;
480
481 utf8 buffer[512] = { 0 };
482 format_string(buffer, sizeof(buffer), stringId, formatArgs);
483 auto ft = Formatter();
484 ft.Add<utf8*>(buffer);
485 DrawTextBasic(dpi, { l, t }, STR_STRING, ft, { colour });
486 textRight = l + gfx_get_string_width(buffer, FontSpriteBase::MEDIUM) + 1;
487 }
488
489 // Border
490 // Resolve the absolute ltrb
491 l = w->windowPos.x + widget.left;
492 t = w->windowPos.y + widget.top + 4;
493 const auto r = w->windowPos.x + widget.right;
494 const auto b = w->windowPos.y + widget.bottom;
495
496 // Get the colour
497 uint8_t colour = w->colours[widget.colour] & 0x7F;
498
499 // Border left of text
500 gfx_fill_rect(dpi, { { l, t }, { l + 4, t } }, ColourMapA[colour].mid_dark);
501 gfx_fill_rect(dpi, { { l + 1, t + 1 }, { l + 4, t + 1 } }, ColourMapA[colour].lighter);
502
503 // Border right of text
504 gfx_fill_rect(dpi, { { textRight, t }, { r - 1, t } }, ColourMapA[colour].mid_dark);
505 gfx_fill_rect(dpi, { { textRight, t + 1 }, { r - 2, t + 1 } }, ColourMapA[colour].lighter);
506
507 // Border right
508 gfx_fill_rect(dpi, { { r - 1, t + 1 }, { r - 1, b - 1 } }, ColourMapA[colour].mid_dark);
509 gfx_fill_rect(dpi, { { r, t }, { r, b } }, ColourMapA[colour].lighter);
510
511 // Border bottom
512 gfx_fill_rect(dpi, { { l, b - 1 }, { r - 2, b - 1 } }, ColourMapA[colour].mid_dark);
513 gfx_fill_rect(dpi, { { l, b }, { r - 1, b } }, ColourMapA[colour].lighter);
514
515 // Border left
516 gfx_fill_rect(dpi, { { l, t + 1 }, { l, b - 2 } }, ColourMapA[colour].mid_dark);
517 gfx_fill_rect(dpi, { { l + 1, t + 2 }, { l + 1, b - 2 } }, ColourMapA[colour].lighter);
518 }
519
520 /**
521 *
522 * rct2: 0x006EB2F9
523 */
WidgetCaptionDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)524 static void WidgetCaptionDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
525 {
526 // Get the widget
527 const auto* widget = &w->widgets[widgetIndex];
528
529 // Resolve the absolute ltrb
530 auto topLeft = w->windowPos + ScreenCoordsXY{ widget->left, widget->top };
531 auto bottomRight = w->windowPos + ScreenCoordsXY{ widget->right, widget->bottom };
532
533 // Get the colour
534 uint8_t colour = w->colours[widget->colour];
535
536 uint8_t press = INSET_RECT_F_60;
537 if (w->flags & WF_10)
538 press |= INSET_RECT_FLAG_FILL_MID_LIGHT;
539
540 gfx_fill_rect_inset(dpi, { topLeft, bottomRight }, colour, press);
541
542 // Black caption bars look slightly green, this fixes that
543 if (colour == 0)
544 gfx_fill_rect(
545 dpi, { { topLeft + ScreenCoordsXY{ 1, 1 } }, { bottomRight - ScreenCoordsXY{ 1, 1 } } }, ColourMapA[colour].dark);
546 else
547 gfx_filter_rect(
548 dpi, { { topLeft + ScreenCoordsXY{ 1, 1 } }, { bottomRight - ScreenCoordsXY{ 1, 1 } } },
549 FilterPaletteID::PaletteDarken3);
550
551 // Draw text
552 if (widget->text == STR_NONE)
553 return;
554
555 topLeft = w->windowPos + ScreenCoordsXY{ widget->left + 2, widget->top + 1 };
556 int32_t width = widget->width() - 4;
557 if ((widget + 1)->type == WindowWidgetType::CloseBox)
558 {
559 width -= 10;
560 if ((widget + 2)->type == WindowWidgetType::CloseBox)
561 width -= 10;
562 }
563 topLeft.x += width / 2;
564 DrawTextEllipsised(
565 dpi, topLeft, width, widget->text, Formatter::Common(), { COLOUR_WHITE | COLOUR_FLAG_OUTLINE, TextAlignment::CENTRE });
566 }
567
568 /**
569 *
570 * rct2: 0x006EBB85
571 */
WidgetCloseboxDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)572 static void WidgetCloseboxDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
573 {
574 // Get the widget
575 const auto& widget = w->widgets[widgetIndex];
576
577 // Resolve the absolute ltrb
578 auto topLeft = w->windowPos + ScreenCoordsXY{ widget.left, widget.top };
579 auto bottomRight = w->windowPos + ScreenCoordsXY{ widget.right, widget.bottom };
580
581 // Check if the button is pressed down
582 uint8_t press = 0;
583 if (w->flags & WF_10)
584 press |= INSET_RECT_FLAG_FILL_MID_LIGHT;
585 if (WidgetIsPressed(w, widgetIndex) || WidgetIsActiveTool(w, widgetIndex))
586 press |= INSET_RECT_FLAG_BORDER_INSET;
587
588 // Get the colour
589 uint8_t colour = w->colours[widget.colour];
590
591 // Draw the button
592 gfx_fill_rect_inset(dpi, { topLeft, bottomRight }, colour, press);
593
594 if (widget.text == STR_NONE)
595 return;
596
597 topLeft = w->windowPos + ScreenCoordsXY{ widget.midX() - 1, std::max<int32_t>(widget.top, widget.midY() - 5) };
598
599 if (WidgetIsDisabled(w, widgetIndex))
600 colour |= COLOUR_FLAG_INSET;
601
602 DrawTextEllipsised(dpi, topLeft, widget.width() - 2, widget.text, Formatter::Common(), { colour, TextAlignment::CENTRE });
603 }
604
605 /**
606 *
607 * rct2: 0x006EBAD9
608 */
WidgetCheckboxDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)609 static void WidgetCheckboxDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
610 {
611 // Get the widget
612 const auto& widget = w->widgets[widgetIndex];
613
614 // Resolve the absolute ltb
615 ScreenCoordsXY topLeft = w->windowPos + ScreenCoordsXY{ widget.left, widget.top };
616 ScreenCoordsXY bottomRight = w->windowPos + ScreenCoordsXY{ widget.right, widget.bottom };
617 ScreenCoordsXY midLeft = { topLeft.x, (topLeft.y + bottomRight.y) / 2 };
618
619 // Get the colour
620 colour_t colour = w->colours[widget.colour];
621
622 // checkbox
623 gfx_fill_rect_inset(dpi, { midLeft - ScreenCoordsXY{ 0, 5 }, midLeft + ScreenCoordsXY{ 9, 4 } }, colour, INSET_RECT_F_60);
624
625 if (WidgetIsDisabled(w, widgetIndex))
626 {
627 colour |= COLOUR_FLAG_INSET;
628 }
629
630 // fill it when checkbox is pressed
631 if (WidgetIsPressed(w, widgetIndex))
632 {
633 gfx_draw_string(
634 dpi, { midLeft - ScreenCoordsXY{ 0, 5 } }, static_cast<const char*>(CheckBoxMarkString),
635 { static_cast<colour_t>(NOT_TRANSLUCENT(colour)) });
636 }
637
638 // draw the text
639 if (widget.text == STR_NONE)
640 return;
641
642 auto [stringId, formatArgs] = widget_get_stringid_and_args(widget);
643 gfx_draw_string_left_centred(dpi, stringId, formatArgs, colour, { midLeft + ScreenCoordsXY{ 14, 0 } });
644 }
645
646 /**
647 *
648 * rct2: 0x006EBD96
649 */
WidgetScrollDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)650 static void WidgetScrollDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
651 {
652 // Get the widget
653 int32_t scrollIndex = window_get_scroll_data_index(w, widgetIndex);
654 const auto& widget = w->widgets[widgetIndex];
655 const auto& scroll = w->scrolls[scrollIndex];
656
657 // Resolve the absolute ltrb
658 ScreenCoordsXY topLeft = w->windowPos + ScreenCoordsXY{ widget.left, widget.top };
659 ScreenCoordsXY bottomRight = w->windowPos + ScreenCoordsXY{ widget.right, widget.bottom };
660
661 // Get the colour
662 uint8_t colour = w->colours[widget.colour];
663
664 // Draw the border
665 gfx_fill_rect_inset(dpi, { topLeft, bottomRight }, colour, INSET_RECT_F_60);
666
667 // Inflate by -1
668 topLeft.x++;
669 topLeft.y++;
670 bottomRight.x--;
671 bottomRight.y--;
672
673 // Horizontal scrollbar
674 if (scroll.flags & HSCROLLBAR_VISIBLE)
675 WidgetHScrollbarDraw(
676 dpi, scroll, topLeft.x, bottomRight.y - SCROLLBAR_WIDTH,
677 ((scroll.flags & VSCROLLBAR_VISIBLE) ? bottomRight.x - (SCROLLBAR_WIDTH + 1) : bottomRight.x), bottomRight.y,
678 colour);
679
680 // Vertical scrollbar
681 if (scroll.flags & VSCROLLBAR_VISIBLE)
682 WidgetVScrollbarDraw(
683 dpi, scroll, bottomRight.x - SCROLLBAR_WIDTH, topLeft.y, bottomRight.x,
684 ((scroll.flags & HSCROLLBAR_VISIBLE) ? bottomRight.y - (SCROLLBAR_WIDTH + 1) : bottomRight.y), colour);
685
686 // Contents
687 if (scroll.flags & HSCROLLBAR_VISIBLE)
688 bottomRight.y -= (SCROLLBAR_WIDTH + 1);
689 if (scroll.flags & VSCROLLBAR_VISIBLE)
690 bottomRight.x -= (SCROLLBAR_WIDTH + 1);
691
692 bottomRight.y++;
693 bottomRight.x++;
694
695 // Create a new inner scroll dpi
696 rct_drawpixelinfo scroll_dpi = *dpi;
697
698 // Clip the scroll dpi against the outer dpi
699 int32_t cl = std::max<int32_t>(dpi->x, topLeft.x);
700 int32_t ct = std::max<int32_t>(dpi->y, topLeft.y);
701 int32_t cr = std::min<int32_t>(dpi->x + dpi->width, bottomRight.x);
702 int32_t cb = std::min<int32_t>(dpi->y + dpi->height, bottomRight.y);
703
704 // Set the respective dpi attributes
705 scroll_dpi.x = cl - topLeft.x + scroll.h_left;
706 scroll_dpi.y = ct - topLeft.y + scroll.v_top;
707 scroll_dpi.width = cr - cl;
708 scroll_dpi.height = cb - ct;
709 scroll_dpi.bits += cl - dpi->x;
710 scroll_dpi.bits += (ct - dpi->y) * (dpi->width + dpi->pitch);
711 scroll_dpi.pitch = (dpi->width + dpi->pitch) - scroll_dpi.width;
712
713 // Draw the scroll contents
714 if (scroll_dpi.width > 0 && scroll_dpi.height > 0)
715 window_event_scroll_paint_call(w, &scroll_dpi, scrollIndex);
716 }
717
WidgetHScrollbarDraw(rct_drawpixelinfo * dpi,const rct_scroll & scroll,int32_t l,int32_t t,int32_t r,int32_t b,int32_t colour)718 static void WidgetHScrollbarDraw(
719 rct_drawpixelinfo* dpi, const rct_scroll& scroll, int32_t l, int32_t t, int32_t r, int32_t b, int32_t colour)
720 {
721 colour &= 0x7F;
722 // Trough
723 gfx_fill_rect(dpi, { { l + SCROLLBAR_WIDTH, t }, { r - SCROLLBAR_WIDTH, b } }, ColourMapA[colour].lighter);
724 gfx_fill_rect(dpi, { { l + SCROLLBAR_WIDTH, t }, { r - SCROLLBAR_WIDTH, b } }, 0x1000000 | ColourMapA[colour].mid_dark);
725 gfx_fill_rect(dpi, { { l + SCROLLBAR_WIDTH, t + 2 }, { r - SCROLLBAR_WIDTH, t + 2 } }, ColourMapA[colour].mid_dark);
726 gfx_fill_rect(dpi, { { l + SCROLLBAR_WIDTH, t + 3 }, { r - SCROLLBAR_WIDTH, t + 3 } }, ColourMapA[colour].lighter);
727 gfx_fill_rect(dpi, { { l + SCROLLBAR_WIDTH, t + 7 }, { r - SCROLLBAR_WIDTH, t + 7 } }, ColourMapA[colour].mid_dark);
728 gfx_fill_rect(dpi, { { l + SCROLLBAR_WIDTH, t + 8 }, { r - SCROLLBAR_WIDTH, t + 8 } }, ColourMapA[colour].lighter);
729
730 // Left button
731 {
732 uint8_t flags = (scroll.flags & HSCROLLBAR_LEFT_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0;
733
734 gfx_fill_rect_inset(dpi, { { l, t }, { l + (SCROLLBAR_WIDTH - 1), b } }, colour, flags);
735 gfx_draw_string(dpi, { l + 1, t }, static_cast<const char*>(BlackLeftArrowString), {});
736 }
737
738 // Thumb
739 {
740 int16_t left = std::max(l + SCROLLBAR_WIDTH, l + scroll.h_thumb_left - 1);
741 int16_t right = std::min(r - SCROLLBAR_WIDTH, l + scroll.h_thumb_right - 1);
742 uint8_t flags = (scroll.flags & HSCROLLBAR_THUMB_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0;
743
744 gfx_fill_rect_inset(dpi, { { left, t }, { right, b } }, colour, flags);
745 }
746
747 // Right button
748 {
749 uint8_t flags = (scroll.flags & HSCROLLBAR_RIGHT_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0;
750
751 gfx_fill_rect_inset(dpi, { { r - (SCROLLBAR_WIDTH - 1), t }, { r, b } }, colour, flags);
752 gfx_draw_string(dpi, { r - 6, t }, static_cast<const char*>(BlackRightArrowString), {});
753 }
754 }
755
WidgetVScrollbarDraw(rct_drawpixelinfo * dpi,const rct_scroll & scroll,int32_t l,int32_t t,int32_t r,int32_t b,int32_t colour)756 static void WidgetVScrollbarDraw(
757 rct_drawpixelinfo* dpi, const rct_scroll& scroll, int32_t l, int32_t t, int32_t r, int32_t b, int32_t colour)
758 {
759 colour &= 0x7F;
760 // Trough
761 gfx_fill_rect(dpi, { { l, t + SCROLLBAR_WIDTH }, { r, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].lighter);
762 gfx_fill_rect(dpi, { { l, t + SCROLLBAR_WIDTH }, { r, b - SCROLLBAR_WIDTH } }, 0x1000000 | ColourMapA[colour].mid_dark);
763 gfx_fill_rect(dpi, { { l + 2, t + SCROLLBAR_WIDTH }, { l + 2, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].mid_dark);
764 gfx_fill_rect(dpi, { { l + 3, t + SCROLLBAR_WIDTH }, { l + 3, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].lighter);
765 gfx_fill_rect(dpi, { { l + 7, t + SCROLLBAR_WIDTH }, { l + 7, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].mid_dark);
766 gfx_fill_rect(dpi, { { l + 8, t + SCROLLBAR_WIDTH }, { l + 8, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].lighter);
767
768 // Up button
769 gfx_fill_rect_inset(
770 dpi, { { l, t }, { r, t + (SCROLLBAR_WIDTH - 1) } }, colour,
771 ((scroll.flags & VSCROLLBAR_UP_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0));
772 gfx_draw_string(dpi, { l + 1, t - 1 }, static_cast<const char*>(BlackUpArrowString), {});
773
774 // Thumb
775 gfx_fill_rect_inset(
776 dpi,
777 { { l, std::max(t + SCROLLBAR_WIDTH, t + scroll.v_thumb_top - 1) },
778 { r, std::min(b - SCROLLBAR_WIDTH, t + scroll.v_thumb_bottom - 1) } },
779 colour, ((scroll.flags & VSCROLLBAR_THUMB_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0));
780
781 // Down button
782 gfx_fill_rect_inset(
783 dpi, { { l, b - (SCROLLBAR_WIDTH - 1) }, { r, b } }, colour,
784 ((scroll.flags & VSCROLLBAR_DOWN_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0));
785 gfx_draw_string(dpi, { l + 1, b - (SCROLLBAR_WIDTH - 1) }, static_cast<const char*>(BlackDownArrowString), {});
786 }
787
788 /**
789 *
790 * rct2: 0x006EB951
791 */
WidgetDrawImage(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)792 static void WidgetDrawImage(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
793 {
794 // Get the widget
795 const auto& widget = w->widgets[widgetIndex];
796
797 // Get the image
798 int32_t image = widget.image;
799 if (image == SPR_NONE)
800 return;
801
802 // Resolve the absolute ltrb
803 auto screenCoords = w->windowPos + ScreenCoordsXY{ widget.left, widget.top };
804
805 // Get the colour
806 uint8_t colour = NOT_TRANSLUCENT(w->colours[widget.colour]);
807
808 if (widget.type == WindowWidgetType::ColourBtn || widget.type == WindowWidgetType::TrnBtn
809 || widget.type == WindowWidgetType::Tab)
810 if (WidgetIsPressed(w, widgetIndex) || WidgetIsActiveTool(w, widgetIndex))
811 image++;
812
813 if (WidgetIsDisabled(w, widgetIndex))
814 {
815 // Draw greyed out (light border bottom right shadow)
816 colour = w->colours[widget.colour];
817 colour = ColourMapA[NOT_TRANSLUCENT(colour)].lighter;
818 gfx_draw_sprite_solid(dpi, image, screenCoords + ScreenCoordsXY{ 1, 1 }, colour);
819
820 // Draw greyed out (dark)
821 colour = w->colours[widget.colour];
822 colour = ColourMapA[NOT_TRANSLUCENT(colour)].mid_light;
823 gfx_draw_sprite_solid(dpi, image, screenCoords, colour);
824 }
825 else
826 {
827 if (image & IMAGE_TYPE_REMAP_2_PLUS)
828 {
829 // ?
830 }
831
832 if (image & IMAGE_TYPE_TRANSPARENT)
833 image &= ~IMAGE_TYPE_TRANSPARENT;
834 else
835 image |= colour << 19;
836
837 gfx_draw_sprite(dpi, ImageId::FromUInt32(image), screenCoords);
838 }
839 }
840
WidgetIsEnabled(rct_window * w,rct_widgetindex widgetIndex)841 bool WidgetIsEnabled(rct_window* w, rct_widgetindex widgetIndex)
842 {
843 if (!WidgetIsVisible(w, widgetIndex))
844 return false;
845 return (w->enabled_widgets & (1LL << widgetIndex)) != 0;
846 }
847
WidgetIsDisabled(rct_window * w,rct_widgetindex widgetIndex)848 bool WidgetIsDisabled(rct_window* w, rct_widgetindex widgetIndex)
849 {
850 return (w->disabled_widgets & (1LL << widgetIndex)) != 0;
851 }
852
WidgetIsHoldable(rct_window * w,rct_widgetindex widgetIndex)853 bool WidgetIsHoldable(rct_window* w, rct_widgetindex widgetIndex)
854 {
855 return (w->hold_down_widgets & (1LL << widgetIndex)) != 0;
856 }
857
WidgetIsVisible(rct_window * w,rct_widgetindex widgetIndex)858 bool WidgetIsVisible(rct_window* w, rct_widgetindex widgetIndex)
859 {
860 return w->widgets[widgetIndex].IsVisible();
861 }
862
WidgetIsPressed(rct_window * w,rct_widgetindex widgetIndex)863 bool WidgetIsPressed(rct_window* w, rct_widgetindex widgetIndex)
864 {
865 if (w->pressed_widgets & (1LL << widgetIndex))
866 {
867 return true;
868 }
869 if (input_get_state() == InputState::WidgetPressed || input_get_state() == InputState::DropdownActive)
870 {
871 if (!(input_test_flag(INPUT_FLAG_WIDGET_PRESSED)))
872 return false;
873 if (gPressedWidget.window_classification != w->classification)
874 return false;
875 if (gPressedWidget.window_number != w->number)
876 return false;
877 if (gPressedWidget.widget_index != widgetIndex)
878 return false;
879 return true;
880 }
881 return false;
882 }
883
WidgetIsHighlighted(rct_window * w,rct_widgetindex widgetIndex)884 bool WidgetIsHighlighted(rct_window* w, rct_widgetindex widgetIndex)
885 {
886 if (gHoverWidget.window_classification != w->classification)
887 return false;
888 if (gHoverWidget.window_number != w->number)
889 return false;
890 if (gHoverWidget.widget_index != widgetIndex)
891 return false;
892 return true;
893 }
894
WidgetIsActiveTool(rct_window * w,rct_widgetindex widgetIndex)895 bool WidgetIsActiveTool(rct_window* w, rct_widgetindex widgetIndex)
896 {
897 if (!(input_test_flag(INPUT_FLAG_TOOL_ACTIVE)))
898 return false;
899 if (gCurrentToolWidget.window_classification != w->classification)
900 return false;
901 if (gCurrentToolWidget.window_number != w->number)
902 return false;
903 if (gCurrentToolWidget.widget_index != widgetIndex)
904 return false;
905
906 return true;
907 }
908
909 /**
910 *
911 * rct2: 0x006E9F92
912 * eax: x / output_x
913 * ebx: y / output_y
914 * ecx: output_scroll_area
915 * edx: scroll_id
916 * esi: w
917 * edi: widget
918 */
WidgetScrollGetPart(rct_window * w,const rct_widget * widget,const ScreenCoordsXY & screenCoords,ScreenCoordsXY & retScreenCoords,int32_t * output_scroll_area,int32_t * scroll_id)919 void WidgetScrollGetPart(
920 rct_window* w, const rct_widget* widget, const ScreenCoordsXY& screenCoords, ScreenCoordsXY& retScreenCoords,
921 int32_t* output_scroll_area, int32_t* scroll_id)
922 {
923 *scroll_id = 0;
924 for (rct_widget* iterator = w->widgets; iterator != widget; iterator++)
925 {
926 if (iterator->type == WindowWidgetType::Scroll)
927 {
928 *scroll_id += 1;
929 }
930 }
931
932 const auto& scroll = w->scrolls[*scroll_id];
933 if ((scroll.flags & HSCROLLBAR_VISIBLE) && screenCoords.y >= (w->windowPos.y + widget->bottom - (SCROLLBAR_WIDTH + 1)))
934 {
935 // horizontal scrollbar
936 int32_t rightOffset = 0;
937 int32_t iteratorLeft = widget->left + w->windowPos.x + SCROLLBAR_WIDTH;
938 int32_t iteratorRight = widget->right + w->windowPos.x - SCROLLBAR_WIDTH;
939 if (!(scroll.flags & VSCROLLBAR_VISIBLE))
940 {
941 rightOffset = SCROLLBAR_WIDTH + 1;
942 }
943
944 if (screenCoords.x <= iteratorLeft)
945 {
946 *output_scroll_area = SCROLL_PART_HSCROLLBAR_LEFT;
947 }
948 else if (screenCoords.x >= iteratorRight + rightOffset)
949 {
950 *output_scroll_area = SCROLL_PART_NONE;
951 }
952 else if (screenCoords.x >= iteratorRight + rightOffset - SCROLLBAR_WIDTH)
953 {
954 *output_scroll_area = SCROLL_PART_HSCROLLBAR_RIGHT;
955 }
956 else if (screenCoords.x < (widget->left + w->windowPos.x + scroll.h_thumb_left))
957 {
958 *output_scroll_area = SCROLL_PART_HSCROLLBAR_LEFT_TROUGH;
959 }
960 else if (screenCoords.x > (widget->left + w->windowPos.x + scroll.h_thumb_right))
961 {
962 *output_scroll_area = SCROLL_PART_HSCROLLBAR_RIGHT_TROUGH;
963 }
964 else
965 {
966 *output_scroll_area = SCROLL_PART_HSCROLLBAR_THUMB;
967 }
968 }
969 else if ((scroll.flags & VSCROLLBAR_VISIBLE) && (screenCoords.x >= w->windowPos.x + widget->right - (SCROLLBAR_WIDTH + 1)))
970 {
971 // vertical scrollbar
972 int32_t bottomOffset = 0;
973 int32_t iteratorTop = widget->top + w->windowPos.y + SCROLLBAR_WIDTH;
974 int32_t iteratorBottom = widget->bottom + w->windowPos.y;
975 if (scroll.flags & HSCROLLBAR_VISIBLE)
976 {
977 bottomOffset = (SCROLLBAR_WIDTH + 1);
978 }
979
980 if (screenCoords.y <= iteratorTop)
981 {
982 *output_scroll_area = SCROLL_PART_VSCROLLBAR_TOP;
983 }
984 else if (screenCoords.y >= (iteratorBottom - bottomOffset))
985 {
986 *output_scroll_area = SCROLL_PART_NONE;
987 }
988 else if (screenCoords.y >= (iteratorBottom - bottomOffset - SCROLLBAR_WIDTH))
989 {
990 *output_scroll_area = SCROLL_PART_VSCROLLBAR_BOTTOM;
991 }
992 else if (screenCoords.y < (widget->top + w->windowPos.y + scroll.v_thumb_top))
993 {
994 *output_scroll_area = SCROLL_PART_VSCROLLBAR_TOP_TROUGH;
995 }
996 else if (screenCoords.y > (widget->top + w->windowPos.y + scroll.v_thumb_bottom))
997 {
998 *output_scroll_area = SCROLL_PART_VSCROLLBAR_BOTTOM_TROUGH;
999 }
1000 else
1001 {
1002 *output_scroll_area = SCROLL_PART_VSCROLLBAR_THUMB;
1003 }
1004 }
1005 else
1006 {
1007 // view
1008 *output_scroll_area = SCROLL_PART_VIEW;
1009 retScreenCoords.x = screenCoords.x - widget->left;
1010 retScreenCoords.y = screenCoords.y - widget->top;
1011 retScreenCoords -= w->windowPos;
1012 if (retScreenCoords.x <= 0 || retScreenCoords.y <= 0)
1013 {
1014 *output_scroll_area = SCROLL_PART_NONE;
1015 }
1016 else
1017 {
1018 retScreenCoords.x += scroll.h_left - 1;
1019 retScreenCoords.y += scroll.v_top - 1;
1020 }
1021 }
1022 }
1023
WidgetSetEnabled(rct_window * w,rct_widgetindex widgetIndex,bool enabled)1024 void WidgetSetEnabled(rct_window* w, rct_widgetindex widgetIndex, bool enabled)
1025 {
1026 if (enabled)
1027 {
1028 w->enabled_widgets |= (1ULL << widgetIndex);
1029 w->disabled_widgets &= ~(1ULL << widgetIndex);
1030 }
1031 else
1032 {
1033 w->enabled_widgets &= ~(1ULL << widgetIndex);
1034 w->disabled_widgets |= (1ULL << widgetIndex);
1035 }
1036 }
1037
WidgetSetDisabled(rct_window * w,rct_widgetindex widgetIndex,bool value)1038 void WidgetSetDisabled(rct_window* w, rct_widgetindex widgetIndex, bool value)
1039 {
1040 if (value)
1041 {
1042 w->disabled_widgets |= (1ULL << widgetIndex);
1043 }
1044 else
1045 {
1046 w->disabled_widgets &= ~(1ULL << widgetIndex);
1047 }
1048 }
1049
WidgetSetHoldable(rct_window * w,rct_widgetindex widgetIndex,bool value)1050 void WidgetSetHoldable(rct_window* w, rct_widgetindex widgetIndex, bool value)
1051 {
1052 if (value)
1053 {
1054 w->hold_down_widgets |= (1ULL << widgetIndex);
1055 }
1056 else
1057 {
1058 w->hold_down_widgets &= ~(1ULL << widgetIndex);
1059 }
1060 }
1061
WidgetSetVisible(rct_window * w,rct_widgetindex widgetIndex,bool value)1062 void WidgetSetVisible(rct_window* w, rct_widgetindex widgetIndex, bool value)
1063 {
1064 if (value)
1065 {
1066 w->widgets[widgetIndex].flags &= ~WIDGET_FLAGS::IS_HIDDEN;
1067 }
1068 else
1069 {
1070 w->widgets[widgetIndex].flags |= WIDGET_FLAGS::IS_HIDDEN;
1071 }
1072 }
1073
WidgetSetCheckboxValue(rct_window * w,rct_widgetindex widgetIndex,int32_t value)1074 void WidgetSetCheckboxValue(rct_window* w, rct_widgetindex widgetIndex, int32_t value)
1075 {
1076 if (value)
1077 w->pressed_widgets |= (1ULL << widgetIndex);
1078 else
1079 w->pressed_widgets &= ~(1ULL << widgetIndex);
1080 }
1081
WidgetTextBoxDraw(rct_drawpixelinfo * dpi,rct_window * w,rct_widgetindex widgetIndex)1082 static void WidgetTextBoxDraw(rct_drawpixelinfo* dpi, rct_window* w, rct_widgetindex widgetIndex)
1083 {
1084 int32_t no_lines = 0;
1085 char wrapped_string[TEXT_INPUT_SIZE];
1086
1087 // Get the widget
1088 const auto& widget = w->widgets[widgetIndex];
1089
1090 // Resolve the absolute ltrb
1091 ScreenCoordsXY topLeft{ w->windowPos + ScreenCoordsXY{ widget.left, widget.top } };
1092 ScreenCoordsXY bottomRight{ w->windowPos + ScreenCoordsXY{ widget.right, widget.bottom } };
1093
1094 // Get the colour
1095 uint8_t colour = w->colours[widget.colour];
1096
1097 bool active = w->classification == gCurrentTextBox.window.classification && w->number == gCurrentTextBox.window.number
1098 && widgetIndex == gCurrentTextBox.widget_index;
1099
1100 // gfx_fill_rect_inset(dpi, l, t, r, b, colour, 0x20 | (!active ? 0x40 : 0x00));
1101 gfx_fill_rect_inset(dpi, { topLeft, bottomRight }, colour, INSET_RECT_F_60);
1102
1103 // Figure out where the text should be positioned vertically.
1104 topLeft.y = w->windowPos.y + widget.textTop();
1105
1106 if (!active || gTextInput == nullptr)
1107 {
1108 if (widget.text != 0)
1109 {
1110 safe_strcpy(wrapped_string, widget.string, 512);
1111 gfx_wrap_string(wrapped_string, bottomRight.x - topLeft.x - 5, FontSpriteBase::MEDIUM, &no_lines);
1112 gfx_draw_string_no_formatting(
1113 dpi, { topLeft.x + 2, topLeft.y }, wrapped_string, { w->colours[1], FontSpriteBase::MEDIUM });
1114 }
1115 return;
1116 }
1117
1118 safe_strcpy(wrapped_string, gTextBoxInput, TEXT_INPUT_SIZE);
1119
1120 // String length needs to add 12 either side of box
1121 // +13 for cursor when max length.
1122 gfx_wrap_string(wrapped_string, bottomRight.x - topLeft.x - 5 - 6, FontSpriteBase::MEDIUM, &no_lines);
1123
1124 gfx_draw_string_no_formatting(dpi, { topLeft.x + 2, topLeft.y }, wrapped_string, { w->colours[1], FontSpriteBase::MEDIUM });
1125
1126 size_t string_length = get_string_size(wrapped_string) - 1;
1127
1128 // Make a copy of the string for measuring the width.
1129 char temp_string[TEXT_INPUT_SIZE] = { 0 };
1130 std::memcpy(temp_string, wrapped_string, std::min(string_length, gTextInput->SelectionStart));
1131 int32_t cur_x = topLeft.x + gfx_get_string_width_no_formatting(temp_string, FontSpriteBase::MEDIUM) + 3;
1132
1133 int32_t width = 6;
1134 if (static_cast<uint32_t>(gTextInput->SelectionStart) < strlen(gTextBoxInput))
1135 {
1136 // Make a new 1 character wide string for measuring the width
1137 // of the character that the cursor is under.
1138 temp_string[1] = '\0';
1139 temp_string[0] = gTextBoxInput[gTextInput->SelectionStart];
1140 width = std::max(gfx_get_string_width_no_formatting(temp_string, FontSpriteBase::MEDIUM) - 2, 4);
1141 }
1142
1143 if (gTextBoxFrameNo <= 15)
1144 {
1145 colour = ColourMapA[w->colours[1]].mid_light;
1146 auto y = topLeft.y + (widget.height() - 1);
1147 gfx_fill_rect(dpi, { { cur_x, y }, { cur_x + width, y } }, colour + 5);
1148 }
1149 }
1150
GetColourButtonImage(colour_t colour)1151 uint32_t GetColourButtonImage(colour_t colour)
1152 {
1153 return SPRITE_ID_PALETTE_COLOUR_1(colour) | IMAGE_TYPE_TRANSPARENT | SPR_PALETTE_BTN;
1154 }
1155