1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 //
8 // Eric Vaughan
9 // Netscape Communications
10 //
11 // See documentation in associated header file
12 //
13
14 #include "nsGridRowLeafLayout.h"
15 #include "nsGridRowGroupLayout.h"
16 #include "nsGridRow.h"
17 #include "nsBoxLayoutState.h"
18 #include "nsIScrollableFrame.h"
19 #include "nsBoxFrame.h"
20 #include "nsGridLayout2.h"
21 #include <algorithm>
22
NS_NewGridRowLeafLayout()23 already_AddRefed<nsBoxLayout> NS_NewGridRowLeafLayout() {
24 RefPtr<nsBoxLayout> layout = new nsGridRowLeafLayout();
25 return layout.forget();
26 }
27
nsGridRowLeafLayout()28 nsGridRowLeafLayout::nsGridRowLeafLayout() : nsGridRowLayout() {}
29
30 nsGridRowLeafLayout::~nsGridRowLeafLayout() = default;
31
GetXULPrefSize(nsIFrame * aBox,nsBoxLayoutState & aState)32 nsSize nsGridRowLeafLayout::GetXULPrefSize(nsIFrame* aBox,
33 nsBoxLayoutState& aState) {
34 int32_t index = 0;
35 nsGrid* grid = GetGrid(aBox, &index);
36 bool isHorizontal = IsXULHorizontal(aBox);
37
38 // If we are not in a grid. Then we just work like a box. But if we are in a
39 // grid ask the grid for our size.
40 if (!grid) {
41 return nsGridRowLayout::GetXULPrefSize(aBox, aState);
42 } else {
43 return grid->GetPrefRowSize(aState, index, isHorizontal);
44 // AddXULBorderAndPadding(aBox, pref);
45 }
46 }
47
GetXULMinSize(nsIFrame * aBox,nsBoxLayoutState & aState)48 nsSize nsGridRowLeafLayout::GetXULMinSize(nsIFrame* aBox,
49 nsBoxLayoutState& aState) {
50 int32_t index = 0;
51 nsGrid* grid = GetGrid(aBox, &index);
52 bool isHorizontal = IsXULHorizontal(aBox);
53
54 if (!grid)
55 return nsGridRowLayout::GetXULMinSize(aBox, aState);
56 else {
57 nsSize minSize = grid->GetMinRowSize(aState, index, isHorizontal);
58 AddXULBorderAndPadding(aBox, minSize);
59 return minSize;
60 }
61 }
62
GetXULMaxSize(nsIFrame * aBox,nsBoxLayoutState & aState)63 nsSize nsGridRowLeafLayout::GetXULMaxSize(nsIFrame* aBox,
64 nsBoxLayoutState& aState) {
65 int32_t index = 0;
66 nsGrid* grid = GetGrid(aBox, &index);
67 bool isHorizontal = IsXULHorizontal(aBox);
68
69 if (!grid)
70 return nsGridRowLayout::GetXULMaxSize(aBox, aState);
71 else {
72 nsSize maxSize;
73 maxSize = grid->GetMaxRowSize(aState, index, isHorizontal);
74 AddXULBorderAndPadding(aBox, maxSize);
75 return maxSize;
76 }
77 }
78
79 /** If a child is added or removed or changes size
80 */
ChildAddedOrRemoved(nsIFrame * aBox,nsBoxLayoutState & aState)81 void nsGridRowLeafLayout::ChildAddedOrRemoved(nsIFrame* aBox,
82 nsBoxLayoutState& aState) {
83 int32_t index = 0;
84 nsGrid* grid = GetGrid(aBox, &index);
85 bool isHorizontal = IsXULHorizontal(aBox);
86
87 if (grid) grid->CellAddedOrRemoved(aState, index, isHorizontal);
88 }
89
PopulateBoxSizes(nsIFrame * aBox,nsBoxLayoutState & aState,nsBoxSize * & aBoxSizes,nscoord & aMinSize,nscoord & aMaxSize,int32_t & aFlexes)90 void nsGridRowLeafLayout::PopulateBoxSizes(nsIFrame* aBox,
91 nsBoxLayoutState& aState,
92 nsBoxSize*& aBoxSizes,
93 nscoord& aMinSize, nscoord& aMaxSize,
94 int32_t& aFlexes) {
95 int32_t index = 0;
96 nsGrid* grid = GetGrid(aBox, &index);
97 bool isHorizontal = IsXULHorizontal(aBox);
98
99 // Our base class SprocketLayout is giving us a chance to change the box sizes
100 // before layout If we are a row lets change the sizes to match our columns.
101 // If we are a column then do the opposite and make them match or rows.
102 if (grid) {
103 nsGridRow* column;
104 int32_t count = grid->GetColumnCount(isHorizontal);
105 nsBoxSize* start = nullptr;
106 nsBoxSize* last = nullptr;
107 nsBoxSize* current = nullptr;
108 nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
109 for (int i = 0; i < count; i++) {
110 column = grid->GetColumnAt(i, isHorizontal);
111
112 // make sure the value was computed before we use it.
113 // !isHorizontal is passed in to invert the behavior of these methods.
114 nscoord pref = grid->GetPrefRowHeight(
115 aState, i, !isHorizontal); // GetPrefColumnWidth
116 nscoord min =
117 grid->GetMinRowHeight(aState, i, !isHorizontal); // GetMinColumnWidth
118 nscoord max =
119 grid->GetMaxRowHeight(aState, i, !isHorizontal); // GetMaxColumnWidth
120 nscoord flex = grid->GetRowFlex(i, !isHorizontal); // GetColumnFlex
121 nscoord left = 0;
122 nscoord right = 0;
123 grid->GetRowOffsets(i, left, right, !isHorizontal); // GetColumnOffsets
124 nsIFrame* box = column->GetBox();
125 bool collapsed = false;
126 nscoord topMargin = column->mTopMargin;
127 nscoord bottomMargin = column->mBottomMargin;
128
129 if (box) collapsed = box->IsXULCollapsed();
130
131 pref = pref - (left + right);
132 if (pref < 0) pref = 0;
133
134 // if this is the first or last column. Take into account that
135 // our row could have a border that could affect our left or right
136 // padding from our columns. If the row has padding subtract it.
137 // would should always be able to garentee that our margin is smaller
138 // or equal to our left or right
139 int32_t firstIndex = 0;
140 int32_t lastIndex = 0;
141 nsGridRow* firstRow = nullptr;
142 nsGridRow* lastRow = nullptr;
143 grid->GetFirstAndLastRow(firstIndex, lastIndex, firstRow, lastRow,
144 !isHorizontal);
145
146 if (i == firstIndex || i == lastIndex) {
147 nsMargin offset = GetTotalMargin(aBox, isHorizontal);
148
149 nsMargin border(0, 0, 0, 0);
150 // can't call GetBorderPadding we will get into recursion
151 aBox->GetXULBorder(border);
152 offset += border;
153 aBox->GetXULPadding(border);
154 offset += border;
155
156 // subtract from out left and right
157 if (i == firstIndex) {
158 if (isHorizontal)
159 left -= offset.left;
160 else
161 left -= offset.top;
162 }
163
164 if (i == lastIndex) {
165 if (isHorizontal)
166 right -= offset.right;
167 else
168 right -= offset.bottom;
169 }
170 }
171
172 // initialize the box size here
173 max = std::max(min, max);
174 pref = nsIFrame::XULBoundsCheck(min, pref, max);
175
176 current = new (aState) nsBoxSize();
177 current->pref = pref;
178 current->min = min;
179 current->max = max;
180 current->flex = flex;
181 current->bogus = column->mIsBogus;
182 current->left = left + topMargin;
183 current->right = right + bottomMargin;
184 current->collapsed = collapsed;
185
186 if (!start) {
187 start = current;
188 last = start;
189 } else {
190 last->next = current;
191 last = current;
192 }
193
194 if (child && !column->mIsBogus) {
195 child = nsIFrame::GetNextXULBox(child);
196 }
197 }
198 aBoxSizes = start;
199 }
200
201 nsSprocketLayout::PopulateBoxSizes(aBox, aState, aBoxSizes, aMinSize,
202 aMaxSize, aFlexes);
203 }
204
ComputeChildSizes(nsIFrame * aBox,nsBoxLayoutState & aState,nscoord & aGivenSize,nsBoxSize * aBoxSizes,nsComputedBoxSize * & aComputedBoxSizes)205 void nsGridRowLeafLayout::ComputeChildSizes(
206 nsIFrame* aBox, nsBoxLayoutState& aState, nscoord& aGivenSize,
207 nsBoxSize* aBoxSizes, nsComputedBoxSize*& aComputedBoxSizes) {
208 // see if we are in a scrollable frame. If we are then there could be
209 // scrollbars present if so we need to subtract them out to make sure our
210 // columns line up.
211 if (aBox) {
212 bool isHorizontal = aBox->IsXULHorizontal();
213
214 // go up the parent chain looking for scrollframes
215 nscoord diff = 0;
216 nsIFrame* parentBox;
217 (void)GetParentGridPart(aBox, &parentBox);
218 while (parentBox) {
219 nsIFrame* scrollbox = nsGrid::GetScrollBox(parentBox);
220 nsIScrollableFrame* scrollable = do_QueryFrame(scrollbox);
221 if (scrollable) {
222 // Don't call GetActualScrollbarSizes here because it's not safe
223 // to call that while we're reflowing the contents of the scrollframe,
224 // which we are here.
225 nsMargin scrollbarSizes = scrollable->GetDesiredScrollbarSizes(&aState);
226 uint32_t visible = scrollable->GetScrollbarVisibility();
227
228 if (isHorizontal && (visible & nsIScrollableFrame::VERTICAL)) {
229 diff += scrollbarSizes.left + scrollbarSizes.right;
230 } else if (!isHorizontal &&
231 (visible & nsIScrollableFrame::HORIZONTAL)) {
232 diff += scrollbarSizes.top + scrollbarSizes.bottom;
233 }
234 }
235
236 (void)GetParentGridPart(parentBox, &parentBox);
237 }
238
239 if (diff > 0) {
240 aGivenSize += diff;
241
242 nsSprocketLayout::ComputeChildSizes(aBox, aState, aGivenSize, aBoxSizes,
243 aComputedBoxSizes);
244
245 aGivenSize -= diff;
246
247 nsComputedBoxSize* s = aComputedBoxSizes;
248 nsComputedBoxSize* last = aComputedBoxSizes;
249 while (s) {
250 last = s;
251 s = s->next;
252 }
253
254 if (last) last->size -= diff;
255
256 return;
257 }
258 }
259
260 nsSprocketLayout::ComputeChildSizes(aBox, aState, aGivenSize, aBoxSizes,
261 aComputedBoxSizes);
262 }
263
264 NS_IMETHODIMP
XULLayout(nsIFrame * aBox,nsBoxLayoutState & aBoxLayoutState)265 nsGridRowLeafLayout::XULLayout(nsIFrame* aBox,
266 nsBoxLayoutState& aBoxLayoutState) {
267 return nsGridRowLayout::XULLayout(aBox, aBoxLayoutState);
268 }
269
DirtyRows(nsIFrame * aBox,nsBoxLayoutState & aState)270 void nsGridRowLeafLayout::DirtyRows(nsIFrame* aBox, nsBoxLayoutState& aState) {
271 if (aBox) {
272 // mark us dirty
273 // XXXldb We probably don't want to walk up the ancestor chain
274 // calling MarkIntrinsicISizesDirty for every row.
275 aState.PresShell()->FrameNeedsReflow(
276 aBox, mozilla::IntrinsicDirty::TreeChange, NS_FRAME_IS_DIRTY);
277 }
278 }
279
CountRowsColumns(nsIFrame * aBox,int32_t & aRowCount,int32_t & aComputedColumnCount)280 void nsGridRowLeafLayout::CountRowsColumns(nsIFrame* aBox, int32_t& aRowCount,
281 int32_t& aComputedColumnCount) {
282 if (aBox) {
283 nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
284
285 // count the children
286 int32_t columnCount = 0;
287 while (child) {
288 child = nsIFrame::GetNextXULBox(child);
289 columnCount++;
290 }
291
292 // if our count is greater than the current column count
293 if (columnCount > aComputedColumnCount) aComputedColumnCount = columnCount;
294
295 aRowCount++;
296 }
297 }
298
BuildRows(nsIFrame * aBox,nsGridRow * aRows)299 int32_t nsGridRowLeafLayout::BuildRows(nsIFrame* aBox, nsGridRow* aRows) {
300 if (aBox) {
301 aRows[0].Init(aBox, false);
302 return 1;
303 }
304
305 return 0;
306 }
307