Compartir a través de


Movimiento del ratón

Cuando se mueve el mouse, Windows publica un mensaje de WM_MOUSEMOVE . De forma predeterminada, WM_MOUSEMOVE va a la ventana que contiene el cursor. Puede invalidar este comportamiento capturando el mouse, que se describe en la sección siguiente.

El mensaje WM_MOUSEMOVE contiene los mismos parámetros que los mensajes para los clics del mouse. Los 16 bits más bajos de lParam contienen la coordenada x y los 16 bits siguientes contienen la coordenada y. Use las macros GET_X_LPARAM y GET_Y_LPARAM para desempaquetar las coordenadas de lParam. El parámetro wParam contiene un OR bit a bit de marcas, que indica el estado de los otros botones del mouse más las teclas MAYÚS y CTRL. El código siguiente obtiene las coordenadas del mouse de lParam.

int xPos = GET_X_LPARAM(lParam); 
int yPos = GET_Y_LPARAM(lParam);

Recuerde que estas coordenadas están en píxeles, no píxeles independientes del dispositivo (DIP). Más adelante en este tema, veremos el código que se convierte entre las dos unidades.

Una ventana también puede recibir un mensaje de WM_MOUSEMOVE si la posición del cursor cambia en relación con la ventana. Por ejemplo, si el cursor se coloca sobre una ventana y el usuario oculta la ventana, la ventana recibe WM_MOUSEMOVE mensajes incluso si el mouse no se movió. Una consecuencia de este comportamiento es que las coordenadas del mouse podrían no cambiar entre WM_MOUSEMOVE mensajes.

Captura del movimiento del mouse fuera de la ventana

De forma predeterminada, una ventana deja de recibir WM_MOUSEMOVE mensajes si el mouse se mueve más allá del borde del área de cliente. Pero para algunas operaciones, es posible que tenga que realizar un seguimiento de la posición del mouse más allá de este punto. Por ejemplo, un programa de dibujo podría permitir al usuario arrastrar el rectángulo de selección más allá del borde de la ventana, como se muestra en el diagrama siguiente.

ilustración de la captura del mouse.

Para recibir mensajes de movimiento del mouse más allá del borde de la ventana, llame a la función SetCapture . Después de llamar a esta función, la ventana seguirá recibiendo WM_MOUSEMOVE mensajes mientras el usuario mantenga al menos un botón del mouse hacia abajo, incluso si el mouse se mueve fuera de la ventana. La ventana de captura debe ser la ventana de primer plano y solo una ventana puede ser la ventana de captura a la vez. Para liberar la captura del mouse, llame a la función ReleaseCapture .

Normalmente, usaría SetCapture y ReleaseCapture de la siguiente manera.

  1. Cuando el usuario presiona el botón izquierdo del mouse, llame a SetCapture para empezar a capturar el mouse.
  2. Responder a los mensajes de movimiento del mouse.
  3. Cuando el usuario suelta el botón izquierdo del mouse, llame a ReleaseCapture.

Ejemplo: círculos de dibujo

Vamos a ampliar el programa Circle del módulo 3 al permitir que el usuario dibuje un círculo con el mouse. Comience con el programa De ejemplo círculo de Direct2D . Modificaremos el código de este ejemplo para agregar un dibujo sencillo. En primer lugar, agregue una nueva variable miembro a la MainWindow clase .

D2D1_POINT_2F ptMouse;

Esta variable almacena la posición del mouse hacia abajo mientras el usuario arrastra el mouse. En el MainWindow constructor, inicialice las variables elipse y ptMouse .

    MainWindow() : pFactory(NULL), pRenderTarget(NULL), pBrush(NULL),
        ellipse(D2D1::Ellipse(D2D1::Point2F(), 0, 0)),
        ptMouse(D2D1::Point2F())
    {
    }

Quite el cuerpo del MainWindow::CalculateLayout método; no es necesario para este ejemplo.

void CalculateLayout() { }

A continuación, declare controladores de mensajes para los mensajes de botón izquierdo, botón izquierdo hacia arriba y movimiento del mouse.

void OnLButtonDown(int pixelX, int pixelY, DWORD flags);
void OnLButtonUp();
void OnMouseMove(int pixelX, int pixelY, DWORD flags);

