接上文:http://www.shnenglu.com/gaimor/archive/2010/09/30/128134.html
本文我們接著說(shuō)UI庫(kù)的消息事件部分:
1.UI基本事件類(lèi)型
基本上就下面幾種:
////////////////////////////////////////////////////////////
/// 枚舉UI消息類(lèi)型
////////////////////////////////////////////////////////////
enum UI_EVENT
{
//! 鼠標(biāo)移動(dòng)
UI_EVENT_MOUSE_MOVE = 0,
//! 鼠標(biāo)點(diǎn)擊
UI_EVENT_MOUSE_CLICK,
//! 鼠標(biāo)進(jìn)入
UI_EVENT_MOUSE_ENTERED,
//! 鼠標(biāo)退出
UI_EVENT_MOUSE_EXITED,
//! 鼠標(biāo)滾輪事件
UI_EVENT_MOUSE_WHEEL,
//! 字符輸入
UI_EVENT_CHAR_INPUT,
//! 鍵盤(pán)按鍵
UI_EVENT_KEY_PRESS,
//! 按鍵退出
UI_EVENT_KEY_RELEASE,
//! 焦點(diǎn)事件
UI_EVENT_LOST_FOCUSE,
UI_EVENT_GAIN_FOCUSE,
//! 滑塊事件
UI_EVENT_SLIDER_MOVE,
//! 編輯事件
UI_EVENT_EDIT,
//! 選擇,反選擇
UI_EVENT_SELECTED,
UI_EVENT_DESELECTED
};
2.
關(guān)于事件一般就3個(gè)相關(guān)對(duì)象:
事件,消息聽(tīng)者和消息處理對(duì)象3個(gè)單元塊:
如下所示:
///////////////////////////////////////////////////////////
/// 定義引擎事件基類(lèi)模板
///////////////////////////////////////////////////////////
template<class EventType = int>
class Event : NonCopyable
{
public:
///////////////////////////////////////////////////////
/// 事件基類(lèi)構(gòu)造函數(shù)
///////////////////////////////////////////////////////
Event(const EventType& type):type_(type){}
////////////////////////////////////////////////////////
/// 事件基類(lèi)析構(gòu)函數(shù)
////////////////////////////////////////////////////////
virtual ~Event(){}
////////////////////////////////////////////////////////
/// 獲取事件類(lèi)型
////////////////////////////////////////////////////////
EventType GetEventType()const{return type_;}
private:
////////////////////////////////////////////////////////
/// 數(shù)據(jù)成員變量
////////////////////////////////////////////////////////
EventType type_;
};
////////////////////////////////////////////////////////////
/// 定義事件聽(tīng)者基類(lèi)
////////////////////////////////////////////////////////////
template<class Event,class Target>
class EventListener
{
public:
////////////////////////////////////////////////////////
/// 事件聽(tīng)者虛析構(gòu)函數(shù)
////////////////////////////////////////////////////////
virtual ~EventListener(){}
////////////////////////////////////////////////////////
/// 消息派送
////////////////////////////////////////////////////////
virtual bool Dispatch(const Event& message,Target object) = 0;
};
////////////////////////////////////////////////////////////
/// 定義事件處理者基類(lèi)
////////////////////////////////////////////////////////////
template<class Event,class EventListener>
class EventHandler
{
public:
////////////////////////////////////////////////////////
/// 事件聽(tīng)者虛析構(gòu)函數(shù)
////////////////////////////////////////////////////////
virtual ~EventHandler(){}
////////////////////////////////////////////////////////
/// 消息處理
////////////////////////////////////////////////////////
virtual bool Process(const Event& message){return false;}
////////////////////////////////////////////////////////
/// 增加,移除事件消息
////////////////////////////////////////////////////////
virtual void AddEventListener(EventListener* listener){}
virtual void RemoveEventListener(EventListener* listener){}
};
然后便是模板實(shí)例:
typedef Event<UI_EVENT> UI_Event;
typedef EventListener<UI_Event,UI_Widget*> UI_EventListener;
typedef EventHandler<UI_Event,UI_EventListener*> UI_EventHandler;
說(shuō)實(shí)話(huà)可以不這樣做而是用boost::function之類(lèi)的函數(shù)綁定
但是沒(méi)法子這樣寫(xiě)我習(xí)慣了 呵呵
2.下面是UI事件的承接部分:
通過(guò)UI管理器承接輸入輸出系統(tǒng)的消息響應(yīng):
{
////////////////////////////////////////////////////////////
/// 蓋莫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事件對(duì)象系列和UI事件輸入部分
下面設(shè)計(jì)UI事件的生成
無(wú)論是鼠標(biāo)還是鍵盤(pán)事件實(shí)際上都相當(dāng)于生成了一個(gè)新的事件
舉例如下:
OnMouseLeftDown(int x,int y)
如果該函數(shù)被調(diào)用
那么就說(shuō)明鼠標(biāo)的左鍵被點(diǎn)擊同時(shí)我們還知道了點(diǎn)擊的位置坐標(biāo)
這樣就生成了一個(gè)UI_MouseClickEvent
對(duì)象
不過(guò)這里需要考慮是鼠標(biāo)雙擊還是單擊
這就要考慮本次點(diǎn)擊和上次點(diǎn)擊的時(shí)間間隔了
這樣就生成了一個(gè)UI鼠標(biāo)事件
那本事件應(yīng)該傳給誰(shuí)?
應(yīng)該是目標(biāo)對(duì)象
直觀一點(diǎn)應(yīng)該是傳給鼠標(biāo)當(dāng)前位置下的控件對(duì)象
但是考慮到實(shí)際情況
這里有一個(gè)聚焦控件和活動(dòng)控件的概念
比如說(shuō)我們打開(kāi)一個(gè)對(duì)話(huà)框(這是一個(gè)聚焦控件)
對(duì)話(huà)框上有1個(gè)按鍵
當(dāng)用戶(hù)點(diǎn)擊本按鍵則消息發(fā)送給這個(gè)按鍵了
但是當(dāng)鼠標(biāo)移出對(duì)話(huà)框之外
一般情況其他控件此時(shí)處于非活動(dòng)狀態(tài)
消息應(yīng)該發(fā)送給聚焦控件
所以這里至少有3各類(lèi)型的控件指針?lè)謩e為:聚焦控件,活動(dòng)控件以及鼠標(biāo)下控件(當(dāng)然他們可以是同一控件)
在這里需要說(shuō)明這三個(gè)控件是這樣切換的
如果發(fā)生特定的uI事件則修改當(dāng)前的聚焦控件等對(duì)象了
4.具體控件對(duì)消息的處理:
生成特定消息,并發(fā)送給相應(yīng)的控件對(duì)象之后那么控件就需要相應(yīng)該消息了:
控件對(duì)象的相關(guān)函數(shù):
///////////////////////////////////////////////////////////
/// 定義UI控件基類(lèi)
///////////////////////////////////////////////////////////
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)
在這里我們通過(guò)Process函數(shù)來(lái)接受UI管理器傳過(guò)來(lái)的消息對(duì)象.
這里是想要的處理
注意我們并不直接根據(jù)消息響應(yīng)控件的各種狀態(tài)!
而是通過(guò)迭代消息聽(tīng)者鏈表的
如下:
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;
}
當(dāng)消息傳來(lái)之后我們并不能確定這就是本控件所需要的消息需要驗(yàn)證它
如何驗(yàn)證?
就看當(dāng)前控件是不是可顯示和活動(dòng)的咯
同時(shí)如何控件有邊框?qū)ο笪覀儎t把消息發(fā)給它以改變可能的邊框外觀
下篇:UI設(shè)計(jì)概要4:UI控件對(duì)象