Поделиться через


Прочие операции мыши

В предыдущих разделах обсуждались щелчки мышью и перемещение мыши. Ниже приведены некоторые другие операции, которые можно выполнять с помощью мыши.

Перетаскивание элементов пользовательского интерфейса

Если пользовательский интерфейс поддерживает перетаскивание элементов пользовательского интерфейса, существует еще одна функция, которую следует вызвать в обработчике сообщений с указателем мыши: DragDetect. Функция DragDetect возвращает значение TRUE , если пользователь инициирует жест мыши, который следует интерпретировать как перетаскивание. В следующем коде показано, как использовать эту функцию.

    case WM_LBUTTONDOWN: 
        {
            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
            if (DragDetect(m_hwnd, pt))
            {
                // Start dragging.
            }
        }
        return 0;

Идея заключается в том, что если программа поддерживает перетаскивание, вы не хотите, чтобы каждый щелчок мыши интерпретировался как перетаскивание. В противном случае пользователь может случайно перетащить что-то, если он просто хотел щелкнуть его (например, выбрать). Но если мышь особенно чувствительна, может быть трудно сохранить мышь совершенно неподвижно во время щелчка. Таким образом, Windows определяет порог перетаскивания в несколько пикселей. Когда пользователь нажимает кнопку мыши, это не считается перетаскиванием, если мышь не пересекает это пороговое значение. Функция DragDetect проверяет, достигнуто ли это пороговое значение. Если функция возвращает значение TRUE, щелчок мышью можно интерпретировать как перетаскивание. В противном случае — нет.

Примечание

Если DragDetect возвращает значение FALSE, Windows подавляет сообщение WM_LBUTTONUP , когда пользователь отпускает кнопку мыши. Поэтому не вызывайте DragDetect , если программа в настоящее время не находится в режиме, поддерживающем перетаскивание. (Например, если перетаскиваемый элемент пользовательского интерфейса уже выбран.) В конце этого модуля мы увидим более длинный пример кода, в котором используется функция DragDetect .

 

Ограничение курсора

Иногда может потребоваться ограничить курсор клиентской областью или частью клиентской области. Функция ClipCursor ограничивает перемещение курсора указанным прямоугольником. Этот прямоугольник предоставляется в координатах экрана, а не в координатах клиента, поэтому точка (0, 0) означает левый верхний угол экрана. Чтобы преобразовать координаты клиента в экранные, вызовите функцию ClientToScreen.

Следующий код ограничивает курсор клиентской областью окна.

    // Get the window client area.
    RECT rc;
    GetClientRect(m_hwnd, &rc);

    // Convert the client area to screen coordinates.
    POINT pt = { rc.left, rc.top };
    POINT pt2 = { rc.right, rc.bottom };
    ClientToScreen(m_hwnd, &pt);
    ClientToScreen(m_hwnd, &pt2);
    SetRect(&rc, pt.x, pt.y, pt2.x, pt2.y);

    // Confine the cursor.
    ClipCursor(&rc);

ClipCursor принимает структуру RECT , а ClientToScreen — структуру POINT . Прямоугольник определяется его верхней левой и нижней правой точками. Вы можете ограничить курсор любой прямоугольной областью, включая области за пределами окна, но ограничение курсора клиентской областью является типичным способом использования функции. Ограничение курсора областью за пределами окна было бы необычным, и пользователи, вероятно, воспримут его как ошибку.

Чтобы снять ограничение, вызовите ClipCursor со значением NULL.

ClipCursor(NULL);

События отслеживания мыши: наведение и выход

Два других сообщения мыши отключены по умолчанию, но могут быть полезны для некоторых приложений:

  • WM_MOUSEHOVER: курсор наведен на клиентную область в течение фиксированного периода времени.
  • WM_MOUSELEAVE: курсор покинул клиентную область.

Чтобы включить эти сообщения, вызовите функцию TrackMouseEvent .

    TRACKMOUSEEVENT tme;
    tme.cbSize = sizeof(tme);
    tme.hwndTrack = hwnd;
    tme.dwFlags = TME_HOVER | TME_LEAVE;
    tme.dwHoverTime = HOVER_DEFAULT;
    TrackMouseEvent(&tme);

