diff --git a/src/openrct2-ui/windows/TextInput.cpp b/src/openrct2-ui/windows/TextInput.cpp index 89fddb39b9..43de2ae3d6 100644 --- a/src/openrct2-ui/windows/TextInput.cpp +++ b/src/openrct2-ui/windows/TextInput.cpp @@ -7,13 +7,6 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ -/** - * Text Input Window - * - * This is a new window created to replace the windows dialog box - * that is used for inputing new text for ride names and peep names. - */ - #include #include #include @@ -25,12 +18,11 @@ #include #include -static constexpr const rct_string_id WINDOW_TITLE = STR_OPTIONS; static constexpr const int32_t WW = 250; static constexpr const int32_t WH = 90; -// clang-format off -enum WINDOW_TEXT_INPUT_WIDGET_IDX { +enum WINDOW_TEXT_INPUT_WIDGET_IDX +{ WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE, @@ -38,121 +30,360 @@ enum WINDOW_TEXT_INPUT_WIDGET_IDX { WIDX_OKAY }; -// 0x9DE4E0 static rct_widget window_text_input_widgets[] = { - WINDOW_SHIM(WINDOW_TITLE, WW, WH), - MakeWidget({170, 68}, {71, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_CANCEL), - MakeWidget({ 10, 68}, {71, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_OK ), + WINDOW_SHIM(STR_NONE, WW, WH), + MakeWidget({ 170, 68 }, { 71, 14 }, WindowWidgetType::Button, WindowColour::Secondary, STR_CANCEL), + MakeWidget({ 10, 68 }, { 71, 14 }, WindowWidgetType::Button, WindowColour::Secondary, STR_OK), { WIDGETS_END } }; -static void window_text_input_close(rct_window *w); -static void window_text_input_mouseup(rct_window *w, rct_widgetindex widgetIndex); -static void window_text_input_periodic_update(rct_window *w); -static void window_text_input_invalidate(rct_window *w); -static void window_text_input_paint(rct_window *w, rct_drawpixelinfo *dpi); -static void draw_ime_composition(rct_drawpixelinfo * dpi, int cursorX, int cursorY); - -//0x9A3F7C -static rct_window_event_list window_text_input_events([](auto& events) +class TextInputWindow final : public Window { - events.close = &window_text_input_close; - events.mouse_up = &window_text_input_mouseup; - events.periodic_update = &window_text_input_periodic_update; - events.invalidate = &window_text_input_invalidate; - events.paint = &window_text_input_paint; -}); -// clang-format on +private: + widget_identifier _parentWidget; -static rct_string_id input_text_description; -static utf8 text_input[TEXT_INPUT_SIZE] = { 0 }; -static rct_windowclass calling_class = 0; -static rct_windownumber calling_number = 0; -static int32_t calling_widget = 0; -static int32_t _maxInputLength; + std::string _title; + rct_string_id _titleStringId = STR_NONE; -static std::string _title; -static std::string _description; -static std::function _callback; -static std::function _cancelCallback; + std::string _description; + rct_string_id _descriptionStringId = STR_NONE; -void window_text_input_open( - rct_window* call_w, rct_widgetindex call_widget, rct_string_id title, rct_string_id description, - rct_string_id existing_text, uintptr_t existing_args, int32_t maxLength) -{ - // Get the raw string - utf8 buffer[std::size(text_input)]{}; - if (existing_text != STR_NONE) - format_string(buffer, maxLength, existing_text, &existing_args); + std::function _callback; + std::function _cancelCallback; - window_text_input_raw_open(call_w, call_widget, title, description, buffer, maxLength); -} + int32_t _cursorBlink{}; + size_t _maxInputLength; + std::string _buffer; + +public: + void OnOpen() override + { + enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_CANCEL) | (1ULL << WIDX_OKAY); + widgets = window_text_input_widgets; + WindowInitScrollWidgets(this); + SetParentWindow(nullptr, 0); + } + + void SetParentWindow(rct_window* parentWindow, rct_widgetindex widgetIndex) + { + // Save calling window details so that the information can be passed back to the correct window & widget + if (parentWindow == nullptr) + { + _parentWidget.window.classification = WC_NULL; + _parentWidget.window.number = 0; + _parentWidget.widget_index = 0; + + colours[0] = COLOUR_GREY; + colours[1] = COLOUR_GREY; + colours[2] = COLOUR_GREY; + } + else + { + _parentWidget.window.classification = parentWindow->classification; + _parentWidget.window.number = parentWindow->number; + _parentWidget.widget_index = widgetIndex; + + colours[0] = parentWindow->colours[1]; + colours[1] = parentWindow->colours[1]; + colours[2] = parentWindow->colours[1]; + } + } + + void SetTitle(rct_string_id title, rct_string_id description) + { + _titleStringId = title; + _descriptionStringId = description; + } + + void SetTitle(std::string_view title, std::string_view description) + { + _titleStringId = STR_NONE; + _title = title; + _descriptionStringId = STR_NONE; + _description = description; + } + + void SetText(std::string_view text, size_t maxLength) + { + _buffer = text; + _buffer.resize(maxLength); + _maxInputLength = maxLength; + gTextInput = context_start_text_input(_buffer.data(), maxLength); + } + + void SetCallback(std::function callback, std::function cancelCallback) + { + _callback = callback; + _cancelCallback = cancelCallback; + } + + void OnClose() override + { + // Make sure that we take it out of the text input + // mode otherwise problems may occur. + context_stop_text_input(); + } + + void OnPeriodicUpdate() override + { + if (HasParentWindow()) + { + // If the calling window is closed then close the text input window + auto parentWindow = GetParentWindow(); + if (parentWindow == nullptr) + { + window_close(this); + return; + } + } + + // Used to blink the cursor. + _cursorBlink++; + if (_cursorBlink > 30) + _cursorBlink = 0; + + Invalidate(); + } + + void OnMouseUp(rct_widgetindex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CANCEL: + case WIDX_CLOSE: + context_stop_text_input(); + ExecuteCallback(false); + window_close(this); + break; + case WIDX_OKAY: + context_stop_text_input(); + ExecuteCallback(true); + window_close(this); + } + } + + void OnPrepareDraw() override + { + // Change window size if required. + int32_t newHeight = CalculateWindowHeight(_buffer.data()); + if (newHeight != height) + { + Invalidate(); + window_set_resize(this, WW, height, WW, height); + } + + widgets[WIDX_OKAY].top = newHeight - 22; + widgets[WIDX_OKAY].bottom = newHeight - 9; + widgets[WIDX_CANCEL].top = newHeight - 22; + widgets[WIDX_CANCEL].bottom = newHeight - 9; + widgets[WIDX_BACKGROUND].bottom = newHeight - 1; + + // Set window title argument + if (_titleStringId == STR_NONE) + { + auto ft = Formatter::Common(); + ft.Add(_title.c_str()); + widgets[WIDX_TITLE].text = STR_STRING; + } + else + { + widgets[WIDX_TITLE].text = _titleStringId; + } + } + + void OnDraw(rct_drawpixelinfo& dpi) override + { + Window::OnDraw(dpi); + + ScreenCoordsXY screenCoords; + screenCoords.y = windowPos.y + 25; + + int32_t no_lines = 0; + int32_t font_height = 0; + + if (_descriptionStringId == STR_NONE) + { + auto* text = _description.c_str(); + gfx_draw_string_centred_wrapped(&dpi, &text, { windowPos.x + WW / 2, screenCoords.y }, WW, STR_STRING, colours[1]); + } + else + { + gfx_draw_string_centred_wrapped( + &dpi, &TextInputDescriptionArgs, { windowPos.x + WW / 2, screenCoords.y }, WW, _descriptionStringId, + colours[1]); + } + + screenCoords.y += 25; + + gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM; + + char wrapped_string[TEXT_INPUT_SIZE]; + safe_strcpy(wrapped_string, _buffer.data(), TEXT_INPUT_SIZE); + + // String length needs to add 12 either side of box + // +13 for cursor when max length. + gfx_wrap_string(wrapped_string, WW - (24 + 13), &no_lines, &font_height); + + gfx_fill_rect_inset( + &dpi, { { windowPos.x + 10, screenCoords.y }, { windowPos.x + WW - 10, screenCoords.y + 10 * (no_lines + 1) + 3 } }, + colours[1], INSET_RECT_F_60); + + screenCoords.y += 1; + + char* wrap_pointer = wrapped_string; + size_t char_count = 0; + uint8_t cur_drawn = 0; + + int32_t cursorX = 0; + int32_t cursorY = 0; + for (int32_t line = 0; line <= no_lines; line++) + { + screenCoords.x = windowPos.x + 12; + gfx_draw_string_no_formatting(&dpi, wrap_pointer, colours[1], screenCoords); + + size_t string_length = get_string_size(wrap_pointer) - 1; + + if (!cur_drawn && (gTextInput->SelectionStart <= char_count + string_length)) + { + // Make a copy of the string for measuring the width. + char temp_string[TEXT_INPUT_SIZE] = { 0 }; + std::memcpy(temp_string, wrap_pointer, gTextInput->SelectionStart - char_count); + cursorX = windowPos.x + 13 + gfx_get_string_width_no_formatting(temp_string); + cursorY = screenCoords.y; + + int32_t textWidth = 6; + if (gTextInput->SelectionStart < strlen(_buffer.data())) + { + // Make a 1 utf8-character wide string for measuring the width + // of the currently selected character. + utf8 tmp[5] = { 0 }; // This is easier than setting temp_string[0..5] + uint32_t codepoint = utf8_get_next(_buffer.data() + gTextInput->SelectionStart, nullptr); + utf8_write_codepoint(tmp, codepoint); + textWidth = std::max(gfx_get_string_width_no_formatting(tmp) - 2, 4); + } + + if (_cursorBlink > 15) + { + uint8_t colour = ColourMapA[colours[1]].mid_light; + // TODO: palette index addition + gfx_fill_rect( + &dpi, { { cursorX, screenCoords.y + 9 }, { cursorX + textWidth, screenCoords.y + 9 } }, colour + 5); + } + + cur_drawn++; + } + + wrap_pointer += string_length + 1; + + if (_buffer[char_count + string_length] == ' ') + char_count++; + char_count += string_length; + + screenCoords.y += 10; + } + + if (!cur_drawn) + { + cursorX = gLastDrawStringX; + cursorY = screenCoords.y - 10; + } + + // IME composition + if (!str_is_null_or_empty(gTextInput->ImeBuffer)) + { + DrawIMEComposition(dpi, cursorX, cursorY); + } + } + + void OnReturnPressed() + { + context_stop_text_input(); + ExecuteCallback(true); + window_close(this); + } + + static int32_t CalculateWindowHeight(std::string_view text) + { + std::string wrappedString(text); + wrappedString.resize(TEXT_INPUT_SIZE); + + // String length needs to add 12 either side of box +13 for cursor when max length. + int32_t numLines{}; + int32_t fontHeight{}; + gfx_wrap_string(wrappedString.data(), WW - (24 + 13), &numLines, &fontHeight); + return numLines * 10 + WH; + } + +private: + static void DrawIMEComposition(rct_drawpixelinfo& dpi, int32_t cursorX, int32_t cursorY) + { + int compositionWidth = gfx_get_string_width(gTextInput->ImeBuffer); + ScreenCoordsXY screenCoords(cursorX - (compositionWidth / 2), cursorY + 13); + int width = compositionWidth; + int height = 10; + + gfx_fill_rect( + &dpi, { screenCoords - ScreenCoordsXY{ 1, 1 }, screenCoords + ScreenCoordsXY{ width + 1, height + 1 } }, + PALETTE_INDEX_12); + gfx_fill_rect(&dpi, { screenCoords, screenCoords + ScreenCoordsXY{ width, height } }, PALETTE_INDEX_0); + gfx_draw_string(&dpi, static_cast(gTextInput->ImeBuffer), COLOUR_DARK_GREEN, screenCoords); + } + + void ExecuteCallback(bool hasValue) + { + if (HasParentWindow()) + { + auto w = GetParentWindow(); + if (w != nullptr) + { + auto value = hasValue ? _buffer.data() : nullptr; + window_event_textinput_call(w, _parentWidget.widget_index, value); + } + } + else + { + if (hasValue) + { + if (_callback) + { + _callback(_buffer.data()); + } + } + else + { + if (_cancelCallback) + { + _cancelCallback(); + } + } + } + } + + bool HasParentWindow() const + { + return _parentWidget.window.classification != WC_NULL; + } + + rct_window* GetParentWindow() const + { + return HasParentWindow() ? window_find_by_number(_parentWidget.window.classification, _parentWidget.window.number) + : nullptr; + } +}; void window_text_input_raw_open( rct_window* call_w, rct_widgetindex call_widget, rct_string_id title, rct_string_id description, const_utf8string existing_text, int32_t maxLength) { - _maxInputLength = maxLength; - window_close_by_class(WC_TEXTINPUT); - // Set the input text - if (existing_text != nullptr) - String::Set(text_input, sizeof(text_input), existing_text); - else - String::Set(text_input, sizeof(text_input), ""); - - // This is the text displayed above the input box - input_text_description = description; - - // Work out the existing size of the window - char wrapped_string[TEXT_INPUT_SIZE]; - safe_strcpy(wrapped_string, text_input, TEXT_INPUT_SIZE); - - int32_t no_lines = 0, font_height = 0; - - // String length needs to add 12 either side of box +13 for cursor when max length. - gfx_wrap_string(wrapped_string, WW - (24 + 13), &no_lines, &font_height); - - int32_t height = no_lines * 10 + WH; - - // Window will be in the centre of the screen - rct_window* w = WindowCreateCentred(WW, height, &window_text_input_events, WC_TEXTINPUT, WF_STICK_TO_FRONT); - - w->widgets = window_text_input_widgets; - w->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_CANCEL) | (1ULL << WIDX_OKAY); - - window_text_input_widgets[WIDX_TITLE].text = title; - - // Save calling window details so that the information can be passed back to the correct window & widget - if (call_w == nullptr) + auto height = TextInputWindow::CalculateWindowHeight(existing_text); + auto w = WindowCreate(WC_TEXTINPUT, WW, height, WF_CENTRE_SCREEN | WF_STICK_TO_FRONT); + if (w != nullptr) { - calling_class = WC_NULL; - calling_number = 0; - calling_widget = 0; - } - else - { - calling_class = call_w->classification; - calling_number = call_w->number; - calling_widget = call_widget; - } - - gTextInput = context_start_text_input(text_input, maxLength); - - WindowInitScrollWidgets(w); - - if (call_w == nullptr) - { - w->colours[0] = COLOUR_GREY; - w->colours[1] = COLOUR_GREY; - w->colours[2] = COLOUR_GREY; - } - else - { - w->colours[0] = call_w->colours[1]; - w->colours[1] = call_w->colours[1]; - w->colours[2] = call_w->colours[1]; + w->SetParentWindow(call_w, call_widget); + w->SetTitle(title, description); + w->SetText(existing_text, maxLength); } } @@ -160,164 +391,22 @@ void window_text_input_open( std::string_view title, std::string_view description, std::string_view initialValue, size_t maxLength, std::function callback, std::function cancelCallback) { - _title = title; - _description = description; - _callback = callback; - _cancelCallback = cancelCallback; - - std::string szInitialValue(initialValue); - auto szDescription = _description.c_str(); - std::memcpy(TextInputDescriptionArgs, &szDescription, sizeof(const char*)); - maxLength = std::min(sizeof(text_input) - 1, maxLength); - window_text_input_raw_open(nullptr, 0, STR_STRING, STR_STRING, szInitialValue.c_str(), static_cast(maxLength)); -} - -static void window_text_input_execute_callback(bool hasValue) -{ - if (calling_class == WC_NULL) + auto height = TextInputWindow::CalculateWindowHeight(initialValue); + auto w = WindowCreate(WC_TEXTINPUT, WW, height, WF_CENTRE_SCREEN | WF_STICK_TO_FRONT); + if (w != nullptr) { - if (hasValue) - { - if (_callback) - { - _callback(text_input); - } - } - else - { - if (_cancelCallback) - { - _cancelCallback(); - } - } - } - else - { - auto calling_w = window_find_by_number(calling_class, calling_number); - if (calling_w != nullptr) - { - auto value = hasValue ? text_input : nullptr; - window_event_textinput_call(calling_w, calling_widget, value); - } + w->SetTitle(title, description); + w->SetText(initialValue, maxLength); + w->SetCallback(callback, cancelCallback); } } -/** - * - */ -static void window_text_input_mouseup(rct_window* w, rct_widgetindex widgetIndex) +void window_text_input_open( + rct_window* call_w, rct_widgetindex call_widget, rct_string_id title, rct_string_id description, + rct_string_id existing_text, uintptr_t existing_args, int32_t maxLength) { - switch (widgetIndex) - { - case WIDX_CANCEL: - case WIDX_CLOSE: - context_stop_text_input(); - window_text_input_execute_callback(false); - window_close(w); - break; - case WIDX_OKAY: - context_stop_text_input(); - window_text_input_execute_callback(true); - window_close(w); - } -} - -/** - * - */ -static void window_text_input_paint(rct_window* w, rct_drawpixelinfo* dpi) -{ - WindowDrawWidgets(w, dpi); - - ScreenCoordsXY screenCoords; - screenCoords.y = w->windowPos.y + 25; - - int32_t no_lines = 0; - int32_t font_height = 0; - - gfx_draw_string_centred_wrapped( - dpi, &TextInputDescriptionArgs, { w->windowPos.x + WW / 2, screenCoords.y }, WW, input_text_description, w->colours[1]); - - screenCoords.y += 25; - - gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM; - - char wrapped_string[TEXT_INPUT_SIZE]; - safe_strcpy(wrapped_string, text_input, TEXT_INPUT_SIZE); - - // String length needs to add 12 either side of box - // +13 for cursor when max length. - gfx_wrap_string(wrapped_string, WW - (24 + 13), &no_lines, &font_height); - - gfx_fill_rect_inset( - dpi, - { { w->windowPos.x + 10, screenCoords.y }, { w->windowPos.x + WW - 10, screenCoords.y + 10 * (no_lines + 1) + 3 } }, - w->colours[1], INSET_RECT_F_60); - - screenCoords.y += 1; - - char* wrap_pointer = wrapped_string; - size_t char_count = 0; - uint8_t cur_drawn = 0; - - int32_t cursorX = 0; - int32_t cursorY = 0; - for (int32_t line = 0; line <= no_lines; line++) - { - screenCoords.x = w->windowPos.x + 12; - gfx_draw_string_no_formatting(dpi, wrap_pointer, w->colours[1], screenCoords); - - size_t string_length = get_string_size(wrap_pointer) - 1; - - if (!cur_drawn && (gTextInput->SelectionStart <= char_count + string_length)) - { - // Make a copy of the string for measuring the width. - char temp_string[TEXT_INPUT_SIZE] = { 0 }; - std::memcpy(temp_string, wrap_pointer, gTextInput->SelectionStart - char_count); - cursorX = w->windowPos.x + 13 + gfx_get_string_width_no_formatting(temp_string); - cursorY = screenCoords.y; - - int32_t width = 6; - if (gTextInput->SelectionStart < strlen(text_input)) - { - // Make a 1 utf8-character wide string for measuring the width - // of the currently selected character. - utf8 tmp[5] = { 0 }; // This is easier than setting temp_string[0..5] - uint32_t codepoint = utf8_get_next(text_input + gTextInput->SelectionStart, nullptr); - utf8_write_codepoint(tmp, codepoint); - width = std::max(gfx_get_string_width_no_formatting(tmp) - 2, 4); - } - - if (w->frame_no > 15) - { - uint8_t colour = ColourMapA[w->colours[1]].mid_light; - // TODO: palette index addition - gfx_fill_rect(dpi, { { cursorX, screenCoords.y + 9 }, { cursorX + width, screenCoords.y + 9 } }, colour + 5); - } - - cur_drawn++; - } - - wrap_pointer += string_length + 1; - - if (text_input[char_count + string_length] == ' ') - char_count++; - char_count += string_length; - - screenCoords.y += 10; - } - - if (!cur_drawn) - { - cursorX = gLastDrawStringX; - cursorY = screenCoords.y - 10; - } - - // IME composition - if (!str_is_null_or_empty(gTextInput->ImeBuffer)) - { - draw_ime_composition(dpi, cursorX, cursorY); - } + auto existingText = format_string(existing_text, &existing_args); + window_text_input_raw_open(call_w, call_widget, title, description, existingText.c_str(), maxLength); } void window_text_input_key(rct_window* w, char keychar) @@ -325,91 +414,11 @@ void window_text_input_key(rct_window* w, char keychar) // If the return button is pressed stop text input if (keychar == '\r') { - context_stop_text_input(); - window_text_input_execute_callback(true); - window_close(w); - } - w->Invalidate(); -} - -void window_text_input_periodic_update(rct_window* w) -{ - if (calling_class != WC_NULL) - { - auto calling_w = window_find_by_number(calling_class, calling_number); - // If the calling window is closed then close the text - // input window. - if (!calling_w) + if (w->classification == WC_TEXTINPUT) { - window_close(w); - return; + auto textInputWindow = static_cast(w); + textInputWindow->OnReturnPressed(); } } - - // Used to blink the cursor. - w->frame_no++; - if (w->frame_no > 30) - w->frame_no = 0; - w->Invalidate(); } - -static void window_text_input_close(rct_window* w) -{ - // Make sure that we take it out of the text input - // mode otherwise problems may occur. - context_stop_text_input(); - - _title = {}; - _description = {}; - _callback = {}; - _cancelCallback = {}; -} - -static void window_text_input_invalidate(rct_window* w) -{ - // Work out the existing size of the window - char wrapped_string[TEXT_INPUT_SIZE]; - safe_strcpy(wrapped_string, text_input, TEXT_INPUT_SIZE); - - int32_t no_lines = 0, font_height = 0; - - // String length needs to add 12 either side of box - // +13 for cursor when max length. - gfx_wrap_string(wrapped_string, WW - (24 + 13), &no_lines, &font_height); - - int32_t height = no_lines * 10 + WH; - - // Change window size if required. - if (height != w->height) - { - w->Invalidate(); - window_set_resize(w, WW, height, WW, height); - } - - window_text_input_widgets[WIDX_OKAY].top = height - 22; - window_text_input_widgets[WIDX_OKAY].bottom = height - 9; - - window_text_input_widgets[WIDX_CANCEL].top = height - 22; - window_text_input_widgets[WIDX_CANCEL].bottom = height - 9; - - window_text_input_widgets[WIDX_BACKGROUND].bottom = height - 1; - - // Set window title argument - auto ft = Formatter::Common(); - ft.Add(_title.c_str()); -} - -static void draw_ime_composition(rct_drawpixelinfo* dpi, int cursorX, int cursorY) -{ - int compositionWidth = gfx_get_string_width(gTextInput->ImeBuffer); - ScreenCoordsXY screenCoords(cursorX - (compositionWidth / 2), cursorY + 13); - int width = compositionWidth; - int height = 10; - - gfx_fill_rect( - dpi, { screenCoords - ScreenCoordsXY{ 1, 1 }, screenCoords + ScreenCoordsXY{ width + 1, height + 1 } }, - PALETTE_INDEX_12); - gfx_fill_rect(dpi, { screenCoords, screenCoords + ScreenCoordsXY{ width, height } }, PALETTE_INDEX_0); - gfx_draw_string(dpi, static_cast(gTextInput->ImeBuffer), COLOUR_DARK_GREEN, screenCoords); -} diff --git a/src/openrct2/interface/Window.cpp b/src/openrct2/interface/Window.cpp index 424e8af1ec..68011d89cf 100644 --- a/src/openrct2/interface/Window.cpp +++ b/src/openrct2/interface/Window.cpp @@ -1449,9 +1449,10 @@ void window_event_update_call(rct_window* w) void window_event_periodic_update_call(rct_window* w) { - if (w->event_handlers != nullptr) - if (w->event_handlers->periodic_update != nullptr) - w->event_handlers->periodic_update(w); + if (w->event_handlers == nullptr) + w->OnPeriodicUpdate(); + else if (w->event_handlers->periodic_update != nullptr) + w->event_handlers->periodic_update(w); } void window_event_unknown_08_call(rct_window* w) diff --git a/src/openrct2/interface/Window_internal.h b/src/openrct2/interface/Window_internal.h index fcd8dafc2e..ab577334d0 100644 --- a/src/openrct2/interface/Window_internal.h +++ b/src/openrct2/interface/Window_internal.h @@ -125,6 +125,9 @@ struct rct_window virtual void OnUpdate() { } + virtual void OnPeriodicUpdate() + { + } virtual void OnPrepareDraw() { }