diff --git a/engine/core/dom/Element.cpp b/engine/core/dom/Element.cpp index 367843c5dd2b0fae2e187ba0c2067786f09e2888..02afe79cc179e34ad49ff2f247f7565b8afb1a28 100644 --- a/engine/core/dom/Element.cpp +++ b/engine/core/dom/Element.cpp @@ -975,14 +975,14 @@ void Element::setMaxContentWidth(double width) double Element::alphabeticBaseline() const { if (RenderBox* box = renderBox()) - return box->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOfInteriorLineBoxes); + return box->firstLineBoxBaseline(FontBaselineOrAuto(AlphabeticBaseline)); return 0; } double Element::ideographicBaseline() const { if (RenderBox* box = renderBox()) - return box->baselinePosition(IdeographicBaseline, true, HorizontalLine, PositionOfInteriorLineBoxes); + return box->firstLineBoxBaseline(FontBaselineOrAuto(IdeographicBaseline)); return 0; } diff --git a/engine/core/rendering/RenderBlock.cpp b/engine/core/rendering/RenderBlock.cpp index af07da2b312c2e7d3f323fd4ba0916d9a1e099ef..85e57b3c85c22d8564b7605be47d171fd6789bd4 100644 --- a/engine/core/rendering/RenderBlock.cpp +++ b/engine/core/rendering/RenderBlock.cpp @@ -1351,11 +1351,11 @@ LayoutUnit RenderBlock::minLineHeightForReplacedRenderer(bool isFirstLine, Layou return std::max(replacedHeight, lineHeight(isFirstLine, HorizontalLine, PositionOfInteriorLineBoxes)); } -int RenderBlock::firstLineBoxBaseline() const +int RenderBlock::firstLineBoxBaseline(FontBaselineOrAuto baselineType) const { for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) { if (!curr->isFloatingOrOutOfFlowPositioned()) { - int result = curr->firstLineBoxBaseline(); + int result = curr->firstLineBoxBaseline(baselineType); if (result != -1) return curr->logicalTop() + result; // Translate to our coordinate space. } diff --git a/engine/core/rendering/RenderBlock.h b/engine/core/rendering/RenderBlock.h index bac68462e7be6a0752610a294826985b865113da..44bf5896ac37385b12cd64eca2272acd27569394 100644 --- a/engine/core/rendering/RenderBlock.h +++ b/engine/core/rendering/RenderBlock.h @@ -216,7 +216,7 @@ protected: virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const override; virtual void computePreferredLogicalWidths() override; - virtual int firstLineBoxBaseline() const override; + virtual int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const override; virtual int inlineBlockBaseline(LineDirectionMode) const override; virtual int lastLineBoxBaseline(LineDirectionMode) const; diff --git a/engine/core/rendering/RenderBox.h b/engine/core/rendering/RenderBox.h index 2304e9b79ef326f52692f3ae5ba6a38e106e2d5b..5d27682ba3b609eea92f782a3ac691c6e9577c6c 100644 --- a/engine/core/rendering/RenderBox.h +++ b/engine/core/rendering/RenderBox.h @@ -66,6 +66,21 @@ enum LayerType { OverflowClipLayer, }; +struct FontBaselineOrAuto { + FontBaselineOrAuto() + : m_auto(true) + , m_baseline(AlphabeticBaseline) + { + } + FontBaselineOrAuto(FontBaseline baseline) + : m_auto(false) + , m_baseline(baseline) + { + } + bool m_auto; + FontBaseline m_baseline; +}; + class RenderBox : public RenderBoxModelObject { public: explicit RenderBox(ContainerNode*); @@ -429,7 +444,7 @@ public: RenderLayer* enclosingFloatPaintingLayer() const; - virtual int firstLineBoxBaseline() const { return -1; } + virtual int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const { return -1; } virtual int inlineBlockBaseline(LineDirectionMode) const { return -1; } // Returns -1 if we should skip this box when computing the baseline of an inline-block. bool isFlexItem() const { return !isInline() && !isFloatingOrOutOfFlowPositioned() && parent() && parent()->isFlexibleBox(); } diff --git a/engine/core/rendering/RenderFlexibleBox.cpp b/engine/core/rendering/RenderFlexibleBox.cpp index bfd8e9775d37444bfce59cd9ec3aa609c52ceca1..629f1c001c9281a008012d8b57e62343f4de6781 100644 --- a/engine/core/rendering/RenderFlexibleBox.cpp +++ b/engine/core/rendering/RenderFlexibleBox.cpp @@ -119,14 +119,14 @@ static int synthesizedBaselineFromContentBox(const RenderBox* box, LineDirection int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode mode) const { ASSERT(mode == PositionOnContainingLine); - int baseline = firstLineBoxBaseline(); + int baseline = firstLineBoxBaseline(FontBaselineOrAuto()); if (baseline == -1) baseline = synthesizedBaselineFromContentBox(this, direction); return beforeMarginInLineDirection(direction) + baseline; } -int RenderFlexibleBox::firstLineBoxBaseline() const +int RenderFlexibleBox::firstLineBoxBaseline(FontBaselineOrAuto baselineType) const { if (m_numberOfInFlowChildrenOnFirstLine <= 0) return -1; @@ -155,7 +155,7 @@ int RenderFlexibleBox::firstLineBoxBaseline() const if (isColumnFlow() && !hasOrthogonalFlow(baselineChild)) return mainAxisExtentForChild(baselineChild) + baselineChild->logicalTop(); - int baseline = baselineChild->firstLineBoxBaseline(); + int baseline = baselineChild->firstLineBoxBaseline(baselineType); if (baseline == -1) { // FIXME: We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root. // This would also fix some cases where the flexbox is orthogonal to its container. @@ -168,7 +168,7 @@ int RenderFlexibleBox::firstLineBoxBaseline() const int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const { - int baseline = firstLineBoxBaseline(); + int baseline = firstLineBoxBaseline(FontBaselineOrAuto()); if (baseline != -1) return baseline; @@ -680,7 +680,7 @@ bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox* child, LayoutUni LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(RenderBox* child) { - LayoutUnit ascent = child->firstLineBoxBaseline(); + LayoutUnit ascent = child->firstLineBoxBaseline(FontBaselineOrAuto()); if (ascent == -1) ascent = crossAxisExtentForChild(child); return ascent + flowAwareMarginBeforeForChild(child); diff --git a/engine/core/rendering/RenderFlexibleBox.h b/engine/core/rendering/RenderFlexibleBox.h index 9d935007d091d9a169402c846005395128e57bae..0bca6796ad372f9207ea1604e8dae763a4f66419 100644 --- a/engine/core/rendering/RenderFlexibleBox.h +++ b/engine/core/rendering/RenderFlexibleBox.h @@ -47,7 +47,7 @@ public: void layout(); virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const override; - virtual int firstLineBoxBaseline() const override; + virtual int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const override; virtual int inlineBlockBaseline(LineDirectionMode) const override; virtual void paintChildren(PaintInfo&, const LayoutPoint&, Vector& layers) override final; diff --git a/engine/core/rendering/RenderParagraph.cpp b/engine/core/rendering/RenderParagraph.cpp index cde5a014399b4afce05935ddf253905c59d74a9d..5b2457ea4ed0028a52a51c80ecf651d00cc16348 100644 --- a/engine/core/rendering/RenderParagraph.cpp +++ b/engine/core/rendering/RenderParagraph.cpp @@ -1356,9 +1356,16 @@ void RenderParagraph::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); } -int RenderParagraph::firstLineBoxBaseline() const +int RenderParagraph::firstLineBoxBaseline(FontBaselineOrAuto baselineType) const { - return firstLineBox() ? firstLineBox()->logicalTop() + style(true)->fontMetrics().ascent(firstRootBox()->baselineType()) : -1; + if (!firstLineBox()) + return -1; + FontBaseline baseline; + if (baselineType.m_auto) + baseline = firstRootBox()->baselineType(); + else + baseline = baselineType.m_baseline; + return firstLineBox()->logicalTop() + style(true)->fontMetrics().ascent(baseline); } int RenderParagraph::lastLineBoxBaseline(LineDirectionMode lineDirection) const diff --git a/engine/core/rendering/RenderParagraph.h b/engine/core/rendering/RenderParagraph.h index d7015634ba4d1e62aa4b6911061a9cad57e2d0c1..3d942c7319927bac2dd2e295b170731d2cb4dde5 100644 --- a/engine/core/rendering/RenderParagraph.h +++ b/engine/core/rendering/RenderParagraph.h @@ -74,7 +74,7 @@ protected: void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const final; - int firstLineBoxBaseline() const final; + int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const final; int lastLineBoxBaseline(LineDirectionMode) const final; private: diff --git a/examples/rendering/baseline.dart b/examples/rendering/baseline.dart new file mode 100644 index 0000000000000000000000000000000000000000..2c52239597d987870f5f552bae8128530659c818 --- /dev/null +++ b/examples/rendering/baseline.dart @@ -0,0 +1,78 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:sky' as sky; + +import 'package:sky/painting/text_style.dart'; +import 'package:sky/rendering/block.dart'; +import 'package:sky/rendering/box.dart'; +import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/paragraph.dart'; +import 'package:sky/rendering/sky_binding.dart'; + +RenderBox getBox(double lh) { + RenderParagraph paragraph = new RenderParagraph( + new InlineStyle( + new TextStyle(), + [ + new InlineText('test'), + new InlineStyle( + new TextStyle( + color: const Color(0xFF0000A0), + fontFamily: 'serif', + fontSize: 50.0, + height: lh + ), + [new InlineText('مرحبا Hello')] + ) + ] + ) + ); + return new RenderPadding( + padding: new EdgeDims.all(10.0), + child: new RenderConstrainedBox( + additionalConstraints: new BoxConstraints.tightFor(height: 200.0), + child: new RenderDecoratedBox( + decoration: new BoxDecoration( + backgroundColor: const Color(0xFFFFFFFF) + ), + child: new RenderPadding( + padding: new EdgeDims.all(10.0), + child: new RenderCustomPaint( + child: paragraph, + callback: (canvas, size) { + double baseline = paragraph.getDistanceToBaseline(TextBaseline.alphabetic); + double w = paragraph.getMaxIntrinsicWidth(new BoxConstraints.loose(size)); + double h = paragraph.getMaxIntrinsicHeight(new BoxConstraints.loose(size)); + Path path = new Path(); + path.moveTo(0.0, 0.0); + path.lineTo(w, 0.0); + path.moveTo(0.0, baseline); + path.lineTo(w, baseline); + path.moveTo(0.0, h); + path.lineTo(w, h); + Paint paint = new Paint(); + paint.color = const Color(0xFFFF9000); + paint.setStyle(sky.PaintingStyle.stroke); + paint.strokeWidth = 3.0; + canvas.drawPath(path, paint); + } + ) + ) + ) + ) + ); +} + +void main() { + RenderBox root = new RenderBlock(children: [ + new RenderConstrainedBox( + additionalConstraints: new BoxConstraints.tightFor(height: 50.0) + ), + getBox(1.0), + getBox(null), + ]); + var b = new SkyBinding(root: root); + // b.onFrame = b.debugDumpRenderTree; +} diff --git a/sdk/lib/rendering/block.dart b/sdk/lib/rendering/block.dart index 4d163db4a29001d57e482ec699dc52da7ed81dc0..ff840446b1a6ef4a4798b5621bae9b176da4d067 100644 --- a/sdk/lib/rendering/block.dart +++ b/sdk/lib/rendering/block.dart @@ -77,6 +77,10 @@ class RenderBlock extends RenderBox with ContainerRenderObjectMixin 'position=$position'; } +enum TextBaseline { alphabetic, ideographic } + abstract class RenderBox extends RenderObject { void setParentData(RenderObject child) { @@ -259,6 +261,31 @@ abstract class RenderBox extends RenderObject { return constraints.constrainHeight(0.0); } + // getDistanceToBaseline() should return the distance from the + // y-coordinate of the position of the box to the y-coordinate of + // the first given baseline in the box's contents. This is used by + // certain layout models to align adjacent boxes on a common + // baseline, regardless of padding, font size differences, etc. If + // there is no baseline, then it should return the distance from the + // y-coordinate of the position of the box to the y-coordinate of + // the bottom of the box, i.e., the height of the box. + // Only call this after layout has been performed. + double getDistanceToBaseline(TextBaseline baseline) { + assert(!needsLayout); + double result = getDistanceToActualBaseline(baseline); + if (result == null) + return size.height; + return result; + } + // getDistanceToActualBaseline() should return the distance from the + // y-coordinate of the position of the box to the y-coordinate of + // the first given baseline in the box's contents, if any, or null + // otherwise. + double getDistanceToActualBaseline(TextBaseline baseline) { + assert(!needsLayout); + return null; + } + // This whole block should only be here in debug builds bool _debugDoingThisLayout = false; bool _debugCanParentUseSize; @@ -348,6 +375,12 @@ class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin // DEFAULT BEHAVIORS FOR RENDERBOX CONTAINERS abstract class RenderBoxContainerDefaultsMixin> implements ContainerRenderObjectMixin { + double defaultGetDistanceToFirstActualBaseline(TextBaseline baseline) { + assert(!needsLayout); + RenderBox child = firstChild; + while (child != null) { + assert(child.parentData is ParentDataType); + double result = child.getDistanceToActualBaseline(baseline); + if (result != null) + return result + child.parentData.position.y; + child = child.parentData.nextSibling; + } + return null; + } + + double defaultGetDistanceToHighestActualBaseline(TextBaseline baseline) { + assert(!needsLayout); + double result; + RenderBox child = firstChild; + while (child != null) { + assert(child.parentData is ParentDataType); + double candidate = child.getDistanceToActualBaseline(baseline); + if (candidate != null) { + candidate += child.parentData.position.x; + if (result != null) + result = math.min(result, candidate); + else + result = candidate; + } + child = child.parentData.nextSibling; + } + return result; + } + void defaultHitTestChildren(HitTestResult result, { Point position }) { // the x, y parameters have the top left of the node's box as the origin ChildType child = lastChild; diff --git a/sdk/lib/rendering/flex.dart b/sdk/lib/rendering/flex.dart index e883e51612b6cd1eb8177098dd9e213c9edde22f..4eee1bd854f656ec197e50fd8fe62c4d044d35af 100644 --- a/sdk/lib/rendering/flex.dart +++ b/sdk/lib/rendering/flex.dart @@ -110,6 +110,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin c.getMaxIntrinsicHeight(innerConstraints)); } + double getDistanceToActualBaseline(TextBaseline baseline) { + assert(!needsLayout); + if (_direction == FlexDirection.horizontal) + return defaultGetDistanceToHighestActualBaseline(baseline); + return defaultGetDistanceToFirstActualBaseline(baseline); + } + int _getFlex(RenderBox child) { assert(child.parentData is FlexBoxParentData); return child.parentData.flex != null ? child.parentData.flex : 0; @@ -267,6 +277,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin 0) { @@ -306,6 +317,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin