Chromium Code Reviews| Index: sky/sdk/lib/rendering/object.dart |
| diff --git a/sky/sdk/lib/rendering/object.dart b/sky/sdk/lib/rendering/object.dart |
| index b384d029deb69cd6a74e850ff5500b1c7c57906b..724bb840679557a1d4c543dfbe142a2311494a4d 100644 |
| --- a/sky/sdk/lib/rendering/object.dart |
| +++ b/sky/sdk/lib/rendering/object.dart |
| @@ -27,10 +27,21 @@ class ParentData { |
| } |
| class PaintingCanvas extends sky.Canvas { |
| - PaintingCanvas(sky.PictureRecorder recorder, Size bounds) : super(recorder, bounds); |
| + PaintingCanvas(sky.PictureRecorder recorder, Rect bounds) : super(recorder, bounds); |
| + List<RenderObject> _descendentsWithPaintingCanvases; // used by RenderObject._updatePaintingCanvas() to find out which RenderObjects to ask to paint |
| void paintChild(RenderObject child, Point point) { |
| - child.paint(this, point.toOffset()); |
| + if (child.createNewDisplayList) { |
| + if (_descendentsWithPaintingCanvases == null) |
| + _descendentsWithPaintingCanvases = new List<RenderObject>(); |
|
abarth-chromium
2015/07/06 20:50:58
You can simplify this pattern by writing:
final L
iansf
2015/07/07 20:36:47
Done.
|
| + assert(!_descendentsWithPaintingCanvases.contains(child)); |
| + _descendentsWithPaintingCanvases.add(child); |
| + translate(point.x, point.y); |
| + drawPaintingNode(child._paintingNode); |
| + translate(-point.x, -point.y); |
|
abarth-chromium
2015/07/06 20:50:58
It looks like drawPaintingNode should take a Point
iansf
2015/07/07 20:36:47
Done.
|
| + } else { |
| + child._paintOnCanvas(this, point.toOffset()); |
| + } |
| } |
| } |
| @@ -138,6 +149,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { |
| assert(_relayoutSubtreeRoot == null); |
| _relayoutSubtreeRoot = this; |
| _nodesNeedingLayout.add(this); |
| + _nodesNeedingPaint.add(this); |
| scheduler.ensureVisualUpdate(); |
| } |
| static void flushLayout() { |
| @@ -238,11 +250,77 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { |
| // PAINTING |
| - static bool debugDoingPaint = false; |
| + static List<RenderObject> _nodesNeedingPaint = new List<RenderObject>(); |
| + static bool _debugDoingPaint = false; |
| + static bool get debugDoingPaint => _debugDoingPaint; |
| + |
| + sky.PaintingNode _paintingNode = new sky.PaintingNode(); // only set if createNewDisplayList is true |
|
abarth-chromium
2015/07/06 20:50:58
Without |final|, this call to new will happen all
iansf
2015/07/07 21:11:22
I made this final -- it's never reassigned.
|
| + sky.PaintingNode get paintingNode => _paintingNode; |
| + bool _needsPaint = true; |
| + bool get needsPaint => _needsPaint; |
| + bool get createNewDisplayList => true; |
|
abarth-chromium
2015/07/06 20:50:58
I'm not sure we want the default behavior to be to
iansf
2015/07/07 21:11:23
Done.
|
| + |
| void markNeedsPaint() { |
| assert(!debugDoingPaint); |
| - scheduler.ensureVisualUpdate(); |
| + if (_needsPaint) return; |
| + if (createNewDisplayList) { |
| + _needsPaint = true; |
| + _nodesNeedingPaint.add(this); |
| + scheduler.ensureVisualUpdate(); |
| + } else if (parent != null) { |
| + if (parent is RenderObject) { |
| + (parent as RenderObject).markNeedsPaint(); // TODO(ianh): remove the cast once the analyzer is cleverer |
| + } |
| + } |
| } |
| + |
| + static void flushPaint() { |
| + _debugDoingPaint = true; |
| + List<RenderObject> dirtyNodes = _nodesNeedingPaint; |
| + _nodesNeedingPaint = new List<RenderObject>(); |
| + dirtyNodes..sort((a, b) => a.depth - b.depth)..forEach((node) { |
| + if (node._needsPaint && node.attached) |
| + node._updatePaintingCanvas(); |
| + }); |
| + assert(_nodesNeedingPaint.length == 0); |
| + _debugDoingPaint = false; |
|
abarth-chromium
2015/07/06 20:50:58
We should wrap the body of this function in a try
iansf
2015/07/07 20:36:47
Done.
|
| + } |
| + |
| + void _updatePaintingCanvas() { |
| + assert(!_needsLayout); |
| + assert(createNewDisplayList); |
| + sky.PictureRecorder recorder = new sky.PictureRecorder(); |
| + PaintingCanvas canvas = new PaintingCanvas(recorder, paintBounds); |
| + _needsPaint = false; |
| + try { |
| + _paintOnCanvas(canvas, Offset.zero); |
| + } catch (e, stack) { |
|
abarth-chromium
2015/07/06 20:50:58
Capturing the stack here is going to be too expens
Hixie
2015/07/06 21:13:42
If you change this make sure the equivalent code i
iansf
2015/07/07 20:36:47
Done.
iansf
2015/07/07 20:36:47
Done.
|
| + print('Exception raised during _updatePaintingCanvas:\n${e}\nContext:\n${this}'); |
| + print(stack); |
| + return; |
| + } |
| + assert(!_needsLayout); // check that the paint() method didn't mark us dirty again |
| + assert(!_needsPaint); // check that the paint() method didn't mark us dirty again |
| + _paintingNode.setBackingDrawable(recorder.endRecordingAsDrawable()); |
| + |
| + if (canvas._descendentsWithPaintingCanvases != null) { |
| + canvas._descendentsWithPaintingCanvases.forEach((node) { |
|
abarth-chromium
2015/07/06 20:50:57
We should use a for-each loop rather than calling
iansf
2015/07/07 20:36:47
Done.
|
| + assert(node.attached == attached); |
| + if (node._needsPaint) |
| + node._updatePaintingCanvas(); |
| + }); |
| + } |
| + |
| + } |
| + |
| + void _paintOnCanvas(PaintingCanvas canvas, Offset offset) { |
| + _needsPaint = false; |
|
abarth-chromium
2015/07/06 20:50:58
Why do we set _needsPaint = false both here and on
iansf
2015/07/07 20:36:47
Done.
|
| + paint(canvas, offset); |
| + assert(!_needsPaint); |
| + } |
| + |
| + Rect get paintBounds; |
| + |
| void paint(PaintingCanvas canvas, Offset offset) { } |