TouchWindowBase.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #include "TouchWindowBase.h"
  2. #include <vector>
  3. #include <cmath>
  4. #ifndef TOUCH_COORD_TO_PIXEL
  5. // TOUCH 输入单位是 1/100 英寸,通常使用这个宏转换为像素
  6. #define TOUCH_COORD_TO_PIXEL(l) ((l) / 100)
  7. #endif
  8. TouchWindowBase::TouchWindowBase()
  9. {
  10. }
  11. TouchWindowBase::~TouchWindowBase()
  12. {
  13. StopInertia();
  14. }
  15. std::int64_t TouchWindowBase::NowMs() const
  16. {
  17. using namespace std::chrono;
  18. return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
  19. }
  20. void TouchWindowBase::AddTouchSample(LONG y)
  21. {
  22. std::int64_t t = NowMs();
  23. m_samples.push_back({ t, y });
  24. // 保留时间窗口或最大条目数
  25. while (!m_samples.empty() && (t - m_samples.front().tms) > SAMPLE_WINDOW_MS)
  26. m_samples.pop_front();
  27. while ((int)m_samples.size() > MAX_SAMPLE_COUNT)
  28. m_samples.pop_front();
  29. }
  30. void TouchWindowBase::StartInertia(float velocityPxPerMs)
  31. {
  32. if (m_inertiaActive)
  33. StopInertia();
  34. m_inertiaVelocityPxPerMs = velocityPxPerMs;
  35. if (std::abs(m_inertiaVelocityPxPerMs) < MIN_INERTIA_VELOCITY)
  36. {
  37. // 速度太小,不启动惯性
  38. m_inertiaActive = false;
  39. return;
  40. }
  41. m_lastInertiaTms = NowMs();
  42. m_inertiaTimerId = ::SetTimer(m_hWnd, INERTIA_TIMER_ID, INERTIA_TIMER_INTERVAL_MS, NULL);
  43. m_inertiaActive = (m_inertiaTimerId != 0);
  44. }
  45. void TouchWindowBase::StopInertia()
  46. {
  47. if (m_inertiaActive && m_inertiaTimerId)
  48. {
  49. ::KillTimer(m_hWnd, m_inertiaTimerId);
  50. m_inertiaTimerId = 0;
  51. }
  52. m_inertiaActive = false;
  53. m_inertiaVelocityPxPerMs = 0.0f;
  54. }
  55. LRESULT TouchWindowBase::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  56. {
  57. switch (uMsg)
  58. {
  59. case WM_CREATE:
  60. // 注册接收触摸(移除:由外部窗口统一注册,避免重复注册)
  61. // ::RegisterTouchWindow(m_hWnd, 0);
  62. break;
  63. case WM_TOUCH:
  64. {
  65. UINT cInputs = LOWORD(wParam);
  66. std::vector<TOUCHINPUT> inputs(cInputs);
  67. if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, inputs.data(), sizeof(TOUCHINPUT)))
  68. {
  69. for (UINT i = 0; i < cInputs; ++i)
  70. {
  71. TOUCHINPUT& ti = inputs[i];
  72. POINT pt;
  73. pt.x = TOUCH_COORD_TO_PIXEL(ti.x);
  74. pt.y = TOUCH_COORD_TO_PIXEL(ti.y);
  75. // 把屏幕坐标转换为目标 paint 窗口客户区坐标
  76. HWND target = m_hWndPaint ? m_hWndPaint : m_hWnd;
  77. ScreenToClient(target, &pt);
  78. // 触摸按下
  79. if (ti.dwFlags & TOUCHEVENTF_DOWN)
  80. {
  81. // 停止可能正在进行的惯性
  82. StopInertia();
  83. m_touchDown = true;
  84. m_lastY = pt.y;
  85. m_accumWheel = 0;
  86. m_samples.clear();
  87. AddTouchSample(pt.y);
  88. // 保存最后触摸点(用于惯性期间发送位置)
  89. m_lastTouchPt = pt;
  90. ::PostMessage(target, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
  91. ::SetCapture(target);
  92. }
  93. // 触摸移动
  94. else if (ti.dwFlags & TOUCHEVENTF_MOVE)
  95. {
  96. if (m_touchDown)
  97. {
  98. LONG dy = pt.y - m_lastY; // 按手机习惯:手指上滑(坐标减小) -> 负增量
  99. m_lastY = pt.y;
  100. AddTouchSample(pt.y);
  101. int wheelUnits = static_cast<int>(dy * PIXEL_TO_WHEEL);
  102. m_accumWheel += wheelUnits;
  103. // 更新最后触摸点(用于惯性期间发送位置)
  104. m_lastTouchPt = pt;
  105. // 每当累积达到 WHEEL_DELTA 就发送一次 WM_MOUSEWHEEL
  106. while (abs(m_accumWheel) >= WHEEL_DELTA)
  107. {
  108. int sendDelta = (m_accumWheel > 0) ? WHEEL_DELTA : -WHEEL_DELTA;
  109. m_accumWheel -= sendDelta;
  110. WPARAM wParamWheel = (WPARAM)(((DWORD)sendDelta) << 16);
  111. LPARAM lParamPos = MAKELPARAM(pt.x, pt.y);
  112. ::PostMessage(target, WM_MOUSEWHEEL, wParamWheel, lParamPos);
  113. }
  114. // 仍然发送鼠标移动,兼容需要拖拽的控件
  115. ::PostMessage(target, WM_MOUSEMOVE, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
  116. }
  117. }
  118. // 触摸抬起
  119. else if (ti.dwFlags & TOUCHEVENTF_UP)
  120. {
  121. if (m_touchDown)
  122. {
  123. // 计算速度(px/ms)
  124. float velocityPxPerMs = 0.0f;
  125. if (m_samples.size() >= 2)
  126. {
  127. TouchSample first = m_samples.front();
  128. TouchSample last = m_samples.back();
  129. std::int64_t dt = last.tms - first.tms;
  130. if (dt > 0)
  131. {
  132. // 与 MOVE 中 dy 方向保持一致:last - first
  133. velocityPxPerMs = static_cast<float>(last.y - first.y) / static_cast<float>(dt);
  134. }
  135. }
  136. m_touchDown = false;
  137. // 保存最后触摸点(确保惯性期间有位置可用)
  138. m_lastTouchPt = pt;
  139. m_lastY = 0;
  140. m_accumWheel = 0;
  141. m_samples.clear();
  142. ::PostMessage(target, WM_LBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));
  143. ::ReleaseCapture();
  144. // 启动惯性滚动(如果速度足够大)
  145. StartInertia(velocityPxPerMs);
  146. }
  147. }
  148. }
  149. CloseTouchInputHandle((HTOUCHINPUT)lParam);
  150. }
  151. else
  152. {
  153. // 可添加日志:GetTouchInputInfo 失败
  154. }
  155. return 0; // 已处理
  156. }
  157. case WM_TIMER:
  158. {
  159. if (wParam == INERTIA_TIMER_ID && m_inertiaActive)
  160. {
  161. HWND target = m_hWndPaint ? m_hWndPaint : m_hWnd;
  162. std::int64_t now = NowMs();
  163. std::int64_t dt = now - m_lastInertiaTms;
  164. if (dt <= 0) dt = INERTIA_TIMER_INTERVAL_MS;
  165. m_lastInertiaTms = now;
  166. // 计算本帧移动的像素
  167. float pixels = m_inertiaVelocityPxPerMs * static_cast<float>(dt);
  168. // 将像素转换为 wheel units 累积并发送
  169. int wheelUnits = static_cast<int>(pixels * PIXEL_TO_WHEEL);
  170. m_accumWheel += wheelUnits;
  171. // 使用保存的最后触摸点(target 的客户区坐标)
  172. POINT pt = m_lastTouchPt;
  173. LPARAM lParamPos = MAKELPARAM(pt.x, pt.y);
  174. while (abs(m_accumWheel) >= WHEEL_DELTA)
  175. {
  176. int sendDelta = (m_accumWheel > 0) ? WHEEL_DELTA : -WHEEL_DELTA;
  177. m_accumWheel -= sendDelta;
  178. WPARAM wParamWheel = (WPARAM)(((DWORD)sendDelta) << 16);
  179. ::PostMessage(target, WM_MOUSEWHEEL, wParamWheel, lParamPos);
  180. }
  181. // 衰减速度(指数衰减)
  182. // velocity *= pow(INERTIA_DECAY_PER_MS, dt)
  183. m_inertiaVelocityPxPerMs *= std::pow(INERTIA_DECAY_PER_MS, static_cast<float>(dt));
  184. // 停止条件
  185. if (std::abs(m_inertiaVelocityPxPerMs) < MIN_INERTIA_VELOCITY)
  186. {
  187. StopInertia();
  188. }
  189. return 0;
  190. }
  191. break;
  192. }
  193. case WM_DESTROY:
  194. StopInertia();
  195. break;
  196. }
  197. // 若未处理,交给基类
  198. return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
  199. }