michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // michael@0: // Eric Vaughan michael@0: // Netscape Communications michael@0: // michael@0: // See documentation in associated header file michael@0: // michael@0: michael@0: #include "nsGridRowLeafLayout.h" michael@0: #include "nsGridRowGroupLayout.h" michael@0: #include "nsGridRow.h" michael@0: #include "nsBoxLayoutState.h" michael@0: #include "nsBox.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsBoxFrame.h" michael@0: #include "nsGridLayout2.h" michael@0: #include michael@0: michael@0: already_AddRefed NS_NewGridRowLeafLayout() michael@0: { michael@0: nsRefPtr layout = new nsGridRowLeafLayout(); michael@0: return layout.forget(); michael@0: } michael@0: michael@0: nsGridRowLeafLayout::nsGridRowLeafLayout():nsGridRowLayout() michael@0: { michael@0: } michael@0: michael@0: nsGridRowLeafLayout::~nsGridRowLeafLayout() michael@0: { michael@0: } michael@0: michael@0: nsSize michael@0: nsGridRowLeafLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: int32_t index = 0; michael@0: nsGrid* grid = GetGrid(aBox, &index); michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: michael@0: // If we are not in a grid. Then we just work like a box. But if we are in a grid michael@0: // ask the grid for our size. michael@0: if (!grid) { michael@0: return nsGridRowLayout::GetPrefSize(aBox, aState); michael@0: } michael@0: else { michael@0: return grid->GetPrefRowSize(aState, index, isHorizontal); michael@0: //AddBorderAndPadding(aBox, pref); michael@0: } michael@0: } michael@0: michael@0: nsSize michael@0: nsGridRowLeafLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: int32_t index = 0; michael@0: nsGrid* grid = GetGrid(aBox, &index); michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: michael@0: if (!grid) michael@0: return nsGridRowLayout::GetMinSize(aBox, aState); michael@0: else { michael@0: nsSize minSize = grid->GetMinRowSize(aState, index, isHorizontal); michael@0: AddBorderAndPadding(aBox, minSize); michael@0: return minSize; michael@0: } michael@0: } michael@0: michael@0: nsSize michael@0: nsGridRowLeafLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: int32_t index = 0; michael@0: nsGrid* grid = GetGrid(aBox, &index); michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: michael@0: if (!grid) michael@0: return nsGridRowLayout::GetMaxSize(aBox, aState); michael@0: else { michael@0: nsSize maxSize; michael@0: maxSize = grid->GetMaxRowSize(aState, index, isHorizontal); michael@0: AddBorderAndPadding(aBox, maxSize); michael@0: return maxSize; michael@0: } michael@0: } michael@0: michael@0: /** If a child is added or removed or changes size michael@0: */ michael@0: void michael@0: nsGridRowLeafLayout::ChildAddedOrRemoved(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: int32_t index = 0; michael@0: nsGrid* grid = GetGrid(aBox, &index); michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: michael@0: if (grid) michael@0: grid->CellAddedOrRemoved(aState, index, isHorizontal); michael@0: } michael@0: michael@0: void michael@0: nsGridRowLeafLayout::PopulateBoxSizes(nsIFrame* aBox, nsBoxLayoutState& aState, nsBoxSize*& aBoxSizes, nscoord& aMinSize, nscoord& aMaxSize, int32_t& aFlexes) michael@0: { michael@0: int32_t index = 0; michael@0: nsGrid* grid = GetGrid(aBox, &index); michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: michael@0: // Our base class SprocketLayout is giving us a chance to change the box sizes before layout michael@0: // If we are a row lets change the sizes to match our columns. If we are a column then do the opposite michael@0: // and make them match or rows. michael@0: if (grid) { michael@0: nsGridRow* column; michael@0: int32_t count = grid->GetColumnCount(isHorizontal); michael@0: nsBoxSize* start = nullptr; michael@0: nsBoxSize* last = nullptr; michael@0: nsBoxSize* current = nullptr; michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: for (int i=0; i < count; i++) michael@0: { michael@0: column = grid->GetColumnAt(i,isHorizontal); michael@0: michael@0: // make sure the value was computed before we use it. michael@0: // !isHorizontal is passed in to invert the behavior of these methods. michael@0: nscoord pref = michael@0: grid->GetPrefRowHeight(aState, i, !isHorizontal); // GetPrefColumnWidth michael@0: nscoord min = michael@0: grid->GetMinRowHeight(aState, i, !isHorizontal); // GetMinColumnWidth michael@0: nscoord max = michael@0: grid->GetMaxRowHeight(aState, i, !isHorizontal); // GetMaxColumnWidth michael@0: nscoord flex = michael@0: grid->GetRowFlex(aState, i, !isHorizontal); // GetColumnFlex michael@0: nscoord left = 0; michael@0: nscoord right = 0; michael@0: grid->GetRowOffsets(aState, i, left, right, !isHorizontal); // GetColumnOffsets michael@0: nsIFrame* box = column->GetBox(); michael@0: bool collapsed = false; michael@0: nscoord topMargin = column->mTopMargin; michael@0: nscoord bottomMargin = column->mBottomMargin; michael@0: michael@0: if (box) michael@0: collapsed = box->IsCollapsed(); michael@0: michael@0: pref = pref - (left + right); michael@0: if (pref < 0) michael@0: pref = 0; michael@0: michael@0: // if this is the first or last column. Take into account that michael@0: // our row could have a border that could affect our left or right michael@0: // padding from our columns. If the row has padding subtract it. michael@0: // would should always be able to garentee that our margin is smaller michael@0: // or equal to our left or right michael@0: int32_t firstIndex = 0; michael@0: int32_t lastIndex = 0; michael@0: nsGridRow* firstRow = nullptr; michael@0: nsGridRow* lastRow = nullptr; michael@0: grid->GetFirstAndLastRow(aState, firstIndex, lastIndex, firstRow, lastRow, !isHorizontal); michael@0: michael@0: if (i == firstIndex || i == lastIndex) { michael@0: nsMargin offset = GetTotalMargin(aBox, isHorizontal); michael@0: michael@0: nsMargin border(0,0,0,0); michael@0: // can't call GetBorderPadding we will get into recursion michael@0: aBox->GetBorder(border); michael@0: offset += border; michael@0: aBox->GetPadding(border); michael@0: offset += border; michael@0: michael@0: // subtract from out left and right michael@0: if (i == firstIndex) michael@0: { michael@0: if (isHorizontal) michael@0: left -= offset.left; michael@0: else michael@0: left -= offset.top; michael@0: } michael@0: michael@0: if (i == lastIndex) michael@0: { michael@0: if (isHorizontal) michael@0: right -= offset.right; michael@0: else michael@0: right -= offset.bottom; michael@0: } michael@0: } michael@0: michael@0: // initialize the box size here michael@0: max = std::max(min, max); michael@0: pref = nsBox::BoundsCheck(min, pref, max); michael@0: michael@0: current = new (aState) nsBoxSize(); michael@0: current->pref = pref; michael@0: current->min = min; michael@0: current->max = max; michael@0: current->flex = flex; michael@0: current->bogus = column->mIsBogus; michael@0: current->left = left + topMargin; michael@0: current->right = right + bottomMargin; michael@0: current->collapsed = collapsed; michael@0: michael@0: if (!start) { michael@0: start = current; michael@0: last = start; michael@0: } else { michael@0: last->next = current; michael@0: last = current; michael@0: } michael@0: michael@0: if (child && !column->mIsBogus) michael@0: child = child->GetNextBox(); michael@0: michael@0: } michael@0: aBoxSizes = start; michael@0: } michael@0: michael@0: nsSprocketLayout::PopulateBoxSizes(aBox, aState, aBoxSizes, aMinSize, aMaxSize, aFlexes); michael@0: } michael@0: michael@0: void michael@0: nsGridRowLeafLayout::ComputeChildSizes(nsIFrame* aBox, michael@0: nsBoxLayoutState& aState, michael@0: nscoord& aGivenSize, michael@0: nsBoxSize* aBoxSizes, michael@0: nsComputedBoxSize*& aComputedBoxSizes) michael@0: { michael@0: // see if we are in a scrollable frame. If we are then there could be scrollbars present michael@0: // if so we need to subtract them out to make sure our columns line up. michael@0: if (aBox) { michael@0: bool isHorizontal = aBox->IsHorizontal(); michael@0: michael@0: // go up the parent chain looking for scrollframes michael@0: nscoord diff = 0; michael@0: nsIFrame* parentBox; michael@0: (void)GetParentGridPart(aBox, &parentBox); michael@0: while (parentBox) { michael@0: nsIFrame* scrollbox = nsGrid::GetScrollBox(parentBox); michael@0: nsIScrollableFrame *scrollable = do_QueryFrame(scrollbox); michael@0: if (scrollable) { michael@0: // Don't call GetActualScrollbarSizes here because it's not safe michael@0: // to call that while we're reflowing the contents of the scrollframe, michael@0: // which we are here. michael@0: nsMargin scrollbarSizes = scrollable->GetDesiredScrollbarSizes(&aState); michael@0: uint32_t visible = scrollable->GetScrollbarVisibility(); michael@0: michael@0: if (isHorizontal && (visible & nsIScrollableFrame::VERTICAL)) { michael@0: diff += scrollbarSizes.left + scrollbarSizes.right; michael@0: } else if (!isHorizontal && (visible & nsIScrollableFrame::HORIZONTAL)) { michael@0: diff += scrollbarSizes.top + scrollbarSizes.bottom; michael@0: } michael@0: } michael@0: michael@0: (void)GetParentGridPart(parentBox, &parentBox); michael@0: } michael@0: michael@0: if (diff > 0) { michael@0: aGivenSize += diff; michael@0: michael@0: nsSprocketLayout::ComputeChildSizes(aBox, aState, aGivenSize, aBoxSizes, aComputedBoxSizes); michael@0: michael@0: aGivenSize -= diff; michael@0: michael@0: nsComputedBoxSize* s = aComputedBoxSizes; michael@0: nsComputedBoxSize* last = aComputedBoxSizes; michael@0: while(s) michael@0: { michael@0: last = s; michael@0: s = s->next; michael@0: } michael@0: michael@0: if (last) michael@0: last->size -= diff; michael@0: michael@0: return; michael@0: } michael@0: } michael@0: michael@0: nsSprocketLayout::ComputeChildSizes(aBox, aState, aGivenSize, aBoxSizes, aComputedBoxSizes); michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGridRowLeafLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: return nsGridRowLayout::Layout(aBox, aBoxLayoutState); michael@0: } michael@0: michael@0: void michael@0: nsGridRowLeafLayout::DirtyRows(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: if (aBox) { michael@0: // mark us dirty michael@0: // XXXldb We probably don't want to walk up the ancestor chain michael@0: // calling MarkIntrinsicWidthsDirty for every row. michael@0: aState.PresShell()->FrameNeedsReflow(aBox, nsIPresShell::eTreeChange, michael@0: NS_FRAME_IS_DIRTY); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsGridRowLeafLayout::CountRowsColumns(nsIFrame* aBox, int32_t& aRowCount, int32_t& aComputedColumnCount) michael@0: { michael@0: if (aBox) { michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: michael@0: // count the children michael@0: int32_t columnCount = 0; michael@0: while(child) { michael@0: child = child->GetNextBox(); michael@0: columnCount++; michael@0: } michael@0: michael@0: // if our count is greater than the current column count michael@0: if (columnCount > aComputedColumnCount) michael@0: aComputedColumnCount = columnCount; michael@0: michael@0: aRowCount++; michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: nsGridRowLeafLayout::BuildRows(nsIFrame* aBox, nsGridRow* aRows) michael@0: { michael@0: if (aBox) { michael@0: aRows[0].Init(aBox, false); michael@0: return 1; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: