|
@@ -1,8 +1,49 @@
|
|
|
#include "StdAfx.h"
|
|
#include "StdAfx.h"
|
|
|
#include "UIEdit.h"
|
|
#include "UIEdit.h"
|
|
|
|
|
+#include "../Utils/UIDiag.h"
|
|
|
|
|
|
|
|
namespace DuiLib
|
|
namespace DuiLib
|
|
|
{
|
|
{
|
|
|
|
|
+ // ==================================================================
|
|
|
|
|
+ // 【诊断 · 新建商品弹框「输入文字消失」(Win10 触摸键盘)】
|
|
|
|
|
+ //
|
|
|
|
|
+ // 现象:手指点触摸键盘输入,已输入的字(尤其中文输入的组合串)会消失;
|
|
|
|
|
+ // 鼠标点同一个键盘则完全正常。
|
|
|
|
|
+ //
|
|
|
|
|
+ // 已确认的代码事实(不用日志就能确定的):
|
|
|
|
|
+ // · 文本**真实存放**在 CEditWnd —— 一个原生的 EDIT 子窗口里(WS_CHILD),
|
|
|
|
|
+ // DuiLib 只通过 EN_CHANGE 把内容同步回 CEditUI::m_sText;
|
|
|
|
|
+ // · 该窗口的生命周期**极短**:只要它失去键盘焦点,
|
|
|
|
|
+ // CEditWnd::OnKillFocus 就会 PostMessage(WM_CLOSE) **把它销毁**;
|
|
|
|
|
+ // · 所以"谁在这一刻抢走了焦点"就是本问题的全部答案。
|
|
|
|
|
+ // 一旦焦点离开 EDIT,输入法正在拼的字会被撤销(中文尤其明显),
|
|
|
|
|
+ // 之后到达的字符也无处可去 —— 用户看到的就是"输入完被自动删除"。
|
|
|
|
|
+ //
|
|
|
|
|
+ // 因此下面这几个 helper 只做一件事:把"抢焦点的是谁"记清楚。
|
|
|
|
|
+ // 判读要点:
|
|
|
|
|
+ // · 若 EDIT-KILLFOCUS 的 newTid != selfTid ⇒ 焦点被**别的进程/线程**抢走
|
|
|
|
|
+ // (触摸键盘 TabTip / TextInputHost 是首要嫌疑);
|
|
|
|
|
+ // · newClass 是 "#32770" ⇒ 被我们自己的弹框拿走了;
|
|
|
|
|
+ // · 若同一个 owner 出现 INIT → FIN → INIT 的抖动 ⇒ 窗口在反复重建,
|
|
|
|
|
+ // 重建时用的是 m_sText(可能落后于 IME 的组合状态)⇒ 字就"没了"。
|
|
|
|
|
+ // ==================================================================
|
|
|
|
|
+ static LPCTSTR DiagWndClass(HWND h)
|
|
|
|
|
+ {
|
|
|
|
|
+ static TCHAR s_cls[128];
|
|
|
|
|
+ s_cls[0] = _T('\0');
|
|
|
|
|
+ if (h != NULL && ::IsWindow(h)) ::GetClassName(h, s_cls, _countof(s_cls));
|
|
|
|
|
+ return s_cls;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static DWORD DiagWndTid(HWND h)
|
|
|
|
|
+ {
|
|
|
|
|
+ DWORD dwTid = 0;
|
|
|
|
|
+ if (h != NULL && ::IsWindow(h)) dwTid = ::GetWindowThreadProcessId(h, NULL);
|
|
|
|
|
+ return dwTid;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 调 CPaintManagerUI 的"最近一次输入是不是触摸"的判据(见 UIManager.h)。
|
|
|
|
|
+ // 只用于日志标注,不参与任何判断。
|
|
|
class CEditWnd : public CWindowWnd
|
|
class CEditWnd : public CWindowWnd
|
|
|
{
|
|
{
|
|
|
public:
|
|
public:
|
|
@@ -17,6 +58,7 @@ namespace DuiLib
|
|
|
|
|
|
|
|
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
|
LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
|
|
|
|
+ LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
|
|
LRESULT OnEditChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
|
LRESULT OnEditChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
|
|
|
|
|
|
|
protected:
|
|
protected:
|
|
@@ -38,6 +80,15 @@ namespace DuiLib
|
|
|
void CEditWnd::Init(CEditUI* pOwner)
|
|
void CEditWnd::Init(CEditUI* pOwner)
|
|
|
{
|
|
{
|
|
|
m_pOwner = pOwner;
|
|
m_pOwner = pOwner;
|
|
|
|
|
+ {
|
|
|
|
|
+ CDuiString sOwnerName = pOwner->GetName();
|
|
|
|
|
+ LPCTSTR pszOwnerText = pOwner->GetText();
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDIT-INIT enter owner=%s focused=%d ownerHwnd(old)=%p text='%s' nFocus=%p focusClass=%s focusTid=%u selfTid=%u"),
|
|
|
|
|
+ sOwnerName.GetData(), (int)(pOwner->IsFocused() ? 1 : 0), (void*)pOwner->GetHWND(),
|
|
|
|
|
+ pszOwnerText != NULL ? pszOwnerText : _T(""),
|
|
|
|
|
+ (void*)::GetFocus(), DiagWndClass(::GetFocus()), (unsigned)DiagWndTid(::GetFocus()),
|
|
|
|
|
+ (unsigned)::GetCurrentThreadId());
|
|
|
|
|
+ }
|
|
|
RECT rcPos = CalPos();
|
|
RECT rcPos = CalPos();
|
|
|
UINT uStyle = 0;
|
|
UINT uStyle = 0;
|
|
|
if(m_pOwner->GetManager()->IsLayered()) {
|
|
if(m_pOwner->GetManager()->IsLayered()) {
|
|
@@ -92,6 +143,18 @@ namespace DuiLib
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
m_bInit = true;
|
|
m_bInit = true;
|
|
|
|
|
+ {
|
|
|
|
|
+ CDuiString sOwnerName = m_pOwner->GetName();
|
|
|
|
|
+ // nFocus 应当就是本窗口;若不等,说明有更晚的 SetFocus 把它抢走了。
|
|
|
|
|
+ // sel=[start,end] 是设给 EDIT 的光标/选择范围:
|
|
|
|
|
+ // 路 B(正常点击)是 [点击处, 点击处];路 A(已聚焦但窗口被销毁后重建)是 [0, 全文] ⇒ 全选。
|
|
|
|
|
+ DWORD dwSelStart = 0, dwSelEnd = 0;
|
|
|
|
|
+ ::SendMessage(m_hWnd, EM_GETSEL, (WPARAM)&dwSelStart, (LPARAM)&dwSelEnd);
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDIT-INIT done owner=%s hwnd=%p nFocus=%p focusIsSelf=%d sel=[%u,%u] text='%s'"),
|
|
|
|
|
+ sOwnerName.GetData(), (void*)m_hWnd, (void*)::GetFocus(),
|
|
|
|
|
+ (int)(::GetFocus() == m_hWnd ? 1 : 0),
|
|
|
|
|
+ (unsigned)LOWORD(dwSelStart), (unsigned)LOWORD(dwSelEnd), m_pOwner->GetText());
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
RECT CEditWnd::CalPos()
|
|
RECT CEditWnd::CalPos()
|
|
@@ -137,6 +200,15 @@ namespace DuiLib
|
|
|
|
|
|
|
|
void CEditWnd::OnFinalMessage(HWND hWnd)
|
|
void CEditWnd::OnFinalMessage(HWND hWnd)
|
|
|
{
|
|
{
|
|
|
|
|
+ {
|
|
|
|
|
+ CDuiString sOwnerName = (m_pOwner != NULL) ? m_pOwner->GetName() : CDuiString(_T("?"));
|
|
|
|
|
+ LPCTSTR pszText = (m_pOwner != NULL) ? m_pOwner->GetText() : (LPCTSTR)NULL;
|
|
|
|
|
+ // 同一个 owner 反复出现 FIN → INIT ⇒ 窗口在抖动重建;
|
|
|
|
|
+ // 重建走的是 m_sText,而 m_sText 可能落后于 IME 尚未提交的组合串 ⇒ 字"没了"。
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDIT-FIN hwnd=%p owner=%s text='%s' nFocus=%p"),
|
|
|
|
|
+ (void*)hWnd, sOwnerName.GetData(), pszText != NULL ? pszText : _T(""),
|
|
|
|
|
+ (void*)::GetFocus());
|
|
|
|
|
+ }
|
|
|
m_pOwner->Invalidate();
|
|
m_pOwner->Invalidate();
|
|
|
// Clear reference and die
|
|
// Clear reference and die
|
|
|
if( m_hBkBrush != NULL ) ::DeleteObject(m_hBkBrush);
|
|
if( m_hBkBrush != NULL ) ::DeleteObject(m_hBkBrush);
|
|
@@ -155,6 +227,7 @@ namespace DuiLib
|
|
|
bHandled = FALSE;
|
|
bHandled = FALSE;
|
|
|
}
|
|
}
|
|
|
else if( uMsg == WM_KILLFOCUS ) lRes = OnKillFocus(uMsg, wParam, lParam, bHandled);
|
|
else if( uMsg == WM_KILLFOCUS ) lRes = OnKillFocus(uMsg, wParam, lParam, bHandled);
|
|
|
|
|
+ else if( uMsg == WM_CLOSE ) lRes = OnClose(uMsg, wParam, lParam, bHandled);
|
|
|
else if( uMsg == OCM_COMMAND ) {
|
|
else if( uMsg == OCM_COMMAND ) {
|
|
|
if( GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE ) lRes = OnEditChanged(uMsg, wParam, lParam, bHandled);
|
|
if( GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE ) lRes = OnEditChanged(uMsg, wParam, lParam, bHandled);
|
|
|
else if( GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE ) {
|
|
else if( GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE ) {
|
|
@@ -215,10 +288,91 @@ namespace DuiLib
|
|
|
LRESULT CEditWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
LRESULT CEditWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
{
|
|
{
|
|
|
LRESULT lRes = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
|
|
LRESULT lRes = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
|
|
|
|
|
+ {
|
|
|
|
|
+ CDuiString sOwnerName = (m_pOwner != NULL) ? m_pOwner->GetName() : CDuiString(_T("?"));
|
|
|
|
|
+ HWND hNewFocus = ::GetFocus();
|
|
|
|
|
+ const DWORD dwSelfTid = ::GetCurrentThreadId();
|
|
|
|
|
+ const DWORD dwNewTid = DiagWndTid(hNewFocus);
|
|
|
|
|
+ // ★ 这条就是"输入法正在拼的字会不会消失"的现场。
|
|
|
|
|
+ // 失焦本身不致命 —— 致命的是"失焦即销毁"叠加"瞬态失焦"(见 OnClose)。
|
|
|
|
|
+ // 判读:
|
|
|
|
|
+ // · newFocus = 00000000 ⇒ 本线程此刻**没有**焦点窗口。
|
|
|
|
|
+ // 真实成因只有两种:整个窗口失活(触摸键盘抢前台时的典型形态),
|
|
|
|
|
+ // 或者焦点正在搬家途中。此时 sameProc 无意义(下面记成 -1,
|
|
|
|
|
+ // 别再把它读成"被别的进程抢走"——NULL 没有进程可言)。
|
|
|
|
|
+ // · newFocus != NULL 且 sameProc=0 ⇒ 真被**别的进程/线程**抢走;
|
|
|
|
|
+ // · newClass = EditWnd ⇒ 我们自己的另一个输入框接走了焦点(正常切框);
|
|
|
|
|
+ // · newClass = #32770 ⇒ 被本线程的弹框本体拿走(窗口内部搬家)。
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDIT-KILLFOCUS hwnd=%p owner=%s newFocus=%p newClass=%s newTid=%u selfTid=%u sameProc=%d -> 投递 WM_CLOSE(去留由 OnClose 复核)"),
|
|
|
|
|
+ (void*)m_hWnd, sOwnerName.GetData(), (void*)hNewFocus, DiagWndClass(hNewFocus),
|
|
|
|
|
+ (unsigned)dwNewTid, (unsigned)dwSelfTid,
|
|
|
|
|
+ (int)(hNewFocus == NULL ? -1 : ((dwNewTid == dwSelfTid) ? 1 : 0)));
|
|
|
|
|
+ }
|
|
|
PostMessage(WM_CLOSE);
|
|
PostMessage(WM_CLOSE);
|
|
|
return lRes;
|
|
return lRes;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // ★ 修复(2026-09-23 · 「手指点触摸键盘输入不进去」)
|
|
|
|
|
+ //
|
|
|
|
|
+ // 背景(真机日志实证,一次"手指点输入框"就够看清楚):
|
|
|
|
|
+ //
|
|
|
|
|
+ // PTR-DOWN stealFocus ::SetFocus(m_hWndPaint) ← UIManager.cpp 抢走键盘焦点
|
|
|
|
|
+ // EDIT-KILLFOCUS PostMessage(WM_CLOSE) ← 销毁请求**排队**(异步)
|
|
|
|
|
+ // EDITUI-SETFOCUS 复用编辑窗 ::SetFocus(hEdit) ← 焦点已经还给 EDIT
|
|
|
|
|
+ // …… 几十毫秒后 ……
|
|
|
|
|
+ // EDIT-FIN 窗口还是被那条排队的 WM_CLOSE 销毁了
|
|
|
|
|
+ //
|
|
|
|
|
+ // 也就是说:**焦点刚被抢回来,窗口照旧死掉**。而 CPaintManagerUI::SetFocus 有
|
|
|
|
|
+ // `pControl == m_pFocus` 早退,不会再发 UIEVENT_SETFOCUS ⇒ 窗口不再重建。
|
|
|
|
|
+ // 触摸键盘敲出的字符、退格、输入法候选词的提交,就全都失去了落点
|
|
|
|
|
+ // (真机日志里对应"9.7 秒零条 EDIT-CHANGE")。
|
|
|
|
|
+ //
|
|
|
|
|
+ // "手指点虚拟键盘"之所以必现、鼠标不犯,是因为手指点它的那一刻本窗口会短暂失活
|
|
|
|
|
+ // (WND-ACTIVATE wParam=0(INACTIVE) fg=00000000 + EDIT-KILLFOCUS newFocus=00000000),
|
|
|
|
|
+ // 于是又排上一条 WM_CLOSE;鼠标那一路不会走到这里。
|
|
|
|
|
+ //
|
|
|
|
|
+ // ⇒ 判据(纯状态、零时间戳):**延后到 WM_CLOSE 真正被取出来处理时**,再看一眼
|
|
|
|
|
+ // 「我的 owner 现在还是不是 DuiLib 的焦点控件」:
|
|
|
|
|
+ //
|
|
|
|
|
+ // · 还是 ⇒ 这次失焦只是**瞬态**(窗口内部焦点搬家 / 本窗口短暂失活),
|
|
|
|
|
+ // owner 还要接着输入 ⇒ 取消销毁,窗口活着等 SETFOCUS 回来;
|
|
|
|
|
+ // · 不是 ⇒ 焦点已被别的控件/别的窗口拿走,或者控件被隐藏/移除、弹框正在销毁
|
|
|
|
|
+ // (那些路径都会先 SetFocus(NULL) 或把 m_pFocus 置 NULL)
|
|
|
|
|
+ // ⇒ 按原设计销毁。
|
|
|
|
|
+ //
|
|
|
|
|
+ // 为什么必须延后:CPaintManagerUI::SetFocus 的顺序是
|
|
|
|
|
+ // 「先 ::SetFocus(画布窗口) 让旧编辑窗失焦 → 再派发 KILLFOCUS → 最后才把 m_pFocus 换成新控件」。
|
|
|
|
|
+ // 若在原生 WM_KILLFOCUS 里当场判断,读到的一定还是**旧值** ⇒ 区分不了
|
|
|
|
|
+ // "瞬态失焦"和"真的切到另一个控件"。
|
|
|
|
|
+ //
|
|
|
|
|
+ // 这条判据对"窗口失活"同样成立 —— 而窗口失活恰恰是手指点触摸键盘时唯一会发生的变数。
|
|
|
|
|
+ LRESULT CEditWnd::OnClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
|
|
+ {
|
|
|
|
|
+ CPaintManagerUI* pManager = (m_pOwner != NULL) ? m_pOwner->GetManager() : NULL;
|
|
|
|
|
+ CControlUI* pMgrFocus = (pManager != NULL) ? pManager->GetFocus() : (CControlUI*)NULL;
|
|
|
|
|
+ const bool bOwnerKeepsFocus = (pManager != NULL && pMgrFocus == m_pOwner);
|
|
|
|
|
+
|
|
|
|
|
+ {
|
|
|
|
|
+ CDuiString sOwnerName = (m_pOwner != NULL) ? m_pOwner->GetName() : CDuiString(_T("?"));
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDIT-CLOSE hwnd=%p owner=%s mgrFocus=%p ownerKeepsFocus=%d -> %s"),
|
|
|
|
|
+ (void*)m_hWnd, sOwnerName.GetData(), (void*)pMgrFocus,
|
|
|
|
|
+ (int)(bOwnerKeepsFocus ? 1 : 0),
|
|
|
|
|
+ bOwnerKeepsFocus ? _T("取消销毁(瞬态失焦,窗口保留)") : _T("销毁"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bHandled = TRUE;
|
|
|
|
|
+ if (bOwnerKeepsFocus)
|
|
|
|
|
+ {
|
|
|
|
|
+ // 不销毁。焦点会由下面两条既有路径送回来,不需要在这里造新机制:
|
|
|
|
|
+ // · 我们自己的 ::SetFocus(m_hWndPaint) → WM_SETFOCUS → 重发 UIEVENT_SETFOCUS
|
|
|
|
|
+ // → CEditUI::DoEvent 的"复用编辑窗"分支 Show+SetFocus;
|
|
|
|
|
+ // · 整个窗口重新被激活 → MessageHandler 的 WM_ACTIVATE 同理。
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ ::DestroyWindow(m_hWnd);
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
LRESULT CEditWnd::OnEditChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
LRESULT CEditWnd::OnEditChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
{
|
|
{
|
|
|
if( !m_bInit ) return 0;
|
|
if( !m_bInit ) return 0;
|
|
@@ -230,6 +384,18 @@ namespace DuiLib
|
|
|
if( pstr == NULL ) return 0;
|
|
if( pstr == NULL ) return 0;
|
|
|
::GetWindowText(m_hWnd, pstr, cchLen);
|
|
::GetWindowText(m_hWnd, pstr, cchLen);
|
|
|
m_pOwner->m_sText = pstr;
|
|
m_pOwner->m_sText = pstr;
|
|
|
|
|
+ {
|
|
|
|
|
+ CDuiString sOwnerName = m_pOwner->GetName();
|
|
|
|
|
+ // 每一次内容变化都留痕 —— "字符进来"与"被清空"都能看到。
|
|
|
|
|
+ // 判读:
|
|
|
|
|
+ // · append 之后突然出现 len=0 ⇒ 有代码把它清掉了。可能是业务侧的
|
|
|
|
|
+ // 数字过滤(首个字符非法时 SetText("")),也可能是 CEditUI::SetText
|
|
|
|
|
+ // 被调用 —— 后者会在 EDITUI-SETTEXT 行留下记录;
|
|
|
|
|
+ // · 只看到 append、从没出现 len=0,用户却说"字没了"
|
|
|
|
|
+ // ⇒ 字符根本没进到这个窗口 ⇒ 焦点已经不在它身上(查 EDIT-KILLFOCUS)。
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDIT-CHANGE hwnd=%p owner=%s len=%d text='%s'"),
|
|
|
|
|
+ (void*)m_hWnd, sOwnerName.GetData(), cchLen - 1, pstr);
|
|
|
|
|
+ }
|
|
|
m_pOwner->GetManager()->SendNotify(m_pOwner, DUI_MSGTYPE_TEXTCHANGED);
|
|
m_pOwner->GetManager()->SendNotify(m_pOwner, DUI_MSGTYPE_TEXTCHANGED);
|
|
|
if( m_pOwner->GetManager()->IsLayered() ) m_pOwner->Invalidate();
|
|
if( m_pOwner->GetManager()->IsLayered() ) m_pOwner->Invalidate();
|
|
|
return 0;
|
|
return 0;
|
|
@@ -269,6 +435,32 @@ namespace DuiLib
|
|
|
|
|
|
|
|
void CEditUI::DoEvent(TEventUI& event)
|
|
void CEditUI::DoEvent(TEventUI& event)
|
|
|
{
|
|
{
|
|
|
|
|
+ // 【诊断 · 新建商品弹框「输入文字消失」】只对焦点/点击类事件留痕,避免鼠标移动刷屏。
|
|
|
|
|
+ //
|
|
|
|
|
+ // 为什么这条日志值得存在:本控件后面那个 `if( m_pWindow ) return;` 会让
|
|
|
|
|
+ // UIEVENT_SETFOCUS **提前返回**,从而走不到 CLabelUI::DoEvent 里的
|
|
|
|
|
+ // `m_bFocused = true` —— 于是 IsFocused() 与真实焦点可能脱节。
|
|
|
|
|
+ // 而下面 UIEVENT_BUTTONDOWN 的三岔口正是**用 IsFocused() 分流**的:
|
|
|
|
|
+ // · IsFocused() && m_pWindow == NULL ⇒ 路 A:新建窗口 + **全选整框**
|
|
|
|
|
+ // · m_pWindow != NULL ⇒ 路 B:光标落到点击处(正常点击)
|
|
|
|
|
+ // · 两者都不满足 ⇒ 什么都不做(点击被静默丢弃)
|
|
|
|
|
+ // m_bFocused / m_pWindow 这两个值必须能在日志里直接看到,否则无从判断走了哪条。
|
|
|
|
|
+ if (event.Type == UIEVENT_SETFOCUS
|
|
|
|
|
+ || event.Type == UIEVENT_KILLFOCUS
|
|
|
|
|
+ || event.Type == UIEVENT_BUTTONDOWN
|
|
|
|
|
+ || event.Type == UIEVENT_BUTTONUP
|
|
|
|
|
+ || event.Type == UIEVENT_TOUCHDOWN
|
|
|
|
|
+ || event.Type == UIEVENT_TOUCHUP
|
|
|
|
|
+ || event.Type == UIEVENT_DBLCLICK
|
|
|
|
|
+ || event.Type == UIEVENT_RBUTTONDOWN)
|
|
|
|
|
+ {
|
|
|
|
|
+ CDuiString sName = GetName();
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDITUI-EVENT type=%d owner=%s m_bFocused=%d m_pWindow=%p editHwnd=%p pt=%d,%d nFocus=%p mgrHwnd=%p"),
|
|
|
|
|
+ event.Type, sName.GetData(), (int)(m_bFocused ? 1 : 0), (void*)m_pWindow,
|
|
|
|
|
+ (void*)GetHWND(), event.ptMouse.x, event.ptMouse.y,
|
|
|
|
|
+ (void*)::GetFocus(), (void*)(m_pManager != NULL ? m_pManager->GetPaintWindow() : (HWND)NULL));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
|
|
if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
|
|
|
if( m_pParent != NULL ) m_pParent->DoEvent(event);
|
|
if( m_pParent != NULL ) m_pParent->DoEvent(event);
|
|
|
else CLabelUI::DoEvent(event);
|
|
else CLabelUI::DoEvent(event);
|
|
@@ -290,7 +482,35 @@ namespace DuiLib
|
|
|
}
|
|
}
|
|
|
if( event.Type == UIEVENT_SETFOCUS && IsEnabled() )
|
|
if( event.Type == UIEVENT_SETFOCUS && IsEnabled() )
|
|
|
{
|
|
{
|
|
|
- if( m_pWindow ) return;
|
|
|
|
|
|
|
+ if( m_pWindow != NULL )
|
|
|
|
|
+ {
|
|
|
|
|
+ // ★ 修复(见本文件顶部「输入文字消失」说明)
|
|
|
|
|
+ //
|
|
|
|
|
+ // 这里是原来的 `if( m_pWindow ) return;`。它有两个后果:
|
|
|
|
|
+ // (1) 走不到 CLabelUI::DoEvent,`m_bFocused = true` 永不执行
|
|
|
|
|
+ // ⇒ IsFocused() 与真实焦点长期脱节(下面 BUTTONDOWN 曾用它分流);
|
|
|
|
|
+ // (2) 编辑窗口**还在**、但键盘焦点已被系统/输入法/触摸键盘挪走时,
|
|
|
|
|
+ // 没有任何代码把焦点还回去 —— 而所有按键(含退格)与输入法
|
|
|
|
|
+ // 候选词的提交都必须依赖它。这正是"切换过一次输入框后
|
|
|
|
|
+ // 连退格都输不进去"的那一格。
|
|
|
|
|
+ //
|
|
|
|
|
+ // 既然 DuiLib 的焦点此刻仍指向本控件(否则收不到 SETFOCUS),
|
|
|
|
|
+ // 就把显示与键盘焦点一并还给它。
|
|
|
|
|
+ HWND hEdit = *m_pWindow;
|
|
|
|
|
+ if (hEdit != NULL && ::IsWindow(hEdit))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!::IsWindowVisible(hEdit)) ::ShowWindow(hEdit, SW_SHOWNOACTIVATE);
|
|
|
|
|
+ if (::GetFocus() != hEdit) ::SetFocus(hEdit);
|
|
|
|
|
+ }
|
|
|
|
|
+ {
|
|
|
|
|
+ CDuiString sName = GetName();
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDITUI-SETFOCUS 复用编辑窗 owner=%s editHwnd=%p nFocus=%p -> Show+SetFocus"),
|
|
|
|
|
+ sName.GetData(), (void*)hEdit, (void*)::GetFocus());
|
|
|
|
|
+ }
|
|
|
|
|
+ CLabelUI::DoEvent(event); // 让基类补上 m_bFocused = true
|
|
|
|
|
+ Invalidate();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
m_pWindow = new CEditWnd();
|
|
m_pWindow = new CEditWnd();
|
|
|
ASSERT(m_pWindow);
|
|
ASSERT(m_pWindow);
|
|
|
m_pWindow->Init(this);
|
|
m_pWindow->Init(this);
|
|
@@ -304,8 +524,17 @@ namespace DuiLib
|
|
|
{
|
|
{
|
|
|
if( IsEnabled() ) {
|
|
if( IsEnabled() ) {
|
|
|
GetManager()->ReleaseCapture();
|
|
GetManager()->ReleaseCapture();
|
|
|
- if( IsFocused() && m_pWindow == NULL )
|
|
|
|
|
|
|
+ // ★ 修复:判据原来还多要求一个 `IsFocused()`。但那个标志由
|
|
|
|
|
+ // CLabelUI::DoEvent 维护,而上面的 SETFOCUS 分支在"编辑窗已存在"
|
|
|
|
|
+ // 时会早退、根本走不到那里 ⇒ IsFocused() 可能长期为假。
|
|
|
|
|
+ // 于是"点击一个还没有编辑窗的输入框"时**两条分支都不走**:
|
|
|
|
|
+ // 点击被静默丢弃(体感就是"点了没反应")。
|
|
|
|
|
+ // 现在只看"有没有编辑窗" —— 点了输入框就一定拿得到输入能力。
|
|
|
|
|
+ if( m_pWindow == NULL )
|
|
|
{
|
|
{
|
|
|
|
|
+ CDuiString sName = GetName();
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDITUI-BUTTONDOWN 无编辑窗 -> 新建并全选 owner=%s m_bFocused=%d"),
|
|
|
|
|
+ sName.GetData(), (int)(m_bFocused ? 1 : 0));
|
|
|
m_pWindow = new CEditWnd();
|
|
m_pWindow = new CEditWnd();
|
|
|
ASSERT(m_pWindow);
|
|
ASSERT(m_pWindow);
|
|
|
m_pWindow->Init(this);
|
|
m_pWindow->Init(this);
|
|
@@ -317,8 +546,11 @@ namespace DuiLib
|
|
|
Edit_SetSel(*m_pWindow, 0, nSize);
|
|
Edit_SetSel(*m_pWindow, 0, nSize);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- else if( m_pWindow != NULL )
|
|
|
|
|
|
|
+ else
|
|
|
{
|
|
{
|
|
|
|
|
+ CDuiString sName = GetName();
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDITUI-BUTTONDOWN 有编辑窗 -> 光标落到点击处 owner=%s pt=%d,%d autoSelAll=%d"),
|
|
|
|
|
+ sName.GetData(), event.ptMouse.x, event.ptMouse.y, (int)(m_bAutoSelAll ? 1 : 0));
|
|
|
if (!m_bAutoSelAll) {
|
|
if (!m_bAutoSelAll) {
|
|
|
RECT rcTextPadding = GetTextPadding();
|
|
RECT rcTextPadding = GetTextPadding();
|
|
|
POINT pt = event.ptMouse;
|
|
POINT pt = event.ptMouse;
|
|
@@ -375,6 +607,16 @@ namespace DuiLib
|
|
|
|
|
|
|
|
void CEditUI::SetText(LPCTSTR pstrText)
|
|
void CEditUI::SetText(LPCTSTR pstrText)
|
|
|
{
|
|
{
|
|
|
|
|
+ {
|
|
|
|
|
+ CDuiString sName = GetName();
|
|
|
|
|
+ // 谁在改文本、改成什么。业务侧的数字过滤在"首个字符非法"时会
|
|
|
|
|
+ // SetText("") —— 那会把整框内容清空(正是"输入完被自动删除"的一种表现)。
|
|
|
|
|
+ // 注意:本函数无论编辑窗在不在都会执行,m_pWindow=NULL 时只改 m_sText,
|
|
|
|
|
+ // 那个新值要等下次 CEditWnd::Init 才会真正写进 EDIT。
|
|
|
|
|
+ DuiLibDiag::Log(_T("EDITUI-SETTEXT owner=%s m_pWindow=%p old='%s' new='%s'"),
|
|
|
|
|
+ sName.GetData(), (void*)m_pWindow,
|
|
|
|
|
+ m_sText.GetData(), pstrText != NULL ? pstrText : _T(""));
|
|
|
|
|
+ }
|
|
|
m_sText = pstrText;
|
|
m_sText = pstrText;
|
|
|
if( m_pWindow != NULL ) Edit_SetText(*m_pWindow, m_sText);
|
|
if( m_pWindow != NULL ) Edit_SetText(*m_pWindow, m_sText);
|
|
|
Invalidate();
|
|
Invalidate();
|