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 "../interface/Theme.h"
11 
12 #include <openrct2-ui/interface/Dropdown.h>
13 #include <openrct2-ui/interface/Widget.h>
14 #include <openrct2-ui/windows/Window.h>
15 #include <openrct2/Context.h>
16 #include <openrct2/Game.h>
17 #include <openrct2/Input.h>
18 #include <openrct2/config/Config.h>
19 #include <openrct2/drawing/Drawing.h>
20 #include <openrct2/localisation/Localisation.h>
21 #include <openrct2/sprites.h>
22 #include <openrct2/util/Util.h>
23 
24 enum
25 {
26     WINDOW_THEMES_TAB_SETTINGS,
27     WINDOW_THEMES_TAB_MAIN_UI,
28     WINDOW_THEMES_TAB_PARK,
29     WINDOW_THEMES_TAB_TOOLS,
30     WINDOW_THEMES_TAB_RIDES_PEEPS,
31     WINDOW_THEMES_TAB_EDITORS,
32     WINDOW_THEMES_TAB_MISC,
33     WINDOW_THEMES_TAB_PROMPTS,
34     WINDOW_THEMES_TAB_FEATURES,
35     WINDOW_THEMES_TAB_COUNT
36 };
37 
38 static void window_themes_mouseup(rct_window* w, rct_widgetindex widgetIndex);
39 static void window_themes_resize(rct_window* w);
40 static void window_themes_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
41 static void window_themes_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
42 static void window_themes_update(rct_window* w);
43 static void window_themes_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height);
44 static void window_themes_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
45 static void window_themes_scrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
46 static void window_themes_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text);
47 static void window_themes_invalidate(rct_window* w);
48 static void window_themes_paint(rct_window* w, rct_drawpixelinfo* dpi);
49 static void window_themes_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex);
50 static void window_themes_draw_tab_images(rct_drawpixelinfo* dpi, rct_window* w);
51 
__anon011d13cd0202(auto& events) 52 static rct_window_event_list window_themes_events([](auto& events) {
53     events.mouse_up = &window_themes_mouseup;
54     events.resize = &window_themes_resize;
55     events.mouse_down = &window_themes_mousedown;
56     events.dropdown = &window_themes_dropdown;
57     events.update = &window_themes_update;
58     events.get_scroll_size = &window_themes_scrollgetsize;
59     events.scroll_mousedown = &window_themes_scrollmousedown;
60     events.scroll_mouseover = &window_themes_scrollmouseover;
61     events.text_input = &window_themes_textinput;
62     events.invalidate = &window_themes_invalidate;
63     events.paint = &window_themes_paint;
64     events.scroll_paint = &window_themes_scrollpaint;
65 });
66 
67 enum WINDOW_THEMES_WIDGET_IDX
68 {
69     WIDX_THEMES_BACKGROUND,
70     WIDX_THEMES_TITLE,
71     WIDX_THEMES_CLOSE,
72     WIDX_THEMES_TAB_CONTENT_PANEL,
73     WIDX_THEMES_SETTINGS_TAB,
74     WIDX_THEMES_MAIN_UI_TAB,
75     WIDX_THEMES_PARK_TAB,
76     WIDX_THEMES_TOOLS_TAB,
77     WIDX_THEMES_RIDE_PEEPS_TAB,
78     WIDX_THEMES_EDITORS_TAB,
79     WIDX_THEMES_MISC_TAB,
80     WIDX_THEMES_PROMPTS_TAB,
81     WIDX_THEMES_FEATURES_TAB,
82     WIDX_THEMES_HEADER_WINDOW,
83     WIDX_THEMES_HEADER_PALETTE,
84     WIDX_THEMES_PRESETS,
85     WIDX_THEMES_PRESETS_DROPDOWN,
86     WIDX_THEMES_DUPLICATE_BUTTON,
87     WIDX_THEMES_DELETE_BUTTON,
88     WIDX_THEMES_RENAME_BUTTON,
89     WIDX_THEMES_COLOURBTN_MASK,
90     WIDX_THEMES_LIST,
91     WIDX_THEMES_RCT1_RIDE_LIGHTS,
92     WIDX_THEMES_RCT1_PARK_LIGHTS,
93     WIDX_THEMES_RCT1_SCENARIO_FONT,
94     WIDX_THEMES_RCT1_BOTTOM_TOOLBAR
95 };
96 
97 static constexpr const rct_string_id WINDOW_TITLE = STR_THEMES_TITLE;
98 static constexpr const int32_t WW = 320;
99 static constexpr const int32_t WH = 107;
100 
101 // clang-format off
102 static rct_widget window_themes_widgets[] = {
103     WINDOW_SHIM(WINDOW_TITLE, WW, WH),
104     MakeWidget({  0, 43}, {320,  64}, WindowWidgetType::Resize,       WindowColour::Secondary                                                                                     ), // tab content panel
105     MakeTab   ({  3, 17},                                                                                                        STR_THEMES_TAB_SETTINGS_TIP        ), // settings tab
106     MakeTab   ({ 34, 17},                                                                                                        STR_THEMES_TAB_MAIN_TIP            ), // main ui tab
107     MakeTab   ({ 65, 17},                                                                                                        STR_THEMES_TAB_PARK_TIP            ), // park tab
108     MakeTab   ({ 96, 17},                                                                                                        STR_THEMES_TAB_TOOLS_TIP           ), // tools tab
109     MakeTab   ({127, 17},                                                                                                        STR_THEMES_TAB_RIDES_AND_GUESTS_TIP), // rides and peeps tab
110     MakeTab   ({158, 17},                                                                                                        STR_THEMES_TAB_EDITORS_TIP         ), // editors tab
111     MakeTab   ({189, 17},                                                                                                        STR_THEMES_TAB_MISC_TIP            ), // misc tab
112     MakeTab   ({220, 17},                                                                                                        STR_THEMES_TAB_PROMPTS_TIP         ), // prompts tab
113     MakeTab   ({251, 17},                                                                                                        STR_THEMES_TAB_FEATURES_TIP        ), // features tab
114     MakeWidget({  5, 46}, {214,  15}, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_THEMES_HEADER_WINDOW                                                           ), // Window header
115     MakeWidget({219, 46}, { 97,  15}, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_THEMES_HEADER_PALETTE                                                          ), // Palette header
116     MakeWidget({125, 60}, {175,  12}, WindowWidgetType::DropdownMenu,     WindowColour::Secondary                                                                                     ), // Preset colour schemes
117     MakeWidget({288, 61}, { 11,  10}, WindowWidgetType::Button,       WindowColour::Secondary, STR_DROPDOWN_GLYPH                                                                 ),
118     MakeWidget({ 10, 82}, { 91,  12}, WindowWidgetType::Button,       WindowColour::Secondary, STR_TITLE_EDITOR_ACTION_DUPLICATE,              STR_THEMES_ACTION_DUPLICATE_TIP    ), // Duplicate button
119     MakeWidget({110, 82}, { 91,  12}, WindowWidgetType::Button,       WindowColour::Secondary, STR_TRACK_MANAGE_DELETE,                        STR_THEMES_ACTION_DELETE_TIP       ), // Delete button
120     MakeWidget({210, 82}, { 91,  12}, WindowWidgetType::Button,       WindowColour::Secondary, STR_TRACK_MANAGE_RENAME,                        STR_THEMES_ACTION_RENAME_TIP       ), // Rename button
121     MakeWidget({  0,  0}, {  1,   1}, WindowWidgetType::ColourBtn,    WindowColour::Secondary                                                                                     ), // colour button mask
122     MakeWidget({  3, 60}, {314,  44}, WindowWidgetType::Scroll,       WindowColour::Secondary, SCROLL_VERTICAL                                                                    ), // staff list
123     MakeWidget({ 10, 54}, {290,  12}, WindowWidgetType::Checkbox,     WindowColour::Secondary, STR_THEMES_OPTION_RCT1_RIDE_CONTROLS                                               ), // rct1 ride lights
124     MakeWidget({ 10, 69}, {290,  12}, WindowWidgetType::Checkbox,     WindowColour::Secondary, STR_THEMES_OPTION_RCT1_PARK_CONTROLS                                               ), // rct1 park lights
125     MakeWidget({ 10, 84}, {290,  12}, WindowWidgetType::Checkbox,     WindowColour::Secondary, STR_THEMES_OPTION_RCT1_SCENARIO_SELECTION_FONT                                     ), // rct1 scenario font
126     MakeWidget({ 10, 99}, {290,  12}, WindowWidgetType::Checkbox,     WindowColour::Secondary, STR_THEMES_OPTION_RCT1_BOTTOM_TOOLBAR                                              ), // rct1 bottom toolbar
127     WIDGETS_END,
128 };
129 
130 static int32_t window_themes_tab_animation_loops[] = {
131     32,
132     32,
133     1,
134     1,
135     64,
136     32,
137     8,
138     14,
139     38,
140 };
141 static int32_t window_themes_tab_animation_divisor[] = {
142     4,
143     4,
144     1,
145     1,
146     4,
147     2,
148     2,
149     2,
150     2,
151 };
152 static int32_t window_themes_tab_sprites[] = {
153     SPR_TAB_PAINT_0,
154     SPR_TAB_KIOSKS_AND_FACILITIES_0,
155     SPR_TAB_PARK_ENTRANCE,
156     SPR_G2_TAB_LAND,
157     SPR_TAB_RIDE_0,
158     SPR_TAB_WRENCH_0,
159     SPR_TAB_GEARS_0,
160     SPR_TAB_STAFF_OPTIONS_0,
161     SPR_TAB_FINANCES_MARKETING_0,
162 };
163 
164 static rct_windowclass window_themes_tab_1_classes[] = {
165     WC_TOP_TOOLBAR,
166     WC_BOTTOM_TOOLBAR,
167     WC_EDITOR_SCENARIO_BOTTOM_TOOLBAR,
168     WC_EDITOR_TRACK_BOTTOM_TOOLBAR,
169     WC_TITLE_MENU,
170     WC_TITLE_EXIT,
171     WC_TITLE_OPTIONS,
172     WC_SCENARIO_SELECT,
173 };
174 
175 static rct_windowclass window_themes_tab_2_classes[] = {
176     WC_PARK_INFORMATION,
177     WC_FINANCES,
178     WC_NEW_CAMPAIGN,
179     WC_RESEARCH,
180     WC_MAP,
181     WC_VIEWPORT,
182     WC_RECENT_NEWS,
183 };
184 
185 static rct_windowclass window_themes_tab_3_classes[] = {
186     WC_LAND,
187     WC_WATER,
188     WC_CLEAR_SCENERY,
189     WC_LAND_RIGHTS,
190     WC_SCENERY,
191     WC_SCENERY_SCATTER,
192     WC_FOOTPATH,
193     WC_RIDE_CONSTRUCTION,
194     WC_TRACK_DESIGN_PLACE,
195     WC_CONSTRUCT_RIDE,
196     WC_TRACK_DESIGN_LIST,
197 };
198 
199 static rct_windowclass window_themes_tab_4_classes[] = {
200     WC_RIDE,
201     WC_RIDE_LIST,
202     WC_PEEP,
203     WC_GUEST_LIST,
204     WC_STAFF,
205     WC_STAFF_LIST,
206     WC_BANNER,
207 };
208 
209 static rct_windowclass window_themes_tab_5_classes[] = {
210     WC_EDITOR_OBJECT_SELECTION,
211     WC_EDITOR_INVENTION_LIST,
212     WC_EDITOR_SCENARIO_OPTIONS,
213     WC_EDITOR_OBJECTIVE_OPTIONS,
214     WC_MAPGEN,
215     WC_MANAGE_TRACK_DESIGN,
216     WC_INSTALL_TRACK,
217 };
218 
219 static rct_windowclass window_themes_tab_6_classes[] = {
220     WC_CHEATS,
221     WC_TILE_INSPECTOR,
222     WC_VIEW_CLIPPING,
223     WC_THEMES,
224     WC_TITLE_EDITOR,
225     WC_OPTIONS,
226     WC_KEYBOARD_SHORTCUT_LIST,
227     WC_CHANGE_KEYBOARD_SHORTCUT,
228     WC_LOADSAVE,
229     WC_ABOUT,
230     WC_CHANGELOG,
231     WC_SERVER_LIST,
232     WC_MULTIPLAYER,
233     WC_PLAYER,
234     WC_CHAT,
235     WC_CONSOLE,
236 };
237 
238 static rct_windowclass window_themes_tab_7_classes[] = {
239     WC_SAVE_PROMPT,
240     WC_DEMOLISH_RIDE_PROMPT,
241     WC_FIRE_PROMPT,
242     WC_TRACK_DELETE_PROMPT,
243     WC_LOADSAVE_OVERWRITE_PROMPT,
244     WC_NETWORK_STATUS,
245 };
246 // clang-format on
247 
248 static rct_windowclass* window_themes_tab_classes[] = {
249     nullptr,
250     window_themes_tab_1_classes,
251     window_themes_tab_2_classes,
252     window_themes_tab_3_classes,
253     window_themes_tab_4_classes,
254     window_themes_tab_5_classes,
255     window_themes_tab_6_classes,
256     window_themes_tab_7_classes,
257 };
258 
259 static uint8_t _selected_tab = 0;
260 static int16_t _colour_index_1 = -1;
261 static int8_t _colour_index_2 = -1;
262 static constexpr const uint8_t _row_height = 32;
263 static constexpr const uint8_t _button_offset_x = 220;
264 static constexpr const uint8_t _button_offset_y = 3;
265 static constexpr const uint8_t _check_offset_y = 3 + 12 + 2;
266 
window_themes_init_vars()267 static void window_themes_init_vars()
268 {
269     _selected_tab = WINDOW_THEMES_TAB_SETTINGS;
270 }
271 
get_window_class_tab_index(int32_t index)272 static rct_windowclass get_window_class_tab_index(int32_t index)
273 {
274     rct_windowclass* classes = window_themes_tab_classes[_selected_tab];
275     return classes[index];
276 }
277 
get_colour_scheme_tab_count()278 static int32_t get_colour_scheme_tab_count()
279 {
280     switch (_selected_tab)
281     {
282         case 1:
283             return sizeof(window_themes_tab_1_classes);
284         case 2:
285             return sizeof(window_themes_tab_2_classes);
286         case 3:
287             return sizeof(window_themes_tab_3_classes);
288         case 4:
289             return sizeof(window_themes_tab_4_classes);
290         case 5:
291             return sizeof(window_themes_tab_5_classes);
292         case 6:
293             return sizeof(window_themes_tab_6_classes);
294         case 7:
295             return sizeof(window_themes_tab_7_classes);
296     }
297     return 0;
298 }
299 
window_themes_draw_tab_images(rct_drawpixelinfo * dpi,rct_window * w)300 static void window_themes_draw_tab_images(rct_drawpixelinfo* dpi, rct_window* w)
301 {
302     for (int32_t i = 0; i < WINDOW_THEMES_TAB_COUNT; i++)
303     {
304         int32_t sprite_idx = window_themes_tab_sprites[i];
305         if (_selected_tab == i)
306             sprite_idx += w->frame_no / window_themes_tab_animation_divisor[_selected_tab];
307         gfx_draw_sprite(
308             dpi, sprite_idx,
309             w->windowPos
310                 + ScreenCoordsXY{ w->widgets[WIDX_THEMES_SETTINGS_TAB + i].left, w->widgets[WIDX_THEMES_SETTINGS_TAB + i].top },
311             0);
312     }
313 }
314 
window_themes_open()315 rct_window* window_themes_open()
316 {
317     rct_window* window;
318 
319     // Check if window is already open
320     window = window_bring_to_front_by_class(WC_THEMES);
321     if (window != nullptr)
322         return window;
323 
324     window = WindowCreateAutoPos(320, 107, &window_themes_events, WC_THEMES, WF_10 | WF_RESIZABLE);
325     window->widgets = window_themes_widgets;
326     window->enabled_widgets = (1ULL << WIDX_THEMES_CLOSE) | (1ULL << WIDX_THEMES_SETTINGS_TAB)
327         | (1ULL << WIDX_THEMES_MAIN_UI_TAB) | (1ULL << WIDX_THEMES_PARK_TAB) | (1ULL << WIDX_THEMES_TOOLS_TAB)
328         | (1ULL << WIDX_THEMES_RIDE_PEEPS_TAB) | (1ULL << WIDX_THEMES_EDITORS_TAB) | (1ULL << WIDX_THEMES_MISC_TAB)
329         | (1ULL << WIDX_THEMES_PROMPTS_TAB) | (1ULL << WIDX_THEMES_FEATURES_TAB) | (1ULL << WIDX_THEMES_COLOURBTN_MASK)
330         | (1ULL << WIDX_THEMES_PRESETS) | (1ULL << WIDX_THEMES_PRESETS_DROPDOWN) | (1ULL << WIDX_THEMES_DUPLICATE_BUTTON)
331         | (1ULL << WIDX_THEMES_DELETE_BUTTON) | (1ULL << WIDX_THEMES_RENAME_BUTTON) | (1ULL << WIDX_THEMES_RCT1_RIDE_LIGHTS)
332         | (1ULL << WIDX_THEMES_RCT1_PARK_LIGHTS) | (1ULL << WIDX_THEMES_RCT1_SCENARIO_FONT)
333         | (1ULL << WIDX_THEMES_RCT1_BOTTOM_TOOLBAR);
334 
335     window_themes_init_vars();
336 
337     WindowInitScrollWidgets(window);
338     window->list_information_type = 0;
339     _colour_index_1 = -1;
340     _colour_index_2 = -1;
341     window->min_width = 320;
342     window->min_height = 107;
343     window->max_width = 320;
344     window->max_height = 107;
345 
346     return window;
347 }
348 
window_themes_mouseup(rct_window * w,rct_widgetindex widgetIndex)349 static void window_themes_mouseup(rct_window* w, rct_widgetindex widgetIndex)
350 {
351     size_t activeAvailableThemeIndex;
352     const utf8* activeThemeName;
353 
354     switch (widgetIndex)
355     {
356         case WIDX_THEMES_CLOSE:
357             window_close(w);
358             break;
359         case WIDX_THEMES_DUPLICATE_BUTTON:;
360             activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex();
361             activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex);
362             window_text_input_open(
363                 w, widgetIndex, STR_TITLE_EDITOR_ACTION_DUPLICATE, STR_THEMES_PROMPT_ENTER_THEME_NAME, {}, STR_STRING,
364                 reinterpret_cast<uintptr_t>(activeThemeName), 64);
365             break;
366         case WIDX_THEMES_DELETE_BUTTON:
367             if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED)
368             {
369                 context_show_error(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {});
370             }
371             else
372             {
373                 ThemeDelete();
374             }
375             break;
376         case WIDX_THEMES_RENAME_BUTTON:
377             if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED)
378             {
379                 context_show_error(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {});
380             }
381             else
382             {
383                 activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex();
384                 activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex);
385                 window_text_input_open(
386                     w, widgetIndex, STR_TRACK_MANAGE_RENAME, STR_THEMES_PROMPT_ENTER_THEME_NAME, {}, STR_STRING,
387                     reinterpret_cast<uintptr_t>(activeThemeName), 64);
388             }
389             break;
390     }
391 }
392 
window_themes_resize(rct_window * w)393 static void window_themes_resize(rct_window* w)
394 {
395     if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS)
396     {
397         w->min_width = 320;
398         w->min_height = 107;
399         w->max_width = 320;
400         w->max_height = 107;
401 
402         if (w->width < w->min_width)
403         {
404             w->width = w->min_width;
405             gfx_invalidate_screen();
406         }
407         if (w->height < w->min_height)
408         {
409             w->height = w->min_height;
410             gfx_invalidate_screen();
411         }
412         if (w->width > w->max_width)
413         {
414             w->width = w->max_width;
415             gfx_invalidate_screen();
416         }
417         if (w->height > w->max_height)
418         {
419             w->height = w->max_height;
420             gfx_invalidate_screen();
421         }
422     }
423     else if (_selected_tab == WINDOW_THEMES_TAB_FEATURES)
424     {
425         w->min_width = 320;
426         w->min_height = 122;
427         w->max_width = 320;
428         w->max_height = 122;
429 
430         if (w->width < w->min_width)
431         {
432             w->width = w->min_width;
433             gfx_invalidate_screen();
434         }
435         if (w->height < w->min_height)
436         {
437             w->height = w->min_height;
438             gfx_invalidate_screen();
439         }
440         if (w->width > w->max_width)
441         {
442             w->width = w->max_width;
443             gfx_invalidate_screen();
444         }
445         if (w->height > w->max_height)
446         {
447             w->height = w->max_height;
448             gfx_invalidate_screen();
449         }
450     }
451     else
452     {
453         w->min_width = 320;
454         w->min_height = 270;
455         w->max_width = 320;
456         w->max_height = 450;
457 
458         if (w->width < w->min_width)
459         {
460             w->width = w->min_width;
461             w->Invalidate();
462         }
463         if (w->height < w->min_height)
464         {
465             w->height = w->min_height;
466             w->Invalidate();
467         }
468         if (w->width > w->max_width)
469         {
470             w->width = w->max_width;
471             w->Invalidate();
472         }
473         if (w->height > w->max_height)
474         {
475             w->height = w->max_height;
476             w->Invalidate();
477         }
478     }
479 }
480 
window_themes_mousedown(rct_window * w,rct_widgetindex widgetIndex,rct_widget * widget)481 static void window_themes_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
482 {
483     int16_t newSelectedTab;
484     int32_t num_items;
485 
486     switch (widgetIndex)
487     {
488         case WIDX_THEMES_SETTINGS_TAB:
489         case WIDX_THEMES_MAIN_UI_TAB:
490         case WIDX_THEMES_PARK_TAB:
491         case WIDX_THEMES_TOOLS_TAB:
492         case WIDX_THEMES_RIDE_PEEPS_TAB:
493         case WIDX_THEMES_EDITORS_TAB:
494         case WIDX_THEMES_MISC_TAB:
495         case WIDX_THEMES_PROMPTS_TAB:
496         case WIDX_THEMES_FEATURES_TAB:
497             newSelectedTab = widgetIndex - WIDX_THEMES_SETTINGS_TAB;
498             if (_selected_tab == newSelectedTab)
499                 break;
500             _selected_tab = static_cast<uint8_t>(newSelectedTab);
501             w->scrolls[0].v_top = 0;
502             w->frame_no = 0;
503             window_event_resize_call(w);
504             w->Invalidate();
505             break;
506         case WIDX_THEMES_PRESETS_DROPDOWN:
507             ThemeManagerLoadAvailableThemes();
508             num_items = static_cast<int32_t>(ThemeManagerGetNumAvailableThemes());
509 
510             widget--;
511             for (int32_t i = 0; i < num_items; i++)
512             {
513                 gDropdownItemsFormat[i] = STR_OPTIONS_DROPDOWN_ITEM;
514                 gDropdownItemsArgs[i] = reinterpret_cast<uintptr_t>(ThemeManagerGetAvailableThemeName(i));
515             }
516 
517             WindowDropdownShowTextCustomWidth(
518                 { w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], 0,
519                 Dropdown::Flag::StayOpen, num_items, widget->width() - 3);
520 
521             Dropdown::SetChecked(static_cast<int32_t>(ThemeManagerGetAvailableThemeIndex()), true);
522             break;
523         case WIDX_THEMES_RCT1_RIDE_LIGHTS:
524             if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED)
525             {
526                 context_show_error(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {});
527             }
528             else
529             {
530                 ThemeSetFlags(ThemeGetFlags() ^ UITHEME_FLAG_USE_LIGHTS_RIDE);
531                 ThemeSave();
532                 window_invalidate_all();
533             }
534             break;
535         case WIDX_THEMES_RCT1_PARK_LIGHTS:
536             if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED)
537             {
538                 context_show_error(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {});
539             }
540             else
541             {
542                 ThemeSetFlags(ThemeGetFlags() ^ UITHEME_FLAG_USE_LIGHTS_PARK);
543                 ThemeSave();
544                 window_invalidate_all();
545             }
546             break;
547         case WIDX_THEMES_RCT1_SCENARIO_FONT:
548             if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED)
549             {
550                 context_show_error(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {});
551             }
552             else
553             {
554                 ThemeSetFlags(ThemeGetFlags() ^ UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT);
555                 ThemeSave();
556                 window_invalidate_all();
557             }
558             break;
559         case WIDX_THEMES_RCT1_BOTTOM_TOOLBAR:
560             if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED)
561             {
562                 context_show_error(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_NONE, {});
563             }
564             else
565             {
566                 ThemeSetFlags(ThemeGetFlags() ^ UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR);
567                 ThemeSave();
568                 window_invalidate_all();
569             }
570     }
571 }
572 
window_themes_dropdown(rct_window * w,rct_widgetindex widgetIndex,int32_t dropdownIndex)573 static void window_themes_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
574 {
575     switch (widgetIndex)
576     {
577         case WIDX_THEMES_LIST:
578             if (dropdownIndex != -1)
579             {
580                 rct_windowclass wc = get_window_class_tab_index(_colour_index_1);
581                 uint8_t colour = ThemeGetColour(wc, _colour_index_2);
582                 colour = (colour & COLOUR_FLAG_TRANSLUCENT) | dropdownIndex;
583                 ThemeSetColour(wc, _colour_index_2, colour);
584                 ColourSchemeUpdateAll();
585                 window_invalidate_all();
586                 _colour_index_1 = -1;
587                 _colour_index_2 = -1;
588             }
589             break;
590         case WIDX_THEMES_PRESETS_DROPDOWN:
591             if (dropdownIndex != -1)
592             {
593                 ThemeManagerSetActiveAvailableTheme(dropdownIndex);
594             }
595             break;
596     }
597 }
598 
window_themes_update(rct_window * w)599 void window_themes_update(rct_window* w)
600 {
601     w->frame_no++;
602     if (w->frame_no >= window_themes_tab_animation_loops[_selected_tab])
603         w->frame_no = 0;
604 
605     widget_invalidate(w, WIDX_THEMES_SETTINGS_TAB + _selected_tab);
606 }
607 
window_themes_scrollgetsize(rct_window * w,int32_t scrollIndex,int32_t * width,int32_t * height)608 void window_themes_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height)
609 {
610     if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS || _selected_tab == WINDOW_THEMES_TAB_FEATURES)
611         return;
612 
613     int32_t scrollHeight = get_colour_scheme_tab_count() * _row_height;
614     int32_t i = scrollHeight - window_themes_widgets[WIDX_THEMES_LIST].bottom + window_themes_widgets[WIDX_THEMES_LIST].top
615         + 21;
616     if (i < 0)
617         i = 0;
618     if (i < w->scrolls[0].v_top)
619     {
620         w->scrolls[0].v_top = i;
621         w->Invalidate();
622     }
623 
624     *width = 420;
625     *height = scrollHeight;
626 }
627 
window_themes_scrollmousedown(rct_window * w,int32_t scrollIndex,const ScreenCoordsXY & screenCoords)628 void window_themes_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
629 {
630     if (screenCoords.y / _row_height < get_colour_scheme_tab_count())
631     {
632         int32_t y2 = screenCoords.y % _row_height;
633         _colour_index_1 = screenCoords.y / _row_height;
634         _colour_index_2 = ((screenCoords.x - _button_offset_x) / 12);
635 
636         rct_windowclass wc = get_window_class_tab_index(_colour_index_1);
637         int32_t numColours = ThemeDescGetNumColours(wc);
638         if (_colour_index_2 < numColours)
639         {
640             if (screenCoords.x >= _button_offset_x && screenCoords.x < _button_offset_x + 12 * 6 && y2 >= _button_offset_y
641                 && y2 < _button_offset_y + 11)
642             {
643                 if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED)
644                 {
645                     context_show_error(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_THEMES_DESC_CANT_CHANGE_THIS_THEME, {});
646                 }
647                 else
648                 {
649                     window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::ColourBtn;
650                     window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK].left = _button_offset_x + _colour_index_2 * 12
651                         + window_themes_widgets[WIDX_THEMES_LIST].left;
652                     window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK].top = _colour_index_1 * _row_height + _button_offset_y
653                         - w->scrolls[0].v_top + window_themes_widgets[WIDX_THEMES_LIST].top;
654                     window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK].right = window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK]
655                                                                                   .left
656                         + 12;
657                     window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK].bottom = window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK]
658                                                                                    .top
659                         + 12;
660 
661                     uint8_t colour = ThemeGetColour(wc, _colour_index_2);
662                     WindowDropdownShowColour(w, &(window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK]), w->colours[1], colour);
663                     widget_invalidate(w, WIDX_THEMES_LIST);
664                 }
665             }
666             else if (
667                 screenCoords.x >= _button_offset_x && screenCoords.x < _button_offset_x + 12 * 6 - 1 && y2 >= _check_offset_y
668                 && y2 < _check_offset_y + 11)
669             {
670                 if (ThemeGetFlags() & UITHEME_FLAG_PREDEFINED)
671                 {
672                     context_show_error(STR_THEMES_ERR_CANT_CHANGE_THIS_THEME, STR_THEMES_DESC_CANT_CHANGE_THIS_THEME, {});
673                 }
674                 else
675                 {
676                     uint8_t colour = ThemeGetColour(wc, _colour_index_2);
677                     if (colour & COLOUR_FLAG_TRANSLUCENT)
678                     {
679                         colour &= ~COLOUR_FLAG_TRANSLUCENT;
680                     }
681                     else
682                     {
683                         colour |= COLOUR_FLAG_TRANSLUCENT;
684                     }
685                     ThemeSetColour(wc, _colour_index_2, colour);
686                     ColourSchemeUpdateAll();
687                     window_invalidate_all();
688                 }
689             }
690         }
691     }
692 }
693 
window_themes_scrollmouseover(rct_window * w,int32_t scrollIndex,const ScreenCoordsXY & screenCoords)694 void window_themes_scrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
695 {
696 }
697 
window_themes_textinput(rct_window * w,rct_widgetindex widgetIndex,char * text)698 static void window_themes_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text)
699 {
700     if (text == nullptr || text[0] == 0)
701         return;
702 
703     switch (widgetIndex)
704     {
705         case WIDX_THEMES_DUPLICATE_BUTTON:
706         case WIDX_THEMES_RENAME_BUTTON:
707             if (filename_valid_characters(text))
708             {
709                 if (ThemeGetIndexForName(text) == SIZE_MAX)
710                 {
711                     if (widgetIndex == WIDX_THEMES_DUPLICATE_BUTTON)
712                     {
713                         ThemeDuplicate(text);
714                     }
715                     else
716                     {
717                         ThemeRename(text);
718                     }
719                     w->Invalidate();
720                 }
721                 else
722                 {
723                     context_show_error(STR_THEMES_ERR_NAME_ALREADY_EXISTS, STR_NONE, {});
724                 }
725             }
726             else
727             {
728                 context_show_error(STR_ERROR_INVALID_CHARACTERS, STR_NONE, {});
729             }
730             break;
731     }
732 }
733 
window_themes_invalidate(rct_window * w)734 void window_themes_invalidate(rct_window* w)
735 {
736     int32_t pressed_widgets = w->pressed_widgets
737         & ~((1LL << WIDX_THEMES_SETTINGS_TAB) | (1LL << WIDX_THEMES_MAIN_UI_TAB) | (1LL << WIDX_THEMES_PARK_TAB)
738             | (1LL << WIDX_THEMES_TOOLS_TAB) | (1LL << WIDX_THEMES_RIDE_PEEPS_TAB) | (1LL << WIDX_THEMES_EDITORS_TAB)
739             | (1LL << WIDX_THEMES_MISC_TAB) | (1LL << WIDX_THEMES_PROMPTS_TAB) | (1LL << WIDX_THEMES_FEATURES_TAB));
740     rct_widgetindex widgetIndex = _selected_tab + WIDX_THEMES_SETTINGS_TAB;
741 
742     w->pressed_widgets = pressed_widgets | (1 << widgetIndex);
743 
744     if (window_find_by_class(WC_DROPDOWN) == nullptr)
745     {
746         _colour_index_1 = -1;
747         _colour_index_2 = -1;
748     }
749 
750     window_themes_widgets[WIDX_THEMES_BACKGROUND].right = w->width - 1;
751     window_themes_widgets[WIDX_THEMES_BACKGROUND].bottom = w->height - 1;
752     window_themes_widgets[WIDX_THEMES_TAB_CONTENT_PANEL].right = w->width - 1;
753     window_themes_widgets[WIDX_THEMES_TAB_CONTENT_PANEL].bottom = w->height - 1;
754     window_themes_widgets[WIDX_THEMES_TITLE].right = w->width - 2;
755     window_themes_widgets[WIDX_THEMES_CLOSE].left = w->width - 2 - 0x0B;
756     window_themes_widgets[WIDX_THEMES_CLOSE].right = w->width - 2 - 0x0B + 0x0A;
757     window_themes_widgets[WIDX_THEMES_LIST].right = w->width - 4;
758     window_themes_widgets[WIDX_THEMES_LIST].bottom = w->height - 0x0F;
759 
760     if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS)
761     {
762         window_themes_widgets[WIDX_THEMES_HEADER_WINDOW].type = WindowWidgetType::Empty;
763         window_themes_widgets[WIDX_THEMES_HEADER_PALETTE].type = WindowWidgetType::Empty;
764         window_themes_widgets[WIDX_THEMES_LIST].type = WindowWidgetType::Empty;
765         window_themes_widgets[WIDX_THEMES_RCT1_RIDE_LIGHTS].type = WindowWidgetType::Empty;
766         window_themes_widgets[WIDX_THEMES_RCT1_PARK_LIGHTS].type = WindowWidgetType::Empty;
767         window_themes_widgets[WIDX_THEMES_RCT1_SCENARIO_FONT].type = WindowWidgetType::Empty;
768         window_themes_widgets[WIDX_THEMES_RCT1_BOTTOM_TOOLBAR].type = WindowWidgetType::Empty;
769         window_themes_widgets[WIDX_THEMES_DUPLICATE_BUTTON].type = WindowWidgetType::Button;
770         window_themes_widgets[WIDX_THEMES_DELETE_BUTTON].type = WindowWidgetType::Button;
771         window_themes_widgets[WIDX_THEMES_RENAME_BUTTON].type = WindowWidgetType::Button;
772         window_themes_widgets[WIDX_THEMES_PRESETS].type = WindowWidgetType::DropdownMenu;
773         window_themes_widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WindowWidgetType::Button;
774         window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::Empty;
775     }
776     else if (_selected_tab == WINDOW_THEMES_TAB_FEATURES)
777     {
778         window_themes_widgets[WIDX_THEMES_HEADER_WINDOW].type = WindowWidgetType::Empty;
779         window_themes_widgets[WIDX_THEMES_HEADER_PALETTE].type = WindowWidgetType::Empty;
780         window_themes_widgets[WIDX_THEMES_LIST].type = WindowWidgetType::Empty;
781         window_themes_widgets[WIDX_THEMES_RCT1_RIDE_LIGHTS].type = WindowWidgetType::Checkbox;
782         window_themes_widgets[WIDX_THEMES_RCT1_PARK_LIGHTS].type = WindowWidgetType::Checkbox;
783         window_themes_widgets[WIDX_THEMES_RCT1_SCENARIO_FONT].type = WindowWidgetType::Checkbox;
784         window_themes_widgets[WIDX_THEMES_RCT1_BOTTOM_TOOLBAR].type = WindowWidgetType::Checkbox;
785         window_themes_widgets[WIDX_THEMES_DUPLICATE_BUTTON].type = WindowWidgetType::Empty;
786         window_themes_widgets[WIDX_THEMES_DELETE_BUTTON].type = WindowWidgetType::Empty;
787         window_themes_widgets[WIDX_THEMES_RENAME_BUTTON].type = WindowWidgetType::Empty;
788         window_themes_widgets[WIDX_THEMES_PRESETS].type = WindowWidgetType::Empty;
789         window_themes_widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WindowWidgetType::Empty;
790         window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::Empty;
791 
792         WidgetSetCheckboxValue(w, WIDX_THEMES_RCT1_RIDE_LIGHTS, ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_RIDE);
793         WidgetSetCheckboxValue(w, WIDX_THEMES_RCT1_PARK_LIGHTS, ThemeGetFlags() & UITHEME_FLAG_USE_LIGHTS_PARK);
794         WidgetSetCheckboxValue(
795             w, WIDX_THEMES_RCT1_SCENARIO_FONT, ThemeGetFlags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT);
796         WidgetSetCheckboxValue(w, WIDX_THEMES_RCT1_BOTTOM_TOOLBAR, ThemeGetFlags() & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR);
797     }
798     else
799     {
800         window_themes_widgets[WIDX_THEMES_HEADER_WINDOW].type = WindowWidgetType::TableHeader;
801         window_themes_widgets[WIDX_THEMES_HEADER_PALETTE].type = WindowWidgetType::TableHeader;
802         window_themes_widgets[WIDX_THEMES_LIST].type = WindowWidgetType::Scroll;
803         window_themes_widgets[WIDX_THEMES_RCT1_RIDE_LIGHTS].type = WindowWidgetType::Empty;
804         window_themes_widgets[WIDX_THEMES_RCT1_PARK_LIGHTS].type = WindowWidgetType::Empty;
805         window_themes_widgets[WIDX_THEMES_RCT1_SCENARIO_FONT].type = WindowWidgetType::Empty;
806         window_themes_widgets[WIDX_THEMES_RCT1_BOTTOM_TOOLBAR].type = WindowWidgetType::Empty;
807         window_themes_widgets[WIDX_THEMES_DUPLICATE_BUTTON].type = WindowWidgetType::Empty;
808         window_themes_widgets[WIDX_THEMES_DELETE_BUTTON].type = WindowWidgetType::Empty;
809         window_themes_widgets[WIDX_THEMES_RENAME_BUTTON].type = WindowWidgetType::Empty;
810         window_themes_widgets[WIDX_THEMES_PRESETS].type = WindowWidgetType::Empty;
811         window_themes_widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WindowWidgetType::Empty;
812         window_themes_widgets[WIDX_THEMES_COLOURBTN_MASK].type = WindowWidgetType::Empty;
813     }
814 }
815 
window_themes_paint(rct_window * w,rct_drawpixelinfo * dpi)816 void window_themes_paint(rct_window* w, rct_drawpixelinfo* dpi)
817 {
818     // Widgets
819     WindowDrawWidgets(w, dpi);
820     window_themes_draw_tab_images(dpi, w);
821 
822     if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS)
823     {
824         DrawTextBasic(
825             dpi, w->windowPos + ScreenCoordsXY{ 10, window_themes_widgets[WIDX_THEMES_PRESETS].top + 1 },
826             STR_THEMES_LABEL_CURRENT_THEME, {}, { w->colours[1] });
827 
828         size_t activeAvailableThemeIndex = ThemeManagerGetAvailableThemeIndex();
829         const utf8* activeThemeName = ThemeManagerGetAvailableThemeName(activeAvailableThemeIndex);
830         auto ft = Formatter();
831         ft.Add<const utf8*>(activeThemeName);
832 
833         auto screenPos = w->windowPos
834             + ScreenCoordsXY{ window_themes_widgets[WIDX_THEMES_PRESETS].left + 1,
835                               window_themes_widgets[WIDX_THEMES_PRESETS].top };
836         auto width = w->windowPos.x + window_themes_widgets[WIDX_THEMES_PRESETS_DROPDOWN].left
837             - window_themes_widgets[WIDX_THEMES_PRESETS].left - 4;
838 
839         DrawTextEllipsised(dpi, screenPos, width, STR_STRING, ft, { w->colours[1] });
840     }
841 }
842 
843 /**
844  *
845  *  rct2: 0x006BD785
846  */
window_themes_scrollpaint(rct_window * w,rct_drawpixelinfo * dpi,int32_t scrollIndex)847 void window_themes_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
848 {
849     ScreenCoordsXY screenCoords;
850 
851     if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS || _selected_tab == WINDOW_THEMES_TAB_FEATURES)
852         return;
853 
854     if ((w->colours[1] & 0x80) == 0)
855         // gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1,
856         // ColourMapA[w->colours[1]].mid_light);
857         gfx_clear(dpi, ColourMapA[w->colours[1]].mid_light);
858     screenCoords.y = 0;
859     for (int32_t i = 0; i < get_colour_scheme_tab_count(); i++)
860     {
861         if (screenCoords.y > dpi->y + dpi->height)
862         {
863             break;
864         }
865         if (screenCoords.y + _row_height >= dpi->y)
866         {
867             if (i + 1 < get_colour_scheme_tab_count())
868             {
869                 int32_t colour = w->colours[1];
870 
871                 auto leftTop = ScreenCoordsXY{ 0, screenCoords.y + _row_height - 2 };
872                 auto rightBottom = ScreenCoordsXY{ window_themes_widgets[WIDX_THEMES_LIST].right,
873                                                    screenCoords.y + _row_height - 2 };
874                 auto yPixelOffset = ScreenCoordsXY{ 0, 1 };
875 
876                 if (colour & COLOUR_FLAG_TRANSLUCENT)
877                 {
878                     translucent_window_palette windowPalette = TranslucentWindowPalettes[BASE_COLOUR(colour)];
879 
880                     gfx_filter_rect(dpi, { leftTop, rightBottom }, windowPalette.highlight);
881                     gfx_filter_rect(dpi, { leftTop + yPixelOffset, rightBottom + yPixelOffset }, windowPalette.shadow);
882                 }
883                 else
884                 {
885                     colour = ColourMapA[w->colours[1]].mid_dark;
886                     gfx_fill_rect(dpi, { leftTop, rightBottom }, colour);
887 
888                     colour = ColourMapA[w->colours[1]].lightest;
889                     gfx_fill_rect(dpi, { leftTop + yPixelOffset, rightBottom + yPixelOffset }, colour);
890                 }
891             }
892 
893             rct_windowclass wc = get_window_class_tab_index(i);
894             int32_t numColours = ThemeDescGetNumColours(wc);
895             for (uint8_t j = 0; j < numColours; j++)
896             {
897                 DrawTextBasic(dpi, { 2, screenCoords.y + 4 }, ThemeDescGetName(wc), {}, { w->colours[1] });
898 
899                 uint8_t colour = ThemeGetColour(wc, j);
900                 uint32_t image = SPRITE_ID_PALETTE_COLOUR_1(colour & ~COLOUR_FLAG_TRANSLUCENT) | SPR_PALETTE_BTN;
901                 if (i == _colour_index_1 && j == _colour_index_2)
902                 {
903                     image = SPRITE_ID_PALETTE_COLOUR_1(colour & ~COLOUR_FLAG_TRANSLUCENT) | SPR_PALETTE_BTN_PRESSED;
904                 }
905                 gfx_draw_sprite(dpi, image, { _button_offset_x + 12 * j, screenCoords.y + _button_offset_y }, 0);
906 
907                 ScreenCoordsXY topLeft{ _button_offset_x + 12 * j, screenCoords.y + _check_offset_y };
908                 ScreenCoordsXY bottomRight{ _button_offset_x + 12 * j + 9, screenCoords.y + _check_offset_y + 10 };
909                 gfx_fill_rect_inset(dpi, { topLeft, bottomRight }, w->colours[1], INSET_RECT_F_E0);
910                 if (colour & COLOUR_FLAG_TRANSLUCENT)
911                 {
912                     gfx_draw_string(
913                         dpi, topLeft, static_cast<const char*>(CheckBoxMarkString),
914                         { static_cast<colour_t>(w->colours[1] & 0x7F), FontSpriteBase::MEDIUM_DARK });
915                 }
916             }
917         }
918 
919         screenCoords.y += _row_height;
920     }
921 }
922