diff --git a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp index 88258421..37ef0233 100644 --- a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp @@ -1486,12 +1486,26 @@ bool IUIScene_AbstractContainerMenu::handleKeyDown(int iPad, int iAction, bool b } break; case ACTION_MENU_UP: +#ifdef _WINDOWS64 + if (g_KBMInput.WasMouseWheelConsumed()) + { + handleAdditionalKeyPress(ACTION_MENU_OTHER_STICK_UP); + break; + } +#endif { //ui.PlayUISFX(eSFX_Focus); m_eCurrTapState = eTapStateUp; } break; case ACTION_MENU_DOWN: +#ifdef _WINDOWS64 + if (g_KBMInput.WasMouseWheelConsumed()) + { + handleAdditionalKeyPress(ACTION_MENU_OTHER_STICK_DOWN); + break; + } +#endif { //ui.PlayUISFX(eSFX_Focus); m_eCurrTapState = eTapStateDown; diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index 0d3f7dac..56aaf259 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -1099,13 +1099,7 @@ void IUIScene_CreativeMenu::handleAdditionalKeyPress(int iAction) break; case ACTION_MENU_OTHER_STICK_DOWN: { - int pageStep = TabSpec::rows; -#ifdef _WINDOWS64 - if (g_KBMInput.WasMouseWheelConsumed()) - { - pageStep = 1; - } -#endif + int pageStep = 1; m_tabPage[m_curTab] += pageStep; if(m_tabPage[m_curTab] >= specs[m_curTab]->getPageCount()) { @@ -1119,13 +1113,7 @@ void IUIScene_CreativeMenu::handleAdditionalKeyPress(int iAction) break; case ACTION_MENU_OTHER_STICK_UP: { - int pageStep = TabSpec::rows; -#ifdef _WINDOWS64 - if (g_KBMInput.WasMouseWheelConsumed()) - { - pageStep = 1; - } -#endif + int pageStep = 1; m_tabPage[m_curTab] -= pageStep; if(m_tabPage[m_curTab] < 0) { diff --git a/Minecraft.Client/Common/UI/UIControl.cpp b/Minecraft.Client/Common/UI/UIControl.cpp index be267ada..3d853ac4 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 29770df2..8445af0f 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; @@ -62,10 +64,8 @@ public: virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); void UpdateControl(); -#ifdef __PSVITA__ void setHidden(bool bHidden) {m_bHidden=bHidden;} bool getHidden(void) {return m_bHidden;} -#endif IggyValuePath *getIggyValuePath(); diff --git a/Minecraft.Client/Common/UI/UIControl_ButtonList.cpp b/Minecraft.Client/Common/UI/UIControl_ButtonList.cpp index 68a3d655..4d60a477 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 44484ac3..666b1f0a 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 dc7bc532..8e679b7c 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) @@ -16,6 +25,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 +91,197 @@ 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) +{ + if (!m_parentScene || !m_parentScene->getMovie()) + return; + + // 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")) + { + IggyValueSetBooleanRS(&caretPath, m_nameVisible, NULL, visible); + } +} + +void UIControl_TextInput::setCaretIndex(int index) +{ + if (!m_parentScene || !m_parentScene->getMovie()) + return; + + 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 ); +} + +#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(), true); + 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(), true); + setCaretVisible(false); + } +} + +void UIControl_TextInput::confirmDirectEdit() +{ + if (m_bDirectEditing) + { + m_bDirectEditing = false; + setLabel(m_editBuffer.c_str(), true); + setCaretVisible(false); + } +} + +#endif diff --git a/Minecraft.Client/Common/UI/UIControl_TextInput.h b/Minecraft.Client/Common/UI/UIControl_TextInput.h index 98032d85..3ff28930 100644 --- a/Minecraft.Client/Common/UI/UIControl_TextInput.h +++ b/Minecraft.Client/Common/UI/UIControl_TextInput.h @@ -6,7 +6,20 @@ class UIControl_TextInput : public UIControl_Base { 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(); @@ -19,4 +32,24 @@ public: virtual void setFocus(bool focus); void SetCharLimit(int iLimit); + + 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 95423642..9109b85f 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -3,6 +3,7 @@ #include "UI.h" #include "UIScene.h" #include "UIControl_Slider.h" +#include "UIControl_TexturePackList.h" #include "..\..\..\Minecraft.World\StringHelpers.h" #include "..\..\LocalPlayer.h" #include "..\..\DLCTexturePack.h" @@ -237,6 +238,8 @@ UIController::UIController() m_winUserIndex = 0; m_mouseDraggingSliderScene = eUIScene_COUNT; m_mouseDraggingSliderId = -1; + m_mouseClickConsumedByScene = false; + m_bMouseHoverHorizontalList = false; m_lastHoverMouseX = -1; m_lastHoverMouseY = -1; m_accumulatedTicks = 0; @@ -784,40 +787,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 +825,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 != 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 +853,110 @@ 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) + { + m_bMouseHoverHorizontalList = false; + vector *controls = pScene->GetControls(); + if (controls) + { + int hitControlId = -1; + S32 hitArea = INT_MAX; + UIControl *hitCtrl = NULL; + for (size_t i = 0; i < controls->size(); ++i) + { + UIControl *ctrl = (*controls)[i]; + if (!ctrl || ctrl->getHidden() || !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 && type != UIControl::eTexturePackList) + 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(); + // TexturePackList origin is where the slot area starts, + // not the top-left of the whole control — use GetRealHeight. + if (type == UIControl::eTexturePackList) + ch = ((UIControl_TexturePackList *)ctrl)->GetRealHeight(); + 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; + hitArea = INT_MAX; + hitCtrl = NULL; + break; // ButtonList takes priority + } + if (type == UIControl::eTexturePackList) + { + // TexturePackList expects coords relative to its origin. + UIControl_TexturePackList *pList = (UIControl_TexturePackList *)ctrl; + pScene->SetFocusToElement(ctrl->getId()); + pList->SetTouchFocus( + (S32)(sceneMouseX - cx), (S32)(sceneMouseY - cy), false); + m_bMouseHoverHorizontalList = true; + hitControlId = -1; + hitArea = INT_MAX; + hitCtrl = NULL; + break; + } + S32 area = cw * ch; + if (area < hitArea) + { + hitControlId = ctrl->getId(); + hitArea = area; + hitCtrl = ctrl; + if (type == UIControl::eSlider) + m_bMouseHoverHorizontalList = true; + } + } + } + + if (hitControlId >= 0 && pScene->getControlFocus() != hitControlId) + { + // During direct editing, don't let hover move focus + // away to other TextInputs (e.g. sign lines). + if (hitCtrl && hitCtrl->getControlType() == UIControl::eTextInput + && pScene->isDirectEditBlocking()) + { + // Skip — keep focus on the actively-edited input + } + else + { + pScene->SetFocusToElement(hitControlId); + // TextInput: SetFocusToElement triggers ChangeState which + // shows the caret. Hide it immediately — the render pass + // happens after both tickInput and scene tick, so no flicker. + if (hitCtrl && hitCtrl->getControlType() == UIControl::eTextInput) + { + ((UIControl_TextInput *)hitCtrl)->setCaretVisible(false); + } + } + } + } + } + bool leftPressed = g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT); bool leftDown = leftPressed || g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT); @@ -890,12 +971,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 +1062,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 +1332,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; } @@ -1214,6 +1340,14 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key) } } + // Right click → ACTION_MENU_X (pick up half stack in inventory) + if (key == ACTION_MENU_X && !g_KBMInput.IsMouseGrabbed()) + { + if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_RIGHT)) { pressed = true; down = true; } + if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_RIGHT)) { released = true; down = false; } + if (!pressed && !released && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT)) { down = true; } + } + // Scroll wheel for list scrolling — only consume the wheel value when the // action key actually matches, so the other direction isn't lost. if (!g_KBMInput.IsMouseGrabbed() && (key == ACTION_MENU_OTHER_STICK_UP || key == ACTION_MENU_OTHER_STICK_DOWN)) @@ -1231,6 +1365,16 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key) pressed = true; down = true; } + + // Remap scroll wheel to navigation actions. Use LEFT/RIGHT when + // hovering a horizontal list (e.g. TexturePackList), UP/DOWN otherwise. + if (pressed && g_KBMInput.IsKBMActive()) + { + if (m_bMouseHoverHorizontalList) + key = (key == ACTION_MENU_OTHER_STICK_UP) ? ACTION_MENU_LEFT : ACTION_MENU_RIGHT; + else + 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 f4b365b5..d362c4c1 100644 --- a/Minecraft.Client/Common/UI/UIController.h +++ b/Minecraft.Client/Common/UI/UIController.h @@ -164,6 +164,8 @@ private: unsigned int m_winUserIndex; EUIScene m_mouseDraggingSliderScene; int m_mouseDraggingSliderId; + bool m_mouseClickConsumedByScene; + bool m_bMouseHoverHorizontalList; 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 6ef6d0e8..061f9832 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; @@ -447,6 +447,19 @@ void UIScene::tick() IggyPlayerTickRS( swf ); m_hasTickedOnce = true; } + +#ifdef _WINDOWS64 + { + vector inputs; + getDirectEditInputs(inputs); + for (size_t i = 0; i < inputs.size(); i++) + { + UIControl_TextInput::EDirectEditResult result = inputs[i]->tickDirectEdit(); + if (result != UIControl_TextInput::eDirectEdit_Continue) + onDirectEditFinished(inputs[i], result); + } + } +#endif } UIControl* UIScene::GetMainPanel() @@ -454,6 +467,113 @@ UIControl* UIScene::GetMainPanel() return NULL; } +#ifdef _WINDOWS64 +bool UIScene::isDirectEditBlocking() +{ + vector inputs; + getDirectEditInputs(inputs); + for (size_t i = 0; i < inputs.size(); i++) + { + if (inputs[i]->isDirectEditing() || inputs[i]->getDirectEditCooldown() > 0) + return true; + } + return false; +} + +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(); + } + + // Click-outside-to-deselect: confirm any active direct edit if + // the click landed outside the editing text input. + { + vector deInputs; + getDirectEditInputs(deInputs); + for (size_t i = 0; i < deInputs.size(); i++) + { + if (!deInputs[i]->isDirectEditing()) + continue; + deInputs[i]->UpdateControl(); + S32 cx = deInputs[i]->getXPos() + panelOffsetX; + S32 cy = deInputs[i]->getYPos() + panelOffsetY; + S32 cw = deInputs[i]->getWidth(); + S32 ch = deInputs[i]->getHeight(); + if (!(cw > 0 && ch > 0 && x >= cx && x <= cx + cw && y >= cy && y <= cy + ch)) + { + deInputs[i]->confirmDirectEdit(); + onDirectEditFinished(deInputs[i], UIControl_TextInput::eDirectEdit_Confirmed); + } + } + } + + vector *controls = GetControls(); + if (!controls) return false; + + // Hit-test controls and pick the smallest-area match to handle + // overlapping Flash bounds correctly without sacrificing precision. + int bestId = -1; + S32 bestArea = INT_MAX; + UIControl *bestCtrl = NULL; + + for (size_t i = 0; i < controls->size(); ++i) + { + UIControl *ctrl = (*controls)[i]; + if (!ctrl || ctrl->getHidden() || !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) + { + S32 area = cw * ch; + if (area < bestArea) + { + bestArea = area; + 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) { @@ -534,12 +654,13 @@ void UIScene::removeControl( UIControl_Base *control, bool centreScene) // update the button positions since they may have changed UpdateSceneControls(); - // mark the button as removed - control->setHidden(true); // remove it from the touchboxes ui.TouchBoxRebuild(control->getParentScene()); #endif + // mark the button as removed so hover/touch hit-tests skip it + control->setHidden(true); + } void UIScene::slideLeft() @@ -900,6 +1021,25 @@ void UIScene::sendInputToMovie(int key, bool repeat, bool pressed, bool released app.DebugPrintf("UI WARNING: Ignoring input as game action does not translate to an Iggy keycode\n"); return; } + +#ifdef _WINDOWS64 + // If a navigation key is pressed with no focused element, focus the first + // available one so arrow keys work even when the mouse is over empty space. + if(pressed && (iggyKeyCode == IGGY_KEYCODE_UP || iggyKeyCode == IGGY_KEYCODE_DOWN || + iggyKeyCode == IGGY_KEYCODE_LEFT || iggyKeyCode == IGGY_KEYCODE_RIGHT)) + { + IggyFocusHandle currentFocus = IGGY_FOCUS_NULL; + IggyFocusableObject focusables[64]; + S32 numFocusables = 0; + IggyPlayerGetFocusableObjects(swf, ¤tFocus, focusables, 64, &numFocusables); + if(currentFocus == IGGY_FOCUS_NULL && numFocusables > 0) + { + IggyPlayerSetFocusRS(swf, focusables[0].object, 0); + return; + } + } +#endif + IggyEvent keyEvent; // 4J Stu - Keyloc is always standard as we don't care about shift/alt IggyMakeEventKey( &keyEvent, pressed?IGGY_KEYEVENT_Down:IGGY_KEYEVENT_Up, (IggyKeycode)iggyKeyCode, IGGY_KEYLOC_Standard ); diff --git a/Minecraft.Client/Common/UI/UIScene.h b/Minecraft.Client/Common/UI/UIScene.h index b4008fa0..8fb4983b 100644 --- a/Minecraft.Client/Common/UI/UIScene.h +++ b/Minecraft.Client/Common/UI/UIScene.h @@ -6,6 +6,7 @@ using namespace std; #include "UIEnums.h" #include "UIControl_Base.h" +#include "UIControl_TextInput.h" class ItemRenderer; class UILayer; @@ -16,22 +17,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 +146,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 +184,19 @@ public: // returns main panel if controls are not living in the root virtual UIControl* GetMainPanel(); +#ifdef _WINDOWS64 + // Direct edit support: scenes override to register their text inputs. + // Base class handles tickDirectEdit in tick(), click-outside-to-deselect + // in handleMouseClick(), and provides isDirectEditBlocking() for guards. + virtual void getDirectEditInputs(vector &inputs) {} + virtual void onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result) {} + bool isDirectEditBlocking(); + + // Mouse click dispatch. Hit-tests C++ controls and picks the smallest-area + // match, then calls 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 c810ad45..4d43a638 100644 --- a/Minecraft.Client/Common/UI/UIScene_AnvilMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_AnvilMenu.cpp @@ -96,6 +96,19 @@ void UIScene_AnvilMenu::tick() { UIScene_AbstractContainerMenu::tick(); +#ifdef _WINDOWS64 + // Live update: sync item name per-keystroke while editing (like Java edition) + if (m_textInputAnvil.isDirectEditing()) + { + const wstring& buf = m_textInputAnvil.getEditBuffer(); + if (buf != m_itemName) + { + m_itemName = buf; + updateItemName(); + } + } +#endif + handleTick(); } @@ -306,26 +319,67 @@ UIControl *UIScene_AnvilMenu::getSection(ESceneSection eSection) return control; } +#ifdef _WINDOWS64 +void UIScene_AnvilMenu::getDirectEditInputs(vector &inputs) +{ + inputs.push_back(&m_textInputAnvil); +} + +void UIScene_AnvilMenu::onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result) +{ + m_itemName = input->getEditBuffer(); + updateItemName(); +} +#endif + 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 (isDirectEditBlocking()) + 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 +391,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 +411,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 +431,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_AnvilMenu.h b/Minecraft.Client/Common/UI/UIScene_AnvilMenu.h index 3afc6333..44f75992 100644 --- a/Minecraft.Client/Common/UI/UIScene_AnvilMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_AnvilMenu.h @@ -55,6 +55,10 @@ protected: virtual UIControl *getSection(ESceneSection eSection); +#ifdef _WINDOWS64 + virtual void getDirectEditInputs(vector &inputs); + virtual void onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result); +#endif static int KeyboardCompleteCallback(LPVOID lpParam,bool bRes); virtual void handleEditNamePressed(); virtual void setEditNameValue(const wstring &name); diff --git a/Minecraft.Client/Common/UI/UIScene_CraftingMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.cpp index 66d8c41e..2d1b4302 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; @@ -254,12 +262,14 @@ wstring UIScene_CraftingMenu::getMoviePath() } } -#ifdef __PSVITA__ +#if defined(__PSVITA__) || defined(_WINDOWS64) UIControl* UIScene_CraftingMenu::GetMainPanel() { return &m_controlMainPanel; } +#endif +#ifdef __PSVITA__ void UIScene_CraftingMenu::handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased) { // perform action on release @@ -383,6 +393,85 @@ void UIScene_CraftingMenu::handleTimerComplete(int id) } #endif +#ifdef _WINDOWS64 +bool UIScene_CraftingMenu::handleMouseClick(F32 x, F32 y) +{ + if (!m_hSlotBoundsValid || m_hSlotSpacing <= 0) + return false; + + // Tab click — tabs sit directly above the H slot row. We derive their + // bounds from the H slot positions cached in customDraw, since the Vita + // TouchPanel controls are full-screen overlays with unusable bounds. + int maxTabs = (m_iContainerType == RECIPE_TYPE_3x3) ? m_iMaxGroup3x3 : m_iMaxGroup2x2; + F32 slotHeight = m_hSlotY1 - m_hSlotY0; + F32 tabRowY0 = (m_hSlotY0 * 0.75f) - slotHeight * 1.55f; + F32 tabRowY1 = tabRowY0 + (slotHeight * 1.7f); + F32 tabRowWidth = m_hSlotSpacing * m_iCraftablesMaxHSlotC; + F32 tabWidth = tabRowWidth / maxTabs; + + if (tabWidth > 0 && x >= m_hSlotX0 && x < m_hSlotX0 + tabRowWidth && + y >= tabRowY0 && y < tabRowY1) + { + int iTab = (int)((x - m_hSlotX0) / tabWidth); + if (iTab >= 0 && iTab < maxTabs && iTab != m_iGroupIndex) + { + showTabHighlight(m_iGroupIndex, false); + m_iGroupIndex = iTab; + showTabHighlight(m_iGroupIndex, true); + m_iCurrentSlotHIndex = 0; + m_iCurrentSlotVIndex = 1; + CheckRecipesAvailable(); + iVSlotIndexA[0] = CanBeMadeA[m_iCurrentSlotHIndex].iCount - 1; + iVSlotIndexA[1] = 0; + iVSlotIndexA[2] = 1; + ui.PlayUISFX(eSFX_Focus); + UpdateVerticalSlots(); + UpdateHighlight(); + setGroupText(GetGroupNameText(m_pGroupA[m_iGroupIndex])); + } + return true; + } + + // H slot click — select or craft + F32 rowWidth = m_hSlotSpacing * m_iCraftablesMaxHSlotC; + if (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) + { + // Only interact with populated slots + if (CanBeMadeA[iNewSlot].iCount == 0) + return true; + + if (iNewSlot == m_iCurrentSlotHIndex) + { + // Click on already-selected slot — craft the item + handleKeyDown(m_iPad, ACTION_MENU_A, false); + } + else + { + 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; + } + } + // Consume all mouse clicks so misses don't generate ACTION_MENU_A + // and accidentally craft. Only blocks mouse-originated presses. + return true; +} +#endif + void UIScene_CraftingMenu::handleReload() { m_slotListInventory.addSlots(CRAFTING_INVENTORY_SLOT_START,CRAFTING_INVENTORY_SLOT_END - CRAFTING_INVENTORY_SLOT_START); @@ -478,6 +567,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 84c9ba65..2f52b8ba 100644 --- a/Minecraft.Client/Common/UI/UIScene_CraftingMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.h @@ -66,10 +66,19 @@ public: #ifdef __PSVITA__ virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased); - virtual UIControl* GetMainPanel(); virtual void handleTouchBoxRebuild(); virtual void handleTimerComplete(int id); #endif +#if defined(__PSVITA__) || defined(_WINDOWS64) + virtual UIControl* GetMainPanel(); +#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; @@ -97,7 +106,7 @@ protected: ETouchInput_TouchPanel_5, ETouchInput_TouchPanel_6, ETouchInput_CraftingHSlots, - + ETouchInput_Count, }; UIControl_Touch m_TouchInput[ETouchInput_Count]; diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp index 2bd06bc6..a9cd9853 100644 --- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp @@ -84,10 +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; -#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. @@ -293,53 +289,6 @@ void UIScene_CreateWorldMenu::tick() { UIScene::tick(); -#ifdef _WINDOWS64 - if (m_iDirectEditCooldown > 0) - m_iDirectEditCooldown--; - - if (m_bDirectEditing) - { - wchar_t ch; - bool changed = false; - while (g_KBMInput.ConsumeChar(ch)) - { - if (ch == 0x08) // backspace - { - if (!m_worldName.empty()) - { - m_worldName.pop_back(); - 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()); - } - else if ((int)m_worldName.length() < 25) - { - m_worldName += ch; - 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_buttonCreateWorld.setEnable(!m_worldName.empty()); - } - else if (changed) - { - m_editWorldName.setLabel(m_worldName.c_str()); - m_buttonCreateWorld.setEnable(!m_worldName.empty()); - } - } -#endif if(m_iSetTexturePackDescription >= 0 ) { @@ -403,11 +352,24 @@ int UIScene_CreateWorldMenu::ContinueOffline(void *pParam,int iPad,C4JStorage::E #endif +#ifdef _WINDOWS64 +void UIScene_CreateWorldMenu::getDirectEditInputs(vector &inputs) +{ + inputs.push_back(&m_editWorldName); +} + +void UIScene_CreateWorldMenu::onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result) +{ + m_worldName = input->getEditBuffer(); + m_buttonCreateWorld.setEnable(!m_worldName.empty()); +} +#endif + void UIScene_CreateWorldMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) { if(m_bIgnoreInput) return; #ifdef _WINDOWS64 - if (m_bDirectEditing || m_iDirectEditCooldown > 0) { handled = true; return; } + if (isDirectEditBlocking()) { handled = true; return; } #endif ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); @@ -464,7 +426,7 @@ void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId) { if(m_bIgnoreInput) return; #ifdef _WINDOWS64 - if (m_bDirectEditing || m_iDirectEditCooldown > 0) return; + if (isDirectEditBlocking()) return; #endif //CD - Added for audio @@ -476,7 +438,7 @@ void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId) { m_bIgnoreInput=true; #ifdef _WINDOWS64 - if (Win64_IsControllerConnected()) + if (!g_KBMInput.IsKBMActive()) { UIKeyboardInitData kbData; kbData.title = app.GetString(IDS_CREATE_NEW_WORLD); @@ -488,11 +450,8 @@ void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId) } else { - // PC without controller: 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; - g_KBMInput.ClearCharBuffer(); + 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 13f38a3b..75bfe602 100644 --- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h @@ -51,11 +51,6 @@ private: DLCPack * m_pDLCPack; bool m_bRebuildTouchBoxes; -#ifdef _WINDOWS64 - bool m_bDirectEditing; - wstring m_worldNameBeforeEdit; - int m_iDirectEditCooldown; -#endif public: UIScene_CreateWorldMenu(int iPad, void *initData, UILayer *parentLayer); @@ -83,6 +78,10 @@ protected: public: // INPUT virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); +#ifdef _WINDOWS64 + virtual void getDirectEditInputs(vector &inputs); + virtual void onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result); +#endif private: void StartSharedLaunchFlow(); diff --git a/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp index 2a8ac9f8..d3da0870 100644 --- a/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp +++ b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp @@ -41,8 +41,72 @@ 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 +void UIScene_DebugCreateSchematic::getDirectEditInputs(vector &inputs) +{ + inputs.push_back(&m_textInputName); + inputs.push_back(&m_textInputStartX); + inputs.push_back(&m_textInputStartY); + inputs.push_back(&m_textInputStartZ); + inputs.push_back(&m_textInputEndX); + inputs.push_back(&m_textInputEndY); + inputs.push_back(&m_textInputEndZ); +} + +void UIScene_DebugCreateSchematic::onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result) +{ + wstring value = input->getEditBuffer(); + int iVal = 0; + if (!value.empty()) + iVal = _fromString(value); + + if (input == &m_textInputName) + { + if (!value.empty()) + swprintf(m_data->name, 64, L"%ls", value.c_str()); + else + swprintf(m_data->name, 64, L"schematic"); + } + else if (input == &m_textInputStartX) m_data->startX = iVal; + else if (input == &m_textInputStartY) m_data->startY = iVal; + else if (input == &m_textInputStartZ) m_data->startZ = iVal; + else if (input == &m_textInputEndX) m_data->endX = iVal; + else if (input == &m_textInputEndY) m_data->endY = iVal; + else if (input == &m_textInputEndZ) m_data->endZ = iVal; +} + +bool UIScene_DebugCreateSchematic::handleMouseClick(F32 x, F32 y) +{ + UIScene::handleMouseClick(x, y); + return true; // always consume to prevent Iggy re-entry on empty space +} +#endif + +void UIScene_DebugCreateSchematic::tick() +{ + UIScene::tick(); +} + void UIScene_DebugCreateSchematic::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) { +#ifdef _WINDOWS64 + if (isDirectEditBlocking()) return; +#endif ui.AnimateKeyPress(iPad, key, repeat, pressed, released); switch(key) @@ -67,6 +131,9 @@ void UIScene_DebugCreateSchematic::handleInput(int iPad, int key, bool repeat, b void UIScene_DebugCreateSchematic::handlePress(F64 controlId, F64 childId) { +#ifdef _WINDOWS64 + if (isDirectEditBlocking()) return; +#endif switch((int)controlId) { case eControl_Create: @@ -112,8 +179,28 @@ 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()) + { + UIControl_TextInput* input = getTextInputForControl(m_keyboardCallbackControl); + 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 +225,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 cbfe785d..e18d9f5d 100644 --- a/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.h +++ b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.h @@ -24,6 +24,7 @@ private: ConsoleSchematicFile::XboxSchematicInitParam *m_data; + public: UIScene_DebugCreateSchematic(int iPad, void *initData, UILayer *parentLayer); @@ -58,8 +59,14 @@ protected: UI_END_MAP_ELEMENTS_AND_NAMES() virtual wstring getMoviePath(); +#ifdef _WINDOWS64 + virtual void getDirectEditInputs(vector &inputs); + virtual void onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result); + 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 +75,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 dd5a429f..62ee60b1 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,7 @@ 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)"); + } wstring UIScene_DebugSetCamera::getMoviePath() @@ -62,8 +63,59 @@ 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; + } +} + +void UIScene_DebugSetCamera::getDirectEditInputs(vector &inputs) +{ + inputs.push_back(&m_textInputX); + inputs.push_back(&m_textInputY); + inputs.push_back(&m_textInputZ); + inputs.push_back(&m_textInputYRot); + inputs.push_back(&m_textInputElevation); +} + +void UIScene_DebugSetCamera::onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result) +{ + wstring value = input->getEditBuffer(); + double val = 0; + if (!value.empty()) val = _fromString(value); + + if (input == &m_textInputX) currentPosition->m_camX = val; + else if (input == &m_textInputY) currentPosition->m_camY = val; + else if (input == &m_textInputZ) currentPosition->m_camZ = val; + else if (input == &m_textInputYRot) currentPosition->m_yRot = val; + else if (input == &m_textInputElevation) currentPosition->m_elev = val; +} + +bool UIScene_DebugSetCamera::handleMouseClick(F32 x, F32 y) +{ + UIScene::handleMouseClick(x, y); + return true; // always consume to prevent Iggy re-entry on empty space +} +#endif + +void UIScene_DebugSetCamera::tick() +{ + UIScene::tick(); +} + void UIScene_DebugSetCamera::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) { +#ifdef _WINDOWS64 + if (isDirectEditBlocking()) { handled = true; return; } +#endif ui.AnimateKeyPress(iPad, key, repeat, pressed, released); switch(key) @@ -88,11 +140,14 @@ void UIScene_DebugSetCamera::handleInput(int iPad, int key, bool repeat, bool pr void UIScene_DebugSetCamera::handlePress(F64 controlId, F64 childId) { +#ifdef _WINDOWS64 + if (isDirectEditBlocking()) return; +#endif switch((int)controlId) { case eControl_Teleport: app.SetXuiServerAction( ProfileManager.GetPrimaryPad(), - eXuiServerAction_SetCameraLocation, + eXuiServerAction_SetCameraLocation, (void *)currentPosition); break; case eControl_CamX: @@ -100,8 +155,26 @@ 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()) + { + UIControl_TextInput* input = getTextInputForControl(m_keyboardCallbackControl); + 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 +192,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 38db1258..d758e049 100644 --- a/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.h +++ b/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.h @@ -26,6 +26,9 @@ private: FreezePlayerParam *fpp; eControls m_keyboardCallbackControl; +#ifdef _WINDOWS64 + UIControl_TextInput* getTextInputForControl(eControls ctrl); +#endif public: UIScene_DebugSetCamera(int iPad, void *initData, UILayer *parentLayer); @@ -54,6 +57,12 @@ protected: UI_END_MAP_ELEMENTS_AND_NAMES() virtual wstring getMoviePath(); + virtual void tick(); +#ifdef _WINDOWS64 + virtual void getDirectEditInputs(vector &inputs); + virtual void onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result); + virtual bool handleMouseClick(F32 x, F32 y); +#endif public: // INPUT diff --git a/Minecraft.Client/Common/UI/UIScene_Keyboard.cpp b/Minecraft.Client/Common/UI/UIScene_Keyboard.cpp index 9e46a42e..b28564c3 100644 --- a/Minecraft.Client/Common/UI/UIScene_Keyboard.cpp +++ b/Minecraft.Client/Common/UI/UIScene_Keyboard.cpp @@ -163,6 +163,12 @@ void UIScene_Keyboard::tick() { UIScene::tick(); + // Sync our buffer from Flash so we pick up changes made via controller/on-screen buttons. + // Without this, switching between controller and keyboard would use stale text. + const wchar_t* flashText = m_KeyboardTextInput.getLabel(); + if (flashText) + m_win64TextBuffer = flashText; + // Accumulate physical keyboard chars into our own buffer, then push to Flash via setLabel. // This bypasses Iggy's focus system (char events only route to the focused element). // The char buffer is cleared on open so Enter/clicks from the triggering action don't leak in. diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp index d6f89832..96dd744e 100644 --- a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp @@ -257,6 +257,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 (isDirectEditBlocking()) 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 +337,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 +551,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,18 +570,52 @@ 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; } +#ifdef _WINDOWS64 +void UIScene_LaunchMoreOptionsMenu::getDirectEditInputs(vector &inputs) +{ + inputs.push_back(&m_editSeed); +} + +void UIScene_LaunchMoreOptionsMenu::onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result) +{ + if (result == UIControl_TextInput::eDirectEdit_Confirmed) + m_params->seed = input->getEditBuffer(); +} +#endif + void UIScene_LaunchMoreOptionsMenu::handlePress(F64 controlId, F64 childId) { if(m_bIgnoreInput) return; +#ifdef _WINDOWS64 + if (isDirectEditBlocking()) 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 +627,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 367db10d..caf9a71f 100644 --- a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h @@ -140,6 +140,10 @@ protected: public: virtual void tick(); virtual void handleDestroy(); +#ifdef _WINDOWS64 + virtual void getDirectEditInputs(vector &inputs); + virtual void onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result); +#endif // INPUT virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); virtual void handleFocusChange(F64 controlId, F64 childId); @@ -160,6 +164,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_LoadOrJoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp index 8664aca1..f8f9dcae 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp @@ -1168,6 +1168,43 @@ void UIScene_LoadOrJoinMenu::handleInput(int iPad, int key, bool repeat, bool pr LaunchSaveTransfer(); } } +#endif +#ifdef _WINDOWS64 + // Right click on a save opens save options (same as RB / ACTION_MENU_RIGHT_SCROLL) + if(pressed && !repeat && ProfileManager.IsFullVersion() && !StorageManager.GetSaveDisabled()) + { + if(DoesSavesListHaveFocus() && (m_iDefaultButtonsC > 0) && (m_iSaveListIndex >= m_iDefaultButtonsC)) + { + m_bIgnoreInput = true; + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + UINT uiIDA[3]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_TITLE_RENAMESAVE; + uiIDA[2]=IDS_TOOLTIPS_DELETESAVE; + ui.RequestAlertMessage(IDS_TOOLTIPS_SAVEOPTIONS, IDS_TEXT_SAVEOPTIONS, uiIDA, 3, iPad,&UIScene_LoadOrJoinMenu::SaveOptionsDialogReturned,this); + } + else + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestAlertMessage(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, iPad,&UIScene_LoadOrJoinMenu::DeleteSaveDialogReturned,this); + } + ui.PlayUISFX(eSFX_Press); + } + else if(DoesMashUpWorldHaveFocus() && (m_iSaveListIndex != JOIN_LOAD_CREATE_BUTTON_INDEX)) + { + LevelGenerationOptions *levelGen = m_generators.at(m_iSaveListIndex - 1); + if(!levelGen->isTutorial() && levelGen->requiresTexturePack()) + { + m_bIgnoreInput = true; + app.HideMashupPackWorld(m_iPad, levelGen->getRequiredTexturePackId()); + m_iState = e_SavesRepopulateAfterMashupHide; + } + ui.PlayUISFX(eSFX_Press); + } + } #endif break; case ACTION_MENU_Y: @@ -2394,7 +2431,7 @@ int UIScene_LoadOrJoinMenu::SaveOptionsDialogReturned(void *pParam,int iPad,C4JS kbData.maxChars = 25; kbData.callback = &UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback; kbData.lpParam = pClass; - kbData.pcMode = !Win64_IsControllerConnected(); + kbData.pcMode = g_KBMInput.IsKBMActive(); ui.NavigateToScene(pClass->m_iPad, eUIScene_Keyboard, &kbData); } #elif defined _DURANGO diff --git a/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp index c29bac2d..9f049c8c 100644 --- a/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp @@ -18,6 +18,12 @@ UIScene_SignEntryMenu::UIScene_SignEntryMenu(int iPad, void *_initData, UILayer m_bConfirmed = false; m_bIgnoreInput = false; + m_iSignCursorFrame = 0; +#ifdef _WINDOWS64 + m_iActiveDirectEditLine = -1; + m_bNeedsInitialEdit = true; + m_bSkipTickNav = false; +#endif m_buttonConfirm.init(app.GetString(IDS_DONE), eControl_Confirm); m_labelMessage.init(app.GetString(IDS_EDIT_SIGN_MESSAGE)); @@ -53,6 +59,7 @@ UIScene_SignEntryMenu::UIScene_SignEntryMenu(int iPad, void *_initData, UILayer UIScene_SignEntryMenu::~UIScene_SignEntryMenu() { + m_sign->SetSelectedLine(-1); m_parentLayer->removeComponent(eUIComponent_MenuBackground); } @@ -77,6 +84,79 @@ void UIScene_SignEntryMenu::tick() { UIScene::tick(); +#ifdef _WINDOWS64 + // On first tick, auto-start editing line 1 if KBM is active (Java-style flow) + if (m_bNeedsInitialEdit) + { + m_bNeedsInitialEdit = false; + if (g_KBMInput.IsKBMActive()) + { + SetFocusToElement(eControl_Line1); + m_iActiveDirectEditLine = 0; + m_textInputLines[0].beginDirectEdit(15); + } + } + + // UP/DOWN navigation — must happen after tickDirectEdit (so typed chars are consumed) + // and before sign cursor update (so the cursor is correct for this frame's render) + // m_bSkipTickNav prevents double-processing when handleInput auto-started editing this frame + if (m_iActiveDirectEditLine >= 0 && !m_bSkipTickNav) + { + int navDir = 0; + if (g_KBMInput.IsKeyPressed(VK_DOWN)) navDir = 1; + else if (g_KBMInput.IsKeyPressed(VK_UP)) navDir = -1; + + if (navDir != 0) + { + int newLine = m_iActiveDirectEditLine + navDir; + if (newLine >= eControl_Line1 && newLine <= eControl_Line4) + { + m_textInputLines[m_iActiveDirectEditLine].confirmDirectEdit(); + SetFocusToElement(newLine); + m_iActiveDirectEditLine = newLine; + m_textInputLines[newLine].beginDirectEdit(15); + } + else if (navDir > 0) + { + m_textInputLines[m_iActiveDirectEditLine].confirmDirectEdit(); + SetFocusToElement(eControl_Confirm); + m_iActiveDirectEditLine = -1; + } + } + } + m_bSkipTickNav = false; + + if (m_iActiveDirectEditLine >= 0 && !m_textInputLines[m_iActiveDirectEditLine].isDirectEditing()) + m_iActiveDirectEditLine = -1; +#endif + + // Blinking > text < cursor on the 3D sign + m_iSignCursorFrame++; + if (m_iSignCursorFrame / 6 % 2 == 0) + { +#ifdef _WINDOWS64 + if (m_iActiveDirectEditLine >= 0) + m_sign->SetSelectedLine(m_iActiveDirectEditLine); + else +#endif + { + int focusedLine = -1; + for (int i = eControl_Line1; i <= eControl_Line4; i++) + { + if (controlHasFocus(i)) + { + focusedLine = i; + break; + } + } + m_sign->SetSelectedLine(focusedLine); + } + } + else + { + m_sign->SetSelectedLine(-1); + } + if(m_bConfirmed) { m_bConfirmed = false; @@ -107,6 +187,9 @@ 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 (isDirectEditBlocking()) { handled = true; return; } +#endif ui.AnimateKeyPress(iPad, key, repeat, pressed, released); @@ -132,31 +215,124 @@ void UIScene_SignEntryMenu::handleInput(int iPad, int key, bool repeat, bool pre #ifdef __ORBIS__ case ACTION_MENU_TOUCHPAD_PRESS: #endif + sendInputToMovie(key, repeat, pressed, released); + handled = true; + break; case ACTION_MENU_UP: case ACTION_MENU_DOWN: sendInputToMovie(key, repeat, pressed, released); +#ifdef _WINDOWS64 + // Auto-start editing if focus moved to a line (e.g. UP from Confirm) + if (g_KBMInput.IsKBMActive()) + { + for (int i = eControl_Line1; i <= eControl_Line4; i++) + { + if (controlHasFocus(i)) + { + m_iActiveDirectEditLine = i; + m_textInputLines[i].beginDirectEdit(15); + m_bSkipTickNav = true; + break; + } + } + } +#endif handled = true; break; } } +#ifdef _WINDOWS64 +void UIScene_SignEntryMenu::getDirectEditInputs(vector &inputs) +{ + for (int i = 0; i < 4; i++) + inputs.push_back(&m_textInputLines[i]); +} + +void UIScene_SignEntryMenu::onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result) +{ + int line = -1; + for (int i = 0; i < 4; i++) + { + if (input == &m_textInputLines[i]) { line = i; break; } + } + if (line != m_iActiveDirectEditLine) return; + + if (result == UIControl_TextInput::eDirectEdit_Confirmed) + { + int newLine = line + 1; + if (newLine <= eControl_Line4) + { + SetFocusToElement(newLine); + m_iActiveDirectEditLine = newLine; + m_textInputLines[newLine].beginDirectEdit(15); + } + else + { + m_iActiveDirectEditLine = -1; + m_bConfirmed = true; + } + } + else if (result == UIControl_TextInput::eDirectEdit_Cancelled) + { + m_iActiveDirectEditLine = -1; + wstring temp = L""; + for (int j = 0; j < 4; j++) + m_sign->SetMessage(j, temp); + navigateBack(); + ui.PlayUISFX(eSFX_Back); + } +} + +bool UIScene_SignEntryMenu::handleMouseClick(F32 x, F32 y) +{ + if (m_iActiveDirectEditLine >= 0) + { + // During direct edit, only the Done button is clickable. + // Hit-test it manually — all other clicks are consumed but ignored. + m_buttonConfirm.UpdateControl(); + S32 cx = m_buttonConfirm.getXPos(); + S32 cy = m_buttonConfirm.getYPos(); + S32 cw = m_buttonConfirm.getWidth(); + S32 ch = m_buttonConfirm.getHeight(); + if (cw > 0 && ch > 0 && x >= cx && x <= cx + cw && y >= cy && y <= cy + ch) + { + m_textInputLines[m_iActiveDirectEditLine].confirmDirectEdit(); + m_iActiveDirectEditLine = -1; + m_bConfirmed = true; + } + return true; + } + return UIScene::handleMouseClick(x, y); +} +#endif + 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 + if (isDirectEditBlocking()) return; +#endif switch((int)controlId) { case eControl_Confirm: @@ -170,6 +346,28 @@ void UIScene_SignEntryMenu::handlePress(F64 controlId, F64 childId) case eControl_Line4: { m_iEditingLine = (int)controlId; +#ifdef _WINDOWS64 + if (g_KBMInput.IsKBMActive()) + { + // Only start editing from keyboard (Enter on focused line), not mouse clicks + if (!g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) + { + 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 +385,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 28b37d53..4e1c2a5b 100644 --- a/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.h @@ -21,6 +21,12 @@ private: int m_iEditingLine; bool m_bConfirmed; bool m_bIgnoreInput; + int m_iSignCursorFrame; +#ifdef _WINDOWS64 + int m_iActiveDirectEditLine; + bool m_bNeedsInitialEdit; + bool m_bSkipTickNav; +#endif UIControl_Button m_buttonConfirm; UIControl_Label m_labelMessage; @@ -50,6 +56,11 @@ protected: public: // INPUT virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); +#ifdef _WINDOWS64 + virtual void getDirectEditInputs(vector &inputs); + virtual void onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result); + virtual bool handleMouseClick(F32 x, F32 y); +#endif protected: void handlePress(F64 controlId, F64 childId); diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 7ae206cf..ba82e919 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1499,7 +1499,7 @@ void Minecraft::run_middle() } // Utility keys always work regardless of KBM active state - if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_PAUSE) && !ui.IsTutorialVisible(i) && !ui.GetMenuDisplayed(i)) + if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_PAUSE) && !ui.GetMenuDisplayed(i)) { localplayers[i]->ullButtonsPressed|=1LL< currentHeapSize ) + { + unsigned int pagesRequired = ( desiredSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE; + void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE); + if( pvRet == NULL ) + { + __debugbreak(); + } + pagesCommitted = pagesRequired; + } + header.WriteHeader( pvSaveMem ); ReleaseSaveAccess(); }