마우스 이동
마우스가 이동하면 Windows에서 WM_MOUSEMOVE 메시지를 게시합니다. 기본적으로 WM_MOUSEMOVE는 커서가 포함된 창으로 이동합니다. 마우스를 캡처하여 이 동작을 재정의할 수 있으며 이 내용은 다음 섹션에서 설명합니다.
WM_MOUSEMOVE 메시지에는 마우스 클릭에 대한 메시지와 동일한 매개 변수가 포함됩니다. lParam의 하위 16비트에는 x 좌표가 포함되고 다음 16비트에는 y 좌표가 포함됩니다. GET_X_LPARAM 및 GET_Y_LPARAM 매크로를 사용하여 lParam에서 좌표를 분석합니다. wParam 매개 변수에는 다른 마우스 단추의 상태와 Shift 및 Ctrl 키를 나타내는 비트 OR 플래그가 포함되어 있습니다. 다음 코드는 lParam에서 마우스 좌표를 가져옵니다.
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
이러한 좌표는 DIP(장치 독립적 픽셀)가 아닌 픽셀 단위입니다. 이 항목의 뒷부분에서는 두 단위 간을 변환하는 코드를 살펴보겠습니다.
창을 기준으로 커서의 위치가 변경되면 창에서 WM_MOUSEMOVE 메시지를 받을 수도 있습니다. 예를 들어 커서가 창 위에 배치되고 사용자가 창을 숨기면 마우스가 움직이지 않더라도 창이 WM_MOUSEMOVE 메시지를 수신합니다. 이 동작의 한 가지 결과는 마우스 좌표가 WM_MOUSEMOVE 메시지 간에 변경되지 않을 수 있다는 것입니다.
창 밖에서 마우스 이동 캡처
기본적으로 창은 마우스가 클라이언트 영역의 가장자리를 지나 이동하는 경우 WM_MOUSEMOVE 메시지 수신을 중지합니다. 그러나 일부 작업의 경우 이 지점을 벗어나는 마우스 위치를 추적해야 할 수 있습니다. 예를 들어 그리기 프로그램을 사용하면 사용자가 다음 다이어그램과 같이 선택 영역 사각형을 창 가장자리 너머로 끌 수 있습니다.
창 가장자리를 지나 마우스 이동 메시지를 받으려면 SetCapture 함수를 호출합니다. 이 함수가 호출되면 마우스가 창 밖으로 이동하더라도 사용자가 마우스 단추를 하나 이상 누르는 동안에는 창에서 WM_MOUSEMOVE 메시지가 계속 수신됩니다. 캡처 창은 포그라운드 창이어야 하며 한 번에 하나의 창만 캡처 창이 될 수 있습니다. 마우스 캡처를 해제하려면 ReleaseCapture 함수를 호출합니다.
일반적으로 다음과 같은 방법으로 SetCapture 및 ReleaseCapture를 사용합니다.
- 사용자가 마우스 왼쪽 단추를 누르면 SetCapture를 호출하여 마우스 캡처를 시작합니다.
- 마우스 이동 메시지에 응답합니다.
- 사용자가 마우스 왼쪽 단추를 놓으면 ReleaseCapture를 호출합니다.
예: 원 그리기
사용자가 마우스로 원을 그릴 수 있도록 하여 모듈 3의 Circle 프로그램을 확장해 보겠습니다.
Direct2D Circle 샘플 프로그램으로 시작합니다. 간단한 그리기를 추가하도록 이 샘플의 코드를 수정합니다. 먼저 MainWindow
클래스에 새 멤버 변수를 추가합니다.
D2D1_POINT_2F ptMouse;
이 변수는 사용자가 마우스를 끄는 동안 마우스 누름 위치를 저장합니다.
MainWindow
생성자에서 ellipse 및 ptMouse 변수를 초기화합니다.
MainWindow() : pFactory(NULL), pRenderTarget(NULL), pBrush(NULL),
ellipse(D2D1::Ellipse(D2D1::Point2F(), 0, 0)),
ptMouse(D2D1::Point2F())
{
}
MainWindow::CalculateLayout
메서드의 본문을 제거합니다. 이 예제에서는 필요하지 않습니다.
void CalculateLayout() { }
다음으로, 왼쪽 단추 누름, 왼쪽 단추 놓음 및 마우스 이동 메시지에 대한 메시지 처리기를 선언합니다.
void OnLButtonDown(int pixelX, int pixelY, DWORD flags);
void OnLButtonUp();
void OnMouseMove(int pixelX, int pixelY, DWORD flags);
마우스 좌표는 실제 픽셀로 제공되지만 Direct2D에는 DIP(장치 독립적 픽셀)로 제공되어야 합니다. 높은 DPI 설정을 올바르게 처리하려면 픽셀 좌표를 DIP로 변환해야 합니다. DPI에 대한 자세한 내용은 DPI 및 장치 독립적 픽셀을 참조하세요. 다음 코드에서는 픽셀을 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;
Direct2D 팩터리 개체를 만든 후 WM_CREATE 처리기에서 DPIScale::Initialize를 호출합니다.
case WM_CREATE:
if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory)))
{
return -1; // Fail CreateWindowEx.
}
DPIScale::Initialize(hwnd);
return 0;
마우스 메시지에서 DIP의 마우스 좌표를 얻으려면 다음을 수행합니다.
- GET_X_LPARAM 및 GET_Y_LPARAM 매크로를 사용하여 픽셀 좌표를 가져옵니다. 이러한 매크로는 WindowsX.h에서 정의되므로 프로젝트에 해당 헤더를 포함해야 합니다.
- 를 호출
DPIScale::PixelsToDips
하여 픽셀을 DIP로 변환합니다.
이제 창 프로시저에 메시지 처리기를 추가합니다.
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;
마지막으로 메시지 처리기 자체를 구현합니다.
왼쪽 단추 누름
왼쪽 단추 누름 메시지의 경우 다음을 수행합니다.
- SetCapture를 호출하여 마우스 캡처를 시작합니다.
- 마우스 클릭 위치를 ptMouse 변수에 저장합니다. 이 위치는 타원에 대한 경계 상자의 왼쪽 위 모서리를 정의합니다.
- 타원 구조를 다시 설정합니다.
- InvalidateRect를 호출합니다. 이 함수는 창을 강제로 다시 페인팅합니다.
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);
}
마우스 이동
마우스 이동 메시지의 경우 마우스 왼쪽 단추가 누름 상태인지 여부를 확인합니다. 이 경우 타원을 다시 계산하고 창을 다시 페인팅합니다. Direct2D에서 타원은 중심점과 x 및 y 반경으로 정의됩니다. 마우스 누름 포인트(ptMouse)와 현재 커서 위치(x, y)로 정의된 경계 상자에 맞는 타원을 그리려고 하므로 타원의 너비, 높이 및 위치를 찾으려면 약간의 산술 연산이 필요합니다.
다음 코드는 타원을 다시 계산한 다음, InvalidateRect를 호출하여 창을 다시 페인팅합니다.
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);
}
}
왼쪽 단추 놓음
왼쪽 단추 놓음 메시지의 경우 ReleaseCapture를 호출하여 마우스 캡처를 해제합니다.
void MainWindow::OnLButtonUp()
{
ReleaseCapture();
}