From 5c8d583b31e51c898b8a2a48a3bddf2320333df0 Mon Sep 17 00:00:00 2001 From: shenlei Date: Wed, 30 Sep 2026 17:00:39 +0900 Subject: [PATCH] fix(touch): raise the soft keyboard only for a tap on the visible focused EditLine A chat line keeps the IME focus under a hidden board (interfacemodule __HideWindows for an NPC dialog, the focus handed back by UnlockWindow/DeactivateWindow), and IsPointInsideFocusedWindow only checked its own show flag and rect, so taps on the NPC dialog or the minimap buttons opened the keyboard. Require the whole parent chain to render and the picked window to be the focused one or its child. Co-Authored-By: Claude Opus 5.5 --- src/platform/ScriptLib/PythonBoot.cpp | 11 ++++++++++- src/platform/ScriptLib/PythonBoot.h | 2 +- tests/port/port_login_flow_test.cpp | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/platform/ScriptLib/PythonBoot.cpp b/src/platform/ScriptLib/PythonBoot.cpp index 864fd6dd..9e6a4631 100644 --- a/src/platform/ScriptLib/PythonBoot.cpp +++ b/src/platform/ScriptLib/PythonBoot.cpp @@ -612,7 +612,16 @@ bool IsPointInsideFocusedWindow(int x, int y) { if (!g_window_manager) return false; UI::CWindow* pWin = g_window_manager->GetActivateWindow(); - return pWin && pWin->IsShow() && pWin->IsIn(x - g_window_manager->GetScreenOffsetX(), y); + if (!pWin || !pWin->IsRendering()) + return false; + // The focus can outlive the EditLine's use: UnlockWindow after an NPC dialog and DeactivateWindow + // hand it back to a chat line whose board is hidden (capture on again), and the full-screen + // GameWindow can hold it. Only a tap that picks the focused window itself (or its child) is one on it. + for (UI::CWindow* pPicked = g_window_manager->PickWindow(x - g_window_manager->GetScreenOffsetX(), y); + pPicked; pPicked = pPicked->GetParent()) + if (pPicked == pWin) + return true; + return false; } bool IsPointInsideActiveUI(int x, int y) diff --git a/src/platform/ScriptLib/PythonBoot.h b/src/platform/ScriptLib/PythonBoot.h index 90e6097b..c2059677 100644 --- a/src/platform/ScriptLib/PythonBoot.h +++ b/src/platform/ScriptLib/PythonBoot.h @@ -101,7 +101,7 @@ void SetTouchInput(bool touch); bool TouchInput(); // An EditLine has focus (40250 ui.EditLine.OnSetFocus -> ime.EnableCaptureInput). bool TextInputFocused(); -// (x, y) hits the focused window, i.e. the tap landed on the focused EditLine. +// (x, y) picks the focused window, shown with all its parents: the tap landed on the focused EditLine. bool IsPointInsideFocusedWindow(int x, int y); struct PlayerStatusInfo { diff --git a/tests/port/port_login_flow_test.cpp b/tests/port/port_login_flow_test.cpp index a0a090fb..4ab02ef8 100644 --- a/tests/port/port_login_flow_test.cpp +++ b/tests/port/port_login_flow_test.cpp @@ -683,6 +683,27 @@ int main(int argc, char** argv) CHECK(pump_until(2, [&] { return server->Has("game2:chat 0 hello"); })); // uichat.__SendTalkingChatPacket only resets the line; a Return on the empty line closes it (eventReturn). CHECK(py_true("_stream.curPhaseWindow.interface.IsOpenChat() and __import__('ime').GetText(0) == ''")); + // Touch hosts raise the system keyboard only for a tap on the focused, visible EditLine. A chat line + // that keeps the focus under a hidden board (interfacemodule.__HideWindows for an NPC dialog, then the + // focus handed back by UnlockWindow/DeactivateWindow) must not, wherever the tap lands. + { + std::string xywh; + CHECK(py("_line = _stream.curPhaseWindow.interface.wndChat.chatInputSet.chatLine")); + CHECK(PythonBoot::Evaluate("'%d %d %d %d' % (_line.GetGlobalPosition() + (_line.GetWidth(), _line.GetHeight()))", + &xywh, nullptr)); + int x = 0, y = 0, w = 0, h = 0; + std::sscanf(xywh.c_str(), "%d %d %d %d", &x, &y, &w, &h); + const int cx = x + w / 2, cy = y + h / 2; + CHECK(w > 0 && h > 0); + CHECK(PythonBoot::TextInputFocused()); + CHECK(PythonBoot::IsPointInsideFocusedWindow(cx, cy)); + CHECK(!PythonBoot::IsPointInsideFocusedWindow(790, 10)); + CHECK(py("_stream.curPhaseWindow.interface.wndChat.Hide()")); + CHECK(PythonBoot::TextInputFocused()); + CHECK(!PythonBoot::IsPointInsideFocusedWindow(cx, cy)); + CHECK(py("_stream.curPhaseWindow.interface.wndChat.Show()")); + CHECK(PythonBoot::IsPointInsideFocusedWindow(cx, cy)); + } PythonBoot::UIChar(VK_RETURN); CHECK(pump_until(1, [] { return py_true("not _stream.curPhaseWindow.interface.IsOpenChat()"); })); }