Partager via


Déplacement de la souris

Lorsque la souris se déplace, Windows publie un message WM_MOUSEMOVE . Par défaut, WM_MOUSEMOVE accède à la fenêtre qui contient le curseur. Vous pouvez remplacer ce comportement en capturant la souris, comme décrit dans la section suivante.

Le message WM_MOUSEMOVE contient les mêmes paramètres que les messages pour les clics de souris. Les 16 bits les plus bas de lParam contiennent la coordonnée x, et les 16 bits suivants contiennent la coordonnée y. Utilisez les macros GET_X_LPARAM et GET_Y_LPARAM pour décompresser les coordonnées de lParam. Le paramètre wParam contient un OR au niveau du bit d’indicateurs, indiquant l’état des autres boutons de la souris, ainsi que les touches Maj et Ctrl. Le code suivant obtient les coordonnées de la souris à partir de lParam.

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

N’oubliez pas que ces coordonnées sont exprimées en pixels, et non en pixels indépendants de l’appareil (DIPs). Plus loin dans cette rubrique, nous allons examiner le code qui convertit entre les deux unités.

Une fenêtre peut également recevoir un message WM_MOUSEMOVE si la position du curseur change par rapport à la fenêtre. Par exemple, si le curseur est positionné sur une fenêtre et que l’utilisateur masque la fenêtre, la fenêtre reçoit WM_MOUSEMOVE messages même si la souris n’a pas été déplacée. L’une des conséquences de ce comportement est que les coordonnées de la souris peuvent ne pas changer entre WM_MOUSEMOVE messages.

Capture du mouvement de la souris à l’extérieur de la fenêtre

Par défaut, une fenêtre cesse de recevoir des messages WM_MOUSEMOVE si la souris dépasse le bord de la zone cliente. Toutefois, pour certaines opérations, vous devrez peut-être suivre la position de la souris au-delà de ce point. Par exemple, un programme de dessin peut permettre à l’utilisateur de faire glisser le rectangle de sélection au-delà du bord de la fenêtre, comme illustré dans le diagramme suivant.

illustration de la capture de la souris.

Pour recevoir des messages de déplacement de souris au-delà du bord de la fenêtre, appelez la fonction SetCapture . Une fois cette fonction appelée, la fenêtre continue de recevoir des messages WM_MOUSEMOVE tant que l’utilisateur maintient au moins un bouton de la souris vers le bas, même si la souris se déplace à l’extérieur de la fenêtre. La fenêtre de capture doit être la fenêtre de premier plan, et une seule fenêtre peut être la fenêtre de capture à la fois. Pour libérer la capture de la souris, appelez la fonction ReleaseCapture .

Vous utilisez généralement SetCapture et ReleaseCapture de la manière suivante.

  1. Lorsque l’utilisateur appuie sur le bouton gauche de la souris, appelez SetCapture pour commencer à capturer la souris.
  2. Répondre aux messages de déplacement de la souris.
  3. Lorsque l’utilisateur relâche le bouton gauche de la souris, appelez ReleaseCapture.

Exemple : dessiner des cercles

Étendons le programme Circle à partir du module 3 en permettant à l’utilisateur de dessiner un cercle avec la souris. Commencez par l’exemple de programme Cercle Direct2D . Nous allons modifier le code de cet exemple pour ajouter un dessin simple. Tout d’abord, ajoutez une nouvelle variable membre à la MainWindow classe .

D2D1_POINT_2F ptMouse;

Cette variable stocke la position de la souris vers le bas pendant que l’utilisateur fait glisser la souris. Dans le MainWindow constructeur, initialisez les variables d’ellipse et ptMouse .

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

Supprimez le corps de la MainWindow::CalculateLayout méthode ; il n’est pas nécessaire pour cet exemple.

void CalculateLayout() { }

Ensuite, déclarez les gestionnaires de messages pour les messages du bouton gauche vers le bas, du bouton gauche vers le haut et du déplacement de la souris.

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

Les coordonnées de la souris sont fournies en pixels physiques, mais Direct2D s’attend à des pixels indépendants de l’appareil (DIPs). Pour gérer correctement les paramètres à haute résolution, vous devez traduire les coordonnées de pixels en DIPs. Pour plus d’informations sur les PPP, consultez PPP et pixels Device-Independent. Le code suivant montre une classe d’assistance qui convertit des pixels en 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;

Appelez DPIScale::Initialize dans votre gestionnaire de WM_CREATE , après avoir créé l’objet de fabrique Direct2D.

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

Pour obtenir les coordonnées de la souris dans les diPs à partir des messages de souris, procédez comme suit :

  1. Utilisez les macros GET_X_LPARAM et GET_Y_LPARAM pour obtenir les coordonnées de pixels. Ces macros étant définies dans WindowsX.h, n’oubliez pas d’inclure cet en-tête dans votre projet.
  2. Appelez DPIScale::PixelsToDips pour convertir des pixels en DIPs.

Ajoutez maintenant les gestionnaires de messages à votre procédure de fenêtre.

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;

Enfin, implémentez les gestionnaires de messages eux-mêmes.

Bouton gauche vers le bas

Pour le message du bouton gauche vers le bas, procédez comme suit :

  1. Appelez SetCapture pour commencer à capturer la souris.
  2. Stockez la position du clic de la souris dans la variable ptMouse . Cette position définit l’angle supérieur gauche du cadre englobant pour l’ellipse.
  3. Réinitialisez la structure d’ellipse.
  4. Appelez InvalidateRect. Cette fonction force la fenêtre à être repeinte.
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);
}

Déplacement de la souris

Pour le message de déplacement de la souris, case activée si le bouton gauche de la souris est désactivé. Si c’est le cas, recalculez l’ellipse et repeindre la fenêtre. Dans Direct2D, une ellipse est définie par le point central et les rayons x et y. Nous voulons dessiner une ellipse qui correspond au cadre englobant défini par le point bas de la souris (ptMouse) et la position actuelle du curseur (x, y). Un peu d’arithmétique est donc nécessaire pour trouver la largeur, la hauteur et la position de l’ellipse.

Le code suivant recalcule l’ellipse, puis appelle InvalidateRect pour repeindre la fenêtre.

Diagramme montrant une ellipse avec des rayons x et 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);
    }
}

Bouton gauche vers le haut

Pour le message du bouton de gauche, appelez simplement ReleaseCapture pour libérer la capture de la souris.

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

Suivant