| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961 |
- #include "StdAfx.h"
- #include "UIEdit.h"
- #include "../Utils/UIDiag.h"
- 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
- {
- public:
- CEditWnd();
- void Init(CEditUI* pOwner);
- RECT CalPos();
- LPCTSTR GetWindowClassName() const;
- LPCTSTR GetSuperClassName() const;
- void OnFinalMessage(HWND hWnd);
- LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
- 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);
- protected:
- enum {
- DEFAULT_TIMERID = 20,
- };
- CEditUI* m_pOwner;
- HBRUSH m_hBkBrush;
- bool m_bInit;
- bool m_bDrawCaret;
- };
- CEditWnd::CEditWnd() : m_pOwner(NULL), m_hBkBrush(NULL), m_bInit(false), m_bDrawCaret(false)
- {
- }
- void CEditWnd::Init(CEditUI* 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();
- UINT uStyle = 0;
- if(m_pOwner->GetManager()->IsLayered()) {
- uStyle = WS_POPUP | ES_AUTOHSCROLL | WS_VISIBLE;
- RECT rcWnd={0};
- ::GetWindowRect(m_pOwner->GetManager()->GetPaintWindow(), &rcWnd);
- rcPos.left += rcWnd.left;
- rcPos.right += rcWnd.left;
- rcPos.top += rcWnd.top - 1;
- rcPos.bottom += rcWnd.top - 1;
- }
- else {
- uStyle = WS_CHILD | ES_AUTOHSCROLL;
- }
- UINT uTextStyle = m_pOwner->GetTextStyle();
- if(uTextStyle & DT_LEFT) uStyle |= ES_LEFT;
- else if(uTextStyle & DT_CENTER) uStyle |= ES_CENTER;
- else if(uTextStyle & DT_RIGHT) uStyle |= ES_RIGHT;
- if( m_pOwner->IsPasswordMode() ) uStyle |= ES_PASSWORD;
- Create(m_pOwner->GetManager()->GetPaintWindow(), NULL, uStyle, 0, rcPos);
- HFONT hFont=NULL;
- int iFontIndex=m_pOwner->GetFont();
- if (iFontIndex!=-1)
- hFont = m_pOwner->GetManager()->GetFont(iFontIndex);
- if (hFont == NULL)
- hFont = m_pOwner->GetManager()->GetDefaultFontInfo()->hFont;
- SetWindowFont(m_hWnd, hFont, TRUE);
- Edit_LimitText(m_hWnd, m_pOwner->GetMaxChar());
- if( m_pOwner->IsPasswordMode() ) Edit_SetPasswordChar(m_hWnd, m_pOwner->GetPasswordChar());
- Edit_SetText(m_hWnd, m_pOwner->GetText());
- Edit_SetModify(m_hWnd, FALSE);
- SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELPARAM(0, 0));
- Edit_Enable(m_hWnd, m_pOwner->IsEnabled() == true);
- Edit_SetReadOnly(m_hWnd, m_pOwner->IsReadOnly() == true);
- //Styls
- LONG styleValue = ::GetWindowLong(m_hWnd, GWL_STYLE);
- styleValue |= pOwner->GetWindowStyls();
- ::SetWindowLong(GetHWND(), GWL_STYLE, styleValue);
- //Styls
- ::ShowWindow(m_hWnd, SW_SHOWNOACTIVATE);
- ::SetFocus(m_hWnd);
- if (m_pOwner->IsAutoSelAll()) {
- int nSize = GetWindowTextLength(m_hWnd);
- if( nSize == 0 ) nSize = 1;
- Edit_SetSel(m_hWnd, 0, nSize);
- }
- else {
- int nSize = GetWindowTextLength(m_hWnd);
- Edit_SetSel(m_hWnd, nSize, nSize);
- }
- 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()
- {
- CDuiRect rcPos = m_pOwner->GetPos();
- RECT rcInset = m_pOwner->GetTextPadding();
- rcPos.left += rcInset.left;
- rcPos.top += rcInset.top;
- rcPos.right -= rcInset.right;
- rcPos.bottom -= rcInset.bottom;
- LONG lEditHeight = m_pOwner->GetManager()->GetFontInfo(m_pOwner->GetFont())->tm.tmHeight;
- if( lEditHeight < rcPos.GetHeight() ) {
- rcPos.top += (rcPos.GetHeight() - lEditHeight) / 2;
- rcPos.bottom = rcPos.top + lEditHeight;
- }
- CControlUI* pParent = m_pOwner;
- RECT rcParent;
- while( pParent = pParent->GetParent() ) {
- if( !pParent->IsVisible() ) {
- rcPos.left = rcPos.top = rcPos.right = rcPos.bottom = 0;
- break;
- }
- rcParent = pParent->GetClientPos();
- if( !::IntersectRect(&rcPos, &rcPos, &rcParent) ) {
- rcPos.left = rcPos.top = rcPos.right = rcPos.bottom = 0;
- break;
- }
- }
- return rcPos;
- }
- LPCTSTR CEditWnd::GetWindowClassName() const
- {
- return _T("EditWnd");
- }
- LPCTSTR CEditWnd::GetSuperClassName() const
- {
- return WC_EDIT;
- }
- 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();
- // Clear reference and die
- if( m_hBkBrush != NULL ) ::DeleteObject(m_hBkBrush);
- if (m_pOwner->GetManager()->IsLayered()) {
- m_pOwner->GetManager()->RemoveNativeWindow(hWnd);
- }
- m_pOwner->m_pWindow = NULL;
- delete this;
- }
- LRESULT CEditWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- LRESULT lRes = 0;
- BOOL bHandled = TRUE;
- if( uMsg == WM_CREATE ) {
- bHandled = FALSE;
- }
- 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 ) {
- 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 ) {
- RECT rcClient;
- ::GetClientRect(m_hWnd, &rcClient);
- ::InvalidateRect(m_hWnd, &rcClient, FALSE);
- }
- }
- else if( uMsg == WM_KEYDOWN && TCHAR(wParam) == VK_RETURN ){
- m_pOwner->GetManager()->SendNotify(m_pOwner, DUI_MSGTYPE_RETURN);
- }
- else if( uMsg == WM_KEYDOWN && TCHAR(wParam) == VK_TAB ){
- if (m_pOwner->GetManager()->IsLayered()) {
- m_pOwner->GetManager()->SetNextTabControl();
- }
- }
- else if( uMsg == OCM__BASE + WM_CTLCOLOREDIT || uMsg == OCM__BASE + WM_CTLCOLORSTATIC ) {
- ::SetBkMode((HDC)wParam, TRANSPARENT);
- DWORD dwTextColor = m_pOwner->GetTextColor();
- ::SetTextColor((HDC)wParam, RGB(GetBValue(dwTextColor),GetGValue(dwTextColor),GetRValue(dwTextColor)));
- DWORD clrColor = m_pOwner->GetNativeEditBkColor();
- if (clrColor < 0xFF000000) {
- if (m_hBkBrush != NULL) ::DeleteObject(m_hBkBrush);
- RECT rcWnd = m_pOwner->GetManager()->GetNativeWindowRect(m_hWnd);
- HBITMAP hBmpEditBk = CRenderEngine::GenerateBitmap(m_pOwner->GetManager(), rcWnd, m_pOwner, clrColor);
- m_hBkBrush = ::CreatePatternBrush(hBmpEditBk);
- ::DeleteObject(hBmpEditBk);
- }
- else {
- if (m_hBkBrush == NULL) {
- m_hBkBrush = ::CreateSolidBrush(RGB(GetBValue(clrColor), GetGValue(clrColor), GetRValue(clrColor)));
- }
- }
- return (LRESULT)m_hBkBrush;
- }
- else if( uMsg == WM_PAINT) {
- bHandled = FALSE;
- }
- else if( uMsg == WM_PRINT ) {
- bHandled = FALSE;
- }
- else if( uMsg == WM_TIMER ) {
- if (wParam == CARET_TIMERID) {
- m_bDrawCaret = !m_bDrawCaret;
- RECT rcClient;
- ::GetClientRect(m_hWnd, &rcClient);
- ::InvalidateRect(m_hWnd, &rcClient, FALSE);
- return 0;
- }
- bHandled = FALSE;
- }
- else bHandled = FALSE;
- if( !bHandled ) return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
- return lRes;
- }
- LRESULT CEditWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
- {
- 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);
- 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*/)
- {
- if( !m_bInit ) return 0;
- if( m_pOwner == NULL ) return 0;
- // Copy text back
- int cchLen = ::GetWindowTextLength(m_hWnd) + 1;
- LPTSTR pstr = static_cast<LPTSTR>(_alloca(cchLen * sizeof(TCHAR)));
- ASSERT(pstr);
- if( pstr == NULL ) return 0;
- ::GetWindowText(m_hWnd, pstr, cchLen);
- 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);
- if( m_pOwner->GetManager()->IsLayered() ) m_pOwner->Invalidate();
- return 0;
- }
- /////////////////////////////////////////////////////////////////////////////////////
- //
- //
- IMPLEMENT_DUICONTROL(CEditUI)
- CEditUI::CEditUI() : m_pWindow(NULL), m_uMaxChar(255), m_bReadOnly(false),
- m_bPasswordMode(false), m_cPasswordChar(_T('*')), m_bAutoSelAll(false), m_uButtonState(0),
- m_dwEditbkColor(0xFFFFFFFF), m_dwEditTextColor(0x00000000), m_iWindowStyls(0),m_dwTipValueColor(0xFFBAC0C5)
- {
- SetTextPadding(CDuiRect(4, 3, 4, 3));
- SetBkColor(0xFFFFFFFF);
- }
- LPCTSTR CEditUI::GetClass() const
- {
- return _T("EditUI");
- }
- LPVOID CEditUI::GetInterface(LPCTSTR pstrName)
- {
- if( _tcsicmp(pstrName, DUI_CTR_EDIT) == 0 ) return static_cast<CEditUI*>(this);
- return CLabelUI::GetInterface(pstrName);
- }
- UINT CEditUI::GetControlFlags() const
- {
- if( !IsEnabled() ) return CControlUI::GetControlFlags();
- return UIFLAG_SETCURSOR | UIFLAG_TABSTOP;
- }
- 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( m_pParent != NULL ) m_pParent->DoEvent(event);
- else CLabelUI::DoEvent(event);
- return;
- }
- if( event.Type == UIEVENT_SETCURSOR && IsEnabled() )
- {
- ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM)));
- return;
- }
- if( event.Type == UIEVENT_WINDOWSIZE )
- {
- //if( m_pWindow != NULL ) m_pManager->SetFocusNeeded(this);
- }
- if( event.Type == UIEVENT_SCROLLWHEEL )
- {
- if( m_pWindow != NULL ) return;
- }
- if( event.Type == UIEVENT_SETFOCUS && IsEnabled() )
- {
- 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();
- ASSERT(m_pWindow);
- m_pWindow->Init(this);
- Invalidate();
- }
- if( event.Type == UIEVENT_KILLFOCUS && IsEnabled() )
- {
- Invalidate();
- }
- if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK || event.Type == UIEVENT_RBUTTONDOWN)
- {
- if( IsEnabled() ) {
- GetManager()->ReleaseCapture();
- // ★ 修复:判据原来还多要求一个 `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();
- ASSERT(m_pWindow);
- m_pWindow->Init(this);
- if( PtInRect(&m_rcItem, event.ptMouse) )
- {
- int nSize = GetWindowTextLength(*m_pWindow);
- if( nSize == 0 ) nSize = 1;
- Edit_SetSel(*m_pWindow, 0, nSize);
- }
- }
- 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) {
- RECT rcTextPadding = GetTextPadding();
- POINT pt = event.ptMouse;
- pt.x -= m_rcItem.left + rcTextPadding.left;
- pt.y -= m_rcItem.top + rcTextPadding.top;
- Edit_SetSel(*m_pWindow, 0, 0);
- ::SendMessage(*m_pWindow, WM_LBUTTONDOWN, event.wParam, MAKELPARAM(pt.x, pt.y));
- }
- }
- }
- return;
- }
- if( event.Type == UIEVENT_MOUSEMOVE )
- {
- return;
- }
- if( event.Type == UIEVENT_BUTTONUP )
- {
- return;
- }
- if( event.Type == UIEVENT_CONTEXTMENU )
- {
- return;
- }
- if( event.Type == UIEVENT_MOUSEENTER )
- {
- if( ::PtInRect(&m_rcItem, event.ptMouse ) ) {
- if( IsEnabled() ) {
- if( (m_uButtonState & UISTATE_HOT) == 0 ) {
- m_uButtonState |= UISTATE_HOT;
- Invalidate();
- }
- }
- }
- }
- if( event.Type == UIEVENT_MOUSELEAVE )
- {
- if( IsEnabled() ) {
- m_uButtonState &= ~UISTATE_HOT;
- Invalidate();
- }
- return;
- }
- CLabelUI::DoEvent(event);
- }
- void CEditUI::SetEnabled(bool bEnable)
- {
- CControlUI::SetEnabled(bEnable);
- if( !IsEnabled() ) {
- m_uButtonState = 0;
- }
- }
- 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;
- if( m_pWindow != NULL ) Edit_SetText(*m_pWindow, m_sText);
- Invalidate();
- }
- void CEditUI::SetMaxChar(UINT uMax)
- {
- m_uMaxChar = uMax;
- if( m_pWindow != NULL ) Edit_LimitText(*m_pWindow, m_uMaxChar);
- }
- UINT CEditUI::GetMaxChar()
- {
- return m_uMaxChar;
- }
- void CEditUI::SetReadOnly(bool bReadOnly)
- {
- if( m_bReadOnly == bReadOnly ) return;
- m_bReadOnly = bReadOnly;
- if( m_pWindow != NULL ) Edit_SetReadOnly(*m_pWindow, m_bReadOnly);
- Invalidate();
- }
- bool CEditUI::IsReadOnly() const
- {
- return m_bReadOnly;
- }
- void CEditUI::SetNumberOnly(bool bNumberOnly)
- {
- if( bNumberOnly )
- {
- m_iWindowStyls |= ES_NUMBER;
- }
- else
- {
- m_iWindowStyls &= ~ES_NUMBER;
- }
- }
- bool CEditUI::IsNumberOnly() const
- {
- return (m_iWindowStyls & ES_NUMBER) ? true:false;
- }
- int CEditUI::GetWindowStyls() const
- {
- return m_iWindowStyls;
- }
- void CEditUI::SetPasswordMode(bool bPasswordMode)
- {
- if( m_bPasswordMode == bPasswordMode ) return;
- m_bPasswordMode = bPasswordMode;
- Invalidate();
- if( m_pWindow != NULL ) {
- LONG styleValue = ::GetWindowLong(*m_pWindow, GWL_STYLE);
- bPasswordMode ? styleValue |= ES_PASSWORD : styleValue &= ~ES_PASSWORD;
- ::SetWindowLong(*m_pWindow, GWL_STYLE, styleValue);
- }
- }
- bool CEditUI::IsPasswordMode() const
- {
- return m_bPasswordMode;
- }
- void CEditUI::SetPasswordChar(TCHAR cPasswordChar)
- {
- if( m_cPasswordChar == cPasswordChar ) return;
- m_cPasswordChar = cPasswordChar;
- if( m_pWindow != NULL ) Edit_SetPasswordChar(*m_pWindow, m_cPasswordChar);
- Invalidate();
- }
- TCHAR CEditUI::GetPasswordChar() const
- {
- return m_cPasswordChar;
- }
- LPCTSTR CEditUI::GetNormalImage()
- {
- return m_sNormalImage;
- }
- void CEditUI::SetNormalImage(LPCTSTR pStrImage)
- {
- m_sNormalImage = pStrImage;
- Invalidate();
- }
- LPCTSTR CEditUI::GetHotImage()
- {
- return m_sHotImage;
- }
- void CEditUI::SetHotImage(LPCTSTR pStrImage)
- {
- m_sHotImage = pStrImage;
- Invalidate();
- }
- LPCTSTR CEditUI::GetFocusedImage()
- {
- return m_sFocusedImage;
- }
- void CEditUI::SetFocusedImage(LPCTSTR pStrImage)
- {
- m_sFocusedImage = pStrImage;
- Invalidate();
- }
- LPCTSTR CEditUI::GetDisabledImage()
- {
- return m_sDisabledImage;
- }
- void CEditUI::SetDisabledImage(LPCTSTR pStrImage)
- {
- m_sDisabledImage = pStrImage;
- Invalidate();
- }
- void CEditUI::SetNativeEditBkColor(DWORD dwBkColor)
- {
- m_dwEditbkColor = dwBkColor;
- }
- DWORD CEditUI::GetNativeEditBkColor() const
- {
- return m_dwEditbkColor;
- }
- void CEditUI::SetNativeEditTextColor( LPCTSTR pStrColor )
- {
- if( *pStrColor == _T('#')) pStrColor = ::CharNext(pStrColor);
- LPTSTR pstr = NULL;
- DWORD clrColor = _tcstoul(pStrColor, &pstr, 16);
- m_dwEditTextColor = clrColor;
- }
- DWORD CEditUI::GetNativeEditTextColor() const
- {
- return m_dwEditTextColor;
- }
- bool CEditUI::IsAutoSelAll()
- {
- return m_bAutoSelAll;
- }
- void CEditUI::SetAutoSelAll(bool bAutoSelAll)
- {
- m_bAutoSelAll = bAutoSelAll;
- }
- void CEditUI::SetSel(long nStartChar, long nEndChar)
- {
- if( m_pWindow != NULL ) Edit_SetSel(*m_pWindow, nStartChar,nEndChar);
- }
- void CEditUI::SetSelAll()
- {
- SetSel(0,-1);
- }
- void CEditUI::SetReplaceSel(LPCTSTR lpszReplace)
- {
- if( m_pWindow != NULL ) Edit_ReplaceSel(*m_pWindow, lpszReplace);
- }
- void CEditUI::SetTipValue( LPCTSTR pStrTipValue )
- {
- m_sTipValue = pStrTipValue;
- }
- LPCTSTR CEditUI::GetTipValue()
- {
- if (!IsResourceText()) return m_sTipValue;
- return CResourceManager::GetInstance()->GetText(m_sTipValue);
- }
- void CEditUI::SetTipValueColor( LPCTSTR pStrColor )
- {
- if( *pStrColor == _T('#')) pStrColor = ::CharNext(pStrColor);
- LPTSTR pstr = NULL;
- DWORD clrColor = _tcstoul(pStrColor, &pstr, 16);
- m_dwTipValueColor = clrColor;
- }
- DWORD CEditUI::GetTipValueColor()
- {
- return m_dwTipValueColor;
- }
- HWND CEditUI::GetHWND()
- {
- if(m_pWindow != NULL) {
- return m_pWindow->GetHWND();
- }
- return NULL;
- }
- void CEditUI::SetPos(RECT rc, bool bNeedInvalidate)
- {
- CControlUI::SetPos(rc, bNeedInvalidate);
- if( m_pWindow != NULL ) {
- RECT rcPos = m_pWindow->CalPos();
- ::SetWindowPos(m_pWindow->GetHWND(), NULL, rcPos.left, rcPos.top, rcPos.right - rcPos.left,
- rcPos.bottom - rcPos.top, SWP_NOZORDER | SWP_NOACTIVATE);
- }
- }
- void CEditUI::Move(SIZE szOffset, bool bNeedInvalidate)
- {
- CControlUI::Move(szOffset, bNeedInvalidate);
- if( m_pWindow != NULL ) {
- RECT rcPos = m_pWindow->CalPos();
- ::SetWindowPos(m_pWindow->GetHWND(), NULL, rcPos.left, rcPos.top, rcPos.right - rcPos.left,
- rcPos.bottom - rcPos.top, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
- }
- }
- void CEditUI::SetVisible(bool bVisible)
- {
- CControlUI::SetVisible(bVisible);
- if( !IsVisible() && m_pWindow != NULL ) m_pManager->SetFocus(NULL);
- }
- void CEditUI::SetInternVisible(bool bVisible)
- {
- if( !IsVisible() && m_pWindow != NULL ) m_pManager->SetFocus(NULL);
- }
- SIZE CEditUI::EstimateSize(SIZE szAvailable)
- {
- if( m_cxyFixed.cy == 0 ) return CDuiSize(m_cxyFixed.cx, m_pManager->GetFontInfo(GetFont())->tm.tmHeight + 6);
- return CControlUI::EstimateSize(szAvailable);
- }
- void CEditUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
- {
- if( _tcsicmp(pstrName, _T("readonly")) == 0 ) SetReadOnly(_tcsicmp(pstrValue, _T("true")) == 0);
- else if( _tcsicmp(pstrName, _T("numberonly")) == 0 ) SetNumberOnly(_tcsicmp(pstrValue, _T("true")) == 0);
- else if( _tcscmp(pstrName, _T("autoselall")) == 0 ) SetAutoSelAll(_tcscmp(pstrValue, _T("true")) == 0);
- else if( _tcsicmp(pstrName, _T("password")) == 0 ) SetPasswordMode(_tcsicmp(pstrValue, _T("true")) == 0);
- else if( _tcsicmp(pstrName, _T("passwordchar")) == 0 ) SetPasswordChar(*pstrValue);
- else if( _tcsicmp(pstrName, _T("maxchar")) == 0 ) SetMaxChar(_ttoi(pstrValue));
- else if( _tcsicmp(pstrName, _T("normalimage")) == 0 ) SetNormalImage(pstrValue);
- else if( _tcsicmp(pstrName, _T("hotimage")) == 0 ) SetHotImage(pstrValue);
- else if( _tcsicmp(pstrName, _T("focusedimage")) == 0 ) SetFocusedImage(pstrValue);
- else if( _tcsicmp(pstrName, _T("disabledimage")) == 0 ) SetDisabledImage(pstrValue);
- else if( _tcsicmp(pstrName, _T("tipvalue")) == 0 ) SetTipValue(pstrValue);
- else if( _tcsicmp(pstrName, _T("tipvaluecolor")) == 0 ) SetTipValueColor(pstrValue);
- else if( _tcsicmp(pstrName, _T("nativetextcolor")) == 0 ) SetNativeEditTextColor(pstrValue);
- else if( _tcsicmp(pstrName, _T("nativebkcolor")) == 0 ) {
- if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);
- LPTSTR pstr = NULL;
- DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
- SetNativeEditBkColor(clrColor);
- }
- else CLabelUI::SetAttribute(pstrName, pstrValue);
- }
- void CEditUI::PaintStatusImage(HDC hDC)
- {
- if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED;
- else m_uButtonState &= ~ UISTATE_FOCUSED;
- if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED;
- else m_uButtonState &= ~ UISTATE_DISABLED;
- if( (m_uButtonState & UISTATE_DISABLED) != 0 ) {
- if( !m_sDisabledImage.IsEmpty() ) {
- if( !DrawImage(hDC, (LPCTSTR)m_sDisabledImage) ) {}
- else return;
- }
- }
- else if( (m_uButtonState & UISTATE_FOCUSED) != 0 ) {
- if( !m_sFocusedImage.IsEmpty() ) {
- if( !DrawImage(hDC, (LPCTSTR)m_sFocusedImage) ) {}
- else return;
- }
- }
- else if( (m_uButtonState & UISTATE_HOT) != 0 ) {
- if( !m_sHotImage.IsEmpty() ) {
- if( !DrawImage(hDC, (LPCTSTR)m_sHotImage) ) {}
- else return;
- }
- }
- if( !m_sNormalImage.IsEmpty() ) {
- if( !DrawImage(hDC, (LPCTSTR)m_sNormalImage) ) {}
- else return;
- }
- }
- void CEditUI::PaintText(HDC hDC)
- {
- DWORD mCurTextColor = m_dwTextColor;
- if( m_dwTextColor == 0 ) mCurTextColor = m_dwTextColor = m_pManager->GetDefaultFontColor();
- if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor();
- CDuiString sDrawText = GetText();
- CDuiString sTipValue = GetTipValue();
- if(sDrawText == sTipValue || sDrawText == _T("")) {
- mCurTextColor = m_dwTipValueColor;
- sDrawText = sTipValue;
- }
- else {
- CDuiString sTemp = sDrawText;
- if( m_bPasswordMode ) {
- sDrawText.Empty();
- LPCTSTR pStr = sTemp.GetData();
- while( *pStr != _T('\0') ) {
- sDrawText += m_cPasswordChar;
- pStr = ::CharNext(pStr);
- }
- }
- }
- RECT rcTextPadding = GetTextPadding();
- RECT rc = m_rcItem;
- rc.left += rcTextPadding.left;
- rc.right -= rcTextPadding.right;
- rc.top += rcTextPadding.top;
- rc.bottom -= rcTextPadding.bottom;
- if( IsEnabled() ) {
- CRenderEngine::DrawText(hDC, m_pManager, rc, sDrawText, mCurTextColor, \
- m_iFont, DT_SINGLELINE | m_uTextStyle);
- }
- else {
- CRenderEngine::DrawText(hDC, m_pManager, rc, sDrawText, m_dwDisabledTextColor, \
- m_iFont, DT_SINGLELINE | m_uTextStyle);
- }
- }
- }
|