list.dart 5.46 KB
Newer Older
Adam Barth's avatar
Adam Barth committed
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 'dart:math' as math;

Adam Barth's avatar
Adam Barth committed
7 8 9 10 11 12 13
import 'box.dart';
import 'object.dart';
import 'viewport.dart';

/// Parent data for use with [RenderList].
class ListParentData extends ContainerBoxParentDataMixin<RenderBox> { }

14 15 16 17 18 19 20 21 22 23
/// A linear layout of children intended for use as a virtual viewport.
///
/// Children are layout out in order along the main axis. If [itemExtent] is
/// non-null, each child is required to have exactly [itemExtent] extent in the
/// main axis. If [itemExtent] is null, each child is required to have the same
/// extent in the main axis as the list itself.
///
/// In the cross axis, the render list expands to fill the available space and
/// each child is required to have the same extent in the cross axis as the list
/// itself.
24
class RenderList extends RenderVirtualViewport<ListParentData> {
25 26 27
  /// Creates a render list.
  ///
  /// By default, the list is oriented vertically and anchored at the start.
Adam Barth's avatar
Adam Barth committed
28 29 30
  RenderList({
    List<RenderBox> children,
    double itemExtent,
31
    EdgeInsets padding,
Adam Barth's avatar
Adam Barth committed
32 33
    int virtualChildCount,
    Offset paintOffset: Offset.zero,
34
    Axis mainAxis: Axis.vertical,
35
    ViewportAnchor anchor: ViewportAnchor.start,
Adam Barth's avatar
Adam Barth committed
36
    LayoutCallback callback
37 38 39 40 41
  }) : _itemExtent = itemExtent,
       _padding = padding,
       super(
         virtualChildCount: virtualChildCount,
         paintOffset: paintOffset,
42
         mainAxis: mainAxis,
43
         anchor: anchor,
44 45
         callback: callback
       ) {
Adam Barth's avatar
Adam Barth committed
46 47 48
    addAll(children);
  }

49 50 51 52
  /// The main-axis extent of each item in the list.
  ///
  /// If [itemExtent] is null, the items are required to match the main-axis
  /// extent of the list itself.
Adam Barth's avatar
Adam Barth committed
53 54
  double get itemExtent => _itemExtent;
  double _itemExtent;
55
  set itemExtent (double newValue) {
Adam Barth's avatar
Adam Barth committed
56 57 58 59 60 61
    if (_itemExtent == newValue)
      return;
    _itemExtent = newValue;
    markNeedsLayout();
  }

62
  /// The amount of space by which to inset the children inside the list.
63 64
  EdgeInsets get padding => _padding;
  EdgeInsets _padding;
65
  set padding (EdgeInsets newValue) {
66 67 68 69 70 71
    if (_padding == newValue)
      return;
    _padding = newValue;
    markNeedsLayout();
  }

72
  @override
Adam Barth's avatar
Adam Barth committed
73 74 75 76 77
  void setupParentData(RenderBox child) {
    if (child.parentData is! ListParentData)
      child.parentData = new ListParentData();
  }

78
  double get _preferredExtent {
79 80
    if (itemExtent == null)
      return double.INFINITY;
81
    final int count = virtualChildCount;
82 83 84
    if (count == null)
      return double.INFINITY;
    double extent = itemExtent * count;
85
    if (padding != null)
86
      extent += padding.along(mainAxis);
87 88 89
    return extent;
  }

90
  double _computeIntrinsicWidth() {
91
    switch (mainAxis) {
92
      case Axis.vertical:
93 94
        assert(debugThrowIfNotCheckingIntrinsics());
        return 0.0;
95
      case Axis.horizontal:
96 97 98 99 100
        final double width = _preferredExtent;
        if (width.isFinite)
          return width;
        assert(debugThrowIfNotCheckingIntrinsics());
        return 0.0;
101
    }
pq's avatar
pq committed
102 103
    assert(mainAxis != null);
    return null;
104 105
  }

106
  @override
107 108
  double computeMinIntrinsicWidth(double height) {
    return _computeIntrinsicWidth();
Adam Barth's avatar
Adam Barth committed
109 110
  }

111
  @override
112 113
  double computeMaxIntrinsicWidth(double height) {
    return _computeIntrinsicWidth();
114 115
  }

116
  double _computeIntrinsicHeight() {
117
    switch (mainAxis) {
118
      case Axis.vertical:
119 120 121 122 123
        final double height = _preferredExtent;
        if (height.isFinite)
          return height;
        assert(debugThrowIfNotCheckingIntrinsics());
        return 0.0;
124
      case Axis.horizontal:
125 126
        assert(debugThrowIfNotCheckingIntrinsics());
        return 0.0;
127
    }
128 129
    assert(mainAxis != null);
    return null;
Adam Barth's avatar
Adam Barth committed
130 131
  }

132
  @override
133 134
  double computeMinIntrinsicHeight(double width) {
    return _computeIntrinsicHeight();
Adam Barth's avatar
Adam Barth committed
135 136
  }

137
  @override
138 139
  double computeMaxIntrinsicHeight(double width) {
    return _computeIntrinsicHeight();
Adam Barth's avatar
Adam Barth committed
140 141
  }

142
  @override
Adam Barth's avatar
Adam Barth committed
143
  void performLayout() {
144
    switch (mainAxis) {
145
      case Axis.vertical:
146 147 148
        size = new Size(constraints.maxWidth,
                        constraints.constrainHeight(_preferredExtent));
        break;
149
      case Axis.horizontal:
150 151 152 153
        size = new Size(constraints.constrainWidth(_preferredExtent),
                        constraints.maxHeight);
        break;
    }
Adam Barth's avatar
Adam Barth committed
154 155 156 157

    if (callback != null)
      invokeLayoutCallback(callback);

158 159 160 161 162 163 164 165 166
    double itemWidth;
    double itemHeight;

    double x = 0.0;
    double dx = 0.0;

    double y = 0.0;
    double dy = 0.0;

167
    switch (mainAxis) {
168
      case Axis.vertical:
169
        itemWidth = math.max(0.0, size.width - (padding == null ? 0.0 : padding.horizontal));
170
        itemHeight = itemExtent ?? size.height;
171
        x = padding != null ? padding.left : 0.0;
172
        dy = itemHeight;
173
        break;
174
      case Axis.horizontal:
175
        itemWidth = itemExtent ?? size.width;
176
        itemHeight = math.max(0.0, size.height - (padding == null ? 0.0 : padding.vertical));
177
        y = padding != null ? padding.top : 0.0;
178
        dx = itemWidth;
179 180 181
        break;
    }

Adam Barth's avatar
Adam Barth committed
182
    BoxConstraints innerConstraints =
183
        new BoxConstraints.tightFor(width: itemWidth, height: itemHeight);
Adam Barth's avatar
Adam Barth committed
184 185 186 187

    RenderBox child = firstChild;
    while (child != null) {
      child.layout(innerConstraints);
188

Adam Barth's avatar
Adam Barth committed
189
      final ListParentData childParentData = child.parentData;
190 191 192 193
      childParentData.offset = new Offset(x, y);
      x += dx;
      y += dy;

Adam Barth's avatar
Adam Barth committed
194 195 196 197 198
      assert(child.parentData == childParentData);
      child = childParentData.nextSibling;
    }
  }
}