UIHorizontalLayout.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. #include "StdAfx.h"
  2. #include "UIHorizontalLayout.h"
  3. namespace DuiLib
  4. {
  5. IMPLEMENT_DUICONTROL(CHorizontalLayoutUI)
  6. CHorizontalLayoutUI::CHorizontalLayoutUI() : m_iSepWidth(0), m_uButtonState(0), m_bImmMode(false)
  7. {
  8. ptLastMouse.x = ptLastMouse.y = 0;
  9. ::ZeroMemory(&m_rcNewPos, sizeof(m_rcNewPos));
  10. }
  11. LPCTSTR CHorizontalLayoutUI::GetClass() const
  12. {
  13. return _T("HorizontalLayoutUI");
  14. }
  15. LPVOID CHorizontalLayoutUI::GetInterface(LPCTSTR pstrName)
  16. {
  17. if( _tcsicmp(pstrName, DUI_CTR_HORIZONTALLAYOUT) == 0 ) return static_cast<CHorizontalLayoutUI*>(this);
  18. return CContainerUI::GetInterface(pstrName);
  19. }
  20. UINT CHorizontalLayoutUI::GetControlFlags() const
  21. {
  22. if( IsEnabled() && m_iSepWidth != 0 ) return UIFLAG_SETCURSOR;
  23. else return 0;
  24. }
  25. void CHorizontalLayoutUI::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 cyNeeded = 0;
  53. int nAdjustables = 0;
  54. int cxFixed = 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.cy -= rcPadding.top + rcPadding.bottom;
  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.cx == 0 ) {
  74. nAdjustables++;
  75. }
  76. else {
  77. if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
  78. if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
  79. }
  80. cxFixed += sz.cx + pControl->GetPadding().left + pControl->GetPadding().right;
  81. sz.cy = MAX(sz.cy, 0);
  82. if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
  83. if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
  84. cyNeeded = MAX(cyNeeded, sz.cy + rcPadding.top + rcPadding.bottom);
  85. nEstimateNum++;
  86. }
  87. cxFixed += (nEstimateNum - 1) * iChildPadding;
  88. // Place elements
  89. int cxNeeded = 0;
  90. int cxExpand = 0;
  91. if( nAdjustables > 0 ) cxExpand = MAX(0, (szAvailable.cx - cxFixed) / nAdjustables);
  92. // Position the elements
  93. SIZE szRemaining = szAvailable;
  94. int iPosX = rc.left;
  95. // 滚动条
  96. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
  97. iPosX -= m_pHorizontalScrollBar->GetScrollPos();
  98. }
  99. else {
  100. // 子控件横向对其方式
  101. if(nAdjustables <= 0) {
  102. UINT iChildAlign = GetChildAlign();
  103. if (iChildAlign == DT_CENTER) {
  104. iPosX += (szAvailable.cx -cxFixed) / 2;
  105. }
  106. else if (iChildAlign == DT_RIGHT) {
  107. iPosX += (szAvailable.cx - cxFixed);
  108. }
  109. }
  110. }
  111. int iEstimate = 0;
  112. int iAdjustable = 0;
  113. int cxFixedRemaining = cxFixed;
  114. for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) {
  115. CControlUI* pControl = static_cast<CControlUI*>(m_items[it2]);
  116. if( !pControl->IsVisible() ) continue;
  117. if( pControl->IsFloat() ) {
  118. SetFloatPos(it2);
  119. continue;
  120. }
  121. iEstimate += 1;
  122. RECT rcPadding = pControl->GetPadding();
  123. szRemaining.cx -= rcPadding.left;
  124. szControlAvailable = szRemaining;
  125. szControlAvailable.cy -= rcPadding.top + rcPadding.bottom;
  126. iControlMaxWidth = pControl->GetFixedWidth();
  127. iControlMaxHeight = pControl->GetFixedHeight();
  128. if (iControlMaxWidth <= 0) iControlMaxWidth = pControl->GetMaxWidth();
  129. if (iControlMaxHeight <= 0) iControlMaxHeight = pControl->GetMaxHeight();
  130. if (szControlAvailable.cx > iControlMaxWidth) szControlAvailable.cx = iControlMaxWidth;
  131. if (szControlAvailable.cy > iControlMaxHeight) szControlAvailable.cy = iControlMaxHeight;
  132. cxFixedRemaining = cxFixedRemaining - (rcPadding.left + rcPadding.right);
  133. if (iEstimate > 1) cxFixedRemaining = cxFixedRemaining - iChildPadding;
  134. SIZE sz = pControl->EstimateSize(szControlAvailable);
  135. if( sz.cx == 0 ) {
  136. iAdjustable++;
  137. sz.cx = cxExpand;
  138. // Distribute remaining to last element (usually round-off left-overs)
  139. if( iAdjustable == nAdjustables ) {
  140. sz.cx = MAX(0, szRemaining.cx - rcPadding.right - cxFixedRemaining);
  141. }
  142. if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
  143. if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
  144. }
  145. else {
  146. if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
  147. if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
  148. cxFixedRemaining -= sz.cx;
  149. }
  150. sz.cy = pControl->GetMaxHeight();
  151. if( sz.cy == 0 ) sz.cy = szAvailable.cy - rcPadding.top - rcPadding.bottom;
  152. if( sz.cy < 0 ) sz.cy = 0;
  153. if( sz.cy > szControlAvailable.cy ) sz.cy = szControlAvailable.cy;
  154. if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
  155. UINT iChildAlign = GetChildVAlign();
  156. if (iChildAlign == DT_VCENTER) {
  157. int iPosY = (rc.bottom + rc.top) / 2;
  158. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
  159. iPosY += m_pVerticalScrollBar->GetScrollRange() / 2;
  160. iPosY -= m_pVerticalScrollBar->GetScrollPos();
  161. }
  162. RECT rcCtrl = { iPosX + rcPadding.left, iPosY - sz.cy/2, iPosX + sz.cx + rcPadding.left, iPosY + sz.cy - sz.cy/2 };
  163. pControl->SetPos(rcCtrl, false);
  164. }
  165. else if (iChildAlign == DT_BOTTOM) {
  166. int iPosY = rc.bottom;
  167. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
  168. iPosY += m_pVerticalScrollBar->GetScrollRange();
  169. iPosY -= m_pVerticalScrollBar->GetScrollPos();
  170. }
  171. RECT rcCtrl = { iPosX + rcPadding.left, iPosY - rcPadding.bottom - sz.cy, iPosX + sz.cx + rcPadding.left, iPosY - rcPadding.bottom };
  172. pControl->SetPos(rcCtrl, false);
  173. }
  174. else {
  175. int iPosY = rc.top;
  176. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
  177. iPosY -= m_pVerticalScrollBar->GetScrollPos();
  178. }
  179. RECT rcCtrl = { iPosX + rcPadding.left, iPosY + rcPadding.top, iPosX + sz.cx + rcPadding.left, iPosY + sz.cy + rcPadding.top };
  180. pControl->SetPos(rcCtrl, false);
  181. }
  182. iPosX += sz.cx + iChildPadding + rcPadding.left + rcPadding.right;
  183. cxNeeded += sz.cx + rcPadding.left + rcPadding.right;
  184. szRemaining.cx -= sz.cx + iChildPadding + rcPadding.right;
  185. }
  186. cxNeeded += (nEstimateNum - 1) * iChildPadding;
  187. // Process the scrollbar
  188. ProcessScrollBar(rc, cxNeeded, cyNeeded);
  189. }
  190. void CHorizontalLayoutUI::DoPostPaint(HDC hDC, const RECT& rcPaint)
  191. {
  192. if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode ) {
  193. RECT rcSeparator = GetThumbRect(true);
  194. CRenderEngine::DrawColor(hDC, rcSeparator, 0xAA000000);
  195. }
  196. }
  197. void CHorizontalLayoutUI::SetSepWidth(int iWidth)
  198. {
  199. m_iSepWidth = iWidth;
  200. }
  201. int CHorizontalLayoutUI::GetSepWidth() const
  202. {
  203. return m_iSepWidth;
  204. }
  205. void CHorizontalLayoutUI::SetSepImmMode(bool bImmediately)
  206. {
  207. if( m_bImmMode == bImmediately ) return;
  208. if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode && m_pManager != NULL ) {
  209. m_pManager->RemovePostPaint(this);
  210. }
  211. m_bImmMode = bImmediately;
  212. }
  213. bool CHorizontalLayoutUI::IsSepImmMode() const
  214. {
  215. return m_bImmMode;
  216. }
  217. void CHorizontalLayoutUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
  218. {
  219. if( _tcsicmp(pstrName, _T("sepwidth")) == 0 ) SetSepWidth(_ttoi(pstrValue));
  220. else if( _tcsicmp(pstrName, _T("sepimm")) == 0 ) SetSepImmMode(_tcsicmp(pstrValue, _T("true")) == 0);
  221. else CContainerUI::SetAttribute(pstrName, pstrValue);
  222. }
  223. void CHorizontalLayoutUI::DoEvent(TEventUI& event)
  224. {
  225. if( m_iSepWidth != 0 ) {
  226. if( event.Type == UIEVENT_BUTTONDOWN && IsEnabled() )
  227. {
  228. RECT rcSeparator = GetThumbRect(false);
  229. if( ::PtInRect(&rcSeparator, event.ptMouse) ) {
  230. m_uButtonState |= UISTATE_CAPTURED;
  231. ptLastMouse = event.ptMouse;
  232. m_rcNewPos = m_rcItem;
  233. if( !m_bImmMode && m_pManager ) m_pManager->AddPostPaint(this);
  234. return;
  235. }
  236. }
  237. if( event.Type == UIEVENT_BUTTONUP )
  238. {
  239. if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
  240. m_uButtonState &= ~UISTATE_CAPTURED;
  241. m_rcItem = m_rcNewPos;
  242. if( !m_bImmMode && m_pManager ) m_pManager->RemovePostPaint(this);
  243. NeedParentUpdate();
  244. return;
  245. }
  246. }
  247. if( event.Type == UIEVENT_MOUSEMOVE )
  248. {
  249. if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
  250. LONG cx = event.ptMouse.x - ptLastMouse.x;
  251. ptLastMouse = event.ptMouse;
  252. RECT rc = m_rcNewPos;
  253. if( m_iSepWidth >= 0 ) {
  254. if( cx > 0 && event.ptMouse.x < m_rcNewPos.right - m_iSepWidth ) return;
  255. if( cx < 0 && event.ptMouse.x > m_rcNewPos.right ) return;
  256. rc.right += cx;
  257. if( rc.right - rc.left <= GetMinWidth() ) {
  258. if( m_rcNewPos.right - m_rcNewPos.left <= GetMinWidth() ) return;
  259. rc.right = rc.left + GetMinWidth();
  260. }
  261. if( rc.right - rc.left >= GetMaxWidth() ) {
  262. if( m_rcNewPos.right - m_rcNewPos.left >= GetMaxWidth() ) return;
  263. rc.right = rc.left + GetMaxWidth();
  264. }
  265. }
  266. else {
  267. if( cx > 0 && event.ptMouse.x < m_rcNewPos.left ) return;
  268. if( cx < 0 && event.ptMouse.x > m_rcNewPos.left - m_iSepWidth ) return;
  269. rc.left += cx;
  270. if( rc.right - rc.left <= GetMinWidth() ) {
  271. if( m_rcNewPos.right - m_rcNewPos.left <= GetMinWidth() ) return;
  272. rc.left = rc.right - GetMinWidth();
  273. }
  274. if( rc.right - rc.left >= GetMaxWidth() ) {
  275. if( m_rcNewPos.right - m_rcNewPos.left >= GetMaxWidth() ) return;
  276. rc.left = rc.right - GetMaxWidth();
  277. }
  278. }
  279. CDuiRect rcInvalidate = GetThumbRect(true);
  280. m_rcNewPos = rc;
  281. m_cxyFixed.cx = m_rcNewPos.right - m_rcNewPos.left;
  282. if( m_bImmMode ) {
  283. m_rcItem = m_rcNewPos;
  284. NeedParentUpdate();
  285. }
  286. else {
  287. rcInvalidate.Join(GetThumbRect(true));
  288. rcInvalidate.Join(GetThumbRect(false));
  289. if( m_pManager ) m_pManager->Invalidate(rcInvalidate);
  290. }
  291. return;
  292. }
  293. }
  294. if( event.Type == UIEVENT_SETCURSOR )
  295. {
  296. RECT rcSeparator = GetThumbRect(false);
  297. if( IsEnabled() && ::PtInRect(&rcSeparator, event.ptMouse) ) {
  298. ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE)));
  299. return;
  300. }
  301. }
  302. }
  303. CContainerUI::DoEvent(event);
  304. }
  305. RECT CHorizontalLayoutUI::GetThumbRect(bool bUseNew) const
  306. {
  307. if( (m_uButtonState & UISTATE_CAPTURED) != 0 && bUseNew) {
  308. if( m_iSepWidth >= 0 ) return CDuiRect(m_rcNewPos.right - m_iSepWidth, m_rcNewPos.top, m_rcNewPos.right, m_rcNewPos.bottom);
  309. else return CDuiRect(m_rcNewPos.left, m_rcNewPos.top, m_rcNewPos.left - m_iSepWidth, m_rcNewPos.bottom);
  310. }
  311. else {
  312. if( m_iSepWidth >= 0 ) return CDuiRect(m_rcItem.right - m_iSepWidth, m_rcItem.top, m_rcItem.right, m_rcItem.bottom);
  313. else return CDuiRect(m_rcItem.left, m_rcItem.top, m_rcItem.left - m_iSepWidth, m_rcItem.bottom);
  314. }
  315. }
  316. }