张洋 4 weeks ago
parent
commit
9980d84d3f

+ 32 - 0
zhipuzi_pos_windows/ui/CTouchWindowBase.cpp

@@ -45,6 +45,13 @@ void CTouchWindowBase::SynthesizeTap(HWND target, POINT pt)
 	::PostMessage(target, WM_LBUTTONUP, 0, lParamPos);
 }
 
+void CTouchWindowBase::ScheduleHoverClear()
+{
+	// 见下方 WM_TIMER(HOVER_CLEAR_TIMER_ID)说明。ExtraInfo=0 的鼠标消息不会被
+	// MessageHandler 入口的 FROMTOUCH 过滤器吞掉,能正常进入 DuiLib 的悬停清理逻辑。
+	::SetTimer(m_hWnd, HOVER_CLEAR_TIMER_ID, HOVER_CLEAR_DELAY_MS, NULL);
+}
+
 void CTouchWindowBase::StartInertia(float velocityPxPerMs)
 {
 	if (m_inertiaActive)
@@ -215,6 +222,18 @@ LRESULT CTouchWindowBase::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
 
 							// 启动惯性滚动(如果速度足够大)
 							StartInertia(velocityPxPerMs);
+
+							// 关键修复(悬浮态卡死):触屏轻触/滑动时,操作系统会先发一条
+							// “将光标移动到触点”的 WM_MOUSEMOVE(不带 FROMTOUCH 标记),它绕过了
+							// MessageHandler 入口的过滤器,于是 DuiLib 会把该控件置为 UISTATE_HOT
+							// (悬浮态);而 DuiLib 只在 WM_MOUSEMOVE 指到“别的控件/窗口外”时才
+							// 发 MOUSELEAVE 清除悬浮态。触屏没有后续“移动光标离开”的动作,于是
+							// 悬浮态永远清不掉 → 按钮/列表项卡在悬浮态不恢复。
+							// 真实鼠标靠“物理移开”触发 MOUSELEAVE;这里用定时器在手势结束后补一次
+							// WM_MOUSEMOVE(-1)(即“光标移到窗口外”),模拟鼠标移开,强制清除悬浮态。
+							// 用定时器延后约 50ms,确保本次手势可能产生的 OS 尾随消息都已处理完,
+							// 避免“我们刚清掉又被 OS 尾随的 move 重新置上”的竞态。
+							ScheduleHoverClear();
 						}
 					}
 				}
@@ -229,6 +248,18 @@ LRESULT CTouchWindowBase::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
 
 		case WM_TIMER:
 		{
+			if (wParam == HOVER_CLEAR_TIMER_ID)
+			{
+				// 模拟“鼠标移开窗口”:给 paint 窗口发一条 WM_MOUSEMOVE,lParam=-1 表示
+				// 坐标为 (-1,-1),FindControl 必然返回 NULL;DuiLib 在 WM_MOUSEMOVE 里对旧
+				// 悬停控件触发 UIEVENT_MOUSELEAVE,从而清除 UISTATE_HOT 悬浮态。
+				// 这样触屏轻触/滑动结束后,按钮/列表项不会卡在悬浮态。
+				HWND target = m_hWndPaint ? m_hWndPaint : m_hWnd;
+				if (target && ::IsWindow(target))
+					::PostMessage(target, WM_MOUSEMOVE, 0, (LPARAM)-1);
+				::KillTimer(m_hWnd, HOVER_CLEAR_TIMER_ID);
+				return 0;
+			}
 			if (wParam == INERTIA_TIMER_ID && m_inertiaActive)
 			{
 				HWND target = m_hWndPaint ? m_hWndPaint : m_hWnd;
@@ -273,6 +304,7 @@ LRESULT CTouchWindowBase::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
 
 		case WM_DESTROY:
 			StopInertia();
+			::KillTimer(m_hWnd, HOVER_CLEAR_TIMER_ID);
 			break;
 	}
 

+ 6 - 0
zhipuzi_pos_windows/ui/CTouchWindowBase.h

@@ -64,6 +64,11 @@ protected:
 	// 惯性参数(可调)
 	static constexpr int INERTIA_TIMER_ID = 0xA001;
 	static constexpr int INERTIA_TIMER_INTERVAL_MS = 16; // 约 60 FPS
+
+	// 触屏抬起后清除“悬停态”定时器:模拟真实鼠标“移开”以清除 UISTATE_HOT 悬浮态,
+	// 避免按钮在触屏轻触后卡在悬浮态(详见下方 ScheduleHoverClear 说明)。
+	static constexpr UINT HOVER_CLEAR_TIMER_ID = 0xA002;
+	static constexpr int  HOVER_CLEAR_DELAY_MS = 50; // 延后一点,确保本次手势的 OS 尾随消息都已处理完
 	// 衰减系数按每毫秒指数衰减: velocity *= pow(INERTIA_DECAY_PER_MS, dt)
 	static constexpr float INERTIA_DECAY_PER_MS = 0.997f; // 值接近 1,越接近 1 惯性持续越久
 	static constexpr float MIN_INERTIA_VELOCITY = 0.008f; // px/ms,低于则停止(调小=惯性更持久)
@@ -73,6 +78,7 @@ protected:
 	// 内部方法
 	void AddTouchSample(LONG y);
 	void SynthesizeTap(HWND target, POINT pt); // 模拟一次“轻触点击”(down+up),仅用于判定为轻触时
+	void ScheduleHoverClear(); // 触屏抬起后安排清除悬停态(避免按钮卡在悬浮态)
 	void StartInertia(float velocityPxPerMs);
 	void StopInertia();
 	std::int64_t NowMs() const;