Sdílet prostřednictvím


Pohyb myši

Když se myš přesune, Windows publikuje zprávu WM_MOUSEMOVE. Ve výchozím nastavení WM_MOUSEMOVE přejde do okna, které obsahuje kurzor. Toto chování můžete přepsat zachycením myši, která je popsaná v další části.

Zpráva WM_MOUSEMOVE obsahuje stejné parametry jako zprávy pro kliknutí myší. Nejnižších 16 bitů lParam obsahují souřadnici x a dalších 16 bitů obsahuje souřadnici y. Pomocí GET_X_LPARAM a GET_Y_LPARAM maker rozbalte souřadnice z lParam. Parametr wParam obsahuje bitové NEBO příznaků, které označují stav ostatních tlačítek myši a kláves SHIFT a CTRL. Následující kód získá souřadnice myši z lParam.

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

Mějte na paměti, že tyto souřadnice jsou v pixelech, nikoli v pixelech nezávislých na zařízeních (DIPS). Později v tomto tématu se podíváme na kód, který převádí mezi těmito dvěma jednotkami.

Okno může také obdržet WM_MOUSEMOVE zprávu, pokud se pozice kurzoru změní vzhledem k oknem. Pokud je například kurzor umístěný nad oknem a uživatel okno skryje, okno obdrží WM_MOUSEMOVE zprávy, i když se myš nepřesunula. Jedním z důsledků tohoto chování je, že souřadnice myši se nemusí mezi WM_MOUSEMOVE zprávami měnit.

Zachycení pohybu myši mimo okno

Ve výchozím nastavení přestane okno přijímat WM_MOUSEMOVE zprávy, pokud se myš přesune za okraj klientské oblasti. U některých operací ale možná budete muset sledovat pozici myši nad tímto bodem. Například výkresový program může uživateli umožnit přetáhnout obdélník výběru za okraj okna, jak je znázorněno v následujícím diagramu.

obrázek zachycení myši.

Chcete-li přijímat zprávy přesunutí myší za okraj okna, zavolejte funkci SetCapture. Po zavolání této funkce bude okno nadále přijímat WM_MOUSEMOVE zprávy, pokud uživatel drží aspoň jedno tlačítko myši dolů, i když se myš přesune mimo okno. Okno zachycení musí být oknem popředí a současně může být jen jedním oknem pro zachycení. Pokud chcete uvolnit zachycení myši, zavolejte funkci ReleaseCapture.

Obvykle byste použili SetCapture a ReleaseCapture následujícím způsobem.

  1. Když uživatel stiskne levé tlačítko myši, zavolejte SetCapture začít zachytávat myš.
  2. Odpovězte na zprávy přesunutí myší.
  3. Když uživatel uvolní levé tlačítko myši, zavolejte ReleaseCapture.

Příklad: Kreslení kruhů

Pojďme rozšířit program Circle z Modulu 3 tím, že uživateli umožníte kreslit kruh myší. Začněte programem Direct2D Circle Sample. Kód v této ukázce upravíme tak, aby se přidal jednoduchý výkres. Nejprve do třídy MainWindow přidejte novou členovou proměnnou.

D2D1_POINT_2F ptMouse;

Tato proměnná ukládá pozici myši dolů, zatímco uživatel přetahuje myš. V konstruktoru MainWindow inicializuje tři tečky a proměnných ptMouse.

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

Odeberte tělo MainWindow::CalculateLayout metody; v tomto příkladu to nevyžaduje.

void CalculateLayout() { }

Dále deklarujte obslužné rutiny zpráv pro levé tlačítko dolů, levé tlačítko nahoru a zprávy přesunutí myší.

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

Souřadnice myši jsou uvedeny ve fyzických pixelech, ale Direct2D očekává pixely nezávislé na zařízení (DIP). Chcete-li správně zpracovat nastavení vysokého DPI, je nutné přeložit souřadnice pixelů do BODŮ. Další informace o DPI naleznete v tématu DPI a Device-Independent Pixely. Následující kód ukazuje pomocnou třídu, která převádí pixely na 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;

Volání DPIScale::Initialize v obslužné rutině WM_CREATE po vytvoření objektu továrny Direct2D.

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

Pokud chcete z zpráv myši získat souřadnice myši v zprávách myši, postupujte takto:

  1. Pomocí GET_X_LPARAM a GET_Y_LPARAM maker získejte souřadnice pixelů. Tato makra jsou definována ve WindowsX.h, takže nezapomeňte do projektu zahrnout toto záhlaví.
  2. Volání DPIScale::PixelsToDips pro převod pixelů na DIP.

Teď přidejte obslužné rutiny zpráv do procedury okna.

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;

Nakonec implementujte samotné obslužné rutiny zpráv.

Levé tlačítko dolů

U zprávy s levým tlačítkem dolů postupujte takto:

  1. Volání SetCapture začít zachytávat myš.
  2. Umístění kliknutí myší uložte do proměnné ptMouse. Tato pozice definuje levý horní roh ohraničujícího rámečku pro tři tečky.
  3. Resetujte strukturu tří teček.
  4. Volání InvalidateRect. Tato funkce vynutí překreslení okna.
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);
}

Přesunutí myší

Zkontrolujte, jestli je levé tlačítko myši mimo provoz. Pokud ano, přepočítejte tři tečky a překreslete okno. V Direct2D je elipsa definována středovým bodem a x- a y-radii. Chceme nakreslit tři tečky, které odpovídají ohraničujícímu rámečku definovanému ukazatelem myši (ptMouse) a aktuální pozici kurzoru (x, y), takže k nalezení šířky, výšky a pozice elipsy je potřeba trochu aritmetiky.

Následující kód přepočítá tři tečky a potom zavolá InvalidateRect překreslit okno.

diagram znázorňující tři tečky s poloměry x a 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);
    }
}

Levé tlačítko nahoru

V případě zprávy s levým tlačítkem jednoduše zavolejte ReleaseCapture uvolnit zachycení myši.

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

Další