Структура TRACKMOUSEEVENT содержит параметры для функции. Элемент dwFlags структуры содержит битовые флаги, указывающие, какие сообщения отслеживания вас интересуют. Вы можете выбрать как WM_MOUSEHOVER , так и WM_MOUSELEAVE, как показано здесь, или только один из двух. Элемент dwHoverTime указывает, сколько времени необходимо навести указатель мыши, прежде чем система создаст сообщение о наведении указателя мыши. Это значение задается в миллисекундах. Константная HOVER_DEFAULT означает использование системного значения по умолчанию.

После получения одного из запрошенных сообщений функция TrackMouseEvent сбрасывается. Чтобы получить другое сообщение отслеживания, необходимо вызвать его еще раз. Однако перед повторным вызовом TrackMouseEvent следует подождать, пока не появится следующее сообщение о перемещении мыши. В противном случае окно может быть переполнено сообщениями отслеживания. Например, при наведении указателя мыши система продолжит создавать поток WM_MOUSEHOVER сообщений, пока мышь неподвижна. На самом деле вам не нужно еще одно сообщение WM_MOUSEHOVER , пока мышь не переместится на другое место и снова не наведен указатель мыши.

Ниже приведен небольшой вспомогательный класс, который можно использовать для управления событиями отслеживания мыши.

class MouseTrackEvents
{
    bool m_bMouseTracking;

public:
    MouseTrackEvents() : m_bMouseTracking(false)
    {
    }
    
    void OnMouseMove(HWND hwnd)
    {
        if (!m_bMouseTracking)
        {
            // Enable mouse tracking.
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(tme);
            tme.hwndTrack = hwnd;
            tme.dwFlags = TME_HOVER | TME_LEAVE;
            tme.dwHoverTime = HOVER_DEFAULT;
            TrackMouseEvent(&tme);
            m_bMouseTracking = true;
        }
    }
    void Reset(HWND hwnd)
    {
        m_bMouseTracking = false;
    }
};

В следующем примере показано, как использовать этот класс в процедуре окна.

LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_MOUSEMOVE:
        mouseTrack.OnMouseMove(m_hwnd);  // Start tracking.

        // TODO: Handle the mouse-move message.

        return 0;

    case WM_MOUSELEAVE:

        // TODO: Handle the mouse-leave message.

        mouseTrack.Reset(m_hwnd);
        return 0;

    case WM_MOUSEHOVER:

        // TODO: Handle the mouse-hover message.

        mouseTrack.Reset(m_hwnd);
        return 0;

    }
    return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}

События отслеживания мыши требуют дополнительной обработки системой, поэтому оставьте их отключенными, если они вам не нужны.

Для полноты ниже приведена функция, которая запрашивает в системе время ожидания при наведении указателя по умолчанию.

UINT GetMouseHoverTime()
{
    UINT msec; 
    if (SystemParametersInfo(SPI_GETMOUSEHOVERTIME, 0, &msec, 0))
    {   
        return msec;
    }
    else
    {
        return 0;
    }
}

Колесико мыши

Следующая функция проверяет наличие колесика мыши.

BOOL IsMouseWheelPresent()
{
    return (GetSystemMetrics(SM_MOUSEWHEELPRESENT) != 0);
}

Если пользователь поворачивает колесико мыши, окно с фокусом получает WM_MOUSEWHEEL сообщение. Параметр wParam этого сообщения содержит целочисленное значение, называемое delta , которое измеряет, насколько сильно было вращалось колесо. Разностная переменная использует произвольные единицы, где 120 единиц определяется как поворот, необходимый для выполнения одного "действия". Конечно, определение действия зависит от вашей программы. Например, если колесико мыши используется для прокрутки текста, каждые 120 единиц поворота будут прокручивать одну строку текста.

Знак дельты указывает направление поворота:

  • Положительный результат. Поворачивайте вперед, от пользователя.
  • Отрицательный: повернуть назад к пользователю.

Значение delta помещается в wParam вместе с некоторыми дополнительными флагами. Используйте макрос GET_WHEEL_DELTA_WPARAM , чтобы получить значение delta.

int delta = GET_WHEEL_DELTA_WPARAM(wParam);

Если колесико мыши имеет высокое разрешение, абсолютное значение delta может быть меньше 120. В этом случае, если действие имеет смысл выполняться с меньшим шагом, это можно сделать. Например, текст может прокручиваться с шагом менее чем на одну строку. В противном случае накапливайте общую дельту до тех пор, пока колесо не поворачивается достаточно для выполнения действия. Сохраните неиспользуемую дельту в переменной и при накоплении 120 единиц (положительных или отрицательных) выполните действие.

Следующая

Ввод с клавиатуры