view.dart 5.07 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 'dart:developer';
6
import 'dart:ui' as ui show Scene, SceneBuilder, window;
7

8
import 'package:vector_math/vector_math_64.dart';
9

10
import 'box.dart';
11
import 'debug.dart';
12 13
import 'layer.dart';
import 'object.dart';
14
import 'binding.dart';
15

16
/// The layout constraints for the root render object.
17 18
class ViewConfiguration {
  const ViewConfiguration({
19
    this.size: Size.zero,
20
    this.devicePixelRatio: 1.0,
21 22
    this.orientation
  });
23

24
  /// The size of the output surface.
25
  final Size size;
26

27 28 29
  /// The pixel density of the output surface.
  final double devicePixelRatio;

30
  /// The orientation of the output surface (aspirational).
31
  final int orientation;
Hixie's avatar
Hixie committed
32

33 34 35 36 37
  /// Creates a transformation matrix that applies the [devicePixelRatio].
  Matrix4 toMatrix() {
    return new Matrix4.diagonal3Values(devicePixelRatio, devicePixelRatio, 1.0);
  }

38
  @override
39
  String toString() => '$size at ${devicePixelRatio}x';
40 41
}

42
/// The root of the render tree.
43 44
///
/// The view represents the total output surface of the render tree and handles
45
/// bootstrapping the rendering pipeline. The view has a unique child
46
/// [RenderBox], which is required to fill the entire output surface.
47 48 49
class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
  RenderView({
    RenderBox child,
50 51 52
    this.timeForRotation: const Duration(microseconds: 83333),
    ViewConfiguration configuration
  }) : _configuration = configuration {
53 54 55
    this.child = child;
  }

56
  /// The amount of time the screen rotation animation should last (aspirational).
57 58
  Duration timeForRotation;

59
  /// The current layout size of the view.
60
  Size get size => _size;
61
  Size _size = Size.zero;
62

63
  /// The current orientation of the view (aspirational).
64
  int get orientation => _orientation;
65
  int _orientation; // 0..3
66

67
  /// The constraints used for the root layout.
68 69
  ViewConfiguration get configuration => _configuration;
  ViewConfiguration _configuration;
70
  set configuration(ViewConfiguration value) {
71
    if (configuration == value)
72
      return;
73
    _configuration = value;
74
    replaceRootLayer(new TransformLayer(transform: configuration.toMatrix()));
75 76 77
    markNeedsLayout();
  }

78
  /// Bootstrap the rendering pipeline by scheduling the first frame.
79
  void scheduleInitialFrame() {
80
    assert(owner != null);
81
    scheduleInitialLayout();
82
    scheduleInitialPaint(new TransformLayer(transform: configuration.toMatrix()));
83
    owner.requestVisualUpdate();
84 85
  }

86 87
  // We never call layout() on this class, so this should never get
  // checked. (This class is laid out using scheduleInitialLayout().)
88
  @override
89
  void debugAssertDoesMeetConstraints() { assert(false); }
90

91
  @override
92 93 94 95
  void performResize() {
    assert(false);
  }

96
  @override
97
  void performLayout() {
98
    if (configuration.orientation != _orientation) {
99
      if (_orientation != null && child != null)
100 101
        child.rotate(oldAngle: _orientation, newAngle: configuration.orientation, time: timeForRotation);
      _orientation = configuration.orientation;
102
    }
103
    _size = configuration.size;
104 105 106 107 108 109
    assert(!_size.isInfinite);

    if (child != null)
      child.layout(new BoxConstraints.tight(_size));
  }

110
  @override
111 112 113 114 115
  void rotate({ int oldAngle, int newAngle, Duration time }) {
    assert(false); // nobody tells the screen to rotate, the whole rotate() dance is started from our performResize()
  }

  bool hitTest(HitTestResult result, { Point position }) {
116 117
    if (child != null)
      child.hitTest(result, position: position);
118 119 120 121
    result.add(new HitTestEntry(this));
    return true;
  }

122
  @override
123
  bool get isRepaintBoundary => true;
Hixie's avatar
Hixie committed
124

125
  @override
126 127
  void paint(PaintingContext context, Offset offset) {
    if (child != null)
Adam Barth's avatar
Adam Barth committed
128
      context.paintChild(child, offset);
129 130
  }

131
  /// Uploads the composited layer tree to the engine.
132 133
  ///
  /// Actually causes the output of the rendering pipeline to appear on screen.
134
  void compositeFrame() {
135
    Timeline.startSync('Compositing');
136
    try {
137
      ui.SceneBuilder builder = new ui.SceneBuilder();
138
      layer.addToScene(builder, Offset.zero);
139 140 141
      ui.Scene scene = builder.build();
      ui.window.render(scene);
      scene.dispose();
142
      assert(() {
143 144
        if (debugRepaintRainbowEnabled)
          debugCurrentRepaintColor = debugCurrentRepaintColor.withHue(debugCurrentRepaintColor.hue + debugRepaintRainbowHueIncrement);
145 146
        return true;
      });
147
    } finally {
148
      Timeline.finishSync();
149 150 151
    }
  }

152
  @override
153
  Rect get paintBounds => Point.origin & size;
154 155

  @override
Hixie's avatar
Hixie committed
156
  Rect get semanticBounds => Point.origin & size;
Hixie's avatar
Hixie committed
157

158
  @override
159 160 161 162 163
  void debugFillDescription(List<String> description) {
    // call to ${super.debugFillDescription(prefix)} is omitted because the root superclasses don't include any interesting information for this class
    description.add('window size: ${ui.window.size} (in device pixels)');
    description.add('device pixel ratio: ${ui.window.devicePixelRatio} (device pixels per logical pixel)');
    description.add('configuration: $configuration (in logical pixels)');
164
  }
165
}