blob: 18663b32f350dfb4dc17e90386ab348a7fc5eb9e [file] [log] [blame]
// 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 'package:sky/painting/paragraph_painter.dart';
import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart';
export 'package:sky/painting/paragraph_painter.dart';
// Unfortunately, using full precision floating point here causes bad layouts
// because floating point math isn't associative. If we add and subtract
// padding, for example, we'll get different values when we estimate sizes and
// when we actually compute layout because the operations will end up associated
// differently. To work around this problem for now, we round fractional pixel
// values up to the nearest whole pixel value. The right long-term fix is to do
// layout using fixed precision arithmetic.
double _applyFloatingPointHack(double layoutValue) {
return layoutValue.ceilToDouble();
}
class RenderParagraph extends RenderBox {
RenderParagraph(TextSpan text)
: _paragraphPainter = new ParagraphPainter(text) {
assert(text != null);
}
ParagraphPainter _paragraphPainter;
BoxConstraints _constraintsForCurrentLayout; // when null, we don't have a current layout
TextSpan get text => _paragraphPainter.text;
void set text(TextSpan value) {
if (_paragraphPainter.text == value)
return;
_paragraphPainter.text = value;
_constraintsForCurrentLayout = null;
markNeedsLayout();
}
void _layout(BoxConstraints constraints) {
assert(constraints != null);
if (_constraintsForCurrentLayout == constraints)
return; // already cached this layout
_paragraphPainter.maxWidth = constraints.maxWidth;
_paragraphPainter.minWidth = constraints.minWidth;
_paragraphPainter.minHeight = constraints.minHeight;
_paragraphPainter.maxHeight = constraints.maxHeight;
_paragraphPainter.layout();
_constraintsForCurrentLayout = constraints;
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
_layout(constraints);
return constraints.constrainWidth(
_applyFloatingPointHack(_paragraphPainter.minContentWidth));
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
_layout(constraints);
return constraints.constrainWidth(
_applyFloatingPointHack(_paragraphPainter.maxContentWidth));
}
double _getIntrinsicHeight(BoxConstraints constraints) {
_layout(constraints);
return constraints.constrainHeight(
_applyFloatingPointHack(_paragraphPainter.height));
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
return _getIntrinsicHeight(constraints);
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
return _getIntrinsicHeight(constraints);
}
double computeDistanceToActualBaseline(TextBaseline baseline) {
assert(!needsLayout);
_layout(constraints);
return _paragraphPainter.computeDistanceToActualBaseline(baseline);
}
void performLayout() {
_layout(constraints);
// _paragraphPainter.width always expands to fill, use maxContentWidth instead.
size = constraints.constrain(new Size(_applyFloatingPointHack(_paragraphPainter.maxContentWidth),
_applyFloatingPointHack(_paragraphPainter.height)));
}
void paint(PaintingContext context, Offset offset) {
// Ideally we could compute the min/max intrinsic width/height with a
// non-destructive operation. However, currently, computing these values
// will destroy state inside the painter. If that happens, we need to
// get back the correct state by calling _layout again.
//
// TODO(abarth): Make computing the min/max intrinsic width/height
// a non-destructive operation.
_layout(constraints);
_paragraphPainter.paint(context.canvas, offset);
}
// we should probably expose a way to do precise (inter-glpyh) hit testing
String debugDescribeSettings(String prefix) {
String result = '${super.debugDescribeSettings(prefix)}';
result += '${prefix}text:\n${text.toString("$prefix ")}\n';
return result;
}
}