This commit is contained in:
KKNecmi 2026-03-06 22:38:59 +03:00
commit 86102e7cd6
37 changed files with 1458 additions and 229 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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, &currentFocus, 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<UIControl *> *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<UIControl *> *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, &currentFocus, 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

View file

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

View file

@ -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<UIControl_TextInput*> 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<UIControl_TextInput*> 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<UIControl_TextInput*> 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<UIControl *> *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, &currentFocus, 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 );

View file

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

View file

@ -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<UIControl_TextInput*> &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];

View file

@ -55,6 +55,10 @@ protected:
virtual UIControl *getSection(ESceneSection eSection);
#ifdef _WINDOWS64
virtual void getDirectEditInputs(vector<UIControl_TextInput*> &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);

View file

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

View file

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

View file

@ -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<UIControl_TextInput*> &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);

View file

@ -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<UIControl_TextInput*> &inputs);
virtual void onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result);
#endif
private:
void StartSharedLaunchFlow();

View file

@ -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<UIControl_TextInput*> &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<int>(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)
{

View file

@ -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<UIControl_TextInput*> &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

View file

@ -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<UIControl_TextInput*> &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<double>(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)
{

View file

@ -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<UIControl_TextInput*> &inputs);
virtual void onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result);
virtual bool handleMouseClick(F32 x, F32 y);
#endif
public:
// INPUT

View file

@ -753,7 +753,7 @@ void UIScene_HUD::handleTimerComplete(int id)
float opacity = pGui->getOpacity(m_iPad, i);
if( opacity > 0 )
{
#ifdef _WINDOWS64
#if 0 // def _WINDOWS64 // Use Iggy chat until Gui::render has visual parity
// Chat drawn by Gui::render with color codes. Hides Iggy chat to avoid double chats.
m_controlLabelBackground[i].setOpacity(0);
m_labelChatText[i].setOpacity(0);

View file

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

View file

@ -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<UIControl_TextInput*> &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;

View file

@ -140,6 +140,10 @@ protected:
public:
virtual void tick();
virtual void handleDestroy();
#ifdef _WINDOWS64
virtual void getDirectEditInputs(vector<UIControl_TextInput*> &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
};

View file

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

View file

@ -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<UIControl_TextInput*> &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;

View file

@ -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<UIControl_TextInput*> &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);

View file

@ -146,6 +146,44 @@ void Font::renderStyleLine(float x0, float y0, float x1, float y1)
t->end();
}
void Font::addCharacterQuad(wchar_t c)
{
float xOff = c % m_cols * m_charWidth;
float yOff = c / m_cols * m_charWidth;
float width = charWidths[c] - .01f;
float height = m_charHeight - .01f;
float fontWidth = m_cols * m_charWidth;
float fontHeight = m_rows * m_charHeight;
const float shear = m_italic ? (height * 0.25f) : 0.0f;
float x0 = xPos, x1 = xPos + width + shear;
float y0 = yPos, y1 = yPos + height;
Tesselator *t = Tesselator::getInstance();
t->tex(xOff / fontWidth, (yOff + 7.99f) / fontHeight);
t->vertex(x0, y1, 0.0f);
t->tex((xOff + width) / fontWidth, (yOff + 7.99f) / fontHeight);
t->vertex(x1, y1, 0.0f);
t->tex((xOff + width) / fontWidth, yOff / fontHeight);
t->vertex(x1, y0, 0.0f);
t->tex(xOff / fontWidth, yOff / fontHeight);
t->vertex(x0, y0, 0.0f);
if (m_bold)
{
float dx = 1.0f;
t->tex(xOff / fontWidth, (yOff + 7.99f) / fontHeight);
t->vertex(x0 + dx, y1, 0.0f);
t->tex((xOff + width) / fontWidth, (yOff + 7.99f) / fontHeight);
t->vertex(x1 + dx, y1, 0.0f);
t->tex((xOff + width) / fontWidth, yOff / fontHeight);
t->vertex(x1 + dx, y0, 0.0f);
t->tex(xOff / fontWidth, yOff / fontHeight);
t->vertex(x0 + dx, y0, 0.0f);
}
xPos += static_cast<float>(charWidths[c]);
}
void Font::renderCharacter(wchar_t c)
{
float xOff = c % m_cols * m_charWidth;
@ -255,11 +293,13 @@ void Font::draw(const wstring &str, bool dropShadow)
{
// Bind the texture
textures->bindTexture(m_textureLocation);
bool noise = false;
m_bold = m_italic = m_underline = m_strikethrough = false;
wstring cleanStr = sanitize(str);
Tesselator *t = Tesselator::getInstance();
t->begin();
for (int i = 0; i < static_cast<int>(cleanStr.length()); ++i)
{
// Map character
@ -270,8 +310,10 @@ void Font::draw(const wstring &str, bool dropShadow)
wchar_t ca = cleanStr[i+1];
if (!isSectionFormatCode(ca))
{
t->end();
renderCharacter(167);
renderCharacter(ca);
t->begin();
i += 1;
continue;
}
@ -294,14 +336,10 @@ void Font::draw(const wstring &str, bool dropShadow)
{
noise = false;
if (colorN < 0 || colorN > 15) colorN = 15;
if (dropShadow) colorN += 16;
int color = colors[colorN];
glColor3f((color >> 16) / 255.0F, ((color >> 8) & 255) / 255.0F, (color & 255) / 255.0F);
}
i += 1;
continue;
}
@ -317,8 +355,10 @@ void Font::draw(const wstring &str, bool dropShadow)
c = newc;
}
renderCharacter(c);
addCharacterQuad(c);
}
t->end();
}
void Font::draw(const wstring& str, int x, int y, int color, bool dropShadow)

