Win32:测试鼠标是否停留了一秒钟

Win32: Test if mouse has hovered for a second

本文关键字:停留 一秒钟 是否 鼠标 测试 Win32      更新时间:2023-10-16

我试图使一个函数,只有当鼠标停留在同一地方的时间增加而不停止程序时才运行(所以没有Sleep(1000)函数)。我试图做的是让回调在鼠标移动时运行paint中的一个函数。在这个函数中,会有一个while循环来确保鼠标在最后一帧之后没有移动,如果点击或移动就会中断。

在回调中:

case WM_MOUSEMOVE:
    POINT pt;
    GetCursorPos(&pt);
    ScreenToClient(hWnd, &pt);
    iPosX = pt.x;
    iPosY = pt.y;
    if (!mouseMoved) {
        mouseMoved = true;
        period = 0;
        InvalidateRect(hWnd, 0, FALSE);
    }
    break;

In the Paint:

       if (mouseMoved && !MouseClicked) {
            test.newBit(1, 100, 0, hdc); //Draws a picture as a test
            oldx = iPosX;
            oldy = iPosY;
            period = 0;
            while (period <= 1000000 && !MouseClicked) {
                oldx = iPosX;
                oldy = iPosY;
                POINT pt;
                GetCursorPos(&pt);
                ScreenToClient(hWnd, &pt);
                iPosX = pt.x;
                iPosY = pt.y;
                if (iPosX != oldx || iPosY != oldy) {
                    mouseMoved = false;
                    period = 0;
                    break;
                }
                period++;
            }
            if (period <= 1000000) {
                period = 0;
                test.newBit(0, 0, 0, hdc); //Draws a picture as a test
                mouseMoved = false;
            }
        }

当您移动鼠标并单击时,会产生随机闪烁。如果你能指出我做错了什么,我将不胜感激。

系统已经为您实现了这个功能。你只需要通过调用TrackMouseEvent:

来请求鼠标悬停消息
TRACKMOUSEEVENT tme = { sizeof( tme ) };
tme.dwFlags = TME_HOVER;
tme.hwndTrack = hWnd;
tme.dwHoverTime = 1000;
::TrackMouseEvent( &tme );

应用程序接收到一个WM_MOUSEHOVER消息,当光标悬停在窗口的客户端区域上一段在调用TrackMouseEvent中指定的时间。悬停跟踪停止,一旦WM_MOUSEHOVER消息产生。为了接收更多的WM_MOUSEHOVER消息,应用程序需要重新请求悬停跟踪(使用与上面相同的代码)。

应用程序可以调用SystemParametersInfo并使用SPI_GETMOUSEHOVERTIME来检索默认的悬停超时

注意,这考虑了悬停矩形(SPI_GETMOUSEHOVERWIDTHSPI_GETMOUSEHOVERHEIGHT)。这是一个鼠标光标周围的区域,允许鼠标移动到其中,而不需要重置悬停超时。


如果您需要一个解决方案,只报告鼠标没有移动时的悬停,您将不得不自己实现它。基于计时器的解决方案是一个干净的解决方案。

每当鼠标移动时,我们需要重置计时器。如果鼠标之前在窗口的客户端区域之外,我们还必须重新请求WM_MOUSELEAVE消息。这是必要的,以便在鼠标离开客户端区域时取消计时器:

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) {
    enum TimerId { TimerId_MouseHover = 1 };
    static const UINT HoverTimeoutInMs = 1000;
    static int PrevX = INT_MIN;
    static int PrevY = INT_MIN;
    static bool IsMouseOutside = true;
    static bool IsMouseHovered = false;
    switch ( message ) {
    case WM_MOUSEMOVE: {
        // If mouse was previously outside, re-request WM_MOUSELEAVE messages
        if ( IsMouseOutside ) {
            TRACKMOUSEEVENT tme = { sizeof( tme ) };
            tme.dwFlags = TME_LEAVE;
            tme.hwndTrack = hWnd;
            ::TrackMouseEvent( &tme );
            IsMouseOutside = false;
        }
        int CurrX = GET_X_LPARAM( lParam );
        int CurrY = GET_Y_LPARAM( lParam );
        if ( ( CurrX != PrevX ) || ( CurrY != PrevY ) ) {
            // Mouse moved -> reset timer
            ::SetTimer( hWnd, TimerId_MouseHover, HoverTimeoutInMs, nullptr );
            PrevX = CurrX;
            PrevY = CurrY;
            IsMouseHovered = false;
            // For testing only:
            ::InvalidateRect( hWnd, nullptr, FALSE );
        }
        return 0;
    }

每当鼠标离开客户端区域时,我们需要取消计时器。如果我们不取消计时器,它将过期,即使当鼠标移动到窗口的客户端区域之外:

    case WM_MOUSELEAVE:
        ::KillTimer( hWnd, TimerId_MouseHover );
        IsMouseOutside = true;
        PrevX = INT_MIN;
        PrevY = INT_MIN;
        return 0;

如果我们的计时器到期,我们有一个悬停事件:

    case WM_TIMER:
        if ( wParam == TimerId_MouseHover ) {
            // The mouse hasn't been moved for the specified timeout:
            // This is a hover event
            IsMouseHovered = true;
            // For testing only:
            ::InvalidateRect( hWnd, nullptr, FALSE );
            return 0;
        }
        else {
            return ::DefWindowProc( hWnd, message, wParam, lParam );
        }

作为测试,让我们用黑色笔刷填充客户端区域,每当悬停超时:

    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint( hWnd, &ps );
        ::FillRect( hdc, &ps.rcPaint, GetStockBrush( IsMouseHovered ? BLACK_BRUSH :
                                                                      WHITE_BRUSH ) );
        EndPaint( hWnd, &ps );
        return 0;
    }