diff --git a/src/openrct2-ui/windows/NewsOptions.cpp b/src/openrct2-ui/windows/NewsOptions.cpp index 40b83bb73f..da35f4747c 100644 --- a/src/openrct2-ui/windows/NewsOptions.cpp +++ b/src/openrct2-ui/windows/NewsOptions.cpp @@ -8,12 +8,10 @@ *****************************************************************************/ #include -#include #include #include #include #include -#include #include static constexpr const rct_string_id WINDOW_TITLE = STR_NOTIFICATION_SETTINGS; @@ -21,13 +19,16 @@ static constexpr const int32_t WH = 300; static constexpr const int32_t WW = 400; // clang-format off -enum { +enum +{ NOTIFICATION_CATEGORY_PARK, NOTIFICATION_CATEGORY_RIDE, - NOTIFICATION_CATEGORY_GUEST + NOTIFICATION_CATEGORY_GUEST, + NOTIFICATION_CATEGORY_COUNT }; -struct NotificationDef { +struct NotificationDef +{ uint8_t category; rct_string_id caption; size_t config_offset; @@ -54,23 +55,28 @@ static constexpr const NotificationDef NewsItemOptionDefinitions[] = { { NOTIFICATION_CATEGORY_GUEST, STR_NOTIFICATION_GUEST_DIED, offsetof(NotificationConfiguration, guest_died) }, }; -enum WindowNewsOptionsWidgetIdx { +enum WindowNewsOptionsWidgetIdx +{ WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE, WIDX_TAB_CONTENT_PANEL, - WIDX_TAB_PARK, + WIDX_FIRST_TAB, + WIDX_TAB_PARK = WIDX_FIRST_TAB, WIDX_TAB_RIDE, WIDX_TAB_GUEST, WIDX_CHECKBOX_0 }; -static rct_widget window_news_options_widgets[] = { +constexpr int MAIN_NEWS_OPTIONS_ENABLED_WIDGETS = + (1ULL << WIDX_CLOSE) | (1ULL << WIDX_TAB_PARK) | (1ULL << WIDX_TAB_RIDE) | (1ULL << WIDX_TAB_GUEST); + +static rct_widget WindowNewsOptionsWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), - MakeWidget({ 0, 43}, {400, 257}, WindowWidgetType::Resize, WindowColour::Secondary), // tab content panel - MakeTab ({ 3, 17} ), // tab 1 - MakeTab ({34, 17} ), // tab 2 - MakeTab ({65, 17} ), // tab 2 + MakeWidget({ 0, 43}, {400, 257}, WindowWidgetType::Resize, WindowColour::Secondary), // Tab content panel + MakeTab ({ 3, 17} ), // Park tab + MakeTab ({34, 17} ), // Ride tab + MakeTab ({65, 17} ), // Guest tab MakeWidget({ 7, 49}, {343, 14}, WindowWidgetType::Checkbox, WindowColour::Tertiary ), MakeWidget({ 0, 0}, {343, 14}, WindowWidgetType::Checkbox, WindowColour::Tertiary ), MakeWidget({ 0, 0}, {343, 14}, WindowWidgetType::Checkbox, WindowColour::Tertiary ), @@ -82,212 +88,198 @@ static rct_widget window_news_options_widgets[] = { MakeWidget({ 0, 0}, {343, 14}, WindowWidgetType::Checkbox, WindowColour::Tertiary ), WIDGETS_END, }; - -static void WindowNewsOptionsMouseup(rct_window *w, rct_widgetindex widgetIndex); -static void WindowNewsOptionsUpdate(rct_window *w); -static void WindowNewsOptionsInvalidate(rct_window *w); -static void WindowNewsOptionsPaint(rct_window *w, rct_drawpixelinfo *dpi); - -static rct_window_event_list window_news_options_events([](auto& events) -{ - events.mouse_up = &WindowNewsOptionsMouseup; - events.update = &WindowNewsOptionsUpdate; - events.invalidate = &WindowNewsOptionsInvalidate; - events.paint = &WindowNewsOptionsPaint; -}); // clang-format on -static void WindowNewsOptionsSetPage(rct_window* w, int32_t page); -static void WindowNewsOptionsDrawTabImages(rct_window* w, rct_drawpixelinfo* dpi); -static bool* GetNotificationValuePtr(const NotificationDef* ndef); +class NewsOptionsWindow final : public Window +{ +public: + void OnOpen() override + { + widgets = WindowNewsOptionsWidgets; + enabled_widgets = MAIN_NEWS_OPTIONS_ENABLED_WIDGETS; + InitScrollWidgets(); + colours[0] = COLOUR_GREY; + colours[1] = COLOUR_LIGHT_BLUE; + colours[2] = COLOUR_LIGHT_BLUE; + } + + void OnMouseUp(rct_widgetindex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_TAB_PARK: + case WIDX_TAB_RIDE: + case WIDX_TAB_GUEST: + SetPage(widgetIndex - WIDX_FIRST_TAB); + break; + default: + { + int32_t checkBoxIndex = widgetIndex - WIDX_CHECKBOX_0; + if (checkBoxIndex >= 0) + { + int32_t matchIndex = 0; + for (size_t i = 0; i < std::size(NewsItemOptionDefinitions); i++) + { + const NotificationDef* ndef = &NewsItemOptionDefinitions[i]; + if (ndef->category != page) + continue; + + if (matchIndex == checkBoxIndex) + { + // Toggle value + bool* configValue = GetNotificationValuePtr(ndef); + *configValue = !(*configValue); + + config_save_default(); + + InvalidateWidget(widgetIndex); + break; + } + matchIndex++; + } + } + break; + } + } + } + + void OnUpdate() override + { + // Tab animation + frame_no++; + InvalidateWidget(WIDX_FIRST_TAB + page); + } + + void OnPrepareDraw() override + { + SetPressedTab(); + + // Set checkboxes + const auto& baseCheckBox = widgets[WIDX_CHECKBOX_0]; + int32_t y = baseCheckBox.top; + + int32_t checkboxWidgetIndex = WIDX_CHECKBOX_0; + rct_widget* checkboxWidget = &widgets[checkboxWidgetIndex]; + for (size_t i = 0; i < std::size(NewsItemOptionDefinitions); i++) + { + const NotificationDef* ndef = &NewsItemOptionDefinitions[i]; + if (ndef->category != page) + continue; + + enabled_widgets |= (1ULL << checkboxWidgetIndex); + + checkboxWidget->type = WindowWidgetType::Checkbox; + checkboxWidget->left = baseCheckBox.left; + checkboxWidget->right = baseCheckBox.right; + checkboxWidget->top = y; + checkboxWidget->bottom = checkboxWidget->top + LIST_ROW_HEIGHT + 3; + checkboxWidget->text = ndef->caption; + + const bool* configValue = GetNotificationValuePtr(ndef); + SetCheckboxValue(checkboxWidgetIndex, *configValue); + + checkboxWidgetIndex++; + checkboxWidget++; + y += LIST_ROW_HEIGHT + 3; + } + + // Remove unused checkboxes + while (checkboxWidget->type != WindowWidgetType::Last) + { + enabled_widgets &= ~(1ULL << checkboxWidgetIndex); + + checkboxWidget->type = WindowWidgetType::Empty; + checkboxWidgetIndex++; + checkboxWidget++; + } + + // Resize window to fit checkboxes exactly + y += 3; + + if (height != y) + { + Invalidate(); + height = y; + widgets[WIDX_BACKGROUND].bottom = y - 1; + widgets[WIDX_TAB_CONTENT_PANEL].bottom = y - 1; + Invalidate(); + } + } + + void OnDraw(rct_drawpixelinfo& dpi) override + { + DrawWidgets(dpi); + DrawTabImages(&dpi); + } + +private: + void SetPage(int32_t p) + { + if (page != p) + { + page = p; + frame_no = 0; + Invalidate(); + } + } + + void DrawTabImages(rct_drawpixelinfo* dpi) + { + DrawTabImage(dpi, NOTIFICATION_CATEGORY_PARK, SPR_TAB_PARK); + DrawTabImage(dpi, NOTIFICATION_CATEGORY_RIDE, SPR_TAB_RIDE_0); + DrawTabImage(dpi, NOTIFICATION_CATEGORY_GUEST, SPR_TAB_GUESTS_0); + } + + void DrawTabImage(rct_drawpixelinfo* dpi, int32_t p, int32_t spriteIndex) + { + rct_widgetindex widgetIndex = WIDX_FIRST_TAB + p; + + if (!(disabled_widgets & (1LL << widgetIndex))) + { + if (page == p) + { + int32_t numFrames = TabAnimationFrames[page]; + if (numFrames > 1) + { + int32_t frame = frame_no / TabAnimationDivisor[page]; + spriteIndex += (frame % numFrames); + } + } + + const auto& widget = widgets[widgetIndex]; + gfx_draw_sprite(dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widget.left, widget.top }); + } + } + + void SetPressedTab() + { + for (int32_t i = 0; i < NOTIFICATION_CATEGORY_COUNT; i++) + pressed_widgets &= ~(1 << (WIDX_FIRST_TAB + i)); + pressed_widgets |= 1LL << (WIDX_FIRST_TAB + page); + } + + bool* GetNotificationValuePtr(const NotificationDef* ndef) + { + bool* configValue = reinterpret_cast(reinterpret_cast(&gConfigNotifications) + ndef->config_offset); + return configValue; + } + + static constexpr int32_t TabAnimationDivisor[3] = { + 1, // Park + 4, // Ride + 4, // Guest + }; + static constexpr int32_t TabAnimationFrames[3] = { + 1, // Park + 16, // Ride + 8, // Guest + }; +}; rct_window* WindowNewsOptionsOpen() { - rct_window* window; - - // Check if window is already open - window = window_bring_to_front_by_class(WC_NOTIFICATION_OPTIONS); - if (window == nullptr) - { - window = WindowCreateCentred(400, 300, &window_news_options_events, WC_NOTIFICATION_OPTIONS, 0); - window->widgets = window_news_options_widgets; - window->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_TAB_PARK) | (1ULL << WIDX_TAB_RIDE) - | (1ULL << WIDX_TAB_GUEST); - WindowInitScrollWidgets(window); - window->colours[0] = COLOUR_GREY; - window->colours[1] = COLOUR_LIGHT_BLUE; - window->colours[2] = COLOUR_LIGHT_BLUE; - } - - return window; -} - -static void WindowNewsOptionsMouseup(rct_window* w, rct_widgetindex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - window_close(w); - break; - case WIDX_TAB_PARK: - case WIDX_TAB_RIDE: - case WIDX_TAB_GUEST: - WindowNewsOptionsSetPage(w, widgetIndex - WIDX_TAB_PARK); - break; - default: - { - int32_t checkBoxIndex = widgetIndex - WIDX_CHECKBOX_0; - if (checkBoxIndex >= 0) - { - int32_t matchIndex = 0; - for (size_t i = 0; i < std::size(NewsItemOptionDefinitions); i++) - { - const NotificationDef* ndef = &NewsItemOptionDefinitions[i]; - if (ndef->category != w->page) - continue; - - if (matchIndex == checkBoxIndex) - { - // Toggle value - bool* configValue = GetNotificationValuePtr(ndef); - *configValue = !(*configValue); - - config_save_default(); - - widget_invalidate(w, widgetIndex); - break; - } - matchIndex++; - } - } - break; - } - } -} - -static void WindowNewsOptionsUpdate(rct_window* w) -{ - w->frame_no++; - widget_invalidate(w, WIDX_TAB_PARK + w->page); -} - -static void WindowNewsOptionsInvalidate(rct_window* w) -{ - // Set pressed tab - w->pressed_widgets &= ~(1ULL << WIDX_TAB_PARK); - w->pressed_widgets &= ~(1ULL << WIDX_TAB_RIDE); - w->pressed_widgets &= ~(1ULL << WIDX_TAB_GUEST); - w->pressed_widgets |= (1ULL << (WIDX_TAB_PARK + w->page)); - - // Set checkboxes - const auto& baseCheckBox = w->widgets[WIDX_CHECKBOX_0]; - int32_t y = baseCheckBox.top; - - int32_t checkboxWidgetIndex = WIDX_CHECKBOX_0; - rct_widget* checkboxWidget = &w->widgets[checkboxWidgetIndex]; - for (size_t i = 0; i < std::size(NewsItemOptionDefinitions); i++) - { - const NotificationDef* ndef = &NewsItemOptionDefinitions[i]; - if (ndef->category != w->page) - continue; - - w->enabled_widgets |= (1ULL << checkboxWidgetIndex); - - checkboxWidget->type = WindowWidgetType::Checkbox; - checkboxWidget->left = baseCheckBox.left; - checkboxWidget->right = baseCheckBox.right; - checkboxWidget->top = y; - checkboxWidget->bottom = checkboxWidget->top + LIST_ROW_HEIGHT + 3; - checkboxWidget->text = ndef->caption; - - const bool* configValue = GetNotificationValuePtr(ndef); - WidgetSetCheckboxValue(w, checkboxWidgetIndex, *configValue); - - checkboxWidgetIndex++; - checkboxWidget++; - y += LIST_ROW_HEIGHT + 3; - } - - // Remove unused checkboxes - while (checkboxWidget->type != WindowWidgetType::Last) - { - w->enabled_widgets &= ~(1ULL << checkboxWidgetIndex); - - checkboxWidget->type = WindowWidgetType::Empty; - checkboxWidgetIndex++; - checkboxWidget++; - } - - // Resize window to fit checkboxes exactly - y += 3; - - if (w->height != y) - { - w->Invalidate(); - w->height = y; - w->widgets[WIDX_BACKGROUND].bottom = y - 1; - w->widgets[WIDX_TAB_CONTENT_PANEL].bottom = y - 1; - w->Invalidate(); - } -} - -static void WindowNewsOptionsPaint(rct_window* w, rct_drawpixelinfo* dpi) -{ - WindowDrawWidgets(w, dpi); - WindowNewsOptionsDrawTabImages(w, dpi); -} - -static void WindowNewsOptionsSetPage(rct_window* w, int32_t page) -{ - if (w->page != page) - { - w->page = page; - w->frame_no = 0; - w->Invalidate(); - } -} - -const int32_t window_news_option_tab_animation_divisor[] = { - 1, - 4, - 4, -}; -const int32_t window_news_option_tab_animation_frames[] = { - 1, - 16, - 8, -}; - -static void WindowNewsOptionsDrawTabImage(rct_window* w, rct_drawpixelinfo* dpi, int32_t page, int32_t spriteIndex) -{ - rct_widgetindex widgetIndex = WIDX_TAB_PARK + page; - - if (!(w->disabled_widgets & (1LL << widgetIndex))) - { - if (w->page == page) - { - int32_t numFrames = window_news_option_tab_animation_frames[w->page]; - if (numFrames > 1) - { - int32_t frame = w->frame_no / window_news_option_tab_animation_divisor[w->page]; - spriteIndex += (frame % numFrames); - } - } - - const auto& widget = w->widgets[widgetIndex]; - gfx_draw_sprite(dpi, ImageId(spriteIndex), w->windowPos + ScreenCoordsXY{ widget.left, widget.top }); - } -} - -static void WindowNewsOptionsDrawTabImages(rct_window* w, rct_drawpixelinfo* dpi) -{ - WindowNewsOptionsDrawTabImage(w, dpi, NOTIFICATION_CATEGORY_PARK, SPR_TAB_PARK); - WindowNewsOptionsDrawTabImage(w, dpi, NOTIFICATION_CATEGORY_RIDE, SPR_TAB_RIDE_0); - WindowNewsOptionsDrawTabImage(w, dpi, NOTIFICATION_CATEGORY_GUEST, SPR_TAB_GUESTS_0); -} - -static bool* GetNotificationValuePtr(const NotificationDef* ndef) -{ - bool* configValue = reinterpret_cast(reinterpret_cast(&gConfigNotifications) + ndef->config_offset); - return configValue; + return WindowFocusOrCreate(WC_NOTIFICATION_OPTIONS, WW, WH, WF_CENTRE_SCREEN); }