张洋 4 weeks ago
parent
commit
3cf27705e4
1 changed files with 40 additions and 11 deletions
  1. 40 11
      DuiLib/Core/UIManager.cpp

+ 40 - 11
DuiLib/Core/UIManager.cpp

@@ -588,7 +588,9 @@ namespace DuiLib
 		//      走 MessageHandler 里原生的鼠标点击路径(CButtonUI 等据此 Activate())。NIM 从不过滤
 		//      这些模拟鼠标,只在 WM_LBUTTON* 分支加了 m_pEventTouch!=NULL 守卫,保证一次手指手势
 		//      要么滚动、要么点击,不会双触发。
-		//   4) 守卫:若 m_pEventClick 已存在(鼠标/指针交互进行中),直接忽略本次触摸,互不打架。
+		//   4) 守卫:DOWN 时若真实鼠标已在交互(m_pEventClick 被真实鼠标置位)则忽略触摸;MOVE 仅对“滚动条”让路
+		//      (原生鼠标独占滑块拖拽),容器照常收 TOUCHMOVE 跟手滚动。注意 m_pEventClick 也会被触摸模拟的
+		//      WM_LBUTTONDOWN 置位,故不能仅因它非空就吞掉 TOUCHMOVE,否则列表滚不动。
 		TouchApi::PFN_GetTouchInputInfo pfnGetInfo = TouchApi::GetGetTouchInputInfo();
 		if (pfnGetInfo == NULL) return;
 
@@ -644,8 +646,12 @@ namespace DuiLib
 			}
 			else if (ti.dwFlags & TouchApi::TOUCHEVTFLAG_MOVE)
 			{
-				// 鼠标/指针交互进行中 -> 忽略触摸
-				if (m_pEventClick != NULL) break;
+				// 鼠标/指针交互进行中 -> 忽略触摸。但注意:一次触摸手势本身会触发 Windows 模拟的
+				// WM_LBUTTONDOWN,从而把 m_pEventClick 置为【当前命中控件】(容器/按钮/滚动条都会)。
+				// 若整句 break,会把容器的 UIEVENT_TOUCHMOVE 一起吞掉→列表无法跟手滚动。
+				// 故只对“滚动条”让路(滚动条滑块拖拽由原生鼠标驱动,见 DOWN 的 ScrollBar break),
+				// 其余控件(容器/按钮)照常派发 TOUCHMOVE:容器据此 1:1 跟手滚动,按钮不处理 TOUCHMOVE 无害。
+				if (m_pEventClick != NULL && m_pEventClick->GetInterface(_T("ScrollBar")) != NULL) break;
 				// 与上一包完全相同的点 -> 去重,避免无谓派发(NIM 同款守卫)
 				if (m_ptLastTouchPos.x == pt.x && m_ptLastTouchPos.y == pt.y) break;
 				// 本次位移增量(相对上一包),随事件下发,滚动容器据此做 1:1 跟手平移
@@ -673,18 +679,38 @@ namespace DuiLib
 			{
 				m_ptLastTouchPos = pt;
 
-				// 触屏抬起模拟“鼠标离开”:清除悬浮态。触摸不会生成 WM_MOUSELEAVE,
-				// 否则按钮/列表项点击后悬浮态残留。滚动条等提前退出的触点 m_pEventTouch 为 NULL,同样要清。
-				if (m_pEventHover != NULL)
+				// 触屏抬起模拟“鼠标离开”:清除悬浮态。触摸永远不会生成 WM_MOUSELEAVE,
+				// 若只清 m_pEventHover,m_aNeedMouseLeaveNeeded 数组里登记过的控件
+				// (按钮/列表项/滚动条等曾收到 MOUSEENTER 并 AddMouseLeaveNeeded 的)会一直残留
+				// 悬浮态,表现为“多个控件同时悬浮”。必须复刻 WM_MOUSEMOVE 里的离开广播:先遍历
+				// 整个数组逐一发 MOUSELEAVE(控件自身在 MOUSELEAVE 里把自己从数组移除),再清 m_pEventHover。
+				if (m_aNeedMouseLeaveNeeded.GetSize() > 0 || m_pEventHover != NULL)
 				{
 					TEventUI event = { 0 };
 					event.Type = UIEVENT_MOUSELEAVE;
-					event.pSender = m_pEventHover;
 					event.ptMouse = pt;
 					event.wKeyState = MapKeyState();
 					event.dwTimestamp = ::GetTickCount();
-					m_pEventHover->Event(event);
-					m_pEventHover = NULL;
+
+					if (m_aNeedMouseLeaveNeeded.GetSize() > 0)
+					{
+						CStdPtrArray aNeedMouseLeaveNeeded(m_aNeedMouseLeaveNeeded.GetSize());
+						aNeedMouseLeaveNeeded.Resize(m_aNeedMouseLeaveNeeded.GetSize());
+						::CopyMemory(aNeedMouseLeaveNeeded.GetData(), m_aNeedMouseLeaveNeeded.GetData(), m_aNeedMouseLeaveNeeded.GetSize() * sizeof(LPVOID));
+						for (int i = 0; i < aNeedMouseLeaveNeeded.GetSize(); i++)
+						{
+							CControlUI* pHover = static_cast<CControlUI*>(aNeedMouseLeaveNeeded[i]);
+							event.pSender = pHover;
+							pHover->Event(event);
+						}
+					}
+					if (m_pEventHover != NULL)
+					{
+						event.pSender = m_pEventHover;
+						m_pEventHover->Event(event);
+						m_pEventHover = NULL;
+					}
+					if (m_hwndTooltip != NULL) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM)&m_ToolTip);
 				}
 
 				// 滚动条等场景本函数未接管(m_pEventTouch 为 NULL),捕获由原生鼠标路径负责,这里不释放。
@@ -2072,8 +2098,11 @@ namespace DuiLib
 			break;
 			case WM_LBUTTONUP:
 			{
-				// 触屏拖拽手势(已发生位移)-> 松开不触发点击,仅滚动(避免“拖拽又误点”)
-				if (m_bTouchMoved) m_pEventClick = NULL;
+				// 触屏拖拽手势(已发生位移)-> 松开不触发“点击”,避免“拖拽又误点”。
+				// 但滚动条滑块拖拽依赖 BUTTONUP 来解除 UISTATE_CAPTURED 并停掉 50ms 定时器,
+				// 绝不能在此提前清空 m_pEventClick,否则滚动条卡死在捕获态。故对滚动条放行 BUTTONUP。
+				if (m_bTouchMoved && m_pEventClick != NULL && m_pEventClick->GetInterface(_T("ScrollBar")) == NULL)
+					m_pEventClick = NULL;
 				m_bTouchMoved = false;  // 用完即清,绝不影响后续真实鼠标点击
 				POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
 				m_ptLastMousePos = pt;