layout/xul/grid/nsGrid.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:b46acd0affc5
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
6 //
7 // Eric Vaughan
8 // Netscape Communications
9 //
10 // See documentation in associated header file
11 //
12
13 #include "nsGrid.h"
14 #include "nsGridRowGroupLayout.h"
15 #include "nsBox.h"
16 #include "nsIScrollableFrame.h"
17 #include "nsSprocketLayout.h"
18 #include "nsGridLayout2.h"
19 #include "nsGridRow.h"
20 #include "nsGridCell.h"
21 #include "nsHTMLReflowState.h"
22
23 /*
24 The grid control expands the idea of boxes from 1 dimension to 2 dimensions.
25 It works by allowing the XUL to define a collection of rows and columns and then
26 stacking them on top of each other. Here is and example.
27
28 Example 1:
29
30 <grid>
31 <columns>
32 <column/>
33 <column/>
34 </columns>
35
36 <rows>
37 <row/>
38 <row/>
39 </rows>
40 </grid>
41
42 example 2:
43
44 <grid>
45 <columns>
46 <column flex="1"/>
47 <column flex="1"/>
48 </columns>
49
50 <rows>
51 <row>
52 <text value="hello"/>
53 <text value="there"/>
54 </row>
55 </rows>
56 </grid>
57
58 example 3:
59
60 <grid>
61
62 <rows>
63 <row>
64 <text value="hello"/>
65 <text value="there"/>
66 </row>
67 </rows>
68
69 <columns>
70 <column>
71 <text value="Hey I'm in the column and I'm on top!"/>
72 </column>
73 <column/>
74 </columns>
75
76 </grid>
77
78 Usually the columns are first and the rows are second, so the rows will be drawn on top of the columns.
79 You can reverse this by defining the rows first.
80 Other tags are then placed in the <row> or <column> tags causing the grid to accommodate everyone.
81 It does this by creating 3 things: A cellmap, a row list, and a column list. The cellmap is a 2
82 dimensional array of nsGridCells. Each cell contains 2 boxes. One cell from the column list
83 and one from the row list. When a cell is asked for its size it returns that smallest size it can
84 be to accommodate the 2 cells. Row lists and Column lists use the same data structure: nsGridRow.
85 Essentially a row and column are the same except a row goes alone the x axis and a column the y.
86 To make things easier and save code everything is written in terms of the x dimension. A flag is
87 passed in called "isHorizontal" that can flip the calculations to the y axis.
88
89 Usually the number of cells in a row match the number of columns, but not always.
90 It is possible to define 5 columns for a grid but have 10 cells in one of the rows.
91 In this case 5 extra columns will be added to the column list to handle the situation.
92 These are called extraColumns/Rows.
93 */
94
95 nsGrid::nsGrid():mBox(nullptr),
96 mRows(nullptr),
97 mColumns(nullptr),
98 mRowsBox(nullptr),
99 mColumnsBox(nullptr),
100 mNeedsRebuild(true),
101 mRowCount(0),
102 mColumnCount(0),
103 mExtraRowCount(0),
104 mExtraColumnCount(0),
105 mCellMap(nullptr),
106 mMarkingDirty(false)
107 {
108 MOZ_COUNT_CTOR(nsGrid);
109 }
110
111 nsGrid::~nsGrid()
112 {
113 FreeMap();
114 MOZ_COUNT_DTOR(nsGrid);
115 }
116
117 /*
118 * This is called whenever something major happens in the grid. And example
119 * might be when many cells or row are added. It sets a flag signaling that
120 * all the grids caches information should be recalculated.
121 */
122 void
123 nsGrid::NeedsRebuild(nsBoxLayoutState& aState)
124 {
125 if (mNeedsRebuild)
126 return;
127
128 // iterate through columns and rows and dirty them
129 mNeedsRebuild = true;
130
131 // find the new row and column box. They could have
132 // been changed.
133 mRowsBox = nullptr;
134 mColumnsBox = nullptr;
135 FindRowsAndColumns(&mRowsBox, &mColumnsBox);
136
137 // tell all the rows and columns they are dirty
138 DirtyRows(mRowsBox, aState);
139 DirtyRows(mColumnsBox, aState);
140 }
141
142
143
144 /**
145 * If we are marked for rebuild. Then build everything
146 */
147 void
148 nsGrid::RebuildIfNeeded()
149 {
150 if (!mNeedsRebuild)
151 return;
152
153 mNeedsRebuild = false;
154
155 // find the row and columns frames
156 FindRowsAndColumns(&mRowsBox, &mColumnsBox);
157
158 // count the rows and columns
159 int32_t computedRowCount = 0;
160 int32_t computedColumnCount = 0;
161 int32_t rowCount = 0;
162 int32_t columnCount = 0;
163
164 CountRowsColumns(mRowsBox, rowCount, computedColumnCount);
165 CountRowsColumns(mColumnsBox, columnCount, computedRowCount);
166
167 // computedRowCount are the actual number of rows as determined by the
168 // columns children.
169 // computedColumnCount are the number of columns as determined by the number
170 // of rows children.
171 // We can use this information to see how many extra columns or rows we need.
172 // This can happen if there are are more children in a row that number of columns
173 // defined. Example:
174 //
175 // <columns>
176 // <column/>
177 // </columns>
178 //
179 // <rows>
180 // <row>
181 // <button/><button/>
182 // </row>
183 // </rows>
184 //
185 // computedColumnCount = 2 // for the 2 buttons in the row tag
186 // computedRowCount = 0 // there is nothing in the column tag
187 // mColumnCount = 1 // one column defined
188 // mRowCount = 1 // one row defined
189 //
190 // So in this case we need to make 1 extra column.
191 //
192
193 // Make sure to update mExtraColumnCount no matter what, since it might
194 // happen that we now have as many columns as are defined, and we wouldn't
195 // want to have a positive mExtraColumnCount hanging about in that case!
196 mExtraColumnCount = computedColumnCount - columnCount;
197 if (computedColumnCount > columnCount) {
198 columnCount = computedColumnCount;
199 }
200
201 // Same for rows.
202 mExtraRowCount = computedRowCount - rowCount;
203 if (computedRowCount > rowCount) {
204 rowCount = computedRowCount;
205 }
206
207 // build and poplulate row and columns arrays
208 BuildRows(mRowsBox, rowCount, &mRows, true);
209 BuildRows(mColumnsBox, columnCount, &mColumns, false);
210
211 // build and populate the cell map
212 mCellMap = BuildCellMap(rowCount, columnCount);
213
214 mRowCount = rowCount;
215 mColumnCount = columnCount;
216
217 // populate the cell map from column and row children
218 PopulateCellMap(mRows, mColumns, mRowCount, mColumnCount, true);
219 PopulateCellMap(mColumns, mRows, mColumnCount, mRowCount, false);
220 }
221
222 void
223 nsGrid::FreeMap()
224 {
225 if (mRows)
226 delete[] mRows;
227
228 if (mColumns)
229 delete[] mColumns;
230
231 if (mCellMap)
232 delete[] mCellMap;
233
234 mRows = nullptr;
235 mColumns = nullptr;
236 mCellMap = nullptr;
237 mColumnCount = 0;
238 mRowCount = 0;
239 mExtraColumnCount = 0;
240 mExtraRowCount = 0;
241 mRowsBox = nullptr;
242 mColumnsBox = nullptr;
243 }
244
245 /**
246 * finds the first <rows> and <columns> tags in the <grid> tag
247 */
248 void
249 nsGrid::FindRowsAndColumns(nsIFrame** aRows, nsIFrame** aColumns)
250 {
251 *aRows = nullptr;
252 *aColumns = nullptr;
253
254 // find the boxes that contain our rows and columns
255 nsIFrame* child = nullptr;
256 // if we have <grid></grid> then mBox will be null (bug 125689)
257 if (mBox)
258 child = mBox->GetChildBox();
259
260 while(child)
261 {
262 nsIFrame* oldBox = child;
263 nsIScrollableFrame *scrollFrame = do_QueryFrame(child);
264 if (scrollFrame) {
265 nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
266 NS_ASSERTION(scrolledFrame,"Error no scroll frame!!");
267 child = do_QueryFrame(scrolledFrame);
268 }
269
270 nsCOMPtr<nsIGridPart> monument = GetPartFromBox(child);
271 if (monument)
272 {
273 nsGridRowGroupLayout* rowGroup = monument->CastToRowGroupLayout();
274 if (rowGroup) {
275 bool isHorizontal = !nsSprocketLayout::IsHorizontal(child);
276 if (isHorizontal)
277 *aRows = child;
278 else
279 *aColumns = child;
280
281 if (*aRows && *aColumns)
282 return;
283 }
284 }
285
286 if (scrollFrame) {
287 child = oldBox;
288 }
289
290 child = child->GetNextBox();
291 }
292 }
293
294 /**
295 * Count the number of rows and columns in the given box. aRowCount well become the actual number
296 * rows defined in the xul. aComputedColumnCount will become the number of columns by counting the number
297 * of cells in each row.
298 */
299 void
300 nsGrid::CountRowsColumns(nsIFrame* aRowBox, int32_t& aRowCount, int32_t& aComputedColumnCount)
301 {
302 aRowCount = 0;
303 aComputedColumnCount = 0;
304 // get the rowboxes layout manager. Then ask it to do the work for us
305 if (aRowBox) {
306 nsCOMPtr<nsIGridPart> monument = GetPartFromBox(aRowBox);
307 if (monument)
308 monument->CountRowsColumns(aRowBox, aRowCount, aComputedColumnCount);
309 }
310 }
311
312
313 /**
314 * Given the number of rows create nsGridRow objects for them and full them out.
315 */
316 void
317 nsGrid::BuildRows(nsIFrame* aBox, int32_t aRowCount, nsGridRow** aRows, bool aIsHorizontal)
318 {
319 // if no rows then return null
320 if (aRowCount == 0) {
321
322 // make sure we free up the memory.
323 if (*aRows)
324 delete[] (*aRows);
325
326 *aRows = nullptr;
327 return;
328 }
329
330 // create the array
331 nsGridRow* row;
332
333 // only create new rows if we have to. Reuse old rows.
334 if (aIsHorizontal)
335 {
336 if (aRowCount > mRowCount) {
337 delete[] mRows;
338 row = new nsGridRow[aRowCount];
339 } else {
340 for (int32_t i=0; i < mRowCount; i++)
341 mRows[i].Init(nullptr, false);
342
343 row = mRows;
344 }
345 } else {
346 if (aRowCount > mColumnCount) {
347 delete[] mColumns;
348 row = new nsGridRow[aRowCount];
349 } else {
350 for (int32_t i=0; i < mColumnCount; i++)
351 mColumns[i].Init(nullptr, false);
352
353 row = mColumns;
354 }
355 }
356
357 // populate it if we can. If not it will contain only dynamic columns
358 if (aBox)
359 {
360 nsCOMPtr<nsIGridPart> monument = GetPartFromBox(aBox);
361 if (monument) {
362 monument->BuildRows(aBox, row);
363 }
364 }
365
366 *aRows = row;
367 }
368
369
370 /**
371 * Given the number of rows and columns. Build a cellmap
372 */
373 nsGridCell*
374 nsGrid::BuildCellMap(int32_t aRows, int32_t aColumns)
375 {
376 int32_t size = aRows*aColumns;
377 int32_t oldsize = mRowCount*mColumnCount;
378 if (size == 0) {
379 delete[] mCellMap;
380 }
381 else {
382 if (size > oldsize) {
383 delete[] mCellMap;
384 return new nsGridCell[size];
385 } else {
386 // clear out cellmap
387 for (int32_t i=0; i < oldsize; i++)
388 {
389 mCellMap[i].SetBoxInRow(nullptr);
390 mCellMap[i].SetBoxInColumn(nullptr);
391 }
392 return mCellMap;
393 }
394 }
395 return nullptr;
396 }
397
398 /**
399 * Run through all the cells in the rows and columns and populate then with 2 cells. One from the row and one
400 * from the column
401 */
402 void
403 nsGrid::PopulateCellMap(nsGridRow* aRows, nsGridRow* aColumns, int32_t aRowCount, int32_t aColumnCount, bool aIsHorizontal)
404 {
405 if (!aRows)
406 return;
407
408 // look through the columns
409 int32_t j = 0;
410
411 for(int32_t i=0; i < aRowCount; i++)
412 {
413 nsIFrame* child = nullptr;
414 nsGridRow* row = &aRows[i];
415
416 // skip bogus rows. They have no cells
417 if (row->mIsBogus)
418 continue;
419
420 child = row->mBox;
421 if (child) {
422 child = child->GetChildBox();
423
424 j = 0;
425
426 while(child && j < aColumnCount)
427 {
428 // skip bogus column. They have no cells
429 nsGridRow* column = &aColumns[j];
430 if (column->mIsBogus)
431 {
432 j++;
433 continue;
434 }
435
436 if (aIsHorizontal)
437 GetCellAt(j,i)->SetBoxInRow(child);
438 else
439 GetCellAt(i,j)->SetBoxInColumn(child);
440
441 child = child->GetNextBox();
442
443 j++;
444 }
445 }
446 }
447 }
448
449 /**
450 * Run through the rows in the given box and mark them dirty so they
451 * will get recalculated and get a layout.
452 */
453 void
454 nsGrid::DirtyRows(nsIFrame* aRowBox, nsBoxLayoutState& aState)
455 {
456 // make sure we prevent others from dirtying things.
457 mMarkingDirty = true;
458
459 // if the box is a grid part have it recursively hand it.
460 if (aRowBox) {
461 nsCOMPtr<nsIGridPart> part = GetPartFromBox(aRowBox);
462 if (part)
463 part->DirtyRows(aRowBox, aState);
464 }
465
466 mMarkingDirty = false;
467 }
468
469 nsGridRow*
470 nsGrid::GetColumnAt(int32_t aIndex, bool aIsHorizontal)
471 {
472 return GetRowAt(aIndex, !aIsHorizontal);
473 }
474
475 nsGridRow*
476 nsGrid::GetRowAt(int32_t aIndex, bool aIsHorizontal)
477 {
478 RebuildIfNeeded();
479
480 if (aIsHorizontal) {
481 NS_ASSERTION(aIndex < mRowCount && aIndex >= 0, "Index out of range");
482 return &mRows[aIndex];
483 } else {
484 NS_ASSERTION(aIndex < mColumnCount && aIndex >= 0, "Index out of range");
485 return &mColumns[aIndex];
486 }
487 }
488
489 nsGridCell*
490 nsGrid::GetCellAt(int32_t aX, int32_t aY)
491 {
492 RebuildIfNeeded();
493
494 NS_ASSERTION(aY < mRowCount && aY >= 0, "Index out of range");
495 NS_ASSERTION(aX < mColumnCount && aX >= 0, "Index out of range");
496 return &mCellMap[aY*mColumnCount+aX];
497 }
498
499 int32_t
500 nsGrid::GetExtraColumnCount(bool aIsHorizontal)
501 {
502 return GetExtraRowCount(!aIsHorizontal);
503 }
504
505 int32_t
506 nsGrid::GetExtraRowCount(bool aIsHorizontal)
507 {
508 RebuildIfNeeded();
509
510 if (aIsHorizontal)
511 return mExtraRowCount;
512 else
513 return mExtraColumnCount;
514 }
515
516
517 /**
518 * These methods return the preferred, min, max sizes for a given row index.
519 * aIsHorizontal if aIsHorizontal is true. If you pass false you will get the inverse.
520 * As if you called GetPrefColumnSize(aState, index, aPref)
521 */
522 nsSize
523 nsGrid::GetPrefRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
524 {
525 nsSize size(0,0);
526 if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
527 return size;
528
529 nscoord height = GetPrefRowHeight(aState, aRowIndex, aIsHorizontal);
530 SetLargestSize(size, height, aIsHorizontal);
531
532 return size;
533 }
534
535 nsSize
536 nsGrid::GetMinRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
537 {
538 nsSize size(0,0);
539 if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
540 return size;
541
542 nscoord height = GetMinRowHeight(aState, aRowIndex, aIsHorizontal);
543 SetLargestSize(size, height, aIsHorizontal);
544
545 return size;
546 }
547
548 nsSize
549 nsGrid::GetMaxRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
550 {
551 nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
552 if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
553 return size;
554
555 nscoord height = GetMaxRowHeight(aState, aRowIndex, aIsHorizontal);
556 SetSmallestSize(size, height, aIsHorizontal);
557
558 return size;
559 }
560
561 // static
562 nsIGridPart*
563 nsGrid::GetPartFromBox(nsIFrame* aBox)
564 {
565 if (!aBox)
566 return nullptr;
567
568 nsBoxLayout* layout = aBox->GetLayoutManager();
569 return layout ? layout->AsGridPart() : nullptr;
570 }
571
572 nsMargin
573 nsGrid::GetBoxTotalMargin(nsIFrame* aBox, bool aIsHorizontal)
574 {
575 nsMargin margin(0,0,0,0);
576 // walk the boxes parent chain getting the border/padding/margin of our parent rows
577
578 // first get the layour manager
579 nsIGridPart* part = GetPartFromBox(aBox);
580 if (part)
581 margin = part->GetTotalMargin(aBox, aIsHorizontal);
582
583 return margin;
584 }
585
586 /**
587 * The first and last rows can be affected by <rows> tags with borders or margin
588 * gets first and last rows and their indexes.
589 * If it fails because there are no rows then:
590 * FirstRow is nullptr
591 * LastRow is nullptr
592 * aFirstIndex = -1
593 * aLastIndex = -1
594 */
595 void
596 nsGrid::GetFirstAndLastRow(nsBoxLayoutState& aState,
597 int32_t& aFirstIndex,
598 int32_t& aLastIndex,
599 nsGridRow*& aFirstRow,
600 nsGridRow*& aLastRow,
601 bool aIsHorizontal)
602 {
603 aFirstRow = nullptr;
604 aLastRow = nullptr;
605 aFirstIndex = -1;
606 aLastIndex = -1;
607
608 int32_t count = GetRowCount(aIsHorizontal);
609
610 if (count == 0)
611 return;
612
613
614 // We could have collapsed columns either before or after our index.
615 // they should not count. So if we are the 5th row and the first 4 are
616 // collaped we become the first row. Or if we are the 9th row and
617 // 10 up to the last row are collapsed we then become the last.
618
619 // see if we are first
620 int32_t i;
621 for (i=0; i < count; i++)
622 {
623 nsGridRow* row = GetRowAt(i,aIsHorizontal);
624 if (!row->IsCollapsed()) {
625 aFirstIndex = i;
626 aFirstRow = row;
627 break;
628 }
629 }
630
631 // see if we are last
632 for (i=count-1; i >= 0; i--)
633 {
634 nsGridRow* row = GetRowAt(i,aIsHorizontal);
635 if (!row->IsCollapsed()) {
636 aLastIndex = i;
637 aLastRow = row;
638 break;
639 }
640
641 }
642 }
643
644 /**
645 * A row can have a top and bottom offset. Usually this is just the top and bottom border/padding.
646 * However if the row is the first or last it could be affected by the fact a column or columns could
647 * have a top or bottom margin.
648 */
649 void
650 nsGrid::GetRowOffsets(nsBoxLayoutState& aState, int32_t aIndex, nscoord& aTop, nscoord& aBottom, bool aIsHorizontal)
651 {
652
653 RebuildIfNeeded();
654
655 nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
656
657 if (row->IsOffsetSet())
658 {
659 aTop = row->mTop;
660 aBottom = row->mBottom;
661 return;
662 }
663
664 // first get the rows top and bottom border and padding
665 nsIFrame* box = row->GetBox();
666
667 // add up all the padding
668 nsMargin margin(0,0,0,0);
669 nsMargin border(0,0,0,0);
670 nsMargin padding(0,0,0,0);
671 nsMargin totalBorderPadding(0,0,0,0);
672 nsMargin totalMargin(0,0,0,0);
673
674 // if there is a box and it's not bogus take its
675 // borders padding into account
676 if (box && !row->mIsBogus)
677 {
678 if (!box->IsCollapsed())
679 {
680 // get real border and padding. GetBorderAndPadding
681 // is redefined on nsGridRowLeafFrame. If we called it here
682 // we would be in finite recurson.
683 box->GetBorder(border);
684 box->GetPadding(padding);
685
686 totalBorderPadding += border;
687 totalBorderPadding += padding;
688 }
689
690 // if we are the first or last row
691 // take into account <rows> tags around us
692 // that could have borders or margins.
693 // fortunately they only affect the first
694 // and last row inside the <rows> tag
695
696 totalMargin = GetBoxTotalMargin(box, aIsHorizontal);
697 }
698
699 if (aIsHorizontal) {
700 row->mTop = totalBorderPadding.top;
701 row->mBottom = totalBorderPadding.bottom;
702 row->mTopMargin = totalMargin.top;
703 row->mBottomMargin = totalMargin.bottom;
704 } else {
705 row->mTop = totalBorderPadding.left;
706 row->mBottom = totalBorderPadding.right;
707 row->mTopMargin = totalMargin.left;
708 row->mBottomMargin = totalMargin.right;
709 }
710
711 // if we are the first or last row take into account the top and bottom borders
712 // of each columns.
713
714 // If we are the first row then get the largest top border/padding in
715 // our columns. If that's larger than the rows top border/padding use it.
716
717 // If we are the last row then get the largest bottom border/padding in
718 // our columns. If that's larger than the rows bottom border/padding use it.
719 int32_t firstIndex = 0;
720 int32_t lastIndex = 0;
721 nsGridRow* firstRow = nullptr;
722 nsGridRow* lastRow = nullptr;
723 GetFirstAndLastRow(aState, firstIndex, lastIndex, firstRow, lastRow, aIsHorizontal);
724
725 if (aIndex == firstIndex || aIndex == lastIndex) {
726 nscoord maxTop = 0;
727 nscoord maxBottom = 0;
728
729 // run through the columns. Look at each column
730 // pick the largest top border or bottom border
731 int32_t count = GetColumnCount(aIsHorizontal);
732
733 for (int32_t i=0; i < count; i++)
734 {
735 nsMargin totalChildBorderPadding(0,0,0,0);
736
737 nsGridRow* column = GetColumnAt(i,aIsHorizontal);
738 nsIFrame* box = column->GetBox();
739
740 if (box)
741 {
742 // ignore collapsed children
743 if (!box->IsCollapsed())
744 {
745 // include the margin of the columns. To the row
746 // at this point border/padding and margins all added
747 // up to more needed space.
748 margin = GetBoxTotalMargin(box, !aIsHorizontal);
749 // get real border and padding. GetBorderAndPadding
750 // is redefined on nsGridRowLeafFrame. If we called it here
751 // we would be in finite recurson.
752 box->GetBorder(border);
753 box->GetPadding(padding);
754 totalChildBorderPadding += border;
755 totalChildBorderPadding += padding;
756 totalChildBorderPadding += margin;
757 }
758
759 nscoord top;
760 nscoord bottom;
761
762 // pick the largest top margin
763 if (aIndex == firstIndex) {
764 if (aIsHorizontal) {
765 top = totalChildBorderPadding.top;
766 } else {
767 top = totalChildBorderPadding.left;
768 }
769 if (top > maxTop)
770 maxTop = top;
771 }
772
773 // pick the largest bottom margin
774 if (aIndex == lastIndex) {
775 if (aIsHorizontal) {
776 bottom = totalChildBorderPadding.bottom;
777 } else {
778 bottom = totalChildBorderPadding.right;
779 }
780 if (bottom > maxBottom)
781 maxBottom = bottom;
782 }
783
784 }
785
786 // If the biggest top border/padding the columns is larger than this rows top border/padding
787 // the use it.
788 if (aIndex == firstIndex) {
789 if (maxTop > (row->mTop + row->mTopMargin))
790 row->mTop = maxTop - row->mTopMargin;
791 }
792
793 // If the biggest bottom border/padding the columns is larger than this rows bottom border/padding
794 // the use it.
795 if (aIndex == lastIndex) {
796 if (maxBottom > (row->mBottom + row->mBottomMargin))
797 row->mBottom = maxBottom - row->mBottomMargin;
798 }
799 }
800 }
801
802 aTop = row->mTop;
803 aBottom = row->mBottom;
804 }
805
806 /**
807 * These methods return the preferred, min, max coord for a given row index if
808 * aIsHorizontal is true. If you pass false you will get the inverse.
809 * As if you called GetPrefColumnHeight(aState, index, aPref).
810 */
811 nscoord
812 nsGrid::GetPrefRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
813 {
814 RebuildIfNeeded();
815
816 nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
817
818 if (row->IsCollapsed())
819 return 0;
820
821 if (row->IsPrefSet())
822 return row->mPref;
823
824 nsIFrame* box = row->mBox;
825
826 // set in CSS?
827 if (box)
828 {
829 bool widthSet, heightSet;
830 nsSize cssSize(-1, -1);
831 nsIFrame::AddCSSPrefSize(box, cssSize, widthSet, heightSet);
832
833 row->mPref = GET_HEIGHT(cssSize, aIsHorizontal);
834
835 // yep do nothing.
836 if (row->mPref != -1)
837 return row->mPref;
838 }
839
840 // get the offsets so they are cached.
841 nscoord top;
842 nscoord bottom;
843 GetRowOffsets(aState, aIndex, top, bottom, aIsHorizontal);
844
845 // is the row bogus? If so then just ask it for its size
846 // it should not be affected by cells in the grid.
847 if (row->mIsBogus)
848 {
849 nsSize size(0,0);
850 if (box)
851 {
852 size = box->GetPrefSize(aState);
853 nsBox::AddMargin(box, size);
854 nsGridLayout2::AddOffset(aState, box, size);
855 }
856
857 row->mPref = GET_HEIGHT(size, aIsHorizontal);
858 return row->mPref;
859 }
860
861 nsSize size(0,0);
862
863 nsGridCell* child;
864
865 int32_t count = GetColumnCount(aIsHorizontal);
866
867 for (int32_t i=0; i < count; i++)
868 {
869 if (aIsHorizontal)
870 child = GetCellAt(i,aIndex);
871 else
872 child = GetCellAt(aIndex,i);
873
874 // ignore collapsed children
875 if (!child->IsCollapsed())
876 {
877 nsSize childSize = child->GetPrefSize(aState);
878
879 nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
880 }
881 }
882
883 row->mPref = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
884
885 return row->mPref;
886 }
887
888 nscoord
889 nsGrid::GetMinRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
890 {
891 RebuildIfNeeded();
892
893 nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
894
895 if (row->IsCollapsed())
896 return 0;
897
898 if (row->IsMinSet())
899 return row->mMin;
900
901 nsIFrame* box = row->mBox;
902
903 // set in CSS?
904 if (box) {
905 bool widthSet, heightSet;
906 nsSize cssSize(-1, -1);
907 nsIFrame::AddCSSMinSize(aState, box, cssSize, widthSet, heightSet);
908
909 row->mMin = GET_HEIGHT(cssSize, aIsHorizontal);
910
911 // yep do nothing.
912 if (row->mMin != -1)
913 return row->mMin;
914 }
915
916 // get the offsets so they are cached.
917 nscoord top;
918 nscoord bottom;
919 GetRowOffsets(aState, aIndex, top, bottom, aIsHorizontal);
920
921 // is the row bogus? If so then just ask it for its size
922 // it should not be affected by cells in the grid.
923 if (row->mIsBogus)
924 {
925 nsSize size(0,0);
926 if (box) {
927 size = box->GetPrefSize(aState);
928 nsBox::AddMargin(box, size);
929 nsGridLayout2::AddOffset(aState, box, size);
930 }
931
932 row->mMin = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
933 return row->mMin;
934 }
935
936 nsSize size(0,0);
937
938 nsGridCell* child;
939
940 int32_t count = GetColumnCount(aIsHorizontal);
941
942 for (int32_t i=0; i < count; i++)
943 {
944 if (aIsHorizontal)
945 child = GetCellAt(i,aIndex);
946 else
947 child = GetCellAt(aIndex,i);
948
949 // ignore collapsed children
950 if (!child->IsCollapsed())
951 {
952 nsSize childSize = child->GetMinSize(aState);
953
954 nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
955 }
956 }
957
958 row->mMin = GET_HEIGHT(size, aIsHorizontal);
959
960 return row->mMin;
961 }
962
963 nscoord
964 nsGrid::GetMaxRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
965 {
966 RebuildIfNeeded();
967
968 nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
969
970 if (row->IsCollapsed())
971 return 0;
972
973 if (row->IsMaxSet())
974 return row->mMax;
975
976 nsIFrame* box = row->mBox;
977
978 // set in CSS?
979 if (box) {
980 bool widthSet, heightSet;
981 nsSize cssSize(-1, -1);
982 nsIFrame::AddCSSMaxSize(box, cssSize, widthSet, heightSet);
983
984 row->mMax = GET_HEIGHT(cssSize, aIsHorizontal);
985
986 // yep do nothing.
987 if (row->mMax != -1)
988 return row->mMax;
989 }
990
991 // get the offsets so they are cached.
992 nscoord top;
993 nscoord bottom;
994 GetRowOffsets(aState, aIndex, top, bottom, aIsHorizontal);
995
996 // is the row bogus? If so then just ask it for its size
997 // it should not be affected by cells in the grid.
998 if (row->mIsBogus)
999 {
1000 nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
1001 if (box) {
1002 size = box->GetPrefSize(aState);
1003 nsBox::AddMargin(box, size);
1004 nsGridLayout2::AddOffset(aState, box, size);
1005 }
1006
1007 row->mMax = GET_HEIGHT(size, aIsHorizontal);
1008 return row->mMax;
1009 }
1010
1011 nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
1012
1013 nsGridCell* child;
1014
1015 int32_t count = GetColumnCount(aIsHorizontal);
1016
1017 for (int32_t i=0; i < count; i++)
1018 {
1019 if (aIsHorizontal)
1020 child = GetCellAt(i,aIndex);
1021 else
1022 child = GetCellAt(aIndex,i);
1023
1024 // ignore collapsed children
1025 if (!child->IsCollapsed())
1026 {
1027 nsSize min = child->GetMinSize(aState);
1028 nsSize childSize = nsBox::BoundsCheckMinMax(min, child->GetMaxSize(aState));
1029 nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
1030 }
1031 }
1032
1033 row->mMax = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
1034
1035 return row->mMax;
1036 }
1037
1038 bool
1039 nsGrid::IsGrid(nsIFrame* aBox)
1040 {
1041 nsIGridPart* part = GetPartFromBox(aBox);
1042 if (!part)
1043 return false;
1044
1045 nsGridLayout2* grid = part->CastToGridLayout();
1046
1047 if (grid)
1048 return true;
1049
1050 return false;
1051 }
1052
1053 /**
1054 * This get the flexibilty of the row at aIndex. It's not trivial. There are a few
1055 * things we need to look at. Specifically we need to see if any <rows> or <columns>
1056 * tags are around us. Their flexibilty will affect ours.
1057 */
1058 nscoord
1059 nsGrid::GetRowFlex(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
1060 {
1061 RebuildIfNeeded();
1062
1063 nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
1064
1065 if (row->IsFlexSet())
1066 return row->mFlex;
1067
1068 nsIFrame* box = row->mBox;
1069 row->mFlex = 0;
1070
1071 if (box) {
1072
1073 // We need our flex but a inflexible row could be around us. If so
1074 // neither are we. However if its the row tag just inside the grid it won't
1075 // affect us. We need to do this for this case:
1076 // <grid>
1077 // <rows>
1078 // <rows> // this is not flexible. So our children should not be flexible
1079 // <row flex="1"/>
1080 // <row flex="1"/>
1081 // </rows>
1082 // <row/>
1083 // </rows>
1084 // </grid>
1085 //
1086 // or..
1087 //
1088 // <grid>
1089 // <rows>
1090 // <rows> // this is not flexible. So our children should not be flexible
1091 // <rows flex="1">
1092 // <row flex="1"/>
1093 // <row flex="1"/>
1094 // </rows>
1095 // <row/>
1096 // </rows>
1097 // </row>
1098 // </grid>
1099
1100
1101 // So here is how it looks
1102 //
1103 // <grid>
1104 // <rows> // parentsParent
1105 // <rows> // parent
1106 // <row flex="1"/>
1107 // <row flex="1"/>
1108 // </rows>
1109 // <row/>
1110 // </rows>
1111 // </grid>
1112
1113 // so the answer is simple: 1) Walk our parent chain. 2) If we find
1114 // someone who is not flexible and they aren't the rows immediately in
1115 // the grid. 3) Then we are not flexible
1116
1117 box = GetScrollBox(box);
1118 nsIFrame* parent = box->GetParentBox();
1119 nsIFrame* parentsParent=nullptr;
1120
1121 while(parent)
1122 {
1123 parent = GetScrollBox(parent);
1124 parentsParent = parent->GetParentBox();
1125
1126 // if our parents parent is not a grid
1127 // the get its flex. If its 0 then we are
1128 // not flexible.
1129 if (parentsParent) {
1130 if (!IsGrid(parentsParent)) {
1131 nscoord flex = parent->GetFlex(aState);
1132 nsIFrame::AddCSSFlex(aState, parent, flex);
1133 if (flex == 0) {
1134 row->mFlex = 0;
1135 return row->mFlex;
1136 }
1137 } else
1138 break;
1139 }
1140
1141 parent = parentsParent;
1142 }
1143
1144 // get the row flex.
1145 row->mFlex = box->GetFlex(aState);
1146 nsIFrame::AddCSSFlex(aState, box, row->mFlex);
1147 }
1148
1149 return row->mFlex;
1150 }
1151
1152 void
1153 nsGrid::SetLargestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
1154 {
1155 if (aIsHorizontal) {
1156 if (aSize.height < aHeight)
1157 aSize.height = aHeight;
1158 } else {
1159 if (aSize.width < aHeight)
1160 aSize.width = aHeight;
1161 }
1162 }
1163
1164 void
1165 nsGrid::SetSmallestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
1166 {
1167 if (aIsHorizontal) {
1168 if (aSize.height > aHeight)
1169 aSize.height = aHeight;
1170 } else {
1171 if (aSize.width < aHeight)
1172 aSize.width = aHeight;
1173 }
1174 }
1175
1176 int32_t
1177 nsGrid::GetRowCount(int32_t aIsHorizontal)
1178 {
1179 RebuildIfNeeded();
1180
1181 if (aIsHorizontal)
1182 return mRowCount;
1183 else
1184 return mColumnCount;
1185 }
1186
1187 int32_t
1188 nsGrid::GetColumnCount(int32_t aIsHorizontal)
1189 {
1190 return GetRowCount(!aIsHorizontal);
1191 }
1192
1193 /*
1194 * A cell in the given row or columns at the given index has had a child added or removed
1195 */
1196 void
1197 nsGrid::CellAddedOrRemoved(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
1198 {
1199 // TBD see if the cell will fit in our current row. If it will
1200 // just add it in.
1201 // but for now rebuild everything.
1202 if (mMarkingDirty)
1203 return;
1204
1205 NeedsRebuild(aState);
1206 }
1207
1208 /**
1209 * A row or columns at the given index had been added or removed
1210 */
1211 void
1212 nsGrid::RowAddedOrRemoved(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
1213 {
1214 // TBD see if we have extra room in the table and just add the new row in
1215 // for now rebuild the world
1216 if (mMarkingDirty)
1217 return;
1218
1219 NeedsRebuild(aState);
1220 }
1221
1222 /*
1223 * Scrollframes are tranparent. If this is given a scrollframe is will return the
1224 * frame inside. If there is no scrollframe it does nothing.
1225 */
1226 nsIFrame*
1227 nsGrid::GetScrolledBox(nsIFrame* aChild)
1228 {
1229 // first see if it is a scrollframe. If so walk down into it and get the scrolled child
1230 nsIScrollableFrame *scrollFrame = do_QueryFrame(aChild);
1231 if (scrollFrame) {
1232 nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
1233 NS_ASSERTION(scrolledFrame,"Error no scroll frame!!");
1234 return scrolledFrame;
1235 }
1236
1237 return aChild;
1238 }
1239
1240 /*
1241 * Scrollframes are tranparent. If this is given a child in a scrollframe is will return the
1242 * scrollframe ourside it. If there is no scrollframe it does nothing.
1243 */
1244 nsIFrame*
1245 nsGrid::GetScrollBox(nsIFrame* aChild)
1246 {
1247 if (!aChild)
1248 return nullptr;
1249
1250 // get parent
1251 nsIFrame* parent = aChild->GetParentBox();
1252
1253 // walk up until we find a scrollframe or a part
1254 // if it's a scrollframe return it.
1255 // if it's a parent then the child passed does not
1256 // have a scroll frame immediately wrapped around it.
1257 while (parent) {
1258 nsIScrollableFrame *scrollFrame = do_QueryFrame(parent);
1259 // scrollframe? Yep return it.
1260 if (scrollFrame)
1261 return parent;
1262
1263 nsCOMPtr<nsIGridPart> parentGridRow = GetPartFromBox(parent);
1264 // if a part then just return the child
1265 if (parentGridRow)
1266 break;
1267
1268 parent = parent->GetParentBox();
1269 }
1270
1271 return aChild;
1272 }
1273
1274
1275
1276 #ifdef DEBUG_grid
1277 void
1278 nsGrid::PrintCellMap()
1279 {
1280
1281 printf("-----Columns------\n");
1282 for (int x=0; x < mColumnCount; x++)
1283 {
1284
1285 nsGridRow* column = GetColumnAt(x);
1286 printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
1287 }
1288
1289 printf("\n-----Rows------\n");
1290 for (x=0; x < mRowCount; x++)
1291 {
1292 nsGridRow* column = GetRowAt(x);
1293 printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
1294 }
1295
1296 printf("\n");
1297
1298 }
1299 #endif

mercurial