DuiLib(四)——控件绘制

duilib的所有控件均绘制在唯一的真实窗口之中,本篇就具体看下这个绘制的过程。所有的绘制过程均在WM_PAINT消息处理过程中完成。由窗口及消息篇可以看到,窗口消息处理最终流到了CPaintManagerUI::MessageHandler中。包括WM_PAINT在内消息均在此函数中处理,我们仅关注WM_PAINT消息

  1     case WM_PAINT:

  2         {

  3             // Should we paint?

  4             RECT rcPaint = { 0 };

  5             if( !::GetUpdateRect(m_hWndPaint, &rcPaint, FALSE) ) return true;

  6             if( m_pRoot == NULL ) {

  7                 PAINTSTRUCT ps = { 0 };

  8                 ::BeginPaint(m_hWndPaint, &ps);

  9                 ::EndPaint(m_hWndPaint, &ps);

 10                 return true;

 11             }            

 12             // Do we need to resize anything?

 13             // This is the time where we layout the controls on the form.

 14             // We delay this even from the WM_SIZE messages since resizing can be

 15             // a very expensize operation.

 16             if( m_bUpdateNeeded ) {

 17                 m_bUpdateNeeded = false;

 18                 RECT rcClient = { 0 };

 19                 ::GetClientRect(m_hWndPaint, &rcClient);

 20                 if( !::IsRectEmpty(&rcClient) ) {

 21                     if( m_pRoot->IsUpdateNeeded() ) {

 22                         m_pRoot->SetPos(rcClient);

 23                         if( m_hDcOffscreen != NULL ) ::DeleteDC(m_hDcOffscreen);

 24                         if( m_hDcBackground != NULL ) ::DeleteDC(m_hDcBackground);

 25                         if( m_hbmpOffscreen != NULL ) ::DeleteObject(m_hbmpOffscreen);

 26                         if( m_hbmpBackground != NULL ) ::DeleteObject(m_hbmpBackground);

 27                         m_hDcOffscreen = NULL;

 28                         m_hDcBackground = NULL;

 29                         m_hbmpOffscreen = NULL;

 30                         m_hbmpBackground = NULL;

 31                     }

 32                     else {

 33                         CControlUI* pControl = NULL;

 34                         while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) ) {

 35                             pControl->SetPos( pControl->GetPos() );

 36                         }

 37                     }

 38                     // We'll want to notify the window when it is first initialized

 39                     // with the correct layout. The window form would take the time

 40                     // to submit swipes/animations.

 41                     if( m_bFirstLayout ) {

 42                         m_bFirstLayout = false;

 43                         SendNotify(m_pRoot, _T("windowinit"),  0, 0, false);

 44                     }

 45                 }

 46             }

 47             // Set focus to first control?

 48             if( m_bFocusNeeded ) {

 49                 SetNextTabControl();

 50             }

 51             //

 52             // Render screen

 53             //

 54             // Prepare offscreen bitmap?

 55             if( m_bOffscreenPaint && m_hbmpOffscreen == NULL )

 56             {

 57                 RECT rcClient = { 0 };

 58                 ::GetClientRect(m_hWndPaint, &rcClient);

 59                 m_hDcOffscreen = ::CreateCompatibleDC(m_hDcPaint);

 60                 m_hbmpOffscreen = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); 

 61                 ASSERT(m_hDcOffscreen);

 62                 ASSERT(m_hbmpOffscreen);

 63             }

 64             // Begin Windows paint

 65             PAINTSTRUCT ps = { 0 };

 66             ::BeginPaint(m_hWndPaint, &ps);  67             if( m_bOffscreenPaint )  68             {

 69                 HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(m_hDcOffscreen, m_hbmpOffscreen);

 70                 int iSaveDC = ::SaveDC(m_hDcOffscreen);

 71                 if( m_bAlphaBackground ) {

 72                     if( m_hbmpBackground == NULL ) {

 73                         RECT rcClient = { 0 };

 74                         ::GetClientRect(m_hWndPaint, &rcClient);

 75                         m_hDcBackground = ::CreateCompatibleDC(m_hDcPaint);;

 76                         m_hbmpBackground = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); 

 77                         ASSERT(m_hDcBackground);

 78                         ASSERT(m_hbmpBackground);

 79                         ::SelectObject(m_hDcBackground, m_hbmpBackground);

 80                         ::BitBlt(m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,

 81                             ps.rcPaint.bottom - ps.rcPaint.top, ps.hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);

 82                     }

 83                     else

 84                         ::SelectObject(m_hDcBackground, m_hbmpBackground);

 85                     ::BitBlt(m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,

 86                         ps.rcPaint.bottom - ps.rcPaint.top, m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);

 87                 }

 88                 m_pRoot->DoPaint(m_hDcOffscreen, ps.rcPaint);//绘制控件

 89                 for( int i = 0; i < m_aPostPaintControls.GetSize(); i++ ) {

 90                     CControlUI* pPostPaintControl = static_cast<CControlUI*>(m_aPostPaintControls[i]);

 91                     pPostPaintControl->DoPostPaint(m_hDcOffscreen, ps.rcPaint);

 92                 }

 93                 ::RestoreDC(m_hDcOffscreen, iSaveDC);

 94                 ::BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,

 95                     ps.rcPaint.bottom - ps.rcPaint.top, m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);

 96                 ::SelectObject(m_hDcOffscreen, hOldBitmap);

 97 

 98                 if( m_bShowUpdateRect ) {

 99                     HPEN hOldPen = (HPEN)::SelectObject(ps.hdc, m_hUpdateRectPen);

100                     ::SelectObject(ps.hdc, ::GetStockObject(HOLLOW_BRUSH));

101                     ::Rectangle(ps.hdc, rcPaint.left, rcPaint.top, rcPaint.right, rcPaint.bottom);

102                     ::SelectObject(ps.hdc, hOldPen);

103                 }

