Bug 1123133 - Vertical writing-mode support in nsFieldSetFrame. r=smontagu

This commit is contained in:
Jonathan Kew 2015-01-19 11:43:58 +00:00
parent 682d07b50e
commit c45717c168
2 changed files with 123 additions and 90 deletions

View File

@ -36,6 +36,7 @@ NS_IMPL_FRAMEARENA_HELPERS(nsFieldSetFrame)
nsFieldSetFrame::nsFieldSetFrame(nsStyleContext* aContext)
: nsContainerFrame(aContext)
, mLegendRect(GetWritingMode())
{
mLegendSpace = 0;
}
@ -49,14 +50,17 @@ nsFieldSetFrame::GetType() const
nsRect
nsFieldSetFrame::VisualBorderRectRelativeToSelf() const
{
nscoord topBorder = StyleBorder()->GetComputedBorderWidth(NS_SIDE_TOP);
nsRect r(nsPoint(0,0), GetSize());
if (topBorder < mLegendRect.height) {
nscoord yoff = (mLegendRect.height - topBorder) / 2;
r.y += yoff;
r.height -= yoff;
WritingMode wm = GetWritingMode();
css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
nscoord legendBorder = StyleBorder()->GetComputedBorderWidth(legendSide);
LogicalRect r(wm, LogicalPoint(wm, 0, 0), GetLogicalSize(wm));
nscoord containerWidth = r.Width(wm);
if (legendBorder < mLegendRect.BSize(wm)) {
nscoord off = (mLegendRect.BSize(wm) - legendBorder) / 2;
r.BStart(wm) += off;
r.BSize(wm) -= off;
}
return r;
return r.GetPhysicalRect(wm, containerWidth);
}
nsIFrame*
@ -193,11 +197,12 @@ nsFieldSetFrame::PaintBorderBackground(nsRenderingContext& aRenderingContext,
nsPoint aPt, const nsRect& aDirtyRect, uint32_t aBGFlags)
{
// if the border is smaller than the legend. Move the border down
// to be centered on the legend.
// to be centered on the legend.
// FIXME: This means border-radius clamping is incorrect; we should
// override nsIFrame::GetBorderRadii.
WritingMode wm = GetWritingMode();
nsRect rect = VisualBorderRectRelativeToSelf();
nscoord yoff = rect.y;
nscoord off = wm.IsVertical() ? rect.x : rect.y;
rect += aPt;
nsPresContext* presContext = PresContext();
@ -207,57 +212,59 @@ nsFieldSetFrame::PaintBorderBackground(nsRenderingContext& aRenderingContext,
nsCSSRendering::PaintBoxShadowInner(presContext, aRenderingContext,
this, rect, aDirtyRect);
if (nsIFrame* legend = GetLegend()) {
nscoord topBorder = StyleBorder()->GetComputedBorderWidth(NS_SIDE_TOP);
if (nsIFrame* legend = GetLegend()) {
css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
nscoord legendBorderWidth =
StyleBorder()->GetComputedBorderWidth(legendSide);
// Use the rect of the legend frame, not mLegendRect, so we draw our
// border under the legend's left and right margins.
nsRect legendRect = legend->GetRect() + aPt;
// we should probably use PaintBorderEdges to do this but for now just use clipping
// to achieve the same effect.
// draw left side
nsRect clipRect(rect);
clipRect.width = legendRect.x - rect.x;
clipRect.height = topBorder;
// border under the legend's inline-start and -end margins.
LogicalRect legendRect(wm, legend->GetRect() + aPt, rect.width);
// Compute clipRect using logical coordinates, so that the legend space
// will be clipped out of the appropriate physical side depending on mode.
LogicalRect clipRect = LogicalRect(wm, rect, rect.width);
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
gfxContext* gfx = aRenderingContext.ThebesContext();
int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
// draw inline-start portion of the block-start side of the border
clipRect.ISize(wm) = legendRect.IStart(wm) - clipRect.IStart(wm);
clipRect.BSize(wm) = legendBorderWidth;
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, *drawTarget));
gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.width),
appUnitsPerDevPixel, *drawTarget));
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext);
gfx->Restore();
// draw right side
clipRect = rect;
clipRect.x = legendRect.XMost();
clipRect.width = rect.XMost() - legendRect.XMost();
clipRect.height = topBorder;
// draw inline-end portion of the block-start side of the border
clipRect = LogicalRect(wm, rect, rect.width);
clipRect.ISize(wm) = clipRect.IEnd(wm) - legendRect.IEnd(wm);
clipRect.IStart(wm) = legendRect.IEnd(wm);
clipRect.BSize(wm) = legendBorderWidth;
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, *drawTarget));
gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.width),
appUnitsPerDevPixel, *drawTarget));
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext);
gfx->Restore();
// draw bottom
clipRect = rect;
clipRect.y += topBorder;
clipRect.height = mRect.height - (yoff + topBorder);
// draw remainder of the border (omitting the block-start side)
clipRect = LogicalRect(wm, rect, rect.width);
clipRect.BStart(wm) += legendBorderWidth;
clipRect.BSize(wm) =
GetLogicalRect(rect.width).BSize(wm) - (off + legendBorderWidth);
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, *drawTarget));
gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.width),
appUnitsPerDevPixel, *drawTarget));
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext);
gfx->Restore();
} else {
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect,
nsRect(aPt, mRect.Size()),
@ -385,8 +392,9 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
// need logic here to push and pull overflow frames.
// Since we're not applying our padding in this frame, we need to add it here
// to compute the available width for our children.
WritingMode innerWM = inner ? inner->GetWritingMode() : GetWritingMode();
WritingMode legendWM = legend ? legend->GetWritingMode() : GetWritingMode();
WritingMode wm = GetWritingMode();
WritingMode innerWM = inner ? inner->GetWritingMode() : wm;
WritingMode legendWM = legend ? legend->GetWritingMode() : wm;
LogicalSize innerAvailSize = aReflowState.ComputedSizeWithPadding(innerWM);
LogicalSize legendAvailSize = aReflowState.ComputedSizeWithPadding(legendWM);
innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) =
@ -405,11 +413,13 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
"Bogus availSize.ISize; should be bigger");
// get our border and padding
nsMargin border = aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding();
nsMargin border = aReflowState.ComputedPhysicalBorderPadding() -
aReflowState.ComputedPhysicalPadding();
LogicalMargin logBorder(wm, border);
// Figure out how big the legend is if there is one.
// Figure out how big the legend is if there is one.
// get the legend's margin
nsMargin legendMargin(0,0,0,0);
LogicalMargin legendMargin(wm);
// reflow the legend only if needed
Maybe<nsHTMLReflowState> legendReflowState;
if (legend) {
@ -426,36 +436,37 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
legendDesiredSize.Width(), legendDesiredSize.Height());
#endif
// figure out the legend's rectangle
legendMargin = legend->GetUsedMargin();
mLegendRect.width = legendDesiredSize.Width() + legendMargin.left + legendMargin.right;
mLegendRect.height = legendDesiredSize.Height() + legendMargin.top + legendMargin.bottom;
mLegendRect.x = 0;
mLegendRect.y = 0;
legendMargin = legend->GetLogicalUsedMargin(wm);
mLegendRect =
LogicalRect(wm, 0, 0,
legendDesiredSize.ISize(wm) + legendMargin.IStartEnd(wm),
legendDesiredSize.BSize(wm) + legendMargin.BStartEnd(wm));
nscoord oldSpace = mLegendSpace;
mLegendSpace = 0;
if (mLegendRect.height > border.top) {
if (mLegendRect.BSize(wm) > logBorder.BStart(wm)) {
// center the border on the legend
mLegendSpace = mLegendRect.height - border.top;
mLegendSpace = mLegendRect.BSize(wm) - logBorder.BStart(wm);
} else {
mLegendRect.y = (border.top - mLegendRect.height)/2;
mLegendRect.BStart(wm) =
(logBorder.BStart(wm) - mLegendRect.BSize(wm)) / 2;
}
// if the legend space changes then we need to reflow the
// if the legend space changes then we need to reflow the
// content area as well.
if (mLegendSpace != oldSpace && inner) {
reflowInner = true;
}
// We'll move the legend to its proper place later.
FinishReflowChild(legend, aPresContext, legendDesiredSize,
legendReflowState.ptr(), 0, 0, NS_FRAME_NO_MOVE_FRAME);
legendReflowState.ptr(), 0, 0, NS_FRAME_NO_MOVE_FRAME);
} else if (!legend) {
mLegendRect.SetEmpty();
mLegendSpace = 0;
} else {
// mLegendSpace and mLegendRect haven't changed, but we need
// the used margin when placing the legend.
legendMargin = legend->GetUsedMargin();
legendMargin = legend->GetLogicalUsedMargin(wm);
}
// reflow the content frame only if needed
@ -489,7 +500,12 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
// Reflow the frame
NS_ASSERTION(kidReflowState.ComputedPhysicalMargin() == nsMargin(0,0,0,0),
"Margins on anonymous fieldset child not supported!");
nsPoint pt(border.left, border.top + mLegendSpace);
nsPoint pt(border.left, border.top);
if (wm.IsVerticalLR()) {
pt.x += mLegendSpace;
} else if (!wm.IsVertical()) {
pt.y += mLegendSpace;
}
ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState,
pt.x, pt.y, 0, aStatus);
@ -498,72 +514,89 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus);
}
LogicalRect contentRect(innerWM);
nscoord containerWidth =
(wm.IsVertical() ? mLegendSpace : 0) +
logBorder.LeftRight(wm) + (inner ? inner->GetSize().width : 0);
LogicalRect contentRect(wm);
if (inner) {
// We don't support margins on inner, so our content rect is just the
// inner's border-box.
contentRect = inner->GetLogicalRect(aReflowState.ComputedWidth());
// inner's border-box. We don't care about container-width at this point,
// as we'll figure out the actual positioning later.
contentRect = inner->GetLogicalRect(wm, containerWidth);
}
// Our content rect must fill up the available width
if (innerAvailSize.ISize(innerWM) > contentRect.ISize(innerWM)) {
contentRect.ISize(innerWM) = innerAvailSize.ISize(innerWM);
LogicalSize availSize = aReflowState.ComputedSizeWithPadding(wm);
if (availSize.ISize(wm) > contentRect.ISize(wm)) {
contentRect.ISize(wm) = innerAvailSize.ISize(wm);
}
//XXX temporary!
nsRect physicalContentRect =
contentRect.GetPhysicalRect(innerWM, aReflowState.ComputedWidth());
if (legend) {
// the legend is postioned horizontally within the inner's content rect
// The legend is positioned inline-wards within the inner's content rect
// (so that padding on the fieldset affects the legend position).
nsRect innerContentRect = physicalContentRect;
innerContentRect.Deflate(aReflowState.ComputedPhysicalPadding());
// if the inner content rect is larger than the legend, we can align the legend
if (innerContentRect.width > mLegendRect.width) {
LogicalRect innerContentRect = contentRect;
innerContentRect.Deflate(wm, aReflowState.ComputedLogicalPadding());
// If the inner content rect is larger than the legend, we can align the
// legend.
if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) {
int32_t align = static_cast<nsLegendFrame*>
(legend->GetContentInsertionFrame())->GetAlign();
if (!wm.IsBidiLTR()) {
if (align == NS_STYLE_TEXT_ALIGN_LEFT ||
align == NS_STYLE_TEXT_ALIGN_MOZ_LEFT) {
align = NS_STYLE_TEXT_ALIGN_END;
} else if (align == NS_STYLE_TEXT_ALIGN_RIGHT ||
align == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) {
align = NS_STYLE_TEXT_ALIGN_DEFAULT;
}
}
switch (align) {
case NS_STYLE_TEXT_ALIGN_RIGHT:
mLegendRect.x = innerContentRect.XMost() - mLegendRect.width;
case NS_STYLE_TEXT_ALIGN_END:
mLegendRect.IStart(wm) =
innerContentRect.IEnd(wm) - mLegendRect.ISize(wm);
break;
case NS_STYLE_TEXT_ALIGN_CENTER:
case NS_STYLE_TEXT_ALIGN_MOZ_CENTER:
// Note: rounding removed; there doesn't seem to be any need
mLegendRect.x = innerContentRect.width / 2 - mLegendRect.width / 2 + innerContentRect.x;
mLegendRect.IStart(wm) = innerContentRect.IStart(wm) +
(innerContentRect.ISize(wm) - mLegendRect.ISize(wm)) / 2;
break;
default:
mLegendRect.x = innerContentRect.x;
mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
break;
}
} else {
// otherwise make place for the legend
mLegendRect.x = innerContentRect.x;
innerContentRect.width = mLegendRect.width;
physicalContentRect.width = mLegendRect.width +
aReflowState.ComputedPhysicalPadding().LeftRight();
mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
innerContentRect.ISize(wm) = mLegendRect.ISize(wm);
contentRect.ISize(wm) = mLegendRect.ISize(wm) +
aReflowState.ComputedLogicalPadding().IStartEnd(wm);
}
// place the legend
nsRect actualLegendRect(mLegendRect);
actualLegendRect.Deflate(legendMargin);
nsPoint actualLegendPos(actualLegendRect.TopLeft());
legendReflowState->ApplyRelativePositioning(&actualLegendPos);
legend->SetPosition(actualLegendPos);
LogicalRect actualLegendRect = mLegendRect;
actualLegendRect.Deflate(wm, legendMargin);
LogicalPoint actualLegendPos(actualLegendRect.Origin(wm));
legendReflowState->ApplyRelativePositioning(&actualLegendPos, containerWidth);
legend->SetPosition(wm, actualLegendPos, containerWidth);
nsContainerFrame::PositionFrameView(legend);
nsContainerFrame::PositionChildViews(legend);
}
// Return our size and our result.
WritingMode wm = aReflowState.GetWritingMode();
nsSize finalSize(physicalContentRect.width + border.LeftRight(),
mLegendSpace + border.TopBottom() +
(inner ? inner->GetRect().height : 0));
aDesiredSize.SetSize(wm, LogicalSize(wm, finalSize));
LogicalSize finalSize(wm, contentRect.ISize(wm) + logBorder.IStartEnd(wm),
mLegendSpace + logBorder.BStartEnd(wm) +
(inner ? inner->GetLogicalSize(wm).BSize(wm) : 0));
aDesiredSize.SetSize(wm, finalSize);
aDesiredSize.SetOverflowAreasToDesiredBounds();
if (legend)
if (legend) {
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend);
if (inner)
}
if (inner) {
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner);
}
// Merge overflow container bounds and status.
aDesiredSize.mOverflowAreas.UnionWith(ocBounds);

View File

@ -99,7 +99,7 @@ public:
nsIFrame* GetLegend() const;
protected:
nsRect mLegendRect;
mozilla::LogicalRect mLegendRect;
nscoord mLegendSpace;
};