parent
4d8f38e538
commit
36d437d6f2
@ -209,7 +209,13 @@ class AppBar extends StatelessWidget {
|
|||||||
toolBarRow.add(new Flexible(
|
toolBarRow.add(new Flexible(
|
||||||
child: new Padding(
|
child: new Padding(
|
||||||
padding: new EdgeInsets.only(left: 8.0),
|
padding: new EdgeInsets.only(left: 8.0),
|
||||||
child: title != null ? new DefaultTextStyle(style: centerStyle, child: title) : null
|
child: title != null ?
|
||||||
|
new DefaultTextStyle(
|
||||||
|
style: centerStyle,
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
child: title
|
||||||
|
) : null
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
if (actions != null)
|
if (actions != null)
|
||||||
|
@ -345,7 +345,12 @@ class _Tab extends StatelessWidget {
|
|||||||
Widget _buildLabelText() {
|
Widget _buildLabelText() {
|
||||||
assert(label.text != null);
|
assert(label.text != null);
|
||||||
TextStyle style = new TextStyle(color: color);
|
TextStyle style = new TextStyle(color: color);
|
||||||
return new Text(label.text, style: style);
|
return new Text(
|
||||||
|
label.text,
|
||||||
|
style: style,
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.fade
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLabelIcon(BuildContext context) {
|
Widget _buildLabelIcon(BuildContext context) {
|
||||||
@ -371,15 +376,15 @@ class _Tab extends StatelessWidget {
|
|||||||
labelContent = _buildLabelIcon(context);
|
labelContent = _buildLabelIcon(context);
|
||||||
} else {
|
} else {
|
||||||
labelContent = new Column(
|
labelContent = new Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Container(
|
new Container(
|
||||||
child: _buildLabelIcon(context),
|
child: _buildLabelIcon(context),
|
||||||
margin: const EdgeInsets.only(bottom: 10.0)
|
margin: const EdgeInsets.only(bottom: 10.0)
|
||||||
),
|
),
|
||||||
_buildLabelText()
|
_buildLabelText()
|
||||||
],
|
]
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,20 +2,42 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
import 'box.dart';
|
import 'box.dart';
|
||||||
import 'object.dart';
|
import 'object.dart';
|
||||||
import 'semantics.dart';
|
import 'semantics.dart';
|
||||||
|
|
||||||
|
/// How overflowing text should be handled.
|
||||||
|
enum TextOverflow {
|
||||||
|
/// Clip the overflowing text to fix its container.
|
||||||
|
clip,
|
||||||
|
|
||||||
|
/// Fade the overflowing text to transparent.
|
||||||
|
fade,
|
||||||
|
|
||||||
|
/// Use an ellipsis to indicate that the text has overflowed.
|
||||||
|
ellipsis,
|
||||||
|
}
|
||||||
|
|
||||||
/// A render object that displays a paragraph of text
|
/// A render object that displays a paragraph of text
|
||||||
class RenderParagraph extends RenderBox {
|
class RenderParagraph extends RenderBox {
|
||||||
|
/// Creates a paragraph render object.
|
||||||
|
///
|
||||||
|
/// The [text], [overflow], and [softWrap] arguments must not be null.
|
||||||
RenderParagraph(TextSpan text, {
|
RenderParagraph(TextSpan text, {
|
||||||
TextAlign textAlign
|
TextAlign textAlign,
|
||||||
}) : _textPainter = new TextPainter(text: text, textAlign: textAlign) {
|
TextOverflow overflow: TextOverflow.clip,
|
||||||
|
bool softWrap: true
|
||||||
|
}) : _softWrap = softWrap,
|
||||||
|
_overflow = overflow,
|
||||||
|
_textPainter = new TextPainter(text: text, textAlign: textAlign) {
|
||||||
assert(text != null);
|
assert(text != null);
|
||||||
assert(text.debugAssertValid());
|
assert(text.debugAssertValid());
|
||||||
|
assert(overflow != null);
|
||||||
|
assert(softWrap != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
final TextPainter _textPainter;
|
final TextPainter _textPainter;
|
||||||
@ -27,6 +49,8 @@ class RenderParagraph extends RenderBox {
|
|||||||
if (_textPainter.text == value)
|
if (_textPainter.text == value)
|
||||||
return;
|
return;
|
||||||
_textPainter.text = value;
|
_textPainter.text = value;
|
||||||
|
_overflowPainter = null;
|
||||||
|
_overflowShader = null;
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,10 +63,34 @@ class RenderParagraph extends RenderBox {
|
|||||||
markNeedsPaint();
|
markNeedsPaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the text should break at soft line breaks.
|
||||||
|
///
|
||||||
|
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
||||||
|
bool get softWrap => _softWrap;
|
||||||
|
bool _softWrap;
|
||||||
|
void set softWrap(bool value) {
|
||||||
|
assert(value != null);
|
||||||
|
if (_softWrap == value)
|
||||||
|
return;
|
||||||
|
_softWrap = value;
|
||||||
|
markNeedsLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How visual overflow should be handled.
|
||||||
|
TextOverflow get overflow => _overflow;
|
||||||
|
TextOverflow _overflow;
|
||||||
|
void set overflow(TextOverflow value) {
|
||||||
|
assert(value != null);
|
||||||
|
if (_overflow == value)
|
||||||
|
return;
|
||||||
|
_overflow = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
|
||||||
void _layoutText(BoxConstraints constraints) {
|
void _layoutText(BoxConstraints constraints) {
|
||||||
assert(constraints != null);
|
assert(constraints != null);
|
||||||
assert(constraints.debugAssertIsValid());
|
assert(constraints.debugAssertIsValid());
|
||||||
_textPainter.layout(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
|
_textPainter.layout(minWidth: constraints.minWidth, maxWidth: _softWrap ? constraints.maxWidth : double.INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -95,10 +143,49 @@ class RenderParagraph extends RenderBox {
|
|||||||
span?.recognizer?.addPointer(event);
|
span?.recognizer?.addPointer(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _hasVisualOverflow = false;
|
||||||
|
TextPainter _overflowPainter;
|
||||||
|
ui.Shader _overflowShader;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void performLayout() {
|
void performLayout() {
|
||||||
_layoutText(constraints);
|
_layoutText(constraints);
|
||||||
size = constraints.constrain(_textPainter.size);
|
size = constraints.constrain(_textPainter.size);
|
||||||
|
|
||||||
|
final bool didOverflowWidth = size.width < _textPainter.width;
|
||||||
|
// TODO(abarth): We're only measuring the sizes of the line boxes here. If
|
||||||
|
// the glyphs draw outside the line boxes, we might think that there isn't
|
||||||
|
// visual overflow when there actually is visual overflow. This can become
|
||||||
|
// a problem if we start having horizontal overflow and introduce a clip
|
||||||
|
// that affects the actual (but undetected) vertical overflow.
|
||||||
|
_hasVisualOverflow = didOverflowWidth || size.height < _textPainter.height;
|
||||||
|
if (didOverflowWidth) {
|
||||||
|
switch (_overflow) {
|
||||||
|
case TextOverflow.clip:
|
||||||
|
_overflowPainter = null;
|
||||||
|
_overflowShader = null;
|
||||||
|
break;
|
||||||
|
case TextOverflow.fade:
|
||||||
|
case TextOverflow.ellipsis:
|
||||||
|
_overflowPainter ??= new TextPainter(
|
||||||
|
text: new TextSpan(style: _textPainter.text.style, text: '\u2026')
|
||||||
|
)..layout();
|
||||||
|
final double overflowUnit = _overflowPainter.width;
|
||||||
|
double fadeEnd = size.width;
|
||||||
|
if (_overflow == TextOverflow.ellipsis)
|
||||||
|
fadeEnd -= overflowUnit / 2.0;
|
||||||
|
final double fadeStart = fadeEnd - _overflowPainter.width;
|
||||||
|
// TODO(abarth): This shader has an LTR bias.
|
||||||
|
_overflowShader = new ui.Gradient.linear(
|
||||||
|
<Point>[new Point(fadeStart, 0.0), new Point(fadeEnd, 0.0)],
|
||||||
|
<Color>[const Color(0xFFFFFFFF), const Color(0x00FFFFFF)]
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_overflowPainter = null;
|
||||||
|
_overflowShader = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -114,7 +201,31 @@ class RenderParagraph extends RenderBox {
|
|||||||
// If you remove this call, make sure that changing the textAlign still
|
// If you remove this call, make sure that changing the textAlign still
|
||||||
// works properly.
|
// works properly.
|
||||||
_layoutText(constraints);
|
_layoutText(constraints);
|
||||||
_textPainter.paint(context.canvas, offset);
|
final Canvas canvas = context.canvas;
|
||||||
|
if (_hasVisualOverflow) {
|
||||||
|
final Rect bounds = offset & size;
|
||||||
|
if (_overflowPainter != null)
|
||||||
|
canvas.saveLayer(bounds, new Paint());
|
||||||
|
else
|
||||||
|
canvas.save();
|
||||||
|
canvas.clipRect(bounds);
|
||||||
|
}
|
||||||
|
_textPainter.paint(canvas, offset);
|
||||||
|
if (_hasVisualOverflow) {
|
||||||
|
if (_overflowShader != null) {
|
||||||
|
canvas.translate(offset.dx, offset.dy);
|
||||||
|
Paint paint = new Paint()
|
||||||
|
..transferMode = TransferMode.modulate
|
||||||
|
..shader = _overflowShader;
|
||||||
|
canvas.drawRect(Point.origin & size, paint);
|
||||||
|
if (_overflow == TextOverflow.ellipsis) {
|
||||||
|
// TODO(abarth): This paint offset has an LTR bias.
|
||||||
|
Offset ellipseOffset = new Offset(size.width - _overflowPainter.width, 0.0);
|
||||||
|
_overflowPainter.paint(canvas, ellipseOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -45,6 +45,7 @@ export 'package:flutter/rendering.dart' show
|
|||||||
RenderObjectPainter,
|
RenderObjectPainter,
|
||||||
ShaderCallback,
|
ShaderCallback,
|
||||||
SingleChildLayoutDelegate,
|
SingleChildLayoutDelegate,
|
||||||
|
TextOverflow,
|
||||||
ValueChanged,
|
ValueChanged,
|
||||||
ViewportAnchor,
|
ViewportAnchor,
|
||||||
ViewportDimensions,
|
ViewportDimensions,
|
||||||
@ -1904,8 +1905,16 @@ class RichText extends LeafRenderObjectWidget {
|
|||||||
/// Creates a paragraph of rich text.
|
/// Creates a paragraph of rich text.
|
||||||
///
|
///
|
||||||
/// The [text] argument is required to be non-null.
|
/// The [text] argument is required to be non-null.
|
||||||
RichText({ Key key, this.text, this.textAlign }) : super(key: key) {
|
RichText({
|
||||||
|
Key key,
|
||||||
|
this.text,
|
||||||
|
this.textAlign,
|
||||||
|
this.softWrap: true,
|
||||||
|
this.overflow: TextOverflow.clip
|
||||||
|
}) : super(key: key) {
|
||||||
assert(text != null);
|
assert(text != null);
|
||||||
|
assert(softWrap != null);
|
||||||
|
assert(overflow != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The text to display in this widget.
|
/// The text to display in this widget.
|
||||||
@ -1914,16 +1923,30 @@ class RichText extends LeafRenderObjectWidget {
|
|||||||
/// How the text should be aligned horizontally.
|
/// How the text should be aligned horizontally.
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
|
|
||||||
|
/// Whether the text should break at soft line breaks.
|
||||||
|
///
|
||||||
|
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
||||||
|
final bool softWrap;
|
||||||
|
|
||||||
|
/// How visual overflow should be handled.
|
||||||
|
final TextOverflow overflow;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderParagraph createRenderObject(BuildContext context) {
|
RenderParagraph createRenderObject(BuildContext context) {
|
||||||
return new RenderParagraph(text, textAlign: textAlign);
|
return new RenderParagraph(text,
|
||||||
|
textAlign: textAlign,
|
||||||
|
softWrap: softWrap,
|
||||||
|
overflow: overflow
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateRenderObject(BuildContext context, RenderParagraph renderObject) {
|
void updateRenderObject(BuildContext context, RenderParagraph renderObject) {
|
||||||
renderObject
|
renderObject
|
||||||
..text = text
|
..text = text
|
||||||
..textAlign = textAlign;
|
..textAlign = textAlign
|
||||||
|
..softWrap = softWrap
|
||||||
|
..overflow = overflow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1937,16 +1960,24 @@ class DefaultTextStyle extends InheritedWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
this.style,
|
this.style,
|
||||||
this.textAlign,
|
this.textAlign,
|
||||||
|
this.softWrap: true,
|
||||||
|
this.overflow: TextOverflow.clip,
|
||||||
Widget child
|
Widget child
|
||||||
}) : super(key: key, child: child) {
|
}) : super(key: key, child: child) {
|
||||||
assert(style != null);
|
assert(style != null);
|
||||||
|
assert(softWrap != null);
|
||||||
|
assert(overflow != null);
|
||||||
assert(child != null);
|
assert(child != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A const-constructible default text style that provides fallback values.
|
/// A const-constructible default text style that provides fallback values.
|
||||||
///
|
///
|
||||||
/// Returned from [of] when the given [BuildContext] doesn't have an enclosing default text style.
|
/// Returned from [of] when the given [BuildContext] doesn't have an enclosing default text style.
|
||||||
const DefaultTextStyle.fallback() : style = const TextStyle(), textAlign = null;
|
const DefaultTextStyle.fallback()
|
||||||
|
: style = const TextStyle(),
|
||||||
|
textAlign = null,
|
||||||
|
softWrap = true,
|
||||||
|
overflow = TextOverflow.clip;
|
||||||
|
|
||||||
/// Creates a default text style that inherits from the given [BuildContext].
|
/// Creates a default text style that inherits from the given [BuildContext].
|
||||||
///
|
///
|
||||||
@ -1959,6 +1990,8 @@ class DefaultTextStyle extends InheritedWidget {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
TextStyle style,
|
TextStyle style,
|
||||||
TextAlign textAlign,
|
TextAlign textAlign,
|
||||||
|
bool softWrap,
|
||||||
|
TextOverflow overflow,
|
||||||
Widget child
|
Widget child
|
||||||
}) {
|
}) {
|
||||||
DefaultTextStyle parent = DefaultTextStyle.of(context);
|
DefaultTextStyle parent = DefaultTextStyle.of(context);
|
||||||
@ -1966,6 +1999,8 @@ class DefaultTextStyle extends InheritedWidget {
|
|||||||
key: key,
|
key: key,
|
||||||
style: parent.style.merge(style),
|
style: parent.style.merge(style),
|
||||||
textAlign: textAlign ?? parent.textAlign,
|
textAlign: textAlign ?? parent.textAlign,
|
||||||
|
softWrap: softWrap ?? parent.softWrap,
|
||||||
|
overflow: overflow ?? parent.overflow,
|
||||||
child: child
|
child: child
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1976,6 +2011,14 @@ class DefaultTextStyle extends InheritedWidget {
|
|||||||
/// How the text should be aligned horizontally.
|
/// How the text should be aligned horizontally.
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
|
|
||||||
|
/// Whether the text should break at soft line breaks.
|
||||||
|
///
|
||||||
|
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
||||||
|
final bool softWrap;
|
||||||
|
|
||||||
|
/// How visual overflow should be handled.
|
||||||
|
final TextOverflow overflow;
|
||||||
|
|
||||||
/// The closest instance of this class that encloses the given context.
|
/// The closest instance of this class that encloses the given context.
|
||||||
///
|
///
|
||||||
/// If no such instance exists, returns an instance created by
|
/// If no such instance exists, returns an instance created by
|
||||||
@ -2019,7 +2062,13 @@ class Text extends StatelessWidget {
|
|||||||
///
|
///
|
||||||
/// If the [style] argument is null, the text will use the style from the
|
/// If the [style] argument is null, the text will use the style from the
|
||||||
/// closest enclosing [DefaultTextStyle].
|
/// closest enclosing [DefaultTextStyle].
|
||||||
Text(this.data, { Key key, this.style, this.textAlign }) : super(key: key) {
|
Text(this.data, {
|
||||||
|
Key key,
|
||||||
|
this.style,
|
||||||
|
this.textAlign,
|
||||||
|
this.softWrap,
|
||||||
|
this.overflow
|
||||||
|
}) : super(key: key) {
|
||||||
assert(data != null);
|
assert(data != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2036,21 +2085,24 @@ class Text extends StatelessWidget {
|
|||||||
/// How the text should be aligned horizontally.
|
/// How the text should be aligned horizontally.
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
|
|
||||||
|
/// Whether the text should break at soft line breaks.
|
||||||
|
///
|
||||||
|
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
||||||
|
final bool softWrap;
|
||||||
|
|
||||||
|
/// How visual overflow should be handled.
|
||||||
|
final TextOverflow overflow;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
DefaultTextStyle defaultTextStyle;
|
DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
|
||||||
TextStyle effectiveTextStyle = style;
|
TextStyle effectiveTextStyle = style;
|
||||||
if (style == null || style.inherit) {
|
if (style == null || style.inherit)
|
||||||
defaultTextStyle ??= DefaultTextStyle.of(context);
|
|
||||||
effectiveTextStyle = defaultTextStyle.style.merge(style);
|
effectiveTextStyle = defaultTextStyle.style.merge(style);
|
||||||
}
|
|
||||||
TextAlign effectiveTextAlign = textAlign;
|
|
||||||
if (effectiveTextAlign == null) {
|
|
||||||
defaultTextStyle ??= DefaultTextStyle.of(context);
|
|
||||||
effectiveTextAlign = defaultTextStyle.textAlign;
|
|
||||||
}
|
|
||||||
return new RichText(
|
return new RichText(
|
||||||
textAlign: effectiveTextAlign,
|
textAlign: textAlign ?? defaultTextStyle.textAlign,
|
||||||
|
softWrap: softWrap ?? defaultTextStyle.softWrap,
|
||||||
|
overflow: overflow ?? defaultTextStyle.overflow,
|
||||||
text: new TextSpan(
|
text: new TextSpan(
|
||||||
style: effectiveTextStyle,
|
style: effectiveTextStyle,
|
||||||
text: data
|
text: data
|
||||||
|
Loading…
x
Reference in New Issue
Block a user