104             }

105             else

106             {

107                 // A standard paint job

108                 int iSaveDC = ::SaveDC(ps.hdc);

109                 m_pRoot->DoPaint(ps.hdc, ps.rcPaint);//绘制控件

110                 ::RestoreDC(ps.hdc, iSaveDC);

111             }

112             // All Done!

113             ::EndPaint(m_hWndPaint, &ps); 114         }

115         // If any of the painting requested a resize again, we'll need

116         // to invalidate the entire window once more.

117         if( m_bUpdateNeeded ) {

118             ::InvalidateRect(m_hWndPaint, NULL, FALSE);

119         }

120         return true;

在::BeginPaint(m_hWndPaint, &ps)和::EndPaint(m_hWndPaint, &ps)中间是窗口绘制部分,duilib包含了两种方式:双缓存方式(解决闪烁问题)和标准方式,默认为双缓存方式。两种方式最终都调用了m_pRoot->DoPaint,m_pRoot为控件容器,且DoPaint为虚函数,实际调用了CContainerUI::DoPaint

 1 void CContainerUI::DoPaint(HDC hDC, const RECT& rcPaint)

 2 {

 3     RECT rcTemp = { 0 };

 4     if( !::IntersectRect(&rcTemp, &rcPaint, &m_rcItem) ) return;

 5 

 6     CRenderClip clip;

 7     CRenderClip::GenerateClip(hDC, rcTemp, clip);

 8     CControlUI::DoPaint(hDC, rcPaint);

 9 

10     if( m_items.GetSize() > 0 ) {

11         RECT rc = m_rcItem;

12         rc.left += m_rcInset.left;

13         rc.top += m_rcInset.top;

14         rc.right -= m_rcInset.right;

15         rc.bottom -= m_rcInset.bottom;

16         if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth();

17         if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();

18 

19         if( !::IntersectRect(&rcTemp, &rcPaint, &rc) ) {

20             for( int it = 0; it < m_items.GetSize(); it++ ) {

21                 CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);

22                 if( !pControl->IsVisible() ) continue;

23                 if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;

24                 if( pControl ->IsFloat() ) {

25                     if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;

26                     pControl->DoPaint(hDC, rcPaint); 27                 }

28             }

29         }

30         else {

31             CRenderClip childClip;

32             CRenderClip::GenerateClip(hDC, rcTemp, childClip);

33             for( int it = 0; it < m_items.GetSize(); it++ ) {

34                 CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);

35                 if( !pControl->IsVisible() ) continue;

36                 if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;

37                 if( pControl ->IsFloat() ) {

38                     if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;

39                     CRenderClip::UseOldClipBegin(hDC, childClip);

40                     pControl->DoPaint(hDC, rcPaint); 41                     CRenderClip::UseOldClipEnd(hDC, childClip);

42                 }

43                 else {

44                     if( !::IntersectRect(&rcTemp, &rc, &pControl->GetPos()) ) continue;

45                     pControl->DoPaint(hDC, rcPaint); 46                 }

47             }

48         }

49     }

50 

51     if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() ) {

52         if( ::IntersectRect(&rcTemp, &rcPaint, &m_pVerticalScrollBar->GetPos()) ) {

53             m_pVerticalScrollBar->DoPaint(hDC, rcPaint);

54         }

55     }

56 

57     if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {

58         if( ::IntersectRect(&rcTemp, &rcPaint, &m_pHorizontalScrollBar->GetPos()) ) {

59             m_pHorizontalScrollBar->DoPaint(hDC, rcPaint);

60         }

61     }

62 }

控件容器绘制完自己后,遍历子控件(包括子控件容器)调用其DoPaint,完成子控件绘制

 1 void CControlUI::DoPaint(HDC hDC, const RECT& rcPaint)

 2 {

 3     if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;

 4 

 5     // 绘制循序:背景颜色->背景图->状态图->文本->边框

 6     if( m_cxyBorderRound.cx > 0 || m_cxyBorderRound.cy > 0 ) {

 7         CRenderClip roundClip;

 8         CRenderClip::GenerateRoundClip(hDC, m_rcPaint,  m_rcItem, m_cxyBorderRound.cx, m_cxyBorderRound.cy, roundClip);

 9         PaintBkColor(hDC);

10         PaintBkImage(hDC);

11         PaintStatusImage(hDC);

12         PaintText(hDC);

13         PaintBorder(hDC);

14     }

15     else {

16         PaintBkColor(hDC);

17         PaintBkImage(hDC);

18         PaintStatusImage(hDC);

19         PaintText(hDC);

20         PaintBorder(hDC);

21     }

22 }

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

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

 

你可能感兴趣的:(lib)