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