您当前的位置: 首页 >  ui

插件开发

暂无认证

  • 1浏览

    0关注

    492博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

第一章 第九小节Duilib的绘制消息流程-WM_PAINT

插件开发 发布时间:2022-03-29 07:40:10 ,浏览量:1

文章目录
    • 1.WM_PAINT消息
    • 2.作者答疑

1.WM_PAINT消息

  经过前面的解析,耐心的读者,对duilib这个库,不论从体系架构,还是从细节上,应该都有了一定程序的了解,本小节从界面库这个最核心的绘制流程上,给大家做轮廓上的解析,之所以这么说,因为一些细节只是为了支持更多的功能,但是不常用,所以留给感兴趣的读者自己研究。   首先用duilib创建的最上层窗口也是一个win32窗口,也就是我们继承至WindowImplBase基类创建的窗口,在窗口消息过程函数中响应窗口绘制消息WM_PAINT,从前面的源码分析可知,这个消息在WindowImplBase基类成员CPaintManagerUI对象中的消息函数MessageHandler中响应,由于整个消息函数源代码太长,就截取WM_PAINT消息部分,如下所示:

case WM_PAINT:
            { 
                if( m_pRoot == NULL ) {
                    PAINTSTRUCT ps = { 0 };
                    ::BeginPaint(m_hWndPaint, &ps);
                    CRenderEngine::DrawColor(m_hDcPaint, ps.rcPaint, 0xFF000000);
                    ::EndPaint(m_hWndPaint, &ps);
                    return true;
                }

                RECT rcClient = { 0 };
                ::GetClientRect(m_hWndPaint, &rcClient);

                RECT rcPaint = { 0 };
                if( !::GetUpdateRect(m_hWndPaint, &rcPaint, FALSE) ) return true;

                // Set focus to first control?
                if( m_bFocusNeeded ) {
                    SetNextTabControl();
                }

                SetPainting(true);

                bool bNeedSizeMsg = false;
                DWORD dwWidth = rcClient.right - rcClient.left;
                DWORD dwHeight = rcClient.bottom - rcClient.top;

                SetPainting(true);
                //是否需要刷新
                if( m_bUpdateNeeded ) {
                    m_bUpdateNeeded = false;
                    if( !::IsRectEmpty(&rcClient) && !::IsIconic(m_hWndPaint) ) {
                        if( m_pRoot->IsUpdateNeeded() ) {
                            RECT rcRoot = 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;
                            if( m_bLayered ) {
                                rcRoot.left += m_rcLayeredInset.left;
                                rcRoot.top += m_rcLayeredInset.top;
                                rcRoot.right -= m_rcLayeredInset.right;
                                rcRoot.bottom -= m_rcLayeredInset.bottom;
                            }
                            m_pRoot->SetPos(rcRoot, true);
                            bNeedSizeMsg = true;
                        }
                        else {
                            CControlUI* pControl = NULL;
                            m_aFoundControls.Empty();
                            m_pRoot->FindControl(__FindControlsFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST | UIFIND_UPDATETEST);
                            for( int it = 0; it IsFloat() ) pControl->SetPos(pControl->GetPos(), true);
                                else pControl->SetPos(pControl->GetRelativePos(), true);
                            }
                            bNeedSizeMsg = true;
                        }
                        // 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);
                            if( m_bLayered && m_bLayeredChanged ) {
                                Invalidate();
                                SetPainting(false);
                                return true;
                            }
                            // 更新阴影窗口显示
                            m_shadow.Update(m_hWndPaint);
                        }
                    }
                }
                else if( m_bLayered && m_bLayeredChanged ) {
                    RECT rcRoot = rcClient;
                    if( m_pOffscreenBits ) ::ZeroMemory(m_pOffscreenBits, (rcRoot.right - rcRoot.left) 
                        * (rcRoot.bottom - rcRoot.top) * 4);
                    rcRoot.left += m_rcLayeredInset.left;
                    rcRoot.top += m_rcLayeredInset.top;
                    rcRoot.right -= m_rcLayeredInset.right;
                    rcRoot.bottom -= m_rcLayeredInset.bottom;
                    m_pRoot->SetPos(rcRoot, true);
                }
				//与透明窗口相关
                if( m_bLayered ) {
                    DWORD dwExStyle = ::GetWindowLong(m_hWndPaint, GWL_EXSTYLE);
                    DWORD dwNewExStyle = dwExStyle | WS_EX_LAYERED;
                    if(dwExStyle != dwNewExStyle) ::SetWindowLong(m_hWndPaint, GWL_EXSTYLE, dwNewExStyle);
                    m_bOffscreenPaint = true;
                    UnionRect(&rcPaint, &rcPaint, &m_rcLayeredUpdate);
                    if( rcPaint.right > rcClient.right ) rcPaint.right = rcClient.right;
                    if( rcPaint.bottom > rcClient.bottom ) rcPaint.bottom = rcClient.bottom;
                    ::ZeroMemory(&m_rcLayeredUpdate, sizeof(m_rcLayeredUpdate));
                }

                //
                // Render screen
                //
                // Prepare offscreen bitmap
                if( m_bOffscreenPaint && m_hbmpOffscreen == NULL ) {
                    m_hDcOffscreen = ::CreateCompatibleDC(m_hDcPaint);
                    m_hbmpOffscreen = CRenderEngine::CreateARGB32Bitmap(m_hDcPaint, dwWidth, dwHeight, (LPBYTE*)&m_pOffscreenBits); 
                    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_bLayered) {
                        for( LONG y = rcClient.bottom - rcPaint.bottom; y  8) * A / 255;
                                        B = (BYTE)(*pOffscreenBits) * A / 255;
                                        *pOffscreenBits = RGB(B, G, R) + ((DWORD)A  0 ) {
            RECT rcInset = GetInset();
            RECT rc = m_rcItem;
            rc.left += rcInset.left;
            rc.top += rcInset.top;
            rc.right -= rcInset.right;
            rc.bottom -= 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 IsVisible() ) continue;
                    if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;
                    if( pControl ->IsFloat() ) {
                        if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;
                        if( !pControl->Paint(hDC, rcPaint, pStopControl) ) return false;//子控件绘制函数
                    }
                }
            }
            else {
                CRenderClip childClip;
                CRenderClip::GenerateClip(hDC, rcTemp, childClip);
                for( int it = 0; it IsVisible() ) continue;
                    if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;
                    if( pControl->IsFloat() ) {
                        if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;
                        CRenderClip::UseOldClipBegin(hDC, childClip);
                        if( !pControl->Paint(hDC, rcPaint, pStopControl) ) return false;
                        CRenderClip::UseOldClipEnd(hDC, childClip);
                    }
                    else {
                        if( !::IntersectRect(&rcTemp, &rc, &pControl->GetPos()) ) continue;
                        if( !pControl->Paint(hDC, rcPaint, pStopControl) ) return false;
                    }
                }
            }
        }

		//垂直滚动条绘制
        if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() ) {
            if( m_pVerticalScrollBar == pStopControl ) return false;
            if( ::IntersectRect(&rcTemp, &rcPaint, &m_pVerticalScrollBar->GetPos()) ) {
                if( !m_pVerticalScrollBar->Paint(hDC, rcPaint, pStopControl) ) return false;
            }
        }
		//水平滚动条绘制
        if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {
            if( m_pHorizontalScrollBar == pStopControl ) return false;
            if( ::IntersectRect(&rcTemp, &rcPaint, &m_pHorizontalScrollBar->GetPos()) ) {
                if( !m_pHorizontalScrollBar->Paint(hDC, rcPaint, pStopControl) ) return false;
            }
        }
        return true;
    }

  从上面的代码可以清晰看出其轮廓,绘制流程是个迭代算法,先调用Paint函数,如果是个容器,调用容器DoPaint函数,由容器DoPaint函数再调用父类CControlUI的DoPaint函数先绘制,然后再调用容器包含的子控件的Paint函数。如果是个简单子控件,则直接调用DoPaint函数函数完成绘制。直到所有控件绘制完毕。所有根控件CControlUI的绘制函数,这是个控件绘制最基本的流程,如下所示:

 bool CControlUI::DoPaint(HDC hDC, const RECT& rcPaint, CControlUI* pStopControl)
    {
        // 绘制循序:背景颜色->背景图->状态图->文本->边框
        SIZE cxyBorderRound;
        RECT rcBorderSize;
        if (m_pManager) {
            cxyBorderRound = GetManager()->GetDPIObj()->Scale(m_cxyBorderRound);
            rcBorderSize = GetManager()->GetDPIObj()->Scale(m_rcBorderSize);
        }
        else {
            cxyBorderRound = m_cxyBorderRound;
            rcBorderSize = m_rcBorderSize;
        }

        if( cxyBorderRound.cx > 0 || cxyBorderRound.cy > 0 ) {
            CRenderClip roundClip;
            CRenderClip::GenerateRoundClip(hDC, m_rcPaint,  m_rcItem, cxyBorderRound.cx, cxyBorderRound.cy, roundClip);
            PaintBkColor(hDC);
            PaintBkImage(hDC);
            PaintStatusImage(hDC);
            PaintForeColor(hDC);
            PaintForeImage(hDC);
            PaintText(hDC);
            PaintBorder(hDC);
        }
        else {
            PaintBkColor(hDC);
            PaintBkImage(hDC);
            PaintStatusImage(hDC);
            PaintForeColor(hDC);
            PaintForeImage(hDC);
            PaintText(hDC);
            PaintBorder(hDC);
        }
        return true;
    }
2.作者答疑

  如有疑问,请留言。

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

微信扫码登录

0.0413s