您当前的位置: 首页 >  ui

插件开发

暂无认证

  • 1浏览

    0关注

    492博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

第一章 第五小节Duilib的CPaintManagerUI类(三)-消息处理

插件开发 发布时间:2022-03-24 08:13:58 ,浏览量:1

文章目录
    • 1.消息流转流程
    • 2.作者答疑

1.消息流转流程

  上一部分,将消息的预处理进行了讲解,这一部分重点分析win32消息怎样转成事件,再如何分发给各个子控件。首先查看事件的源代码,如下所示:

    // Structure for notifications from the system
    // to the control implementation.
    typedef struct UILIB_API tagTEventUI
    {
        int Type;
        CControlUI* pSender;
        DWORD dwTimestamp;
        POINT ptMouse;
        TCHAR chKey;
        WORD wKeyState;
        WPARAM wParam;
        LPARAM lParam;
    } TEventUI;

  先查看在消息函数下关键的一个消息,鼠标左键按下,在这个消息里定位子控件,并响应相应的函数,源代码如下所示:

case WM_LBUTTONDOWN:
            {
                // We alway set focus back to our app (this helps
                // when Win32 child windows are placed on the dialog
                // and we need to remove them on focus change).
                if (!m_bNoActivate) ::SetFocus(m_hWndPaint);
                if( m_pRoot == NULL ) break;
                // 根据点位置查找子控件
                POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
                m_ptLastMousePos = pt;
                CControlUI* pControl = FindControl(pt);
                if( pControl == NULL ) break;
                if( pControl->GetManager() != this ) break;

                // 击中控件绘制
                if(pControl->IsDragEnabled()) {
                    m_bDragMode = true;
                    if( m_hDragBitmap != NULL ) {
                        ::DeleteObject(m_hDragBitmap);
                        m_hDragBitmap = NULL;
                    }
                    m_hDragBitmap = CRenderEngine::GenerateBitmap(this, pControl, pControl->GetPos());
                }

                SetCapture();
                m_pEventClick = pControl;
                pControl->SetFocus();
				//消息转事件
                TEventUI event = { 0 };
                event.Type = UIEVENT_BUTTONDOWN;
                event.pSender = pControl;
                event.wParam = wParam;
                event.lParam = lParam;
                event.ptMouse = pt;
                event.wKeyState = (WORD)wParam;
                event.dwTimestamp = ::GetTickCount();
                pControl->Event(event);//转入事件处理
            }
            break;

  这段代码非常清晰明了的展示了,win32消息转为事件,再调用事件处理函数。首先从win32消息里获取鼠标点位置,然后调用FindControl函数查找点所击中的子控件,源代码如下所示:

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

    CControlUI* CPaintManagerUI::FindControl(LPCTSTR pstrName) const
    {
        ASSERT(m_pRoot);
        return static_cast(m_mNameHash.Find(pstrName));
    }

  从根节点依次往下查找子节点是否包含该点,找到最下的那个子控件作为目标控件。填充参数,在pControl->Event(event);中转入子控件的事件处理。代码如下所示:

    void CControlUI::Event(TEventUI& event)
    {
        if( OnEvent(&event) ) DoEvent(event);
    }

  OnEvent是个CEventSource成员变量,在委托那一小节里有比较清晰的讲解。先调用注册的委托对象数组,对该事件数据进行处理。接着调用DoEvent函数,作者假设击中的是一个按钮,则DoEvent源代码如下:

