paragraph.dart 4.14 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 6
import 'package:flutter/gestures.dart';

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

11
/// A render object that displays a paragraph of text
12 13
class RenderParagraph extends RenderBox {

14 15
  RenderParagraph(
    TextSpan text
16
  ) : _textPainter = new TextPainter(text) {
17
    assert(text != null);
18 19
  }

20
  final TextPainter _textPainter;
21 22 23

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

24
  /// The text to display
25
  TextSpan get text => _textPainter.text;
26
  void set text(TextSpan value) {
27
    if (_textPainter.text == value)
28
      return;
29
    _textPainter.text = value;
30 31 32 33
    _constraintsForCurrentLayout = null;
    markNeedsLayout();
  }

34 35
  // TODO(abarth): This logic should live in TextPainter and be shared with RenderEditableLine.
  void _layoutText(BoxConstraints constraints) {
36
    assert(constraints != null);
Hixie's avatar
Hixie committed
37
    assert(constraints.debugAssertIsNormalized);
38 39
    if (_constraintsForCurrentLayout == constraints)
      return; // already cached this layout
40 41 42 43 44
    _textPainter.maxWidth = constraints.maxWidth;
    _textPainter.minWidth = constraints.minWidth;
    _textPainter.minHeight = constraints.minHeight;
    _textPainter.maxHeight = constraints.maxHeight;
    _textPainter.layout();
45
    // By default, we shrinkwrap to the intrinsic width.
46 47 48 49
    double width = constraints.constrainWidth(_textPainter.maxIntrinsicWidth);
    _textPainter.minWidth = width;
    _textPainter.maxWidth = width;
    _textPainter.layout();
50 51 52 53
    _constraintsForCurrentLayout = constraints;
  }

  double getMinIntrinsicWidth(BoxConstraints constraints) {
54 55
    _layoutText(constraints);
    return constraints.constrainWidth(_textPainter.minIntrinsicWidth);
56 57 58
  }

  double getMaxIntrinsicWidth(BoxConstraints constraints) {
59 60
    _layoutText(constraints);
    return constraints.constrainWidth(_textPainter.maxIntrinsicWidth);
61 62 63
  }

  double _getIntrinsicHeight(BoxConstraints constraints) {
64 65
    _layoutText(constraints);
    return constraints.constrainHeight(_textPainter.size.height);
66 67 68
  }

  double getMinIntrinsicHeight(BoxConstraints constraints) {
Hixie's avatar
Hixie committed
69
    assert(constraints.debugAssertIsNormalized);
70 71 72 73
    return _getIntrinsicHeight(constraints);
  }

  double getMaxIntrinsicHeight(BoxConstraints constraints) {
Hixie's avatar
Hixie committed
74
    assert(constraints.debugAssertIsNormalized);
75 76 77 78 79
    return _getIntrinsicHeight(constraints);
  }

  double computeDistanceToActualBaseline(TextBaseline baseline) {
    assert(!needsLayout);
80 81
    _layoutText(constraints);
    return _textPainter.computeDistanceToActualBaseline(baseline);
82 83
  }

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

86 87 88 89 90 91 92 93 94 95
  void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
    if (event is! PointerDownEvent)
      return;
    _layoutText(constraints);
    Offset offset = entry.localPosition.toOffset();
    TextPosition position = _textPainter.getPositionForOffset(offset);
    TextSpan span = _textPainter.text.getSpanForPosition(position);
    span?.recognizer?.addPointer(event);
  }

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

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

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

119 120 121 122 123
  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';
124 125
  }
}