提交 9f4b2fe7 编写于 作者: C Collin Jackson

Refactor of text rendering into painting layer

上级 f338ec97
// 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';
// This must be immutable, because we won't notice when it changes
abstract class TextSpan {
sky.Node _toDOM(sky.Document owner);
String toString([String prefix = '']);
void _applyStyleToContainer(sky.Element container) {
}
}
class PlainTextSpan extends TextSpan {
PlainTextSpan(this.text) {
assert(text != null);
}
final String text;
sky.Node _toDOM(sky.Document owner) {
return owner.createText(text);
}
bool operator ==(other) => other is PlainTextSpan && text == other.text;
int get hashCode => text.hashCode;
String toString([String prefix = '']) => '${prefix}${runtimeType}: "${text}"';
}
class StyledTextSpan extends TextSpan {
StyledTextSpan(this.style, this.children) {
assert(style != null);
assert(children != null);
}
final TextStyle style;
final List<TextSpan> children;
sky.Node _toDOM(sky.Document owner) {
sky.Element parent = owner.createElement('t');
style.applyToCSSStyle(parent.style);
for (TextSpan child in children) {
parent.appendChild(child._toDOM(owner));
}
return parent;
}
void _applyStyleToContainer(sky.Element container) {
style.applyToContainerCSSStyle(container.style);
}
bool operator ==(other) {
if (identical(this, other))
return true;
if (other is! StyledTextSpan
|| style != other.style
|| children.length != other.children.length)
return false;
for (int i = 0; i < children.length; ++i) {
if (children[i] != other.children[i])
return false;
}
return true;
}
int get hashCode {
int value = 373;
value = 37 * value + style.hashCode;
for (TextSpan child in children)
value = 37 * value + child.hashCode;
return value;
}
String toString([String prefix = '']) {
List<String> result = [];
result.add('${prefix}${runtimeType}:');
var indent = '${prefix} ';
result.add('${style.toString(indent)}');
for (TextSpan child in children) {
result.add(child.toString(indent));
}
return result.join('\n');
}
}
class ParagraphPainter {
ParagraphPainter(TextSpan text) {
_layoutRoot.rootElement = _document.createElement('p');
assert(text != null);
this.text = text;
}
final sky.Document _document = new sky.Document();
final sky.LayoutRoot _layoutRoot = new sky.LayoutRoot();
TextSpan _text;
TextSpan get text => _text;
void set text(TextSpan value) {
_text = value;
_layoutRoot.rootElement.setChild(_text._toDOM(_document));
_layoutRoot.rootElement.removeAttribute('style');
_text._applyStyleToContainer(_layoutRoot.rootElement);
}
double get minWidth => _layoutRoot.minWidth;
void set minWidth(value) {
_layoutRoot.minWidth = value;
}
double get maxWidth => _layoutRoot.maxWidth;
void set maxWidth(value) {
_layoutRoot.maxWidth = value;
}
double get minHeight => _layoutRoot.minHeight;
void set minHeight(value) {
_layoutRoot.minHeight = value;
}
double get maxHeight => _layoutRoot.maxHeight;
void set maxHeight(value) {
_layoutRoot.maxHeight = value;
}
double get minContentWidth => _layoutRoot.rootElement.minContentWidth;
double get maxContentWidth => _layoutRoot.rootElement.maxContentWidth;
double get height => _layoutRoot.rootElement.height;
double computeDistanceToActualBaseline(TextBaseline baseline) {
sky.Element root = _layoutRoot.rootElement;
switch (baseline) {
case TextBaseline.alphabetic: return root.alphabeticBaseline;
case TextBaseline.ideographic: return root.ideographicBaseline;
}
}
void layout() => _layoutRoot.layout();
void paint(sky.Canvas canvas, sky.Offset offset) {
// TODO(ianh): Make LayoutRoot support a paint offset so we don't
// need to translate for each span of text.
canvas.translate(offset.dx, offset.dy);
_layoutRoot.paint(canvas);
canvas.translate(-offset.dx, -offset.dy);
}
}
......@@ -2,92 +2,11 @@
// 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/painting/paragraph_painter.dart';
import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart';
abstract class RenderInline {
sky.Node _toDOM(sky.Document owner);
String toString([String prefix = '']);
void _applyStyleToContainer(sky.Element container) {
}
}
class RenderText extends RenderInline {
RenderText(this.text) {
assert(text != null);
}
final String text;
sky.Node _toDOM(sky.Document owner) {
return owner.createText(text);
}
bool operator ==(other) => other is RenderText && text == other.text;
int get hashCode => text.hashCode;
String toString([String prefix = '']) => '${prefix}InlineText: "${text}"';
}
class RenderStyled extends RenderInline {
RenderStyled(this.style, this.children) {
assert(style != null);
assert(children != null);
}
final TextStyle style;
final List<RenderInline> children;
sky.Node _toDOM(sky.Document owner) {
sky.Element parent = owner.createElement('t');
style.applyToCSSStyle(parent.style);
for (RenderInline child in children) {
parent.appendChild(child._toDOM(owner));
}
return parent;
}
void _applyStyleToContainer(sky.Element container) {
style.applyToContainerCSSStyle(container.style);
}
bool operator ==(other) {
if (identical(this, other))
return true;
if (other is! RenderStyled
|| style != other.style
|| children.length != other.children.length)
return false;
for (int i = 0; i < children.length; ++i) {
if (children[i] != other.children[i])
return false;
}
return true;
}
int get hashCode {
int value = 373;
value = 37 * value + style.hashCode;
for (RenderInline child in children)
value = 37 * value + child.hashCode;
return value;
}
String toString([String prefix = '']) {
List<String> result = [];
result.add('${prefix}InlineStyle:');
var indent = '${prefix} ';
result.add('${style.toString(indent)}');
for (RenderInline child in children) {
result.add(child.toString(indent));
}
return result.join('\n');
}
}
export 'package:sky/painting/paragraph_painter.dart' show TextSpan, PlainTextSpan, StyledTextSpan;
// Unfortunately, using full precision floating point here causes bad layouts
// because floating point math isn't associative. If we add and subtract
......@@ -102,25 +21,20 @@ double _applyFloatingPointHack(double layoutValue) {
class RenderParagraph extends RenderBox {
RenderParagraph(RenderInline inline) {
_layoutRoot.rootElement = _document.createElement('p');
this.inline = inline;
RenderParagraph(TextSpan text)
: _paragraphPainter = new ParagraphPainter(text) {
assert(text != null);
}
final sky.Document _document = new sky.Document();
final sky.LayoutRoot _layoutRoot = new sky.LayoutRoot();
ParagraphPainter _paragraphPainter;
BoxConstraints _constraintsForCurrentLayout; // when null, we don't have a current layout
RenderInline _inline;
RenderInline get inline => _inline;
void set inline (RenderInline value) {
if (_inline == value)
TextSpan get text => _paragraphPainter.text;
void set text(TextSpan value) {
if (_paragraphPainter.text == value)
return;
_inline = value;
_layoutRoot.rootElement.setChild(_inline._toDOM(_document));
_layoutRoot.rootElement.removeAttribute('style');
_inline._applyStyleToContainer(_layoutRoot.rootElement);
_paragraphPainter.text = value;
_constraintsForCurrentLayout = null;
markNeedsLayout();
}
......@@ -129,30 +43,30 @@ class RenderParagraph extends RenderBox {
assert(constraints != null);
if (_constraintsForCurrentLayout == constraints)
return; // already cached this layout
_layoutRoot.maxWidth = constraints.maxWidth;
_layoutRoot.minWidth = constraints.minWidth;
_layoutRoot.minHeight = constraints.minHeight;
_layoutRoot.maxHeight = constraints.maxHeight;
_layoutRoot.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(_layoutRoot.rootElement.minContentWidth));
_applyFloatingPointHack(_paragraphPainter.minContentWidth));
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
_layout(constraints);
return constraints.constrainWidth(
_applyFloatingPointHack(_layoutRoot.rootElement.maxContentWidth));
_applyFloatingPointHack(_paragraphPainter.maxContentWidth));
}
double _getIntrinsicHeight(BoxConstraints constraints) {
_layout(constraints);
return constraints.constrainHeight(
_applyFloatingPointHack(_layoutRoot.rootElement.height));
_applyFloatingPointHack(_paragraphPainter.height));
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
......@@ -166,42 +80,33 @@ class RenderParagraph extends RenderBox {
double computeDistanceToActualBaseline(TextBaseline baseline) {
assert(!needsLayout);
_layout(constraints);
sky.Element root = _layoutRoot.rootElement;
switch (baseline) {
case TextBaseline.alphabetic: return root.alphabeticBaseline;
case TextBaseline.ideographic: return root.ideographicBaseline;
}
return _paragraphPainter.computeDistanceToActualBaseline(baseline);
}
void performLayout() {
_layout(constraints);
sky.Element root = _layoutRoot.rootElement;
// rootElement.width always expands to fill, use maxContentWidth instead.
size = constraints.constrain(new Size(_applyFloatingPointHack(root.maxContentWidth),
_applyFloatingPointHack(root.height)));
// _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 layout root. If that happens, we need to
// 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.
// TODO(ianh): Make LayoutRoot support a paint offset so we don't
// need to translate for each span of text.
_layout(constraints);
context.canvas.translate(offset.dx, offset.dy);
_layoutRoot.paint(context.canvas);
context.canvas.translate(-offset.dx, -offset.dy);
_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}inline:\n${inline.toString("$prefix ")}\n';
result += '${prefix}text:\n${text.toString("$prefix ")}\n';
return result;
}
}
......@@ -10,6 +10,7 @@ import 'package:sky/base/image_resource.dart';
import 'package:sky/mojo/asset_bundle.dart';
import 'package:sky/mojo/net/image_cache.dart' as image_cache;
import 'package:sky/painting/text_style.dart';
import 'package:sky/painting/paragraph_painter.dart';
import 'package:sky/rendering/block.dart';
import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/flex.dart';
......@@ -446,16 +447,16 @@ class Flexible extends ParentDataNode {
}
class Paragraph extends LeafRenderObjectWrapper {
Paragraph({ Key key, this.inline }) : super(key: key);
Paragraph({ Key key, this.text }) : super(key: key);
final RenderInline inline;
final TextSpan text;
RenderParagraph createNode() => new RenderParagraph(inline);
RenderParagraph createNode() => new RenderParagraph(text);
RenderParagraph get root => super.root;
void syncRenderObject(Widget old) {
super.syncRenderObject(old);
root.inline = inline;
root.text = text;
}
}
......@@ -464,25 +465,25 @@ class StyledText extends Component {
// Where "string" is text to display and text-style is an instance of
// TextStyle. The text-style applies to all of the elements that follow.
StyledText({ this.elements, Key key }) : super(key: key) {
assert(_toInline(elements) != null);
assert(_toSpan(elements) != null);
}
final dynamic elements;
RenderInline _toInline(dynamic element) {
TextSpan _toSpan(dynamic element) {
if (element is String)
return new RenderText(element);
return new PlainTextSpan(element);
if (element is Iterable) {
dynamic first = element.first;
if (first is! TextStyle)
throw new ArgumentError("First element of Iterable is a ${first.runtimeType} not a TextStyle");
return new RenderStyled(first, element.skip(1).map(_toInline).toList());
return new StyledTextSpan(first, element.skip(1).map(_toSpan).toList());
}
throw new ArgumentError("Element is ${element.runtimeType} not a String or an Iterable");
}
Widget build() {
return new Paragraph(inline: _toInline(elements));
return new Paragraph(text: _toSpan(elements));
}
}
......@@ -493,7 +494,7 @@ class Text extends Component {
final TextStyle style;
Widget build() {
RenderInline inline = new RenderText(data);
TextSpan text = new PlainTextSpan(data);
TextStyle defaultStyle = DefaultTextStyle.of(this);
TextStyle combinedStyle;
if (defaultStyle != null) {
......@@ -505,8 +506,8 @@ class Text extends Component {
combinedStyle = style;
}
if (combinedStyle != null)
inline = new RenderStyled(combinedStyle, [inline]);
return new Paragraph(inline: inline);
text = new StyledTextSpan(combinedStyle, [text]);
return new Paragraph(text: text);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册