From 3c5613b8aa5edaba89131ed599581d024e0c0592 Mon Sep 17 00:00:00 2001 From: MrTheShy <49885496+MrTheShy@users.noreply.github.com> Date: Fri, 6 Mar 2026 04:01:19 +0100 Subject: [PATCH] Overhaul mouse support and generalize direct text editing to all UI scenes This is a large rework of the Windows64 KBM (keyboard+mouse) input layer. It touches the mouse hover system, the mouse click dispatch, and the direct text editing infrastructure, then applies all of it to every scene that has text input fields or non-standard clickable elements. MOUSE HOVER REWRITE (UIController.cpp tickInput) The old hover code had two structural problems: (a) Scene lookup was group-first: it iterated UI groups and checked all layers within each group. The Tooltips layer on eUIGroup_Fullscreen (which holds non-interactive overlays like button hints) would be found before in-game menus on eUIGroup_Player1. The tooltip scene focusable objects captured mouse input and prevented hover from reaching the actual menu. Fixed by switching to layer-first lookup across all groups, and skipping eUILayer_Tooltips entirely since those are never interactive. (b) On tabbed menus (LaunchMoreOptionsMenu Game vs World tabs), all controls from all tabs are registered in Flash at the same time. There was no filtering, so controls from inactive tabs had phantom hitboxes that overlapped the active tab controls, making certain buttons unhoverable. Fixed by introducing parent panel tracking: each UIControl now has a m_pParentPanel pointer, set automatically by the UI_MAP_ELEMENT macro during mapElementsAndNames(). The hover code checks the control parent panel against the scene GetMainPanel() and skips mismatches. This is the same technique the Vita touch code used, but applied to mouse hover. The coordinate conversion was also simplified. The old code had two separate scaling paths (window dimensions for hover, display dimensions for sliders). Now there is one conversion from window pixel coords to SWF coords using the scene own render dimensions. REUSING VITA TOUCH APIs FOR MOUSE (ButtonList, UIScene) Several APIs originally gated behind __PSVITA__ are now enabled for Win64: - UIControl_ButtonList::SetTouchFocus(x,y) and CanTouchTrigger(x,y): the Flash-side ActionScript methods were already registered on all platforms in setupControl(), only the C++ wrappers were ifdef-gated. Opening the ifdefs to include _WINDOWS64 lets the mouse hover code delegate to Flash for list item highlighting, which handles internal scrolling and item layout that would be impractical to replicate in C++. - UIScene::SetFocusToElement(id): programmatic focus-by-control-ID, used as a fallback when Iggy focusable objects do not match the C++ hit test. - UIScene_LaunchMoreOptionsMenu::GetMainPanel(): returns the active tab panel control, needed by the hover code to filter inactive tab controls. MOUSE CLICK DISPATCH (UIScene.cpp handleMouseClick) Left-clicking previously relied entirely on Iggy ACTION_MENU_OK dispatch, which routes to whatever Flash considers focused. This broke for custom- drawn elements that are not Flash buttons (crafting recipe slots), and for scenes where Iggy focus did not match what the user visually clicked. Added a virtual handleMouseClick(x, y) on UIScene with a default implementation that hit-tests C++ controls. When multiple controls report overlapping bounds (common in debug scenes where TextInputs report full Flash-width), it picks the one whose left edge X is closest to the click. Returns true to consume the click and suppress the normal ACTION_MENU_A dispatch via a m_mouseClickConsumedByScene flag on UIController. The default implementation handles buttons, text inputs, and checkboxes (toggling state and calling handleCheckboxToggled directly). CRAFTING MENU MOUSE CLICK (UIScene_CraftingMenu.cpp) The crafting menu recipe slots (H slots) are rendered through Iggy custom draw callback, not as Flash buttons. They have no focusable objects, so mouse clicking did nothing. The solution caches SWF-space positions during rendering: inside customDraw, when H slot 0 and H slot 1 are drawn, the code extracts SWF coordinates from the D3D11 transform matrix via gdraw_D3D11_CalculateCustomDraw_4J. The X difference between slot 0 and slot 1 gives the uniform slot spacing. handleMouseClick then uses these cached bounds to determine which recipe slot was clicked, resets the vertical slot indices (same pattern as the constructor), updates the highlight and vertical slots display, and re-shows the old slot icon. This mirrors the existing controller LEFT/RIGHT navigation in the base class handleKeyDown. DIRECT EDIT REFACTORING (UIControl_TextInput) The direct text editing feature (type directly into text fields instead of opening the virtual keyboard) was originally implemented inline in CreateWorldMenu with all the state, character consumption, cursor tracking, caret visibility, and cooldown logic hardcoded in one scene. Moved everything into UIControl_TextInput: - beginDirectEdit(charLimit): captures current label, inits cursor at end - tickDirectEdit(): consumes chars, handles Backspace/Enter/Escape, arrow keys (Left/Right/Home/End/Delete), enforces caret visibility every tick (because setLabel and Flash focus transitions continuously reset it), returns Confirmed/Cancelled/Continue - cancelDirectEdit() / confirmDirectEdit(): programmatic control - isDirectEditing() / getDirectEditCooldown() / getEditBuffer(): state query For SWFs that lack the m_mcCaret MovieClip child (like AnvilMenu), the existence check validates by reading a property from the resolved path, since IggyValuePathMakeNameRef always succeeds even for undefined refs. When no caret exists, the control inserts a _ character at the cursor position as a visual fallback. The caret check result is cached in m_bHasCaret/m_bCaretChecked to avoid repeated Iggy calls that could corrupt internal state. SCENES UPDATED WITH DIRECT EDIT + VIRTUAL KEYBOARD Every scene with text input now supports both input modes: direct editing when KBM is active, virtual keyboard (via NavigateToScene eUIScene_Keyboard) when using a controller. The mode is chosen at press time based on g_KBMInput.IsKBMActive(). - CreateWorldMenu: refactored to use the new UIControl_TextInput API, removing ~80 lines of inline editing code. - AnvilMenu: item renaming now supports direct edit. The keyboard callback uses Win64_GetKeyboardText instead of InputManager.GetText (which reads from a different buffer on Win64). The virtual keyboard is opened with eUILayer_Fullscreen + eUIGroup_Fullscreen so it does not hide the anvil container menu underneath. Added null guards on getMovie() in setCostLabel and showCross since the AnvilMenu SWF may not fully load on Win64. - SignEntryMenu: all 4 sign lines support direct edit. Clicking a different line while editing confirms the current one. Each line cooldown timer is checked independently to prevent Enter from re-opening the edit. - LaunchMoreOptionsMenu: seed field direct edit with proper input blocking. - DebugCreateSchematic: all 7 text inputs (name + start/end XYZ coords). handleMouseClick is overridden to always consume clicks during edit to prevent Iggy re-entry on empty space. - DebugSetCamera: all 5 inputs (camera XYZ + Y rotation + elevation). Clicking a different field while editing confirms the current value and opens the new one. Float display formatting changed from %f to %.2f. All keyboard completion callbacks on Win64 now use Win64_GetKeyboardText (two params: buffer + size) instead of InputManager.GetText, which reads from the correct g_Win64KeyboardResult global when using the in-game keyboard scene. SCROLL WHEEL Mouse wheel events (ACTION_MENU_OTHER_STICK_UP/DOWN) are now centrally remapped to ACTION_MENU_UP/DOWN in UIController::handleKeyPress when KBM is active. Previously each scene would need to handle OTHER_STICK actions separately, and most did not, so scroll wheel only worked in a few places. --- Minecraft.Client/Common/UI/UIControl.cpp | 2 + Minecraft.Client/Common/UI/UIControl.h | 2 + .../Common/UI/UIControl_ButtonList.cpp | 2 +- .../Common/UI/UIControl_ButtonList.h | 2 +- .../Common/UI/UIControl_TextInput.cpp | 180 ++++++++++++- .../Common/UI/UIControl_TextInput.h | 29 +++ Minecraft.Client/Common/UI/UIController.cpp | 244 +++++++++++++----- Minecraft.Client/Common/UI/UIController.h | 1 + Minecraft.Client/Common/UI/UIScene.cpp | 76 +++++- Minecraft.Client/Common/UI/UIScene.h | 21 +- .../Common/UI/UIScene_AnvilMenu.cpp | 45 +++- .../Common/UI/UIScene_CraftingMenu.cpp | 67 +++++ .../Common/UI/UIScene_CraftingMenu.h | 7 + .../Common/UI/UIScene_CreateWorldMenu.cpp | 108 +------- .../Common/UI/UIScene_CreateWorldMenu.h | 6 - .../UI/UIScene_DebugCreateSchematic.cpp | 106 +++++++- .../Common/UI/UIScene_DebugCreateSchematic.h | 9 + .../Common/UI/UIScene_DebugSetCamera.cpp | 127 ++++++++- .../Common/UI/UIScene_DebugSetCamera.h | 8 + .../UI/UIScene_LaunchMoreOptionsMenu.cpp | 43 ++- .../Common/UI/UIScene_LaunchMoreOptionsMenu.h | 4 +- .../Common/UI/UIScene_SignEntryMenu.cpp | 75 +++++- .../Common/UI/UIScene_SignEntryMenu.h | 3 + 23 files changed, 976 insertions(+), 191 deletions(-) diff --git a/Minecraft.Client/Common/UI/UIControl.cpp b/Minecraft.Client/Common/UI/UIControl.cpp index be267ada6..3d853ac46 100644 --- a/Minecraft.Client/Common/UI/UIControl.cpp +++ b/Minecraft.Client/Common/UI/UIControl.cpp @@ -12,6 +12,8 @@ UIControl::UIControl() m_isVisible = true; m_bHidden = false; m_eControlType = eNoControl; + m_id = -1; + m_pParentPanel = NULL; } bool UIControl::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) diff --git a/Minecraft.Client/Common/UI/UIControl.h b/Minecraft.Client/Common/UI/UIControl.h index 29770df28..9062ce9ba 100644 --- a/Minecraft.Client/Common/UI/UIControl.h +++ b/Minecraft.Client/Common/UI/UIControl.h @@ -38,12 +38,14 @@ protected: bool m_bHidden; // set by the Remove call public: + UIControl *m_pParentPanel; // set by UI_MAP_ELEMENT macro during mapElementsAndNames void setControlType(eUIControlType eType) {m_eControlType=eType;} eUIControlType getControlType() {return m_eControlType;} void setId(int iID) { m_id=iID; } int getId() { return m_id; } UIScene * getParentScene() {return m_parentScene;} + UIControl* getParentPanel() { return m_pParentPanel; } protected: IggyValuePath m_iggyPath; diff --git a/Minecraft.Client/Common/UI/UIControl_ButtonList.cpp b/Minecraft.Client/Common/UI/UIControl_ButtonList.cpp index 68a3d655a..4d60a477c 100644 --- a/Minecraft.Client/Common/UI/UIControl_ButtonList.cpp +++ b/Minecraft.Client/Common/UI/UIControl_ButtonList.cpp @@ -159,7 +159,7 @@ void UIControl_ButtonList::setButtonLabel(int iButtonId, const wstring &label) IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie(), &result, getIggyValuePath(), m_funcSetButtonLabel, 2 , value ); } -#ifdef __PSVITA__ +#if defined(__PSVITA__) || defined(_WINDOWS64) void UIControl_ButtonList::SetTouchFocus(S32 iX, S32 iY, bool bRepeat) { IggyDataValue result; diff --git a/Minecraft.Client/Common/UI/UIControl_ButtonList.h b/Minecraft.Client/Common/UI/UIControl_ButtonList.h index 44484ac3f..666b1f0af 100644 --- a/Minecraft.Client/Common/UI/UIControl_ButtonList.h +++ b/Minecraft.Client/Common/UI/UIControl_ButtonList.h @@ -37,7 +37,7 @@ public: void setButtonLabel(int iButtonId, const wstring &label); -#ifdef __PSVITA__ +#if defined(__PSVITA__) || defined(_WINDOWS64) void SetTouchFocus(S32 iX, S32 iY, bool bRepeat); bool CanTouchTrigger(S32 iX, S32 iY); #endif diff --git a/Minecraft.Client/Common/UI/UIControl_TextInput.cpp b/Minecraft.Client/Common/UI/UIControl_TextInput.cpp index fd8024679..193dbf41f 100644 --- a/Minecraft.Client/Common/UI/UIControl_TextInput.cpp +++ b/Minecraft.Client/Common/UI/UIControl_TextInput.cpp @@ -5,6 +5,15 @@ UIControl_TextInput::UIControl_TextInput() { m_bHasFocus = false; + m_bHasCaret = false; + m_bCaretChecked = false; +#ifdef _WINDOWS64 + m_bDirectEditing = false; + m_iCursorPos = 0; + m_iCharLimit = 0; + m_iDirectEditCooldown = 0; + m_iCaretBlinkTimer = 0; +#endif } bool UIControl_TextInput::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) @@ -85,7 +94,27 @@ void UIControl_TextInput::SetCharLimit(int iLimit) void UIControl_TextInput::setCaretVisible(bool visible) { - // Always send to Flash — Iggy's focus system can re-enable the caret at any time + // Check once whether this SWF's FJ_TextInput actually has a m_mcCaret child. + // IggyValuePathMakeNameRef always succeeds (creates a ref to undefined), + // so we validate by trying to read a property from the resolved path. + if (!m_bCaretChecked) + { + IggyValuePath caretPath; + if (IggyValuePathMakeNameRef(&caretPath, getIggyValuePath(), "m_mcCaret")) + { + rrbool test = false; + IggyResult res = IggyValueGetBooleanRS(&caretPath, m_nameVisible, NULL, &test); + m_bHasCaret = (res == 0); + } + else + { + m_bHasCaret = false; + } + m_bCaretChecked = true; + } + if (!m_bHasCaret) + return; + IggyValuePath caretPath; if (IggyValuePathMakeNameRef(&caretPath, getIggyValuePath(), "m_mcCaret")) { @@ -101,3 +130,152 @@ void UIControl_TextInput::setCaretIndex(int index) value[0].number = index; IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcSetCaretIndex , 1 , value ); } + +#ifdef _WINDOWS64 + +void UIControl_TextInput::beginDirectEdit(int charLimit) +{ + const wchar_t* current = getLabel(); + m_editBuffer = current ? current : L""; + m_textBeforeEdit = m_editBuffer; + m_iCursorPos = (int)m_editBuffer.length(); + m_iCharLimit = charLimit; + m_bDirectEditing = true; + m_iDirectEditCooldown = 0; + m_iCaretBlinkTimer = 0; + g_KBMInput.ClearCharBuffer(); + setCaretVisible(true); + setCaretIndex(m_iCursorPos); +} + +UIControl_TextInput::EDirectEditResult UIControl_TextInput::tickDirectEdit() +{ + if (m_iDirectEditCooldown > 0) + m_iDirectEditCooldown--; + + if (!m_bDirectEditing) + { + setCaretVisible(false); + return eDirectEdit_Continue; + } + + // Enforce caret visibility and position every tick — setLabel() and Flash + // focus changes can reset both at any time. + setCaretVisible(true); + setCaretIndex(m_iCursorPos); + + // For SWFs without m_mcCaret, insert '_' at the cursor position. + // All characters remain visible — '_' sits between them like a cursor. + if (!m_bHasCaret) + { + wstring display = m_editBuffer; + display.insert(m_iCursorPos, 1, L'_'); + setLabel(display.c_str()); + } + + EDirectEditResult result = eDirectEdit_Continue; + bool changed = false; + + // Consume typed characters from the KBM buffer + wchar_t ch; + while (g_KBMInput.ConsumeChar(ch)) + { + if (ch == 0x08) // Backspace + { + if (m_iCursorPos > 0) + { + m_editBuffer.erase(m_iCursorPos - 1, 1); + m_iCursorPos--; + changed = true; + } + } + else if (ch == 0x0D) // Enter — confirm edit + { + m_bDirectEditing = false; + m_iDirectEditCooldown = 4; + setLabel(m_editBuffer.c_str()); + setCaretVisible(false); + return eDirectEdit_Confirmed; + } + else if (m_iCharLimit <= 0 || (int)m_editBuffer.length() < m_iCharLimit) + { + m_editBuffer.insert(m_iCursorPos, 1, ch); + m_iCursorPos++; + changed = true; + } + } + + // Arrow keys, Home, End, Delete for cursor movement + if (g_KBMInput.IsKeyPressed(VK_LEFT) && m_iCursorPos > 0) + { + m_iCursorPos--; + setCaretIndex(m_iCursorPos); + } + if (g_KBMInput.IsKeyPressed(VK_RIGHT) && m_iCursorPos < (int)m_editBuffer.length()) + { + m_iCursorPos++; + setCaretIndex(m_iCursorPos); + } + if (g_KBMInput.IsKeyPressed(VK_HOME)) + { + m_iCursorPos = 0; + setCaretIndex(m_iCursorPos); + } + if (g_KBMInput.IsKeyPressed(VK_END)) + { + m_iCursorPos = (int)m_editBuffer.length(); + setCaretIndex(m_iCursorPos); + } + if (g_KBMInput.IsKeyPressed(VK_DELETE) && m_iCursorPos < (int)m_editBuffer.length()) + { + m_editBuffer.erase(m_iCursorPos, 1); + changed = true; + } + + // Escape — cancel edit and restore original text + if (g_KBMInput.IsKeyPressed(VK_ESCAPE)) + { + m_editBuffer = m_textBeforeEdit; + m_bDirectEditing = false; + m_iDirectEditCooldown = 4; + setLabel(m_editBuffer.c_str()); + setCaretVisible(false); + return eDirectEdit_Cancelled; + } + + if (changed) + { + if (m_bHasCaret) + { + setLabel(m_editBuffer.c_str()); + setCaretIndex(m_iCursorPos); + } + // SWFs without caret: the cursor block above already updates the label every tick + } + + return eDirectEdit_Continue; +} + +void UIControl_TextInput::cancelDirectEdit() +{ + if (m_bDirectEditing) + { + m_editBuffer = m_textBeforeEdit; + m_bDirectEditing = false; + m_iDirectEditCooldown = 4; + setLabel(m_editBuffer.c_str()); + setCaretVisible(false); + } +} + +void UIControl_TextInput::confirmDirectEdit() +{ + if (m_bDirectEditing) + { + m_bDirectEditing = false; + setLabel(m_editBuffer.c_str()); + setCaretVisible(false); + } +} + +#endif diff --git a/Minecraft.Client/Common/UI/UIControl_TextInput.h b/Minecraft.Client/Common/UI/UIControl_TextInput.h index 4ced89581..3ff289309 100644 --- a/Minecraft.Client/Common/UI/UIControl_TextInput.h +++ b/Minecraft.Client/Common/UI/UIControl_TextInput.h @@ -8,6 +8,18 @@ private: IggyName m_textName, m_funcChangeState, m_funcSetCharLimit; IggyName m_funcSetCaretIndex; bool m_bHasFocus; + bool m_bHasCaret; + bool m_bCaretChecked; + +#ifdef _WINDOWS64 + bool m_bDirectEditing; + wstring m_textBeforeEdit; + wstring m_editBuffer; + int m_iCursorPos; + int m_iCharLimit; + int m_iDirectEditCooldown; + int m_iCaretBlinkTimer; +#endif public: UIControl_TextInput(); @@ -23,4 +35,21 @@ public: void setCaretVisible(bool visible); void setCaretIndex(int index); + +#ifdef _WINDOWS64 + enum EDirectEditResult + { + eDirectEdit_Continue, + eDirectEdit_Confirmed, + eDirectEdit_Cancelled, + }; + + void beginDirectEdit(int charLimit = 0); + EDirectEditResult tickDirectEdit(); + void cancelDirectEdit(); + void confirmDirectEdit(); + bool isDirectEditing() const { return m_bDirectEditing; } + int getDirectEditCooldown() const { return m_iDirectEditCooldown; } + const wstring& getEditBuffer() const { return m_editBuffer; } +#endif }; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index ad35894a5..67eca8ffc 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -237,6 +237,7 @@ UIController::UIController() m_winUserIndex = 0; m_mouseDraggingSliderScene = eUIScene_COUNT; m_mouseDraggingSliderId = -1; + m_mouseClickConsumedByScene = false; m_lastHoverMouseX = -1; m_lastHoverMouseY = -1; m_accumulatedTicks = 0; @@ -784,40 +785,36 @@ void UIController::tickInput() #endif { #ifdef _WINDOWS64 + m_mouseClickConsumedByScene = false; if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) { UIScene *pScene = NULL; - for (int grp = 0; grp < eUIGroup_COUNT && !pScene; ++grp) + // Search by layer priority across all groups (layer-first). + // Tooltip layer is skipped because it holds non-interactive + // overlays (button hints, timer) that should never capture mouse. + // Old group-first order found those tooltips on eUIGroup_Fullscreen + // before reaching in-game menus on eUIGroup_Player1. + static const EUILayer mouseLayers[] = { +#ifndef _CONTENT_PACKAGE + eUILayer_Debug, +#endif + eUILayer_Error, + eUILayer_Alert, + eUILayer_Popup, + eUILayer_Fullscreen, + eUILayer_Scene, + }; + for (int l = 0; l < _countof(mouseLayers) && !pScene; ++l) { - pScene = m_groups[grp]->GetTopScene(eUILayer_Debug); - if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Tooltips); - if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Error); - if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Alert); - if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Popup); - if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Fullscreen); - if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Scene); + for (int grp = 0; grp < eUIGroup_COUNT && !pScene; ++grp) + { + pScene = m_groups[grp]->GetTopScene(mouseLayers[l]); + } } if (pScene && pScene->getMovie()) { - Iggy *movie = pScene->getMovie(); int rawMouseX = g_KBMInput.GetMouseX(); int rawMouseY = g_KBMInput.GetMouseY(); - F32 mouseX = (F32)rawMouseX; - F32 mouseY = (F32)rawMouseY; - - extern HWND g_hWnd; - if (g_hWnd) - { - RECT rc; - GetClientRect(g_hWnd, &rc); - int winW = rc.right - rc.left; - int winH = rc.bottom - rc.top; - if (winW > 0 && winH > 0) - { - mouseX = mouseX * (m_fScreenWidth / (F32)winW); - mouseY = mouseY * (m_fScreenHeight / (F32)winH); - } - } // Only update hover focus when the mouse has actually moved, // so that mouse-wheel scrolling can change list selection @@ -826,43 +823,21 @@ void UIController::tickInput() m_lastHoverMouseX = rawMouseX; m_lastHoverMouseY = rawMouseY; - if (mouseMoved) + // Convert mouse to scene/movie coordinates + F32 sceneMouseX = (F32)rawMouseX; + F32 sceneMouseY = (F32)rawMouseY; { - IggyFocusHandle currentFocus = IGGY_FOCUS_NULL; - IggyFocusableObject focusables[64]; - S32 numFocusables = 0; - IggyPlayerGetFocusableObjects(movie, ¤tFocus, focusables, 64, &numFocusables); - - if (numFocusables > 0 && numFocusables <= 64) + extern HWND g_hWnd; + RECT rc; + if (g_hWnd && GetClientRect(g_hWnd, &rc)) { - IggyFocusHandle hitObject = IGGY_FOCUS_NULL; - for (S32 i = 0; i < numFocusables; ++i) + int winW = rc.right - rc.left; + int winH = rc.bottom - rc.top; + if (winW > 0 && winH > 0) { - if (mouseX >= focusables[i].x0 && mouseX <= focusables[i].x1 && - mouseY >= focusables[i].y0 && mouseY <= focusables[i].y1) - { - hitObject = focusables[i].object; - break; - } + sceneMouseX = sceneMouseX * ((F32)pScene->getRenderWidth() / (F32)winW); + sceneMouseY = sceneMouseY * ((F32)pScene->getRenderHeight() / (F32)winH); } - - if (hitObject != IGGY_FOCUS_NULL && hitObject != currentFocus) - { - IggyPlayerSetFocusRS(movie, hitObject, 0); - } - } - } - - // Convert mouse to scene/movie coordinates for slider hit testing - F32 sceneMouseX = mouseX; - F32 sceneMouseY = mouseY; - { - S32 displayWidth = 0, displayHeight = 0; - pScene->GetParentLayer()->getRenderDimensions(displayWidth, displayHeight); - if (displayWidth > 0 && displayHeight > 0) - { - sceneMouseX = mouseX * ((F32)pScene->getRenderWidth() / (F32)displayWidth); - sceneMouseY = mouseY * ((F32)pScene->getRenderHeight() / (F32)displayHeight); } } @@ -876,6 +851,105 @@ void UIController::tickInput() panelOffsetY = pMainPanel->getYPos(); } + // Mouse hover — hit test against C++ control bounds. + // Simple controls use SetFocusToElement; list controls + // use their own SetTouchFocus for Flash-side hit testing. + if (mouseMoved) + { + vector *controls = pScene->GetControls(); + if (controls) + { + int hitControlId = -1; + S32 hitCx = -1; + for (size_t i = 0; i < controls->size(); ++i) + { + UIControl *ctrl = (*controls)[i]; + if (!ctrl || !ctrl->getVisible() || ctrl->getId() < 0) + continue; + + UIControl::eUIControlType type = ctrl->getControlType(); + if (type != UIControl::eButton && type != UIControl::eTextInput && + type != UIControl::eCheckBox && type != UIControl::eSlider && + type != UIControl::eButtonList) + continue; + + // If the scene has an active panel (e.g. tab menus), + // skip controls that aren't children of that panel. + if (pMainPanel && ctrl->getParentPanel() != pMainPanel) + continue; + + ctrl->UpdateControl(); + S32 cx = ctrl->getXPos() + panelOffsetX; + S32 cy = ctrl->getYPos() + panelOffsetY; + S32 cw = ctrl->getWidth(); + S32 ch = ctrl->getHeight(); + if (cw <= 0 || ch <= 0) + continue; + + if (sceneMouseX >= cx && sceneMouseX <= cx + cw && + sceneMouseY >= cy && sceneMouseY <= cy + ch) + { + if (type == UIControl::eButtonList) + { + // ButtonList manages focus internally via Flash — + // pass mouse coords so it can highlight the right item. + ((UIControl_ButtonList *)ctrl)->SetTouchFocus( + (S32)sceneMouseX, (S32)sceneMouseY, false); + hitControlId = -1; + hitCx = -1; + break; // ButtonList takes priority + } + else if (cx > hitCx) + { + // When multiple controls overlap (e.g. debug scenes + // with side-by-side TextInputs reporting full width), + // pick the one whose left edge is closest to mouse X. + hitControlId = ctrl->getId(); + hitCx = cx; + } + } + } + + if (hitControlId >= 0 && pScene->getControlFocus() != hitControlId) + { + // Set focus via Iggy's internal focus system so that + // action dispatch (ACTION_MENU_OK) targets the right + // control. SetFocusToElement calls Flash's AS SetFocus + // which triggers full focus behavior (including caret + // visibility on TextInputs), so prefer IggyPlayerSetFocusRS. + Iggy *movie = pScene->getMovie(); + IggyFocusHandle currentFocus = IGGY_FOCUS_NULL; + IggyFocusableObject focusables[64]; + S32 numFocusables = 0; + IggyPlayerGetFocusableObjects(movie, ¤tFocus, focusables, 64, &numFocusables); + // Iggy focusable bounds may overlap (same wide-width issue + // as C++ bounds). Pick the one with largest x0 <= mouseX. + bool iggyFocusSet = false; + S32 bestFocusX0 = -1; + S32 bestFocusIdx = -1; + for (S32 fi = 0; fi < numFocusables && fi < 64; ++fi) + { + if (sceneMouseX >= focusables[fi].x0 && sceneMouseX <= focusables[fi].x1 && + sceneMouseY >= focusables[fi].y0 && sceneMouseY <= focusables[fi].y1) + { + if (focusables[fi].x0 > bestFocusX0) + { + bestFocusX0 = focusables[fi].x0; + bestFocusIdx = fi; + } + } + } + if (bestFocusIdx >= 0) + { + IggyPlayerSetFocusRS(movie, focusables[bestFocusIdx].object, 0); + iggyFocusSet = true; + } + if (!iggyFocusSet) + pScene->SetFocusToElement(hitControlId); + } + } + } + bool leftPressed = g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT); bool leftDown = leftPressed || g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT); @@ -890,12 +964,51 @@ void UIController::tickInput() vector *controls = pScene->GetControls(); if (controls) { + // Set Iggy dispatch focus for TextInput on click (not hover) + // so ACTION_MENU_OK targets the correct text field. + for (size_t i = 0; i < controls->size(); ++i) + { + UIControl *ctrl = (*controls)[i]; + if (!ctrl || ctrl->getControlType() != UIControl::eTextInput || !ctrl->getVisible()) + continue; + if (pMainPanel && ctrl->getParentPanel() != pMainPanel) + continue; + ctrl->UpdateControl(); + S32 cx = ctrl->getXPos() + panelOffsetX; + S32 cy = ctrl->getYPos() + panelOffsetY; + S32 cw = ctrl->getWidth(); + S32 ch = ctrl->getHeight(); + if (cw > 0 && ch > 0 && + sceneMouseX >= cx && sceneMouseX <= cx + cw && + sceneMouseY >= cy && sceneMouseY <= cy + ch) + { + Iggy *movie = pScene->getMovie(); + IggyFocusHandle currentFocus = IGGY_FOCUS_NULL; + IggyFocusableObject focusables[64]; + S32 numFocusables = 0; + IggyPlayerGetFocusableObjects(movie, ¤tFocus, focusables, 64, &numFocusables); + for (S32 fi = 0; fi < numFocusables && fi < 64; ++fi) + { + if (sceneMouseX >= focusables[fi].x0 && sceneMouseX <= focusables[fi].x1 && + sceneMouseY >= focusables[fi].y0 && sceneMouseY <= focusables[fi].y1) + { + IggyPlayerSetFocusRS(movie, focusables[fi].object, 0); + break; + } + } + break; + } + } + for (size_t i = 0; i < controls->size(); ++i) { UIControl *ctrl = (*controls)[i]; if (!ctrl || ctrl->getControlType() != UIControl::eSlider || !ctrl->getVisible()) continue; + if (pMainPanel && ctrl->getParentPanel() != pMainPanel) + continue; + UIControl_Slider *pSlider = (UIControl_Slider *)ctrl; pSlider->UpdateControl(); S32 cx = pSlider->getXPos() + panelOffsetX; @@ -942,6 +1055,12 @@ void UIController::tickInput() m_mouseDraggingSliderScene = eUIScene_COUNT; m_mouseDraggingSliderId = -1; } + + // Let the scene handle mouse clicks for custom navigation (e.g. crafting slots) + if (leftPressed && m_mouseDraggingSliderId < 0) + { + m_mouseClickConsumedByScene = pScene->handleMouseClick(sceneMouseX, sceneMouseY); + } } } #endif @@ -1206,7 +1325,7 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key) if ((key == ACTION_MENU_OK || key == ACTION_MENU_A) && !g_KBMInput.IsMouseGrabbed()) { - if (m_mouseDraggingSliderId < 0) + if (m_mouseDraggingSliderId < 0 && !m_mouseClickConsumedByScene) { if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) { pressed = true; down = true; } if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_LEFT)) { released = true; down = false; } @@ -1231,6 +1350,13 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key) pressed = true; down = true; } + + // Remap scroll wheel to UP/DOWN so all scenes get it without + // needing per-scene OTHER_STICK handling. + if (pressed && g_KBMInput.IsKBMActive()) + { + key = (key == ACTION_MENU_OTHER_STICK_UP) ? ACTION_MENU_UP : ACTION_MENU_DOWN; + } } } #endif diff --git a/Minecraft.Client/Common/UI/UIController.h b/Minecraft.Client/Common/UI/UIController.h index 373d67b2c..46983c100 100644 --- a/Minecraft.Client/Common/UI/UIController.h +++ b/Minecraft.Client/Common/UI/UIController.h @@ -160,6 +160,7 @@ private: unsigned int m_winUserIndex; EUIScene m_mouseDraggingSliderScene; int m_mouseDraggingSliderId; + bool m_mouseClickConsumedByScene; int m_lastHoverMouseX; int m_lastHoverMouseY; //bool m_bSysUIShowing; diff --git a/Minecraft.Client/Common/UI/UIScene.cpp b/Minecraft.Client/Common/UI/UIScene.cpp index 07e383538..9fb67d9e4 100644 --- a/Minecraft.Client/Common/UI/UIScene.cpp +++ b/Minecraft.Client/Common/UI/UIScene.cpp @@ -234,7 +234,7 @@ void UIScene::initialiseMovie() m_bUpdateOpacity = true; } -#ifdef __PSVITA__ +#if defined(__PSVITA__) || defined(_WINDOWS64) void UIScene::SetFocusToElement(int iID) { IggyDataValue result; @@ -454,6 +454,80 @@ UIControl* UIScene::GetMainPanel() return NULL; } +#ifdef _WINDOWS64 +bool UIScene::handleMouseClick(F32 x, F32 y) +{ + S32 panelOffsetX = 0, panelOffsetY = 0; + UIControl *pMainPanel = GetMainPanel(); + if (pMainPanel) + { + pMainPanel->UpdateControl(); + panelOffsetX = pMainPanel->getXPos(); + panelOffsetY = pMainPanel->getYPos(); + } + + vector *controls = GetControls(); + if (!controls) return false; + + // Flash may report overlapping bounds for side-by-side controls (e.g. + // TextInputs with full 630px width in debug scenes). Among all controls + // that contain the click point, pick the one whose left edge (cx) is + // closest to the click X — i.e. largest cx that is still <= x. + int bestId = -1; + S32 bestCx = -1; + UIControl *bestCtrl = NULL; + + for (size_t i = 0; i < controls->size(); ++i) + { + UIControl *ctrl = (*controls)[i]; + if (!ctrl || !ctrl->getVisible() || ctrl->getId() < 0) + continue; + + UIControl::eUIControlType type = ctrl->getControlType(); + if (type != UIControl::eButton && type != UIControl::eTextInput && + type != UIControl::eCheckBox) + continue; + + if (pMainPanel && ctrl->getParentPanel() != pMainPanel) + continue; + + ctrl->UpdateControl(); + S32 cx = ctrl->getXPos() + panelOffsetX; + S32 cy = ctrl->getYPos() + panelOffsetY; + S32 cw = ctrl->getWidth(); + S32 ch = ctrl->getHeight(); + if (cw <= 0 || ch <= 0) + continue; + + if (x >= cx && x <= cx + cw && y >= cy && y <= cy + ch) + { + if (cx > bestCx) + { + bestCx = cx; + bestId = ctrl->getId(); + bestCtrl = ctrl; + } + } + } + + if (bestId >= 0 && bestCtrl) + { + if (bestCtrl->getControlType() == UIControl::eCheckBox) + { + UIControl_CheckBox *cb = (UIControl_CheckBox *)bestCtrl; + bool newState = !cb->IsChecked(); + cb->setChecked(newState); + handleCheckboxToggled((F64)bestId, newState); + } + else + { + handlePress((F64)bestId, 0); + } + return true; + } + return false; +} +#endif void UIScene::addTimer(int id, int ms) { diff --git a/Minecraft.Client/Common/UI/UIScene.h b/Minecraft.Client/Common/UI/UIScene.h index b4008fa01..416e9374b 100644 --- a/Minecraft.Client/Common/UI/UIScene.h +++ b/Minecraft.Client/Common/UI/UIScene.h @@ -16,22 +16,26 @@ class UILayer; virtual bool mapElementsAndNames() \ { \ parentClass::mapElementsAndNames(); \ - IggyValuePath *currentRoot = IggyPlayerRootPath ( getMovie() ); + IggyValuePath *currentRoot = IggyPlayerRootPath ( getMovie() ); \ + UIControl *_mapPanel = NULL; #define UI_END_MAP_ELEMENTS_AND_NAMES() \ return true; \ } #define UI_MAP_ELEMENT( var, name) \ - { var.setupControl(this, currentRoot , name ); m_controls.push_back(&var); } + { var.setupControl(this, currentRoot , name ); var.m_pParentPanel = _mapPanel; m_controls.push_back(&var); } #define UI_BEGIN_MAP_CHILD_ELEMENTS( parent ) \ { \ IggyValuePath *lastRoot = currentRoot; \ - currentRoot = parent.getIggyValuePath(); + UIControl *_lastPanel = _mapPanel; \ + currentRoot = parent.getIggyValuePath(); \ + _mapPanel = &parent; #define UI_END_MAP_CHILD_ELEMENTS() \ currentRoot = lastRoot; \ + _mapPanel = _lastPanel; \ } #define UI_MAP_NAME( var, name ) \ @@ -141,8 +145,10 @@ public: virtual void tick(); IggyName registerFastName(const wstring &name); +#if defined(__PSVITA__) || defined(_WINDOWS64) + void SetFocusToElement(int iID); +#endif #ifdef __PSVITA__ - void SetFocusToElement(int iID); void UpdateSceneControls(); #endif protected: @@ -177,6 +183,13 @@ public: // returns main panel if controls are not living in the root virtual UIControl* GetMainPanel(); +#ifdef _WINDOWS64 + // Mouse click dispatch. Default implementation hit-tests C++ controls with + // "best match" logic (largest left-edge X) to handle overlapping Flash bounds, + // then calls the virtual handlePress. Override for custom behaviour (e.g. crafting). + virtual bool handleMouseClick(F32 x, F32 y); +#endif + void removeControl( UIControl_Base *control, bool centreScene); void slideLeft(); void slideRight(); diff --git a/Minecraft.Client/Common/UI/UIScene_AnvilMenu.cpp b/Minecraft.Client/Common/UI/UIScene_AnvilMenu.cpp index c810ad455..0d6918661 100644 --- a/Minecraft.Client/Common/UI/UIScene_AnvilMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_AnvilMenu.cpp @@ -96,6 +96,15 @@ void UIScene_AnvilMenu::tick() { UIScene_AbstractContainerMenu::tick(); +#ifdef _WINDOWS64 + UIControl_TextInput::EDirectEditResult editResult = m_textInputAnvil.tickDirectEdit(); + if (editResult == UIControl_TextInput::eDirectEdit_Confirmed) + { + m_itemName = m_textInputAnvil.getEditBuffer(); + updateItemName(); + } +#endif + handleTick(); } @@ -308,24 +317,52 @@ UIControl *UIScene_AnvilMenu::getSection(ESceneSection eSection) int UIScene_AnvilMenu::KeyboardCompleteCallback(LPVOID lpParam,bool bRes) { - // 4J HEG - No reason to set value if keyboard was cancelled UIScene_AnvilMenu *pClass=(UIScene_AnvilMenu *)lpParam; pClass->setIgnoreInput(false); if (bRes) { +#ifdef _WINDOWS64 + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t)); + Win64_GetKeyboardText(pchText, 128); + pClass->setEditNameValue((wchar_t *)pchText); + pClass->m_itemName = (wchar_t *)pchText; + pClass->updateItemName(); +#else uint16_t pchText[128]; ZeroMemory(pchText, 128 * sizeof(uint16_t) ); InputManager.GetText(pchText); pClass->setEditNameValue((wchar_t *)pchText); pClass->m_itemName = (wchar_t *)pchText; pClass->updateItemName(); +#endif } return 0; } void UIScene_AnvilMenu::handleEditNamePressed() { +#ifdef _WINDOWS64 + if (m_textInputAnvil.isDirectEditing() || m_textInputAnvil.getDirectEditCooldown() > 0) + return; + + if (g_KBMInput.IsKBMActive()) + { + m_textInputAnvil.beginDirectEdit(30); + } + else + { + setIgnoreInput(true); + UIKeyboardInitData kbData; + kbData.title = app.GetString(IDS_TITLE_RENAME); + kbData.defaultText = m_textInputAnvil.getLabel(); + kbData.maxChars = 30; + kbData.callback = &UIScene_AnvilMenu::KeyboardCompleteCallback; + kbData.lpParam = this; + ui.NavigateToScene(m_iPad, eUIScene_Keyboard, &kbData, eUILayer_Fullscreen, eUIGroup_Fullscreen); + } +#else setIgnoreInput(true); #if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__ int language = XGetLanguage(); @@ -337,13 +374,13 @@ void UIScene_AnvilMenu::handleEditNamePressed() InputManager.RequestKeyboard(app.GetString(IDS_TITLE_RENAME),m_textInputAnvil.getLabel(),(DWORD)m_iPad,30,&UIScene_AnvilMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Default); break; default: - // 4J Stu - Use a different keyboard for non-asian languages so we don't have prediction on InputManager.RequestKeyboard(app.GetString(IDS_TITLE_RENAME),m_textInputAnvil.getLabel(),(DWORD)m_iPad,30,&UIScene_AnvilMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Alphabet_Extended); break; } #else InputManager.RequestKeyboard(app.GetString(IDS_TITLE_RENAME),m_textInputAnvil.getLabel(),(DWORD)m_iPad,30,&UIScene_AnvilMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Default); #endif +#endif } void UIScene_AnvilMenu::setEditNameValue(const wstring &name) @@ -357,6 +394,8 @@ void UIScene_AnvilMenu::setEditNameEditable(bool enabled) void UIScene_AnvilMenu::setCostLabel(const wstring &label, bool canAfford) { + if (!getMovie()) return; + IggyDataValue result; IggyDataValue value[2]; @@ -375,6 +414,8 @@ void UIScene_AnvilMenu::showCross(bool show) { if(m_showingCross != show) { + if (!getMovie()) return; + IggyDataValue result; IggyDataValue value[1]; diff --git a/Minecraft.Client/Common/UI/UIScene_CraftingMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.cpp index 66d8c41ee..369d4c51d 100644 --- a/Minecraft.Client/Common/UI/UIScene_CraftingMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.cpp @@ -4,6 +4,9 @@ #include "..\..\MultiplayerLocalPlayer.h" #include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" #include "UIScene_CraftingMenu.h" +#ifdef _WINDOWS64 +#include "..\..\Windows64\Iggy\gdraw\gdraw_d3d11.h" +#endif #ifdef __PSVITA__ #define GAME_CRAFTING_TOUCHUPDATE_TIMER_ID 0 @@ -12,6 +15,11 @@ UIScene_CraftingMenu::UIScene_CraftingMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) { +#ifdef _WINDOWS64 + m_hSlotBoundsValid = false; + m_hSlotX0 = m_hSlotY0 = m_hSlotY1 = 0; + m_hSlotSpacing = 0; +#endif m_bIgnoreKeyPresses = false; CraftingPanelScreenInput* initData = (CraftingPanelScreenInput*)_initData; @@ -383,6 +391,39 @@ void UIScene_CraftingMenu::handleTimerComplete(int id) } #endif +#ifdef _WINDOWS64 +bool UIScene_CraftingMenu::handleMouseClick(F32 x, F32 y) +{ + if (!m_hSlotBoundsValid) + return false; + + F32 rowWidth = m_hSlotSpacing * m_iCraftablesMaxHSlotC; + + if (m_hSlotSpacing > 0 && x >= m_hSlotX0 && x < m_hSlotX0 + rowWidth && + y >= m_hSlotY0 && y < m_hSlotY1) + { + int iNewSlot = (int)((x - m_hSlotX0) / m_hSlotSpacing); + if (iNewSlot >= 0 && iNewSlot < m_iCraftablesMaxHSlotC) + { + int iOldHSlot = m_iCurrentSlotHIndex; + m_iCurrentSlotHIndex = iNewSlot; + m_iCurrentSlotVIndex = 1; + iVSlotIndexA[0] = CanBeMadeA[m_iCurrentSlotHIndex].iCount - 1; + iVSlotIndexA[1] = 0; + iVSlotIndexA[2] = 1; + UpdateVerticalSlots(); + UpdateHighlight(); + if (CanBeMadeA[iOldHSlot].iCount > 0) + setShowCraftHSlot(iOldHSlot, true); + ui.PlayUISFX(eSFX_Focus); + return true; + } + } + + return false; +} +#endif + void UIScene_CraftingMenu::handleReload() { m_slotListInventory.addSlots(CRAFTING_INVENTORY_SLOT_START,CRAFTING_INVENTORY_SLOT_END - CRAFTING_INVENTORY_SLOT_START); @@ -478,6 +519,32 @@ void UIScene_CraftingMenu::customDraw(IggyCustomDrawCallbackRegion *region) { decorations = false; int iIndex = slotId - CRAFTING_H_SLOT_START; +#ifdef _WINDOWS64 + // Cache H slot SWF-space positions from the custom draw transform matrix + if (iIndex == 0 || iIndex == 1) + { + F32 mat[16]; + gdraw_D3D11_CalculateCustomDraw_4J(region, mat); + // Matrix to SWF coords (same formula as setupCustomDrawMatrices) + F32 sw = (F32)getRenderWidth(); + F32 sh = (F32)getRenderHeight(); + F32 swfX = sw * (1.0f + mat[3]) / 2.0f; + F32 swfY = sh * (1.0f - mat[7]) / 2.0f; + if (iIndex == 0) + { + m_hSlotX0 = swfX; + m_hSlotY0 = swfY; + // Slot visual height from matrix scale and region height + F32 slotH = sh * (-mat[5]) / 2.0f * region->y1; + m_hSlotY1 = swfY + slotH; + } + else + { + m_hSlotSpacing = swfX - m_hSlotX0; + m_hSlotBoundsValid = (m_hSlotSpacing > 0); + } + } +#endif if(m_hSlotsInfo[iIndex].show) { item = m_hSlotsInfo[iIndex].item; diff --git a/Minecraft.Client/Common/UI/UIScene_CraftingMenu.h b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.h index 84c9ba659..be3fe6c7d 100644 --- a/Minecraft.Client/Common/UI/UIScene_CraftingMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.h @@ -70,6 +70,13 @@ public: virtual void handleTouchBoxRebuild(); virtual void handleTimerComplete(int id); #endif +#ifdef _WINDOWS64 + virtual bool handleMouseClick(F32 x, F32 y); + // Cached from customDraw — H slot bounding boxes in SWF space + F32 m_hSlotX0, m_hSlotY0, m_hSlotY1; + F32 m_hSlotSpacing; // x distance between slot 0 and slot 1 + bool m_hSlotBoundsValid; +#endif protected: UIControl m_controlMainPanel; diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp index bdf949870..364c9ebda 100644 --- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp @@ -84,11 +84,6 @@ UIScene_CreateWorldMenu::UIScene_CreateWorldMenu(int iPad, void *initData, UILay m_iGameModeId = GameType::SURVIVAL->getId(); m_pDLCPack = NULL; m_bRebuildTouchBoxes = false; -#ifdef _WINDOWS64 - m_bDirectEditing = false; - m_iDirectEditCooldown = 0; - m_iCursorPos = 0; -#endif m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); // 4J-PB - read the settings for the online flag. We'll only save this setting if the user changed it. @@ -295,92 +290,11 @@ void UIScene_CreateWorldMenu::tick() UIScene::tick(); #ifdef _WINDOWS64 - 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()) + UIControl_TextInput::EDirectEditResult editResult = m_editWorldName.tickDirectEdit(); + if (editResult == UIControl_TextInput::eDirectEdit_Confirmed || editResult == UIControl_TextInput::eDirectEdit_Cancelled) { - m_editWorldName.setCaretVisible(m_bDirectEditing); - if (m_bDirectEditing) - m_editWorldName.setCaretIndex(m_iCursorPos); - } - - if (m_bDirectEditing) - { - wchar_t ch; - bool changed = false; - while (g_KBMInput.ConsumeChar(ch)) - { - if (ch == 0x08) // backspace - { - if (m_iCursorPos > 0) - { - m_worldName.erase(m_iCursorPos - 1, 1); - m_iCursorPos--; - changed = true; - } - } - else if (ch == 0x0D) // enter - confirm - { - 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.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)) - { - m_worldName = m_worldNameBeforeEdit; - 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()); - } + m_worldName = m_editWorldName.getEditBuffer(); + m_buttonCreateWorld.setEnable(!m_worldName.empty()); } #endif @@ -450,7 +364,7 @@ void UIScene_CreateWorldMenu::handleInput(int iPad, int key, bool repeat, bool p { if(m_bIgnoreInput) return; #ifdef _WINDOWS64 - if (m_bDirectEditing || m_iDirectEditCooldown > 0) { handled = true; return; } + if (m_editWorldName.isDirectEditing() || m_editWorldName.getDirectEditCooldown() > 0) { handled = true; return; } #endif ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); @@ -507,7 +421,7 @@ void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId) { if(m_bIgnoreInput) return; #ifdef _WINDOWS64 - if (m_bDirectEditing || m_iDirectEditCooldown > 0) return; + if (m_editWorldName.isDirectEditing() || m_editWorldName.getDirectEditCooldown() > 0) return; #endif //CD - Added for audio @@ -531,14 +445,8 @@ void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId) } else { - // PC with KBM active: edit the name field directly in-place. - 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); + m_bIgnoreInput = false; + m_editWorldName.beginDirectEdit(25); } #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 1f941c91b..49ab6ec50 100644 --- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h @@ -51,12 +51,6 @@ private: DLCPack * m_pDLCPack; bool m_bRebuildTouchBoxes; -#ifdef _WINDOWS64 - bool m_bDirectEditing; - wstring m_worldNameBeforeEdit; - int m_iDirectEditCooldown; - int m_iCursorPos; -#endif public: UIScene_CreateWorldMenu(int iPad, void *initData, UILayer *parentLayer); diff --git a/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp index 2a8ac9f8e..2eee6ce6a 100644 --- a/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp +++ b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp @@ -34,6 +34,10 @@ UIScene_DebugCreateSchematic::UIScene_DebugCreateSchematic(int iPad, void *initD m_buttonCreate.init(L"Create",eControl_Create); m_data = new ConsoleSchematicFile::XboxSchematicInitParam(); + +#ifdef _WINDOWS64 + m_activeDirectEditControl = eControl_Create; // sentinel: no active edit +#endif } wstring UIScene_DebugCreateSchematic::getMoviePath() @@ -41,8 +45,76 @@ wstring UIScene_DebugCreateSchematic::getMoviePath() return L"DebugCreateSchematic"; } +UIControl_TextInput* UIScene_DebugCreateSchematic::getTextInputForControl(eControls ctrl) +{ + switch (ctrl) + { + case eControl_Name: return &m_textInputName; + case eControl_StartX: return &m_textInputStartX; + case eControl_StartY: return &m_textInputStartY; + case eControl_StartZ: return &m_textInputStartZ; + case eControl_EndX: return &m_textInputEndX; + case eControl_EndY: return &m_textInputEndY; + case eControl_EndZ: return &m_textInputEndZ; + default: return NULL; + } +} + +#ifdef _WINDOWS64 +bool UIScene_DebugCreateSchematic::handleMouseClick(F32 x, F32 y) +{ + if (m_activeDirectEditControl != eControl_Create) return true; + UIScene::handleMouseClick(x, y); + return true; // always consume to prevent Iggy re-entry on empty space +} +#endif + +void UIScene_DebugCreateSchematic::tick() +{ + UIScene::tick(); + +#ifdef _WINDOWS64 + UIControl_TextInput* allInputs[] = { &m_textInputName, &m_textInputStartX, &m_textInputStartY, &m_textInputStartZ, &m_textInputEndX, &m_textInputEndY, &m_textInputEndZ }; + for (int i = 0; i < 7; i++) + allInputs[i]->tickDirectEdit(); + + if (m_activeDirectEditControl != eControl_Create) + { + UIControl_TextInput* active = getTextInputForControl(m_activeDirectEditControl); + if (active && !active->isDirectEditing()) + { + // Edit finished — apply value + wstring value = active->getEditBuffer(); + int iVal = 0; + if (!value.empty() && m_activeDirectEditControl != eControl_Name) + iVal = _fromString(value); + + switch (m_activeDirectEditControl) + { + case eControl_Name: + if (!value.empty()) + swprintf(m_data->name, 64, L"%ls", value.c_str()); + else + swprintf(m_data->name, 64, L"schematic"); + break; + case eControl_StartX: m_data->startX = iVal; break; + case eControl_StartY: m_data->startY = iVal; break; + case eControl_StartZ: m_data->startZ = iVal; break; + case eControl_EndX: m_data->endX = iVal; break; + case eControl_EndY: m_data->endY = iVal; break; + case eControl_EndZ: m_data->endZ = iVal; break; + } + m_activeDirectEditControl = eControl_Create; + } + } +#endif +} + void UIScene_DebugCreateSchematic::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) { +#ifdef _WINDOWS64 + if (m_activeDirectEditControl != eControl_Create) return; +#endif ui.AnimateKeyPress(iPad, key, repeat, pressed, released); switch(key) @@ -67,6 +139,9 @@ void UIScene_DebugCreateSchematic::handleInput(int iPad, int key, bool repeat, b void UIScene_DebugCreateSchematic::handlePress(F64 controlId, F64 childId) { +#ifdef _WINDOWS64 + if (m_activeDirectEditControl != eControl_Create) return; +#endif switch((int)controlId) { case eControl_Create: @@ -112,8 +187,29 @@ void UIScene_DebugCreateSchematic::handlePress(F64 controlId, F64 childId) case eControl_EndX: case eControl_EndY: case eControl_EndZ: - m_keyboardCallbackControl = (eControls)((int)controlId); - InputManager.RequestKeyboard(L"Enter something",L"",(DWORD)0,25,&UIScene_DebugCreateSchematic::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Default); + { + m_keyboardCallbackControl = (eControls)((int)controlId); +#ifdef _WINDOWS64 + if (g_KBMInput.IsKBMActive()) + { + m_activeDirectEditControl = m_keyboardCallbackControl; + UIControl_TextInput* input = getTextInputForControl(m_activeDirectEditControl); + if (input) input->beginDirectEdit(25); + } + else + { + UIKeyboardInitData kbData; + kbData.title = L"Enter something"; + kbData.defaultText = L""; + kbData.maxChars = 25; + kbData.callback = &UIScene_DebugCreateSchematic::KeyboardCompleteCallback; + kbData.lpParam = this; + ui.NavigateToScene(m_iPad, eUIScene_Keyboard, &kbData, eUILayer_Fullscreen, eUIGroup_Fullscreen); + } +#else + InputManager.RequestKeyboard(L"Enter something",L"",(DWORD)0,25,&UIScene_DebugCreateSchematic::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Default); +#endif + } break; }; } @@ -138,9 +234,15 @@ int UIScene_DebugCreateSchematic::KeyboardCompleteCallback(LPVOID lpParam,bool b { UIScene_DebugCreateSchematic *pClass=(UIScene_DebugCreateSchematic *)lpParam; +#ifdef _WINDOWS64 + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t)); + Win64_GetKeyboardText(pchText, 128); +#else uint16_t pchText[128]; ZeroMemory(pchText, 128 * sizeof(uint16_t) ); InputManager.GetText(pchText); +#endif if(pchText[0]!=0) { diff --git a/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.h b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.h index cbfe785d0..f830727a2 100644 --- a/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.h +++ b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.h @@ -24,6 +24,10 @@ private: ConsoleSchematicFile::XboxSchematicInitParam *m_data; +#ifdef _WINDOWS64 + eControls m_activeDirectEditControl; +#endif + public: UIScene_DebugCreateSchematic(int iPad, void *initData, UILayer *parentLayer); @@ -58,8 +62,12 @@ protected: UI_END_MAP_ELEMENTS_AND_NAMES() virtual wstring getMoviePath(); +#ifdef _WINDOWS64 + virtual bool handleMouseClick(F32 x, F32 y); +#endif public: + virtual void tick(); // INPUT virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); @@ -68,6 +76,7 @@ protected: virtual void handleCheckboxToggled(F64 controlId, bool selected); private: + UIControl_TextInput* getTextInputForControl(eControls ctrl); static int KeyboardCompleteCallback(LPVOID lpParam,const bool bRes); }; #endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.cpp b/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.cpp index dd5a429f0..440d51b4c 100644 --- a/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.cpp +++ b/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.cpp @@ -31,19 +31,19 @@ UIScene_DebugSetCamera::UIScene_DebugSetCamera(int iPad, void *initData, UILayer WCHAR TempString[256]; - swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_camX); + swprintf( (WCHAR *)TempString, 256, L"%.2f", currentPosition->m_camX); m_textInputX.init(TempString, eControl_CamX); - swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_camY); + swprintf( (WCHAR *)TempString, 256, L"%.2f", currentPosition->m_camY); m_textInputY.init(TempString, eControl_CamY); - swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_camZ); + swprintf( (WCHAR *)TempString, 256, L"%.2f", currentPosition->m_camZ); m_textInputZ.init(TempString, eControl_CamZ); - swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_yRot); + swprintf( (WCHAR *)TempString, 256, L"%.2f", currentPosition->m_yRot); m_textInputYRot.init(TempString, eControl_YRot); - swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_elev); + swprintf( (WCHAR *)TempString, 256, L"%.2f", currentPosition->m_elev); m_textInputElevation.init(TempString, eControl_Elevation); m_checkboxLockPlayer.init(L"Lock Player", eControl_LockPlayer, app.GetFreezePlayers()); @@ -55,6 +55,10 @@ UIScene_DebugSetCamera::UIScene_DebugSetCamera(int iPad, void *initData, UILayer m_labelCamY.init(L"CamY"); m_labelCamZ.init(L"CamZ"); m_labelYRotElev.init(L"Y-Rot & Elevation (Degs)"); + +#ifdef _WINDOWS64 + m_activeDirectEditControl = eControl_Teleport; // sentinel — no active edit +#endif } wstring UIScene_DebugSetCamera::getMoviePath() @@ -62,8 +66,87 @@ wstring UIScene_DebugSetCamera::getMoviePath() return L"DebugSetCamera"; } +#ifdef _WINDOWS64 +UIControl_TextInput* UIScene_DebugSetCamera::getTextInputForControl(eControls ctrl) +{ + switch (ctrl) + { + case eControl_CamX: return &m_textInputX; + case eControl_CamY: return &m_textInputY; + case eControl_CamZ: return &m_textInputZ; + case eControl_YRot: return &m_textInputYRot; + case eControl_Elevation: return &m_textInputElevation; + default: return NULL; + } +} + +bool UIScene_DebugSetCamera::handleMouseClick(F32 x, F32 y) +{ + // If currently editing, confirm the current edit before processing the click + if (m_activeDirectEditControl != eControl_Teleport) + { + UIControl_TextInput* active = getTextInputForControl(m_activeDirectEditControl); + if (active && active->isDirectEditing()) + { + wstring value = active->getEditBuffer(); + double val = 0; + if (!value.empty()) val = _fromString(value); + switch (m_activeDirectEditControl) + { + case eControl_CamX: currentPosition->m_camX = val; break; + case eControl_CamY: currentPosition->m_camY = val; break; + case eControl_CamZ: currentPosition->m_camZ = val; break; + case eControl_YRot: currentPosition->m_yRot = val; break; + case eControl_Elevation: currentPosition->m_elev = val; break; + } + active->confirmDirectEdit(); + } + m_activeDirectEditControl = eControl_Teleport; + } + + UIScene::handleMouseClick(x, y); + return true; // always consume to prevent Iggy re-entry on empty space +} +#endif + +void UIScene_DebugSetCamera::tick() +{ + UIScene::tick(); + +#ifdef _WINDOWS64 + UIControl_TextInput* inputs[] = { &m_textInputX, &m_textInputY, &m_textInputZ, &m_textInputYRot, &m_textInputElevation }; + for (int i = 0; i < 5; i++) + { + UIControl_TextInput::EDirectEditResult result = inputs[i]->tickDirectEdit(); + if (result == UIControl_TextInput::eDirectEdit_Confirmed) + { + wstring value = inputs[i]->getEditBuffer(); + double val = 0; + if (!value.empty()) val = _fromString(value); + eControls ctrl = (eControls)i; // eControl_CamX=0, CamY=1, CamZ=2, YRot=3, Elevation=4 + switch (ctrl) + { + case eControl_CamX: currentPosition->m_camX = val; break; + case eControl_CamY: currentPosition->m_camY = val; break; + case eControl_CamZ: currentPosition->m_camZ = val; break; + case eControl_YRot: currentPosition->m_yRot = val; break; + case eControl_Elevation: currentPosition->m_elev = val; break; + } + m_activeDirectEditControl = eControl_Teleport; + } + else if (result == UIControl_TextInput::eDirectEdit_Cancelled) + { + m_activeDirectEditControl = eControl_Teleport; + } + } +#endif +} + void UIScene_DebugSetCamera::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) { +#ifdef _WINDOWS64 + if (m_activeDirectEditControl != eControl_Teleport) { handled = true; return; } +#endif ui.AnimateKeyPress(iPad, key, repeat, pressed, released); switch(key) @@ -88,11 +171,14 @@ void UIScene_DebugSetCamera::handleInput(int iPad, int key, bool repeat, bool pr void UIScene_DebugSetCamera::handlePress(F64 controlId, F64 childId) { +#ifdef _WINDOWS64 + if (m_activeDirectEditControl != eControl_Teleport) return; +#endif switch((int)controlId) { case eControl_Teleport: app.SetXuiServerAction( ProfileManager.GetPrimaryPad(), - eXuiServerAction_SetCameraLocation, + eXuiServerAction_SetCameraLocation, (void *)currentPosition); break; case eControl_CamX: @@ -100,8 +186,27 @@ void UIScene_DebugSetCamera::handlePress(F64 controlId, F64 childId) case eControl_CamZ: case eControl_YRot: case eControl_Elevation: - m_keyboardCallbackControl = (eControls)((int)controlId); + m_keyboardCallbackControl = (eControls)((int)controlId); +#ifdef _WINDOWS64 + if (g_KBMInput.IsKBMActive()) + { + m_activeDirectEditControl = m_keyboardCallbackControl; + UIControl_TextInput* input = getTextInputForControl(m_activeDirectEditControl); + if (input) input->beginDirectEdit(25); + } + else + { + UIKeyboardInitData kbData; + kbData.title = L"Enter value"; + kbData.defaultText = L""; + kbData.maxChars = 25; + kbData.callback = &UIScene_DebugSetCamera::KeyboardCompleteCallback; + kbData.lpParam = this; + ui.NavigateToScene(m_iPad, eUIScene_Keyboard, &kbData, eUILayer_Fullscreen, eUIGroup_Fullscreen); + } +#else InputManager.RequestKeyboard(L"Enter something",L"",(DWORD)0,25,&UIScene_DebugSetCamera::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Default); +#endif break; }; } @@ -119,9 +224,13 @@ void UIScene_DebugSetCamera::handleCheckboxToggled(F64 controlId, bool selected) int UIScene_DebugSetCamera::KeyboardCompleteCallback(LPVOID lpParam,bool bRes) { UIScene_DebugSetCamera *pClass=(UIScene_DebugSetCamera *)lpParam; - uint16_t pchText[2048];//[128]; - ZeroMemory(pchText, 2048/*128*/ * sizeof(uint16_t) ); + uint16_t pchText[2048]; + ZeroMemory(pchText, 2048 * sizeof(uint16_t)); +#ifdef _WINDOWS64 + Win64_GetKeyboardText(pchText, 2048); +#else InputManager.GetText(pchText); +#endif if(pchText[0]!=0) { diff --git a/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.h b/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.h index 38db1258b..95b72f761 100644 --- a/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.h +++ b/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.h @@ -26,6 +26,10 @@ private: FreezePlayerParam *fpp; eControls m_keyboardCallbackControl; +#ifdef _WINDOWS64 + eControls m_activeDirectEditControl; + UIControl_TextInput* getTextInputForControl(eControls ctrl); +#endif public: UIScene_DebugSetCamera(int iPad, void *initData, UILayer *parentLayer); @@ -54,6 +58,10 @@ protected: UI_END_MAP_ELEMENTS_AND_NAMES() virtual wstring getMoviePath(); + virtual void tick(); +#ifdef _WINDOWS64 + virtual bool handleMouseClick(F32 x, F32 y); +#endif public: // INPUT diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp index d6f89832c..7c3ec0138 100644 --- a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp @@ -211,6 +211,12 @@ void UIScene_LaunchMoreOptionsMenu::tick() { UIScene::tick(); +#ifdef _WINDOWS64 + UIControl_TextInput::EDirectEditResult editResult = m_editSeed.tickDirectEdit(); + if (editResult == UIControl_TextInput::eDirectEdit_Confirmed) + m_params->seed = m_editSeed.getEditBuffer(); +#endif + bool bMultiplayerAllowed = ProfileManager.IsSignedInLive(m_params->iPad) && ProfileManager.AllowedToPlayMultiplayer(m_params->iPad); if (bMultiplayerAllowed != m_bMultiplayerAllowed) @@ -257,6 +263,9 @@ void UIScene_LaunchMoreOptionsMenu::handleDestroy() void UIScene_LaunchMoreOptionsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) { if(m_bIgnoreInput) return; +#ifdef _WINDOWS64 + if (m_editSeed.isDirectEditing() || m_editSeed.getDirectEditCooldown() > 0) return; +#endif //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); @@ -334,7 +343,9 @@ void UIScene_LaunchMoreOptionsMenu::handleTouchInput(unsigned int iPad, S32 x, S } } } +#endif +#if defined(__PSVITA__) || defined(_WINDOWS64) UIControl* UIScene_LaunchMoreOptionsMenu::GetMainPanel() { if(m_tabIndex == 0) @@ -546,11 +557,16 @@ int UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback(LPVOID lpParam,b { UIScene_LaunchMoreOptionsMenu *pClass=(UIScene_LaunchMoreOptionsMenu *)lpParam; pClass->m_bIgnoreInput=false; - // 4J HEG - No reason to set value if keyboard was cancelled if (bRes) { +#ifdef _WINDOWS64 + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t)); + Win64_GetKeyboardText(pchText, 128); + pClass->m_editSeed.setLabel((wchar_t *)pchText); + pClass->m_params->seed = (wchar_t *)pchText; +#else #ifdef __PSVITA__ - //CD - Changed to 2048 [SCE_IME_MAX_TEXT_LENGTH] uint16_t pchText[2048]; ZeroMemory(pchText, 2048 * sizeof(uint16_t) ); #else @@ -560,6 +576,7 @@ int UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback(LPVOID lpParam,b InputManager.GetText(pchText); pClass->m_editSeed.setLabel((wchar_t *)pchText); pClass->m_params->seed = (wchar_t *)pchText; +#endif } return 0; } @@ -567,11 +584,31 @@ int UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback(LPVOID lpParam,b void UIScene_LaunchMoreOptionsMenu::handlePress(F64 controlId, F64 childId) { if(m_bIgnoreInput) return; +#ifdef _WINDOWS64 + if (m_editSeed.isDirectEditing() || m_editSeed.getDirectEditCooldown() > 0) return; +#endif switch((int)controlId) { case eControl_EditSeed: { +#ifdef _WINDOWS64 + if (g_KBMInput.IsKBMActive()) + { + m_editSeed.beginDirectEdit(60); + } + else + { + m_bIgnoreInput = true; + UIKeyboardInitData kbData; + kbData.title = app.GetString(IDS_CREATE_NEW_WORLD_SEED); + kbData.defaultText = m_editSeed.getLabel(); + kbData.maxChars = 60; + kbData.callback = &UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback; + kbData.lpParam = this; + ui.NavigateToScene(m_iPad, eUIScene_Keyboard, &kbData); + } +#else m_bIgnoreInput=true; #ifdef __PS3__ int language = XGetLanguage(); @@ -583,12 +620,12 @@ void UIScene_LaunchMoreOptionsMenu::handlePress(F64 controlId, F64 childId) InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD_SEED),m_editSeed.getLabel(),(DWORD)0,60,&UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback,this,C_4JInput::EKeyboardMode_Default); break; default: - // 4J Stu - Use a different keyboard for non-asian languages so we don't have prediction on InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD_SEED),m_editSeed.getLabel(),(DWORD)0,60,&UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback,this,C_4JInput::EKeyboardMode_Alphabet_Extended); break; } #else InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD_SEED),m_editSeed.getLabel(),(DWORD)0,60,&UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback,this,C_4JInput::EKeyboardMode_Default); +#endif #endif } break; diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h index 367db10d3..e7da49183 100644 --- a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h @@ -160,6 +160,8 @@ private: #ifdef __PSVITA__ virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased); - virtual UIControl* GetMainPanel(); #endif //__PSVITA__ +#if defined(__PSVITA__) || defined(_WINDOWS64) + virtual UIControl* GetMainPanel(); +#endif }; diff --git a/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp index c29bac2d3..cfb1624bf 100644 --- a/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp @@ -18,6 +18,9 @@ UIScene_SignEntryMenu::UIScene_SignEntryMenu(int iPad, void *_initData, UILayer m_bConfirmed = false; m_bIgnoreInput = false; +#ifdef _WINDOWS64 + m_iActiveDirectEditLine = -1; +#endif m_buttonConfirm.init(app.GetString(IDS_DONE), eControl_Confirm); m_labelMessage.init(app.GetString(IDS_EDIT_SIGN_MESSAGE)); @@ -77,6 +80,18 @@ void UIScene_SignEntryMenu::tick() { UIScene::tick(); +#ifdef _WINDOWS64 + for (int i = 0; i < 4; i++) + m_textInputLines[i].tickDirectEdit(); + + if (m_iActiveDirectEditLine >= 0) + { + UIControl_TextInput& line = m_textInputLines[m_iActiveDirectEditLine]; + if (!line.isDirectEditing()) + m_iActiveDirectEditLine = -1; + } +#endif + if(m_bConfirmed) { m_bConfirmed = false; @@ -107,6 +122,30 @@ void UIScene_SignEntryMenu::tick() void UIScene_SignEntryMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) { if(m_bConfirmed || m_bIgnoreInput) return; +#ifdef _WINDOWS64 + if (m_iActiveDirectEditLine >= 0) + { + // Mouse click while editing — confirm current line and let click through + if (key == ACTION_MENU_OK && pressed && g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) + { + m_textInputLines[m_iActiveDirectEditLine].confirmDirectEdit(); + m_iActiveDirectEditLine = -1; + } + else + { + handled = true; + return; + } + } + for (int i = 0; i < 4; i++) + { + if (m_textInputLines[i].getDirectEditCooldown() > 0) + { + handled = true; + return; + } + } +#endif ui.AnimateKeyPress(iPad, key, repeat, pressed, released); @@ -142,21 +181,36 @@ void UIScene_SignEntryMenu::handleInput(int iPad, int key, bool repeat, bool pre int UIScene_SignEntryMenu::KeyboardCompleteCallback(LPVOID lpParam,bool bRes) { - // 4J HEG - No reason to set value if keyboard was cancelled UIScene_SignEntryMenu *pClass=(UIScene_SignEntryMenu *)lpParam; pClass->m_bIgnoreInput = false; if (bRes) { +#ifdef _WINDOWS64 + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t)); + Win64_GetKeyboardText(pchText, 128); + pClass->m_textInputLines[pClass->m_iEditingLine].setLabel((wchar_t *)pchText); +#else uint16_t pchText[128]; ZeroMemory(pchText, 128 * sizeof(uint16_t) ); InputManager.GetText(pchText); pClass->m_textInputLines[pClass->m_iEditingLine].setLabel((wchar_t *)pchText); +#endif } return 0; } void UIScene_SignEntryMenu::handlePress(F64 controlId, F64 childId) { +#ifdef _WINDOWS64 + // After direct edit ends (Enter/Escape), skip input for a few frames + // to absorb the matching ACTION_MENU_OK that would re-open the edit. + for (int i = 0; i < 4; i++) + { + if (m_textInputLines[i].isDirectEditing() || m_textInputLines[i].getDirectEditCooldown() > 0) + return; + } +#endif switch((int)controlId) { case eControl_Confirm: @@ -170,6 +224,24 @@ void UIScene_SignEntryMenu::handlePress(F64 controlId, F64 childId) case eControl_Line4: { m_iEditingLine = (int)controlId; +#ifdef _WINDOWS64 + if (g_KBMInput.IsKBMActive()) + { + m_iActiveDirectEditLine = m_iEditingLine; + m_textInputLines[m_iEditingLine].beginDirectEdit(15); + } + else + { + m_bIgnoreInput = true; + UIKeyboardInitData kbData; + kbData.title = app.GetString(IDS_SIGN_TITLE); + kbData.defaultText = m_textInputLines[m_iEditingLine].getLabel(); + kbData.maxChars = 15; + kbData.callback = &UIScene_SignEntryMenu::KeyboardCompleteCallback; + kbData.lpParam = this; + ui.NavigateToScene(m_iPad, eUIScene_Keyboard, &kbData, eUILayer_Fullscreen, eUIGroup_Fullscreen); + } +#else m_bIgnoreInput = true; #ifdef _XBOX_ONE // 4J-PB - Xbox One uses the Windows virtual keyboard, and doesn't have the Xbox 360 Latin keyboard type, so we can't restrict the input set to alphanumeric. The closest we get is the emailSmtpAddress type. @@ -187,6 +259,7 @@ void UIScene_SignEntryMenu::handlePress(F64 controlId, F64 childId) } #else InputManager.RequestKeyboard(app.GetString(IDS_SIGN_TITLE),m_textInputLines[m_iEditingLine].getLabel(),(DWORD)m_iPad,15,&UIScene_SignEntryMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Alphabet); +#endif #endif } break; diff --git a/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.h b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.h index 28b37d531..68767be74 100644 --- a/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.h @@ -21,6 +21,9 @@ private: int m_iEditingLine; bool m_bConfirmed; bool m_bIgnoreInput; +#ifdef _WINDOWS64 + int m_iActiveDirectEditLine; +#endif UIControl_Button m_buttonConfirm; UIControl_Label m_labelMessage;