-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathview.dart
300 lines (268 loc) · 10.9 KB
/
view.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:developer';
import 'dart:io' show Platform;
import 'dart:ui' as ui show Scene, SceneBuilder, Window;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart' show MouseTrackerAnnotation;
import 'package:flutter/services.dart';
import 'package:vector_math/vector_math_64.dart';
import 'binding.dart';
import 'box.dart';
import 'debug.dart';
import 'layer.dart';
import 'object.dart';
/// The layout constraints for the root render object.
@immutable
class ViewConfiguration {
/// Creates a view configuration.
///
/// By default, the view has zero [size] and a [devicePixelRatio] of 1.0.
const ViewConfiguration({
this.size = Size.zero,
this.devicePixelRatio = 1.0,
});
/// The size of the output surface.
final Size size;
/// The pixel density of the output surface.
final double devicePixelRatio;
/// Creates a transformation matrix that applies the [devicePixelRatio].
Matrix4 toMatrix() {
return Matrix4.diagonal3Values(devicePixelRatio, devicePixelRatio, 1.0);
}
@override
String toString() => '$size at ${debugFormatDouble(devicePixelRatio)}x';
}
/// The root of the render tree.
///
/// The view represents the total output surface of the render tree and handles
/// bootstrapping the rendering pipeline. The view has a unique child
/// [RenderBox], which is required to fill the entire output surface.
class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
/// Creates the root of the render tree.
///
/// Typically created by the binding (e.g., [RendererBinding]).
///
/// The [configuration] must not be null.
RenderView({
RenderBox child,
@required ViewConfiguration configuration,
@required ui.Window window,
}) : assert(configuration != null),
_configuration = configuration,
_window = window {
this.child = child;
}
/// The current layout size of the view.
Size get size => _size;
Size _size = Size.zero;
/// The constraints used for the root layout.
ViewConfiguration get configuration => _configuration;
ViewConfiguration _configuration;
/// The configuration is initially set by the `configuration` argument
/// passed to the constructor.
///
/// Always call [prepareInitialFrame] before changing the configuration.
set configuration(ViewConfiguration value) {
assert(value != null);
if (configuration == value)
return;
_configuration = value;
replaceRootLayer(_updateMatricesAndCreateNewRootLayer());
assert(_rootTransform != null);
markNeedsLayout();
}
final ui.Window _window;
/// Whether Flutter should automatically compute the desired system UI.
///
/// When this setting is enabled, Flutter will hit-test the layer tree at the
/// top and bottom of the screen on each frame looking for an
/// [AnnotatedRegionLayer] with an instance of a [SystemUiOverlayStyle]. The
/// hit-test result from the top of the screen provides the status bar settings
/// and the hit-test result from the bottom of the screen provides the system
/// nav bar settings.
///
/// Setting this to false does not cause previous automatic adjustments to be
/// reset, nor does setting it to true cause the app to update immediately.
///
/// If you want to imperatively set the system ui style instead, it is
/// recommended that [automaticSystemUiAdjustment] is set to false.
///
/// See also:
///
/// * [AnnotatedRegion], for placing [SystemUiOverlayStyle] in the layer tree.
/// * [SystemChrome.setSystemUIOverlayStyle], for imperatively setting the system ui style.
bool automaticSystemUiAdjustment = true;
/// Bootstrap the rendering pipeline by scheduling the first frame.
///
/// Deprecated. Call [prepareInitialFrame] followed by a call to
/// [PipelineOwner.requestVisualUpdate] on [owner] instead.
@Deprecated(
'Call prepareInitialFrame followed by owner.requestVisualUpdate() instead. '
'This feature was deprecated after v1.10.0.'
)
void scheduleInitialFrame() {
prepareInitialFrame();
owner.requestVisualUpdate();
}
/// Bootstrap the rendering pipeline by preparing the first frame.
///
/// This should only be called once, and must be called before changing
/// [configuration]. It is typically called immediately after calling the
/// constructor.
///
/// This does not actually schedule the first frame. Call
/// [PipelineOwner.requestVisualUpdate] on [owner] to do that.
void prepareInitialFrame() {
assert(owner != null);
assert(_rootTransform == null);
scheduleInitialLayout();
scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer());
assert(_rootTransform != null);
}
Matrix4 _rootTransform;
TransformLayer _updateMatricesAndCreateNewRootLayer() {
_rootTransform = configuration.toMatrix();
final TransformLayer rootLayer = TransformLayer(transform: _rootTransform);
rootLayer.attach(this);
assert(_rootTransform != null);
return rootLayer;
}
// We never call layout() on this class, so this should never get
// checked. (This class is laid out using scheduleInitialLayout().)
@override
void debugAssertDoesMeetConstraints() { assert(false); }
@override
void performResize() {
assert(false);
}
@override
void performLayout() {
assert(_rootTransform != null);
_size = configuration.size;
assert(_size.isFinite);
if (child != null)
child.layout(BoxConstraints.tight(_size));
}
@override
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()
}
/// Determines the set of render objects located at the given position.
///
/// Returns true if the given point is contained in this render object or one
/// of its descendants. Adds any render objects that contain the point to the
/// given hit test result.
///
/// The [position] argument is in the coordinate system of the render view,
/// which is to say, in logical pixels. This is not necessarily the same
/// coordinate system as that expected by the root [Layer], which will
/// normally be in physical (device) pixels.
bool hitTest(HitTestResult result, { Offset position }) {
if (child != null)
child.hitTest(BoxHitTestResult.wrap(result), position: position);
result.add(HitTestEntry(this));
return true;
}
/// Determines the set of mouse tracker annotations at the given position.
///
/// See also:
///
/// * [Layer.findAllAnnotations], which is used by this method to find all
/// [AnnotatedRegionLayer]s annotated for mouse tracking.
Iterable<MouseTrackerAnnotation> hitTestMouseTrackers(Offset position) {
// Layer hit testing is done using device pixels, so we have to convert
// the logical coordinates of the event location back to device pixels
// here.
return layer.findAllAnnotations<MouseTrackerAnnotation>(
position * configuration.devicePixelRatio
).annotations;
}
@override
bool get isRepaintBoundary => true;
@override
void paint(PaintingContext context, Offset offset) {
if (child != null)
context.paintChild(child, offset);
}
@override
void applyPaintTransform(RenderBox child, Matrix4 transform) {
assert(_rootTransform != null);
transform.multiply(_rootTransform);
super.applyPaintTransform(child, transform);
}
/// Uploads the composited layer tree to the engine.
///
/// Actually causes the output of the rendering pipeline to appear on screen.
void compositeFrame() {
Timeline.startSync('Compositing', arguments: timelineWhitelistArguments);
try {
final ui.SceneBuilder builder = ui.SceneBuilder();
final ui.Scene scene = layer.buildScene(builder);
if (automaticSystemUiAdjustment)
_updateSystemChrome();
_window.render(scene);
scene.dispose();
assert(() {
if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
debugCurrentRepaintColor = debugCurrentRepaintColor.withHue((debugCurrentRepaintColor.hue + 2.0) % 360.0);
return true;
}());
} finally {
Timeline.finishSync();
}
}
void _updateSystemChrome() {
final Rect bounds = paintBounds;
final Offset top = Offset(bounds.center.dx, _window.padding.top / _window.devicePixelRatio);
final Offset bottom = Offset(bounds.center.dx, bounds.center.dy - _window.padding.bottom / _window.devicePixelRatio);
final SystemUiOverlayStyle upperOverlayStyle = layer.find<SystemUiOverlayStyle>(top);
// Only android has a customizable system navigation bar.
SystemUiOverlayStyle lowerOverlayStyle;
switch (defaultTargetPlatform) {
case TargetPlatform.android:
lowerOverlayStyle = layer.find<SystemUiOverlayStyle>(bottom);
break;
case TargetPlatform.fuchsia:
case TargetPlatform.iOS:
case TargetPlatform.macOS:
break;
}
// If there are no overlay styles in the UI don't bother updating.
if (upperOverlayStyle != null || lowerOverlayStyle != null) {
final SystemUiOverlayStyle overlayStyle = SystemUiOverlayStyle(
statusBarBrightness: upperOverlayStyle?.statusBarBrightness,
statusBarIconBrightness: upperOverlayStyle?.statusBarIconBrightness,
statusBarColor: upperOverlayStyle?.statusBarColor,
systemNavigationBarColor: lowerOverlayStyle?.systemNavigationBarColor,
systemNavigationBarDividerColor: lowerOverlayStyle?.systemNavigationBarDividerColor,
systemNavigationBarIconBrightness: lowerOverlayStyle?.systemNavigationBarIconBrightness,
);
SystemChrome.setSystemUIOverlayStyle(overlayStyle);
}
}
@override
Rect get paintBounds => Offset.zero & (size * configuration.devicePixelRatio);
@override
Rect get semanticBounds {
assert(_rootTransform != null);
return MatrixUtils.transformRect(_rootTransform, Offset.zero & size);
}
@override
// ignore: MUST_CALL_SUPER
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
// call to ${super.debugFillProperties(description)} is omitted because the
// root superclasses don't include any interesting information for this
// class
assert(() {
properties.add(DiagnosticsNode.message('debug mode enabled - ${kIsWeb ? 'Web' : Platform.operatingSystem}'));
return true;
}());
properties.add(DiagnosticsProperty<Size>('window size', _window.physicalSize, tooltip: 'in physical pixels'));
properties.add(DoubleProperty('device pixel ratio', _window.devicePixelRatio, tooltip: 'physical pixels per logical pixel'));
properties.add(DiagnosticsProperty<ViewConfiguration>('configuration', configuration, tooltip: 'in logical pixels'));
if (_window.semanticsEnabled)
properties.add(DiagnosticsNode.message('semantics enabled'));
}
}