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
// Copyright 2016 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 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:test/test.dart';
import 'rendering_tester.dart';
class TestTree {
TestTree() {
// viewport incoming constraints are tight 800x600
// viewport is vertical by default
root = new RenderViewport(
// Place the child to be evaluated within both a repaint boundary and a
// layout-root element (in this case a tightly constrained box). Otherwise
// the act of transplanting the root into a new container will cause the
// relayout/repaint of the new parent node to satisfy the test.
child: new RenderRepaintBoundary(
child: new RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(height: 20.0, width: 20.0),
child: new RenderRepaintBoundary(
child: new RenderCustomPaint(
painter: new TestCallbackPainter(
onPaint: () { painted = true; }
),
child: new RenderPositionedBox(
child: child = new RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(height: 20.0, width: 20.0)
)
)
)
)
)
)
);
}
RenderObject root;
RenderConstrainedBox child;
bool painted = false;
}
class MutableCompositor extends RenderProxyBox {
MutableCompositor({ RenderBox child }) : super(child);
bool _alwaysComposite = false;
@override
bool get alwaysNeedsCompositing => _alwaysComposite;
}
class TestCompositingBitsTree {
TestCompositingBitsTree() {
// viewport incoming constraints are tight 800x600
// viewport is vertical by default
root = new RenderViewport(
// Place the child to be evaluated within a repaint boundary. Otherwise
// the act of transplanting the root into a new container will cause the
// repaint of the new parent node to satisfy the test.
child: new RenderRepaintBoundary(
child: compositor = new MutableCompositor(
child: new RenderCustomPaint(
painter: new TestCallbackPainter(
onPaint: () { painted = true; }
),
child: child = new RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(height: 20.0, width: 20.0)
)
)
)
)
);
}
RenderObject root;
MutableCompositor compositor;
RenderConstrainedBox child;
bool painted = false;
}
void main() {
test('objects can be detached and re-attached: layout', () {
TestTree testTree = new TestTree();
// Lay out
layout(testTree.root, phase: EnginePhase.layout);
expect(testTree.child.size, equals(const Size(20.0, 20.0)));
// Remove testTree from the custom render view
renderer.renderView.child = null;
expect(testTree.child.owner, isNull);
// Dirty one of the elements
testTree.child.additionalConstraints =
const BoxConstraints.tightFor(height: 5.0, width: 5.0);
// Lay out again
layout(testTree.root, phase: EnginePhase.layout);
expect(testTree.child.size, equals(const Size(5.0, 5.0)));
});
test('objects can be detached and re-attached: compositingBits', () {
TestCompositingBitsTree testTree = new TestCompositingBitsTree();
// Lay out, composite, and paint
layout(testTree.root, phase: EnginePhase.paint);
expect(testTree.painted, isTrue);
// Remove testTree from the custom render view
renderer.renderView.child = null;
expect(testTree.child.owner, isNull);
// Dirty one of the elements
testTree.compositor._alwaysComposite = true;
testTree.child.markNeedsCompositingBitsUpdate();
testTree.painted = false;
// Lay out, composite, and paint again
layout(testTree.root, phase: EnginePhase.paint);
expect(testTree.painted, isTrue);
});
test('objects can be detached and re-attached: paint', () {
TestTree testTree = new TestTree();
// Lay out, composite, and paint
layout(testTree.root, phase: EnginePhase.paint);
expect(testTree.painted, isTrue);
// Remove testTree from the custom render view
renderer.renderView.child = null;
expect(testTree.child.owner, isNull);
// Dirty one of the elements
testTree.child.markNeedsPaint();
testTree.painted = false;
// Lay out, composite, and paint again
layout(testTree.root, phase: EnginePhase.paint);
expect(testTree.painted, isTrue);
});
test('objects can be detached and re-attached: semantics', () {
TestTree testTree = new TestTree();
int semanticsUpdateCount = 0;
SemanticsHandle semanticsHandle = renderer.pipelineOwner.ensureSemantics(
listener: () {
++semanticsUpdateCount;
}
);
// Lay out, composite, paint, and update semantics
layout(testTree.root, phase: EnginePhase.flushSemantics);
expect(semanticsUpdateCount, 1);
// Remove testTree from the custom render view
renderer.renderView.child = null;
expect(testTree.child.owner, isNull);
// Dirty one of the elements
semanticsUpdateCount = 0;
testTree.child.markNeedsSemanticsUpdate();
expect(semanticsUpdateCount, 0);
// Lay out, composite, paint, and update semantics again
layout(testTree.root, phase: EnginePhase.flushSemantics);
expect(semanticsUpdateCount, 1);
semanticsHandle.dispose();
});
}