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