1// Copyright 2015 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import 'dart:async'; 6import 'dart:developer'; 7import 'dart:typed_data'; 8 9import 'package:flutter/foundation.dart'; 10import 'package:flutter/gestures.dart'; 11import 'package:flutter/scheduler.dart'; 12import 'package:flutter/semantics.dart'; 13import 'package:flutter/services.dart'; 14 15import 'box.dart'; 16import 'debug.dart'; 17import 'object.dart'; 18import 'view.dart'; 19 20export 'package:flutter/gestures.dart' show HitTestResult; 21 22// Examples can assume: 23// dynamic context; 24 25/// The glue between the render tree and the Flutter engine. 26mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { 27 @override 28 void initInstances() { 29 super.initInstances(); 30 _instance = this; 31 _pipelineOwner = PipelineOwner( 32 onNeedVisualUpdate: ensureVisualUpdate, 33 onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, 34 onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, 35 ); 36 window 37 ..onMetricsChanged = handleMetricsChanged 38 ..onTextScaleFactorChanged = handleTextScaleFactorChanged 39 ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged 40 ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged 41 ..onSemanticsAction = _handleSemanticsAction; 42 initRenderView(); 43 _handleSemanticsEnabledChanged(); 44 assert(renderView != null); 45 addPersistentFrameCallback(_handlePersistentFrameCallback); 46 _mouseTracker = _createMouseTracker(); 47 } 48 49 /// The current [RendererBinding], if one has been created. 50 static RendererBinding get instance => _instance; 51 static RendererBinding _instance; 52 53 @override 54 void initServiceExtensions() { 55 super.initServiceExtensions(); 56 57 assert(() { 58 // these service extensions only work in debug mode 59 registerBoolServiceExtension( 60 name: 'debugPaint', 61 getter: () async => debugPaintSizeEnabled, 62 setter: (bool value) { 63 if (debugPaintSizeEnabled == value) 64 return Future<void>.value(); 65 debugPaintSizeEnabled = value; 66 return _forceRepaint(); 67 }, 68 ); 69 registerBoolServiceExtension( 70 name: 'debugPaintBaselinesEnabled', 71 getter: () async => debugPaintBaselinesEnabled, 72 setter: (bool value) { 73 if (debugPaintBaselinesEnabled == value) 74 return Future<void>.value(); 75 debugPaintBaselinesEnabled = value; 76 return _forceRepaint(); 77 }, 78 ); 79 registerBoolServiceExtension( 80 name: 'repaintRainbow', 81 getter: () async => debugRepaintRainbowEnabled, 82 setter: (bool value) { 83 final bool repaint = debugRepaintRainbowEnabled && !value; 84 debugRepaintRainbowEnabled = value; 85 if (repaint) 86 return _forceRepaint(); 87 return Future<void>.value(); 88 }, 89 ); 90 registerBoolServiceExtension( 91 name: 'debugCheckElevationsEnabled', 92 getter: () async => debugCheckElevationsEnabled, 93 setter: (bool value) { 94 if (debugCheckElevationsEnabled == value) { 95 return Future<void>.value(); 96 } 97 debugCheckElevationsEnabled = value; 98 return _forceRepaint(); 99 } 100 ); 101 registerSignalServiceExtension( 102 name: 'debugDumpLayerTree', 103 callback: () { 104 debugDumpLayerTree(); 105 return debugPrintDone; 106 }, 107 ); 108 return true; 109 }()); 110 111 if (!kReleaseMode) { 112 // these service extensions work in debug or profile mode 113 registerSignalServiceExtension( 114 name: 'debugDumpRenderTree', 115 callback: () { 116 debugDumpRenderTree(); 117 return debugPrintDone; 118 }, 119 ); 120 121 registerSignalServiceExtension( 122 name: 'debugDumpSemanticsTreeInTraversalOrder', 123 callback: () { 124 debugDumpSemanticsTree(DebugSemanticsDumpOrder.traversalOrder); 125 return debugPrintDone; 126 }, 127 ); 128 129 registerSignalServiceExtension( 130 name: 'debugDumpSemanticsTreeInInverseHitTestOrder', 131 callback: () { 132 debugDumpSemanticsTree(DebugSemanticsDumpOrder.inverseHitTest); 133 return debugPrintDone; 134 }, 135 ); 136 } 137 } 138 139 /// Creates a [RenderView] object to be the root of the 140 /// [RenderObject] rendering tree, and initializes it so that it 141 /// will be rendered when the engine is next ready to display a 142 /// frame. 143 /// 144 /// Called automatically when the binding is created. 145 void initRenderView() { 146 assert(renderView == null); 147 renderView = RenderView(configuration: createViewConfiguration(), window: window); 148 renderView.scheduleInitialFrame(); 149 } 150 151 /// The object that manages state about currently connected mice, for hover 152 /// notification. 153 MouseTracker get mouseTracker => _mouseTracker; 154 MouseTracker _mouseTracker; 155 156 /// The render tree's owner, which maintains dirty state for layout, 157 /// composite, paint, and accessibility semantics 158 PipelineOwner get pipelineOwner => _pipelineOwner; 159 PipelineOwner _pipelineOwner; 160 161 /// The render tree that's attached to the output surface. 162 RenderView get renderView => _pipelineOwner.rootNode; 163 /// Sets the given [RenderView] object (which must not be null), and its tree, to 164 /// be the new render tree to display. The previous tree, if any, is detached. 165 set renderView(RenderView value) { 166 assert(value != null); 167 _pipelineOwner.rootNode = value; 168 } 169 170 /// Called when the system metrics change. 171 /// 172 /// See [Window.onMetricsChanged]. 173 @protected 174 void handleMetricsChanged() { 175 assert(renderView != null); 176 renderView.configuration = createViewConfiguration(); 177 scheduleForcedFrame(); 178 } 179 180 /// Called when the platform text scale factor changes. 181 /// 182 /// See [Window.onTextScaleFactorChanged]. 183 @protected 184 void handleTextScaleFactorChanged() { } 185 186 /// {@template on_platform_brightness_change} 187 /// Called when the platform brightness changes. 188 /// 189 /// The current platform brightness can be queried either from a Flutter 190 /// binding, or from a [MediaQuery] widget. 191 /// 192 /// {@tool sample} 193 /// Querying [Window.platformBrightness]. 194 /// 195 /// ```dart 196 /// final Brightness brightness = WidgetsBinding.instance.window.platformBrightness; 197 /// ``` 198 /// {@end-tool} 199 /// 200 /// {@tool sample} 201 /// Querying [MediaQuery] directly. 202 /// 203 /// ```dart 204 /// final Brightness brightness = MediaQuery.platformBrightnessOf(context); 205 /// ``` 206 /// {@end-tool} 207 /// 208 /// {@tool sample} 209 /// Querying [MediaQueryData]. 210 /// 211 /// ```dart 212 /// final MediaQueryData mediaQueryData = MediaQuery.of(context); 213 /// final Brightness brightness = mediaQueryData.platformBrightness; 214 /// ``` 215 /// {@end-tool} 216 /// 217 /// See [Window.onPlatformBrightnessChanged]. 218 /// {@endtemplate} 219 @protected 220 void handlePlatformBrightnessChanged() { } 221 222 /// Returns a [ViewConfiguration] configured for the [RenderView] based on the 223 /// current environment. 224 /// 225 /// This is called during construction and also in response to changes to the 226 /// system metrics. 227 /// 228 /// Bindings can override this method to change what size or device pixel 229 /// ratio the [RenderView] will use. For example, the testing framework uses 230 /// this to force the display into 800x600 when a test is run on the device 231 /// using `flutter run`. 232 ViewConfiguration createViewConfiguration() { 233 final double devicePixelRatio = window.devicePixelRatio; 234 return ViewConfiguration( 235 size: window.physicalSize / devicePixelRatio, 236 devicePixelRatio: devicePixelRatio, 237 ); 238 } 239 240 SemanticsHandle _semanticsHandle; 241 242 // Creates a [MouseTracker] which manages state about currently connected 243 // mice, for hover notification. 244 MouseTracker _createMouseTracker() { 245 return MouseTracker(pointerRouter, renderView.hitTestMouseTrackers); 246 } 247 248 void _handleSemanticsEnabledChanged() { 249 setSemanticsEnabled(window.semanticsEnabled); 250 } 251 252 /// Whether the render tree associated with this binding should produce a tree 253 /// of [SemanticsNode] objects. 254 void setSemanticsEnabled(bool enabled) { 255 if (enabled) { 256 _semanticsHandle ??= _pipelineOwner.ensureSemantics(); 257 } else { 258 _semanticsHandle?.dispose(); 259 _semanticsHandle = null; 260 } 261 } 262 263 void _handleSemanticsAction(int id, SemanticsAction action, ByteData args) { 264 _pipelineOwner.semanticsOwner?.performAction( 265 id, 266 action, 267 args != null ? const StandardMessageCodec().decodeMessage(args) : null, 268 ); 269 } 270 271 void _handleSemanticsOwnerCreated() { 272 renderView.scheduleInitialSemantics(); 273 } 274 275 void _handleSemanticsOwnerDisposed() { 276 renderView.clearSemantics(); 277 } 278 279 void _handlePersistentFrameCallback(Duration timeStamp) { 280 drawFrame(); 281 } 282 283 /// Pump the rendering pipeline to generate a frame. 284 /// 285 /// This method is called by [handleDrawFrame], which itself is called 286 /// automatically by the engine when it is time to lay out and paint a frame. 287 /// 288 /// Each frame consists of the following phases: 289 /// 290 /// 1. The animation phase: The [handleBeginFrame] method, which is registered 291 /// with [Window.onBeginFrame], invokes all the transient frame callbacks 292 /// registered with [scheduleFrameCallback], in registration order. This 293 /// includes all the [Ticker] instances that are driving [AnimationController] 294 /// objects, which means all of the active [Animation] objects tick at this 295 /// point. 296 /// 297 /// 2. Microtasks: After [handleBeginFrame] returns, any microtasks that got 298 /// scheduled by transient frame callbacks get to run. This typically includes 299 /// callbacks for futures from [Ticker]s and [AnimationController]s that 300 /// completed this frame. 301 /// 302 /// After [handleBeginFrame], [handleDrawFrame], which is registered with 303 /// [Window.onDrawFrame], is called, which invokes all the persistent frame 304 /// callbacks, of which the most notable is this method, [drawFrame], which 305 /// proceeds as follows: 306 /// 307 /// 3. The layout phase: All the dirty [RenderObject]s in the system are laid 308 /// out (see [RenderObject.performLayout]). See [RenderObject.markNeedsLayout] 309 /// for further details on marking an object dirty for layout. 310 /// 311 /// 4. The compositing bits phase: The compositing bits on any dirty 312 /// [RenderObject] objects are updated. See 313 /// [RenderObject.markNeedsCompositingBitsUpdate]. 314 /// 315 /// 5. The paint phase: All the dirty [RenderObject]s in the system are 316 /// repainted (see [RenderObject.paint]). This generates the [Layer] tree. See 317 /// [RenderObject.markNeedsPaint] for further details on marking an object 318 /// dirty for paint. 319 /// 320 /// 6. The compositing phase: The layer tree is turned into a [Scene] and 321 /// sent to the GPU. 322 /// 323 /// 7. The semantics phase: All the dirty [RenderObject]s in the system have 324 /// their semantics updated (see [RenderObject.semanticsAnnotator]). This 325 /// generates the [SemanticsNode] tree. See 326 /// [RenderObject.markNeedsSemanticsUpdate] for further details on marking an 327 /// object dirty for semantics. 328 /// 329 /// For more details on steps 3-7, see [PipelineOwner]. 330 /// 331 /// 8. The finalization phase: After [drawFrame] returns, [handleDrawFrame] 332 /// then invokes post-frame callbacks (registered with [addPostFrameCallback]). 333 /// 334 /// Some bindings (for example, the [WidgetsBinding]) add extra steps to this 335 /// list (for example, see [WidgetsBinding.drawFrame]). 336 // 337 // When editing the above, also update widgets/binding.dart's copy. 338 @protected 339 void drawFrame() { 340 assert(renderView != null); 341 pipelineOwner.flushLayout(); 342 pipelineOwner.flushCompositingBits(); 343 pipelineOwner.flushPaint(); 344 renderView.compositeFrame(); // this sends the bits to the GPU 345 pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. 346 } 347 348 @override 349 Future<void> performReassemble() async { 350 await super.performReassemble(); 351 Timeline.startSync('Dirty Render Tree', arguments: timelineWhitelistArguments); 352 try { 353 renderView.reassemble(); 354 } finally { 355 Timeline.finishSync(); 356 } 357 scheduleWarmUpFrame(); 358 await endOfFrame; 359 } 360 361 @override 362 void hitTest(HitTestResult result, Offset position) { 363 assert(renderView != null); 364 renderView.hitTest(result, position: position); 365 super.hitTest(result, position); 366 } 367 368 Future<void> _forceRepaint() { 369 RenderObjectVisitor visitor; 370 visitor = (RenderObject child) { 371 child.markNeedsPaint(); 372 child.visitChildren(visitor); 373 }; 374 instance?.renderView?.visitChildren(visitor); 375 return endOfFrame; 376 } 377} 378 379/// Prints a textual representation of the entire render tree. 380void debugDumpRenderTree() { 381 debugPrint(RendererBinding.instance?.renderView?.toStringDeep() ?? 'Render tree unavailable.'); 382} 383 384/// Prints a textual representation of the entire layer tree. 385void debugDumpLayerTree() { 386 debugPrint(RendererBinding.instance?.renderView?.debugLayer?.toStringDeep() ?? 'Layer tree unavailable.'); 387} 388 389/// Prints a textual representation of the entire semantics tree. 390/// This will only work if there is a semantics client attached. 391/// Otherwise, a notice that no semantics are available will be printed. 392/// 393/// The order in which the children of a [SemanticsNode] will be printed is 394/// controlled by the [childOrder] parameter. 395void debugDumpSemanticsTree(DebugSemanticsDumpOrder childOrder) { 396 debugPrint(RendererBinding.instance?.renderView?.debugSemantics?.toStringDeep(childOrder: childOrder) ?? 'Semantics not collected.'); 397} 398 399/// A concrete binding for applications that use the Rendering framework 400/// directly. This is the glue that binds the framework to the Flutter engine. 401/// 402/// You would only use this binding if you are writing to the 403/// rendering layer directly. If you are writing to a higher-level 404/// library, such as the Flutter Widgets library, then you would use 405/// that layer's binding. 406/// 407/// See also [BindingBase]. 408class RenderingFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, SemanticsBinding, RendererBinding { 409 /// Creates a binding for the rendering layer. 410 /// 411 /// The `root` render box is attached directly to the [renderView] and is 412 /// given constraints that require it to fill the window. 413 RenderingFlutterBinding({ RenderBox root }) { 414 assert(renderView != null); 415 renderView.child = root; 416 } 417} 418