UIVerticalLayout.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #include "StdAfx.h"
  2. #include "UIVerticalLayout.h"
  3. namespace DuiLib
  4. {
  5. IMPLEMENT_DUICONTROL(CVerticalLayoutUI)
  6. CVerticalLayoutUI::CVerticalLayoutUI() : m_iSepHeight(0), m_uButtonState(0), m_bImmMode(false)
  7. {
  8. ptLastMouse.x = ptLastMouse.y = 0;
  9. ::ZeroMemory(&m_rcNewPos, sizeof(m_rcNewPos));
  10. }
  11. LPCTSTR CVerticalLayoutUI::GetClass() const
  12. {
  13. return _T("VerticalLayoutUI");
  14. }
  15. LPVOID CVerticalLayoutUI::GetInterface(LPCTSTR pstrName)
  16. {
  17. if( _tcsicmp(pstrName, DUI_CTR_VERTICALLAYOUT) == 0 ) return static_cast<CVerticalLayoutUI*>(this);
  18. return CContainerUI::GetInterface(pstrName);
  19. }
  20. UINT CVerticalLayoutUI::GetControlFlags() const
  21. {
  22. if( IsEnabled() && m_iSepHeight != 0 ) return UIFLAG_SETCURSOR;
  23. else return 0;
  24. }
  25. void CVerticalLayoutUI::SetPos(RECT rc, bool bNeedInvalidate)
  26. {
  27. CControlUI::SetPos(rc, bNeedInvalidate);
  28. rc = m_rcItem;
  29. // Adjust for inset
  30. RECT rcInset = GetInset();
  31. rc.left += rcInset.left;
  32. rc.top += rcInset.top;
  33. rc.right -= rcInset.right;
  34. rc.bottom -= rcInset.bottom;
  35. // 【R63】fixedscrollbar="true" 时无论滚动条显不显示都预留它的宽度:
  36. // 滚动条出现/消失不再改变内容区宽度 ⇒ 消除「内容左右抖动」。
  37. // 默认 false 时行为与改动前完全一致。
  38. if( m_pVerticalScrollBar && (IsFixedScrollbar() || m_pVerticalScrollBar->IsVisible()) )
  39. rc.right -= m_pVerticalScrollBar->GetFixedWidth();
  40. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();
  41. if( m_items.GetSize() == 0) {
  42. ProcessScrollBar(rc, 0, 0);
  43. return;
  44. }
  45. int iChildPadding = GetChildPadding();
  46. // Determine the minimum size
  47. SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top };
  48. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() )
  49. szAvailable.cx += m_pHorizontalScrollBar->GetScrollRange();
  50. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() )
  51. szAvailable.cy += m_pVerticalScrollBar->GetScrollRange();
  52. int cxNeeded = 0;
  53. int nAdjustables = 0;
  54. int cyFixed = 0;
  55. int nEstimateNum = 0;
  56. SIZE szControlAvailable;
  57. int iControlMaxWidth = 0;
  58. int iControlMaxHeight = 0;
  59. for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) {
  60. CControlUI* pControl = static_cast<CControlUI*>(m_items[it1]);
  61. if( !pControl->IsVisible() ) continue;
  62. if( pControl->IsFloat() ) continue;
  63. szControlAvailable = szAvailable;
  64. RECT rcPadding = pControl->GetPadding();
  65. szControlAvailable.cx -= rcPadding.left + rcPadding.right;
  66. iControlMaxWidth = pControl->GetFixedWidth();
  67. iControlMaxHeight = pControl->GetFixedHeight();
  68. if (iControlMaxWidth <= 0) iControlMaxWidth = pControl->GetMaxWidth();
  69. if (iControlMaxHeight <= 0) iControlMaxHeight = pControl->GetMaxHeight();
  70. if (szControlAvailable.cx > iControlMaxWidth) szControlAvailable.cx = iControlMaxWidth;
  71. if (szControlAvailable.cy > iControlMaxHeight) szControlAvailable.cy = iControlMaxHeight;
  72. SIZE sz = pControl->EstimateSize(szControlAvailable);
  73. if( sz.cy == 0 ) {
  74. nAdjustables++;
  75. }
  76. else {
  77. if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
  78. if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
  79. }
  80. cyFixed += sz.cy + pControl->GetPadding().top + pControl->GetPadding().bottom;
  81. sz.cx = MAX(sz.cx, 0);
  82. if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
  83. if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
  84. cxNeeded = MAX(cxNeeded, sz.cx + rcPadding.left + rcPadding.right);
  85. nEstimateNum++;
  86. }
  87. cyFixed += (nEstimateNum - 1) * iChildPadding;
  88. // Place elements
  89. int cyNeeded = 0;
  90. int cyExpand = 0;
  91. if( nAdjustables > 0 ) cyExpand = MAX(0, (szAvailable.cy - cyFixed) / nAdjustables);
  92. // Position the elements
  93. SIZE szRemaining = szAvailable;
  94. int iPosY = rc.top;
  95. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
  96. iPosY -= m_pVerticalScrollBar->GetScrollPos();
  97. }
  98. else {
  99. // 子控件垂直对其方式
  100. if(nAdjustables <= 0) {
  101. UINT iChildAlign = GetChildVAlign();
  102. if (iChildAlign == DT_VCENTER) {
  103. iPosY += (szAvailable.cy -cyFixed) / 2;
  104. }
  105. else if (iChildAlign == DT_BOTTOM) {
  106. iPosY += (szAvailable.cy - cyFixed);
  107. }
  108. }
  109. }
  110. int iEstimate = 0;
  111. int iAdjustable = 0;
  112. int cyFixedRemaining = cyFixed;
  113. for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) {
  114. CControlUI* pControl = static_cast<CControlUI*>(m_items[it2]);
  115. if( !pControl->IsVisible() ) continue;
  116. if( pControl->IsFloat() ) {
  117. SetFloatPos(it2);
  118. continue;
  119. }
  120. iEstimate += 1;
  121. RECT rcPadding = pControl->GetPadding();
  122. szRemaining.cy -= rcPadding.top;
  123. szControlAvailable = szRemaining;
  124. szControlAvailable.cx -= rcPadding.left + rcPadding.right;
  125. iControlMaxWidth = pControl->GetFixedWidth();
  126. iControlMaxHeight = pControl->GetFixedHeight();
  127. if (iControlMaxWidth <= 0) iControlMaxWidth = pControl->GetMaxWidth();
  128. if (iControlMaxHeight <= 0) iControlMaxHeight = pControl->GetMaxHeight();
  129. if (szControlAvailable.cx > iControlMaxWidth) szControlAvailable.cx = iControlMaxWidth;
  130. if (szControlAvailable.cy > iControlMaxHeight) szControlAvailable.cy = iControlMaxHeight;
  131. cyFixedRemaining = cyFixedRemaining - (rcPadding.top + rcPadding.bottom);
  132. if (iEstimate > 1) cyFixedRemaining = cyFixedRemaining - iChildPadding;
  133. SIZE sz = pControl->EstimateSize(szControlAvailable);
  134. if( sz.cy == 0 ) {
  135. iAdjustable++;
  136. sz.cy = cyExpand;
  137. // Distribute remaining to last element (usually round-off left-overs)
  138. if( iAdjustable == nAdjustables ) {
  139. sz.cy = MAX(0, szRemaining.cy - rcPadding.bottom - cyFixedRemaining);
  140. }
  141. if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
  142. if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
  143. }
  144. else {
  145. if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
  146. if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
  147. cyFixedRemaining -= sz.cy;
  148. }
  149. sz.cx = MAX(sz.cx, 0);
  150. if( sz.cx == 0 ) sz.cx = szAvailable.cx - rcPadding.left - rcPadding.right;
  151. if( sz.cx > szControlAvailable.cx ) sz.cx = szControlAvailable.cx;
  152. if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
  153. UINT iChildAlign = GetChildAlign();
  154. if (iChildAlign == DT_CENTER) {
  155. int iPosX = (rc.right + rc.left) / 2;
  156. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
  157. iPosX += m_pHorizontalScrollBar->GetScrollRange() / 2;
  158. iPosX -= m_pHorizontalScrollBar->GetScrollPos();
  159. }
  160. RECT rcCtrl = { iPosX - sz.cx/2, iPosY + rcPadding.top, iPosX + sz.cx - sz.cx/2, iPosY + sz.cy + rcPadding.top };
  161. pControl->SetPos(rcCtrl, false);
  162. }
  163. else if (iChildAlign == DT_RIGHT) {
  164. int iPosX = rc.right;
  165. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
  166. iPosX += m_pHorizontalScrollBar->GetScrollRange();
  167. iPosX -= m_pHorizontalScrollBar->GetScrollPos();
  168. }
  169. RECT rcCtrl = { iPosX - rcPadding.right - sz.cx, iPosY + rcPadding.top, iPosX - rcPadding.right, iPosY + sz.cy + rcPadding.top };
  170. pControl->SetPos(rcCtrl, false);
  171. }
  172. else {
  173. int iPosX = rc.left;
  174. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
  175. iPosX -= m_pHorizontalScrollBar->GetScrollPos();
  176. }
  177. RECT rcCtrl = { iPosX + rcPadding.left, iPosY + rcPadding.top, iPosX + rcPadding.left + sz.cx, iPosY + sz.cy + rcPadding.top };
  178. pControl->SetPos(rcCtrl, false);
  179. }
  180. iPosY += sz.cy + iChildPadding + rcPadding.top + rcPadding.bottom;
  181. cyNeeded += sz.cy + rcPadding.top + rcPadding.bottom;
  182. szRemaining.cy -= sz.cy + iChildPadding + rcPadding.bottom;
  183. }
  184. cyNeeded += (nEstimateNum - 1) * iChildPadding;
  185. // Process the scrollbar
  186. ProcessScrollBar(rc, cxNeeded, cyNeeded);
  187. }
  188. void CVerticalLayoutUI::DoPostPaint(HDC hDC, const RECT& rcPaint)
  189. {
  190. if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode ) {
  191. RECT rcSeparator = GetThumbRect(true);
  192. CRenderEngine::DrawColor(hDC, rcSeparator, 0xAA000000);
  193. }
  194. }
  195. void CVerticalLayoutUI::SetSepHeight(int iHeight)
  196. {
  197. m_iSepHeight = iHeight;
  198. }
  199. int CVerticalLayoutUI::GetSepHeight() const
  200. {
  201. if(m_pManager != NULL) m_pManager->GetDPIObj()->Scale(m_iSepHeight);
  202. return m_iSepHeight;
  203. }
  204. void CVerticalLayoutUI::SetSepImmMode(bool bImmediately)
  205. {
  206. if( m_bImmMode == bImmediately ) return;
  207. if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode && m_pManager != NULL ) {
  208. m_pManager->RemovePostPaint(this);
  209. }
  210. m_bImmMode = bImmediately;
  211. }
  212. bool CVerticalLayoutUI::IsSepImmMode() const
  213. {
  214. return m_bImmMode;
  215. }
  216. void CVerticalLayoutUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
  217. {
  218. if( _tcsicmp(pstrName, _T("sepheight")) == 0 ) SetSepHeight(_ttoi(pstrValue));
  219. else if( _tcsicmp(pstrName, _T("sepimm")) == 0 ) SetSepImmMode(_tcsicmp(pstrValue, _T("true")) == 0);
  220. else CContainerUI::SetAttribute(pstrName, pstrValue);
  221. }
  222. void CVerticalLayoutUI::DoEvent(TEventUI& event)
  223. {
  224. if( m_iSepHeight != 0 ) {
  225. if( event.Type == UIEVENT_BUTTONDOWN && IsEnabled() )
  226. {
  227. RECT rcSeparator = GetThumbRect(false);
  228. if( ::PtInRect(&rcSeparator, event.ptMouse) ) {
  229. m_uButtonState |= UISTATE_CAPTURED;
  230. ptLastMouse = event.ptMouse;
  231. m_rcNewPos = m_rcItem;
  232. if( !m_bImmMode && m_pManager ) m_pManager->AddPostPaint(this);
  233. return;
  234. }
  235. }
  236. if( event.Type == UIEVENT_BUTTONUP )
  237. {
  238. if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
  239. m_uButtonState &= ~UISTATE_CAPTURED;
  240. m_rcItem = m_rcNewPos;
  241. if( !m_bImmMode && m_pManager ) m_pManager->RemovePostPaint(this);
  242. NeedParentUpdate();
  243. return;
  244. }
  245. }
  246. if( event.Type == UIEVENT_MOUSEMOVE )
  247. {
  248. if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
  249. LONG cy = event.ptMouse.y - ptLastMouse.y;
  250. ptLastMouse = event.ptMouse;
  251. RECT rc = m_rcNewPos;
  252. if( m_iSepHeight >= 0 ) {
  253. if( cy > 0 && event.ptMouse.y < m_rcNewPos.bottom + m_iSepHeight ) return;
  254. if( cy < 0 && event.ptMouse.y > m_rcNewPos.bottom ) return;
  255. rc.bottom += cy;
  256. if( rc.bottom - rc.top <= GetMinHeight() ) {
  257. if( m_rcNewPos.bottom - m_rcNewPos.top <= GetMinHeight() ) return;
  258. rc.bottom = rc.top + GetMinHeight();
  259. }
  260. if( rc.bottom - rc.top >= GetMaxHeight() ) {
  261. if( m_rcNewPos.bottom - m_rcNewPos.top >= GetMaxHeight() ) return;
  262. rc.bottom = rc.top + GetMaxHeight();
  263. }
  264. }
  265. else {
  266. if( cy > 0 && event.ptMouse.y < m_rcNewPos.top ) return;
  267. if( cy < 0 && event.ptMouse.y > m_rcNewPos.top + m_iSepHeight ) return;
  268. rc.top += cy;
  269. if( rc.bottom - rc.top <= GetMinHeight() ) {
  270. if( m_rcNewPos.bottom - m_rcNewPos.top <= GetMinHeight() ) return;
  271. rc.top = rc.bottom - GetMinHeight();
  272. }
  273. if( rc.bottom - rc.top >= GetMaxHeight() ) {
  274. if( m_rcNewPos.bottom - m_rcNewPos.top >= GetMaxHeight() ) return;
  275. rc.top = rc.bottom - GetMaxHeight();
  276. }
  277. }
  278. CDuiRect rcInvalidate = GetThumbRect(true);
  279. m_rcNewPos = rc;
  280. m_cxyFixed.cy = GetManager()->GetDPIObj()->Scale(m_rcNewPos.bottom - m_rcNewPos.top);
  281. if( m_bImmMode ) {
  282. m_rcItem = m_rcNewPos;
  283. NeedParentUpdate();
  284. }
  285. else {
  286. rcInvalidate.Join(GetThumbRect(true));
  287. rcInvalidate.Join(GetThumbRect(false));
  288. if( m_pManager ) m_pManager->Invalidate(rcInvalidate);
  289. }
  290. return;
  291. }
  292. }
  293. if( event.Type == UIEVENT_SETCURSOR )
  294. {
  295. RECT rcSeparator = GetThumbRect(false);
  296. if( IsEnabled() && ::PtInRect(&rcSeparator, event.ptMouse) ) {
  297. ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZENS)));
  298. return;
  299. }
  300. }
  301. }
  302. CContainerUI::DoEvent(event);
  303. }
  304. RECT CVerticalLayoutUI::GetThumbRect(bool bUseNew) const
  305. {
  306. if( (m_uButtonState & UISTATE_CAPTURED) != 0 && bUseNew) {
  307. if( m_iSepHeight >= 0 )
  308. return CDuiRect(m_rcNewPos.left, MAX(m_rcNewPos.bottom - m_iSepHeight, m_rcNewPos.top),
  309. m_rcNewPos.right, m_rcNewPos.bottom);
  310. else
  311. return CDuiRect(m_rcNewPos.left, m_rcNewPos.top, m_rcNewPos.right,
  312. MIN(m_rcNewPos.top - m_iSepHeight, m_rcNewPos.bottom));
  313. }
  314. else {
  315. if( m_iSepHeight >= 0 )
  316. return CDuiRect(m_rcItem.left, MAX(m_rcItem.bottom - m_iSepHeight, m_rcItem.top), m_rcItem.right,
  317. m_rcItem.bottom);
  318. else
  319. return CDuiRect(m_rcItem.left, m_rcItem.top, m_rcItem.right,
  320. MIN(m_rcItem.top - m_iSepHeight, m_rcItem.bottom));
  321. }
  322. }
  323. }