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