Commit c27a9e82 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Add offstage attribute to OffStage (#5059)

Also, add a few more debugging flags.
parent 0265dd3c
...@@ -39,7 +39,6 @@ export 'src/rendering/layer.dart'; ...@@ -39,7 +39,6 @@ export 'src/rendering/layer.dart';
export 'src/rendering/list.dart'; export 'src/rendering/list.dart';
export 'src/rendering/node.dart'; export 'src/rendering/node.dart';
export 'src/rendering/object.dart'; export 'src/rendering/object.dart';
export 'src/rendering/overflow.dart';
export 'src/rendering/paragraph.dart'; export 'src/rendering/paragraph.dart';
export 'src/rendering/performance_overlay.dart'; export 'src/rendering/performance_overlay.dart';
export 'src/rendering/proxy_box.dart'; export 'src/rendering/proxy_box.dart';
......
...@@ -14,5 +14,6 @@ ...@@ -14,5 +14,6 @@
library scheduler; library scheduler;
export 'src/scheduler/binding.dart'; export 'src/scheduler/binding.dart';
export 'src/scheduler/debug.dart';
export 'src/scheduler/priority.dart'; export 'src/scheduler/priority.dart';
export 'src/scheduler/ticker.dart'; export 'src/scheduler/ticker.dart';
// 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.
import 'box.dart';
import 'object.dart';
/// Lays the child out as if it was in the tree, but without painting anything,
/// without making the child available for hit testing, and without taking any
/// room in the parent.
class RenderOffStage extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
/// Creates an off-stage render object.
RenderOffStage({ RenderBox child }) {
this.child = child;
}
@override
bool get sizedByParent => true;
@override
void performResize() {
size = constraints.smallest;
}
@override
void performLayout() {
if (child != null)
child.layout(constraints);
}
@override
bool hitTest(HitTestResult result, { Point position }) => false;
@override
void paint(PaintingContext context, Offset offset) { }
@override
void visitChildrenForSemantics(RenderObjectVisitor visitor) { }
}
...@@ -1980,6 +1980,108 @@ class RenderIgnorePointer extends RenderProxyBox { ...@@ -1980,6 +1980,108 @@ class RenderIgnorePointer extends RenderProxyBox {
} }
} }
/// Lays the child out as if it was in the tree, but without painting anything,
/// without making the child available for hit testing, and without taking any
/// room in the parent.
class RenderOffStage extends RenderProxyBox {
/// Creates an off-stage render object.
RenderOffStage({
bool offstage: true,
RenderBox child
}) : _offstage = offstage, super(child) {
assert(offstage != null);
}
/// Whether the child is hidden from the rest of the tree.
///
/// If true, the child is laid out as if it was in the tree, but without
/// painting anything, without making the child available for hit testing, and
/// without taking any room in the parent.
///
/// If false, the child is included in the tree as normal.
bool get offstage => _offstage;
bool _offstage;
set offstage(bool value) {
assert(value != null);
if (value == _offstage)
return;
_offstage = value;
markNeedsLayout();
}
@override
double computeMinIntrinsicWidth(double height) {
if (offstage)
return 0.0;
return super.computeMinIntrinsicWidth(height);
}
@override
double computeMaxIntrinsicWidth(double height) {
if (offstage)
return 0.0;
return super.computeMaxIntrinsicWidth(height);
}
@override
double computeMinIntrinsicHeight(double width) {
if (offstage)
return 0.0;
return super.computeMinIntrinsicHeight(width);
}
@override
double computeMaxIntrinsicHeight(double width) {
if (offstage)
return 0.0;
return super.computeMaxIntrinsicHeight(width);
}
@override
double computeDistanceToActualBaseline(TextBaseline baseline) {
if (offstage)
return null;
return super.computeDistanceToActualBaseline(baseline);
}
@override
bool get sizedByParent => offstage;
@override
void performResize() {
assert(offstage);
size = constraints.smallest;
}
@override
void performLayout() {
if (offstage) {
child?.layout(constraints);
} else {
super.performLayout();
}
}
@override
bool hitTest(HitTestResult result, { Point position }) {
return !offstage && super.hitTest(result, position: position);
}
@override
void paint(PaintingContext context, Offset offset) {
if (offstage)
return;
super.paint(context, offset);
}
@override
void visitChildrenForSemantics(RenderObjectVisitor visitor) {
if (offstage)
return;
super.visitChildrenForSemantics(visitor);
}
}
/// A render object that absorbs pointers during hit testing. /// A render object that absorbs pointers during hit testing.
/// ///
/// When [absorbing] is `true`, this render object prevents its subtree from /// When [absorbing] is `true`, this render object prevents its subtree from
......
...@@ -11,6 +11,7 @@ import 'dart:ui' show VoidCallback; ...@@ -11,6 +11,7 @@ import 'dart:ui' show VoidCallback;
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'debug.dart';
import 'priority.dart'; import 'priority.dart';
export 'dart:ui' show VoidCallback; export 'dart:ui' show VoidCallback;
...@@ -426,6 +427,11 @@ abstract class SchedulerBinding extends BindingBase { ...@@ -426,6 +427,11 @@ abstract class SchedulerBinding extends BindingBase {
Timeline.startSync('Frame'); Timeline.startSync('Frame');
_firstRawTimeStampInEpoch ??= rawTimeStamp; _firstRawTimeStampInEpoch ??= rawTimeStamp;
Duration timeStamp = _adjustForEpoch(rawTimeStamp); Duration timeStamp = _adjustForEpoch(rawTimeStamp);
assert(() {
if (debugPrintBeginFrameBanner)
print('━‬━‬━‬━‬━‬━‬━┫ Begin Frame ($timeStamp) ┣━‬━‬━‬━‬━‬━‬━');
return true;
});
_lastRawTimeStamp = rawTimeStamp; _lastRawTimeStamp = rawTimeStamp;
assert(!_isProducingFrame); assert(!_isProducingFrame);
_isProducingFrame = true; _isProducingFrame = true;
......
// 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.
/// Print a banner at the beginning of each frame.
bool debugPrintBeginFrameBanner = false;
...@@ -1087,11 +1087,33 @@ class SizedOverflowBox extends SingleChildRenderObjectWidget { ...@@ -1087,11 +1087,33 @@ class SizedOverflowBox extends SingleChildRenderObjectWidget {
/// room in the parent. /// room in the parent.
class OffStage extends SingleChildRenderObjectWidget { class OffStage extends SingleChildRenderObjectWidget {
/// Creates a widget that visually hides its child. /// Creates a widget that visually hides its child.
OffStage({ Key key, Widget child }) OffStage({ Key key, this.offstage: true, Widget child })
: super(key: key, child: child); : super(key: key, child: child) {
assert(offstage != null);
}
/// Whether the child is hidden from the rest of the tree.
///
/// If true, the child is laid out as if it was in the tree, but without
/// painting anything, without making the child available for hit testing, and
/// without taking any room in the parent.
///
/// If false, the child is included in the tree as normal.
final bool offstage;
@override
RenderOffStage createRenderObject(BuildContext context) => new RenderOffStage(offstage: offstage);
@override @override
RenderOffStage createRenderObject(BuildContext context) => new RenderOffStage(); void updateRenderObject(BuildContext context, RenderOffStage renderObject) {
renderObject.offstage = offstage;
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('offstage: $offstage');
}
} }
/// A widget that attempts to size the child to a specific aspect ratio. /// A widget that attempts to size the child to a specific aspect ratio.
......
...@@ -8,6 +8,9 @@ import 'package:flutter/foundation.dart'; ...@@ -8,6 +8,9 @@ import 'package:flutter/foundation.dart';
import 'framework.dart'; import 'framework.dart';
import 'table.dart'; import 'table.dart';
/// Log the dirty widgets that are built each frame.
bool debugPrintRebuildDirtyWidgets = false;
Key _firstNonUniqueKey(Iterable<Widget> widgets) { Key _firstNonUniqueKey(Iterable<Widget> widgets) {
Set<Key> keySet = new HashSet<Key>(); Set<Key> keySet = new HashSet<Key>();
for (Widget widget in widgets) { for (Widget widget in widgets) {
......
...@@ -1236,6 +1236,11 @@ class BuildOwner { ...@@ -1236,6 +1236,11 @@ class BuildOwner {
int dirtyCount = _dirtyElements.length; int dirtyCount = _dirtyElements.length;
int index = 0; int index = 0;
while (index < dirtyCount) { while (index < dirtyCount) {
assert(() {
if (debugPrintRebuildDirtyWidgets)
debugPrint('Rebuilding ${_dirtyElements[index].widget}');
return true;
});
_dirtyElements[index].rebuild(); _dirtyElements[index].rebuild();
index += 1; index += 1;
if (dirtyCount < _dirtyElements.length) { if (dirtyCount < _dirtyElements.length) {
......
...@@ -136,11 +136,11 @@ class _FindsWidgetMatcher extends Matcher { ...@@ -136,11 +136,11 @@ class _FindsWidgetMatcher extends Matcher {
} }
} }
bool _hasAncestorOfType(Finder finder, Type targetType) { bool _hasAncestorMatching(Finder finder, bool predicate(Widget widget)) {
expect(finder, findsOneWidget); expect(finder, findsOneWidget);
bool result = false; bool result = false;
finder.evaluate().single.visitAncestorElements((Element ancestor) { finder.evaluate().single.visitAncestorElements((Element ancestor) {
if (ancestor.widget.runtimeType == targetType) { if (predicate(ancestor.widget)) {
result = true; result = true;
return false; return false;
} }
...@@ -149,11 +149,22 @@ bool _hasAncestorOfType(Finder finder, Type targetType) { ...@@ -149,11 +149,22 @@ bool _hasAncestorOfType(Finder finder, Type targetType) {
return result; return result;
} }
bool _hasAncestorOfType(Finder finder, Type targetType) {
return _hasAncestorMatching(finder, (Widget widget) => widget.runtimeType == targetType);
}
class _IsOffStage extends Matcher { class _IsOffStage extends Matcher {
const _IsOffStage(); const _IsOffStage();
@override @override
bool matches(Finder finder, Map<dynamic, dynamic> matchState) => _hasAncestorOfType(finder, OffStage); bool matches(Finder finder, Map<dynamic, dynamic> matchState) {
return _hasAncestorMatching(finder, (Widget widget) {
if (widget.runtimeType != OffStage)
return false;
OffStage offstage = widget;
return offstage.offstage;
});
}
@override @override
Description describe(Description description) => description.add('offstage'); Description describe(Description description) => description.add('offstage');
...@@ -163,7 +174,20 @@ class _IsOnStage extends Matcher { ...@@ -163,7 +174,20 @@ class _IsOnStage extends Matcher {
const _IsOnStage(); const _IsOnStage();
@override @override
bool matches(Finder finder, Map<dynamic, dynamic> matchState) => !_hasAncestorOfType(finder, OffStage); bool matches(Finder finder, Map<dynamic, dynamic> matchState) {
expect(finder, findsOneWidget);
bool result = true;
finder.evaluate().single.visitAncestorElements((Element ancestor) {
Widget widget = ancestor.widget;
if (widget.runtimeType == OffStage) {
OffStage offstage = widget;
result = !offstage.offstage;
return false;
}
return true;
});
return result;
}
@override @override
Description describe(Description description) => description.add('onstage'); Description describe(Description description) => description.add('onstage');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment