paragraph.dart 3.93 KB
Newer Older
1 2 3 4
// 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.

5
import 'package:flutter/painting.dart';
6 7 8

import 'box.dart';
import 'object.dart';
Hixie's avatar
Hixie committed
9
import 'semantics.dart';
10

11 12 13 14 15 16 17 18 19 20
export 'package:flutter/painting.dart' show
  FontStyle,
  FontWeight,
  PlainTextSpan,
  StyledTextSpan,
  TextAlign,
  TextBaseline,
  TextDecoration,
  TextDecorationStyle,
  TextSpan,
21
  TextStyle;
22

23
/// A render object that displays a paragraph of text
24 25
class RenderParagraph extends RenderBox {

26 27
  RenderParagraph(
    TextSpan text
28
  ) : _textPainter = new TextPainter(text) {
29
    assert(text != null);
30 31
  }

32
  final TextPainter _textPainter;
33 34 35

  BoxConstraints _constraintsForCurrentLayout; // when null, we don't have a current layout

36
  /// The text to display
37
  TextSpan get text => _textPainter.text;
38
  void set text(TextSpan value) {
39
    if (_textPainter.text == value)
40
      return;
41
    _textPainter.text = value;
42 43 44 45
    _constraintsForCurrentLayout = null;
    markNeedsLayout();
  }

46 47
  // TODO(abarth): This logic should live in TextPainter and be shared with RenderEditableLine.
  void _layoutText(BoxConstraints constraints) {
48
    assert(constraints != null);
49
    assert(constraints.isNormalized);
50 51
    if (_constraintsForCurrentLayout == constraints)
      return; // already cached this layout
52 53 54 55 56
    _textPainter.maxWidth = constraints.maxWidth;
    _textPainter.minWidth = constraints.minWidth;
    _textPainter.minHeight = constraints.minHeight;
    _textPainter.maxHeight = constraints.maxHeight;
    _textPainter.layout();
57
    // By default, we shrinkwrap to the intrinsic width.
58 59 60 61
    double width = constraints.constrainWidth(_textPainter.maxIntrinsicWidth);
    _textPainter.minWidth = width;
    _textPainter.maxWidth = width;
    _textPainter.layout();
62 63 64 65
    _constraintsForCurrentLayout = constraints;
  }

  double getMinIntrinsicWidth(BoxConstraints constraints) {
66 67
    _layoutText(constraints);
    return constraints.constrainWidth(_textPainter.minIntrinsicWidth);
68 69 70
  }

  double getMaxIntrinsicWidth(BoxConstraints constraints) {
71 72
    _layoutText(constraints);
    return constraints.constrainWidth(_textPainter.maxIntrinsicWidth);
73 74 75
  }

  double _getIntrinsicHeight(BoxConstraints constraints) {
76 77
    _layoutText(constraints);
    return constraints.constrainHeight(_textPainter.size.height);
78 79 80
  }

  double getMinIntrinsicHeight(BoxConstraints constraints) {
81
    assert(constraints.isNormalized);
82 83 84 85
    return _getIntrinsicHeight(constraints);
  }

  double getMaxIntrinsicHeight(BoxConstraints constraints) {
86
    assert(constraints.isNormalized);
87 88 89 90 91
    return _getIntrinsicHeight(constraints);
  }

  double computeDistanceToActualBaseline(TextBaseline baseline) {
    assert(!needsLayout);
92 93
    _layoutText(constraints);
    return _textPainter.computeDistanceToActualBaseline(baseline);
94 95
  }

Adam Barth's avatar
Adam Barth committed
96 97
  bool hitTestSelf(Point position) => true;

98
  void performLayout() {
99 100
    _layoutText(constraints);
    size = constraints.constrain(_textPainter.size);
101 102
  }

103
  void paint(PaintingContext context, Offset offset) {
104 105
    // Ideally we could compute the min/max intrinsic width/height with a
    // non-destructive operation. However, currently, computing these values
106
    // will destroy state inside the painter. If that happens, we need to
107 108 109 110
    // get back the correct state by calling _layout again.
    //
    // TODO(abarth): Make computing the min/max intrinsic width/height
    // a non-destructive operation.
111 112
    _layoutText(constraints);
    _textPainter.paint(context.canvas, offset);
113 114
  }

Hixie's avatar
Hixie committed
115 116 117 118 119
  Iterable<SemanticAnnotator> getSemanticAnnotators() sync* {
    yield (SemanticsNode node) {
      node.label = text.toPlainText();
    };
  }
120

121 122 123 124 125
  String debugDescribeChildren(String prefix) {
    return '$prefix \u2558\u2550\u2566\u2550\u2550 text \u2550\u2550\u2550\n'
           '${text.toString("$prefix   \u2551 ")}\n'
           '$prefix   \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n'
           '$prefix\n';
126 127
  }
}