diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index a118062035..b873a99b03 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -939,6 +939,12 @@ declare global { closeWindows(classification: string, id?: number): void; closeAllWindows(): void; + /** + * Shows a text input prompt and calls the given callback when entered. + * @param desc The parameters for the text input window. + */ + showTextInput(desc: TextInputDesc): void; + /** * Begins a new tool session. The cursor will change to the style specified by the * given tool descriptor and cursor events will be provided. @@ -949,6 +955,36 @@ declare global { registerMenuItem(text: string, callback: () => void): void; } + /** + * Parameters for the text input window. + */ + interface TextInputDesc { + /** + * The title of the text input window. + */ + title: string; + + /** + * The description to show above the text box. + */ + description: string; + + /** + * The current value of the text box. + */ + initialValue?: string; + + /** + * The maximum length the value can be. + */ + maxLength?: number; + + /** + * The function to call when the user has entered a new value and pressed OK. + */ + callback: (value: string) => void; + } + interface TileSelection { range: MapRange; tiles: CoordsXY[]; diff --git a/src/openrct2-ui/scripting/ScUi.hpp b/src/openrct2-ui/scripting/ScUi.hpp index b4e527a738..2c7c2b6653 100644 --- a/src/openrct2-ui/scripting/ScUi.hpp +++ b/src/openrct2-ui/scripting/ScUi.hpp @@ -11,6 +11,7 @@ #ifdef ENABLE_SCRIPTING +# include "../windows/Window.h" # include "CustomMenu.h" # include "ScTileSelection.hpp" # include "ScViewport.hpp" @@ -180,6 +181,30 @@ namespace OpenRCT2::Scripting return {}; } + void showTextInput(const DukValue& desc) + { + try + { + auto plugin = _scriptEngine.GetExecInfo().GetCurrentPlugin(); + auto title = desc["title"].as_string(); + auto description = desc["description"].as_string(); + auto initialValue = AsOrDefault(desc["maxLength"], ""); + auto maxLength = AsOrDefault(desc["maxLength"], std::numeric_limits::max()); + auto callback = desc["callback"]; + window_text_input_open( + title, description, initialValue, std::max(0, maxLength), + [this, plugin, callback](const std::string_view& value) { + auto dukValue = ToDuk(_scriptEngine.GetContext(), value); + _scriptEngine.ExecutePluginCall(plugin, callback, { dukValue }, false); + }, + {}); + } + catch (const DukException&) + { + duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters."); + } + } + void activateTool(const DukValue& desc) { InitialiseCustomTool(_scriptEngine, desc); @@ -205,6 +230,7 @@ namespace OpenRCT2::Scripting dukglue_register_method(ctx, &ScUi::closeWindows, "closeWindows"); dukglue_register_method(ctx, &ScUi::closeAllWindows, "closeAllWindows"); dukglue_register_method(ctx, &ScUi::getWindow, "getWindow"); + dukglue_register_method(ctx, &ScUi::showTextInput, "showTextInput"); dukglue_register_method(ctx, &ScUi::activateTool, "activateTool"); dukglue_register_method(ctx, &ScUi::registerMenuItem, "registerMenuItem"); } diff --git a/src/openrct2-ui/windows/TextInput.cpp b/src/openrct2-ui/windows/TextInput.cpp index 0dc8a77431..720a2943f6 100644 --- a/src/openrct2-ui/windows/TextInput.cpp +++ b/src/openrct2-ui/windows/TextInput.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2019 OpenRCT2 developers + * Copyright (c) 2014-2020 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -94,6 +94,11 @@ static rct_windownumber calling_number = 0; static int32_t calling_widget = 0; static int32_t _maxInputLength; +static std::string _title; +static std::string _description; +static std::function _callback; +static std::function _cancelCallback; + 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) @@ -144,16 +149,81 @@ void window_text_input_raw_open( 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 - calling_class = call_w->classification; - calling_number = call_w->number; - calling_widget = call_widget; + if (call_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); window_init_scroll_widgets(w); - w->colours[0] = call_w->colours[0]; - w->colours[1] = call_w->colours[1]; - w->colours[2] = call_w->colours[2]; + + 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[0]; + w->colours[1] = call_w->colours[1]; + w->colours[2] = call_w->colours[2]; + } +} + +void window_text_input_open( + const std::string_view& title, const std::string_view& description, const 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) + { + 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); + } + } } /** @@ -161,26 +231,17 @@ void window_text_input_raw_open( */ static void window_text_input_mouseup(rct_window* w, rct_widgetindex widgetIndex) { - rct_window* calling_w; - - calling_w = window_find_by_number(calling_class, calling_number); switch (widgetIndex) { case WIDX_CANCEL: case WIDX_CLOSE: context_stop_text_input(); - // Pass back the text that has been entered. - // ecx when zero means text input failed - if (calling_w != nullptr) - window_event_textinput_call(calling_w, calling_widget, nullptr); + window_text_input_execute_callback(false); window_close(w); break; case WIDX_OKAY: context_stop_text_input(); - // Pass back the text that has been entered. - // ecx when nonzero means text input success - if (calling_w != nullptr) - window_event_textinput_call(calling_w, calling_widget, text_input); + window_text_input_execute_callback(true); window_close(w); } } @@ -285,32 +346,24 @@ void window_text_input_key(rct_window* w, char keychar) if (keychar == '\r') { context_stop_text_input(); + window_text_input_execute_callback(true); window_close(w); - // Window was closed and its unique_ptr is gone, - // don't try invalidating it. - w = window_find_by_number(calling_class, calling_number); - // Pass back the text that has been entered. - // ecx when nonzero means text input success - if (w) - window_event_textinput_call(w, calling_widget, text_input); - - // Update the window pointer, as it may have been closed by textinput handler - w = window_find_by_number(calling_class, calling_number); } - - if (w) - w->Invalidate(); + w->Invalidate(); } void window_text_input_periodic_update(rct_window* w) { - rct_window* 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 (calling_class != WC_NULL) { - window_close(w); - return; + 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) + { + window_close(w); + return; + } } // Used to blink the cursor. @@ -326,6 +379,11 @@ 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) @@ -356,6 +414,9 @@ static void window_text_input_invalidate(rct_window* w) window_text_input_widgets[WIDX_CANCEL].bottom = height - 9; window_text_input_widgets[WIDX_BACKGROUND].bottom = height - 1; + + // Set window title argument + set_format_arg(0, const char*, _title.c_str()); } static void draw_ime_composition(rct_drawpixelinfo* dpi, int cursorX, int cursorY) diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h index 35be3ee703..f98281c46f 100644 --- a/src/openrct2-ui/windows/Window.h +++ b/src/openrct2-ui/windows/Window.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2019 OpenRCT2 developers + * Copyright (c) 2014-2020 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -156,6 +156,10 @@ 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); +void window_text_input_open( + const std::string_view& title, const std::string_view& description, const std::string_view& initialValue, size_t maxLength, + std::function okCallback, std::function cancelCallback); + rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, const rct_object_entry* missingObjects); rct_window* window_ride_construction_open(); diff --git a/src/openrct2/scripting/Duktape.hpp b/src/openrct2/scripting/Duktape.hpp index 54f26c8eee..43d6ba3b53 100644 --- a/src/openrct2/scripting/Duktape.hpp +++ b/src/openrct2/scripting/Duktape.hpp @@ -199,6 +199,12 @@ namespace OpenRCT2::Scripting return DukValue::take_from_stack(ctx); } + template<> inline DukValue ToDuk(duk_context* ctx, const std::string_view& value) + { + duk_push_lstring(ctx, value.data(), value.size()); + return DukValue::take_from_stack(ctx); + } + } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/title/TitleSequenceManager.cpp b/src/openrct2/title/TitleSequenceManager.cpp index 394d05bcfc..ca59fd019e 100644 --- a/src/openrct2/title/TitleSequenceManager.cpp +++ b/src/openrct2/title/TitleSequenceManager.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2019 OpenRCT2 developers + * Copyright (c) 2014-2020 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -163,7 +163,7 @@ namespace TitleSequenceManager auto path = Path::Combine(GetUserSequencesPath(), name); if (isZip) { - path = Path::Combine(path, TITLE_SEQUENCE_EXTENSION); + path += TITLE_SEQUENCE_EXTENSION; } return path; }