Bagikan melalui


Gerakan mouse

Ketika mouse bergerak, Windows memposting pesan WM_MOUSEMOVE . Secara default, WM_MOUSEMOVE masuk ke jendela yang berisi kursor. Anda dapat mengambil alih perilaku ini dengan menangkap mouse, yang dijelaskan di bagian berikutnya.

Pesan WM_MOUSEMOVE berisi parameter yang sama dengan pesan untuk klik mouse. 16 bit terendah lParam berisi koordinat x, dan 16 bit berikutnya berisi koordinat y. Gunakan makro GET_X_LPARAM dan GET_Y_LPARAM untuk membuka kemasan koordinat dari lParam. Parameter wParam berisi bendera OR bitwise, yang menunjukkan status tombol mouse lainnya ditambah tombol SHIFT dan CTRL. Kode berikut mendapatkan koordinat mouse dari lParam.

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

Ingat bahwa koordinat ini dalam piksel, bukan piksel independen perangkat (DIP). Nantinya dalam topik ini, kita akan melihat kode yang mengonversi antara dua unit.

Jendela juga dapat menerima pesan WM_MOUSEMOVE jika posisi kursor berubah relatif terhadap jendela. Misalnya, jika kursor diposisikan di atas jendela, dan pengguna menyembunyikan jendela, jendela menerima WM_MOUSEMOVE pesan bahkan jika mouse tidak bergerak. Salah satu konsekuensi dari perilaku ini adalah bahwa koordinat mouse mungkin tidak berubah antara pesan WM_MOUSEMOVE .

Menangkap gerakan mouse di luar jendela

Secara default, jendela berhenti menerima pesan WM_MOUSEMOVE jika mouse bergerak melewati tepi area klien. Tetapi untuk beberapa operasi, Anda mungkin perlu melacak posisi mouse di luar titik ini. Misalnya, program gambar mungkin memungkinkan pengguna untuk menyeret persegi panjang pilihan di luar tepi jendela, seperti yang ditunjukkan pada diagram berikut.

ilustrasi pengambilan mouse.

Untuk menerima pesan pemindahan mouse melewati tepi jendela, panggil fungsi SetCapture . Setelah fungsi ini dipanggil, jendela akan terus menerima pesan WM_MOUSEMOVE selama pengguna menahan setidaknya satu tombol mouse ke bawah, bahkan jika mouse bergerak di luar jendela. Jendela pengambilan harus merupakan jendela latar depan, dan hanya satu jendela yang dapat menjadi jendela pengambilan pada satu waktu. Untuk melepaskan tangkapan mouse, panggil fungsi ReleaseCapture .

Anda biasanya akan menggunakan SetCapture dan ReleaseCapture dengan cara berikut.

  1. Saat pengguna menekan tombol mouse kiri, panggil SetCapture untuk mulai menangkap mouse.
  2. Merespons pesan pemindahan mouse.
  3. Saat pengguna merilis tombol mouse kiri, panggil ReleaseCapture.

Contoh: menggambar lingkaran

Mari kita perluas program Circle dari Modul 3 dengan memungkinkan pengguna menggambar lingkaran dengan mouse. Mulailah dengan program Sampel Lingkaran Direct2D . Kami akan memodifikasi kode dalam sampel ini untuk menambahkan gambar sederhana. Pertama, tambahkan variabel anggota baru ke MainWindow kelas .

D2D1_POINT_2F ptMouse;

Variabel ini menyimpan posisi mouse-down saat pengguna menyeret mouse. MainWindow Dalam konstruktor, inisialisasi variabel elips dan ptMouse.

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

Hapus isi MainWindow::CalculateLayout metode ; tidak diperlukan untuk contoh ini.

void CalculateLayout() { }

Selanjutnya, deklarasikan penangan pesan untuk pesan tombol kiri bawah, tombol kiri atas, dan pemindahan mouse.

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

Koordinat mouse diberikan dalam piksel fisik, tetapi Direct2D mengharapkan piksel independen perangkat (DIP). Untuk menangani pengaturan DPI tinggi dengan benar, Anda harus menerjemahkan koordinat piksel ke dalam DIP. Untuk diskusi selengkapnya tentang DPI, lihat DPI dan Device-Independent Pixels. Kode berikut menunjukkan kelas pembantu yang mengonversi piksel menjadi 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;

Panggil DPIScale::Initialize di handler WM_CREATE Anda, setelah Anda membuat objek pabrik Direct2D.

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

Untuk mendapatkan koordinat mouse di DIP dari pesan mouse, lakukan hal berikut:

  1. Gunakan makro GET_X_LPARAM dan GET_Y_LPARAM untuk mendapatkan koordinat piksel. Makro ini didefinisikan dalam WindowsX.h, jadi ingatlah untuk menyertakan header tersebut dalam proyek Anda.
  2. Panggil DPIScale::PixelsToDips untuk mengonversi piksel menjadi DIP.

Sekarang tambahkan penangan pesan ke prosedur jendela Anda.

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;

Terakhir, terapkan penangan pesan itu sendiri.

Tombol kiri ke bawah

Untuk pesan bawah tombol kiri, lakukan hal berikut:

  1. Panggil SetCapture untuk mulai menangkap mouse.
  2. Simpan posisi klik mouse di variabel ptMouse . Posisi ini mendefinisikan sudut kiri atas kotak pembatas untuk elips.
  3. Atur ulang struktur elips.
  4. Panggil InvalidateRect. Fungsi ini memaksa jendela untuk dicat ulang.
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);
}

Gerakan mouse

Untuk pesan pemindahan mouse, periksa apakah tombol mouse kiri tidak berfungsi. Jika ya, hitung ulang elips dan repaint jendela. Di Direct2D, elips didefinisikan oleh titik tengah dan x- dan y-radii. Kami ingin menggambar elips yang sesuai dengan kotak pembatas yang ditentukan oleh titik mouse-bawah (ptMouse) dan posisi kursor saat ini (x, y), sehingga sedikit aritmatika diperlukan untuk menemukan lebar, tinggi, dan posisi elips.

Kode berikut menghitung ulang elips lalu memanggil InvalidateRect untuk mengecat ulang jendela.

Diagram yang memperlihatkan elips dengan radius x dan 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);
    }
}

Tombol kiri ke atas

Untuk pesan tombol kiri ke atas, cukup panggil ReleaseCapture untuk melepaskan tangkapan mouse.

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

Berikutnya