zoukankan      html  css  js  c++  java
  • 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-masterDuiLibCoreUIBase.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-masterDuiLibCoreUIManager.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-masterDuiLibCoreUIManager.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-masterDuiLibCoreUIManager.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-masterDuiLibCoreUIControl.cpp
    void CControlUI::Event(TEventUI& event)
    {
        if( OnEvent(&event) ) DoEvent(event);
    }
    
    //duilib-masterDuiLibControlUIButton.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-masterDuiLibCoreUIManager.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

  • 相关阅读:
    js的基本数据类型有哪些?
    页面优化的方式有哪些?
    为什么要用rem
    sass开发过程中遇到的几个坑
    js事件监听
    js数组去重的方法
    什么是怪异盒模型
    Cookie SameSite属性介绍及其在ASP.NET项目中的应用
    HttpServlet在第一个Servlet程序中的知识点
    Myeclipse 2017 创建第一个servlet步骤
  • 原文地址:https://www.cnblogs.com/wwgk/p/14344629.html
Copyright © 2011-2022 走看看