#include "TouchWindowBase.h" #include #include #ifndef TOUCH_COORD_TO_PIXEL // TOUCH 输入单位是 1/100 英寸,通常使用这个宏转换为像素 #define TOUCH_COORD_TO_PIXEL(l) ((l) / 100) #endif TouchWindowBase::TouchWindowBase() { } TouchWindowBase::~TouchWindowBase() { StopInertia(); } std::int64_t TouchWindowBase::NowMs() const { using namespace std::chrono; return duration_cast(steady_clock::now().time_since_epoch()).count(); } void TouchWindowBase::AddTouchSample(LONG y) { std::int64_t t = NowMs(); m_samples.push_back({ t, y }); // 保留时间窗口或最大条目数 while (!m_samples.empty() && (t - m_samples.front().tms) > SAMPLE_WINDOW_MS) m_samples.pop_front(); while ((int)m_samples.size() > MAX_SAMPLE_COUNT) m_samples.pop_front(); } void TouchWindowBase::StartInertia(float velocityPxPerMs) { if (m_inertiaActive) StopInertia(); m_inertiaVelocityPxPerMs = velocityPxPerMs; if (std::abs(m_inertiaVelocityPxPerMs) < MIN_INERTIA_VELOCITY) { // 速度太小,不启动惯性 m_inertiaActive = false; return; } m_lastInertiaTms = NowMs(); m_inertiaTimerId = ::SetTimer(m_hWnd, INERTIA_TIMER_ID, INERTIA_TIMER_INTERVAL_MS, NULL); m_inertiaActive = (m_inertiaTimerId != 0); } void TouchWindowBase::StopInertia() { if (m_inertiaActive && m_inertiaTimerId) { ::KillTimer(m_hWnd, m_inertiaTimerId); m_inertiaTimerId = 0; } m_inertiaActive = false; m_inertiaVelocityPxPerMs = 0.0f; } LRESULT TouchWindowBase::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: // 注册接收触摸(移除:由外部窗口统一注册,避免重复注册) // ::RegisterTouchWindow(m_hWnd, 0); break; case WM_TOUCH: { UINT cInputs = LOWORD(wParam); std::vector inputs(cInputs); if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, inputs.data(), sizeof(TOUCHINPUT))) { for (UINT i = 0; i < cInputs; ++i) { TOUCHINPUT& ti = inputs[i]; POINT pt; pt.x = TOUCH_COORD_TO_PIXEL(ti.x); pt.y = TOUCH_COORD_TO_PIXEL(ti.y); // 把屏幕坐标转换为目标 paint 窗口客户区坐标 HWND target = m_hWndPaint ? m_hWndPaint : m_hWnd; ScreenToClient(target, &pt); // 触摸按下 if (ti.dwFlags & TOUCHEVENTF_DOWN) { // 停止可能正在进行的惯性 StopInertia(); m_touchDown = true; m_lastY = pt.y; m_accumWheel = 0; m_samples.clear(); AddTouchSample(pt.y); // 保存最后触摸点(用于惯性期间发送位置) m_lastTouchPt = pt; ::PostMessage(target, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(pt.x, pt.y)); ::SetCapture(target); } // 触摸移动 else if (ti.dwFlags & TOUCHEVENTF_MOVE) { if (m_touchDown) { LONG dy = pt.y - m_lastY; // 按手机习惯:手指上滑(坐标减小) -> 负增量 m_lastY = pt.y; AddTouchSample(pt.y); int wheelUnits = static_cast(dy * PIXEL_TO_WHEEL); m_accumWheel += wheelUnits; // 更新最后触摸点(用于惯性期间发送位置) m_lastTouchPt = pt; // 每当累积达到 WHEEL_DELTA 就发送一次 WM_MOUSEWHEEL while (abs(m_accumWheel) >= WHEEL_DELTA) { int sendDelta = (m_accumWheel > 0) ? WHEEL_DELTA : -WHEEL_DELTA; m_accumWheel -= sendDelta; WPARAM wParamWheel = (WPARAM)(((DWORD)sendDelta) << 16); LPARAM lParamPos = MAKELPARAM(pt.x, pt.y); ::PostMessage(target, WM_MOUSEWHEEL, wParamWheel, lParamPos); } // 仍然发送鼠标移动,兼容需要拖拽的控件 ::PostMessage(target, WM_MOUSEMOVE, MK_LBUTTON, MAKELPARAM(pt.x, pt.y)); } } // 触摸抬起 else if (ti.dwFlags & TOUCHEVENTF_UP) { if (m_touchDown) { // 计算速度(px/ms) float velocityPxPerMs = 0.0f; if (m_samples.size() >= 2) { TouchSample first = m_samples.front(); TouchSample last = m_samples.back(); std::int64_t dt = last.tms - first.tms; if (dt > 0) { // 与 MOVE 中 dy 方向保持一致:last - first velocityPxPerMs = static_cast(last.y - first.y) / static_cast(dt); } } m_touchDown = false; // 保存最后触摸点(确保惯性期间有位置可用) m_lastTouchPt = pt; m_lastY = 0; m_accumWheel = 0; m_samples.clear(); ::PostMessage(target, WM_LBUTTONUP, 0, MAKELPARAM(pt.x, pt.y)); ::ReleaseCapture(); // 启动惯性滚动(如果速度足够大) StartInertia(velocityPxPerMs); } } } CloseTouchInputHandle((HTOUCHINPUT)lParam); } else { // 可添加日志:GetTouchInputInfo 失败 } return 0; // 已处理 } case WM_TIMER: { if (wParam == INERTIA_TIMER_ID && m_inertiaActive) { HWND target = m_hWndPaint ? m_hWndPaint : m_hWnd; std::int64_t now = NowMs(); std::int64_t dt = now - m_lastInertiaTms; if (dt <= 0) dt = INERTIA_TIMER_INTERVAL_MS; m_lastInertiaTms = now; // 计算本帧移动的像素 float pixels = m_inertiaVelocityPxPerMs * static_cast(dt); // 将像素转换为 wheel units 累积并发送 int wheelUnits = static_cast(pixels * PIXEL_TO_WHEEL); m_accumWheel += wheelUnits; // 使用保存的最后触摸点(target 的客户区坐标) POINT pt = m_lastTouchPt; LPARAM lParamPos = MAKELPARAM(pt.x, pt.y); while (abs(m_accumWheel) >= WHEEL_DELTA) { int sendDelta = (m_accumWheel > 0) ? WHEEL_DELTA : -WHEEL_DELTA; m_accumWheel -= sendDelta; WPARAM wParamWheel = (WPARAM)(((DWORD)sendDelta) << 16); ::PostMessage(target, WM_MOUSEWHEEL, wParamWheel, lParamPos); } // 衰减速度(指数衰减) // velocity *= pow(INERTIA_DECAY_PER_MS, dt) m_inertiaVelocityPxPerMs *= std::pow(INERTIA_DECAY_PER_MS, static_cast(dt)); // 停止条件 if (std::abs(m_inertiaVelocityPxPerMs) < MIN_INERTIA_VELOCITY) { StopInertia(); } return 0; } break; } case WM_DESTROY: StopInertia(); break; } // 若未处理,交给基类 return CWindowWnd::HandleMessage(uMsg, wParam, lParam); }