• duilib学习领悟(4)


      使用duilib创建的主窗口绘制工作全都发生在一个 真实存在的主窗口句柄当中,这个绘制过程稍稍有些复杂,但再复杂也逃不过WM_PAINT消息,所有的绘制工作也都由这个WM_PAINT消息完成.在上几篇中,我们总结了,所有的窗口消息都被CPaintManager类拦截处理,它成为所有消息的路由中心,我们看看WM_PAINT消息的处理:

      

        case WM_PAINT:
            {
                // Should we paint?
                RECT rcPaint = { 0 };
                if( !::GetUpdateRect(m_hWndPaint, &rcPaint, FALSE) ) return true;
                if( m_pRoot == NULL ) {
                    PAINTSTRUCT ps = { 0 };
                    ::BeginPaint(m_hWndPaint, &ps);
                    ::EndPaint(m_hWndPaint, &ps);
                    return true;
                }            
                // Do we need to resize anything?
                // This is the time where we layout the controls on the form.
                // We delay this even from the WM_SIZE messages since resizing can be
                // a very expensize operation.
                if( m_bUpdateNeeded ) {
                    m_bUpdateNeeded = false;
                    RECT rcClient = { 0 };
                    ::GetClientRect(m_hWndPaint, &rcClient);
                    if( !::IsRectEmpty(&rcClient) ) {
                        if( m_pRoot->IsUpdateNeeded() ) {
                            m_pRoot->SetPos(rcClient);
                            if( m_hDcOffscreen != NULL ) ::DeleteDC(m_hDcOffscreen);
                            if( m_hDcBackground != NULL ) ::DeleteDC(m_hDcBackground);
                            if( m_hbmpOffscreen != NULL ) ::DeleteObject(m_hbmpOffscreen);
                            if( m_hbmpBackground != NULL ) ::DeleteObject(m_hbmpBackground);
                            m_hDcOffscreen = NULL;
                            m_hDcBackground = NULL;
                            m_hbmpOffscreen = NULL;
                            m_hbmpBackground = NULL;
                        }
                        else {
                            CControlUI* pControl = NULL;
                            while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) ) {
                                pControl->SetPos( pControl->GetPos() );
                            }
                        }
                        // We'll want to notify the window when it is first initialized
                        // with the correct layout. The window form would take the time
                        // to submit swipes/animations.
                        if( m_bFirstLayout ) {
                            m_bFirstLayout = false;
                            SendNotify(m_pRoot, DUI_MSGTYPE_WINDOWINIT,  0, 0, false);
                        }
                    }
                }
                // Set focus to first control?
                if( m_bFocusNeeded ) {
                    SetNextTabControl();
                }
                //
                // Render screen
                //
                // Prepare offscreen bitmap?
                if( m_bOffscreenPaint && m_hbmpOffscreen == NULL )
                {
                    RECT rcClient = { 0 };
                    ::GetClientRect(m_hWndPaint, &rcClient);
                    m_hDcOffscreen = ::CreateCompatibleDC(m_hDcPaint);
                    m_hbmpOffscreen = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); 
                    ASSERT(m_hDcOffscreen);
                    ASSERT(m_hbmpOffscreen);
                }
                // Begin Windows paint
                PAINTSTRUCT ps = { 0 };
                ::BeginPaint(m_hWndPaint, &ps);
                if( m_bOffscreenPaint )
                {
                    HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(m_hDcOffscreen, m_hbmpOffscreen);
                    int iSaveDC = ::SaveDC(m_hDcOffscreen);
                    if( m_bAlphaBackground ) {
                        if( m_hbmpBackground == NULL ) {
                            RECT rcClient = { 0 };
                            ::GetClientRect(m_hWndPaint, &rcClient);
                            m_hDcBackground = ::CreateCompatibleDC(m_hDcPaint);;
                            m_hbmpBackground = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); 
                            ASSERT(m_hDcBackground);
                            ASSERT(m_hbmpBackground);
                            ::SelectObject(m_hDcBackground, m_hbmpBackground);
                            ::BitBlt(m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,
                                ps.rcPaint.bottom - ps.rcPaint.top, ps.hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
                        }
                        else
                            ::SelectObject(m_hDcBackground, m_hbmpBackground);
                        ::BitBlt(m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,
                            ps.rcPaint.bottom - ps.rcPaint.top, m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
                    }
    
                    //绘制控件
                    m_pRoot->DoPaint(m_hDcOffscreen, ps.rcPaint);
                    for( int i = 0; i < m_aPostPaintControls.GetSize(); i++ ) {
                        CControlUI* pPostPaintControl = static_cast<CControlUI*>(m_aPostPaintControls[i]);
                        pPostPaintControl->DoPostPaint(m_hDcOffscreen, ps.rcPaint);
                    }
                    ::RestoreDC(m_hDcOffscreen, iSaveDC);
                    ::BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,
                        ps.rcPaint.bottom - ps.rcPaint.top, m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
                    ::SelectObject(m_hDcOffscreen, hOldBitmap);
    
                    if( m_bShowUpdateRect ) {
                        HPEN hOldPen = (HPEN)::SelectObject(ps.hdc, m_hUpdateRectPen);
                        ::SelectObject(ps.hdc, ::GetStockObject(HOLLOW_BRUSH));
                        ::Rectangle(ps.hdc, rcPaint.left, rcPaint.top, rcPaint.right, rcPaint.bottom);
                        ::SelectObject(ps.hdc, hOldPen);
                    }
                }
                else
                {
                    // A standard paint job
                    int iSaveDC = ::SaveDC(ps.hdc);
    
                    //绘制控件
                    m_pRoot->DoPaint(ps.hdc, ps.rcPaint);
                    ::RestoreDC(ps.hdc, iSaveDC);
                }
                // All Done!
                ::EndPaint(m_hWndPaint, &ps);
            }
            // If any of the painting requested a resize again, we'll need
            // to invalidate the entire window once more.
            if( m_bUpdateNeeded ) {
                ::InvalidateRect(m_hWndPaint, NULL, FALSE);
            }
            return true;

      所有的绘制工作都会回到基类CControlUI的DoPaint() 代码如下:

    void CControlUI::DoPaint(HDC hDC, const RECT& rcPaint)
    {
        if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;
    
        // 绘制循序:背景颜色->背景图->状态图->文本->边框
        if( m_cxyBorderRound.cx > 0 || m_cxyBorderRound.cy > 0 ) {
            CRenderClip roundClip;
            CRenderClip::GenerateRoundClip(hDC, m_rcPaint,  m_rcItem, m_cxyBorderRound.cx, m_cxyBorderRound.cy, roundClip);
            PaintBkColor(hDC);
            PaintBkImage(hDC);
            PaintStatusImage(hDC);
            PaintText(hDC);
            PaintBorder(hDC);
        }
        else {
            PaintBkColor(hDC);
            PaintBkImage(hDC);
            PaintStatusImage(hDC);
            PaintText(hDC);
            PaintBorder(hDC);
        }
    }

      但是,这里又引出一个问题,绘制工作是如何路由到CControlUI的DoPaint()中的呢,如果程序中比较简单,就像我们现在这样,没有用到容器,只有一个按钮,那么就相对简单,调用m_pRoot->DoPaint()直到调用的就是基类的DoPaint():

      假如我们的窗口中,使用的控件不只一个,必然用到容器,这时候再调用m_pRoot->DoPaint()就不是直接调用CControlUI::DoPaint()而是调用的容器类的DoPaint(),来看代码:

      

        void CContainerUI::DoPaint(HDC hDC, const RECT& rcPaint)
        {
            RECT rcTemp = { 0 };
            if( !::IntersectRect(&rcTemp, &rcPaint, &m_rcItem) ) return;
    
            CRenderClip clip;
            CRenderClip::GenerateClip(hDC, rcTemp, clip);
            CControlUI::DoPaint(hDC, rcPaint);
    
            if( m_items.GetSize() > 0 ) {
                RECT rc = m_rcItem;
                rc.left += m_rcInset.left;
                rc.top += m_rcInset.top;
                rc.right -= m_rcInset.right;
                rc.bottom -= m_rcInset.bottom;
                if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth();
                if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();
    
                if( !::IntersectRect(&rcTemp, &rcPaint, &rc) ) {
                    for( int it = 0; it < m_items.GetSize(); it++ ) {
                        CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);
                        if( !pControl->IsVisible() ) continue;
                        if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;
                        if( pControl ->IsFloat() ) {
                            if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;
                            pControl->DoPaint(hDC, rcPaint);
                        }
                    }
                }
                else {
                    CRenderClip childClip;
                    CRenderClip::GenerateClip(hDC, rcTemp, childClip);
                    for( int it = 0; it < m_items.GetSize(); it++ ) {
                        CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);
                        if( !pControl->IsVisible() ) continue;
                        if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;
                        if( pControl ->IsFloat() ) {
                            if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;
                            CRenderClip::UseOldClipBegin(hDC, childClip);
                            pControl->DoPaint(hDC, rcPaint);
                            CRenderClip::UseOldClipEnd(hDC, childClip);
                        }
                        else {
                            if( !::IntersectRect(&rcTemp, &rc, &pControl->GetPos()) ) continue;
                            pControl->DoPaint(hDC, rcPaint);
                        }
                    }
                }
            }
    
            if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() ) {
                if( ::IntersectRect(&rcTemp, &rcPaint, &m_pVerticalScrollBar->GetPos()) ) {
                    m_pVerticalScrollBar->DoPaint(hDC, rcPaint);
                }
            }
    
            if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {
                if( ::IntersectRect(&rcTemp, &rcPaint, &m_pHorizontalScrollBar->GetPos()) ) {
                    m_pHorizontalScrollBar->DoPaint(hDC, rcPaint);
                }
            }
        }

      控件容器绘制完自己后,遍历子控件(包括子控件容器)调用其DoPaint,完成子控件绘制(代码已经在上上面贴出);

      最终的绘制都是通过渲染引擎CRenderEngine实现的。

      这样看来,整个绘制思路还是很清晰的:CPaintManagerUI::MessageHandler(WM_PAINT)--->CContainerUI::DoPaint--->CControlUI::DoPaint--->CRenderEngine。

      OK绘制工作完成!

      

  • 相关阅读:
    刚开发的游戏《天黑请闭眼》
    用手机控制服务器
    专业网站打包/解包asp工具(E文精装版本)!
    令我爱慕的女子(转自7di.net)
    8088 汇编速查手册
    Asp调用函数是否会影响性能?
    文档管理器
    ubuntu install xxx.deb
    Java线程池的原理及几类线程池的介绍
    ubuntu download file path
  • 原文地址:https://www.cnblogs.com/xiejiulong/p/3793005.html
Copyright © 2020-2023  润新知