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