Compartilhar via


Movimento do mouse

Quando o mouse se move, o Windows posta uma mensagem WM_MOUSEMOVE . Por padrão, WM_MOUSEMOVE vai para a janela que contém o cursor. Você pode substituir esse comportamento capturando o mouse, que é descrito na próxima seção.

A mensagem WM_MOUSEMOVE contém os mesmos parâmetros que as mensagens para cliques do mouse. Os 16 bits mais baixos de lParam contêm a coordenada x e os próximos 16 bits contêm a coordenada y. Use as macros GET_X_LPARAM e GET_Y_LPARAM para desempacotar as coordenadas do lParam. O parâmetro wParam contém um OR bit a bit de sinalizadores, indicando o estado dos outros botões do mouse mais as teclas SHIFT e CTRL. O código a seguir obtém as coordenadas do mouse do lParam.

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

Lembre-se de que essas coordenadas estão em pixels, não em DIPs (pixels independentes de dispositivo). Posteriormente neste tópico, examinaremos o código que converte entre as duas unidades.

Uma janela também poderá receber uma mensagem WM_MOUSEMOVE se a posição do cursor for alterada em relação à janela. Por exemplo, se o cursor estiver posicionado sobre uma janela e o usuário ocultar a janela, a janela receberá WM_MOUSEMOVE mensagens mesmo que o mouse não tenha se movido. Uma consequência desse comportamento é que as coordenadas do mouse podem não mudar entre mensagens WM_MOUSEMOVE .

Capturando o movimento do mouse fora da janela

Por padrão, uma janela para de receber mensagens WM_MOUSEMOVE se o mouse passar pela borda da área do cliente. Mas, para algumas operações, talvez seja necessário rastrear a posição do mouse além desse ponto. Por exemplo, um programa de desenho pode permitir que o usuário arraste o retângulo de seleção para além da borda da janela, conforme mostrado no diagrama a seguir.

uma ilustração da captura do mouse.

Para receber mensagens de movimentação do mouse além da borda da janela, chame a função SetCapture . Depois que essa função for chamada, a janela continuará recebendo WM_MOUSEMOVE mensagens, desde que o usuário mantenha pelo menos um botão do mouse para baixo, mesmo que o mouse se mova para fora da janela. A janela de captura deve ser a janela em primeiro plano e apenas uma janela pode ser a janela de captura por vez. Para liberar a captura do mouse, chame a função ReleaseCapture .

Normalmente, você usaria SetCapture e ReleaseCapture da seguinte maneira.

  1. Quando o usuário pressionar o botão esquerdo do mouse, chame SetCapture para começar a capturar o mouse.
  2. Responder a mensagens de movimentação do mouse.
  3. Quando o usuário liberar o botão esquerdo do mouse, chame ReleaseCapture.

Exemplo: círculos de desenho

Vamos estender o programa Círculo do Módulo 3 , permitindo que o usuário desenhe um círculo com o mouse. Comece com o programa Direct2D Circle Sample. Modificaremos o código neste exemplo para adicionar um desenho simples. Primeiro, adicione uma nova variável de membro à MainWindow classe .

D2D1_POINT_2F ptMouse;

Essa variável armazena a posição do mouse para baixo enquanto o usuário arrasta o mouse. MainWindow No construtor, inicialize as variáveis de elipse e ptMouse.

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

Remova o corpo do MainWindow::CalculateLayout método; ele não é necessário para este exemplo.

void CalculateLayout() { }

Em seguida, declare manipuladores de mensagens para o botão esquerdo para baixo, botão esquerdo para cima e mensagens de movimentação do mouse.

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

As coordenadas do mouse são fornecidas em pixels físicos, mas Direct2D espera DIPs (pixels independentes de dispositivo). Para manipular as configurações de alto DPI corretamente, você deve converter as coordenadas de pixel em DIPs. Para obter mais discussões sobre DPI, consulte DPI e pixels de Device-Independent. O código a seguir mostra uma classe auxiliar que converte pixels em DIPs.

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;

Chame DPIScale::Initialize no manipulador de WM_CREATE depois de criar o objeto de fábrica Direct2D.

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

Para obter as coordenadas do mouse em DIPs das mensagens do mouse, faça o seguinte:

  1. Use as macros GET_X_LPARAM e GET_Y_LPARAM para obter as coordenadas de pixel. Essas macros são definidas no WindowsX.h, portanto, lembre-se de incluir esse cabeçalho em seu projeto.
  2. Chame DPIScale::PixelsToDips para converter pixels em DIPs.

Agora, adicione os manipuladores de mensagens ao procedimento de janela.

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 fim, implemente os próprios manipuladores de mensagens.

Botão esquerdo para baixo

Para a mensagem de botão esquerdo para baixo, faça o seguinte:

  1. Chame SetCapture para começar a capturar o mouse.
  2. Armazene a posição do clique do mouse na variável ptMouse . Essa posição define o canto superior esquerdo da caixa delimitadora para a elipse.
  3. Redefina a estrutura de elipse.
  4. Chame InvalidateRect. Essa função força a janela a ser repintada.
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 o mouse

Para a mensagem de movimentação do mouse, marcar se o botão esquerdo do mouse está para baixo. Se for, recalcule a elipse e repinte a janela. Em Direct2D, uma elipse é definida pelo ponto central e pelos raios x e y. Queremos desenhar uma elipse que se ajuste à caixa delimitadora definida pelo ponto do mouse para baixo (ptMouse) e a posição atual do cursor (x, y), portanto, um pouco de aritmética é necessário para localizar a largura, altura e posição da elipse.

O código a seguir recalcula a elipse e chama InvalidateRect para repintar a janela.

Diagrama que mostra uma elipse com raios 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ão esquerdo para cima

Para a mensagem de botão esquerdo, basta chamar ReleaseCapture para liberar a captura do mouse.

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

Avançar