nsListBoxLayout.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsListBoxLayout.h"
  6. #include "nsListBoxBodyFrame.h"
  7. #include "nsBox.h"
  8. #include "nsBoxLayoutState.h"
  9. #include "nsIScrollableFrame.h"
  10. #include "nsIReflowCallback.h"
  11. #include "mozilla/dom/NameSpaceConstants.h"
  12. #include "nsGkAtoms.h"
  13. #include "nsContentUtils.h"
  14. nsListBoxLayout::nsListBoxLayout() : nsGridRowGroupLayout()
  15. {
  16. }
  17. ////////// nsBoxLayout //////////////
  18. nsSize
  19. nsListBoxLayout::GetXULPrefSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState)
  20. {
  21. nsSize pref = nsGridRowGroupLayout::GetXULPrefSize(aBox, aBoxLayoutState);
  22. nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
  23. if (frame) {
  24. nscoord rowheight = frame->GetRowHeightAppUnits();
  25. pref.height = frame->GetRowCount() * rowheight;
  26. // Pad the height.
  27. nscoord y = frame->GetAvailableHeight();
  28. if (pref.height > y && y > 0 && rowheight > 0) {
  29. nscoord m = (pref.height-y)%rowheight;
  30. nscoord remainder = m == 0 ? 0 : rowheight - m;
  31. pref.height += remainder;
  32. }
  33. if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
  34. nsGkAtoms::sizemode)) {
  35. nscoord width = frame->ComputeIntrinsicISize(aBoxLayoutState);
  36. if (width > pref.width)
  37. pref.width = width;
  38. }
  39. }
  40. return pref;
  41. }
  42. nsSize
  43. nsListBoxLayout::GetXULMinSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState)
  44. {
  45. nsSize minSize = nsGridRowGroupLayout::GetXULMinSize(aBox, aBoxLayoutState);
  46. nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
  47. if (frame) {
  48. nscoord rowheight = frame->GetRowHeightAppUnits();
  49. minSize.height = frame->GetRowCount() * rowheight;
  50. // Pad the height.
  51. nscoord y = frame->GetAvailableHeight();
  52. if (minSize.height > y && y > 0 && rowheight > 0) {
  53. nscoord m = (minSize.height-y)%rowheight;
  54. nscoord remainder = m == 0 ? 0 : rowheight - m;
  55. minSize.height += remainder;
  56. }
  57. if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
  58. nsGkAtoms::sizemode)) {
  59. nscoord width = frame->ComputeIntrinsicISize(aBoxLayoutState);
  60. if (width > minSize.width)
  61. minSize.width = width;
  62. }
  63. }
  64. return minSize;
  65. }
  66. nsSize
  67. nsListBoxLayout::GetXULMaxSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState)
  68. {
  69. nsSize maxSize = nsGridRowGroupLayout::GetXULMaxSize(aBox, aBoxLayoutState);
  70. nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
  71. if (frame) {
  72. nscoord rowheight = frame->GetRowHeightAppUnits();
  73. maxSize.height = frame->GetRowCount() * rowheight;
  74. // Pad the height.
  75. nscoord y = frame->GetAvailableHeight();
  76. if (maxSize.height > y && y > 0 && rowheight > 0) {
  77. nscoord m = (maxSize.height-y)%rowheight;
  78. nscoord remainder = m == 0 ? 0 : rowheight - m;
  79. maxSize.height += remainder;
  80. }
  81. }
  82. return maxSize;
  83. }
  84. NS_IMETHODIMP
  85. nsListBoxLayout::XULLayout(nsIFrame* aBox, nsBoxLayoutState& aState)
  86. {
  87. return LayoutInternal(aBox, aState);
  88. }
  89. /////////// nsListBoxLayout /////////////////////////
  90. /**
  91. * Called to layout our our children. Does no frame construction
  92. */
  93. NS_IMETHODIMP
  94. nsListBoxLayout::LayoutInternal(nsIFrame* aBox, nsBoxLayoutState& aState)
  95. {
  96. int32_t redrawStart = -1;
  97. // Get the start y position.
  98. nsListBoxBodyFrame* body = static_cast<nsListBoxBodyFrame*>(aBox);
  99. if (!body) {
  100. NS_ERROR("Frame encountered that isn't a listboxbody!");
  101. return NS_ERROR_FAILURE;
  102. }
  103. nsMargin margin;
  104. // Get our client rect.
  105. nsRect clientRect;
  106. aBox->GetXULClientRect(clientRect);
  107. // Get the starting y position and the remaining available
  108. // height.
  109. nscoord availableHeight = body->GetAvailableHeight();
  110. nscoord yOffset = body->GetYPosition();
  111. if (availableHeight <= 0) {
  112. bool fixed = (body->GetFixedRowSize() != -1);
  113. if (fixed)
  114. availableHeight = 10;
  115. else
  116. return NS_OK;
  117. }
  118. // run through all our currently created children
  119. nsIFrame* box = nsBox::GetChildXULBox(body);
  120. // if the reason is resize or initial we must relayout.
  121. nscoord rowHeight = body->GetRowHeightAppUnits();
  122. while (box) {
  123. // If this box is dirty or if it has dirty children, we
  124. // call layout on it.
  125. nsRect childRect(box->GetRect());
  126. box->GetXULMargin(margin);
  127. // relayout if we must or we are dirty or some of our children are dirty
  128. // or the client area is wider than us
  129. // XXXldb There should probably be a resize check here too!
  130. if (NS_SUBTREE_DIRTY(box) || childRect.width < clientRect.width) {
  131. childRect.x = 0;
  132. childRect.y = yOffset;
  133. childRect.width = clientRect.width;
  134. nsSize size = box->GetXULPrefSize(aState);
  135. body->SetRowHeight(size.height);
  136. childRect.height = rowHeight;
  137. childRect.Deflate(margin);
  138. box->SetXULBounds(aState, childRect);
  139. box->XULLayout(aState);
  140. } else {
  141. // if the child did not need to be relayed out. Then its easy.
  142. // Place the child by just grabbing its rect and adjusting the y.
  143. int32_t newPos = yOffset+margin.top;
  144. // are we pushing down or pulling up any rows?
  145. // Then we may have to redraw everything below the moved
  146. // rows.
  147. if (redrawStart == -1 && childRect.y != newPos)
  148. redrawStart = newPos;
  149. childRect.y = newPos;
  150. box->SetXULBounds(aState, childRect);
  151. }
  152. // Ok now the available size gets smaller and we move the
  153. // starting position of the next child down some.
  154. nscoord size = childRect.height + margin.top + margin.bottom;
  155. yOffset += size;
  156. availableHeight -= size;
  157. box = nsBox::GetNextXULBox(box);
  158. }
  159. // We have enough available height left to add some more rows
  160. // Since we can't do this during layout, we post a callback
  161. // that will be processed after the reflow completes.
  162. body->PostReflowCallback();
  163. // if rows were pushed down or pulled up because some rows were added
  164. // before them then redraw everything under the inserted rows. The inserted
  165. // rows will automatically be redrawn because the were marked dirty on insertion.
  166. if (redrawStart > -1) {
  167. aBox->XULRedraw(aState);
  168. }
  169. return NS_OK;
  170. }
  171. // Creation Routines ///////////////////////////////////////////////////////////////////////
  172. already_AddRefed<nsBoxLayout> NS_NewListBoxLayout()
  173. {
  174. RefPtr<nsBoxLayout> layout = new nsListBoxLayout();
  175. return layout.forget();
  176. }