提交 61b71a6f 编写于 作者: H Hans Muller

Styling for text fragments

This is a completion of Eric's WIP patch:
https://codereview.chromium.org/1179663005/

Low level support for creating a paragraph that contains
runs of styled text. The styles may be nested.

The Paragraph and RenderParagraph classes have been
replaced by Inline and RenderInline. Styled text is defined
with a tree of InlineText and InlineStyle objects.

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/1177833012.
上级 a7aa9513
...@@ -75,8 +75,12 @@ porchetta bacon kevin meatball meatloaf pig beef ribs chicken. Brisket ribeye ...@@ -75,8 +75,12 @@ porchetta bacon kevin meatball meatloaf pig beef ribs chicken. Brisket ribeye
andouille leberkas capicola meatloaf. Chicken pig ball tip pork picanha bresaola andouille leberkas capicola meatloaf. Chicken pig ball tip pork picanha bresaola
alcatra. Pork pork belly alcatra, flank chuck drumstick biltong doner jowl. alcatra. Pork pork belly alcatra, flank chuck drumstick biltong doner jowl.
Pancetta meatball tongue tenderloin rump tail jowl boudin."""; Pancetta meatball tongue tenderloin rump tail jowl boudin.""";
RenderParagraph paragraph = new RenderParagraph(text: meatyString, color: const Color(0xFF009900)); var text = new InlineStyle(
padding = new RenderPadding(padding: const EdgeDims.all(10.0), child: paragraph); new TextStyle(color: const Color(0xFF009900)),
[new InlineText(meatyString)]);
padding = new RenderPadding(
padding: const EdgeDims.all(10.0),
child: new RenderParagraph(text));
column.add(padding); column.add(padding);
// Bottom cell // Bottom cell
......
...@@ -20,7 +20,7 @@ void main() { ...@@ -20,7 +20,7 @@ void main() {
var table = new RenderFlex(direction: FlexDirection.vertical); var table = new RenderFlex(direction: FlexDirection.vertical);
void addRow(FlexJustifyContent justify) { void addRow(FlexJustifyContent justify) {
RenderParagraph paragraph = new RenderParagraph(text: "${justify}"); RenderParagraph paragraph = new RenderParagraph(new InlineText("${justify}"));
table.add(new RenderPadding(child: paragraph, padding: new EdgeDims.only(top: 20.0))); table.add(new RenderPadding(child: paragraph, padding: new EdgeDims.only(top: 20.0)));
var row = new RenderFlex(direction: FlexDirection.horizontal); var row = new RenderFlex(direction: FlexDirection.horizontal);
row.add(new RenderSolidColorBox(const Color(0xFFFFCCCC), desiredSize: new Size(80.0, 60.0))); row.add(new RenderSolidColorBox(const Color(0xFFFFCCCC), desiredSize: new Size(80.0, 60.0)));
......
...@@ -31,9 +31,12 @@ andouille leberkas capicola meatloaf. Chicken pig ball tip pork picanha bresaola ...@@ -31,9 +31,12 @@ andouille leberkas capicola meatloaf. Chicken pig ball tip pork picanha bresaola
alcatra. Pork pork belly alcatra, flank chuck drumstick biltong doner jowl. alcatra. Pork pork belly alcatra, flank chuck drumstick biltong doner jowl.
Pancetta meatball tongue tenderloin rump tail jowl boudin."""; Pancetta meatball tongue tenderloin rump tail jowl boudin.""";
var text = new InlineStyle(
new TextStyle(color: const Color(0xFF009900)),
[new InlineText(meatyString)]);
child = new RenderDecoratedBox( child = new RenderDecoratedBox(
decoration: new BoxDecoration(backgroundColor: const Color(0xFFFFFFFF)), decoration: new BoxDecoration(backgroundColor: const Color(0xFFFFFFFF)),
child: new RenderParagraph(text: meatyString, color: const Color(0xFF009900)) child: new RenderParagraph(text)
); );
flexRoot.add(child); flexRoot.add(child);
child.parentData.flex = 1; child.parentData.flex = 1;
......
...@@ -80,13 +80,14 @@ class RenderTouchDemo extends RenderBox { ...@@ -80,13 +80,14 @@ class RenderTouchDemo extends RenderBox {
AppView app; AppView app;
void main() { void main() {
var para = new RenderParagraph(text: "Touch me!"); var paragraph = new RenderParagraph(new InlineText("Touch me!"));
var stack = new RenderStack(children: [ var stack = new RenderStack(children: [
new RenderTouchDemo(), new RenderTouchDemo(),
para, paragraph,
]); ]);
// Make the paragraph not fill the whole screen so it doesn't eat events. // Prevent the RenderParagraph from filling the whole screen so
para.parentData..top = 40.0 // that it doesn't eat events.
paragraph.parentData..top = 40.0
..left = 20.0; ..left = 20.0;
app = new AppView(root: stack); app = new AppView(root: stack);
} }
...@@ -93,8 +93,6 @@ class EditableText extends Component { ...@@ -93,8 +93,6 @@ class EditableText extends Component {
// // style: _cursorStyle // // style: _cursorStyle
// )); // ));
return new Paragraph( return new Text(hack);
text: hack
);
} }
} }
...@@ -7,11 +7,6 @@ import 'dart:sky' as sky; ...@@ -7,11 +7,6 @@ import 'dart:sky' as sky;
import 'box.dart'; import 'box.dart';
import 'object.dart'; import 'object.dart';
class RenderInline extends RenderObject {
RenderInline(this.data);
String data;
}
enum FontWeight { enum FontWeight {
light, // 300 light, // 300
regular, // 400 regular, // 400
...@@ -69,6 +64,29 @@ class TextStyle { ...@@ -69,6 +64,29 @@ class TextStyle {
return value; return value;
} }
void _applyToCSSStyle(sky.CSSStyleDeclaration cssStyle) {
if (color != null) {
cssStyle['color'] = 'rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha / 255.0})';
}
if (fontSize != null) {
cssStyle['font-size'] = "${fontSize}px";
}
if (fontWeight != null) {
cssStyle['font-weight'] = const {
FontWeight.light: '300',
FontWeight.regular: '400',
FontWeight.medium: '500',
}[fontWeight];
}
if (textAlign != null) {
cssStyle['text-align'] = const {
TextAlign.left: 'left',
TextAlign.right: 'right',
TextAlign.center: 'center',
}[textAlign];
}
}
String toString([String prefix = '']) { String toString([String prefix = '']) {
List<String> result = []; List<String> result = [];
if (color != null) if (color != null)
...@@ -76,15 +94,63 @@ class TextStyle { ...@@ -76,15 +94,63 @@ class TextStyle {
if (fontSize != null) if (fontSize != null)
result.add('${prefix}fontSize: $fontSize'); result.add('${prefix}fontSize: $fontSize');
if (fontWeight != null) if (fontWeight != null)
result.add('${prefix}fontWeight: fontWeight'); result.add('${prefix}fontWeight: $fontWeight');
if (textAlign != null) if (textAlign != null)
result.add('${prefix}textAlign: textAlign'); result.add('${prefix}textAlign: $textAlign');
if (result.isEmpty) if (result.isEmpty)
return '${prefix}<no style specified>'; return '${prefix}<no style specified>';
return result.join('\n'); return result.join('\n');
} }
} }
class InlineBase {
sky.Node _toDOM(sky.Document owner);
String toString([String prefix = '']);
}
class InlineText extends InlineBase {
InlineText(this.text) {
assert(text != null);
}
final String text;
sky.Node _toDOM(sky.Document owner) {
return owner.createText(text);
}
String toString([String prefix = '']) => '${prefix}InlineText: "${text}"';
}
class InlineStyle extends InlineBase {
InlineStyle(this.style, this.children) {
assert(style != null && children != null);
}
final TextStyle style;
final List<InlineBase> children;
sky.Node _toDOM(sky.Document owner) {
sky.Element parent = owner.createElement('t');
style._applyToCSSStyle(parent.style);
for (InlineBase child in children) {
parent.appendChild(child._toDOM(owner));
}
return parent;
}
String toString([String prefix = '']) {
List<String> result = [];
result.add('${prefix}InlineStyle:');
var indent = '${prefix} ';
result.add('${style.toString(indent)}');
for (InlineBase child in children) {
result.add(child.toString(indent));
}
return result.join('\n');
}
}
// Unfortunately, using full precision floating point here causes bad layouts // Unfortunately, using full precision floating point here causes bad layouts
// because floating point math isn't associative. If we add and subtract // 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 // padding, for example, we'll get different values when we estimate sizes and
...@@ -98,36 +164,24 @@ double _applyFloatingPointHack(double layoutValue) { ...@@ -98,36 +164,24 @@ double _applyFloatingPointHack(double layoutValue) {
class RenderParagraph extends RenderBox { class RenderParagraph extends RenderBox {
RenderParagraph({ RenderParagraph(InlineBase inlineValue) {
String text,
Color color,
TextStyle style
}) : _style = style {
_layoutRoot.rootElement = _document.createElement('p'); _layoutRoot.rootElement = _document.createElement('p');
this.text = text; inline = inlineValue;
} }
final sky.Document _document = new sky.Document(); final sky.Document _document = new sky.Document();
final sky.LayoutRoot _layoutRoot = new sky.LayoutRoot(); final sky.LayoutRoot _layoutRoot = new sky.LayoutRoot();
String get text => (_layoutRoot.rootElement.firstChild as sky.Text).data; InlineBase _inline;
void set text (String value) { BoxConstraints _constraintsForCurrentLayout;
_layoutRoot.rootElement.setChild(_document.createText(value));
markNeedsLayout();
}
TextStyle _style; String get inline => _inline;
TextStyle get style => _style; void set inline (InlineBase value) {
void set style (TextStyle value) { _inline = value;
if (_style != value) { _layoutRoot.rootElement.setChild(_inline._toDOM(_document));
// TODO(hansmuller): decide if a new layout or paint is needed
markNeedsLayout(); markNeedsLayout();
_style = value;
}
} }
BoxConstraints _constraintsForCurrentLayout;
sky.Element _layout(BoxConstraints constraints) { sky.Element _layout(BoxConstraints constraints) {
_layoutRoot.maxWidth = constraints.maxWidth; _layoutRoot.maxWidth = constraints.maxWidth;
_layoutRoot.minWidth = constraints.minWidth; _layoutRoot.minWidth = constraints.minWidth;
...@@ -182,31 +236,6 @@ class RenderParagraph extends RenderBox { ...@@ -182,31 +236,6 @@ class RenderParagraph extends RenderBox {
if (_constraintsForCurrentLayout != constraints && constraints != null) if (_constraintsForCurrentLayout != constraints && constraints != null)
_layout(constraints); _layout(constraints);
if (style != null) {
var cssStyle = _layoutRoot.rootElement.style;
if (style.color != null) {
Color c = style.color;
cssStyle['color'] =
'rgba(${c.red}, ${c.green}, ${c.blue}, ${c.alpha / 255.0})';
}
if (style.fontSize != null) {
cssStyle['font-size'] = "${style.fontSize}px";
}
if (style.fontWeight != null) {
cssStyle['font-weight'] = const {
FontWeight.light: '300',
FontWeight.regular: '400',
FontWeight.medium: '500',
}[style.fontWeight];
}
if (style.textAlign != null) {
cssStyle['text-align'] = const {
TextAlign.left: 'left',
TextAlign.right: 'right',
TextAlign.center: 'center',
}[style.textAlign];
}
}
_layoutRoot.paint(canvas); _layoutRoot.paint(canvas);
} }
...@@ -214,9 +243,7 @@ class RenderParagraph extends RenderBox { ...@@ -214,9 +243,7 @@ class RenderParagraph extends RenderBox {
String debugDescribeSettings(String prefix) { String debugDescribeSettings(String prefix) {
String result = '${super.debugDescribeSettings(prefix)}'; String result = '${super.debugDescribeSettings(prefix)}';
if (style != null) result += '${prefix}inline: ${inline}\n';
result += '${prefix}style:\n' + style.toString('$prefix ') + '\n';
result += '${prefix}text: ${text}\n';
return result; return result;
} }
} }
...@@ -345,25 +345,22 @@ class Flexible extends ParentDataNode { ...@@ -345,25 +345,22 @@ class Flexible extends ParentDataNode {
: super(child, new FlexBoxParentData()..flex = flex, key: key); : super(child, new FlexBoxParentData()..flex = flex, key: key);
} }
class Paragraph extends RenderObjectWrapper { class Inline extends RenderObjectWrapper {
Inline({ Object key, this.text }) : super(key: key);
Paragraph({ String key, this.text, this.style }) : super(key: key);
RenderParagraph get root => super.root; RenderParagraph get root => super.root;
RenderParagraph createNode() => new RenderParagraph(text: text, style: style); RenderParagraph createNode() => new RenderParagraph(text);
final String text; final InlineBase text;
final TextStyle style;
void syncRenderObject(Widget old) { void syncRenderObject(Widget old) {
super.syncRenderObject(old); super.syncRenderObject(old);
root.text = text; root.inline = text;
root.style = style;
} }
void insert(RenderObjectWrapper child, dynamic slot) { void insert(RenderObjectWrapper child, dynamic slot) {
assert(false); assert(false);
// Paragraph does not support having children currently // Inline does not support having children currently
} }
} }
...@@ -373,7 +370,11 @@ class Text extends Component { ...@@ -373,7 +370,11 @@ class Text extends Component {
final String data; final String data;
final TextStyle style; final TextStyle style;
bool get interchangeable => true; bool get interchangeable => true;
Widget build() => new Paragraph(text: data, style: style); Widget build() {
InlineBase text = new InlineText(data);
if (style != null) text = new InlineStyle(style, [text]);
return new Inline(text: text);
}
} }
class Image extends RenderObjectWrapper { class Image extends RenderObjectWrapper {
......
...@@ -31,7 +31,7 @@ PAINT FOR FRAME #2 ---------------------------------------------- ...@@ -31,7 +31,7 @@ PAINT FOR FRAME #2 ----------------------------------------------
2 | | | | | | | TestDisplayList() constructor: 800.0 x 600.0 2 | | | | | | | TestDisplayList() constructor: 800.0 x 600.0
2 | | | | | | | paintChild RenderImage at Point(8.0, 8.0) 2 | | | | | | | paintChild RenderImage at Point(8.0, 8.0)
2 | | | | | | | | TestDisplayList() constructor: 800.0 x 600.0 2 | | | | | | | | TestDisplayList() constructor: 800.0 x 600.0
2 | | | | | | paintChild RenderPadding at Point(40.0, 20.0) 2 | | | | | | paintChild RenderPadding at Point(40.0, 16.0)
2 | | | | | | | TestDisplayList() constructor: 800.0 x 600.0 2 | | | | | | | TestDisplayList() constructor: 800.0 x 600.0
2 | | | | | | | paintChild RenderParagraph at Point(24.0, 0.0) 2 | | | | | | | paintChild RenderParagraph at Point(24.0, 0.0)
2 | | | | | | | | TestDisplayList() constructor: 800.0 x 600.0 2 | | | | | | | | TestDisplayList() constructor: 800.0 x 600.0
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册