diff --git a/Minecraft.Client/Common/UI/UIControl_TextInput.cpp b/Minecraft.Client/Common/UI/UIControl_TextInput.cpp index dc7bc5326..fd8024679 100644 --- a/Minecraft.Client/Common/UI/UIControl_TextInput.cpp +++ b/Minecraft.Client/Common/UI/UIControl_TextInput.cpp @@ -16,6 +16,7 @@ bool UIControl_TextInput::setupControl(UIScene *scene, IggyValuePath *parent, co m_textName = registerFastName(L"text"); m_funcChangeState = registerFastName(L"ChangeState"); m_funcSetCharLimit = registerFastName(L"SetCharLimit"); + m_funcSetCaretIndex = registerFastName(L"SetCaretIndex"); return success; } @@ -81,3 +82,22 @@ void UIControl_TextInput::SetCharLimit(int iLimit) value[0].number = iLimit; IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcSetCharLimit , 1 , value ); } + +void UIControl_TextInput::setCaretVisible(bool visible) +{ + // Always send to Flash — Iggy's focus system can re-enable the caret at any time + IggyValuePath caretPath; + if (IggyValuePathMakeNameRef(&caretPath, getIggyValuePath(), "m_mcCaret")) + { + IggyValueSetBooleanRS(&caretPath, m_nameVisible, NULL, visible); + } +} + +void UIControl_TextInput::setCaretIndex(int index) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = index; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcSetCaretIndex , 1 , value ); +} diff --git a/Minecraft.Client/Common/UI/UIControl_TextInput.h b/Minecraft.Client/Common/UI/UIControl_TextInput.h index 98032d858..4ced89581 100644 --- a/Minecraft.Client/Common/UI/UIControl_TextInput.h +++ b/Minecraft.Client/Common/UI/UIControl_TextInput.h @@ -6,6 +6,7 @@ class UIControl_TextInput : public UIControl_Base { private: IggyName m_textName, m_funcChangeState, m_funcSetCharLimit; + IggyName m_funcSetCaretIndex; bool m_bHasFocus; public: @@ -19,4 +20,7 @@ public: virtual void setFocus(bool focus); void SetCharLimit(int iLimit); + + void setCaretVisible(bool visible); + void setCaretIndex(int index); }; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp index 6afb67e49..bdf949870 100644 --- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp @@ -87,6 +87,7 @@ UIScene_CreateWorldMenu::UIScene_CreateWorldMenu(int iPad, void *initData, UILay #ifdef _WINDOWS64 m_bDirectEditing = false; m_iDirectEditCooldown = 0; + m_iCursorPos = 0; #endif m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); @@ -297,6 +298,15 @@ void UIScene_CreateWorldMenu::tick() if (m_iDirectEditCooldown > 0) m_iDirectEditCooldown--; + // Control caret visibility and position every tick — setLabel() and Flash + // focus changes reset both, so we must continuously enforce them. + if (g_KBMInput.IsKBMActive()) + { + m_editWorldName.setCaretVisible(m_bDirectEditing); + if (m_bDirectEditing) + m_editWorldName.setCaretIndex(m_iCursorPos); + } + if (m_bDirectEditing) { wchar_t ch; @@ -305,9 +315,10 @@ void UIScene_CreateWorldMenu::tick() { if (ch == 0x08) // backspace { - if (!m_worldName.empty()) + if (m_iCursorPos > 0) { - m_worldName.pop_back(); + m_worldName.erase(m_iCursorPos - 1, 1); + m_iCursorPos--; changed = true; } } @@ -316,14 +327,44 @@ void UIScene_CreateWorldMenu::tick() m_bDirectEditing = false; m_iDirectEditCooldown = 4; // absorb the matching ACTION_MENU_OK that follows m_editWorldName.setLabel(m_worldName.c_str()); + m_editWorldName.setCaretVisible(false); + break; } else if ((int)m_worldName.length() < 25) { - m_worldName += ch; + m_worldName.insert(m_iCursorPos, 1, ch); + m_iCursorPos++; changed = true; } } + // Arrow keys move the cursor within the text + if (g_KBMInput.IsKeyPressed(VK_LEFT) && m_iCursorPos > 0) + { + m_iCursorPos--; + m_editWorldName.setCaretIndex(m_iCursorPos); + } + if (g_KBMInput.IsKeyPressed(VK_RIGHT) && m_iCursorPos < (int)m_worldName.length()) + { + m_iCursorPos++; + m_editWorldName.setCaretIndex(m_iCursorPos); + } + if (g_KBMInput.IsKeyPressed(VK_HOME)) + { + m_iCursorPos = 0; + m_editWorldName.setCaretIndex(m_iCursorPos); + } + if (g_KBMInput.IsKeyPressed(VK_END)) + { + m_iCursorPos = (int)m_worldName.length(); + m_editWorldName.setCaretIndex(m_iCursorPos); + } + if (g_KBMInput.IsKeyPressed(VK_DELETE) && m_iCursorPos < (int)m_worldName.length()) + { + m_worldName.erase(m_iCursorPos, 1); + changed = true; + } + // Escape cancels and restores the original name if (m_bDirectEditing && g_KBMInput.IsKeyPressed(VK_ESCAPE)) { @@ -331,11 +372,13 @@ void UIScene_CreateWorldMenu::tick() m_bDirectEditing = false; m_iDirectEditCooldown = 4; m_editWorldName.setLabel(m_worldName.c_str()); + m_editWorldName.setCaretVisible(false); m_buttonCreateWorld.setEnable(!m_worldName.empty()); } else if (changed) { m_editWorldName.setLabel(m_worldName.c_str()); + m_editWorldName.setCaretIndex(m_iCursorPos); m_buttonCreateWorld.setEnable(!m_worldName.empty()); } } @@ -492,7 +535,10 @@ void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId) m_bIgnoreInput = false; // Don't block input - m_bDirectEditing is the guard m_worldNameBeforeEdit = m_worldName; m_bDirectEditing = true; + m_iCursorPos = (int)m_worldName.length(); g_KBMInput.ClearCharBuffer(); + m_editWorldName.setCaretVisible(true); + m_editWorldName.setCaretIndex(m_iCursorPos); } #else InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD),m_editWorldName.getLabel(),(DWORD)0,25,&UIScene_CreateWorldMenu::KeyboardCompleteWorldNameCallback,this,C_4JInput::EKeyboardMode_Default); diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h index 13f38a3b7..1f941c91b 100644 --- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h @@ -55,6 +55,7 @@ private: bool m_bDirectEditing; wstring m_worldNameBeforeEdit; int m_iDirectEditCooldown; + int m_iCursorPos; #endif public: