鼠标事件

鼠标事件种类分为:

1.左键按下抬起

2.右键按下抬起

3.鼠标移动(坐标)

4.滑轮上下滑动

5.鼠标进入移出客户区

对应的事件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Event
{
public:
enum class Type
{
LPress,
LRelease,
RPress,
RRelease,
WheelUp,
WheelDown,
Move,
Enter,
Leave,
Invalid
};
private:
Type type;
bool leftIsPressed;
bool rightIsPressed;
int x;
int y;
public:
Event() noexcept
:
type(Type::Invalid),
leftIsPressed(false),
rightIsPressed(false),
x(0),
y(0)
{}
Event( Type type,const Mouse& parent ) noexcept
:
type( type ),
leftIsPressed( parent.leftIsPressed ),
rightIsPressed( parent.rightIsPressed ),
x( parent.x ),
y( parent.y )
{}
Type GetType() const noexcept
{
return type;
}
std::pair<int,int> GetPos() const noexcept
{
return{ x,y };
}
int GetPosX() const noexcept
{
return x;
}
int GetPosY() const noexcept
{
return y;
}
bool LeftIsPressed() const noexcept
{
return leftIsPressed;
}
bool RightIsPressed() const noexcept
{
return rightIsPressed;
}
};

状态管理

由于鼠标键位没有键盘那么多,只需要一个Queue存储所有事件;

其他状态使用bool值记录即可;

1
2
3
4
5
6
7
int x;
int y;
bool leftIsPressed = false;
bool rightIsPressed = false;
bool isInWindow = false;
int wheelDeltaCarry = 0;
std::queue<Event> buffer;

滑轮移动:

Windows的滑轮移动事件,wParam代表滑动间隔,标准是120,可以按倍数增加;

因此滑轮检测记录数值,没到120的倍数触发一次事件;

超出屏幕:

超出屏幕有两种情况,鼠标有键位按下和没有键位按下;

按下超出,鼠标被屏幕捕获(capture,Win32Api),无法超出屏幕,松开时解除;

未按下时正常;

以下是WindowProc函数中鼠标消息的拦截处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*****************鼠标消息******************/
case WM_MOUSEMOVE:
{
const POINTS pt = MAKEPOINTS(lParam);
//是否在客户区内
if (pt.x >= 0 && pt.x < width && pt.y >= 0 && pt.y < height)
{
mouse.OnMouseMove(pt.x, pt.y);
if (!mouse.IsInWindow())
{
SetCapture(hWnd);
mouse.OnMouseEnter();
}
}
else
{
//左右键是否按下,也可以使用LeftIsPressd(),不允许鼠标按下时离开客户区
if (wParam & (MK_LBUTTON | MK_RBUTTON))
{
mouse.OnMouseMove(pt.x, pt.y);
}
else
{
ReleaseCapture();
mouse.OnMouseLeave();
}
}
break;
}
case WM_LBUTTONDOWN:
{
const POINTS pt = MAKEPOINTS(lParam);
mouse.OnLeftPressed(pt.x, pt.y);
break;
}
case WM_RBUTTONDOWN:
{
const POINTS pt = MAKEPOINTS(lParam);
mouse.OnRightPressed(pt.x, pt.y);
break;
}
case WM_LBUTTONUP:
{
const POINTS pt = MAKEPOINTS(lParam);
mouse.OnLeftReleased(pt.x, pt.y);
break;
}
case WM_RBUTTONUP:
{
const POINTS pt = MAKEPOINTS(lParam);
mouse.OnRightReleased(pt.x, pt.y);
break;
}
case WM_MOUSEWHEEL:
{
const POINTS pt = MAKEPOINTS(lParam);
const int delta = GET_WHEEL_DELTA_WPARAM(wParam);
mouse.OnWheelDetla(pt.x, pt.y, delta);
}
/*****************鼠标消息******************/

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Mouse
{
friend class Window;
public:
class Event{}//上面展示过,省略;
public:
Mouse() = default;
Mouse( const Mouse& ) = delete;
Mouse& operator=( const Mouse& ) = delete;
std::pair<int,int> GetPos() const noexcept;
int GetPosX() const noexcept;
int GetPosY() const noexcept;
bool IsInWindow() const noexcept;
bool LeftIsPressed() const noexcept;
bool RightIsPressed() const noexcept;
std::optional<Mouse::Event> Read() noexcept;
bool IsEmpty() const noexcept
{
return buffer.empty();
}
void Flush() noexcept;
private:
void OnMouseMove( int x,int y ) noexcept;
void OnMouseLeave() noexcept;
void OnMouseEnter() noexcept;
void OnLeftPressed( int x,int y ) noexcept;
void OnLeftReleased( int x,int y ) noexcept;
void OnRightPressed( int x,int y ) noexcept;
void OnRightReleased( int x,int y ) noexcept;
void OnWheelUp( int x,int y ) noexcept;
void OnWheelDown( int x,int y ) noexcept;
void TrimBuffer() noexcept;
void OnWheelDetla(int x, int y,int delta) noexcept;
private:
static constexpr unsigned int bufferSize = 16u;
int x;
int y;
bool leftIsPressed = false;
bool rightIsPressed = false;
bool isInWindow = false;
int wheelDeltaCarry = 0;
std::queue<Event> buffer;
};

实现中都是简单代码,只列一下滑轮事件的实现;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Mouse::OnWheelDetla(int x, int y, int delta) noexcept
{
wheelDeltaCarry += delta;
//鼠标滑轮的dip是120没格子,超过这个值就触发一次事件
while (wheelDeltaCarry >= WHEEL_DELTA)
{
wheelDeltaCarry -= WHEEL_DELTA;
OnWheelUp(x, y);
}
while (wheelDeltaCarry <= -WHEEL_DELTA)
{
wheelDeltaCarry += WHEEL_DELTA;
OnWheelDown(x, y);
}
}