mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-27 20:25:44 +00:00
Bug 1151213 part 1 - [css-grid][css-align] Implement layout for the 'align-self' and 'justify-self' properties on grid items. r=dholbert
This commit is contained in:
parent
6f1fa89293
commit
8abe331366
@ -829,6 +829,243 @@ GetDisplayFlagsForGridItem(nsIFrame* aFrame)
|
||||
return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
|
||||
}
|
||||
|
||||
static nscoord
|
||||
SpaceToFill(WritingMode aWM, const LogicalSize& aSize, nscoord aMargin,
|
||||
LogicalAxis aAxis, nscoord aCBSize)
|
||||
{
|
||||
nscoord size = aAxis == eLogicalAxisBlock ? aSize.BSize(aWM)
|
||||
: aSize.ISize(aWM);
|
||||
return aCBSize - (size + aMargin);
|
||||
}
|
||||
|
||||
static bool
|
||||
AlignJustifySelf(uint8_t aAlignment, bool aOverflowSafe, LogicalAxis aAxis,
|
||||
bool aSameSide, nscoord aCBSize, const nsHTMLReflowState& aRS,
|
||||
const LogicalSize& aChildSize, LogicalSize* aContentSize,
|
||||
LogicalPoint* aPos)
|
||||
{
|
||||
MOZ_ASSERT(aAlignment != NS_STYLE_ALIGN_AUTO, "unexpected 'auto' "
|
||||
"computed value for normal flow grid item");
|
||||
MOZ_ASSERT(aAlignment != NS_STYLE_ALIGN_LEFT &&
|
||||
aAlignment != NS_STYLE_ALIGN_RIGHT,
|
||||
"caller should map that to the corresponding START/END");
|
||||
|
||||
// Map some alignment values to 'start' / 'end'.
|
||||
switch (aAlignment) {
|
||||
case NS_STYLE_ALIGN_SELF_START: // align/justify-self: self-start
|
||||
aAlignment = MOZ_LIKELY(aSameSide) ? NS_STYLE_ALIGN_START
|
||||
: NS_STYLE_ALIGN_END;
|
||||
break;
|
||||
case NS_STYLE_ALIGN_SELF_END: // align/justify-self: self-end
|
||||
aAlignment = MOZ_LIKELY(aSameSide) ? NS_STYLE_ALIGN_END
|
||||
: NS_STYLE_ALIGN_START;
|
||||
break;
|
||||
case NS_STYLE_ALIGN_FLEX_START: // same as 'start' for Grid
|
||||
aAlignment = NS_STYLE_ALIGN_START;
|
||||
break;
|
||||
case NS_STYLE_ALIGN_FLEX_END: // same as 'end' for Grid
|
||||
aAlignment = NS_STYLE_ALIGN_END;
|
||||
break;
|
||||
}
|
||||
|
||||
// XXX try to condense this code a bit by adding the necessary convenience
|
||||
// methods? (bug 1209710)
|
||||
|
||||
// Get the item's margin corresponding to the container's start/end side.
|
||||
const LogicalMargin margin = aRS.ComputedLogicalMargin();
|
||||
WritingMode wm = aRS.GetWritingMode();
|
||||
nscoord marginStart, marginEnd;
|
||||
if (aAxis == eLogicalAxisBlock) {
|
||||
if (MOZ_LIKELY(aSameSide)) {
|
||||
marginStart = margin.BStart(wm);
|
||||
marginEnd = margin.BEnd(wm);
|
||||
} else {
|
||||
marginStart = margin.BEnd(wm);
|
||||
marginEnd = margin.BStart(wm);
|
||||
}
|
||||
} else {
|
||||
if (MOZ_LIKELY(aSameSide)) {
|
||||
marginStart = margin.IStart(wm);
|
||||
marginEnd = margin.IEnd(wm);
|
||||
} else {
|
||||
marginStart = margin.IEnd(wm);
|
||||
marginEnd = margin.IStart(wm);
|
||||
}
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-align-3/#overflow-values
|
||||
// This implements <overflow-position> = 'safe'.
|
||||
if (MOZ_UNLIKELY(aOverflowSafe) && aAlignment != NS_STYLE_ALIGN_START) {
|
||||
nscoord space = SpaceToFill(wm, aChildSize, marginStart + marginEnd,
|
||||
aAxis, aCBSize);
|
||||
// XXX we might want to include == 0 here as an optimization -
|
||||
// I need to see what the baseline/last-baseline code looks like first.
|
||||
if (space < 0) {
|
||||
aAlignment = NS_STYLE_ALIGN_START;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the position and size (aPos/aContentSize) for the requested alignment.
|
||||
bool didResize = false;
|
||||
nscoord offset = 0;
|
||||
switch (aAlignment) {
|
||||
case NS_STYLE_ALIGN_BASELINE:
|
||||
case NS_STYLE_ALIGN_LAST_BASELINE:
|
||||
NS_WARNING("NYI: baseline/last-baseline for grid (bug 1151204)"); // XXX
|
||||
case NS_STYLE_ALIGN_START:
|
||||
offset = marginStart;
|
||||
break;
|
||||
case NS_STYLE_ALIGN_END: {
|
||||
nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm)
|
||||
: aChildSize.ISize(wm);
|
||||
offset = aCBSize - (size + marginEnd);
|
||||
break;
|
||||
}
|
||||
case NS_STYLE_ALIGN_CENTER:
|
||||
offset = SpaceToFill(wm, aChildSize, marginStart + marginEnd,
|
||||
aAxis, aCBSize) / 2;
|
||||
break;
|
||||
case NS_STYLE_ALIGN_STRETCH: {
|
||||
offset = marginStart;
|
||||
const auto& styleMargin = aRS.mStyleMargin->mMargin;
|
||||
if (aAxis == eLogicalAxisBlock
|
||||
? (aRS.mStylePosition->BSize(wm).GetUnit() == eStyleUnit_Auto &&
|
||||
styleMargin.GetBStartUnit(wm) != eStyleUnit_Auto &&
|
||||
styleMargin.GetBEndUnit(wm) != eStyleUnit_Auto)
|
||||
: (aRS.mStylePosition->ISize(wm).GetUnit() == eStyleUnit_Auto &&
|
||||
styleMargin.GetIStartUnit(wm) != eStyleUnit_Auto &&
|
||||
styleMargin.GetIEndUnit(wm) != eStyleUnit_Auto)) {
|
||||
nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm)
|
||||
: aChildSize.ISize(wm);
|
||||
nscoord gap = aCBSize - (size + marginStart + marginEnd);
|
||||
if (gap > 0) {
|
||||
// Note: The ComputedMax* values are always content-box max values,
|
||||
// even for box-sizing:border-box.
|
||||
LogicalMargin bp = aRS.ComputedLogicalBorderPadding();
|
||||
// XXX ApplySkipSides is probably not very useful here since we
|
||||
// might not have created any next-in-flow yet. Use the reflow status
|
||||
// instead? Do all fragments stretch? (bug 1144096).
|
||||
bp.ApplySkipSides(aRS.frame->GetLogicalSkipSides());
|
||||
nscoord bpInAxis = aAxis == eLogicalAxisBlock ? bp.BStartEnd(wm)
|
||||
: bp.IStartEnd(wm);
|
||||
nscoord contentSize = size - bpInAxis;
|
||||
NS_ASSERTION(contentSize >= 0, "huh?");
|
||||
const nscoord unstretchedContentSize = contentSize;
|
||||
contentSize += gap;
|
||||
nscoord max = aAxis == eLogicalAxisBlock ? aRS.ComputedMaxBSize()
|
||||
: aRS.ComputedMaxISize();
|
||||
if (MOZ_UNLIKELY(contentSize > max)) {
|
||||
contentSize = max;
|
||||
gap = contentSize - unstretchedContentSize;
|
||||
}
|
||||
// |gap| is now how much the content size is actually allowed to grow.
|
||||
didResize = gap > 0;
|
||||
if (didResize) {
|
||||
(aAxis == eLogicalAxisBlock ? aContentSize->BSize(wm)
|
||||
: aContentSize->ISize(wm)) = contentSize;
|
||||
if (MOZ_UNLIKELY(!aSameSide)) {
|
||||
offset += gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unknown align-/justify-self value");
|
||||
}
|
||||
if (offset != 0) {
|
||||
nscoord& pos = aAxis == eLogicalAxisBlock ? aPos->B(wm) : aPos->I(wm);
|
||||
pos += MOZ_LIKELY(aSameSide) ? offset : -offset;
|
||||
}
|
||||
return didResize;
|
||||
}
|
||||
|
||||
static bool
|
||||
SameSide(WritingMode aContainerWM, LogicalSide aContainerSide,
|
||||
WritingMode aChildWM, LogicalSide aChildSide)
|
||||
{
|
||||
MOZ_ASSERT(aContainerWM.PhysicalAxis(GetAxis(aContainerSide)) ==
|
||||
aChildWM.PhysicalAxis(GetAxis(aChildSide)));
|
||||
return aContainerWM.PhysicalSide(aContainerSide) ==
|
||||
aChildWM.PhysicalSide(aChildSide);
|
||||
}
|
||||
|
||||
static Maybe<LogicalAxis>
|
||||
AlignSelf(uint8_t aAlignSelf, const LogicalRect& aCB, const WritingMode aCBWM,
|
||||
const nsHTMLReflowState& aRS, const LogicalSize& aSize,
|
||||
LogicalSize* aContentSize, LogicalPoint* aPos)
|
||||
{
|
||||
Maybe<LogicalAxis> resizedAxis;
|
||||
auto alignSelf = aAlignSelf;
|
||||
bool overflowSafe = alignSelf & NS_STYLE_ALIGN_SAFE;
|
||||
alignSelf &= ~NS_STYLE_ALIGN_FLAG_BITS;
|
||||
MOZ_ASSERT(alignSelf != NS_STYLE_ALIGN_LEFT &&
|
||||
alignSelf != NS_STYLE_ALIGN_RIGHT,
|
||||
"Grid's 'align-self' axis is never parallel to the container's "
|
||||
"inline axis, so these should've computed to 'start' already");
|
||||
if (MOZ_UNLIKELY(alignSelf == NS_STYLE_ALIGN_AUTO)) {
|
||||
// Happens in rare edge cases when 'position' was ignored by the frame
|
||||
// constructor (and the style system computed 'auto' based on 'position').
|
||||
alignSelf = NS_STYLE_ALIGN_STRETCH;
|
||||
}
|
||||
WritingMode childWM = aRS.GetWritingMode();
|
||||
bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
|
||||
// |sameSide| is true if the container's start side in this axis is the same
|
||||
// as the child's start side, in the child's parallel axis.
|
||||
bool sameSide = SameSide(aCBWM, eLogicalSideBStart,
|
||||
childWM, isOrthogonal ? eLogicalSideIStart
|
||||
: eLogicalSideBStart);
|
||||
LogicalAxis axis = isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock;
|
||||
if (AlignJustifySelf(alignSelf, overflowSafe, axis, sameSide,
|
||||
aCB.BSize(aCBWM), aRS, aSize, aContentSize, aPos)) {
|
||||
resizedAxis.emplace(axis);
|
||||
}
|
||||
return resizedAxis;
|
||||
}
|
||||
|
||||
static Maybe<LogicalAxis>
|
||||
JustifySelf(uint8_t aJustifySelf, const LogicalRect& aCB, const WritingMode aCBWM,
|
||||
const nsHTMLReflowState& aRS, const LogicalSize& aSize,
|
||||
LogicalSize* aContentSize, LogicalPoint* aPos)
|
||||
{
|
||||
Maybe<LogicalAxis> resizedAxis;
|
||||
auto justifySelf = aJustifySelf;
|
||||
bool overflowSafe = justifySelf & NS_STYLE_JUSTIFY_SAFE;
|
||||
justifySelf &= ~NS_STYLE_JUSTIFY_FLAG_BITS;
|
||||
if (MOZ_UNLIKELY(justifySelf == NS_STYLE_ALIGN_AUTO)) {
|
||||
// Happens in rare edge cases when 'position' was ignored by the frame
|
||||
// constructor (and the style system computed 'auto' based on 'position').
|
||||
justifySelf = NS_STYLE_ALIGN_STRETCH;
|
||||
}
|
||||
WritingMode childWM = aRS.GetWritingMode();
|
||||
bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
|
||||
// |sameSide| is true if the container's start side in this axis is the same
|
||||
// as the child's start side, in the child's parallel axis.
|
||||
bool sameSide = SameSide(aCBWM, eLogicalSideIStart,
|
||||
childWM, isOrthogonal ? eLogicalSideBStart
|
||||
: eLogicalSideIStart);
|
||||
// Grid's 'justify-self' axis is always parallel to the container's inline
|
||||
// axis, so justify-self:left|right always applies.
|
||||
switch (justifySelf) {
|
||||
case NS_STYLE_JUSTIFY_LEFT:
|
||||
justifySelf = aCBWM.IsBidiLTR() ? NS_STYLE_JUSTIFY_START
|
||||
: NS_STYLE_JUSTIFY_END;
|
||||
break;
|
||||
case NS_STYLE_JUSTIFY_RIGHT:
|
||||
justifySelf = aCBWM.IsBidiLTR() ? NS_STYLE_JUSTIFY_END
|
||||
: NS_STYLE_JUSTIFY_START;
|
||||
break;
|
||||
}
|
||||
|
||||
LogicalAxis axis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
|
||||
if (AlignJustifySelf(justifySelf, overflowSafe, axis, sameSide,
|
||||
aCB.ISize(aCBWM), aRS, aSize, aContentSize, aPos)) {
|
||||
resizedAxis.emplace(axis);
|
||||
}
|
||||
return resizedAxis;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// Frame class boilerplate
|
||||
@ -2367,6 +2604,7 @@ nsGridContainerFrame::ReflowChildren(GridReflowState& aState,
|
||||
(aContentArea.Size(wm) +
|
||||
aState.mReflowState->ComputedLogicalBorderPadding().Size(wm)).GetPhysicalSize(wm);
|
||||
nsPresContext* pc = PresContext();
|
||||
nsStyleContext* containerSC = StyleContext();
|
||||
for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
|
||||
nsIFrame* child = *aState.mIter;
|
||||
const bool isGridItem = child->GetType() != nsGkAtoms::placeholderFrame;
|
||||
@ -2383,36 +2621,63 @@ nsGridContainerFrame::ReflowChildren(GridReflowState& aState,
|
||||
}
|
||||
WritingMode childWM = child->GetWritingMode();
|
||||
LogicalSize childCBSize = cb.Size(wm).ConvertTo(childWM, wm);
|
||||
nsHTMLReflowState childRS(pc, *aState.mReflowState, child, childCBSize);
|
||||
const LogicalMargin margin = childRS.ComputedLogicalMargin();
|
||||
if (childRS.ComputedBSize() == NS_AUTOHEIGHT && MOZ_LIKELY(isGridItem)) {
|
||||
// XXX the start of an align-self:stretch impl. Needs min-/max-bsize
|
||||
// clamping though, and check the prop value is actually 'stretch'!
|
||||
LogicalMargin bp = childRS.ComputedLogicalBorderPadding();
|
||||
bp.ApplySkipSides(child->GetLogicalSkipSides());
|
||||
nscoord bSize = childCBSize.BSize(childWM) - bp.BStartEnd(childWM) -
|
||||
margin.BStartEnd(childWM);
|
||||
childRS.SetComputedBSize(std::max(bSize, 0));
|
||||
}
|
||||
// XXX temporary workaround to avoid being INCOMPLETE until we have
|
||||
// support for fragmentation (bug 1144096)
|
||||
childCBSize.BSize(childWM) = NS_UNCONSTRAINEDSIZE;
|
||||
|
||||
Maybe<nsHTMLReflowState> childRS; // Maybe<> so we can reuse the space
|
||||
childRS.emplace(pc, *aState.mReflowState, child, childCBSize);
|
||||
// We need the width of the child before we can correctly convert
|
||||
// the writing-mode of its origin, so we reflow at (0, 0) using a dummy
|
||||
// containerSize, and then pass the correct position to FinishReflowChild.
|
||||
nsHTMLReflowMetrics childSize(childRS);
|
||||
Maybe<nsHTMLReflowMetrics> childSize; // Maybe<> so we can reuse the space
|
||||
childSize.emplace(*childRS);
|
||||
nsReflowStatus childStatus;
|
||||
const nsSize dummyContainerSize;
|
||||
ReflowChild(child, pc, childSize, childRS, childWM, LogicalPoint(childWM),
|
||||
ReflowChild(child, pc, *childSize, *childRS, childWM, LogicalPoint(childWM),
|
||||
dummyContainerSize, 0, childStatus);
|
||||
LogicalPoint childPos =
|
||||
cb.Origin(wm).ConvertTo(childWM, wm,
|
||||
containerSize - childSize.PhysicalSize() -
|
||||
margin.Size(childWM).GetPhysicalSize(childWM));
|
||||
childPos.I(childWM) += margin.IStart(childWM);
|
||||
childPos.B(childWM) += margin.BStart(childWM);
|
||||
childRS.ApplyRelativePositioning(&childPos, containerSize);
|
||||
FinishReflowChild(child, pc, childSize, &childRS, childWM, childPos,
|
||||
containerSize - childSize->PhysicalSize());
|
||||
// Apply align/justify-self and reflow again if that affects the size.
|
||||
if (isGridItem) {
|
||||
LogicalSize oldSize = childSize->Size(childWM); // from the ReflowChild()
|
||||
LogicalSize newContentSize(childWM);
|
||||
auto align = childRS->mStylePosition->ComputedAlignSelf(
|
||||
childRS->mStyleDisplay, containerSC);
|
||||
Maybe<LogicalAxis> alignResize =
|
||||
AlignSelf(align, cb, wm, *childRS, oldSize, &newContentSize, &childPos);
|
||||
auto justify = childRS->mStylePosition->ComputedJustifySelf(
|
||||
childRS->mStyleDisplay, containerSC);
|
||||
Maybe<LogicalAxis> justifyResize =
|
||||
JustifySelf(justify, cb, wm, *childRS, oldSize, &newContentSize, &childPos);
|
||||
if (alignResize || justifyResize) {
|
||||
FinishReflowChild(child, pc, *childSize, childRS.ptr(), childWM,
|
||||
LogicalPoint(childWM), containerSize,
|
||||
NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW);
|
||||
childSize.reset(); // In reverse declaration order since it runs
|
||||
childRS.reset(); // destructors.
|
||||
childRS.emplace(pc, *aState.mReflowState, child, childCBSize);
|
||||
if ((alignResize && alignResize.value() == eLogicalAxisBlock) ||
|
||||
(justifyResize && justifyResize.value() == eLogicalAxisBlock)) {
|
||||
childRS->SetComputedBSize(newContentSize.BSize(childWM));
|
||||
childRS->SetBResize(true);
|
||||
}
|
||||
if ((alignResize && alignResize.value() == eLogicalAxisInline) ||
|
||||
(justifyResize && justifyResize.value() == eLogicalAxisInline)) {
|
||||
childRS->SetComputedISize(newContentSize.ISize(childWM));
|
||||
childRS->SetIResize(true);
|
||||
}
|
||||
childSize.emplace(*childRS);
|
||||
ReflowChild(child, pc, *childSize, *childRS, childWM,
|
||||
LogicalPoint(childWM), dummyContainerSize, 0, childStatus);
|
||||
}
|
||||
}
|
||||
childRS->ApplyRelativePositioning(&childPos, containerSize);
|
||||
FinishReflowChild(child, pc, *childSize, childRS.ptr(), childWM, childPos,
|
||||
containerSize, 0);
|
||||
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child);
|
||||
// XXX deal with 'childStatus' not being COMPLETE
|
||||
// XXX deal with 'childStatus' not being COMPLETE (bug 1144096)
|
||||
}
|
||||
|
||||
if (IsAbsoluteContainer()) {
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "nsFontMetrics.h"
|
||||
#include "nsBlockFrame.h"
|
||||
#include "nsLineBox.h"
|
||||
#include "nsFlexContainerFrame.h"
|
||||
#include "nsImageFrame.h"
|
||||
#include "nsTableFrame.h"
|
||||
#include "nsTableCellFrame.h"
|
||||
@ -2037,18 +2036,6 @@ IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay,
|
||||
captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
|
||||
}
|
||||
|
||||
static nsFlexContainerFrame*
|
||||
GetFlexContainer(nsIFrame* aFrame)
|
||||
{
|
||||
nsIFrame* parent = aFrame->GetParent();
|
||||
if (!parent ||
|
||||
parent->GetType() != nsGkAtoms::flexContainerFrame) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return static_cast<nsFlexContainerFrame*>(parent);
|
||||
}
|
||||
|
||||
// Flex items resolve block-axis percentage margin & padding against the flex
|
||||
// container's block-size (which is the containing block block-size).
|
||||
// For everything else: the CSS21 spec requires that margin and padding
|
||||
@ -2306,8 +2293,9 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
|
||||
ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
|
||||
}
|
||||
|
||||
const nsFlexContainerFrame* flexContainerFrame = GetFlexContainer(frame);
|
||||
if (flexContainerFrame) {
|
||||
nsIFrame* parent = frame->GetParent();
|
||||
nsIAtom* parentFrameType = parent ? parent->GetType() : nullptr;
|
||||
if (parentFrameType == nsGkAtoms::flexContainerFrame) {
|
||||
computeSizeFlags =
|
||||
ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
|
||||
|
||||
@ -2343,11 +2331,13 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
|
||||
NS_ASSERTION(ComputedBSize() == NS_UNCONSTRAINEDSIZE ||
|
||||
ComputedBSize() >= 0, "Bogus block-size");
|
||||
|
||||
// Exclude inline tables and flex items from the block margin calculations
|
||||
// Exclude inline tables, side captions, flex and grid items from block
|
||||
// margin calculations.
|
||||
if (isBlock &&
|
||||
!IsSideCaption(frame, mStyleDisplay, cbwm) &&
|
||||
mStyleDisplay->mDisplay != NS_STYLE_DISPLAY_INLINE_TABLE &&
|
||||
!flexContainerFrame) {
|
||||
parentFrameType != nsGkAtoms::flexContainerFrame &&
|
||||
parentFrameType != nsGkAtoms::gridContainerFrame) {
|
||||
CalculateBlockSideMargins(aFrameType);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user