UIHorizontalLayout.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. #include "stdafx.h"
  2. #include "UIHorizontalLayout.h"
  3. namespace DuiLib
  4. {
  5. CHorizontalLayoutUI::CHorizontalLayoutUI() : m_iSepWidth(0), m_uButtonState(0), m_bImmMode(false)
  6. {
  7. m_ptLastMouse.x = m_ptLastMouse.y = 0;
  8. ::ZeroMemory(&m_rcNewPos, sizeof(m_rcNewPos));
  9. }
  10. LPCTSTR CHorizontalLayoutUI::GetClass() const
  11. {
  12. return DUI_CTR_HORIZONTALLAYOUT;
  13. }
  14. LPVOID CHorizontalLayoutUI::GetInterface(LPCTSTR pstrName)
  15. {
  16. if( _tcscmp(pstrName, DUI_CTR_HORIZONTALLAYOUT) == 0 ) return static_cast<CHorizontalLayoutUI*>(this);
  17. return CContainerUI::GetInterface(pstrName);
  18. }
  19. UINT CHorizontalLayoutUI::GetControlFlags() const
  20. {
  21. if( IsEnabled() && m_iSepWidth != 0 ) return UIFLAG_SETCURSOR;
  22. else return 0;
  23. }
  24. void CHorizontalLayoutUI::SetPos(RECT rc, bool bNeedInvalidate)
  25. {
  26. CControlUI::SetPos(rc, bNeedInvalidate);
  27. rc = m_rcItem;
  28. // Adjust for inset
  29. rc.left += m_rcInset.left;
  30. rc.top += m_rcInset.top;
  31. rc.right -= m_rcInset.right;
  32. rc.bottom -= m_rcInset.bottom;
  33. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth();
  34. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();
  35. if( m_items.GetSize() == 0) {
  36. ProcessScrollBar(rc, 0, 0);
  37. return;
  38. }
  39. // Determine the minimum size
  40. SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top };
  41. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() )
  42. szAvailable.cx += m_pHorizontalScrollBar->GetScrollRange();
  43. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() )
  44. szAvailable.cy += m_pVerticalScrollBar->GetScrollRange();
  45. int cyNeeded = 0;
  46. int nAdjustables = 0;
  47. int cxFixed = 0;
  48. int nEstimateNum = 0;
  49. SIZE szControlAvailable;
  50. int iControlMaxWidth = 0;
  51. int iControlMaxHeight = 0;
  52. for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) {
  53. CControlUI* pControl = static_cast<CControlUI*>(m_items[it1]);
  54. if( !pControl->IsVisible() ) continue;
  55. if( pControl->IsFloat() ) continue;
  56. szControlAvailable = szAvailable;
  57. RECT rcPadding = pControl->GetPadding();
  58. szControlAvailable.cy -= rcPadding.top + rcPadding.bottom;
  59. iControlMaxWidth = pControl->GetFixedWidth();
  60. iControlMaxHeight = pControl->GetFixedHeight();
  61. if (iControlMaxWidth <= 0) iControlMaxWidth = pControl->GetMaxWidth();
  62. if (iControlMaxHeight <= 0) iControlMaxHeight = pControl->GetMaxHeight();
  63. if (szControlAvailable.cx > iControlMaxWidth) szControlAvailable.cx = iControlMaxWidth;
  64. if (szControlAvailable.cy > iControlMaxHeight) szControlAvailable.cy = iControlMaxHeight;
  65. SIZE sz = { 0 };
  66. if (pControl->GetFixedWidth() == 0) {
  67. nAdjustables++;
  68. sz.cy = pControl->GetFixedHeight();
  69. }
  70. else {
  71. sz = pControl->EstimateSize(szControlAvailable);
  72. if (sz.cx == 0) {
  73. nAdjustables++;
  74. }
  75. else {
  76. if (sz.cx < pControl->GetMinWidth()) sz.cx = pControl->GetMinWidth();
  77. if (sz.cx > pControl->GetMaxWidth()) sz.cx = pControl->GetMaxWidth();
  78. }
  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) * m_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. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
  96. iPosX -= m_pHorizontalScrollBar->GetScrollPos();
  97. }
  98. int iEstimate = 0;
  99. int iAdjustable = 0;
  100. int cxFixedRemaining = cxFixed;
  101. for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) {
  102. CControlUI* pControl = static_cast<CControlUI*>(m_items[it2]);
  103. if( !pControl->IsVisible() ) continue;
  104. if( pControl->IsFloat() ) {
  105. SetFloatPos(it2);
  106. continue;
  107. }
  108. iEstimate += 1;
  109. RECT rcPadding = pControl->GetPadding();
  110. szRemaining.cx -= rcPadding.left;
  111. szControlAvailable = szRemaining;
  112. szControlAvailable.cy -= rcPadding.top + rcPadding.bottom;
  113. iControlMaxWidth = pControl->GetFixedWidth();
  114. iControlMaxHeight = pControl->GetFixedHeight();
  115. if (iControlMaxWidth <= 0) iControlMaxWidth = pControl->GetMaxWidth();
  116. if (iControlMaxHeight <= 0) iControlMaxHeight = pControl->GetMaxHeight();
  117. if (szControlAvailable.cx > iControlMaxWidth) szControlAvailable.cx = iControlMaxWidth;
  118. if (szControlAvailable.cy > iControlMaxHeight) szControlAvailable.cy = iControlMaxHeight;
  119. cxFixedRemaining = cxFixedRemaining - (rcPadding.left + rcPadding.right);
  120. if (iEstimate > 1) cxFixedRemaining = cxFixedRemaining - m_iChildPadding;
  121. SIZE sz = pControl->EstimateSize(szControlAvailable);
  122. if (pControl->GetFixedWidth() == 0 || sz.cx == 0) {
  123. iAdjustable++;
  124. sz.cx = cxExpand;
  125. // Distribute remaining to last element (usually round-off left-overs)
  126. if( iAdjustable == nAdjustables ) {
  127. sz.cx = MAX(0, szRemaining.cx - rcPadding.right - cxFixedRemaining);
  128. }
  129. if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
  130. if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
  131. }
  132. else {
  133. if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
  134. if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
  135. cxFixedRemaining -= sz.cx;
  136. }
  137. sz.cy = pControl->GetMaxHeight();
  138. if( sz.cy == 0 ) sz.cy = szAvailable.cy - rcPadding.top - rcPadding.bottom;
  139. if( sz.cy < 0 ) sz.cy = 0;
  140. if( sz.cy > szControlAvailable.cy ) sz.cy = szControlAvailable.cy;
  141. if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
  142. UINT iChildAlign = GetChildVAlign();
  143. if (iChildAlign == DT_VCENTER) {
  144. int iPosY = (rc.bottom + rc.top) / 2;
  145. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
  146. iPosY += m_pVerticalScrollBar->GetScrollRange() / 2;
  147. iPosY -= m_pVerticalScrollBar->GetScrollPos();
  148. }
  149. RECT rcCtrl = { iPosX + rcPadding.left, iPosY - sz.cy/2, iPosX + sz.cx + rcPadding.left, iPosY + sz.cy - sz.cy/2 };
  150. pControl->SetPos(rcCtrl, false);
  151. }
  152. else if (iChildAlign == DT_BOTTOM) {
  153. int iPosY = rc.bottom;
  154. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
  155. iPosY += m_pVerticalScrollBar->GetScrollRange();
  156. iPosY -= m_pVerticalScrollBar->GetScrollPos();
  157. }
  158. RECT rcCtrl = { iPosX + rcPadding.left, iPosY - rcPadding.bottom - sz.cy, iPosX + sz.cx + rcPadding.left, iPosY - rcPadding.bottom };
  159. pControl->SetPos(rcCtrl, false);
  160. }
  161. else {
  162. int iPosY = rc.top;
  163. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
  164. iPosY -= m_pVerticalScrollBar->GetScrollPos();
  165. }
  166. RECT rcCtrl = { iPosX + rcPadding.left, iPosY + rcPadding.top, iPosX + sz.cx + rcPadding.left, iPosY + sz.cy + rcPadding.top };
  167. pControl->SetPos(rcCtrl, false);
  168. }
  169. iPosX += sz.cx + m_iChildPadding + rcPadding.left + rcPadding.right;
  170. cxNeeded += sz.cx + rcPadding.left + rcPadding.right;
  171. szRemaining.cx -= sz.cx + m_iChildPadding + rcPadding.right;
  172. }
  173. cxNeeded += (nEstimateNum - 1) * m_iChildPadding;
  174. // Process the scrollbar
  175. ProcessScrollBar(rc, cxNeeded, cyNeeded);
  176. }
  177. void CHorizontalLayoutUI::DoPostPaint(HDC hDC, const RECT& rcPaint)
  178. {
  179. if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode ) {
  180. RECT rcSeparator = GetThumbRect(true);
  181. CRenderEngine::DrawColor(hDC, rcSeparator, 0xAA000000);
  182. }
  183. }
  184. void CHorizontalLayoutUI::SetSepWidth(int iWidth)
  185. {
  186. m_iSepWidth = iWidth;
  187. }
  188. int CHorizontalLayoutUI::GetSepWidth() const
  189. {
  190. return m_iSepWidth;
  191. }
  192. void CHorizontalLayoutUI::SetSepImmMode(bool bImmediately)
  193. {
  194. if( m_bImmMode == bImmediately ) return;
  195. if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode && m_pManager != NULL ) {
  196. m_pManager->RemovePostPaint(this);
  197. }
  198. m_bImmMode = bImmediately;
  199. }
  200. bool CHorizontalLayoutUI::IsSepImmMode() const
  201. {
  202. return m_bImmMode;
  203. }
  204. void CHorizontalLayoutUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
  205. {
  206. if( _tcscmp(pstrName, _T("sepwidth")) == 0 ) SetSepWidth(_ttoi(pstrValue));
  207. else if( _tcscmp(pstrName, _T("sepimm")) == 0 ) SetSepImmMode(_tcscmp(pstrValue, _T("true")) == 0);
  208. else CContainerUI::SetAttribute(pstrName, pstrValue);
  209. }
  210. void CHorizontalLayoutUI::DoEvent(TEventUI& event)
  211. {
  212. if( m_iSepWidth != 0 ) {
  213. if( event.Type == UIEVENT_BUTTONDOWN && IsEnabled() )
  214. {
  215. RECT rcSeparator = GetThumbRect(false);
  216. if( ::PtInRect(&rcSeparator, event.ptMouse) ) {
  217. m_uButtonState |= UISTATE_CAPTURED;
  218. m_ptLastMouse = event.ptMouse;
  219. m_rcNewPos = m_rcItem;
  220. if( !m_bImmMode && m_pManager ) m_pManager->AddPostPaint(this);
  221. return;
  222. }
  223. }
  224. if( event.Type == UIEVENT_BUTTONUP )
  225. {
  226. if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
  227. m_uButtonState &= ~UISTATE_CAPTURED;
  228. m_rcItem = m_rcNewPos;
  229. if( !m_bImmMode && m_pManager ) m_pManager->RemovePostPaint(this);
  230. NeedParentUpdate();
  231. return;
  232. }
  233. }
  234. if( event.Type == UIEVENT_MOUSEMOVE )
  235. {
  236. if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
  237. LONG cx = event.ptMouse.x - m_ptLastMouse.x;
  238. m_ptLastMouse = event.ptMouse;
  239. RECT rc = m_rcNewPos;
  240. if( m_iSepWidth >= 0 ) {
  241. if( cx > 0 && event.ptMouse.x < m_rcNewPos.right - m_iSepWidth ) return;
  242. if( cx < 0 && event.ptMouse.x > m_rcNewPos.right ) return;
  243. rc.right += cx;
  244. if( rc.right - rc.left <= GetMinWidth() ) {
  245. if( m_rcNewPos.right - m_rcNewPos.left <= GetMinWidth() ) return;
  246. rc.right = rc.left + GetMinWidth();
  247. }
  248. if( rc.right - rc.left >= GetMaxWidth() ) {
  249. if( m_rcNewPos.right - m_rcNewPos.left >= GetMaxWidth() ) return;
  250. rc.right = rc.left + GetMaxWidth();
  251. }
  252. }
  253. else {
  254. if( cx > 0 && event.ptMouse.x < m_rcNewPos.left ) return;
  255. if( cx < 0 && event.ptMouse.x > m_rcNewPos.left - m_iSepWidth ) return;
  256. rc.left += cx;
  257. if( rc.right - rc.left <= GetMinWidth() ) {
  258. if( m_rcNewPos.right - m_rcNewPos.left <= GetMinWidth() ) return;
  259. rc.left = rc.right - GetMinWidth();
  260. }
  261. if( rc.right - rc.left >= GetMaxWidth() ) {
  262. if( m_rcNewPos.right - m_rcNewPos.left >= GetMaxWidth() ) return;
  263. rc.left = rc.right - GetMaxWidth();
  264. }
  265. }
  266. CDuiRect rcInvalidate = GetThumbRect(true);
  267. m_rcNewPos = rc;
  268. m_cxyFixed.cx = m_rcNewPos.right - m_rcNewPos.left;
  269. if( m_bImmMode ) {
  270. m_rcItem = m_rcNewPos;
  271. NeedParentUpdate();
  272. }
  273. else {
  274. rcInvalidate.Join(GetThumbRect(true));
  275. rcInvalidate.Join(GetThumbRect(false));
  276. if( m_pManager ) m_pManager->Invalidate(rcInvalidate);
  277. }
  278. return;
  279. }
  280. }
  281. if( event.Type == UIEVENT_SETCURSOR )
  282. {
  283. RECT rcSeparator = GetThumbRect(false);
  284. if( IsEnabled() && ::PtInRect(&rcSeparator, event.ptMouse) ) {
  285. ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE)));
  286. return;
  287. }
  288. }
  289. }
  290. CContainerUI::DoEvent(event);
  291. }
  292. RECT CHorizontalLayoutUI::GetThumbRect(bool bUseNew) const
  293. {
  294. if( (m_uButtonState & UISTATE_CAPTURED) != 0 && bUseNew) {
  295. if( m_iSepWidth >= 0 ) return CDuiRect(m_rcNewPos.right - m_iSepWidth, m_rcNewPos.top, m_rcNewPos.right, m_rcNewPos.bottom);
  296. else return CDuiRect(m_rcNewPos.left, m_rcNewPos.top, m_rcNewPos.left - m_iSepWidth, m_rcNewPos.bottom);
  297. }
  298. else {
  299. if( m_iSepWidth >= 0 ) return CDuiRect(m_rcItem.right - m_iSepWidth, m_rcItem.top, m_rcItem.right, m_rcItem.bottom);
  300. else return CDuiRect(m_rcItem.left, m_rcItem.top, m_rcItem.left - m_iSepWidth, m_rcItem.bottom);
  301. }
  302. }
  303. }