最新消息:想得多,做的少。一天到晚瞎鸡巴搞。

duilib源码剖析 day6 Notify消息过程

曲径通幽 阿虚 360浏览 0评论

.      Notify的消息过程很好调试只需要在Notify内下一个断点,点下按钮就能得到完整的调用过程。

CFrameWindowWnd::Notify()
CPaintManagerUI::SendNotify()
CPaintManagerUI::SendNotify()
CButtonUI::Activate()
CButtonUI::DoEvent()
CControlUI::Event()
CPaintManagerUI::MessageHandler()
CFrameWindowWnd::HandleMessage()
CWindowWnd::__WndProc()
CWndShadow::ParentProc()

.      调用栈为从下往上的一个过程,最先是从CWndShadow::ParentProc()开始到CFrameWindowWnd::Notify()结束这一条线。

为什么从CWndShadow开始

.      在day3后续有写“但是m_pWndShadow->Create(m_hWnd);中却修改了CFrameWindowWnd设置的消息循环回调函数,先指向了CWndShadow的ParentProc()在通过函数指针调用CWindowWnd::__WndProc。”如果将CWndShadow代码注释那么则开始处为CWindowWnd::__WndProc()。

CWindowWnd::__WndProc()

.      消息从CWindowWnd::__WndProc()看,虽然是从这里开始。但是过程并没有处理什么东西,转向了CFrameWindowWnd::HandleMessage()。

CPaintManagerUI::MessageHandler()

.      CPaintManagerUI::MessageHandler()是主要的消息处理函数,处理了各种事件消息函数也挺长的,不可能全部都写一遍太耗时间了。简单记录下CButtonUI按钮的事件过程即可。

CButtonUI按钮事件过程

.      对界面按一次鼠标会产生两条消息事件WM_LBUTTONDOWNWM_LBUTTONUP。在两个消息中下断点,对界面任意CButtonUI按钮按下将先会断点在WM_LBUTTONDOWN中。

WM_LBUTTONDOWN做了什么

POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
m_ptLastMousePos = pt;
CControlUI* pControl = FindControl(pt);
if( pControl == NULL ) 
break;	

.      函数开头将参数lParam转换成POINT位置坐标得到的是出表点击窗口的坐标,而后调用FindControl()函数来搜索控件指针。

FindControl()

CControlUI* CPaintManagerUI::FindControl(POINT pt) const
{
    ASSERT(m_pRoot);
    return m_pRoot->FindControl(
        __FindControlFromPoint
        , &pt
        , UIFIND_VISIBLE | UIFIND_HITTEST | UIFIND_TOP_FIRST);
}

.      FindControl()函数从控件根节点调用控件中的FindControl(),并且提供了一个回调函数__FindControlFromPoint()。用于判断鼠标点击是否在当前控件范围内。成功返回控件指针,否则返回空。

CControlUI* CALLBACK CPaintManagerUI::__FindControlFromPoint(CControlUI* pThis, LPVOID pData)
{
    LPPOINT pPoint = static_cast<LPPOINT>(pData);
    //判断point这个点是否命中了当前矩形范围
    return ::PtInRect(&pThis->GetPos(), *pPoint) ? pThis : NULL;
}

CContainerUI::FindControl()控件搜索方式

for( int it = m_items.GetSize() - 1; it >= 0; it-- ) 
{
    pResult = static_cast<CControlUI*>(m_items[it])->FindControl(Proc, pData, uFlags);
    if( pResult != NULL ) 
    {
        if( (uFlags & UIFIND_HITTEST) != 0 
            && !pResult->IsFloat() 
            && !::PtInRect(&rc, *(static_cast<LPPOINT>(pData))) )
            continue;
        else 
            return pResult;
    }          
}

.      控件搜索的核心就是从根节点开始倒叙遍历子控件节点并且子节点继续调用FindControl()。

.      可能调试的时候发现进入FindControl()。函数有些不对,有时候进入的是CContainerUI::FindControl()但是又有可能进入CControlUI::FindControl()。到底是哪个?其实进入哪个FindControl()函数看你点的是什么控件。我测试点击的控件是CButtonUI知道这个控件名称看我整理出来的【控件是如何组装的】只要是C++不是特别的菜都能一眼明白。

继续

.      搜索到控件之后交给m_pEventClick保存,接下来对Find到的控件设置焦点和对窗口进行设置鼠标捕获以及封装了一个TEventUI结构体。

typedef struct DUILIB_API tagTEventUI
{
    //消息类型
    int Type;
    //控件指针
    CControlUI* pSender;
    //当前点击时间
    DWORD dwTimestamp;
    //鼠标点击位置
    POINT ptMouse;
    TCHAR chKey;
    //当前键盘状态(是否使用了SHIFT之类的按键)
    WORD wKeyState;
    //消息事件参数
    WPARAM wParam;
    LPARAM lParam;
} TEventUI;

.      封装后调用控件的Event()。交给控件处理。

总结

.      对于鼠标点击产生的WM_LBUTTONDOWN首先会查找控件,是否点击的位置存在控件。如果存在控件那么将会封装一个TEventUI事件并且调用CControlUI::Event()来转给控件由控件自己判断如何处理WM_LBUTTONDOWN事件。

WM_LBUTTONUP做了什么

.      事件不长,开始判断是否m_pEventClick为空因为鼠标点击中控件后会将控件指针保存在m_pEventClick中。接下来还是封装了一个TEventUI事件并且调用CControlUI::Event()来转给控件由控件自己判断如何处理。对于CButtonUI对于WM_LBUTTONUP事件会调用Activate()函数。

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;
}

.      Activate()函数中,如果控件可见和启动并且m_pManager指向了CPaintManagerUI将调用CPaintManagerUI::SendNotify()。

bool CButtonUI::Activate()
{
	if( !CControlUI::Activate() ) 
        return false;
		
    if( m_pManager != NULL ) 
        m_pManager->SendNotify(this, DUI_MSGTYPE_CLICK);
		
    return true;
}
void SendNotify(TNotifyUI& Msg, bool bAsync = false, bool bEnableRepeat = true);
void SendNotify(CControlUI* pControl, LPCTSTR pstrMessage, WPARAM wParam = 0, LPARAM lParam = 0, bool bAsync = false, bool bEnableRepeat = true);

.      SendNotify()是一个重载函数通过观察参数,通过观察Activate()调用的SendNotify()和单步调试得知进入的是第二个SendNotify().

.      第二个SendNotify()只做了一件事情封装了一个新的结构体TNotifyUI并调用第一个SendNotify()

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);
}

typedef struct tagTNotifyUI 
{
    //自定义消息类型字符串
	CDuiString sType;
	CDuiString sVirtualWnd;
	//响应控件指针    
    CControlUI* pSender;
	//产生时间
    DWORD dwTimestamp;
	//鼠标点击位置
    POINT ptMouse;
	//参数
    WPARAM wParam;
	LPARAM lParam;
} TNotifyUI;

.      在第二个SendNotify()可以看出控件是可以传递异步消息的,但是怎么使用目前还不知道只关注同步传递消息。

.      在第二个SendNotify()中就是遍历m_aNotifiers队列调用队列中所有INotifyUI:: Notify()函数。通知是什么时候插入的?就是在day3讲的时候。

结束

.      当继续跟进Notify()就能看到最终进入了CFrameWindowWnd::Notify()。对于Notify事件梳理就到这里结束。当然对于不同的控件方式肯定有所不同。讲个最简单的搞明白DirectUI原理即可。

转载请注明:虚无 » duilib源码剖析 day6 Notify消息过程

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址