diff --git a/distribution/changelog.txt b/distribution/changelog.txt index d96be4316f..b4c3ac183e 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -18,6 +18,7 @@ - Feature: [#13613] Add single-rail roller coaster (Rocky Mountain Construction Raptor). - Feature: [#13614] Add terrain surfaces from RollerCoaster Tycoon 1. - Feature: [#13675] [Plugin] Add context.setInterval and context.setTimeout. +- Feature: [#13927] [Plugin] Add isVisible and text box widget. - Change: [#13346] [Plugin] Renamed FootpathScenery to FootpathAddition, fix typos. - Fix: [#4605, #11912] Water palettes are not updated properly when selected in Object Selection. - Fix: [#9631, #10716] Banners drawing glitches when there are more than 32 on the screen at once. diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 5ba2d63844..0c2abd31a2 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2020 OpenRCT2 developers + * Copyright (c) 2014-2021 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -1861,7 +1861,7 @@ declare global { * Represents the type of a widget, e.g. button or label. */ type WidgetType = - "button" | "checkbox" | "colourpicker" | "dropdown" | "groupbox" | "label" | "listview" | "spinner" | "viewport"; + "button" | "checkbox" | "colourpicker" | "dropdown" | "groupbox" | "label" | "listview" | "spinner" | "textbox" | "viewport"; interface Widget { type: WidgetType; @@ -1956,6 +1956,12 @@ declare global { onIncrement: () => void; } + interface TextBoxWidget extends Widget { + text: string; + maxLength: number; + onChange: (text: string) => void; + } + interface ViewportWidget extends Widget { viewport: Viewport } diff --git a/src/openrct2-ui/scripting/CustomWindow.cpp b/src/openrct2-ui/scripting/CustomWindow.cpp index 4adb8eab22..b267545e30 100644 --- a/src/openrct2-ui/scripting/CustomWindow.cpp +++ b/src/openrct2-ui/scripting/CustomWindow.cpp @@ -54,6 +54,7 @@ namespace OpenRCT2::Ui::Windows static void window_custom_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget); static void window_custom_resize(rct_window* w); static void window_custom_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex); + static void window_custom_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text); static void window_custom_update(rct_window* w); static void window_custom_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height); static void window_custom_scrollmousedrag(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); @@ -70,6 +71,7 @@ namespace OpenRCT2::Ui::Windows events.resize = &window_custom_resize; events.mouse_down = &window_custom_mousedown; events.dropdown = &window_custom_dropdown; + events.text_input = &window_custom_textinput; events.update = &window_custom_update; events.get_scroll_size = &window_custom_scrollgetsize; events.scroll_mousedown = &window_custom_scrollmousedown; @@ -98,6 +100,7 @@ namespace OpenRCT2::Ui::Windows std::vector ListViewColumns; ScrollbarType Scrollbars{}; int32_t SelectedIndex{}; + int32_t MaxLength{}; std::optional SelectedCell; bool IsChecked{}; bool IsDisabled{}; @@ -194,6 +197,12 @@ namespace OpenRCT2::Ui::Windows result.OnIncrement = desc["onIncrement"]; result.OnDecrement = desc["onDecrement"]; } + else if (result.Type == "textbox") + { + result.Text = ProcessString(desc["text"]); + result.MaxLength = AsOrDefault(desc["maxLength"], 32); + result.OnChange = desc["onChange"]; + } result.HasBorder = AsOrDefault(desc["border"], result.HasBorder); return result; } @@ -554,6 +563,11 @@ namespace OpenRCT2::Ui::Windows InvokeEventHandler(info.Owner, widgetDesc->OnIncrement); } } + else if (widgetDesc->Type == "textbox") + { + auto* text = const_cast(widgetDesc->Text.c_str()); + window_start_textbox(w, widgetIndex, STR_STRING, text, widgetDesc->MaxLength + 1); + } } } @@ -577,6 +591,28 @@ namespace OpenRCT2::Ui::Windows } } + static void window_custom_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text) + { + if (text == nullptr) + return; + + auto& info = GetInfo(w); + auto widgetDesc = info.GetCustomWidgetDesc(w, widgetIndex); + if (widgetDesc != nullptr) + { + if (widgetDesc->Type == "textbox") + { + UpdateWidgetText(w, widgetIndex, text); + + std::vector args; + auto ctx = widgetDesc->OnChange.context(); + duk_push_string(ctx, text); + args.push_back(DukValue::take_from_stack(ctx)); + InvokeEventHandler(info.Owner, widgetDesc->OnChange, args); + } + } + } + static void window_custom_update(rct_window* w) { const auto& info = GetInfo(w); @@ -961,6 +997,13 @@ namespace OpenRCT2::Ui::Windows widget.text = STR_NUMERIC_UP; widgetList.push_back(widget); } + else if (desc.Type == "textbox") + { + widget.type = WindowWidgetType::TextBox; + widget.string = const_cast(desc.Text.c_str()); + widget.flags |= WIDGET_FLAGS::TEXT_IS_STRING; + widgetList.push_back(widget); + } else if (desc.Type == "viewport") { widget.type = WindowWidgetType::Viewport; @@ -1327,6 +1370,33 @@ namespace OpenRCT2::Ui::Windows return nullptr; } + int32_t GetWidgetMaxLength(rct_window* w, rct_widgetindex widgetIndex) + { + if (w->custom_info != nullptr) + { + auto& customInfo = GetInfo(w); + auto customWidgetInfo = customInfo.GetCustomWidgetDesc(w, widgetIndex); + if (customWidgetInfo != nullptr) + { + return customWidgetInfo->MaxLength; + } + } + return 0; + } + + void SetWidgetMaxLength(rct_window* w, rct_widgetindex widgetIndex, int32_t value) + { + if (w->custom_info != nullptr) + { + auto& customInfo = GetInfo(w); + auto customWidgetInfo = customInfo.GetCustomWidgetDesc(w, widgetIndex); + if (customWidgetInfo != nullptr) + { + customWidgetInfo->MaxLength = value; + } + } + } + } // namespace OpenRCT2::Ui::Windows #endif diff --git a/src/openrct2-ui/scripting/CustomWindow.h b/src/openrct2-ui/scripting/CustomWindow.h index 84890e36f3..c30d6249c7 100644 --- a/src/openrct2-ui/scripting/CustomWindow.h +++ b/src/openrct2-ui/scripting/CustomWindow.h @@ -34,6 +34,8 @@ namespace OpenRCT2::Ui::Windows std::string GetWidgetName(rct_window* w, rct_widgetindex widgetIndex); void SetWidgetName(rct_window* w, rct_widgetindex widgetIndex, std::string_view name); CustomListView* GetCustomListView(rct_window* w, rct_widgetindex widgetIndex); + int32_t GetWidgetMaxLength(rct_window* w, rct_widgetindex widgetIndex); + void SetWidgetMaxLength(rct_window* w, rct_widgetindex widgetIndex, int32_t value); } // namespace OpenRCT2::Ui::Windows #endif diff --git a/src/openrct2-ui/scripting/ScWidget.hpp b/src/openrct2-ui/scripting/ScWidget.hpp index 115e0a84d3..372531b814 100644 --- a/src/openrct2-ui/scripting/ScWidget.hpp +++ b/src/openrct2-ui/scripting/ScWidget.hpp @@ -832,6 +832,41 @@ namespace OpenRCT2::Scripting } }; + class ScTextBoxWidget : public ScWidget + { + public: + ScTextBoxWidget(rct_windowclass c, rct_windownumber n, rct_widgetindex widgetIndex) + : ScWidget(c, n, widgetIndex) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScTextBoxWidget::maxLength_get, &ScTextBoxWidget::maxLength_set, "maxLength"); + } + + private: + int32_t maxLength_get() const + { + auto w = GetWindow(); + if (w != nullptr && IsCustomWindow()) + { + return OpenRCT2::Ui::Windows::GetWidgetMaxLength(w, _widgetIndex); + } + return 0; + } + + void maxLength_set(int32_t value) + { + auto w = GetWindow(); + if (w != nullptr && IsCustomWindow()) + { + OpenRCT2::Ui::Windows::SetWidgetMaxLength(w, _widgetIndex, value); + } + } + }; + inline DukValue ScWidget::ToDukValue(duk_context* ctx, rct_window* w, rct_widgetindex widgetIndex) { const auto& widget = w->widgets[widgetIndex]; @@ -851,6 +886,8 @@ namespace OpenRCT2::Scripting return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); case WindowWidgetType::Scroll: return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + case WindowWidgetType::TextBox: + return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); default: return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); } diff --git a/src/openrct2-ui/scripting/UiExtensions.cpp b/src/openrct2-ui/scripting/UiExtensions.cpp index 3cf4404550..21947810b5 100644 --- a/src/openrct2-ui/scripting/UiExtensions.cpp +++ b/src/openrct2-ui/scripting/UiExtensions.cpp @@ -37,6 +37,7 @@ void UiScriptExtensions::Extend(ScriptEngine& scriptEngine) ScCheckBoxWidget::Register(ctx); ScDropdownWidget::Register(ctx); ScListViewWidget::Register(ctx); + ScTextBoxWidget::Register(ctx); ScWindow::Register(ctx); InitialiseCustomMenuItems(scriptEngine); diff --git a/src/openrct2/interface/Window.cpp b/src/openrct2/interface/Window.cpp index 835eb83514..8e7460101f 100644 --- a/src/openrct2/interface/Window.cpp +++ b/src/openrct2/interface/Window.cpp @@ -2003,12 +2003,18 @@ void window_cancel_textbox() if (gUsingWidgetTextBox) { rct_window* w = window_find_by_number(gCurrentTextBox.window.classification, gCurrentTextBox.window.number); - window_event_textinput_call(w, gCurrentTextBox.widget_index, nullptr); + if (w != nullptr) + { + window_event_textinput_call(w, gCurrentTextBox.widget_index, nullptr); + } gCurrentTextBox.window.classification = WC_NULL; gCurrentTextBox.window.number = 0; context_stop_text_input(); gUsingWidgetTextBox = false; - widget_invalidate(w, gCurrentTextBox.widget_index); + if (w != nullptr) + { + widget_invalidate(w, gCurrentTextBox.widget_index); + } gCurrentTextBox.widget_index = static_cast(WindowWidgetType::Last); } }