View file

@ -47,6 +47,7 @@ public:
private:
void renderCharacter(wchar_t c); // 4J added
void addCharacterQuad(wchar_t c);
void renderStyleLine(float x0, float y0, float x1, float y1); // solid line for underline/strikethrough
public:

View file

@ -971,7 +971,7 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_ALPHA_TEST);
#if defined(_WINDOWS64)
#if 0 // defined(_WINDOWS64) // Temporarily disable this chat in favor of iggy chat until we have better visual parity
glPushMatrix();
glTranslatef(0.0f, static_cast<float>(screenHeight - iSafezoneYHalf - iTooltipsYOffset - 16 - 3 + 22) - 24.0f, 0.0f);

View file

@ -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<<MINECRAFT_ACTION_PAUSEMENU;
app.DebugPrintf("PAUSE PRESSED (keyboard) - ipad = %d\n",i);
@ -1522,7 +1522,7 @@ void Minecraft::run_middle()
}
#endif
#ifndef _FINAL_BUILD
#if _DEBUG // ndef _FINAL_BUILD // Disable conflicting debug functionality in release builds
if( app.DebugSettingsOn() && app.GetUseDPadForDebug() )
{
localplayers[i]->ullDpad_last = 0;
@ -3651,7 +3651,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
if (player->missTime > 0) player->missTime--;
#ifdef _DEBUG_MENUS_ENABLED
#ifdef _DEBUG//_MENUS_ENABLED // disable DPad cheats on release builds
if(app.DebugSettingsOn())
{
#ifndef __PSVITA__

View file

@ -126,40 +126,16 @@ void KeyboardMouseInput::Tick()
}
}
#ifdef _WINDOWS64
if (!IsGameInFront())
{
ClipCursor(NULL);
while (ShowCursor(TRUE) < 0) {}
}
else if (m_cursorHiddenForUI)
{
ClipCursor(NULL);
}
else if (m_mouseGrabbed && g_hWnd)
{
ClipCursorToWindow(g_hWnd);
RECT rc;
GetClientRect(g_hWnd, &rc);
POINT center;
center.x = (rc.right - rc.left) / 2;
center.y = (rc.bottom - rc.top) / 2;
ClientToScreen(g_hWnd, &center);
SetCursorPos(center.x, center.y);
}
#else
if ((m_mouseGrabbed || m_cursorHiddenForUI) && g_hWnd)
{
RECT rc;
GetClientRect(g_hWnd, &rc);
POINT center;
center.x = (rc.right - rc.left) / 2;
center.y = (rc.bottom - rc.top) / 2;
ClientToScreen(g_hWnd, &center);
SetCursorPos(center.x, center.y);
}
#endif
if ((m_mouseGrabbed || m_cursorHiddenForUI) && m_windowFocused && g_hWnd)
{
RECT rc;
GetClientRect(g_hWnd, &rc);
POINT center;
center.x = (rc.right - rc.left) / 2;
center.y = (rc.bottom - rc.top) / 2;
ClientToScreen(g_hWnd, &center);
SetCursorPos(center.x, center.y);
}
}
void KeyboardMouseInput::OnKeyDown(int vkCode)

