마우스 이동
마우스가 이동하면 Windows에서 WM_MOUSEMOVE 메시지를 게시합니다. 기본적으로 WM_MOUSEMOVE 커서가 포함된 창으로 이동합니다. 다음 섹션에서 설명하는 마우스를 캡처하는 이 동작을 재정의할 수 있습니다.
WM_MOUSEMOVE 메시지에는 마우스 클릭에 대한 메시지와 동일한 매개 변수가 포함됩니다. 가장 낮은 16비트 lParam 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 원 프로그램을 확장해 보겠습니다.
Direct2D Circle 샘플 프로그램으로 시작합니다. 간단한 그리기를 추가하도록 이 샘플의 코드를 수정합니다. 먼저 MainWindow
클래스에 새 멤버 변수를 추가합니다.
D2D1_POINT_2F ptMouse;
이 변수는 사용자가 마우스를 끄는 동안 마우스 아래쪽 위치를 저장합니다.
MainWindow
생성자에서 줄임표 초기화하고 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 및 Device-Independent Pixels참조하세요. 다음 코드는 픽셀을 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-radii로 정의됩니다. 마우스 아래쪽 점(ptMouse)과 현재 커서 위치(x, y)로 정의된 경계 상자에 맞는 타원을 그리려고 하므로 줄임표의 너비, 높이 및 위치를 찾으려면 약간의 산술 연산이 필요합니다.
다음 코드는 줄임표를 다시 계산한 다음 InvalidateRect 호출하여 창을 다시 칠합니다.
x 및 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);
}
}
왼쪽 단추 위로
왼쪽 단추 위로 메시지의 경우 ReleaseCapture 호출하여 마우스 캡처를 해제합니다.
void MainWindow::OnLButtonUp()
{
ReleaseCapture();
}