void CButtonUI::DoEvent(TEventUI& event)
	{
		if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type DoEvent(event);
			else CLabelUI::DoEvent(event);
			return;
		}

		if( event.Type == UIEVENT_SETFOCUS ) 
		{
			Invalidate();
		}
		if( event.Type == UIEVENT_KILLFOCUS ) 
		{
			Invalidate();
		}
		if( event.Type == UIEVENT_KEYDOWN )
		{
			if (IsKeyboardEnabled() && IsEnabled()) {
				if( event.chKey == VK_SPACE || event.chKey == VK_RETURN ) {
					Activate();
					return;
				}
			}
		}
		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_MOUSEMOVE )
		{
			if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
				if( ::PtInRect(&m_rcItem, event.ptMouse) ) m_uButtonState |= UISTATE_PUSHED;
				else m_uButtonState &= ~UISTATE_PUSHED;
				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;
		}
		if( event.Type == UIEVENT_CONTEXTMENU )
		{
			if( IsContextMenuUsed() && IsEnabled()) {
				m_pManager->SendNotify(this, DUI_MSGTYPE_MENU, event.wParam, event.lParam);//发送通知
			}
			return;
		}
		if( event.Type == UIEVENT_MOUSEENTER )
		{
            if( ::PtInRect(&m_rcItem, event.ptMouse ) ) {
                if( IsEnabled() ) {
                    if( (m_uButtonState & UISTATE_HOT) == 0  ) {
                        m_uButtonState |= UISTATE_HOT;
                        Invalidate();
                    }
                }
            }
			if ( GetFadeAlphaDelta() > 0 ) {
				m_pManager->SetTimer(this, FADE_TIMERID, FADE_ELLAPSE);
			}
		}
		if( event.Type == UIEVENT_MOUSELEAVE )
		{
            if( !::PtInRect(&m_rcItem, event.ptMouse ) ) {
                if( IsEnabled() ) {
                    if( (m_uButtonState & UISTATE_HOT) != 0  ) {
                        m_uButtonState &= ~UISTATE_HOT;
                        Invalidate();
                    }
                }
                if (m_pManager) m_pManager->RemoveMouseLeaveNeeded(this);
                if ( GetFadeAlphaDelta() > 0 ) {
                    m_pManager->SetTimer(this, FADE_TIMERID, FADE_ELLAPSE);
                }
            }
            else {
                if (m_pManager) m_pManager->AddMouseLeaveNeeded(this);
                return;
            }
		}
		if( event.Type == UIEVENT_SETCURSOR )
		{
			::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
			return;
		}
		if( event.Type == UIEVENT_TIMER  && event.wParam == FADE_TIMERID ) 
		{
			if( (m_uButtonState & UISTATE_HOT) != 0 ) {
				if( m_uFadeAlpha > m_uFadeAlphaDelta ) m_uFadeAlpha -= m_uFadeAlphaDelta;
				else {
					m_uFadeAlpha = 0;
					m_pManager->KillTimer(this, FADE_TIMERID);
				}
			}
			else {
				if( m_uFadeAlpha KillTimer(this, FADE_TIMERID);
				}
			}
			Invalidate();
			return;
		}
		CLabelUI::DoEvent(event);
	}

  如果击中的控件中的左键击中代码进行相应,如果没有处理函数,再传给父类进行响应,依次到最后。 【小节】仔细的读者阅读到这,对duilib的消息传递,一定会有一个清晰的认识,在duilib中,有两个路线传递消息,一条是从win32操作系统通过系统消息转事件传递信息,原理是通过点位查找当前子控件,再调用自动控件的DoEvent函数处理系统消息。另一条是duilib的自定义消息路线,子控件通过SendNotify函数,将消息进行转发,调用注册在子控件成员CPaintManagerUI中转发通知消息,所以这条路线是不能在真正的win32窗口间传递消息的。SendNotify函数源代码如下:

 void CPaintManagerUI::SendNotify(TNotifyUI& Msg, bool bAsync /*= false*/)
    {
        Msg.ptMouse = m_ptLastMousePos;
        Msg.dwTimestamp = ::GetTickCount();
        if( m_bUsedVirtualWnd )
        {
            Msg.sVirtualWnd = Msg.pSender->GetVirtualWnd();
        }

        if( !bAsync ) {//同步处理
            // Send to all listeners
            if( Msg.pSender != NULL ) {
                if( Msg.pSender->OnNotify ) Msg.pSender->OnNotify(&Msg);
            }
            for( int i = 0; 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();
        }
    }

  读者阅读到这,思路上可能还有些模糊的地方,有个关系可能在头脑里没有清晰的确立起来,CPaintManagerUI是WindowImplBase的一个成员。而子控件是CPaintManagerUI的一个树形结构成员。这样关系就清楚起来,对于一个用WindowImplBase创建的窗口而言,它只有一个CPaintManagerUI,它处理绘制和窗口消息。而窗口内子控件间如果要通信就通过Notify的通讯路线进行交互。如此整个duilib内的消息处理流程就已经讲述完毕。

2.作者答疑

  如有疑问,请留言。

关注
打赏
1665481431
查看更多评论
立即登录/注册

微信扫码登录

0.0413s