Las coordenadas del mouse se proporcionan en píxeles físicos, pero Direct2D espera píxeles independientes del dispositivo (DIP). Para controlar la configuración de valores altos de PPP correctamente, debe traducir las coordenadas de píxeles en DIP. Para obtener más información sobre PPP, consulte PPP y Device-Independent Píxeles. En el código siguiente se muestra una clase auxiliar que convierte píxeles en DIP.

class DPIScale
{
    static float scale;

public:
    static void Initialize(HWND hwnd)
    {
        float dpi = GetDpiForWindow(hwnd);
        scale = dpi/96.0f;
    }

    template <typename T>
    static D2D1_POINT_2F PixelsToDips(T x, T y)
    {
        return D2D1::Point2F(static_cast<float>(x) / scale, static_cast<float>(y) / scale);
    }
};

float DPIScale::scale = 1.0f;

Llame a PPPScale::Initialize en el controlador de WM_CREATE , después de crear el objeto de fábrica de Direct2D.

case WM_CREATE:
    if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory)))
    {
        return -1;  // Fail CreateWindowEx.
    }
    DPIScale::Initialize(hwnd);
    return 0;

Para obtener las coordenadas del mouse en DIP de los mensajes del mouse, haga lo siguiente:

  1. Use las macros GET_X_LPARAM y GET_Y_LPARAM para obtener las coordenadas de píxel. Estas macros se definen en WindowsX.h, por lo que no olvide incluir ese encabezado en el proyecto.
  2. Llame DPIScale::PixelsToDips a para convertir píxeles en DIP.

Ahora agregue los controladores de mensajes al procedimiento de ventana.

case WM_LBUTTONDOWN: 
    OnLButtonDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (DWORD)wParam);
    return 0;

case WM_LBUTTONUP: 
    OnLButtonUp();
    return 0;

case WM_MOUSEMOVE: 
    OnMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (DWORD)wParam);
    return 0;

Por último, implemente los propios controladores de mensajes.

Botón izquierdo hacia abajo

Para el mensaje del botón izquierdo, haga lo siguiente:

  1. Llame a SetCapture para empezar a capturar el mouse.
  2. Almacene la posición del clic del mouse en la variable ptMouse . Esta posición define la esquina superior izquierda del cuadro de límite para la elipse.
  3. Restablezca la estructura de puntos suspensivos.
  4. Llame a InvalidateRect. Esta función obliga a que la ventana se vuelva a pintar.
void MainWindow::OnLButtonDown(int pixelX, int pixelY, DWORD flags)
{
    SetCapture(m_hwnd);
    ellipse.point = ptMouse = DPIScale::PixelsToDips(pixelX, pixelY);
    ellipse.radiusX = ellipse.radiusY = 1.0f; 
    InvalidateRect(m_hwnd, NULL, FALSE);
}

Mover el mouse

Para el mensaje de movimiento del mouse, compruebe si el botón izquierdo del mouse está inactivo. Si es así, vuelva a calcular la elipse y vuelva a dibujar la ventana. En Direct2D, la elipse se define mediante el punto central y los radios x e y. Queremos dibujar una elipse que se ajuste al rectángulo delimitador definido por el punto de desplazamiento del mouse (ptMouse) y la posición actual del cursor (x, y), por lo que se necesita un poco de aritmética para encontrar el ancho, el alto y la posición de la elipse.

El código siguiente recalcula la elipse y, a continuación, llama a InvalidateRect para volver a dibujar la ventana.

Diagrama que muestra una elipse con radios x e y.

void MainWindow::OnMouseMove(int pixelX, int pixelY, DWORD flags)
{
    if (flags & MK_LBUTTON) 
    { 
        const D2D1_POINT_2F dips = DPIScale::PixelsToDips(pixelX, pixelY);

        const float width = (dips.x - ptMouse.x) / 2;
        const float height = (dips.y - ptMouse.y) / 2;
        const float x1 = ptMouse.x + width;
        const float y1 = ptMouse.y + height;

        ellipse = D2D1::Ellipse(D2D1::Point2F(x1, y1), width, height);

        InvalidateRect(m_hwnd, NULL, FALSE);
    }
}

Botón izquierdo hacia arriba

Para el mensaje de botón izquierdo, simplemente llame a ReleaseCapture para liberar la captura del mouse.

void MainWindow::OnLButtonUp()
{
    ReleaseCapture(); 
}

Siguientes