UIEdit.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. #include "StdAfx.h"
  2. #include "UIEdit.h"
  3. #include "../Utils/UIDiag.h"
  4. namespace DuiLib
  5. {
  6. // ==================================================================
  7. // 【诊断 · 新建商品弹框「输入文字消失」(Win10 触摸键盘)】
  8. //
  9. // 现象:手指点触摸键盘输入,已输入的字(尤其中文输入的组合串)会消失;
  10. // 鼠标点同一个键盘则完全正常。
  11. //
  12. // 已确认的代码事实(不用日志就能确定的):
  13. // · 文本**真实存放**在 CEditWnd —— 一个原生的 EDIT 子窗口里(WS_CHILD),
  14. // DuiLib 只通过 EN_CHANGE 把内容同步回 CEditUI::m_sText;
  15. // · 该窗口的生命周期**极短**:只要它失去键盘焦点,
  16. // CEditWnd::OnKillFocus 就会 PostMessage(WM_CLOSE) **把它销毁**;
  17. // · 所以"谁在这一刻抢走了焦点"就是本问题的全部答案。
  18. // 一旦焦点离开 EDIT,输入法正在拼的字会被撤销(中文尤其明显),
  19. // 之后到达的字符也无处可去 —— 用户看到的就是"输入完被自动删除"。
  20. //
  21. // 因此下面这几个 helper 只做一件事:把"抢焦点的是谁"记清楚。
  22. // 判读要点:
  23. // · 若 EDIT-KILLFOCUS 的 newTid != selfTid ⇒ 焦点被**别的进程/线程**抢走
  24. // (触摸键盘 TabTip / TextInputHost 是首要嫌疑);
  25. // · newClass 是 "#32770" ⇒ 被我们自己的弹框拿走了;
  26. // · 若同一个 owner 出现 INIT → FIN → INIT 的抖动 ⇒ 窗口在反复重建,
  27. // 重建时用的是 m_sText(可能落后于 IME 的组合状态)⇒ 字就"没了"。
  28. // ==================================================================
  29. static LPCTSTR DiagWndClass(HWND h)
  30. {
  31. static TCHAR s_cls[128];
  32. s_cls[0] = _T('\0');
  33. if (h != NULL && ::IsWindow(h)) ::GetClassName(h, s_cls, _countof(s_cls));
  34. return s_cls;
  35. }
  36. static DWORD DiagWndTid(HWND h)
  37. {
  38. DWORD dwTid = 0;
  39. if (h != NULL && ::IsWindow(h)) dwTid = ::GetWindowThreadProcessId(h, NULL);
  40. return dwTid;
  41. }
  42. // 调 CPaintManagerUI 的"最近一次输入是不是触摸"的判据(见 UIManager.h)。
  43. // 只用于日志标注,不参与任何判断。
  44. class CEditWnd : public CWindowWnd
  45. {
  46. public:
  47. CEditWnd();
  48. void Init(CEditUI* pOwner);
  49. RECT CalPos();
  50. LPCTSTR GetWindowClassName() const;
  51. LPCTSTR GetSuperClassName() const;
  52. void OnFinalMessage(HWND hWnd);
  53. LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
  54. LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
  55. LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
  56. LRESULT OnEditChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
  57. protected:
  58. enum {
  59. DEFAULT_TIMERID = 20,
  60. };
  61. CEditUI* m_pOwner;
  62. HBRUSH m_hBkBrush;
  63. bool m_bInit;
  64. bool m_bDrawCaret;
  65. };
  66. CEditWnd::CEditWnd() : m_pOwner(NULL), m_hBkBrush(NULL), m_bInit(false), m_bDrawCaret(false)
  67. {
  68. }
  69. void CEditWnd::Init(CEditUI* pOwner)
  70. {
  71. m_pOwner = pOwner;
  72. {
  73. CDuiString sOwnerName = pOwner->GetName();
  74. LPCTSTR pszOwnerText = pOwner->GetText();
  75. DuiLibDiag::Log(_T("EDIT-INIT enter owner=%s focused=%d ownerHwnd(old)=%p text='%s' nFocus=%p focusClass=%s focusTid=%u selfTid=%u"),
  76. sOwnerName.GetData(), (int)(pOwner->IsFocused() ? 1 : 0), (void*)pOwner->GetHWND(),
  77. pszOwnerText != NULL ? pszOwnerText : _T(""),
  78. (void*)::GetFocus(), DiagWndClass(::GetFocus()), (unsigned)DiagWndTid(::GetFocus()),
  79. (unsigned)::GetCurrentThreadId());
  80. }
  81. RECT rcPos = CalPos();
  82. UINT uStyle = 0;
  83. if(m_pOwner->GetManager()->IsLayered()) {
  84. uStyle = WS_POPUP | ES_AUTOHSCROLL | WS_VISIBLE;
  85. RECT rcWnd={0};
  86. ::GetWindowRect(m_pOwner->GetManager()->GetPaintWindow(), &rcWnd);
  87. rcPos.left += rcWnd.left;
  88. rcPos.right += rcWnd.left;
  89. rcPos.top += rcWnd.top - 1;
  90. rcPos.bottom += rcWnd.top - 1;
  91. }
  92. else {
  93. uStyle = WS_CHILD | ES_AUTOHSCROLL;
  94. }
  95. UINT uTextStyle = m_pOwner->GetTextStyle();
  96. if(uTextStyle & DT_LEFT) uStyle |= ES_LEFT;
  97. else if(uTextStyle & DT_CENTER) uStyle |= ES_CENTER;
  98. else if(uTextStyle & DT_RIGHT) uStyle |= ES_RIGHT;
  99. if( m_pOwner->IsPasswordMode() ) uStyle |= ES_PASSWORD;
  100. Create(m_pOwner->GetManager()->GetPaintWindow(), NULL, uStyle, 0, rcPos);
  101. HFONT hFont=NULL;
  102. int iFontIndex=m_pOwner->GetFont();
  103. if (iFontIndex!=-1)
  104. hFont = m_pOwner->GetManager()->GetFont(iFontIndex);
  105. if (hFont == NULL)
  106. hFont = m_pOwner->GetManager()->GetDefaultFontInfo()->hFont;
  107. SetWindowFont(m_hWnd, hFont, TRUE);
  108. Edit_LimitText(m_hWnd, m_pOwner->GetMaxChar());
  109. if( m_pOwner->IsPasswordMode() ) Edit_SetPasswordChar(m_hWnd, m_pOwner->GetPasswordChar());
  110. Edit_SetText(m_hWnd, m_pOwner->GetText());
  111. Edit_SetModify(m_hWnd, FALSE);
  112. SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELPARAM(0, 0));
  113. Edit_Enable(m_hWnd, m_pOwner->IsEnabled() == true);
  114. Edit_SetReadOnly(m_hWnd, m_pOwner->IsReadOnly() == true);
  115. //Styls
  116. LONG styleValue = ::GetWindowLong(m_hWnd, GWL_STYLE);
  117. styleValue |= pOwner->GetWindowStyls();
  118. ::SetWindowLong(GetHWND(), GWL_STYLE, styleValue);
  119. //Styls
  120. ::ShowWindow(m_hWnd, SW_SHOWNOACTIVATE);
  121. ::SetFocus(m_hWnd);
  122. if (m_pOwner->IsAutoSelAll()) {
  123. int nSize = GetWindowTextLength(m_hWnd);
  124. if( nSize == 0 ) nSize = 1;
  125. Edit_SetSel(m_hWnd, 0, nSize);
  126. }
  127. else {
  128. int nSize = GetWindowTextLength(m_hWnd);
  129. Edit_SetSel(m_hWnd, nSize, nSize);
  130. }
  131. m_bInit = true;
  132. {
  133. CDuiString sOwnerName = m_pOwner->GetName();
  134. // nFocus 应当就是本窗口;若不等,说明有更晚的 SetFocus 把它抢走了。
  135. // sel=[start,end] 是设给 EDIT 的光标/选择范围:
  136. // 路 B(正常点击)是 [点击处, 点击处];路 A(已聚焦但窗口被销毁后重建)是 [0, 全文] ⇒ 全选。
  137. DWORD dwSelStart = 0, dwSelEnd = 0;
  138. ::SendMessage(m_hWnd, EM_GETSEL, (WPARAM)&dwSelStart, (LPARAM)&dwSelEnd);
  139. DuiLibDiag::Log(_T("EDIT-INIT done owner=%s hwnd=%p nFocus=%p focusIsSelf=%d sel=[%u,%u] text='%s'"),
  140. sOwnerName.GetData(), (void*)m_hWnd, (void*)::GetFocus(),
  141. (int)(::GetFocus() == m_hWnd ? 1 : 0),
  142. (unsigned)LOWORD(dwSelStart), (unsigned)LOWORD(dwSelEnd), m_pOwner->GetText());
  143. }
  144. }
  145. RECT CEditWnd::CalPos()
  146. {
  147. CDuiRect rcPos = m_pOwner->GetPos();
  148. RECT rcInset = m_pOwner->GetTextPadding();
  149. rcPos.left += rcInset.left;
  150. rcPos.top += rcInset.top;
  151. rcPos.right -= rcInset.right;
  152. rcPos.bottom -= rcInset.bottom;
  153. LONG lEditHeight = m_pOwner->GetManager()->GetFontInfo(m_pOwner->GetFont())->tm.tmHeight;
  154. if( lEditHeight < rcPos.GetHeight() ) {
  155. rcPos.top += (rcPos.GetHeight() - lEditHeight) / 2;
  156. rcPos.bottom = rcPos.top + lEditHeight;
  157. }
  158. CControlUI* pParent = m_pOwner;
  159. RECT rcParent;
  160. while( pParent = pParent->GetParent() ) {
  161. if( !pParent->IsVisible() ) {
  162. rcPos.left = rcPos.top = rcPos.right = rcPos.bottom = 0;
  163. break;
  164. }
  165. rcParent = pParent->GetClientPos();
  166. if( !::IntersectRect(&rcPos, &rcPos, &rcParent) ) {
  167. rcPos.left = rcPos.top = rcPos.right = rcPos.bottom = 0;
  168. break;
  169. }
  170. }
  171. return rcPos;
  172. }
  173. LPCTSTR CEditWnd::GetWindowClassName() const
  174. {
  175. return _T("EditWnd");
  176. }
  177. LPCTSTR CEditWnd::GetSuperClassName() const
  178. {
  179. return WC_EDIT;
  180. }
  181. void CEditWnd::OnFinalMessage(HWND hWnd)
  182. {
  183. {
  184. CDuiString sOwnerName = (m_pOwner != NULL) ? m_pOwner->GetName() : CDuiString(_T("?"));
  185. LPCTSTR pszText = (m_pOwner != NULL) ? m_pOwner->GetText() : (LPCTSTR)NULL;
  186. // 同一个 owner 反复出现 FIN → INIT ⇒ 窗口在抖动重建;
  187. // 重建走的是 m_sText,而 m_sText 可能落后于 IME 尚未提交的组合串 ⇒ 字"没了"。
  188. DuiLibDiag::Log(_T("EDIT-FIN hwnd=%p owner=%s text='%s' nFocus=%p"),
  189. (void*)hWnd, sOwnerName.GetData(), pszText != NULL ? pszText : _T(""),
  190. (void*)::GetFocus());
  191. }
  192. m_pOwner->Invalidate();
  193. // Clear reference and die
  194. if( m_hBkBrush != NULL ) ::DeleteObject(m_hBkBrush);
  195. if (m_pOwner->GetManager()->IsLayered()) {
  196. m_pOwner->GetManager()->RemoveNativeWindow(hWnd);
  197. }
  198. m_pOwner->m_pWindow = NULL;
  199. delete this;
  200. }
  201. LRESULT CEditWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  202. {
  203. LRESULT lRes = 0;
  204. BOOL bHandled = TRUE;
  205. if( uMsg == WM_CREATE ) {
  206. bHandled = FALSE;
  207. }
  208. else if( uMsg == WM_KILLFOCUS ) lRes = OnKillFocus(uMsg, wParam, lParam, bHandled);
  209. else if( uMsg == WM_CLOSE ) lRes = OnClose(uMsg, wParam, lParam, bHandled);
  210. else if( uMsg == OCM_COMMAND ) {
  211. if( GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE ) lRes = OnEditChanged(uMsg, wParam, lParam, bHandled);
  212. else if( GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE ) {
  213. RECT rcClient;
  214. ::GetClientRect(m_hWnd, &rcClient);
  215. ::InvalidateRect(m_hWnd, &rcClient, FALSE);
  216. }
  217. }
  218. else if( uMsg == WM_KEYDOWN && TCHAR(wParam) == VK_RETURN ){
  219. m_pOwner->GetManager()->SendNotify(m_pOwner, DUI_MSGTYPE_RETURN);
  220. }
  221. else if( uMsg == WM_KEYDOWN && TCHAR(wParam) == VK_TAB ){
  222. if (m_pOwner->GetManager()->IsLayered()) {
  223. m_pOwner->GetManager()->SetNextTabControl();
  224. }
  225. }
  226. else if( uMsg == OCM__BASE + WM_CTLCOLOREDIT || uMsg == OCM__BASE + WM_CTLCOLORSTATIC ) {
  227. ::SetBkMode((HDC)wParam, TRANSPARENT);
  228. DWORD dwTextColor = m_pOwner->GetTextColor();
  229. ::SetTextColor((HDC)wParam, RGB(GetBValue(dwTextColor),GetGValue(dwTextColor),GetRValue(dwTextColor)));
  230. DWORD clrColor = m_pOwner->GetNativeEditBkColor();
  231. if (clrColor < 0xFF000000) {
  232. if (m_hBkBrush != NULL) ::DeleteObject(m_hBkBrush);
  233. RECT rcWnd = m_pOwner->GetManager()->GetNativeWindowRect(m_hWnd);
  234. HBITMAP hBmpEditBk = CRenderEngine::GenerateBitmap(m_pOwner->GetManager(), rcWnd, m_pOwner, clrColor);
  235. m_hBkBrush = ::CreatePatternBrush(hBmpEditBk);
  236. ::DeleteObject(hBmpEditBk);
  237. }
  238. else {
  239. if (m_hBkBrush == NULL) {
  240. m_hBkBrush = ::CreateSolidBrush(RGB(GetBValue(clrColor), GetGValue(clrColor), GetRValue(clrColor)));
  241. }
  242. }
  243. return (LRESULT)m_hBkBrush;
  244. }
  245. else if( uMsg == WM_PAINT) {
  246. bHandled = FALSE;
  247. }
  248. else if( uMsg == WM_PRINT ) {
  249. bHandled = FALSE;
  250. }
  251. else if( uMsg == WM_TIMER ) {
  252. if (wParam == CARET_TIMERID) {
  253. m_bDrawCaret = !m_bDrawCaret;
  254. RECT rcClient;
  255. ::GetClientRect(m_hWnd, &rcClient);
  256. ::InvalidateRect(m_hWnd, &rcClient, FALSE);
  257. return 0;
  258. }
  259. bHandled = FALSE;
  260. }
  261. else bHandled = FALSE;
  262. if( !bHandled ) return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
  263. return lRes;
  264. }
  265. LRESULT CEditWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  266. {
  267. LRESULT lRes = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
  268. {
  269. CDuiString sOwnerName = (m_pOwner != NULL) ? m_pOwner->GetName() : CDuiString(_T("?"));
  270. HWND hNewFocus = ::GetFocus();
  271. const DWORD dwSelfTid = ::GetCurrentThreadId();
  272. const DWORD dwNewTid = DiagWndTid(hNewFocus);
  273. // ★ 这条就是"输入法正在拼的字会不会消失"的现场。
  274. // 失焦本身不致命 —— 致命的是"失焦即销毁"叠加"瞬态失焦"(见 OnClose)。
  275. // 判读:
  276. // · newFocus = 00000000 ⇒ 本线程此刻**没有**焦点窗口。
  277. // 真实成因只有两种:整个窗口失活(触摸键盘抢前台时的典型形态),
  278. // 或者焦点正在搬家途中。此时 sameProc 无意义(下面记成 -1,
  279. // 别再把它读成"被别的进程抢走"——NULL 没有进程可言)。
  280. // · newFocus != NULL 且 sameProc=0 ⇒ 真被**别的进程/线程**抢走;
  281. // · newClass = EditWnd ⇒ 我们自己的另一个输入框接走了焦点(正常切框);
  282. // · newClass = #32770 ⇒ 被本线程的弹框本体拿走(窗口内部搬家)。
  283. DuiLibDiag::Log(_T("EDIT-KILLFOCUS hwnd=%p owner=%s newFocus=%p newClass=%s newTid=%u selfTid=%u sameProc=%d -> 投递 WM_CLOSE(去留由 OnClose 复核)"),
  284. (void*)m_hWnd, sOwnerName.GetData(), (void*)hNewFocus, DiagWndClass(hNewFocus),
  285. (unsigned)dwNewTid, (unsigned)dwSelfTid,
  286. (int)(hNewFocus == NULL ? -1 : ((dwNewTid == dwSelfTid) ? 1 : 0)));
  287. }
  288. PostMessage(WM_CLOSE);
  289. return lRes;
  290. }
  291. // ★ 修复(2026-09-23 · 「手指点触摸键盘输入不进去」)
  292. //
  293. // 背景(真机日志实证,一次"手指点输入框"就够看清楚):
  294. //
  295. // PTR-DOWN stealFocus ::SetFocus(m_hWndPaint) ← UIManager.cpp 抢走键盘焦点
  296. // EDIT-KILLFOCUS PostMessage(WM_CLOSE) ← 销毁请求**排队**(异步)
  297. // EDITUI-SETFOCUS 复用编辑窗 ::SetFocus(hEdit) ← 焦点已经还给 EDIT
  298. // …… 几十毫秒后 ……
  299. // EDIT-FIN 窗口还是被那条排队的 WM_CLOSE 销毁了
  300. //
  301. // 也就是说:**焦点刚被抢回来,窗口照旧死掉**。而 CPaintManagerUI::SetFocus 有
  302. // `pControl == m_pFocus` 早退,不会再发 UIEVENT_SETFOCUS ⇒ 窗口不再重建。
  303. // 触摸键盘敲出的字符、退格、输入法候选词的提交,就全都失去了落点
  304. // (真机日志里对应"9.7 秒零条 EDIT-CHANGE")。
  305. //
  306. // "手指点虚拟键盘"之所以必现、鼠标不犯,是因为手指点它的那一刻本窗口会短暂失活
  307. // (WND-ACTIVATE wParam=0(INACTIVE) fg=00000000 + EDIT-KILLFOCUS newFocus=00000000),
  308. // 于是又排上一条 WM_CLOSE;鼠标那一路不会走到这里。
  309. //
  310. // ⇒ 判据(纯状态、零时间戳):**延后到 WM_CLOSE 真正被取出来处理时**,再看一眼
  311. // 「我的 owner 现在还是不是 DuiLib 的焦点控件」:
  312. //
  313. // · 还是 ⇒ 这次失焦只是**瞬态**(窗口内部焦点搬家 / 本窗口短暂失活),
  314. // owner 还要接着输入 ⇒ 取消销毁,窗口活着等 SETFOCUS 回来;
  315. // · 不是 ⇒ 焦点已被别的控件/别的窗口拿走,或者控件被隐藏/移除、弹框正在销毁
  316. // (那些路径都会先 SetFocus(NULL) 或把 m_pFocus 置 NULL)
  317. // ⇒ 按原设计销毁。
  318. //
  319. // 为什么必须延后:CPaintManagerUI::SetFocus 的顺序是
  320. // 「先 ::SetFocus(画布窗口) 让旧编辑窗失焦 → 再派发 KILLFOCUS → 最后才把 m_pFocus 换成新控件」。
  321. // 若在原生 WM_KILLFOCUS 里当场判断,读到的一定还是**旧值** ⇒ 区分不了
  322. // "瞬态失焦"和"真的切到另一个控件"。
  323. //
  324. // 这条判据对"窗口失活"同样成立 —— 而窗口失活恰恰是手指点触摸键盘时唯一会发生的变数。
  325. LRESULT CEditWnd::OnClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  326. {
  327. CPaintManagerUI* pManager = (m_pOwner != NULL) ? m_pOwner->GetManager() : NULL;
  328. CControlUI* pMgrFocus = (pManager != NULL) ? pManager->GetFocus() : (CControlUI*)NULL;
  329. const bool bOwnerKeepsFocus = (pManager != NULL && pMgrFocus == m_pOwner);
  330. {
  331. CDuiString sOwnerName = (m_pOwner != NULL) ? m_pOwner->GetName() : CDuiString(_T("?"));
  332. DuiLibDiag::Log(_T("EDIT-CLOSE hwnd=%p owner=%s mgrFocus=%p ownerKeepsFocus=%d -> %s"),
  333. (void*)m_hWnd, sOwnerName.GetData(), (void*)pMgrFocus,
  334. (int)(bOwnerKeepsFocus ? 1 : 0),
  335. bOwnerKeepsFocus ? _T("取消销毁(瞬态失焦,窗口保留)") : _T("销毁"));
  336. }
  337. bHandled = TRUE;
  338. if (bOwnerKeepsFocus)
  339. {
  340. // 不销毁。焦点会由下面两条既有路径送回来,不需要在这里造新机制:
  341. // · 我们自己的 ::SetFocus(m_hWndPaint) → WM_SETFOCUS → 重发 UIEVENT_SETFOCUS
  342. // → CEditUI::DoEvent 的"复用编辑窗"分支 Show+SetFocus;
  343. // · 整个窗口重新被激活 → MessageHandler 的 WM_ACTIVATE 同理。
  344. return 0;
  345. }
  346. ::DestroyWindow(m_hWnd);
  347. return 0;
  348. }
  349. LRESULT CEditWnd::OnEditChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  350. {
  351. if( !m_bInit ) return 0;
  352. if( m_pOwner == NULL ) return 0;
  353. // Copy text back
  354. int cchLen = ::GetWindowTextLength(m_hWnd) + 1;
  355. LPTSTR pstr = static_cast<LPTSTR>(_alloca(cchLen * sizeof(TCHAR)));
  356. ASSERT(pstr);
  357. if( pstr == NULL ) return 0;
  358. ::GetWindowText(m_hWnd, pstr, cchLen);
  359. m_pOwner->m_sText = pstr;
  360. {
  361. CDuiString sOwnerName = m_pOwner->GetName();
  362. // 每一次内容变化都留痕 —— "字符进来"与"被清空"都能看到。
  363. // 判读:
  364. // · append 之后突然出现 len=0 ⇒ 有代码把它清掉了。可能是业务侧的
  365. // 数字过滤(首个字符非法时 SetText("")),也可能是 CEditUI::SetText
  366. // 被调用 —— 后者会在 EDITUI-SETTEXT 行留下记录;
  367. // · 只看到 append、从没出现 len=0,用户却说"字没了"
  368. // ⇒ 字符根本没进到这个窗口 ⇒ 焦点已经不在它身上(查 EDIT-KILLFOCUS)。
  369. DuiLibDiag::Log(_T("EDIT-CHANGE hwnd=%p owner=%s len=%d text='%s'"),
  370. (void*)m_hWnd, sOwnerName.GetData(), cchLen - 1, pstr);
  371. }
  372. m_pOwner->GetManager()->SendNotify(m_pOwner, DUI_MSGTYPE_TEXTCHANGED);
  373. if( m_pOwner->GetManager()->IsLayered() ) m_pOwner->Invalidate();
  374. return 0;
  375. }
  376. /////////////////////////////////////////////////////////////////////////////////////
  377. //
  378. //
  379. IMPLEMENT_DUICONTROL(CEditUI)
  380. CEditUI::CEditUI() : m_pWindow(NULL), m_uMaxChar(255), m_bReadOnly(false),
  381. m_bPasswordMode(false), m_cPasswordChar(_T('*')), m_bAutoSelAll(false), m_uButtonState(0),
  382. m_dwEditbkColor(0xFFFFFFFF), m_dwEditTextColor(0x00000000), m_iWindowStyls(0),m_dwTipValueColor(0xFFBAC0C5)
  383. {
  384. SetTextPadding(CDuiRect(4, 3, 4, 3));
  385. SetBkColor(0xFFFFFFFF);
  386. }
  387. LPCTSTR CEditUI::GetClass() const
  388. {
  389. return _T("EditUI");
  390. }
  391. LPVOID CEditUI::GetInterface(LPCTSTR pstrName)
  392. {
  393. if( _tcsicmp(pstrName, DUI_CTR_EDIT) == 0 ) return static_cast<CEditUI*>(this);
  394. return CLabelUI::GetInterface(pstrName);
  395. }
  396. UINT CEditUI::GetControlFlags() const
  397. {
  398. if( !IsEnabled() ) return CControlUI::GetControlFlags();
  399. return UIFLAG_SETCURSOR | UIFLAG_TABSTOP;
  400. }
  401. void CEditUI::DoEvent(TEventUI& event)
  402. {
  403. // 【诊断 · 新建商品弹框「输入文字消失」】只对焦点/点击类事件留痕,避免鼠标移动刷屏。
  404. //
  405. // 为什么这条日志值得存在:本控件后面那个 `if( m_pWindow ) return;` 会让
  406. // UIEVENT_SETFOCUS **提前返回**,从而走不到 CLabelUI::DoEvent 里的
  407. // `m_bFocused = true` —— 于是 IsFocused() 与真实焦点可能脱节。
  408. // 而下面 UIEVENT_BUTTONDOWN 的三岔口正是**用 IsFocused() 分流**的:
  409. // · IsFocused() && m_pWindow == NULL ⇒ 路 A:新建窗口 + **全选整框**
  410. // · m_pWindow != NULL ⇒ 路 B:光标落到点击处(正常点击)
  411. // · 两者都不满足 ⇒ 什么都不做(点击被静默丢弃)
  412. // m_bFocused / m_pWindow 这两个值必须能在日志里直接看到,否则无从判断走了哪条。
  413. if (event.Type == UIEVENT_SETFOCUS
  414. || event.Type == UIEVENT_KILLFOCUS
  415. || event.Type == UIEVENT_BUTTONDOWN
  416. || event.Type == UIEVENT_BUTTONUP
  417. || event.Type == UIEVENT_TOUCHDOWN
  418. || event.Type == UIEVENT_TOUCHUP
  419. || event.Type == UIEVENT_DBLCLICK
  420. || event.Type == UIEVENT_RBUTTONDOWN)
  421. {
  422. CDuiString sName = GetName();
  423. DuiLibDiag::Log(_T("EDITUI-EVENT type=%d owner=%s m_bFocused=%d m_pWindow=%p editHwnd=%p pt=%d,%d nFocus=%p mgrHwnd=%p"),
  424. event.Type, sName.GetData(), (int)(m_bFocused ? 1 : 0), (void*)m_pWindow,
  425. (void*)GetHWND(), event.ptMouse.x, event.ptMouse.y,
  426. (void*)::GetFocus(), (void*)(m_pManager != NULL ? m_pManager->GetPaintWindow() : (HWND)NULL));
  427. }
  428. if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
  429. if( m_pParent != NULL ) m_pParent->DoEvent(event);
  430. else CLabelUI::DoEvent(event);
  431. return;
  432. }
  433. if( event.Type == UIEVENT_SETCURSOR && IsEnabled() )
  434. {
  435. ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM)));
  436. return;
  437. }
  438. if( event.Type == UIEVENT_WINDOWSIZE )
  439. {
  440. //if( m_pWindow != NULL ) m_pManager->SetFocusNeeded(this);
  441. }
  442. if( event.Type == UIEVENT_SCROLLWHEEL )
  443. {
  444. if( m_pWindow != NULL ) return;
  445. }
  446. if( event.Type == UIEVENT_SETFOCUS && IsEnabled() )
  447. {
  448. if( m_pWindow != NULL )
  449. {
  450. // ★ 修复(见本文件顶部「输入文字消失」说明)
  451. //
  452. // 这里是原来的 `if( m_pWindow ) return;`。它有两个后果:
  453. // (1) 走不到 CLabelUI::DoEvent,`m_bFocused = true` 永不执行
  454. // ⇒ IsFocused() 与真实焦点长期脱节(下面 BUTTONDOWN 曾用它分流);
  455. // (2) 编辑窗口**还在**、但键盘焦点已被系统/输入法/触摸键盘挪走时,
  456. // 没有任何代码把焦点还回去 —— 而所有按键(含退格)与输入法
  457. // 候选词的提交都必须依赖它。这正是"切换过一次输入框后
  458. // 连退格都输不进去"的那一格。
  459. //
  460. // 既然 DuiLib 的焦点此刻仍指向本控件(否则收不到 SETFOCUS),
  461. // 就把显示与键盘焦点一并还给它。
  462. HWND hEdit = *m_pWindow;
  463. if (hEdit != NULL && ::IsWindow(hEdit))
  464. {
  465. if (!::IsWindowVisible(hEdit)) ::ShowWindow(hEdit, SW_SHOWNOACTIVATE);
  466. if (::GetFocus() != hEdit) ::SetFocus(hEdit);
  467. }
  468. {
  469. CDuiString sName = GetName();
  470. DuiLibDiag::Log(_T("EDITUI-SETFOCUS 复用编辑窗 owner=%s editHwnd=%p nFocus=%p -> Show+SetFocus"),
  471. sName.GetData(), (void*)hEdit, (void*)::GetFocus());
  472. }
  473. CLabelUI::DoEvent(event); // 让基类补上 m_bFocused = true
  474. Invalidate();
  475. return;
  476. }
  477. m_pWindow = new CEditWnd();
  478. ASSERT(m_pWindow);
  479. m_pWindow->Init(this);
  480. Invalidate();
  481. }
  482. if( event.Type == UIEVENT_KILLFOCUS && IsEnabled() )
  483. {
  484. Invalidate();
  485. }
  486. if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK || event.Type == UIEVENT_RBUTTONDOWN)
  487. {
  488. if( IsEnabled() ) {
  489. GetManager()->ReleaseCapture();
  490. // ★ 修复:判据原来还多要求一个 `IsFocused()`。但那个标志由
  491. // CLabelUI::DoEvent 维护,而上面的 SETFOCUS 分支在"编辑窗已存在"
  492. // 时会早退、根本走不到那里 ⇒ IsFocused() 可能长期为假。
  493. // 于是"点击一个还没有编辑窗的输入框"时**两条分支都不走**:
  494. // 点击被静默丢弃(体感就是"点了没反应")。
  495. // 现在只看"有没有编辑窗" —— 点了输入框就一定拿得到输入能力。
  496. if( m_pWindow == NULL )
  497. {
  498. CDuiString sName = GetName();
  499. DuiLibDiag::Log(_T("EDITUI-BUTTONDOWN 无编辑窗 -> 新建并全选 owner=%s m_bFocused=%d"),
  500. sName.GetData(), (int)(m_bFocused ? 1 : 0));
  501. m_pWindow = new CEditWnd();
  502. ASSERT(m_pWindow);
  503. m_pWindow->Init(this);
  504. if( PtInRect(&m_rcItem, event.ptMouse) )
  505. {
  506. int nSize = GetWindowTextLength(*m_pWindow);
  507. if( nSize == 0 ) nSize = 1;
  508. Edit_SetSel(*m_pWindow, 0, nSize);
  509. }
  510. }
  511. else
  512. {
  513. CDuiString sName = GetName();
  514. DuiLibDiag::Log(_T("EDITUI-BUTTONDOWN 有编辑窗 -> 光标落到点击处 owner=%s pt=%d,%d autoSelAll=%d"),
  515. sName.GetData(), event.ptMouse.x, event.ptMouse.y, (int)(m_bAutoSelAll ? 1 : 0));
  516. if (!m_bAutoSelAll) {
  517. RECT rcTextPadding = GetTextPadding();
  518. POINT pt = event.ptMouse;
  519. pt.x -= m_rcItem.left + rcTextPadding.left;
  520. pt.y -= m_rcItem.top + rcTextPadding.top;
  521. Edit_SetSel(*m_pWindow, 0, 0);
  522. ::SendMessage(*m_pWindow, WM_LBUTTONDOWN, event.wParam, MAKELPARAM(pt.x, pt.y));
  523. }
  524. }
  525. }
  526. return;
  527. }
  528. if( event.Type == UIEVENT_MOUSEMOVE )
  529. {
  530. return;
  531. }
  532. if( event.Type == UIEVENT_BUTTONUP )
  533. {
  534. return;
  535. }
  536. if( event.Type == UIEVENT_CONTEXTMENU )
  537. {
  538. return;
  539. }
  540. if( event.Type == UIEVENT_MOUSEENTER )
  541. {
  542. if( ::PtInRect(&m_rcItem, event.ptMouse ) ) {
  543. if( IsEnabled() ) {
  544. if( (m_uButtonState & UISTATE_HOT) == 0 ) {
  545. m_uButtonState |= UISTATE_HOT;
  546. Invalidate();
  547. }
  548. }
  549. }
  550. }
  551. if( event.Type == UIEVENT_MOUSELEAVE )
  552. {
  553. if( IsEnabled() ) {
  554. m_uButtonState &= ~UISTATE_HOT;
  555. Invalidate();
  556. }
  557. return;
  558. }
  559. CLabelUI::DoEvent(event);
  560. }
  561. void CEditUI::SetEnabled(bool bEnable)
  562. {
  563. CControlUI::SetEnabled(bEnable);
  564. if( !IsEnabled() ) {
  565. m_uButtonState = 0;
  566. }
  567. }
  568. void CEditUI::SetText(LPCTSTR pstrText)
  569. {
  570. {
  571. CDuiString sName = GetName();
  572. // 谁在改文本、改成什么。业务侧的数字过滤在"首个字符非法"时会
  573. // SetText("") —— 那会把整框内容清空(正是"输入完被自动删除"的一种表现)。
  574. // 注意:本函数无论编辑窗在不在都会执行,m_pWindow=NULL 时只改 m_sText,
  575. // 那个新值要等下次 CEditWnd::Init 才会真正写进 EDIT。
  576. DuiLibDiag::Log(_T("EDITUI-SETTEXT owner=%s m_pWindow=%p old='%s' new='%s'"),
  577. sName.GetData(), (void*)m_pWindow,
  578. m_sText.GetData(), pstrText != NULL ? pstrText : _T(""));
  579. }
  580. m_sText = pstrText;
  581. if( m_pWindow != NULL ) Edit_SetText(*m_pWindow, m_sText);
  582. Invalidate();
  583. }
  584. void CEditUI::SetMaxChar(UINT uMax)
  585. {
  586. m_uMaxChar = uMax;
  587. if( m_pWindow != NULL ) Edit_LimitText(*m_pWindow, m_uMaxChar);
  588. }
  589. UINT CEditUI::GetMaxChar()
  590. {
  591. return m_uMaxChar;
  592. }
  593. void CEditUI::SetReadOnly(bool bReadOnly)
  594. {
  595. if( m_bReadOnly == bReadOnly ) return;
  596. m_bReadOnly = bReadOnly;
  597. if( m_pWindow != NULL ) Edit_SetReadOnly(*m_pWindow, m_bReadOnly);
  598. Invalidate();
  599. }
  600. bool CEditUI::IsReadOnly() const
  601. {
  602. return m_bReadOnly;
  603. }
  604. void CEditUI::SetNumberOnly(bool bNumberOnly)
  605. {
  606. if( bNumberOnly )
  607. {
  608. m_iWindowStyls |= ES_NUMBER;
  609. }
  610. else
  611. {
  612. m_iWindowStyls &= ~ES_NUMBER;
  613. }
  614. }
  615. bool CEditUI::IsNumberOnly() const
  616. {
  617. return (m_iWindowStyls & ES_NUMBER) ? true:false;
  618. }
  619. int CEditUI::GetWindowStyls() const
  620. {
  621. return m_iWindowStyls;
  622. }
  623. void CEditUI::SetPasswordMode(bool bPasswordMode)
  624. {
  625. if( m_bPasswordMode == bPasswordMode ) return;
  626. m_bPasswordMode = bPasswordMode;
  627. Invalidate();
  628. if( m_pWindow != NULL ) {
  629. LONG styleValue = ::GetWindowLong(*m_pWindow, GWL_STYLE);
  630. bPasswordMode ? styleValue |= ES_PASSWORD : styleValue &= ~ES_PASSWORD;
  631. ::SetWindowLong(*m_pWindow, GWL_STYLE, styleValue);
  632. }
  633. }
  634. bool CEditUI::IsPasswordMode() const
  635. {
  636. return m_bPasswordMode;
  637. }
  638. void CEditUI::SetPasswordChar(TCHAR cPasswordChar)
  639. {
  640. if( m_cPasswordChar == cPasswordChar ) return;
  641. m_cPasswordChar = cPasswordChar;
  642. if( m_pWindow != NULL ) Edit_SetPasswordChar(*m_pWindow, m_cPasswordChar);
  643. Invalidate();
  644. }
  645. TCHAR CEditUI::GetPasswordChar() const
  646. {
  647. return m_cPasswordChar;
  648. }
  649. LPCTSTR CEditUI::GetNormalImage()
  650. {
  651. return m_sNormalImage;
  652. }
  653. void CEditUI::SetNormalImage(LPCTSTR pStrImage)
  654. {
  655. m_sNormalImage = pStrImage;
  656. Invalidate();
  657. }
  658. LPCTSTR CEditUI::GetHotImage()
  659. {
  660. return m_sHotImage;
  661. }
  662. void CEditUI::SetHotImage(LPCTSTR pStrImage)
  663. {
  664. m_sHotImage = pStrImage;
  665. Invalidate();
  666. }
  667. LPCTSTR CEditUI::GetFocusedImage()
  668. {
  669. return m_sFocusedImage;
  670. }
  671. void CEditUI::SetFocusedImage(LPCTSTR pStrImage)
  672. {
  673. m_sFocusedImage = pStrImage;
  674. Invalidate();
  675. }
  676. LPCTSTR CEditUI::GetDisabledImage()
  677. {
  678. return m_sDisabledImage;
  679. }
  680. void CEditUI::SetDisabledImage(LPCTSTR pStrImage)
  681. {
  682. m_sDisabledImage = pStrImage;
  683. Invalidate();
  684. }
  685. void CEditUI::SetNativeEditBkColor(DWORD dwBkColor)
  686. {
  687. m_dwEditbkColor = dwBkColor;
  688. }
  689. DWORD CEditUI::GetNativeEditBkColor() const
  690. {
  691. return m_dwEditbkColor;
  692. }
  693. void CEditUI::SetNativeEditTextColor( LPCTSTR pStrColor )
  694. {
  695. if( *pStrColor == _T('#')) pStrColor = ::CharNext(pStrColor);
  696. LPTSTR pstr = NULL;
  697. DWORD clrColor = _tcstoul(pStrColor, &pstr, 16);
  698. m_dwEditTextColor = clrColor;
  699. }
  700. DWORD CEditUI::GetNativeEditTextColor() const
  701. {
  702. return m_dwEditTextColor;
  703. }
  704. bool CEditUI::IsAutoSelAll()
  705. {
  706. return m_bAutoSelAll;
  707. }
  708. void CEditUI::SetAutoSelAll(bool bAutoSelAll)
  709. {
  710. m_bAutoSelAll = bAutoSelAll;
  711. }
  712. void CEditUI::SetSel(long nStartChar, long nEndChar)
  713. {
  714. if( m_pWindow != NULL ) Edit_SetSel(*m_pWindow, nStartChar,nEndChar);
  715. }
  716. void CEditUI::SetSelAll()
  717. {
  718. SetSel(0,-1);
  719. }
  720. void CEditUI::SetReplaceSel(LPCTSTR lpszReplace)
  721. {
  722. if( m_pWindow != NULL ) Edit_ReplaceSel(*m_pWindow, lpszReplace);
  723. }
  724. void CEditUI::SetTipValue( LPCTSTR pStrTipValue )
  725. {
  726. m_sTipValue = pStrTipValue;
  727. }
  728. LPCTSTR CEditUI::GetTipValue()
  729. {
  730. if (!IsResourceText()) return m_sTipValue;
  731. return CResourceManager::GetInstance()->GetText(m_sTipValue);
  732. }
  733. void CEditUI::SetTipValueColor( LPCTSTR pStrColor )
  734. {
  735. if( *pStrColor == _T('#')) pStrColor = ::CharNext(pStrColor);
  736. LPTSTR pstr = NULL;
  737. DWORD clrColor = _tcstoul(pStrColor, &pstr, 16);
  738. m_dwTipValueColor = clrColor;
  739. }
  740. DWORD CEditUI::GetTipValueColor()
  741. {
  742. return m_dwTipValueColor;
  743. }
  744. HWND CEditUI::GetHWND()
  745. {
  746. if(m_pWindow != NULL) {
  747. return m_pWindow->GetHWND();
  748. }
  749. return NULL;
  750. }
  751. void CEditUI::SetPos(RECT rc, bool bNeedInvalidate)
  752. {
  753. CControlUI::SetPos(rc, bNeedInvalidate);
  754. if( m_pWindow != NULL ) {
  755. RECT rcPos = m_pWindow->CalPos();
  756. ::SetWindowPos(m_pWindow->GetHWND(), NULL, rcPos.left, rcPos.top, rcPos.right - rcPos.left,
  757. rcPos.bottom - rcPos.top, SWP_NOZORDER | SWP_NOACTIVATE);
  758. }
  759. }
  760. void CEditUI::Move(SIZE szOffset, bool bNeedInvalidate)
  761. {
  762. CControlUI::Move(szOffset, bNeedInvalidate);
  763. if( m_pWindow != NULL ) {
  764. RECT rcPos = m_pWindow->CalPos();
  765. ::SetWindowPos(m_pWindow->GetHWND(), NULL, rcPos.left, rcPos.top, rcPos.right - rcPos.left,
  766. rcPos.bottom - rcPos.top, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  767. }
  768. }
  769. void CEditUI::SetVisible(bool bVisible)
  770. {
  771. CControlUI::SetVisible(bVisible);
  772. if( !IsVisible() && m_pWindow != NULL ) m_pManager->SetFocus(NULL);
  773. }
  774. void CEditUI::SetInternVisible(bool bVisible)
  775. {
  776. if( !IsVisible() && m_pWindow != NULL ) m_pManager->SetFocus(NULL);
  777. }
  778. SIZE CEditUI::EstimateSize(SIZE szAvailable)
  779. {
  780. if( m_cxyFixed.cy == 0 ) return CDuiSize(m_cxyFixed.cx, m_pManager->GetFontInfo(GetFont())->tm.tmHeight + 6);
  781. return CControlUI::EstimateSize(szAvailable);
  782. }
  783. void CEditUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
  784. {
  785. if( _tcsicmp(pstrName, _T("readonly")) == 0 ) SetReadOnly(_tcsicmp(pstrValue, _T("true")) == 0);
  786. else if( _tcsicmp(pstrName, _T("numberonly")) == 0 ) SetNumberOnly(_tcsicmp(pstrValue, _T("true")) == 0);
  787. else if( _tcscmp(pstrName, _T("autoselall")) == 0 ) SetAutoSelAll(_tcscmp(pstrValue, _T("true")) == 0);
  788. else if( _tcsicmp(pstrName, _T("password")) == 0 ) SetPasswordMode(_tcsicmp(pstrValue, _T("true")) == 0);
  789. else if( _tcsicmp(pstrName, _T("passwordchar")) == 0 ) SetPasswordChar(*pstrValue);
  790. else if( _tcsicmp(pstrName, _T("maxchar")) == 0 ) SetMaxChar(_ttoi(pstrValue));
  791. else if( _tcsicmp(pstrName, _T("normalimage")) == 0 ) SetNormalImage(pstrValue);
  792. else if( _tcsicmp(pstrName, _T("hotimage")) == 0 ) SetHotImage(pstrValue);
  793. else if( _tcsicmp(pstrName, _T("focusedimage")) == 0 ) SetFocusedImage(pstrValue);
  794. else if( _tcsicmp(pstrName, _T("disabledimage")) == 0 ) SetDisabledImage(pstrValue);
  795. else if( _tcsicmp(pstrName, _T("tipvalue")) == 0 ) SetTipValue(pstrValue);
  796. else if( _tcsicmp(pstrName, _T("tipvaluecolor")) == 0 ) SetTipValueColor(pstrValue);
  797. else if( _tcsicmp(pstrName, _T("nativetextcolor")) == 0 ) SetNativeEditTextColor(pstrValue);
  798. else if( _tcsicmp(pstrName, _T("nativebkcolor")) == 0 ) {
  799. if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);
  800. LPTSTR pstr = NULL;
  801. DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
  802. SetNativeEditBkColor(clrColor);
  803. }
  804. else CLabelUI::SetAttribute(pstrName, pstrValue);
  805. }
  806. void CEditUI::PaintStatusImage(HDC hDC)
  807. {
  808. if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED;
  809. else m_uButtonState &= ~ UISTATE_FOCUSED;
  810. if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED;
  811. else m_uButtonState &= ~ UISTATE_DISABLED;
  812. if( (m_uButtonState & UISTATE_DISABLED) != 0 ) {
  813. if( !m_sDisabledImage.IsEmpty() ) {
  814. if( !DrawImage(hDC, (LPCTSTR)m_sDisabledImage) ) {}
  815. else return;
  816. }
  817. }
  818. else if( (m_uButtonState & UISTATE_FOCUSED) != 0 ) {
  819. if( !m_sFocusedImage.IsEmpty() ) {
  820. if( !DrawImage(hDC, (LPCTSTR)m_sFocusedImage) ) {}
  821. else return;
  822. }
  823. }
  824. else if( (m_uButtonState & UISTATE_HOT) != 0 ) {
  825. if( !m_sHotImage.IsEmpty() ) {
  826. if( !DrawImage(hDC, (LPCTSTR)m_sHotImage) ) {}
  827. else return;
  828. }
  829. }
  830. if( !m_sNormalImage.IsEmpty() ) {
  831. if( !DrawImage(hDC, (LPCTSTR)m_sNormalImage) ) {}
  832. else return;
  833. }
  834. }
  835. void CEditUI::PaintText(HDC hDC)
  836. {
  837. DWORD mCurTextColor = m_dwTextColor;
  838. if( m_dwTextColor == 0 ) mCurTextColor = m_dwTextColor = m_pManager->GetDefaultFontColor();
  839. if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor();
  840. CDuiString sDrawText = GetText();
  841. CDuiString sTipValue = GetTipValue();
  842. if(sDrawText == sTipValue || sDrawText == _T("")) {
  843. mCurTextColor = m_dwTipValueColor;
  844. sDrawText = sTipValue;
  845. }
  846. else {
  847. CDuiString sTemp = sDrawText;
  848. if( m_bPasswordMode ) {
  849. sDrawText.Empty();
  850. LPCTSTR pStr = sTemp.GetData();
  851. while( *pStr != _T('\0') ) {
  852. sDrawText += m_cPasswordChar;
  853. pStr = ::CharNext(pStr);
  854. }
  855. }
  856. }
  857. RECT rcTextPadding = GetTextPadding();
  858. RECT rc = m_rcItem;
  859. rc.left += rcTextPadding.left;
  860. rc.right -= rcTextPadding.right;
  861. rc.top += rcTextPadding.top;
  862. rc.bottom -= rcTextPadding.bottom;
  863. if( IsEnabled() ) {
  864. CRenderEngine::DrawText(hDC, m_pManager, rc, sDrawText, mCurTextColor, \
  865. m_iFont, DT_SINGLELINE | m_uTextStyle);
  866. }
  867. else {
  868. CRenderEngine::DrawText(hDC, m_pManager, rc, sDrawText, m_dwDisabledTextColor, \
  869. m_iFont, DT_SINGLELINE | m_uTextStyle);
  870. }
  871. }
  872. }