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.
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.
- Když uživatel stiskne levé tlačítko myši, zavolejte SetCapture začít zachytávat myš.
- Odpovězte na zprávy přesunutí myší.
- 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:
- 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í.
- 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:
- Volání SetCapture začít zachytávat myš.
- 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.
- Resetujte strukturu tří teček.
- 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.
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();
}