マウスの移動
マウスが移動すると、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) ではないことに注意してください。 このトピックの後半では、2 つのユニット間で変換するコードについて説明します。
ウィンドウは、カーソルの位置がウィンドウに対して相対的に変化した場合に、 WM_MOUSEMOVE メッセージを受信することもできます。 たとえば、カーソルをウィンドウの上に置き、ユーザーがウィンドウを非表示にした場合、マウスが移動しなかった場合でも、ウィンドウ はWM_MOUSEMOVE メッセージを受信します。 この動作の結果の 1 つは、WM_MOUSEMOVEメッセージ間でマウス座標 が 変更されないことです。
ウィンドウの外でのマウスの動きのキャプチャ
既定では、マウスがクライアント領域の端を越えると、ウィンドウは WM_MOUSEMOVE メッセージの受信を停止します。 ただし、一部の操作では、このポイントを超えるマウスの位置を追跡することが必要な場合があります。 たとえば、次の図に示すように、描画プログラムを使用すると、ユーザーは選択四角形をウィンドウの端の向こうにドラッグできます。
ウィンドウの端を越えてマウス移動メッセージを受信するには、 SetCapture 関数を呼び出します。 この関数が呼び出されると、ユーザーが少なくとも 1 つのマウス ボタンを押したままウィンドウの外に移動した場合でも、ウィンドウは引き続きWM_MOUSEMOVEメッセージを受信します。 キャプチャ ウィンドウはフォアグラウンド ウィンドウである必要があり、一度にキャプチャ ウィンドウにできるウィンドウは 1 つだけです。 マウス キャプチャを解放するには、 ReleaseCapture 関数を呼び出します。
通常は、次の方法で SetCapture と ReleaseCapture を使用します。
- ユーザーがマウスの左ボタンを押すと、 SetCapture を呼び出してマウスのキャプチャを開始します。
- マウス移動メッセージに応答します。
- ユーザーがマウスの左ボタンを離したら、 ReleaseCapture を呼び出します。
例: 円の描画
ユーザーがマウスで円を描画できるようにすることで、 モジュール 3 から Circle プログラムを拡張してみましょう。
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 ピクセル」を参照してください。 次のコードは、ピクセルを 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();
}