Duilib 源码分析(二)消息处理
入口函数是_tWinMain
int APIENTRY _tWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nShowCmd)
{
// 绘制管理器CPaintManagerUI绑定窗口句柄
CPaintManagerUI::SetInstance(hInstance);
// 绘制管理器CPaintManagerUI设置资源目录,用于加载XML
CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstan
// 创建窗口
CDuilibWnd duilibWnd;
duilibWnd.Create(NULL, _T("标题"),UI_WNDSTYLE_FRAME,WS_EX_WINDOWEDGE);
// 显示窗口并且监听消息
duilibWnd.ShowModal();
return 0;
}
CDuilibWnd 继承 CWindowWnd和INotifyUI
class CDuilibWnd :public CWindowWnd, public INotifyUI
{
// 绘制管理器:负责绘制界面和管理消息
CPaintManagerUI m_PaintManager
// CWindowWnd中处理Window消息
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
// INotifyUI中处理Duilib消息
void Notify(TNotifyUI& msg);
}
CWindowWnd::Create注册并且创建窗口
// duilib-master\DuiLib\Core\UIBase.cpp
HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
{
if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
ASSERT(m_hWnd!=NULL);
return m_hWnd;
}
// 这里就是Win32一样的注册窗口,处理消息的函数是CWindowWnd::__WndProc
bool CWindowWnd::RegisterWindowClass()
{
WNDCLASS wc = { 0 };
wc.style = GetClassStyle();
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hIcon = NULL;
wc.lpfnWndProc = CWindowWnd::__WndProc;
wc.hInstance = CPaintManagerUI::GetInstance();
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = GetWindowClassName();
ATOM ret = ::RegisterClass(&wc);
ASSERT(ret!=NULL || ::GetLastError()==ERROR_CLASS_ALREADY_EXISTS);
return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
}
// CWindowWnd::__WndProc转发消息给CWindowWnd::HandleMessage
LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CWindowWnd* pThis = NULL;
...
.
if( pThis != NULL ) {
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
使用时,先继承CWindowWnd,然后重写HandleMessage
消息先由绘制管理器CPaintManagerUI::MessageHandler 处理
class CDuilibWnd : public CWindowWnd
{
CPaintManagerUI m_PaintManager; // 绘制管理器
//重写HandleMessage
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// 绘制管理器处理消息
if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes))
{
return lRes;
}
// 父类CWindowWnd处理消息
return __super::HandleMessage(uMsg, wParam, lParam);
}
}
CPaintManagerUI::MessageHandler中消息处理的流程
1、私有消息case WM_APP + 1:
1.1、m_aMessageFilters[i]->MessageHandler
1.2、pMsg->pSender->OnNotify
1.3、m_aNotifiers[j]->Notify
//duilib-master\DuiLib\Core\UIManager.cpp
bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)
{
// 1、m_aMessageFilters[i]->MessageHandler处理消息
for( int i = 0; i < m_aMessageFilters.GetSize(); i++ ){
static_cast<IMessageFilterUI*>(m_aMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);
}
// Notify处理消息(这里处理的是异步消息)
TNotifyUI* pMsg = NULL;
while( pMsg = static_cast<TNotifyUI*>(m_aAsyncNotify.GetAt(0)) ) {
m_aAsyncNotify.Remove(0);
// 2、pMsg->pSender->OnNotify处理消息
if( pMsg->pSender != NULL ) {
if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg);
}
// 3、m_aNotifiers[j]->Notify处理消息
for( int j = 0; j < m_aNotifiers.GetSize(); j++ ) {
static_cast<INotifyUI*>(m_aNotifiers[j])->Notify(*pMsg);
}
delete pMsg;
}
}
对应添加消息处理的方法就有
//1、m_aMessageFilters[i]->MessageHandler
//duilib-master\DuiLib\Core\UIManager.cpp
bool CPaintManagerUI::AddMessageFilter(IMessageFilterUI* pFilter)
{
if (pFilter == NULL) return false;
ASSERT(m_aMessageFilters.Find(pFilter)<0);
return m_aMessageFilters.Add(pFilter)
}
//2、pMsg->pSender->OnNotify
OnNotify += MakeDelegate(this,&CFrameWindowWnd::OnAlphaChanged);
//3、m_aNotifiers[j]->Notify
//duilib-master\DuiLib\Core\UIManager.cpp
bool CPaintManagerUI::AddNotifier(INotifyUI* pNotifier)
{
if (pNotifier == NULL) return false;
ASSERT(m_aNotifiers.Find(pNotifier)<0);
return m_aNotifiers.Add(pNotifie);
}
2、系统Windows消息
// 鼠标左键按下的消息
case WM_LBUTTONDOWN:
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
CControlUI* pControl = FindControl(pt);
m_pEventClick = pControl;
TEventUI event = { 0 };
event.Type = UIEVENT_BUTTONDOWN; // WM_XXX 转成 UIEVENT_XXX
event.pSender = pControl;
event.wParam = wParam;
event.lParam = lParam;
event.ptMouse = pt;
event.wKeyState = (WORD)wParam;
event.dwTimestamp = ::GetTickCount();
pControl->Event(event);
}
// 鼠标左键弹起的消息
case WM_LBUTTONUP:
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
TEventUI event = { 0 };
event.Type = UIEVENT_BUTTONUP; // WM_XXX 转成 UIEVENT_XXX
event.pSender = m_pEventClick;
event.wParam = wParam;
event.lParam = lParam;
event.ptMouse = pt;
event.wKeyState = (WORD)wParam;
event.dwTimestamp = ::GetTickCount();
CControlUI* pClick = m_pEventClick;
m_pEventClick = NULL;
pClick->Event(event);
}
Duiib的UI事件
//duilib-master\DuiLib\Core\UIControl.cpp
void CControlUI::Event(TEventUI& event)
{
if( OnEvent(&event) ) DoEvent(event);
}
//duilib-master\DuiLib\Control\UIButton.cpp
void CButtonUI::DoEvent(TEventUI& event)
{
if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK )
{
if( ::PtInRect(&m_rcItem, event.ptMouse) && IsEnabled() ) {
m_uButtonState |= UISTATE_PUSHED | UISTATE_CAPTURED;
Invalidate();
}
return;
}
if( event.Type == UIEVENT_BUTTONUP )
{
if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
if( ::PtInRect(&m_rcItem, event.ptMouse) && IsEnabled()) Activate();
m_uButtonState &= ~(UISTATE_PUSHED | UISTATE_CAPTURED);
Invalidate();
}
return;
}
}
// 按钮被激活,发出消息DUI_MSGTYPE_CLICK
bool CButtonUI::Activate()
{
if( !CControlUI::Activate() ) return false;
if( m_pManager != NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_CLICK); // UIEVENT_XXX 转成 DUI_MSGTYPE_XXX
return true;
}
Duilib控件发出通知
//duilib-master\DuiLib\Core\UIManager.cpp
void CPaintManagerUI::SendNotify(CControlUI* pControl, LPCTSTR pstrMessage, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/, bool bAsync /*= false*/, bool bEnableRepeat /*= true*/)
{
TNotifyUI Msg;
Msg.pSender = pControl;
Msg.sType = pstrMessage;
Msg.wParam = wParam;
Msg.lParam = lParam;
SendNotify(Msg, bAsync, bEnableRepeat);
}
void CPaintManagerUI::SendNotify(TNotifyUI& Msg, bool bAsync /*= false*/, bool bEnableRepeat /*= true*/)
{
// 同步消息,立马处理
if( !bAsync ) {
for( int i = 0; i < m_aNotifiers.GetSize(); i++ )
static_cast<INotifyUI*>(m_aNotifiers[i])->Notify(Msg);
}else{
// 异步消息,暂存起来,下一消息处理
TNotifyUI *pMsg = new TNotifyUI;
pMsg->pSender = Msg.pSender;
pMsg->sType = Msg.sType;
pMsg->wParam = Msg.wParam;
pMsg->lParam = Msg.lParam;
pMsg->ptMouse = Msg.ptMouse;
pMsg->dwTimestamp = Msg.dwTimestamp;
m_aAsyncNotify.Add(pMsg);
PostAsyncNotify();
}
}
// 自己给自己发消息,用于触发异步消息的处理
void CPaintManagerUI::PostAsyncNotify()
{
if (!m_bAsyncNotifyPosted) {
// 给自己发消息WM_APP+1,触发处理异步消息:m_aAsyncNotify
::PostMessage(m_hWndPaint, WM_APP + 1, 0, 0L);
m_bAsyncNotifyPosted = true;
}
}
// 最终由Notify处理Duilib的通知
void Notify(TNotifyUI& msg)
{
if (msg.sType == DUI_MSGTYPE_CLICK)
{
if (msg.pSender->GetName() == _T("btn"))
{
::MessageBox(NULL, _T("按钮内容"), _T("按钮标题"), NULL);
}
}
}
小结
使用duilib绘制界面的软件本质还是win32软件,所以还是通过注册窗口过程来接收处理消息。消息的转变过程是WM_XXX -> UIEVENT_XXX -> DUI_MSGTYPE_XXX。最终是由Notify函数来响应duilib界面的操作消息。
Duilib技术交流群:799142530
源码地址:https://github.com/KongKong20/DuilibTutor