diff --git a/src/openrct2-ui/TextComposition.cpp b/src/openrct2-ui/TextComposition.cpp index 76ff77fcd6..28b762a9ac 100644 --- a/src/openrct2-ui/TextComposition.cpp +++ b/src/openrct2-ui/TextComposition.cpp @@ -36,19 +36,16 @@ bool TextComposition::IsActive() return SDL_IsTextInputActive() && _session.Buffer != nullptr; } -TextInputSession* TextComposition::Start(utf8* buffer, size_t bufferSize) +TextInputSession* TextComposition::Start(u8string& buffer, size_t maxLength) { - Guard::ArgumentNotNull(buffer); - // TODO This doesn't work, and position could be improved to where text entry is SDL_Rect rect = { 10, 10, 100, 100 }; SDL_SetTextInputRect(&rect); SDL_StartTextInput(); - _session.Buffer = buffer; - _session.BufferSize = bufferSize - 1; - _session.Size = strlen(buffer); - _session.SelectionStart = _session.Size; + _session.Buffer = &buffer; + _session.MaxLength = maxLength; + _session.SelectionStart = buffer.size(); _session.SelectionSize = 0; _session.ImeBuffer = _imeBuffer; RecalculateLength(); @@ -177,7 +174,7 @@ void TextComposition::HandleMessage(const SDL_Event* e) case SDLK_c: if ((modifier & KEYBOARD_PRIMARY_MODIFIER) && _session.Length) { - SDL_SetClipboardText(_session.Buffer); + SDL_SetClipboardText(_session.Buffer->c_str()); ContextShowError(STR_COPY_INPUT_TO_CLIPBOARD, STR_NONE, {}); } break; @@ -202,8 +199,8 @@ void TextComposition::CursorHome() void TextComposition::CursorEnd() { - size_t selectionOffset = _session.Size; - const utf8* ch = _session.Buffer + _session.SelectionStart; + size_t selectionOffset = _session.Buffer->size(); + const utf8* ch = _session.Buffer->c_str() + _session.SelectionStart; while (!UTF8IsCodepointStart(ch) && selectionOffset > 0) { ch--; @@ -218,7 +215,7 @@ void TextComposition::CursorLeft() size_t selectionOffset = _session.SelectionStart; if (selectionOffset > 0) { - const utf8* ch = _session.Buffer + selectionOffset; + const utf8* ch = _session.Buffer->c_str() + selectionOffset; do { ch--; @@ -232,10 +229,10 @@ void TextComposition::CursorLeft() void TextComposition::CursorRight() { size_t selectionOffset = _session.SelectionStart; - size_t selectionMaxOffset = _session.Size; + size_t selectionMaxOffset = _session.Buffer->size(); if (selectionOffset < selectionMaxOffset) { - const utf8* ch = _session.Buffer + _session.SelectionStart; + const utf8* ch = _session.Buffer->c_str() + _session.SelectionStart; do { ch++; @@ -260,36 +257,33 @@ void TextComposition::Insert(const utf8* text) void TextComposition::InsertCodepoint(codepoint_t codepoint) { size_t codepointLength = UTF8GetCodepointLength(codepoint); - size_t remainingSize = _session.BufferSize - _session.Size; - if (codepointLength <= remainingSize) + size_t remainingSize = _session.MaxLength - _session.Length; + if (remainingSize > 0) { - utf8* buffer = _session.Buffer; + const auto bufSize = _session.Buffer->size(); + _session.Buffer->resize(_session.Buffer->size() + codepointLength); + + // FIXME: Just insert the codepoint into the string, don't use memmove + + utf8* buffer = _session.Buffer->data(); utf8* insertPtr = buffer + _session.SelectionStart; - if (_session.SelectionStart < _session.Size) + if (_session.SelectionStart < bufSize) { - // Shift bytes (including null terminator) right to make room for new codepoint + // Shift bytes to the right to make room for new codepoint utf8* targetShiftPtr = insertPtr + codepointLength; - size_t shiftSize = _session.Size - _session.SelectionStart + 1; + size_t shiftSize = bufSize - _session.SelectionStart; memmove(targetShiftPtr, insertPtr, shiftSize); } - else - { - // Character is appended onto the end, so set byte after it to null terminator - buffer[_session.Size + codepointLength] = 0; - } UTF8WriteCodepoint(insertPtr, codepoint); _session.SelectionStart += codepointLength; - _session.Size += codepointLength; _session.Length++; } } void TextComposition::Clear() { - utf8* buffer = _session.Buffer; - buffer[0] = 0; - _session.Size = 0; + _session.Buffer->clear(); _session.Length = 0; _session.SelectionStart = 0; _session.SelectionSize = 0; @@ -298,31 +292,34 @@ void TextComposition::Clear() void TextComposition::Delete() { size_t selectionOffset = _session.SelectionStart; - size_t selectionMaxOffset = _session.Size; + size_t selectionMaxOffset = _session.Buffer->size(); // Find out how many bytes to delete. - const utf8* ch = _session.Buffer + _session.SelectionStart; + const utf8* ch = _session.Buffer->c_str() + _session.SelectionStart; do { ch++; selectionOffset++; } while (!UTF8IsCodepointStart(ch) && selectionOffset < selectionMaxOffset); - utf8* buffer = _session.Buffer; + // FIXME: Just erase the range with iterators. + utf8* buffer = _session.Buffer->data(); utf8* targetShiftPtr = buffer + _session.SelectionStart; utf8* sourceShiftPtr = targetShiftPtr + _session.SelectionSize; size_t bytesToSkip = selectionOffset - _session.SelectionStart; // std::min() is used to ensure that shiftSize doesn't underflow; it should be between 0 and _session.Size - size_t shiftSize = _session.Size - - std::min(_session.Size, (_session.SelectionStart - _session.SelectionSize + bytesToSkip)); + size_t shiftSize = _session.Buffer->size() + - std::min(_session.Buffer->size(), (_session.SelectionStart - _session.SelectionSize + bytesToSkip)); memmove(targetShiftPtr, sourceShiftPtr, shiftSize); _session.SelectionSize = 0; + + _session.Buffer->resize(_session.Buffer->size() - shiftSize); + RecalculateLength(); } void TextComposition::RecalculateLength() { - _session.Size = String::SizeOf(_session.Buffer); - _session.Length = String::LengthOf(_session.Buffer); + _session.Length = String::LengthOf(_session.Buffer->c_str()); } diff --git a/src/openrct2-ui/TextComposition.h b/src/openrct2-ui/TextComposition.h index 8e08d1b154..3dcc96575b 100644 --- a/src/openrct2-ui/TextComposition.h +++ b/src/openrct2-ui/TextComposition.h @@ -32,7 +32,7 @@ namespace OpenRCT2::Ui public: bool IsActive(); - TextInputSession* Start(utf8* buffer, size_t bufferSize); + TextInputSession* Start(u8string& buffer, size_t maxLength); void Stop(); void HandleMessage(const SDL_Event* e); diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index d784e6fe6a..8831e26987 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -310,9 +310,9 @@ public: return _textComposition.IsActive(); } - TextInputSession* StartTextInput(utf8* buffer, size_t bufferSize) override + TextInputSession* StartTextInput(u8string& buffer, size_t maxLength) override { - return _textComposition.Start(buffer, bufferSize); + return _textComposition.Start(buffer, maxLength); } void StopTextInput() override diff --git a/src/openrct2-ui/interface/InGameConsole.cpp b/src/openrct2-ui/interface/InGameConsole.cpp index 7d5f6e4a76..d850762e96 100644 --- a/src/openrct2-ui/interface/InGameConsole.cpp +++ b/src/openrct2-ui/interface/InGameConsole.cpp @@ -76,24 +76,22 @@ void InGameConsole::Input(ConsoleInput input) if (_consoleHistoryIndex > 0) { _consoleHistoryIndex--; - std::memcpy(_consoleCurrentLine, _consoleHistory[_consoleHistoryIndex], CONSOLE_INPUT_SIZE); + _consoleCurrentLine = _consoleHistory[_consoleHistoryIndex]; } - _consoleTextInputSession->Size = strlen(_consoleTextInputSession->Buffer); - _consoleTextInputSession->Length = UTF8Length(_consoleTextInputSession->Buffer); - _consoleTextInputSession->SelectionStart = strlen(_consoleCurrentLine); + _consoleTextInputSession->Length = UTF8Length(_consoleCurrentLine.c_str()); + _consoleTextInputSession->SelectionStart = _consoleCurrentLine.size(); break; case ConsoleInput::HistoryNext: - if (_consoleHistoryIndex < _consoleHistoryCount - 1) + if (_consoleHistoryIndex < _consoleHistory.size() - 1) { _consoleHistoryIndex++; - std::memcpy(_consoleCurrentLine, _consoleHistory[_consoleHistoryIndex], CONSOLE_INPUT_SIZE); - _consoleTextInputSession->Size = strlen(_consoleTextInputSession->Buffer); - _consoleTextInputSession->Length = UTF8Length(_consoleTextInputSession->Buffer); - _consoleTextInputSession->SelectionStart = strlen(_consoleCurrentLine); + _consoleCurrentLine = _consoleHistory[_consoleHistoryIndex]; + _consoleTextInputSession->Length = UTF8Length(_consoleCurrentLine.c_str()); + _consoleTextInputSession->SelectionStart = _consoleCurrentLine.size(); } else { - _consoleHistoryIndex = _consoleHistoryCount; + _consoleHistoryIndex = _consoleHistory.size(); ClearInput(); } break; @@ -116,23 +114,21 @@ void InGameConsole::Input(ConsoleInput input) void InGameConsole::ClearInput() { - _consoleCurrentLine[0] = 0; + _consoleCurrentLine.clear(); if (_isOpen) { - ContextStartTextInput(_consoleCurrentLine, sizeof(_consoleCurrentLine)); + _consoleTextInputSession = ContextStartTextInput(_consoleCurrentLine, CONSOLE_INPUT_SIZE); } } -void InGameConsole::HistoryAdd(const utf8* src) +void InGameConsole::HistoryAdd(const u8string& src) { - if (_consoleHistoryCount >= CONSOLE_HISTORY_SIZE) + if (_consoleHistory.size() >= CONSOLE_HISTORY_SIZE) { - for (int32_t i = 0; i < _consoleHistoryCount - 1; i++) - std::memcpy(_consoleHistory[i], _consoleHistory[i + 1], CONSOLE_INPUT_SIZE); - _consoleHistoryCount--; + _consoleHistory.pop_front(); } - std::memcpy(_consoleHistory[_consoleHistoryCount++], src, CONSOLE_INPUT_SIZE); - _consoleHistoryIndex = _consoleHistoryCount; + _consoleHistory.push_back(src); + _consoleHistoryIndex = _consoleHistory.size(); } void InGameConsole::ScrollToEnd() @@ -148,9 +144,9 @@ void InGameConsole::RefreshCaret(size_t position) { _consoleCaretTicks = 0; _selectionStart = position; - char tempString[TEXT_INPUT_SIZE] = { 0 }; - std::memcpy(tempString, &_consoleCurrentLine, _selectionStart); - _caretScreenPosX = GfxGetStringWidthNoFormatting(tempString, InGameConsoleGetFontStyle()); + + auto text = u8string_view{ _consoleCurrentLine }.substr(0, _selectionStart); + _caretScreenPosX = GfxGetStringWidthNoFormatting(text, InGameConsoleGetFontStyle()); } void InGameConsole::Scroll(int32_t linesToScroll) @@ -181,7 +177,7 @@ void InGameConsole::Open() _isOpen = true; ScrollToEnd(); RefreshCaret(); - _consoleTextInputSession = ContextStartTextInput(_consoleCurrentLine, sizeof(_consoleCurrentLine)); + _consoleTextInputSession = ContextStartTextInput(_consoleCurrentLine, CONSOLE_INPUT_SIZE); } void InGameConsole::Close() diff --git a/src/openrct2-ui/interface/InGameConsole.h b/src/openrct2-ui/interface/InGameConsole.h index 19582921f5..000a93d550 100644 --- a/src/openrct2-ui/interface/InGameConsole.h +++ b/src/openrct2-ui/interface/InGameConsole.h @@ -30,14 +30,17 @@ namespace OpenRCT2::Ui ScreenCoordsXY _consoleTopLeft; ScreenCoordsXY _consoleBottomRight; ScreenCoordsXY _lastMainViewport; - std::deque _consoleLines; - utf8 _consoleCurrentLine[CONSOLE_INPUT_SIZE] = {}; + + std::vector _consoleLines; + u8string _consoleCurrentLine; + int32_t _consoleCaretTicks; int32_t _consoleScrollPos = 0; TextInputSession* _consoleTextInputSession; - utf8 _consoleHistory[CONSOLE_HISTORY_SIZE][CONSOLE_INPUT_SIZE]; - int32_t _consoleHistoryIndex = 0; - int32_t _consoleHistoryCount = 0; + + std::deque _consoleHistory; + size_t _consoleHistoryIndex = 0; + size_t _selectionStart = 0; int32_t _caretScreenPosX = 0; @@ -67,7 +70,7 @@ namespace OpenRCT2::Ui private: void ClearInput(); void ClearLine(); - void HistoryAdd(const utf8* src); + void HistoryAdd(const u8string& src); void WritePrompt(); void ScrollToEnd(); void Invalidate() const; diff --git a/src/openrct2-ui/interface/Widget.cpp b/src/openrct2-ui/interface/Widget.cpp index 3ffbbdbb6a..03ba5f3c74 100644 --- a/src/openrct2-ui/interface/Widget.cpp +++ b/src/openrct2-ui/interface/Widget.cpp @@ -1168,7 +1168,7 @@ static void WidgetTextBoxDraw(DrawPixelInfo* dpi, WindowBase& w, WidgetIndex wid + 3; int32_t width = 6; - if (static_cast(gTextInput->SelectionStart) < strlen(gTextBoxInput)) + if (static_cast(gTextInput->SelectionStart) < gTextBoxInput.size()) { // Make a new 1 character wide string for measuring the width // of the character that the cursor is under. diff --git a/src/openrct2-ui/windows/EditorObjectiveOptions.cpp b/src/openrct2-ui/windows/EditorObjectiveOptions.cpp index f774ebd41d..c10fab1c77 100644 --- a/src/openrct2-ui/windows/EditorObjectiveOptions.cpp +++ b/src/openrct2-ui/windows/EditorObjectiveOptions.cpp @@ -129,7 +129,7 @@ static void WindowEditorObjectiveOptionsMainResize(WindowBase *w); static void WindowEditorObjectiveOptionsMainMousedown(WindowBase *w, WidgetIndex widgetIndex, Widget* widget); static void WindowEditorObjectiveOptionsMainDropdown(WindowBase *w, WidgetIndex widgetIndex, int32_t dropdownIndex); static void WindowEditorObjectiveOptionsMainUpdate(WindowBase *w); -static void WindowEditorObjectiveOptionsMainTextinput(WindowBase *w, WidgetIndex widgetIndex, char *text); +static void WindowEditorObjectiveOptionsMainTextinput(WindowBase *w, WidgetIndex widgetIndex, const char *text); static void WindowEditorObjectiveOptionsMainInvalidate(WindowBase *w); static void WindowEditorObjectiveOptionsMainPaint(WindowBase *w, DrawPixelInfo *dpi); @@ -673,7 +673,7 @@ static void WindowEditorObjectiveOptionsMainUpdate(WindowBase* w) * * rct2: 0x00671A73 */ -static void WindowEditorObjectiveOptionsMainTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text) +static void WindowEditorObjectiveOptionsMainTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text) { if (text == nullptr) return; diff --git a/src/openrct2-ui/windows/MapGen.cpp b/src/openrct2-ui/windows/MapGen.cpp index 27252433c6..39dc6c9cf6 100644 --- a/src/openrct2-ui/windows/MapGen.cpp +++ b/src/openrct2-ui/windows/MapGen.cpp @@ -204,7 +204,7 @@ static void WindowMapgenBaseMouseup(WindowBase* w, WidgetIndex widgetIndex); static void WindowMapgenBaseMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); static void WindowMapgenBaseDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); static void WindowMapgenBaseUpdate(WindowBase* w); -static void WindowMapgenTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text); +static void WindowMapgenTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text); static void WindowMapgenBaseInvalidate(WindowBase* w); static void WindowMapgenBasePaint(WindowBase* w, DrawPixelInfo* dpi); @@ -623,7 +623,7 @@ static void WindowMapgenBaseUpdate(WindowBase* w) WidgetInvalidate(*w, WIDX_TAB_1); } -static void WindowMapgenTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text) +static void WindowMapgenTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text) { int32_t value; char* end; diff --git a/src/openrct2-ui/windows/Multiplayer.cpp b/src/openrct2-ui/windows/Multiplayer.cpp index b432c0db02..18313f3125 100644 --- a/src/openrct2-ui/windows/Multiplayer.cpp +++ b/src/openrct2-ui/windows/Multiplayer.cpp @@ -144,7 +144,7 @@ static void WindowMultiplayerGroupsUpdate(WindowBase *w); static void WindowMultiplayerGroupsScrollgetsize(WindowBase *w, int32_t scrollIndex, int32_t *width, int32_t *height); static void WindowMultiplayerGroupsScrollmousedown(WindowBase *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); static void WindowMultiplayerGroupsScrollmouseover(WindowBase *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void WindowMultiplayerGroupsTextInput(WindowBase *w, WidgetIndex widgetIndex, char *text); +static void WindowMultiplayerGroupsTextInput(WindowBase *w, WidgetIndex widgetIndex, const char *text); static void WindowMultiplayerGroupsInvalidate(WindowBase *w); static void WindowMultiplayerGroupsPaint(WindowBase *w, DrawPixelInfo *dpi); static void WindowMultiplayerGroupsScrollpaint(WindowBase *w, DrawPixelInfo *dpi, int32_t scrollIndex); @@ -814,7 +814,7 @@ static void WindowMultiplayerGroupsScrollmouseover(WindowBase* w, int32_t scroll w->Invalidate(); } -static void WindowMultiplayerGroupsTextInput(WindowBase* w, WidgetIndex widgetIndex, char* text) +static void WindowMultiplayerGroupsTextInput(WindowBase* w, WidgetIndex widgetIndex, const char* text) { if (widgetIndex != WIDX_RENAME_GROUP) return; diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 91086f4894..d5f5ce6249 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -436,7 +436,7 @@ static void WindowRideMainResize(WindowBase* w); static void WindowRideMainMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); static void WindowRideMainDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); static void WindowRideMainUpdate(WindowBase* w); -static void WindowRideMainTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text); +static void WindowRideMainTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text); static void WindowRideMainViewportRotate(WindowBase* w); static void WindowRideMainInvalidate(WindowBase* w); static void WindowRideMainPaint(WindowBase* w, DrawPixelInfo* dpi); @@ -459,7 +459,7 @@ static void WindowRideOperatingLengthWindow(WindowBase* w, WidgetIndex widgetInd static void WindowRideOperatingTweakTextInput(WindowBase* w, const Ride& ride); static void WindowRideOperatingDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex); static void WindowRideOperatingUpdate(WindowBase* w); -static void WindowRideOperatingTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text); +static void WindowRideOperatingTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text); static void WindowRideOperatingInvalidate(WindowBase* w); static void WindowRideOperatingPaint(WindowBase* w, DrawPixelInfo* dpi); @@ -518,7 +518,7 @@ static void WindowRideIncomeMouseup(WindowBase* w, WidgetIndex widgetIndex); static void WindowRideIncomeResize(WindowBase* w); static void WindowRideIncomeMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget); static void WindowRideIncomeUpdate(WindowBase* w); -static void WindowRideIncomeTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text); +static void WindowRideIncomeTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text); static void WindowRideIncomeInvalidate(WindowBase* w); static void WindowRideIncomePaint(WindowBase* w, DrawPixelInfo* dpi); static bool WindowRideIncomeCanModifyPrimaryPrice(WindowBase* w); @@ -2273,7 +2273,7 @@ static void WindowRideMainUpdate(WindowBase* w) * * rct2: 0x006AF2F9 */ -static void WindowRideMainTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text) +static void WindowRideMainTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text) { if (widgetIndex != WIDX_RENAME || text == nullptr) return; @@ -3425,7 +3425,7 @@ static void WindowRideOperatingUpdate(WindowBase* w) } } -static void WindowRideOperatingTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text) +static void WindowRideOperatingTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text) { if (text == nullptr) return; @@ -6601,7 +6601,7 @@ static void WindowRideIncomeUpdate(WindowBase* w) } } -static void WindowRideIncomeTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text) +static void WindowRideIncomeTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text) { if ((widgetIndex != WIDX_PRIMARY_PRICE && widgetIndex != WIDX_SECONDARY_PRICE) || text == nullptr) return; diff --git a/src/openrct2-ui/windows/ServerList.cpp b/src/openrct2-ui/windows/ServerList.cpp index 9779876085..560ab95fef 100644 --- a/src/openrct2-ui/windows/ServerList.cpp +++ b/src/openrct2-ui/windows/ServerList.cpp @@ -81,7 +81,7 @@ static void WindowServerListUpdate(WindowBase* w); static void WindowServerListScrollGetsize(WindowBase* w, int32_t scrollIndex, int32_t* width, int32_t* height); static void WindowServerListScrollMousedown(WindowBase* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); static void WindowServerListScrollMouseover(WindowBase* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void WindowServerListTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text); +static void WindowServerListTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text); static OpenRCT2String WindowServerListTooltip(WindowBase* const w, const WidgetIndex widgetIndex, StringId fallback); static void WindowServerListInvalidate(WindowBase* w); static void WindowServerListPaint(WindowBase* w, DrawPixelInfo* dpi); @@ -303,7 +303,7 @@ static void WindowServerListScrollMouseover(WindowBase* w, int32_t scrollIndex, } } -static void WindowServerListTextinput(WindowBase* w, WidgetIndex widgetIndex, char* text) +static void WindowServerListTextinput(WindowBase* w, WidgetIndex widgetIndex, const char* text) { if (text == nullptr) return; diff --git a/src/openrct2-ui/windows/TextInput.cpp b/src/openrct2-ui/windows/TextInput.cpp index 83d0eaad92..8462fb8dfd 100644 --- a/src/openrct2-ui/windows/TextInput.cpp +++ b/src/openrct2-ui/windows/TextInput.cpp @@ -56,7 +56,7 @@ private: int32_t _cursorBlink{}; size_t _maxInputLength{}; - std::vector _buffer; + u8string _buffer; public: void OnOpen() override @@ -108,10 +108,13 @@ public: void SetText(std::string_view text, size_t maxLength) { - _buffer.resize(maxLength); - SafeStrCpy(_buffer.data(), std::string(text).c_str(), maxLength); + _buffer = u8string{ text }; + if (_buffer.size() > maxLength) + { + _buffer.resize(maxLength); + } _maxInputLength = maxLength; - gTextInput = ContextStartTextInput(_buffer.data(), maxLength); + gTextInput = ContextStartTextInput(_buffer, maxLength); } void SetCallback(std::function callback, std::function cancelCallback) diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index d36cfa56b0..e85e1b3d88 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -1439,7 +1439,7 @@ const uint8_t* ContextGetKeysPressed() return GetContext()->GetUiContext()->GetKeysPressed(); } -TextInputSession* ContextStartTextInput(utf8* buffer, size_t maxLength) +TextInputSession* ContextStartTextInput(u8string& buffer, size_t maxLength) { return GetContext()->GetUiContext()->StartTextInput(buffer, maxLength); } diff --git a/src/openrct2/Context.h b/src/openrct2/Context.h index 3ae83bf2f8..c038e0e72b 100644 --- a/src/openrct2/Context.h +++ b/src/openrct2/Context.h @@ -52,10 +52,9 @@ struct CursorState struct TextInputSession { - utf8* Buffer; // UTF-8 stream - size_t BufferSize; // Maximum number of bytes (excluding null terminator) - size_t Size; // Number of bytes (excluding null terminator) + u8string* Buffer; // UTF-8 string buffer, non-owning. size_t Length; // Number of codepoints + size_t MaxLength; // Maximum length of text, Length can't be larger than this. size_t SelectionStart; // Selection start, in bytes size_t SelectionSize; // Selection length in bytes @@ -200,7 +199,7 @@ void ContextSetCursorPosition(const ScreenCoordsXY& cursorPosition); const CursorState* ContextGetCursorState(); const uint8_t* ContextGetKeysState(); const uint8_t* ContextGetKeysPressed(); -TextInputSession* ContextStartTextInput(utf8* buffer, size_t maxLength); +TextInputSession* ContextStartTextInput(u8string& buffer, size_t maxLength); void ContextStopTextInput(); bool ContextIsInputActive(); void ContextTriggerResize(); diff --git a/src/openrct2/interface/Chat.cpp b/src/openrct2/interface/Chat.cpp index 79542ac142..6d3d763751 100644 --- a/src/openrct2/interface/Chat.cpp +++ b/src/openrct2/interface/Chat.cpp @@ -27,9 +27,9 @@ using namespace OpenRCT2; using namespace OpenRCT2::Audio; bool gChatOpen = false; -static char _chatCurrentLine[CHAT_MAX_MESSAGE_LENGTH]; -static char _chatHistory[CHAT_HISTORY_SIZE][CHAT_INPUT_SIZE]; -static uint32_t _chatHistoryTime[CHAT_HISTORY_SIZE]; +static u8string _chatCurrentLine; +static std::deque _chatHistory; +static std::deque _chatHistoryTime; static uint32_t _chatHistoryIndex = 0; static uint32_t _chatCaretTicks = 0; static int32_t _chatLeft; @@ -40,8 +40,8 @@ static int32_t _chatWidth; static int32_t _chatHeight; static TextInputSession* _chatTextInputSession; -static const char* ChatGetHistory(uint32_t index); -static uint32_t ChatHistoryGetTime(uint32_t index); +static const u8string& ChatGetHistory(size_t index); +static uint32_t ChatHistoryGetTime(size_t index); static void ChatClearInput(); static int32_t ChatHistoryDrawString(DrawPixelInfo* dpi, const char* text, const ScreenCoordsXY& screenCoords, int32_t width); @@ -54,7 +54,7 @@ bool ChatAvailable() void ChatOpen() { gChatOpen = true; - _chatTextInputSession = ContextStartTextInput(_chatCurrentLine, sizeof(_chatCurrentLine)); + _chatTextInputSession = ContextStartTextInput(_chatCurrentLine, CHAT_MAX_MESSAGE_LENGTH); } void ChatClose() @@ -77,8 +77,8 @@ void ChatToggle() void ChatInit() { - std::memset(_chatHistory, 0x00, sizeof(_chatHistory)); - std::memset(_chatHistoryTime, 0x00, sizeof(_chatHistoryTime)); + _chatHistory.clear(); + _chatHistoryTime.clear(); } void ChatUpdate() @@ -103,7 +103,7 @@ void ChatDraw(DrawPixelInfo* dpi, uint8_t chatBackgroundColor) _chatBottom = ContextGetHeight() - 45; _chatTop = _chatBottom - 10; - char* inputLine = _chatCurrentLine; + const char* inputLine = _chatCurrentLine.c_str(); int32_t inputLineHeight = 10; // Draw chat window @@ -112,14 +112,13 @@ void ChatDraw(DrawPixelInfo* dpi, uint8_t chatBackgroundColor) inputLineHeight = ChatStringWrappedGetHeight(inputLine, _chatWidth - 10); _chatTop -= inputLineHeight; - for (int32_t i = 0; i < CHAT_HISTORY_SIZE; i++) + for (const auto& entry : _chatHistory) { - if (ChatGetHistory(i)[0] == '\0') + if (entry.empty()) { continue; } - - lineBuffer.assign(ChatGetHistory(i)); + lineBuffer = entry; int32_t lineHeight = ChatStringWrappedGetHeight(lineBuffer, _chatWidth - 10); _chatTop -= (lineHeight + 5); } @@ -160,15 +159,18 @@ void ChatDraw(DrawPixelInfo* dpi, uint8_t chatBackgroundColor) int32_t stringHeight = 0; // Draw chat history - for (int32_t i = 0; i < CHAT_HISTORY_SIZE; i++, screenCoords.y -= stringHeight) + for (size_t i = 0; i < CHAT_HISTORY_SIZE; i++, screenCoords.y -= stringHeight) { + if (i >= _chatHistory.size()) + break; + uint32_t expireTime = ChatHistoryGetTime(i) + 10000; if (!gChatOpen && Platform::GetTicks() > expireTime) { break; } - lineBuffer.assign(ChatGetHistory(i)); + lineBuffer = ChatGetHistory(i); auto lineCh = lineBuffer.c_str(); stringHeight = ChatHistoryDrawString(dpi, lineCh, screenCoords, _chatWidth - 10) + 5; GfxSetDirtyBlocks( @@ -198,7 +200,7 @@ void ChatDraw(DrawPixelInfo* dpi, uint8_t chatBackgroundColor) // TODO: Show caret if the input text has multiple lines if (_chatCaretTicks < 15 && GfxGetStringWidth(lineBuffer, FontStyle::Medium) < (_chatWidth - 10)) { - lineBuffer.assign(_chatCurrentLine, _chatTextInputSession->SelectionStart); + lineBuffer.assign(_chatCurrentLine.c_str(), _chatTextInputSession->SelectionStart); int32_t caretX = screenCoords.x + GfxGetStringWidth(lineBuffer, FontStyle::Medium); int32_t caretY = screenCoords.y + 14; @@ -219,12 +221,14 @@ void ChatAddHistory(std::string_view s) std::string buffer = timeBuffer; buffer += s; - // Add to history list - int32_t index = _chatHistoryIndex % CHAT_HISTORY_SIZE; - std::fill_n(_chatHistory[index], CHAT_INPUT_SIZE, 0x00); - std::memcpy(_chatHistory[index], buffer.c_str(), std::min(buffer.size(), CHAT_INPUT_SIZE - 1)); - _chatHistoryTime[index] = Platform::GetTicks(); - _chatHistoryIndex++; + if (_chatHistory.size() >= CHAT_HISTORY_SIZE) + { + _chatHistory.pop_front(); + _chatHistoryTime.pop_front(); + } + + _chatHistory.push_back(buffer); + _chatHistoryTime.push_back(Platform::GetTicks()); // Log to file (src only as logging does its own timestamp) NetworkAppendChatLog(s); @@ -237,9 +241,9 @@ void ChatInput(enum ChatInput input) switch (input) { case ChatInput::Send: - if (_chatCurrentLine[0] != '\0') + if (!_chatCurrentLine.empty()) { - NetworkSendChat(_chatCurrentLine); + NetworkSendChat(_chatCurrentLine.c_str()); } ChatClearInput(); ChatClose(); @@ -252,19 +256,19 @@ void ChatInput(enum ChatInput input) } } -static const char* ChatGetHistory(uint32_t index) +static const u8string& ChatGetHistory(size_t index) { - return _chatHistory[(_chatHistoryIndex + CHAT_HISTORY_SIZE - index - 1) % CHAT_HISTORY_SIZE]; + return _chatHistory[index]; } -static uint32_t ChatHistoryGetTime(uint32_t index) +static uint32_t ChatHistoryGetTime(size_t index) { - return _chatHistoryTime[(_chatHistoryIndex + CHAT_HISTORY_SIZE - index - 1) % CHAT_HISTORY_SIZE]; + return _chatHistoryTime[index]; } static void ChatClearInput() { - _chatCurrentLine[0] = 0; + _chatCurrentLine.clear(); } // This method is the same as gfx_draw_string_left_wrapped. diff --git a/src/openrct2/interface/Window.cpp b/src/openrct2/interface/Window.cpp index 859837daba..9d69e6f893 100644 --- a/src/openrct2/interface/Window.cpp +++ b/src/openrct2/interface/Window.cpp @@ -44,7 +44,7 @@ WindowBase* gWindowAudioExclusive; WidgetIdentifier gCurrentTextBox = { { WindowClass::Null, 0 }, 0 }; WindowCloseModifier gLastCloseModifier = { { WindowClass::Null, 0 }, CloseWindowModifier::None }; -char gTextBoxInput[TEXT_INPUT_SIZE] = { 0 }; +u8string gTextBoxInput; int32_t gTextBoxFrameNo = 0; bool gUsingWidgetTextBox = false; TextInputSession* gTextInput; @@ -1570,7 +1570,7 @@ void WindowEventScrollMouseoverCall(WindowBase* w, int32_t scrollIndex, const Sc w->event_handlers->scroll_mouseover(w, scrollIndex, screenCoords); } -void WindowEventTextinputCall(WindowBase* w, WidgetIndex widgetIndex, char* text) +void WindowEventTextinputCall(WindowBase* w, WidgetIndex widgetIndex, const char* text) { if (w->event_handlers == nullptr) { @@ -1988,16 +1988,16 @@ void WindowStartTextbox( WindowCloseByClass(WindowClass::Textinput); // Clear the text input buffer - std::fill_n(gTextBoxInput, maxLength, 0x00); + gTextBoxInput.clear(); // Enter in the text input buffer any existing // text. if (existing_text != STR_NONE) - OpenRCT2::FormatStringLegacy(gTextBoxInput, TEXT_INPUT_SIZE, existing_text, &existing_args); - - // In order to prevent strings that exceed the maxLength - // from crashing the game. - gTextBoxInput[maxLength - 1] = '\0'; + { + char tempBuf[TEXT_INPUT_SIZE]{}; + size_t len = OpenRCT2::FormatStringLegacy(tempBuf, TEXT_INPUT_SIZE, existing_text, &existing_args); + gTextBoxInput.assign(tempBuf, len); + } gTextInput = ContextStartTextInput(gTextBoxInput, maxLength); } @@ -2037,7 +2037,7 @@ void WindowUpdateTextbox() gTextBoxFrameNo = 0; WindowBase* w = WindowFindByNumber(gCurrentTextBox.window.classification, gCurrentTextBox.window.number); WidgetInvalidate(*w, gCurrentTextBox.widget_index); - WindowEventTextinputCall(w, gCurrentTextBox.widget_index, gTextBoxInput); + WindowEventTextinputCall(w, gCurrentTextBox.widget_index, gTextBoxInput.c_str()); } } diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index 2102c62530..fb2b66a401 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -46,7 +46,7 @@ enum class CloseWindowModifier : uint8_t; #define TEXT_INPUT_SIZE 1024 #define TOP_TOOLBAR_HEIGHT 27 -extern char gTextBoxInput[TEXT_INPUT_SIZE]; +extern u8string gTextBoxInput; extern int32_t gTextBoxFrameNo; extern bool gUsingWidgetTextBox; extern struct TextInputSession* gTextInput; @@ -240,7 +240,7 @@ struct WindowEventList void (*scroll_mousedown)(struct WindowBase*, int32_t, const ScreenCoordsXY&){}; void (*scroll_mousedrag)(struct WindowBase*, int32_t, const ScreenCoordsXY&){}; void (*scroll_mouseover)(struct WindowBase*, int32_t, const ScreenCoordsXY&){}; - void (*text_input)(struct WindowBase*, WidgetIndex, char*){}; + void (*text_input)(struct WindowBase*, WidgetIndex, const char*){}; void (*viewport_rotate)(struct WindowBase*){}; void (*scroll_select)(struct WindowBase*, int32_t, int32_t){}; OpenRCT2String (*tooltip)(struct WindowBase*, const WidgetIndex, const StringId){}; @@ -705,7 +705,7 @@ void WindowGetScrollSize(WindowBase* w, int32_t scrollIndex, int32_t* width, int void WindowEventScrollMousedownCall(WindowBase* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); void WindowEventScrollMousedragCall(WindowBase* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); void WindowEventScrollMouseoverCall(WindowBase* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -void WindowEventTextinputCall(WindowBase* w, WidgetIndex widgetIndex, char* text); +void WindowEventTextinputCall(WindowBase* w, WidgetIndex widgetIndex, const char* text); void WindowEventViewportRotateCall(WindowBase* w); void WindowEventScrollSelectCall(WindowBase* w, int32_t scrollIndex, int32_t scrollAreaType); OpenRCT2String WindowEventTooltipCall(WindowBase* w, const WidgetIndex widgetIndex, const StringId fallback); diff --git a/src/openrct2/ui/DummyUiContext.cpp b/src/openrct2/ui/DummyUiContext.cpp index 3c54746d72..17323a7378 100644 --- a/src/openrct2/ui/DummyUiContext.cpp +++ b/src/openrct2/ui/DummyUiContext.cpp @@ -182,7 +182,7 @@ namespace OpenRCT2::Ui { return false; } - TextInputSession* StartTextInput([[maybe_unused]] utf8* buffer, [[maybe_unused]] size_t bufferSize) override + TextInputSession* StartTextInput([[maybe_unused]] u8string& buffer, [[maybe_unused]] size_t maxLength) override { return nullptr; } diff --git a/src/openrct2/ui/UiContext.h b/src/openrct2/ui/UiContext.h index eeed4b3c9b..52f4c13cde 100644 --- a/src/openrct2/ui/UiContext.h +++ b/src/openrct2/ui/UiContext.h @@ -154,7 +154,7 @@ namespace OpenRCT2 // Text input virtual bool IsTextInputActive() abstract; - virtual TextInputSession* StartTextInput(utf8* buffer, size_t bufferSize) abstract; + virtual TextInputSession* StartTextInput(u8string& buffer, size_t maxLength) abstract; virtual void StopTextInput() abstract; // In-game UI