Merge pull request #21653 from duncanspumpkin/moveTextInput

Move Text input widget to Ui library
This commit is contained in:
Michael Steenbeek 2024-04-26 18:53:43 +02:00 committed by GitHub
commit 7a1b4ce425
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 136 additions and 126 deletions

View File

@ -84,7 +84,7 @@ void TextComposition::HandleMessage(const SDL_Event* e)
Insert(e->text.text);
console.RefreshCaret(_session.SelectionStart);
WindowUpdateTextbox();
OpenRCT2::Ui::Windows::WindowUpdateTextbox();
}
break;
case SDL_KEYDOWN:
@ -127,7 +127,7 @@ void TextComposition::HandleMessage(const SDL_Event* e)
Delete();
console.RefreshCaret(_session.SelectionStart);
WindowUpdateTextbox();
OpenRCT2::Ui::Windows::WindowUpdateTextbox();
}
break;
case SDLK_HOME:
@ -149,11 +149,11 @@ void TextComposition::HandleMessage(const SDL_Event* e)
_session.SelectionStart = startOffset;
Delete();
console.RefreshCaret(_session.SelectionStart);
WindowUpdateTextbox();
OpenRCT2::Ui::Windows::WindowUpdateTextbox();
break;
}
case SDLK_RETURN:
WindowCancelTextbox();
OpenRCT2::Ui::Windows::WindowCancelTextbox();
break;
case SDLK_LEFT:
if (modifier & KEYBOARD_PRIMARY_MODIFIER)
@ -182,7 +182,7 @@ void TextComposition::HandleMessage(const SDL_Event* e)
utf8* text = SDL_GetClipboardText();
Insert(text);
SDL_free(text);
WindowUpdateTextbox();
OpenRCT2::Ui::Windows::WindowUpdateTextbox();
}
break;
}

View File