View file

@ -604,7 +604,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
else if (vk == VK_MENU)
vk = (lParam & (1 << 24)) ? VK_RMENU : VK_LMENU;
g_KBMInput.OnKeyDown(vk);
break;
return DefWindowProc(hWnd, message, wParam, lParam);
}
case WM_KEYUP:
case WM_SYSKEYUP:

View file

@ -463,6 +463,23 @@ BOOL ConsoleSaveFileOriginal::closeHandle( FileEntry *file )
void ConsoleSaveFileOriginal::finalizeWrite()
{
LockSaveAccess();
// Ensure buffer is large enough for the full file including header table.
// New file entries (e.g. from RegionFile creation) increase GetFileSize()
// without triggering MoveDataBeyond, so the committed pages may be short.
DWORD currentHeapSize = pagesCommitted * CSF_PAGE_SIZE;
DWORD desiredSize = header.GetFileSize();
if( desiredSize > 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();
}

View file

@ -8,7 +8,8 @@
This project contains the source code of Minecraft Legacy Console Edition v1.6.0560.0 (TU19) from https://archive.org/details/minecraft-legacy-console-edition-source-code, with some fixes and improvements applied.
[Nightly Build](https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly)
## Download
Windows users can download our [Nightly Build](https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly)! Simply download the `.zip` file and extract it to a folder where you'd like to keep the game. You can set your username in `username.txt` (you'll have to make this file) and add servers to connect to in `servers.txt`
## Platform Support
@ -34,30 +35,42 @@ Basic LAN multiplayer is available on the Windows build
- Other players on the same LAN can discover the session from the in-game Join Game menu
- Game connections use TCP port `25565` by default
- LAN discovery uses UDP port `25566`
- Add servers to your server list with `servers.txt` (temp solution)
- Rename yourself without losing data by keeping your `uid.dat`
This feature is based on [LCEMP](https://github.com/LCEMP/LCEMP/)
Parts of this feature are based on code from [LCEMP](https://github.com/LCEMP/LCEMP) (thanks!)
### servers.txt
To add a server to your game, create the `servers.txt` file in the same directory as you have `Minecraft.Client.exe`. Inside, follow this format:
```
serverip.example.com
25565
The name of your server in UI!
```
For example, here's a valid servers.txt
```
1.1.1.1
25565
Cloudflare's Very Own LCE Server
127.0.0.1
25565
Localhost Test Crap
```
### Launch Arguments
| Argument | Description |
|--------------------|-----------------------------------------------------------------------------------------------------|
| `-name <username>` | Sets your in-game username. |
| `-server` | Launches a headless server instead of the client. |
| `-ip <address>` | Client mode: manually connect to an IP. Server mode: override the bind IP from `server.properties`. |
| `-port <port>` | Client mode: override the join port. Server mode: override the listen port from `server.properties`.|
| `-fullscreen` | Launches the game in Fullscreen mode |
Example:
```
Minecraft.Client.exe -name Steve -ip 192.168.0.25 -port 25565
Minecraft.Client.exe -name Steve -fullscreen
```
Headless server example:
```
Minecraft.Client.exe -server -ip 0.0.0.0 -port 25565
```
The headless server also reads and writes `server.properties` in the working directory. If `-ip` / `-port` are omitted in `-server` mode, it falls back to `server-ip` / `server-port` from that file. Dedicated-server host options such as `trust-players`, `pvp`, `fire-spreads`, `tnt`, `difficulty`, `gamemode`, `spawn-animals`, and `spawn-npcs` are persisted there as well.
## Controls (Keyboard & Mouse)
- **Movement**: `W` `A` `S` `D`
@ -65,6 +78,7 @@ The headless server also reads and writes `server.properties` in the working dir
- **Sneak / Fly (Down)**: `Shift` (Hold)
- **Sprint**: `Ctrl` (Hold) or Double-tap `W`
- **Inventory**: `E`
- **Chat**: `T`
- **Drop Item**: `Q`
- **Crafting**: `C` Use `Q` and `E` to move through tabs (cycles Left/Right)
- **Toggle View (FPS/TPS)**: `F5`