接上文:http://www.cppblog.com/gaimor/archive/2010/09/30/128134.html
本文我们接着说UI库的消息事件部分:
1.UI基本事件类型
基本上就下面几种:
////////////////////////////////////////////////////////////
/// 枚举UI消息类型
////////////////////////////////////////////////////////////
enum UI_EVENT
{
//! 鼠标移动
UI_EVENT_MOUSE_MOVE = 0,
//! 鼠标点击
UI_EVENT_MOUSE_CLICK,
//! 鼠标进入
UI_EVENT_MOUSE_ENTERED,
//! 鼠标退出
UI_EVENT_MOUSE_EXITED,
//! 鼠标滚轮事件
UI_EVENT_MOUSE_WHEEL,
//! 字符输入
UI_EVENT_CHAR_INPUT,
//! 键盘按键
UI_EVENT_KEY_PRESS,
//! 按键退出
UI_EVENT_KEY_RELEASE,
//! 焦点事件
UI_EVENT_LOST_FOCUSE,
UI_EVENT_GAIN_FOCUSE,
//! 滑块事件
UI_EVENT_SLIDER_MOVE,
//! 编辑事件
UI_EVENT_EDIT,
//! 选择,反选择
UI_EVENT_SELECTED,
UI_EVENT_DESELECTED
};
2.
关于事件一般就3个相关对象:
事件,消息听者和消息处理对象3个单元块:
如下所示:
///////////////////////////////////////////////////////////
/// 定义引擎事件基类模板
///////////////////////////////////////////////////////////
template<class EventType = int>
class Event : NonCopyable
{
public:
///////////////////////////////////////////////////////
/// 事件基类构造函数
///////////////////////////////////////////////////////
Event(const EventType& type):type_(type){}
////////////////////////////////////////////////////////
/// 事件基类析构函数
////////////////////////////////////////////////////////
virtual ~Event(){}
////////////////////////////////////////////////////////
/// 获取事件类型
////////////////////////////////////////////////////////
EventType GetEventType()const{return type_;}
private:
////////////////////////////////////////////////////////
/// 数据成员变量
////////////////////////////////////////////////////////
EventType type_;
};
////////////////////////////////////////////////////////////
/// 定义事件听者基类
////////////////////////////////////////////////////////////
template<class Event,class Target>
class EventListener
{
public:
////////////////////////////////////////////////////////
/// 事件听者虚析构函数
////////////////////////////////////////////////////////
virtual ~EventListener(){}
////////////////////////////////////////////////////////
/// 消息派送
////////////////////////////////////////////////////////
virtual bool Dispatch(const Event& message,Target object) = 0;
};
////////////////////////////////////////////////////////////
/// 定义事件处理者基类
////////////////////////////////////////////////////////////
template<class Event,class EventListener>
class EventHandler
{
public:
////////////////////////////////////////////////////////
/// 事件听者虚析构函数
////////////////////////////////////////////////////////
virtual ~EventHandler(){}
////////////////////////////////////////////////////////
/// 消息处理
////////////////////////////////////////////////////////
virtual bool Process(const Event& message){return false;}
////////////////////////////////////////////////////////
/// 增加,移除事件消息
////////////////////////////////////////////////////////
virtual void AddEventListener(EventListener* listener){}
virtual void RemoveEventListener(EventListener* listener){}
};
然后便是模板实例:
typedef Event<UI_EVENT> UI_Event;
typedef EventListener<UI_Event,UI_Widget*> UI_EventListener;
typedef EventHandler<UI_Event,UI_EventListener*> UI_EventHandler;
说实话可以不这样做而是用boost::function之类的函数绑定
但是没法子这样写我习惯了 呵呵
2.下面是UI事件的承接部分:
通过UI管理器承接输入输出系统的消息响应:
{
////////////////////////////////////////////////////////////
/// 盖莫GUI管理器
////////////////////////////////////////////////////////////
class GAPI UI_WidgetManager : public UI_EventListenerImpl
public:
////////////////////////////////////////////////////////
/// 按键处理
////////////////////////////////////////////////////////
bool OnMouseLeftDown(int x,int y);
bool OnMouseLeftUp(int x,int y);
bool OnMouseMiddleDown(int x,int y);
bool OnMouseMiddleUp(int x,int y);
bool OnMouseRightDown(int x,int y);
bool OnMouseRightUp(int x,int y);
bool OnMouseMove(int x,int y);
bool OnChar(wchar_t code);
bool OnKeyDown(int code);
bool OnKeyUp(int code);
bool OnMouseWheel(int z);
public:
3.事件生成:
以上2部分分别是UI事件对象系列和UI事件输入部分
下面设计UI事件的生成
无论是鼠标还是键盘事件实际上都相当于生成了一个新的事件
举例如下:
OnMouseLeftDown(int x,int y)
如果该函数被调用
那么就说明鼠标的左键被点击同时我们还知道了点击的位置坐标
这样就生成了一个UI_MouseClickEvent
对象
不过这里需要考虑是鼠标双击还是单击
这就要考虑本次点击和上次点击的时间间隔了
这样就生成了一个UI鼠标事件
那本事件应该传给谁?
应该是目标对象
直观一点应该是传给鼠标当前位置下的控件对象
但是考虑到实际情况
这里有一个聚焦控件和活动控件的概念
比如说我们打开一个对话框(这是一个聚焦控件)
对话框上有1个按键
当用户点击本按键则消息发送给这个按键了
但是当鼠标移出对话框之外
一般情况其他控件此时处于非活动状态
消息应该发送给聚焦控件
所以这里至少有3各类型的控件指针分别为:聚焦控件,活动控件以及鼠标下控件(当然他们可以是同一控件)
在这里需要说明这三个控件是这样切换的
如果发生特定的uI事件则修改当前的聚焦控件等对象了
4.具体控件对消息的处理:
生成特定消息,并发送给相应的控件对象之后那么控件就需要相应该消息了:
控件对象的相关函数:
///////////////////////////////////////////////////////////
/// 定义UI控件基类
///////////////////////////////////////////////////////////
class GAPI UI_Widget : public SlotHolder,public UI_EventHandler,public Object
{
public:
typedef std::list<UI_EventListener*> UIEventListener;
typedef std::list<UI_EventListener*>::iterator UIEventListenerItr;
void RemoveEventListener(UI_EventListener* listener);
////////////////////////////////////////////////////////
/// 消息处理
////////////////////////////////////////////////////////
virtual bool Process(const UI_Event& event)
在这里我们通过Process函数来接受UI管理器传过来的消息对象.
这里是想要的处理
注意我们并不直接根据消息响应控件的各种状态!
而是通过迭代消息听者链表的
如下:
bool UI_Widget::Process(const UI_Event& event)
{
bool ret = false;
if(IsVisible() && IsEnabled())
{
UIEventListenerItr it;
for(it = message_listeners_.begin();it != message_listeners_.end();++it)
{
UI_EventListener* listener = *it;
ret = ret || listener->Dispatch(event,this);
}
//! 处理控件边框事件
if(border_)
border_->Process(event);
}
return ret;
}
当消息传来之后我们并不能确定这就是本控件所需要的消息需要验证它
如何验证?
就看当前控件是不是可显示和活动的咯
同时如何控件有边框对象我们则把消息发给它以改变可能的边框外观
下篇:UI设计概要4:UI控件对象