@ -207,7 +207,7 @@ void InputManager::Process(const InputEvent& e)
return;
}
if (gUsingWidgetTextBox)
if (OpenRCT2::Ui::Windows::IsUsingWidgetTextBox())
{
return;
}
@ -383,7 +383,7 @@ bool InputManager::GetState(const ShortcutInput& shortcut) const
bool InputManager::HasTextInputFocus() const
{
if (gUsingWidgetTextBox || gChatOpen)
if (OpenRCT2::Ui::Windows::IsUsingWidgetTextBox() || gChatOpen)
return true;
auto w = WindowFindByClass(WindowClass::Textinput);

View File

@ -1022,8 +1022,8 @@ static void InputWidgetLeft(const ScreenCoordsXY& screenCoords, WindowBase* w, W
if (widgetIndex == -1)
return;
if (windowClass != gCurrentTextBox.window.classification || windowNumber != gCurrentTextBox.window.number
|| widgetIndex != gCurrentTextBox.widget_index)
if (windowClass != GetCurrentTextBox().window.classification || windowNumber != GetCurrentTextBox().window.number
|| widgetIndex != GetCurrentTextBox().widget_index)
{
WindowCancelTextbox();
}

View File

@ -1137,8 +1137,9 @@ static void WidgetTextBoxDraw(DrawPixelInfo& dpi, WindowBase& w, WidgetIndex wid
// Get the colour
uint8_t colour = w.colours[widget.colour];
bool active = w.classification == gCurrentTextBox.window.classification && w.number == gCurrentTextBox.window.number
&& widgetIndex == gCurrentTextBox.widget_index;
auto& tbIdent = OpenRCT2::Ui::Windows::GetCurrentTextBox();
bool active = w.classification == tbIdent.window.classification && w.number == tbIdent.window.number
&& widgetIndex == tbIdent.widget_index;
// GfxFillRectInset(dpi, l, t, r, b, colour, 0x20 | (!active ? 0x40 : 0x00));
GfxFillRectInset(dpi, { topLeft, bottomRight }, colour, INSET_RECT_F_60);
@ -1146,7 +1147,8 @@ static void WidgetTextBoxDraw(DrawPixelInfo& dpi, WindowBase& w, WidgetIndex wid
// Figure out where the text should be positioned vertically.
topLeft.y = w.windowPos.y + widget.textTop();
if (!active || gTextInput == nullptr)
auto* textInput = OpenRCT2::Ui::Windows::GetTextboxSession();
if (!active || textInput == nullptr)
{
if (widget.text != 0)
{
@ -1160,27 +1162,27 @@ static void WidgetTextBoxDraw(DrawPixelInfo& dpi, WindowBase& w, WidgetIndex wid
// String length needs to add 12 either side of box
// +13 for cursor when max length.
u8string wrappedString;
GfxWrapString(gTextBoxInput, bottomRight.x - topLeft.x - 5 - 6, FontStyle::Medium, &wrappedString, nullptr);
GfxWrapString(*textInput->Buffer, bottomRight.x - topLeft.x - 5 - 6, FontStyle::Medium, &wrappedString, nullptr);
DrawText(dpi, { topLeft.x + 2, topLeft.y }, { w.colours[1] }, wrappedString.c_str(), true);
// Make a trimmed view of the string for measuring the width.
int32_t curX = topLeft.x
+ GfxGetStringWidthNoFormatting(
u8string_view{ wrappedString.c_str(), std::min(wrappedString.length(), gTextInput->SelectionStart) },
u8string_view{ wrappedString.c_str(), std::min(wrappedString.length(), textInput->SelectionStart) },
FontStyle::Medium)
+ 3;
int32_t width = 6;
if (static_cast<uint32_t>(gTextInput->SelectionStart) < gTextBoxInput.size())
if (static_cast<uint32_t>(textInput->SelectionStart) < textInput->Buffer->size())
{
// Make a new 1 character wide string for measuring the width
// of the character that the cursor is under.
// of the character that the cursor is under. (NOTE: this is broken for multi byte utf8 codepoints)
width = std::max(
GfxGetStringWidthNoFormatting(u8string{ gTextBoxInput[gTextInput->SelectionStart] }, FontStyle::Medium) - 2, 4);
GfxGetStringWidthNoFormatting(u8string{ textInput->Buffer[textInput->SelectionStart] }, FontStyle::Medium) - 2, 4);
}
if (gTextBoxFrameNo <= 15)
if (OpenRCT2::Ui::Windows::TextBoxCaretIsFlashed())
{
colour = ColourMapA[w.colours[1]].mid_light;
auto y = topLeft.y + (widget.height() - 1);

View File

@ -835,6 +835,12 @@ ScreenCoordsXY WindowGetViewportSoundIconPos(WindowBase& w)
namespace OpenRCT2::Ui::Windows
{
static u8string _textBoxInput;
static int32_t _textBoxFrameNo = 0;
static bool _usingWidgetTextBox = false;
static TextInputSession* _textInput;
static WidgetIdentifier _currentTextBox = { { WindowClass::Null, 0 }, 0 };
WindowBase* WindowGetListening()
{
for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++)
@ -859,4 +865,77 @@ namespace OpenRCT2::Ui::Windows
{
return window.classification;
}
void WindowStartTextbox(const WindowBase& callW, WidgetIndex callWidget, u8string existingText, int32_t maxLength)
{
if (_usingWidgetTextBox)
WindowCancelTextbox();
_usingWidgetTextBox = true;
_currentTextBox.window.classification = callW.classification;
_currentTextBox.window.number = callW.number;
_currentTextBox.widget_index = callWidget;
_textBoxFrameNo = 0;
WindowCloseByClass(WindowClass::Textinput);
_textBoxInput = existingText;
_textInput = ContextStartTextInput(_textBoxInput, maxLength);
}
void WindowCancelTextbox()
{
if (_usingWidgetTextBox)
{
WindowBase* w = WindowFindByNumber(_currentTextBox.window.classification, _currentTextBox.window.number);
_currentTextBox.window.classification = WindowClass::Null;
_currentTextBox.window.number = 0;
ContextStopTextInput();
_usingWidgetTextBox = false;
if (w != nullptr)
{
WidgetInvalidate(*w, _currentTextBox.widget_index);
}
_currentTextBox.widget_index = static_cast<uint16_t>(WindowWidgetType::Last);
}
}
void WindowUpdateTextboxCaret()
{
_textBoxFrameNo++;
if (_textBoxFrameNo > 30)
_textBoxFrameNo = 0;
}
void WindowUpdateTextbox()
{
if (_usingWidgetTextBox)
{
_textBoxFrameNo = 0;
WindowBase* w = WindowFindByNumber(_currentTextBox.window.classification, _currentTextBox.window.number);
WidgetInvalidate(*w, _currentTextBox.widget_index);
w->OnTextInput(_currentTextBox.widget_index, _textBoxInput);
}
}
const TextInputSession* GetTextboxSession()
{
return _textInput;
}
void SetTexboxSession(TextInputSession* session)
{
_textInput = session;
}
bool IsUsingWidgetTextBox()
{
return _usingWidgetTextBox;
}
bool TextBoxCaretIsFlashed()
{
return _textBoxFrameNo <= 15;
}
const WidgetIdentifier& GetCurrentTextBox()
{
return _currentTextBox;
}
} // namespace OpenRCT2::Ui::Windows

View File

@ -12,6 +12,8 @@
#include <openrct2/interface/Window.h>
#include <openrct2/interface/Window_internal.h>
struct TextInputSession;
struct Window : WindowBase
{
virtual void OnDraw(DrawPixelInfo& dpi) override;
@ -73,4 +75,15 @@ namespace OpenRCT2::Ui::Windows
WindowBase* WindowGetListening();
WindowClass WindowGetClassification(const WindowBase& window);
void WindowStartTextbox(const WindowBase& callW, WidgetIndex callWidget, u8string existingText, int32_t maxLength);
void WindowCancelTextbox();
void WindowUpdateTextboxCaret();
void WindowUpdateTextbox();
const TextInputSession* GetTextboxSession();
void SetTexboxSession(TextInputSession* session);
bool IsUsingWidgetTextBox();
bool TextBoxCaretIsFlashed();
const WidgetIdentifier& GetCurrentTextBox();
} // namespace OpenRCT2::Ui::Windows

View File

@ -653,8 +653,7 @@ namespace OpenRCT2::Ui::Windows
}
else if (widgetDesc->Type == "textbox")
{
auto* text = const_cast<char*>(widgetDesc->Text.c_str());
WindowStartTextbox(*this, widgetIndex, STR_STRING, text, widgetDesc->MaxLength + 1);
WindowStartTextbox(*this, widgetIndex, widgetDesc->Text, widgetDesc->MaxLength + 1);
}
}
}

View File

@ -328,7 +328,7 @@ static std::vector<Widget> _window_editor_object_selection_widgets = {
void OnUpdate() override
{
if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number)
if (GetCurrentTextBox().window.classification == classification && GetCurrentTextBox().window.number == number)
{
WindowUpdateTextboxCaret();
WidgetInvalidate(*this, WIDX_FILTER_TEXT_BOX);
@ -422,7 +422,7 @@ static std::vector<Widget> _window_editor_object_selection_widgets = {
break;
}
case WIDX_FILTER_TEXT_BOX:
WindowStartTextbox(*this, widgetIndex, STR_STRING, _filter_string, sizeof(_filter_string));
WindowStartTextbox(*this, widgetIndex, _filter_string, sizeof(_filter_string));
break;
case WIDX_FILTER_CLEAR_BUTTON:
std::fill_n(_filter_string, sizeof(_filter_string), 0x00);

View File

@ -325,7 +325,7 @@ static Widget window_new_ride_widgets[] = {
WidgetInvalidate(*this, WIDX_TAB_1 + static_cast<int32_t>(_currentTab));
if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number)
if (GetCurrentTextBox().window.classification == classification && GetCurrentTextBox().window.number == number)
{
WindowUpdateTextboxCaret();
WidgetInvalidate(*this, WIDX_FILTER_TEXT_BOX);
@ -370,7 +370,7 @@ static Widget window_new_ride_widgets[] = {
SetPage(_currentTab);
break;
case WIDX_FILTER_TEXT_BOX:
WindowStartTextbox(*this, widgetIndex, STR_STRING, _filter.data(), kTextInputSize);
WindowStartTextbox(*this, widgetIndex, _filter, kTextInputSize);
break;
case WIDX_FILTER_CLEAR_BUTTON:
_filter.clear();

View File

@ -276,7 +276,7 @@ static Widget WindowSceneryBaseWidgets[] = {
Invalidate();
break;
case WIDX_FILTER_TEXT_BOX:
WindowStartTextbox(*this, widgetIndex, STR_STRING, _filteredSceneryTab.Filter.data(), kTextInputSize);
WindowStartTextbox(*this, widgetIndex, _filteredSceneryTab.Filter, kTextInputSize);
break;
case WIDX_FILTER_CLEAR_BUTTON:
_tabEntries[_activeTabIndex].Filter.clear();
@ -463,7 +463,7 @@ static Widget WindowSceneryBaseWidgets[] = {
}
}
if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number)
if (GetCurrentTextBox().window.classification == classification && GetCurrentTextBox().window.number == number)
{
WindowUpdateTextboxCaret();
WidgetInvalidate(*this, WIDX_FILTER_TEXT_BOX);

View File

@ -131,7 +131,7 @@ static Widget _serverListWidgets[] = {
Close();
break;
case WIDX_PLAYER_NAME_INPUT:
WindowStartTextbox(*this, widgetIndex, STR_STRING, _playerName.c_str(), MaxPlayerNameLength);
WindowStartTextbox(*this, widgetIndex, _playerName, MaxPlayerNameLength);
break;
case WIDX_LIST:
{
@ -205,7 +205,7 @@ static Widget _serverListWidgets[] = {
void OnUpdate() override
{
if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number)
if (GetCurrentTextBox().window.classification == classification && GetCurrentTextBox().window.number == number)
{
WindowUpdateTextboxCaret();
InvalidateWidget(WIDX_PLAYER_NAME_INPUT);

View File

@ -95,19 +95,19 @@ static Widget _windowServerStartWidgets[] = {
Close();
break;
case WIDX_PORT_INPUT:
WindowStartTextbox(*this, widgetIndex, STR_STRING, _port, 6);
WindowStartTextbox(*this, widgetIndex, _port, 6);
break;
case WIDX_NAME_INPUT:
WindowStartTextbox(*this, widgetIndex, STR_STRING, _name, 64);
WindowStartTextbox(*this, widgetIndex, _name, 64);
break;
case WIDX_DESCRIPTION_INPUT:
WindowStartTextbox(*this, widgetIndex, STR_STRING, _description, MAX_SERVER_DESCRIPTION_LENGTH);
WindowStartTextbox(*this, widgetIndex, _description, MAX_SERVER_DESCRIPTION_LENGTH);
break;
case WIDX_GREETING_INPUT:
WindowStartTextbox(*this, widgetIndex, STR_STRING, _greeting, kChatInputSize);
WindowStartTextbox(*this, widgetIndex, _greeting, kChatInputSize);
break;
case WIDX_PASSWORD_INPUT:
WindowStartTextbox(*this, widgetIndex, STR_STRING, _password, 32);
WindowStartTextbox(*this, widgetIndex, _password, 32);
break;
case WIDX_MAXPLAYERS_INCREASE:
if (gConfigNetwork.Maxplayers < 255)
@ -154,7 +154,7 @@ static Widget _windowServerStartWidgets[] = {
}
void OnUpdate() override
{
if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number)
if (GetCurrentTextBox().window.classification == classification && GetCurrentTextBox().window.number == number)
{
WindowUpdateTextboxCaret();
WidgetInvalidate(*this, WIDX_NAME_INPUT);

View File

@ -114,7 +114,7 @@ namespace OpenRCT2::Ui::Windows
text = String::UTF8TruncateCodePoints(text, maxLength);
_buffer = u8string{ text };
_maxInputLength = maxLength;
gTextInput = ContextStartTextInput(_buffer, maxLength);
SetTexboxSession(ContextStartTextInput(_buffer, maxLength));
}
void SetCallback(std::function<void(std::string_view)> callback, std::function<void()> cancelCallback)
@ -238,6 +238,7 @@ namespace OpenRCT2::Ui::Windows
size_t char_count = 0;
uint8_t cur_drawn = 0;
auto* textInput = GetTextboxSession();
int32_t cursorX = 0;
int32_t cursorY = 0;
for (int32_t line = 0; line <= no_lines; line++)
@ -246,22 +247,21 @@ namespace OpenRCT2::Ui::Windows
DrawText(dpi, screenCoords, { colours[1], FontStyle::Medium, TextAlignment::LEFT }, wrapPointer, true);
size_t string_length = GetStringSize(wrapPointer) - 1;
if (!cur_drawn && (gTextInput->SelectionStart <= char_count + string_length))
if (!cur_drawn && (textInput->SelectionStart <= char_count + string_length))
{
// Make a view of the string for measuring the width.
cursorX = windowPos.x + 13
+ GfxGetStringWidthNoFormatting(
u8string_view{ wrapPointer, gTextInput->SelectionStart - char_count }, FontStyle::Medium);
u8string_view{ wrapPointer, textInput->SelectionStart - char_count }, FontStyle::Medium);
cursorY = screenCoords.y;
int32_t textWidth = 6;
if (gTextInput->SelectionStart < strlen(_buffer.data()))
if (textInput->SelectionStart < strlen(_buffer.data()))
{
// Make a 1 utf8-character wide string for measuring the width
// of the currently selected character.
utf8 tmp[5] = {}; // This is easier than setting temp_string[0..5]
uint32_t codepoint = UTF8GetNext(_buffer.data() + gTextInput->SelectionStart, nullptr);
uint32_t codepoint = UTF8GetNext(_buffer.data() + textInput->SelectionStart, nullptr);
UTF8WriteCodepoint(tmp, codepoint);
textWidth = std::max(GfxGetStringWidthNoFormatting(tmp, FontStyle::Medium) - 2, 4);
}
@ -293,7 +293,7 @@ namespace OpenRCT2::Ui::Windows
}
// IME composition
if (!String::IsNullOrEmpty(gTextInput->ImeBuffer))
if (!String::IsNullOrEmpty(textInput->ImeBuffer))
{
IMEComposition(cursorX, cursorY);
}

View File

@ -297,8 +297,7 @@ static Widget _trackListWidgets[] = {
}
break;
case WIDX_FILTER_STRING:
WindowStartTextbox(
*this, widgetIndex, STR_STRING, _filterString, sizeof(_filterString)); // TODO check this out
WindowStartTextbox(*this, widgetIndex, _filterString, sizeof(_filterString));
break;
case WIDX_FILTER_CLEAR:
// Keep the highlighted item selected
@ -434,7 +433,7 @@ static Widget _trackListWidgets[] = {
void OnUpdate() override
{
if (gCurrentTextBox.window.classification == classification && gCurrentTextBox.window.number == number)
if (GetCurrentTextBox().window.classification == classification && GetCurrentTextBox().window.number == number)
{
WindowUpdateTextboxCaret();
WidgetInvalidate(*this, WIDX_FILTER_STRING); // TODO Check this

View File

@ -45,12 +45,7 @@ using namespace OpenRCT2;
std::list<std::shared_ptr<WindowBase>> g_window_list;
WindowBase* gWindowAudioExclusive;
WidgetIdentifier gCurrentTextBox = { { WindowClass::Null, 0 }, 0 };
WindowCloseModifier gLastCloseModifier = { { WindowClass::Null, 0 }, CloseWindowModifier::None };
u8string gTextBoxInput;
int32_t gTextBoxFrameNo = 0;
bool gUsingWidgetTextBox = false;
TextInputSession* gTextInput;
uint32_t gWindowUpdateTicks;
uint16_t gWindowMapFlashingFlags;
@ -1649,70 +1644,6 @@ void TextinputCancel()
WindowCloseByClass(WindowClass::Textinput);
}
void WindowStartTextbox(
WindowBase& call_w, WidgetIndex call_widget, StringId existing_text, const char* existing_args, int32_t maxLength)
{
if (gUsingWidgetTextBox)
WindowCancelTextbox();
gUsingWidgetTextBox = true;
gCurrentTextBox.window.classification = call_w.classification;
gCurrentTextBox.window.number = call_w.number;
gCurrentTextBox.widget_index = call_widget;
gTextBoxFrameNo = 0;
WindowCloseByClass(WindowClass::Textinput);
// Clear the text input buffer
gTextBoxInput.clear();
// Enter in the text input buffer any existing
// text.
if (existing_text != STR_NONE)
{
char tempBuf[kTextInputSize]{};
size_t len = FormatStringLegacy(tempBuf, kTextInputSize, existing_text, &existing_args);
gTextBoxInput.assign(tempBuf, len);
}
gTextInput = ContextStartTextInput(gTextBoxInput, maxLength);
}
void WindowCancelTextbox()
{
if (gUsingWidgetTextBox)
{
WindowBase* w = WindowFindByNumber(gCurrentTextBox.window.classification, gCurrentTextBox.window.number);
gCurrentTextBox.window.classification = WindowClass::Null;
gCurrentTextBox.window.number = 0;
ContextStopTextInput();
gUsingWidgetTextBox = false;
if (w != nullptr)
{
WidgetInvalidate(*w, gCurrentTextBox.widget_index);
}
gCurrentTextBox.widget_index = static_cast<uint16_t>(WindowWidgetType::Last);
}
}
void WindowUpdateTextboxCaret()
{
gTextBoxFrameNo++;
if (gTextBoxFrameNo > 30)
gTextBoxFrameNo = 0;
}
void WindowUpdateTextbox()
{
if (gUsingWidgetTextBox)
{
gTextBoxFrameNo = 0;
WindowBase* w = WindowFindByNumber(gCurrentTextBox.window.classification, gCurrentTextBox.window.number);
WidgetInvalidate(*w, gCurrentTextBox.widget_index);
w->OnTextInput(gCurrentTextBox.widget_index, gTextBoxInput);
}
}
bool WindowIsVisible(WindowBase& w)
{
// w->visibility is used to prevent repeat calculations within an iteration by caching the result

View File

@ -31,7 +31,6 @@
struct DrawPixelInfo;
struct WindowBase;
struct TrackDesignFileRef;
struct TextInputSession;
struct ScenarioIndexEntry;
struct WindowCloseModifier;
@ -52,11 +51,6 @@ constexpr uint8_t kDropdownHeight = 12;
constexpr uint16_t kTextInputSize = 1024;
constexpr uint16_t kTopToolbarHeight = 27;
extern u8string gTextBoxInput;
extern int32_t gTextBoxFrameNo;
extern bool gUsingWidgetTextBox;
extern struct TextInputSession* gTextInput;
using rct_windownumber = uint16_t;
using WidgetIndex = int16_t;
@ -72,7 +66,6 @@ struct WidgetIdentifier
WidgetIndex widget_index;
};
extern WidgetIdentifier gCurrentTextBox;
extern WindowCloseModifier gLastCloseModifier;
using WidgetFlags = uint32_t;
@ -631,12 +624,6 @@ void TextinputCancel();
void WindowMoveAndSnap(WindowBase& w, ScreenCoordsXY newWindowCoords, int32_t snapProximity);
int32_t WindowCanResize(const WindowBase& w);
void WindowStartTextbox(
WindowBase& call_w, WidgetIndex call_widget, StringId existing_text, const char* existing_args, int32_t maxLength);
void WindowCancelTextbox();
void WindowUpdateTextboxCaret();
void WindowUpdateTextbox();
bool WindowIsVisible(WindowBase& w);
bool SceneryToolIsActive();