summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklinDM <mrmineshafter17@gmail.com>2022-04-02 15:56:02 +0800
committerFranklinDM <mrmineshafter17@gmail.com>2022-04-02 23:14:18 +0800
commited8d7f13914404f048c3a57c5000f524179c3d89 (patch)
treea65bb2f88fe025e0d19c5ff10e7d6dfb00f3aaed
parent7e975c2c5b7554982031d84527a51edda6157d73 (diff)
downloaduxp-ed8d7f13914404f048c3a57c5000f524179c3d89.tar.gz
Issue #1838 - Part 4: Implement flexbox layout for `(column|row)-gap` properties
This implements flexbox layout for the gap properties without the refactoring work performed on `nsFlexContainerFrame`. Partially based on https://bugzilla.mozilla.org/show_bug.cgi?id=1398483 and https://bugzilla.mozilla.org/show_bug.cgi?id=1454822 This excludes the second part of bug 1454822, the width caching implementation, because it is out of scope and currently causes unstable layout with `writing-mode: vertical-lr` (see bug 1709937).
-rw-r--r--layout/generic/nsFlexContainerFrame.cpp233
-rw-r--r--layout/generic/nsFlexContainerFrame.h8
2 files changed, 178 insertions, 63 deletions
diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp
index 8a393c3fe7..9b5372832c 100644
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -34,6 +34,7 @@ typedef nsFlexContainerFrame::FlexboxAxisTracker FlexboxAxisTracker;
typedef nsFlexContainerFrame::StrutInfo StrutInfo;
typedef nsFlexContainerFrame::CachedMeasuringReflowResult
CachedMeasuringReflowResult;
+typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
static mozilla::LazyLogModule gFlexContainerLog("nsFlexContainerFrame");
@@ -811,17 +812,23 @@ protected:
class nsFlexContainerFrame::FlexLine : public LinkedListElement<FlexLine>
{
public:
- FlexLine()
+ explicit FlexLine(nscoord aMainGapSize)
: mNumItems(0),
mNumFrozenItems(0),
mTotalInnerHypotheticalMainSize(0),
mTotalOuterHypotheticalMainSize(0),
mLineCrossSize(0),
mFirstBaselineOffset(nscoord_MIN),
- mLastBaselineOffset(nscoord_MIN)
+ mLastBaselineOffset(nscoord_MIN),
+ mMainGapSize(aMainGapSize)
{}
- // Returns the sum of our FlexItems' outer hypothetical main sizes.
+ nscoord GetSumOfGaps() const {
+ return mNumItems > 0 ? (mNumItems - 1) * mMainGapSize : 0;
+ }
+
+ // Returns the sum of our FlexItems' outer hypothetical main sizes
+ // and the sum of the main axis {row,column}-gaps between items.
// ("outer" = margin-box, and "hypothetical" = before flexing)
nscoord GetTotalOuterHypotheticalMainSize() const {
return mTotalOuterHypotheticalMainSize;
@@ -890,8 +897,16 @@ public:
if (aItem->IsFrozen()) {
mNumFrozenItems++;
}
- mTotalInnerHypotheticalMainSize += aItemInnerHypotheticalMainSize;
+
+ mTotalInnerHypotheticalMainSize += (aItemOuterHypotheticalMainSize -
+ aItemInnerHypotheticalMainSize);
mTotalOuterHypotheticalMainSize += aItemOuterHypotheticalMainSize;
+
+ // If the item added was not the first item in the line, we add in
+ // any gap space as needed.
+ if (mNumItems >= 2) {
+ mTotalOuterHypotheticalMainSize += mMainGapSize;
+ }
}
// Computes the cross-size and baseline position of this FlexLine, based on
@@ -935,6 +950,22 @@ public:
return mLastBaselineOffset;
}
+ /**
+ * Returns the number of items held in this line. Used for total gap
+ * calculations.
+ */
+ uint32_t GetNumItems() const {
+ return mNumItems;
+ }
+
+ /**
+ * Returns the gap size in the main axis for this line. Used for gap
+ * calculations.
+ */
+ nscoord GetMainGapSize() const {
+ return mMainGapSize;
+ }
+
// Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
// CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
void ResolveFlexibleLengths(nscoord aFlexContainerMainSize);
@@ -973,6 +1004,9 @@ private:
nscoord mLineCrossSize;
nscoord mFirstBaselineOffset;
nscoord mLastBaselineOffset;
+
+ // Maintain size of each {row,column}-gap in the main axis
+ const nscoord mMainGapSize;
};
// Information about a strut left behind by a FlexItem that's been collapsed
@@ -2241,6 +2275,9 @@ public:
"miscounted the number of auto margins");
}
+ // Advances past the gap space (if any) between two flex items
+ void TraverseGap(nscoord aGapSize) { mPosition += aGapSize; }
+
// Advances past the packing space (if any) between two flex items
void TraversePackingSpace();
@@ -2266,7 +2303,11 @@ public:
const ReflowInput& aReflowInput,
nscoord aContentBoxCrossSize,
bool aIsCrossSizeDefinite,
- const FlexboxAxisTracker& aAxisTracker);
+ const FlexboxAxisTracker& aAxisTracker,
+ const nscoord aCrossGapSize);
+
+ // Advances past the gap (if any) between two flex lines
+ void TraverseGap() { mPosition += mCrossGapSize; }
// Advances past the packing space (if any) between two flex lines
void TraversePackingSpace();
@@ -2288,6 +2329,8 @@ private:
uint32_t mNumPackingSpacesRemaining;
// XXX this should be uint16_t when we add explicit fallback handling
uint8_t mAlignContent;
+
+ nscoord mCrossGapSize = 0;
};
// Utility class for managing our position along the cross axis, *within* a
@@ -2581,14 +2624,12 @@ FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
}
MOZ_ASSERT(!IsEmpty(), "empty lines should take the early-return above");
- // Subtract space occupied by our items' margins/borders/padding, so we can
- // just be dealing with the space available for our flex items' content
- // boxes.
- nscoord spaceReservedForMarginBorderPadding =
- mTotalOuterHypotheticalMainSize - mTotalInnerHypotheticalMainSize;
-
+ // Subtract space occupied by our items' margins/borders/padding/gaps, so
+ // we can just be dealing with the space available for our flex items'
+ // content boxes.
nscoord spaceAvailableForFlexItemsContentBoxes =
- aFlexContainerMainSize - spaceReservedForMarginBorderPadding;
+ aFlexContainerMainSize - (mTotalInnerHypotheticalMainSize +
+ GetSumOfGaps());
nscoord origAvailableFreeSpace;
bool isOrigAvailFreeSpaceInitialized = false;
@@ -2867,6 +2908,9 @@ MainAxisPositionTracker::
mNumAutoMarginsInMainAxis += item->GetNumAutoMarginsInAxis(mAxis);
}
+ // Subtract space required for row/col gap from the remaining packing space
+ mPackingSpaceRemaining -= aLine->GetSumOfGaps();
+
if (mPackingSpaceRemaining <= 0) {
// No available packing space to use for resolving auto margins.
mNumAutoMarginsInMainAxis = 0;
@@ -3014,12 +3058,14 @@ CrossAxisPositionTracker::
const ReflowInput& aReflowInput,
nscoord aContentBoxCrossSize,
bool aIsCrossSizeDefinite,
- const FlexboxAxisTracker& aAxisTracker)
+ const FlexboxAxisTracker& aAxisTracker,
+ const nscoord aCrossGapSize)
: PositionTracker(aAxisTracker.GetCrossAxis(),
aAxisTracker.IsCrossAxisReversed()),
mPackingSpaceRemaining(0),
mNumPackingSpacesRemaining(0),
- mAlignContent(aReflowInput.mStylePosition->mAlignContent)
+ mAlignContent(aReflowInput.mStylePosition->mAlignContent),
+ mCrossGapSize(aCrossGapSize)
{
MOZ_ASSERT(aFirstLine, "null first line pointer");
@@ -3072,6 +3118,11 @@ CrossAxisPositionTracker::
numLines++;
}
+ // Subtract space required for row/col gap from the remaining packing space
+ MOZ_ASSERT(numLines >= 1,
+ "GenerateFlexLines should've produced at least 1 line");
+ mPackingSpaceRemaining -= aCrossGapSize * (numLines - 1);
+
// If packing space is negative, 'space-between' and 'stretch' behave like
// 'flex-start', and 'space-around' and 'space-evenly' behave like 'center'.
// In those cases, it's simplest to just pretend we have a different
@@ -3673,9 +3724,10 @@ FlexboxAxisTracker::InitAxesFromModernProps(
// back depending on aShouldInsertAtFront), and returns a pointer to it.
static FlexLine*
AddNewFlexLineToList(LinkedList<FlexLine>& aLines,
- bool aShouldInsertAtFront)
+ bool aShouldInsertAtFront,
+ nscoord aMainGapSize)
{
- FlexLine* newLine = new FlexLine();
+ FlexLine* newLine = new FlexLine(aMainGapSize);
if (aShouldInsertAtFront) {
aLines.insertFront(newLine);
} else {
@@ -3692,6 +3744,7 @@ nsFlexContainerFrame::GenerateFlexLines(
nscoord aAvailableBSizeForContent,
const nsTArray<StrutInfo>& aStruts,
const FlexboxAxisTracker& aAxisTracker,
+ nscoord aMainGapSize,
nsTArray<nsIFrame*>& aPlaceholders, /* out */
LinkedList<FlexLine>& aLines /* out */)
{
@@ -3710,7 +3763,8 @@ nsFlexContainerFrame::GenerateFlexLines(
// We have at least one FlexLine. Even an empty flex container has a single
// (empty) flex line.
- FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+ FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront,
+ aMainGapSize);
nscoord wrapThreshold;
if (isSingleLine) {
@@ -3759,7 +3813,8 @@ nsFlexContainerFrame::GenerateFlexLines(
// Honor "page-break-before", if we're multi-line and this line isn't empty:
if (!isSingleLine && !curLine->IsEmpty() &&
childFrame->StyleDisplay()->mBreakBefore) {
- curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+ curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront,
+ aMainGapSize);
}
UniquePtr<FlexItem> item;
@@ -3782,11 +3837,16 @@ nsFlexContainerFrame::GenerateFlexLines(
// Check if we need to wrap |item| to a new line
// (i.e. check if its outer hypothetical main size pushes our line over
// the threshold)
- if (wrapThreshold != NS_UNCONSTRAINEDSIZE &&
- !curLine->IsEmpty() && // No need to wrap at start of a line.
- wrapThreshold < (curLine->GetTotalOuterHypotheticalMainSize() +
- itemOuterHypotheticalMainSize)) {
- curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+ // Don't wrap if unconstrained and if this will be line's first item.
+ if (wrapThreshold != NS_UNCONSTRAINEDSIZE && !curLine->IsEmpty()) {
+ nscoord newOuterSize = curLine->GetTotalOuterHypotheticalMainSize() +
+ itemOuterHypotheticalMainSize;
+ // Account for gap between this line's previous item and this item
+ newOuterSize += aMainGapSize;
+ if (newOuterSize == nscoord_MAX || newOuterSize > wrapThreshold) {
+ curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront,
+ aMainGapSize);
+ }
}
// Add item to current flex line (and update the line's bookkeeping about
@@ -3798,7 +3858,8 @@ nsFlexContainerFrame::GenerateFlexLines(
// Honor "page-break-after", if we're multi-line and have more children:
if (!isSingleLine && childFrame->GetNextSibling() &&
childFrame->StyleDisplay()->mBreakAfter) {
- curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+ curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront,
+ aMainGapSize);
}
itemIdxInContainer++;
}
@@ -3990,6 +4051,9 @@ FlexLine::PositionItemsInMainAxis(uint8_t aJustifyContent,
mainAxisPosnTracker.ExitChildFrame(itemMainBorderBoxSize);
mainAxisPosnTracker.ExitMargin(item->GetMargin());
mainAxisPosnTracker.TraversePackingSpace();
+ if (item->getNext()) {
+ mainAxisPosnTracker.TraverseGap(mMainGapSize);
+ }
}
}
@@ -4178,16 +4242,37 @@ nsFlexContainerFrame::Reflow(nsPresContext* aPresContext,
nscoord contentBoxMainSize = GetMainSizeFromReflowInput(aReflowInput,
axisTracker);
+ // Calculate gap size for main and cross axis
+ nscoord mainGapSize;
+ nscoord crossGapSize;
+ if (axisTracker.IsRowOriented()) {
+ mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mColumnGap,
+ contentBoxMainSize);
+ crossGapSize =
+ nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap,
+ GetEffectiveComputedBSize(aReflowInput));
+ } else {
+ mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap,
+ contentBoxMainSize);
+ NS_WARNING_ASSERTION(aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE,
+ "Unconstrained inline size; this should only result "
+ "from huge sizes (not intrinsic sizing w/ orthogonal "
+ "flows)");
+ crossGapSize =
+ nsLayoutUtils::ResolveGapToLength(stylePos->mColumnGap,
+ aReflowInput.ComputedISize());
+ }
+
AutoTArray<StrutInfo, 1> struts;
DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
contentBoxMainSize, availableBSizeForContent,
- struts, axisTracker);
+ struts, axisTracker, mainGapSize, crossGapSize);
if (!struts.IsEmpty()) {
// We're restarting flex layout, with new knowledge of collapsed items.
DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
contentBoxMainSize, availableBSizeForContent,
- struts, axisTracker);
+ struts, axisTracker, mainGapSize, crossGapSize);
}
}
@@ -4312,7 +4397,9 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
nscoord aContentBoxMainSize,
nscoord aAvailableBSizeForContent,
nsTArray<StrutInfo>& aStruts,
- const FlexboxAxisTracker& aAxisTracker)
+ const FlexboxAxisTracker& aAxisTracker,
+ nscoord aMainGapSize,
+ nscoord aCrossGapSize)
{
aStatus = NS_FRAME_COMPLETE;
@@ -4324,6 +4411,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
aContentBoxMainSize,
aAvailableBSizeForContent,
aStruts, aAxisTracker,
+ aMainGapSize,
placeholderKids, lines);
if (lines.getFirst()->IsEmpty() &&
@@ -4397,7 +4485,8 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
CrossAxisPositionTracker
crossAxisPosnTracker(lines.getFirst(),
aReflowInput, contentBoxCrossSize,
- isCrossSizeDefinite, aAxisTracker);
+ isCrossSizeDefinite, aAxisTracker,
+ aCrossGapSize);
// Now that we know the cross size of each line (including
// "align-content:stretch" adjustments, from the CrossAxisPositionTracker
@@ -4447,6 +4536,10 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
aAxisTracker);
crossAxisPosnTracker.TraverseLine(*line);
crossAxisPosnTracker.TraversePackingSpace();
+
+ if (line->getNext()) {
+ crossAxisPosnTracker.TraverseGap();
+ }
}
// If the container should derive its baseline from the last FlexLine,
@@ -4817,33 +4910,65 @@ nsFlexContainerFrame::ReflowPlaceholders(nsPresContext* aPresContext,
}
}
-/* virtual */ nscoord
-nsFlexContainerFrame::GetMinISize(nsRenderingContext* aRenderingContext)
+nscoord
+nsFlexContainerFrame::GetIntrinsicISize(nsRenderingContext* aRenderingContext,
+ IntrinsicISizeType aType)
{
- nscoord minISize = 0;
- DISPLAY_MIN_WIDTH(this, minISize);
-
+ nscoord containerISize = 0;
RenumberList();
const nsStylePosition* stylePos = StylePosition();
const FlexboxAxisTracker axisTracker(this, GetWritingMode());
+ nscoord mainGapSize;
+ if (axisTracker.IsRowOriented()) {
+ mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mColumnGap,
+ NS_UNCONSTRAINEDSIZE);
+ } else {
+ mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap,
+ NS_UNCONSTRAINEDSIZE);
+ }
+
+ // The loop below sets aside space for a gap before each item besides the
+ // first. This bool helps us handle that special-case.
+ bool onFirstChild = true;
+
for (nsIFrame* childFrame : mFrames) {
- nscoord childMinISize =
- nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
- nsLayoutUtils::MIN_ISIZE);
- // For a horizontal single-line flex container, the intrinsic min
- // isize is the sum of its items' min isizes.
- // For a column-oriented flex container, or for a multi-line row-
- // oriented flex container, the intrinsic min isize is the max of
- // its items' min isizes.
+ nscoord childISize = nsLayoutUtils::IntrinsicForContainer(
+ aRenderingContext, childFrame, aType);
+ // * For a row-oriented single-line flex container, the intrinsic
+ // {min/pref}-isize is the sum of its items' {min/pref}-isizes and
+ // (n-1) column gaps.
+ // * For a column-oriented flex container, the intrinsic min isize
+ // is the max of its items' min isizes.
+ // * For a row-oriented multi-line flex container, the intrinsic
+ // pref isize is former (sum), and its min isize is the latter (max).
+ bool isSingleLine = (NS_STYLE_FLEX_WRAP_NOWRAP == stylePos->mFlexWrap);
if (axisTracker.IsRowOriented() &&
- NS_STYLE_FLEX_WRAP_NOWRAP == stylePos->mFlexWrap) {
- minISize += childMinISize;
+ (isSingleLine || aType == nsLayoutUtils::PREF_ISIZE)) {
+ containerISize += childISize;
+ if (!onFirstChild) {
+ containerISize += mainGapSize;
+ }
+ onFirstChild = false;
} else {
- minISize = std::max(minISize, childMinISize);
+ // col-oriented, or MIN_ISIZE for multi-line row flex container
+ containerISize = std::max(containerISize, childISize);
}
}
+
+ return containerISize;
+}
+
+/* virtual */ nscoord
+nsFlexContainerFrame::GetMinISize(nsRenderingContext* aRenderingContext)
+{
+ nscoord minISize = 0;
+ DISPLAY_MIN_WIDTH(this, minISize);
+
+ // TODO: See bug 1454822
+ minISize = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::MIN_ISIZE);
+
return minISize;
}
@@ -4853,24 +4978,8 @@ nsFlexContainerFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
nscoord prefISize = 0;
DISPLAY_PREF_WIDTH(this, prefISize);
- RenumberList();
-
- // XXXdholbert Optimization: We could cache our intrinsic widths like
- // nsBlockFrame does (and return it early from this function if it's set).
- // Whenever anything happens that might change it, set it to
- // NS_INTRINSIC_WIDTH_UNKNOWN (like nsBlockFrame::MarkIntrinsicISizesDirty
- // does)
- const FlexboxAxisTracker axisTracker(this, GetWritingMode());
+ // TODO: See bug 1454822
+ prefISize = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::PREF_ISIZE);
- for (nsIFrame* childFrame : mFrames) {
- nscoord childPrefISize =
- nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
- nsLayoutUtils::PREF_ISIZE);
- if (axisTracker.IsRowOriented()) {
- prefISize += childPrefISize;
- } else {
- prefISize = std::max(prefISize, childPrefISize);
- }
- }
return prefISize;
}
diff --git a/layout/generic/nsFlexContainerFrame.h b/layout/generic/nsFlexContainerFrame.h
index 9edcb87974..39de79b577 100644
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -72,6 +72,9 @@ public:
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) override;
+ nscoord GetIntrinsicISize(nsRenderingContext* aRenderingContext,
+ nsLayoutUtils::IntrinsicISizeType aType);
+
virtual nscoord
GetMinISize(nsRenderingContext* aRenderingContext) override;
virtual nscoord
@@ -160,7 +163,9 @@ protected:
nscoord aContentBoxMainSize,
nscoord aAvailableBSizeForContent,
nsTArray<StrutInfo>& aStruts,
- const FlexboxAxisTracker& aAxisTracker);
+ const FlexboxAxisTracker& aAxisTracker,
+ nscoord aMainGapSize,
+ nscoord aCrossGapSize);
/**
* Checks whether our child-frame list "mFrames" is sorted, using the given
@@ -244,6 +249,7 @@ protected:
nscoord aAvailableBSizeForContent,
const nsTArray<StrutInfo>& aStruts,
const FlexboxAxisTracker& aAxisTracker,
+ nscoord aMainGapSize,
nsTArray<nsIFrame*>& aPlaceholders,
mozilla::LinkedList<FlexLine>& aLines);