Skip to content Skip to footer

Разработка собственных компонентов – Захват мыши

В продолжение темы о создании собственных компонентов wxWidgets решил написать эту заметку. Касается наш сегодняшний разговор обработки событий от мыши, а точнее, захвату мыши компонентом при нажатии. Что есть захват мыши? Это когда наш компонент продолжает обрабатывать события, поступающие при передвижении курсора мыши, даже когда сам курсор находится вне компонента.
Для начала создадим простенький компонент и хост-приложение для него:

MouseCaptureTestCtrl.h

#ifndef _MOUSE_CAPTURE_TEST_CONTROL_H
#define _MOUSE_CAPTURE_TEST_CONTROL_H

#include <wx/wx.h>

class MouseCaptureTestControl : public wxControl
{
public:
	MouseCaptureTestControl(wxWindow * parent,
		wxWindowID id = wxID_ANY,
		const wxPoint & pos = wxDefaultPosition,
		const wxSize & size = wxDefaultSize,
		long style = wxNO_BORDER);
	DECLARE_EVENT_TABLE()
	void OnPaint(wxPaintEvent & event);
	void OnEraseBackground(wxEraseEvent & event);
	void OnMouseMove(wxMouseEvent & event);
};

#endif

MouseCaptureTestCtrl.cpp

#include "MouseCaptureTestControl.h"
#include <wx/dcbuffer.h>

BEGIN_EVENT_TABLE(MouseCaptureTestControl, wxControl)
EVT_PAINT(MouseCaptureTestControl::OnPaint)
EVT_ERASE_BACKGROUND(MouseCaptureTestControl::OnEraseBackground)
EVT_MOTION(MouseCaptureTestControl::OnMouseMove)
END_EVENT_TABLE()

MouseCaptureTestControl::MouseCaptureTestControl(wxWindow * parent,
	wxWindowID id, const wxPoint & pos, const wxSize & size, long style)
	: wxControl(parent, id, pos, size, style)
{
}

void MouseCaptureTestControl::OnPaint(wxPaintEvent & event)
{
	wxBufferedPaintDC dc(this);
	dc.SetBackground(wxBrush(GetBackgroundColour()));
	dc.Clear();
	wxPoint pos = ScreenToClient(wxGetMousePosition());
	dc.SetFont(GetFont());
	dc.SetTextForeground(GetForegroundColour());
	dc.DrawLabel(wxString::Format(wxT("%i,%i"), pos.x, pos.y), wxNullBitmap,
		GetClientRect(), wxALIGN_CENTER);
}

void MouseCaptureTestControl::OnEraseBackground(wxEraseEvent & event)
{
}

void MouseCaptureTestControl::OnMouseMove(wxMouseEvent & event)
{
	Refresh();
}

wxTest.cpp

#include <wx/wx.h>
#include "MouseCaptureTestControl.h"

class MyApp : public wxApp
{
	virtual bool OnInit();
};

DECLARE_APP(MyApp);

class MyFrame : public wxFrame
{
	void CreateControls();
public:
	bool Create(wxWindow * parent)
	{
		bool res = wxFrame::Create(parent, wxID_ANY, wxT("Da, Yo!"),
			wxDefaultPosition, wxSize(400, 300));
		if(res)
		{
			CreateControls();
		}
		return res;
	}
	MyFrame()
	{
		Create(NULL);
	}
	DECLARE_EVENT_TABLE()
	void OnExit(wxCommandEvent & event)
	{
		Close();
	}
};

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_EXIT, MyFrame::OnExit)
END_EVENT_TABLE()


void MyFrame::CreateControls()
{
	wxMenuBar * menuBar = new wxMenuBar;
	SetMenuBar(menuBar);

	wxMenu * fileMenu = new wxMenu;
	fileMenu->Append(wxID_EXIT, _("Exit\tAlt+F4"));
	menuBar->Append(fileMenu, _("File"));

	wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(sizer);

	MouseCaptureTestControl * control = new MouseCaptureTestControl(this, wxID_ANY);
	sizer->Add(control, 1, wxEXPAND);

	CreateStatusBar();
}

bool MyApp::OnInit()
{
	MyFrame * frame = new MyFrame;
	SetTopWindow(frame);
	frame->Centre();
	frame->Show();
	return true;
}

IMPLEMENT_APP(MyApp);

Итак, что же у нас получилось. У нас получился новый компонент, который при перемещении над ним курсора мыши отображает координаты мыши.

Для того чтобы наш компонент продолжал обрабатывать событие wxEVT_MOTION даже когда курсор находится вне компонента нам необходимо добавить пару обработчиков – обработчики нажатия и отжатия левой кнопки мыши.

MouseCaptureTestCtrl.h

...
class MouseCaptureTestControl : public wxControl
{
	DECLARE_EVENT_TABLE()
	...
	void OnLeftMouseDown(wxMouseEvent & event);
	void OnLeftMouseUp(wxMouseEvent & event);
};
...

MouseCaptureTestCtrl.cpp

...
BEGIN_EVENT_TABLE(MouseCaptureTestControl, wxControl)
...
EVT_LEFT_DOWN(MouseCaptureTestControl::OnLeftMouseDown)
EVT_LEFT_UP(MouseCaptureTestControl::OnLeftMouseUp)
END_EVENT_TABLE()
...
void MouseCaptureTestControl::OnLeftMouseDown(wxMouseEvent & event)
{
	CaptureMouse();
}

void MouseCaptureTestControl::OnLeftMouseUp(wxMouseEvent & event)
{
	ReleaseMouse();
}

Когда пользователь нажимает левой кнопкой мыши на компоненте, происходит захват мыши, и пока кнопка не будет отжата, компонент будет отрисовывать новые значения координат мыши при перемещении курсора. Такой функционал может быть востребован при разработке, например, графических редакторов, когда курсор мыши может выходить за пределы области рисования. Вот это мы сейчас и рассмотрим на простеньком примере.

MouseCaptureTestCtrl.h

...
#include <wx/dynarray.h>

class MyLine
{
	wxPoint start;
	wxPoint end;
	MyLine(wxPoint s, wxPoint e) : start(s), end(e) {}
	void Draw(wxDC & dc)
	{
		dc.DrawLine(start, end);
	}
};

WX_DECLARE_OBJARRAY(MyLine, MyLineArray);

class MouseCaptureTestControl : public wxControl
{
	wxPoint m_LineStart;
	MyLineArray m_Lines;
...
};
...

MouseCaptureTestCtrl.cpp

...
#include <wx/arrimpl.cpp>

WX_DEFINE_OBJARRAY(MyLineArray)

...
void MouseCaptureTestControl::OnPaint(wxPaintEvent & event)
{
	wxBufferedPaintDC dc(this);
	dc.SetBackground(wxBrush(GetBackgroundColour()));
	dc.Clear();
	wxPoint pos = ScreenToClient(wxGetMousePosition());
	dc.SetFont(GetFont());
	dc.SetTextForeground(GetForegroundColour());
	dc.DrawLabel(wxString::Format(wxT("%i,%i"), pos.x, pos.y), wxNullBitmap,
		GetClientRect(), wxALIGN_CENTER);
	for(size_t i = 0; i < m_Lines.Count(); i++)
	{
		m_Lines&#91;i&#93;.Draw(dc);
	}
}
...
void MouseCaptureTestControl::OnLeftMouseDown(wxMouseEvent & event)
{
	CaptureMouse();
	m_LineStart = event.GetPosition();
}

void MouseCaptureTestControl::OnLeftMouseUp(wxMouseEvent & event)
{
	if(GetCapture() == this)
	{
		m_Lines.Add(MyLine(m_LineStart, event.GetPosition()));
	}
	ReleaseMouse();
	Refresh();
}
&#91;/sourcecode&#93;
Итак, что у нас получилось. Мы создали новый класс <code>MyLine</code> и добавили в наш компонент массив объектов этого класса. При нажатии кнопки мыши мы запоминаем координату, где было произведено нажатие, а пи отжатии добавляем в массив новую линию, концы которой соответствуют точке нажатия и отжатия кнопки мыши. Ну и сам компонент отрисовывает все линии в обработчике события <code>wxEVT_PAINT</code>.

<center><img src="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2009/01/mouse_capture_2.png"></center>
Все это чудесно, но чего-то не хватает. А не хватает у нас отрисовки создаваемой линии в процессе перемещения курсора при нажатой левой кнопке.  Для того чтобы добавить эту отрисовку нужно дописать несколько строк:

<b>MouseCaptureTestCtrl.cpp</b>
[sourcecode language="cpp"]
...
void MouseCaptureTestControl::OnPaint(wxPaintEvent & event)
{
	...
	if(GetCapture() == this)
	{
		MyLine(m_LineStart, pos).Draw(dc);
	}
}
...

Ну вот, как видно, линия продолжает отрисовываться даже когда курсор находится за пределами компонента, что, собственно, и нужно было продемонстрировать.

Скачать исходный код примера.

Leave a comment

0.0/5