输入系统的事件派遣流程

//注入鼠标移动的消息
bool System::injectMouseMove(float delta_x, float delta_y)
{
MouseEventArgs ma(0);
MouseCursor& mouse = MouseCursor::getSingleton();
//看到d_mouseScalingFactor 的作用了吧!
ma.moveDelta.d_x = delta_x * d_mouseScalingFactor;
ma.moveDelta.d_y = delta_y * d_mouseScalingFactor;
ma.sysKeys = d_sysKeys;
CEGUI 深入解析 第4 章 CEGUI 核心控制体系
- 87 -
ma.wheelChange = 0;
ma.clickCount = 0;
ma.button = NoButton;
//更新位置,offsetPosition 改变mouse 的位置信息
mouse.offsetPosition(ma.moveDelta);
ma.position = mouse.getPosition();
//getTargetWindow 函数获取鼠标下的目标窗口
Window* dest_window = getTargetWindow(ma.position);
// 如果先前的鼠标焦点窗口和目前的不同
if (dest_window != d_wndWithMouse)
{
// 保存先前的鼠标焦点窗口
Window* oldWindow = d_wndWithMouse;
// 设置新的鼠标焦点窗口
d_wndWithMouse = dest_window;
// 通知旧的窗口鼠标已经离开了onMouseLeaves 是Window类提供的事件处理函数
if (oldWindow)
{
ma.window = oldWindow;
oldWindow->onMouseLeaves(ma);
}
// 通知新窗口鼠标进入
if (d_wndWithMouse)
{
ma.window = d_wndWithMouse;
ma.handled = false;
d_wndWithMouse->onMouseEnters(ma);
}
}
// 通知当前的鼠标焦点窗口,鼠标移动了
if (dest_window)
{
// 由于使用了前面的时间参数ma,所以这里需要设置处理标志为false,初始化
ma.handled = false;
// 循环处理直到事件被处理为止
while ((!ma.handled) && (dest_window != 0))
{
ma.window = dest_window;
dest_window->onMouseMove(ma);
dest_window = getNextTargetWindow(dest_window);
}
}
return ma.handled;
CEGUI 深入解析 第4 章 CEGUI 核心控制体系
- 88 -
}
//鼠标离开游戏窗口的消息
bool System::injectMouseLeaves(void)
{
MouseEventArgs ma(0);
// 如果当前有鼠标上的窗口,则通知它并且设置鼠标上窗口为空
if (d_wndWithMouse)
{
ma.position = MouseCursor::getSingleton().getPosition();
ma.moveDelta = Vector2(0.0f, 0.0f);
ma.button = NoButton;
ma.sysKeys = d_sysKeys;
ma.wheelChange = 0;
ma.window = d_wndWithMouse;
ma.clickCount = 0;
d_wndWithMouse->onMouseLeaves(ma);
d_wndWithMouse = 0;
}
return ma.handled;
}
//鼠标按下的注入消息
bool System::injectMouseButtonDown(MouseButton button)
{
// 这里更新了鼠标点击的标志到d_sysKeys 里
d_sysKeys |= mouseButtonToSyskey(button);
MouseEventArgs ma(0);
//获取鼠标位置,这个位置是通过injectMousePosition 函数注入系统中的
ma.position = MouseCursor::getSingleton().getPosition();
ma.moveDelta = Vector2(0.0f, 0.0f);
ma.button = button;
ma.sysKeys = d_sysKeys;
ma.wheelChange = 0;
// 找到目标窗口
Window* dest_window = getTargetWindow(ma.position);
//产生多重鼠标点击事件,根据按钮获取相应的MouseClickTracker 结构
MouseClickTracker& tkr = d_clickTrackerPimpl->click_trackers[button];
//累加鼠标点击次数
tkr.d_click_count++;
// 重设结构的条件1.超出双击时间,2.鼠标位置不在限定区域内,3.目标窗口改变,
//4.单件次数大于3,CEGUI 最大支持3 击
if ((tkr.d_timer.elapsed() > d_dblclick_timeout) ||
(!tkr.d_click_area.isPointInRect(ma.position)) ||
(tkr.d_target_window != dest_window) ||
(tkr.d_click_count > 3))
CEGUI 深入解析 第4 章 CEGUI 核心控制体系
- 89 -
{
//重设为单击
tkr.d_click_count = 1;
// 创建新的点击区域,供下次鼠标按下是检查
tkr.d_click_area.setPosition(ma.position);
tkr.d_click_area.setSize(d_dblclick_size);
tkr.d_click_area.offset(Point(-(d_dblclick_size.d_width / 2), -(d_dblclick_size.d_height /
2)));
// 重设目标窗口
tkr.d_target_window = dest_window;
}
// 设置单击次数到事件参数中
ma.clickCount = tkr.d_click_count;
// 循环直到消息被处理或者找不到目标窗口
while ((!ma.handled) && (dest_window != 0))
{
ma.window = dest_window;
//如果窗口需要多重点击,必须调用setWantsMultiClickEvents 设置关注
if (dest_window->wantsMultiClickEvents())
{
switch (tkr.d_click_count)
{
case 1:
dest_window->onMouseButtonDown(ma);
break;
case 2:
dest_window->onMouseDoubleClicked(ma);
break;
case 3:
dest_window->onMouseTripleClicked(ma);
break;
}
}
// 如果当前窗口不需要多重点击,则全部认为是单击
else
{
dest_window->onMouseButtonDown(ma);
}
//查找下一个目标窗口
dest_window = getNextTargetWindow(dest_window);
}
// 重设定时器
tkr.d_timer.restart();
return ma.handled;
CEGUI 深入解析 第4 章 CEGUI 核心控制体系
- 90 -
}
//注入鼠标抬起的事件
bool System::injectMouseButtonUp(MouseButton button)
{
//取消系统记录的鼠标按下的标志,在鼠标按下时记录的
d_sysKeys &= ~mouseButtonToSyskey(button);
MouseEventArgs ma(0);
ma.position = MouseCursor::getSingleton().getPosition();
ma.moveDelta = Vector2(0.0f, 0.0f);
ma.button = button;
ma.sysKeys = d_sysKeys;
ma.wheelChange = 0;
// 获取多重单击的跟踪结构
MouseClickTracker& tkr = d_clickTrackerPimpl->click_trackers[button];
// 设置单击次数到事件参数中
ma.clickCount = tkr.d_click_count;
//获取目标窗口,根据鼠标位置
Window* const initial_dest_window = getTargetWindow(ma.position);
Window* dest_window = initial_dest_window;
// 循环激发窗口的鼠标抬起事件,指定事件被处理或者找不到目标窗口
while ((!ma.handled) && (dest_window != 0))
{
ma.window = dest_window;
dest_window->onMouseButtonUp(ma);
dest_window = getNextTargetWindow(dest_window);
}
bool wasUpHandled = ma.handled;
// 如果满足鼠标单击的条件1.时间小于单击的最长逝去时间,2.鼠标在跟踪区域内,
//3.目前的跟踪窗口和目标窗口相同
if ((tkr.d_timer.elapsed() <= d_click_timeout) &&
(tkr.d_click_area.isPointInRect(ma.position)) &&
(tkr.d_target_window == initial_dest_window))
{
ma.handled = false;
dest_window = initial_dest_window;
// 循环直到事件被处理或者找不到目标窗口
while ((!ma.handled) && (dest_window != 0))
{
ma.window = dest_window;
dest_window->onMouseClicked(ma);
dest_window = getNextTargetWindow(dest_window);
}
}
CEGUI 深入解析 第4 章 CEGUI 核心控制体系
- 91 -
return (ma.handled | wasUpHandled);
}
//注入鼠标滚动消息
bool System::injectMouseWheelChange(float delta)
{
//设置鼠标滚动事件的参数
MouseEventArgs ma(0);
ma.position = MouseCursor::getSingleton().getPosition();
ma.moveDelta = Vector2(0.0f, 0.0f);
ma.button = NoButton;
ma.sysKeys = d_sysKeys;
ma.wheelChange = delta;
ma.clickCount = 0;
Window* dest_window = getTargetWindow(ma.position);
// 循环执行,直到事件被处理或者没有目标窗口
while ((!ma.handled) && (dest_window != 0))
{
ma.window = dest_window;
dest_window->onMouseWheel(ma);
dest_window = getNextTargetWindow(dest_window);
}
return ma.handled;
}
//注入鼠标位置信息
bool System::injectMousePosition(float x_pos, float y_pos)
{
// 看到了吧,这里设置鼠标的位置到MouseCursor 中保存起来
MouseCursor::getSingleton().setPosition(Point(x_pos, y_pos));
// 注入鼠标移动消息,并传递移动的位置为(0,0),可以不调用这个函数吗?
return injectMouseMove(0, 0);
}
//上面6 个函数使用到来两个获取目标才看到函数,读者一定希望了解他们是如何工作的
//获取鼠标下面的目标窗口的函数
Window* System::getTargetWindow(const Point& pt) const
{
Window* dest_window = 0;
// 如果找不到所有窗口的父窗口(CEGUI 中称为底板),则无法找到目标窗口,因此这
//个窗口一定要设置,否则CEGUI 无法正常工作,它的类型可以是任何Window 的子类
if (d_activeSheet)
{
//首先得到捕获窗口输入的窗口
dest_window = Window::getCaptureWindow();
//如果找不到则获取鼠标下的最高层的子窗口
if (!dest_window)
CEGUI 深入解析 第4 章 CEGUI 核心控制体系
- 92 -
{
dest_window = d_activeSheet->getTargetChildAtPosition(pt);
//如果还是找不到,则默认窗口底板为鼠标下窗口
if (!dest_window)
{
dest_window = d_activeSheet;
}
}
//如果有捕获鼠标输入的窗口
else
{
//如果自己不出来,而是需要子窗口处理则获取窗口下的目标子窗口
if (dest_window->distributesCapturedInputs())
{
Window* child_window = dest_window->getTargetChildAtPosition(pt);
//如果找到了则设置,如果没找到目标窗口还是捕获输入的窗口
if (child_window)
{
dest_window = child_window;
}
}
}
// 如果模态窗口不为空则模态窗口重写目标鼠标
if (d_modalTarget != 0 && dest_window != d_modalTarget)
{
//如果模态窗口不是当前目标窗口的祖先则设置模态窗口为当前目标窗口
if (!dest_window->isAncestor(d_modalTarget))
{
dest_window = d_modalTarget;
}
}
}
return dest_window;
}
//获取下一个目标窗口
Window* System::getNextTargetWindow(Window* w) const
{
//如果没有父链递归到模态窗口则继续顺着父链调用下去
if (w != d_modalTarget)
{
return w->getParent();
}
// 否则返回空,终止各种inject 函数中的循环调用
CEGUI 深入解析 第4 章 CEGUI 核心控制体系
- 93 -
return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的事件派遣系统模块的实现: ```python class EventDispatcher: def __init__(self): self.listeners = {} def add_listener(self, event_type, listener): if event_type not in self.listeners: self.listeners[event_type] = [] self.listeners[event_type].append(listener) def remove_listener(self, event_type, listener): if event_type in self.listeners: listeners = self.listeners[event_type] if listener in listeners: listeners.remove(listener) if len(listeners) == 0: del self.listeners[event_type] def dispatch_event(self, event_type, event_data=None): if event_type in self.listeners: listeners = self.listeners[event_type] for listener in listeners: listener(event_type, event_data) ``` 这个模块实现了一个 `EventDispatcher` 类,它可以通过 `add_listener` 方法添加监听器,通过 `remove_listener` 方法移除监听器,通过 `dispatch_event` 方法触发事件并通知所有监听器。 使用方法如下: ```python # 创建事件派发器 dispatcher = EventDispatcher() # 定义一些事件类型 EVENT_TYPE_A = "EVENT_TYPE_A" EVENT_TYPE_B = "EVENT_TYPE_B" # 定义一些监听器函数 def listener_a(event_type, event_data): print("Listener A received event:", event_type, event_data) def listener_b(event_type, event_data): print("Listener B received event:", event_type, event_data) # 添加监听器 dispatcher.add_listener(EVENT_TYPE_A, listener_a) dispatcher.add_listener(EVENT_TYPE_B, listener_b) dispatcher.add_listener(EVENT_TYPE_A, listener_b) # 触发事件 dispatcher.dispatch_event(EVENT_TYPE_A, {"message": "Hello world!"}) dispatcher.dispatch_event(EVENT_TYPE_B, {"number": 42}) # 移除监听器 dispatcher.remove_listener(EVENT_TYPE_A, listener_a) dispatcher.remove_listener(EVENT_TYPE_B, listener_b) # 再次触发事件 dispatcher.dispatch_event(EVENT_TYPE_A, {"message": "Goodbye world!"}) dispatcher.dispatch_event(EVENT_TYPE_B, {"number": 100}) ``` 输出结果如下: ``` Listener A received event: EVENT_TYPE_A {'message': 'Hello world!'} Listener B received event: EVENT_TYPE_A {'message': 'Hello world!'} Listener B received event: EVENT_TYPE_B {'number': 42} Listener A received event: EVENT_TYPE_B {'number': 42} ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值