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';
export 'src/rendering/list.dart';
export 'src/rendering/node.dart';
export 'src/rendering/object.dart';
export 'src/rendering/overflow.dart';
export 'src/rendering/paragraph.dart';
export 'src/rendering/performance_overlay.dart';
export 'src/rendering/proxy_box.dart';
......
......@@ -14,5 +14,6 @@
library scheduler;
export 'src/scheduler/binding.dart';
export 'src/scheduler/debug.dart';
export 'src/scheduler/priority.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 {
}
}
/// 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.
///
/// When [absorbing] is `true`, this render object prevents its subtree from
......
......@@ -11,6 +11,7 @@ import 'dart:ui' show VoidCallback;
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'debug.dart';
import 'priority.dart';
export 'dart:ui' show VoidCallback;
......@@ -426,6 +427,11 @@ abstract class SchedulerBinding extends BindingBase {
Timeline.startSync('Frame');
_firstRawTimeStampInEpoch ??= rawTimeStamp;
Duration timeStamp = _adjustForEpoch(rawTimeStamp);
assert(() {
if (debugPrintBeginFrameBanner)
print('━‬━‬━‬━‬━‬━‬━┫ Begin Frame ($timeStamp) ┣━‬━‬━‬━‬━‬━‬━');
return true;
});
_lastRawTimeStamp = rawTimeStamp;
assert(!_isProducingFrame);
_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 {
/// room in the parent.
class OffStage extends SingleChildRenderObjectWidget {
/// Creates a widget that visually hides its child.
OffStage({ Key key, Widget child })
: super(key: key, child: child);
OffStage({ Key key, this.offstage: true, Widget 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
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.
......
......@@ -8,6 +8,9 @@ import 'package:flutter/foundation.dart';
import 'framework.dart';
import 'table.dart';
/// Log the dirty widgets that are built each frame.
bool debugPrintRebuildDirtyWidgets = false;
Key _firstNonUniqueKey(Iterable<Widget> widgets) {
Set<Key> keySet = new HashSet<Key>();
for (Widget widget in widgets) {
......
......@@ -1236,6 +1236,11 @@ class BuildOwner {
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
assert(() {
if (debugPrintRebuildDirtyWidgets)
debugPrint('Rebuilding ${_dirtyElements[index].widget}');
return true;
});
_dirtyElements[index].rebuild();
index += 1;
if (dirtyCount < _dirtyElements.length) {
......
......@@ -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);
bool result = false;
finder.evaluate().single.visitAncestorElements((Element ancestor) {
if (ancestor.widget.runtimeType == targetType) {
if (predicate(ancestor.widget)) {
result = true;
return false;
}
......@@ -149,11 +149,22 @@ bool _hasAncestorOfType(Finder finder, Type targetType) {
return result;
}
bool _hasAncestorOfType(Finder finder, Type targetType) {
return _hasAncestorMatching(finder, (Widget widget) => widget.runtimeType == targetType);
}
class _IsOffStage extends Matcher {
const _IsOffStage();
@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
Description describe(Description description) => description.add('offstage');
......@@ -163,7 +174,20 @@ class _IsOnStage extends Matcher {
const _IsOnStage();
@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
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