Merge pull request #19447 from ZehMatt/fix-19445

Fix #19445: Text not cleared using Ctrl+Backspace
This commit is contained in:
Matthias Moninger 2023-02-26 19:22:12 +02:00 committed by GitHub
commit c062f59d33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 313 additions and 178 deletions

View File

@ -25,6 +25,7 @@
- Improved: [#19067] New Ride window now allows filtering similarly to Object Selection.
- Improved: [#19272] Scenery window now allows filtering similarly to Object Selection.
- Improved: [#19463] Added W and Y with circumflex to sprite font (for Welsh).
- Improved: [#19447] The control key now enables word jumping in text input fields.
- Change: [#19018] Renamed actions to fit the naming scheme.
- Change: [#19091] [Plugin] Add game action information to callback arguments of custom actions.
- Change: [#19233] Reduce lift speed minimum and maximum values for “Classic Wooden Coaster”.

View File

@ -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();
@ -121,14 +118,6 @@ void TextComposition::HandleMessage(const SDL_Event* e)
break;
}
// Clear the input on <CTRL>Backspace (Windows/Linux) or <MOD>Backspace (macOS)
if (key == SDLK_BACKSPACE && (modifier & KEYBOARD_PRIMARY_MODIFIER))
{
Clear();
console.RefreshCaret(_session.SelectionStart);
WindowUpdateTextbox();
}
switch (key)
{
case SDLK_BACKSPACE:
@ -136,7 +125,10 @@ void TextComposition::HandleMessage(const SDL_Event* e)
if (_session.SelectionStart > 0)
{
size_t endOffset = _session.SelectionStart;
CursorLeft();
if (modifier & KEYBOARD_PRIMARY_MODIFIER)
CaretMoveToLeftToken();
else
CaretMoveLeft();
_session.SelectionSize = endOffset - _session.SelectionStart;
Delete();
@ -145,17 +137,20 @@ void TextComposition::HandleMessage(const SDL_Event* e)
}
break;
case SDLK_HOME:
CursorHome();
CaretMoveToStart();
console.RefreshCaret(_session.SelectionStart);
break;
case SDLK_END:
CursorEnd();
CaretMoveToEnd();
console.RefreshCaret(_session.SelectionStart);
break;
case SDLK_DELETE:
{
size_t startOffset = _session.SelectionStart;
CursorRight();
if (modifier & KEYBOARD_PRIMARY_MODIFIER)
CaretMoveToRightToken();
else
CaretMoveRight();
_session.SelectionSize = _session.SelectionStart - startOffset;
_session.SelectionStart = startOffset;
Delete();
@ -167,17 +162,23 @@ void TextComposition::HandleMessage(const SDL_Event* e)
WindowCancelTextbox();
break;
case SDLK_LEFT:
CursorLeft();
if (modifier & KEYBOARD_PRIMARY_MODIFIER)
CaretMoveToLeftToken();
else
CaretMoveLeft();
console.RefreshCaret(_session.SelectionStart);
break;
case SDLK_RIGHT:
CursorRight();
if (modifier & KEYBOARD_PRIMARY_MODIFIER)
CaretMoveToRightToken();
else
CaretMoveRight();
console.RefreshCaret(_session.SelectionStart);
break;
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;
@ -195,15 +196,15 @@ void TextComposition::HandleMessage(const SDL_Event* e)
}
}
void TextComposition::CursorHome()
void TextComposition::CaretMoveToStart()
{
_session.SelectionStart = 0;
}
void TextComposition::CursorEnd()
void TextComposition::CaretMoveToEnd()
{
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--;
@ -213,38 +214,166 @@ void TextComposition::CursorEnd()
_session.SelectionStart = selectionOffset;
}
void TextComposition::CursorLeft()
void TextComposition::CaretMoveLeft()
{
size_t selectionOffset = _session.SelectionStart;
if (selectionOffset > 0)
if (selectionOffset == 0)
return;
const utf8* ch = _session.Buffer->c_str() + selectionOffset;
do
{
const utf8* ch = _session.Buffer + selectionOffset;
do
ch--;
selectionOffset--;
} while (!UTF8IsCodepointStart(ch) && selectionOffset > 0);
_session.SelectionStart = selectionOffset;
}
void TextComposition::CaretMoveRight()
{
size_t selectionOffset = _session.SelectionStart;
size_t selectionMaxOffset = _session.Buffer->size();
if (selectionOffset >= selectionMaxOffset)
return;
const utf8* ch = _session.Buffer->c_str() + _session.SelectionStart;
do
{
ch++;
selectionOffset++;
} while (!UTF8IsCodepointStart(ch) && selectionOffset < selectionMaxOffset);
_session.SelectionSize = std::max<size_t>(0, _session.SelectionSize - (selectionOffset - _session.SelectionStart));
_session.SelectionStart = selectionOffset;
}
static bool isWhitespace(uint32_t cp)
{
return cp == ' ' || cp == '\t';
}
void TextComposition::CaretMoveToLeftToken()
{
if (_session.SelectionStart == 0)
return;
size_t selectionOffset = _session.SelectionStart - 1;
size_t lastChar = selectionOffset;
const utf8* ch = _session.Buffer->c_str() + selectionOffset;
// Read until first non-whitespace.
while (selectionOffset > 0)
{
while (!UTF8IsCodepointStart(ch) && selectionOffset > 0)
{
ch--;
selectionOffset--;
} while (!UTF8IsCodepointStart(ch) && selectionOffset > 0);
}
_session.SelectionStart = selectionOffset;
auto cp = UTF8GetNext(ch, nullptr);
if (!isWhitespace(cp))
{
lastChar = selectionOffset;
break;
}
ch--;
selectionOffset--;
}
// Skip white spaces.
while (selectionOffset > 0)
{
while (!UTF8IsCodepointStart(ch) && selectionOffset > 0)
{
ch--;
selectionOffset--;
}
auto cp = UTF8GetNext(ch, nullptr);
if (isWhitespace(cp))
break;
lastChar = selectionOffset;
ch--;
selectionOffset--;
}
_session.SelectionSize = std::max<size_t>(0, _session.SelectionSize - (selectionOffset - _session.SelectionStart));
_session.SelectionStart = selectionOffset == 0 ? 0 : lastChar;
}
void TextComposition::CursorRight()
void TextComposition::CaretMoveToRightToken()
{
size_t selectionOffset = _session.SelectionStart;
size_t selectionMaxOffset = _session.Size;
if (selectionOffset < selectionMaxOffset)
{
const utf8* ch = _session.Buffer + _session.SelectionStart;
do
{
ch++;
selectionOffset++;
} while (!UTF8IsCodepointStart(ch) && selectionOffset < selectionMaxOffset);
size_t selectionMaxOffset = _session.Buffer->size();
_session.SelectionSize = std::max<size_t>(0, _session.SelectionSize - (selectionOffset - _session.SelectionStart));
_session.SelectionStart = selectionOffset;
if (selectionOffset >= selectionMaxOffset)
return;
const utf8* ch = _session.Buffer->c_str() + selectionOffset;
// Find a valid codepoint start.
while (!UTF8IsCodepointStart(ch) && selectionOffset < selectionMaxOffset)
{
ch++;
selectionOffset++;
}
auto cp = UTF8GetNext(ch, nullptr);
if (isWhitespace(cp))
{
// Read until first non-whitespace.
while (selectionOffset < selectionMaxOffset)
{
do
{
ch++;
selectionOffset++;
} while (!UTF8IsCodepointStart(ch) && selectionOffset < selectionMaxOffset);
cp = UTF8GetNext(ch, nullptr);
if (!isWhitespace(cp))
break;
}
}
else
{
// Read until first non-whitespace.
while (selectionOffset < selectionMaxOffset)
{
do
{
ch++;
selectionOffset++;
} while (!UTF8IsCodepointStart(ch) && selectionOffset < selectionMaxOffset);
cp = UTF8GetNext(ch, nullptr);
if (isWhitespace(cp))
break;
}
// Skip white spaces.
while (selectionOffset < selectionMaxOffset)
{
// Read until first non-whitespace.
do
{
ch++;
selectionOffset++;
} while (!UTF8IsCodepointStart(ch) && selectionOffset < selectionMaxOffset);
cp = UTF8GetNext(ch, nullptr);
if (!isWhitespace(cp))
break;
}
}
_session.SelectionSize = std::max<size_t>(0, _session.SelectionSize - (selectionOffset - _session.SelectionStart));
_session.SelectionStart = selectionOffset;
}
void TextComposition::Insert(const utf8* text)
@ -260,36 +389,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 +424,33 @@ void TextComposition::Clear()
void TextComposition::Delete()
{
size_t selectionOffset = _session.SelectionStart;
size_t selectionMaxOffset = _session.Size;
size_t selectionMaxOffset = std::min(_session.SelectionStart + _session.SelectionSize, _session.Buffer->size());
if (selectionOffset >= selectionMaxOffset)
return;
// Find out how many bytes to delete.
const utf8* ch = _session.Buffer + _session.SelectionStart;
do
const utf8* ch = _session.Buffer->c_str() + _session.SelectionStart;
while (selectionOffset < selectionMaxOffset)
{
ch++;
selectionOffset++;
} while (!UTF8IsCodepointStart(ch) && selectionOffset < selectionMaxOffset);
do
{
ch++;
selectionOffset++;
} while (!UTF8IsCodepointStart(ch) && selectionOffset < selectionMaxOffset);
}
utf8* buffer = _session.Buffer;
utf8* targetShiftPtr = buffer + _session.SelectionStart;
utf8* sourceShiftPtr = targetShiftPtr + _session.SelectionSize;
size_t bytesToSkip = selectionOffset - _session.SelectionStart;
if (bytesToSkip == 0)
return;
// 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));
memmove(targetShiftPtr, sourceShiftPtr, shiftSize);
_session.Buffer->erase(
_session.Buffer->begin() + _session.SelectionStart, _session.Buffer->begin() + _session.SelectionStart + bytesToSkip);
_session.SelectionSize = 0;
RecalculateLength();
}
void TextComposition::RecalculateLength()
{
_session.Size = String::SizeOf(_session.Buffer);
_session.Length = String::LengthOf(_session.Buffer);
_session.Length = String::LengthOf(_session.Buffer->c_str());
}

View File

@ -32,15 +32,17 @@ 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);
private:
void CursorHome();
void CursorEnd();
void CursorLeft();
void CursorRight();
void CaretMoveToStart();
void CaretMoveToEnd();
void CaretMoveLeft();
void CaretMoveRight();
void CaretMoveToLeftToken();
void CaretMoveToRightToken();
void Insert(const utf8* text);
void InsertCodepoint(codepoint_t codepoint);
void Clear();

View File

@ -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

View File

@ -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()

View File

@ -30,14 +30,17 @@ namespace OpenRCT2::Ui
ScreenCoordsXY _consoleTopLeft;
ScreenCoordsXY _consoleBottomRight;
ScreenCoordsXY _lastMainViewport;
std::deque<std::string> _consoleLines;
utf8 _consoleCurrentLine[CONSOLE_INPUT_SIZE] = {};
std::vector<std::string> _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<u8string> _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;

View File

@ -1168,7 +1168,7 @@ static void WidgetTextBoxDraw(DrawPixelInfo* dpi, WindowBase& w, WidgetIndex wid
+ 3;
int32_t width = 6;
if (static_cast<uint32_t>(gTextInput->SelectionStart) < strlen(gTextBoxInput))
if (static_cast<uint32_t>(gTextInput->SelectionStart) < gTextBoxInput.size())
{
// Make a new 1 character wide string for measuring the width
// of the character that the cursor is under.

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -56,7 +56,7 @@ private:
int32_t _cursorBlink{};
size_t _maxInputLength{};
std::vector<utf8> _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<void(std::string_view)> callback, std::function<void()> cancelCallback)

View File

@ -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);
}

View File

@ -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();

View File

@ -27,10 +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 uint32_t _chatHistoryIndex = 0;
static u8string _chatCurrentLine;
static std::deque<u8string> _chatHistory;
static std::deque<uint32_t> _chatHistoryTime;
static uint32_t _chatCaretTicks = 0;
static int32_t _chatLeft;
static int32_t _chatTop;
@ -40,8 +39,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 +53,7 @@ bool ChatAvailable()
void ChatOpen()
{
gChatOpen = true;
_chatTextInputSession = ContextStartTextInput(_chatCurrentLine, sizeof(_chatCurrentLine));
_chatTextInputSession = ContextStartTextInput(_chatCurrentLine, CHAT_MAX_MESSAGE_LENGTH);
}
void ChatClose()
@ -77,8 +76,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 +102,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 +111,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 +158,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 +199,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 +220,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<size_t>(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 +240,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 +255,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.

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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