Commit 2a9de0aa authored by Adam Barth's avatar Adam Barth

Add the ability to custom paint foregrounds

parent a2b8f8b9
...@@ -938,61 +938,82 @@ abstract class CustomPainter { ...@@ -938,61 +938,82 @@ abstract class CustomPainter {
void paint(PaintingCanvas canvas, Size size); void paint(PaintingCanvas canvas, Size size);
bool shouldRepaint(CustomPainter oldDelegate); bool shouldRepaint(CustomPainter oldDelegate);
bool hitTest(Point position) => true; bool hitTest(Point position) => null;
} }
/// Delegates its painting to [onPaint] /// Delegates its painting
/// ///
/// When asked to paint, custom paint first calls its callback with the current /// When asked to paint, custom paint first asks painter to paint with the
/// canvas and then paints its children. The coodinate system of the canvas /// current canvas and then paints its children. After painting its children,
/// matches the coordinate system of the custom paint object. The callback is /// custom paint asks foregroundPainter to paint. The coodinate system of the
/// expected to paint with in a rectangle starting at the origin and /// canvas matches the coordinate system of the custom paint object. The
/// encompassing a region of the given size. If the callback paints outside /// painters are expected to paint with in a rectangle starting at the origin
/// and encompassing a region of the given size. If the painters paints outside
/// those bounds, there might be insufficient memory allocated to rasterize the /// those bounds, there might be insufficient memory allocated to rasterize the
/// painting commands and the resulting behavior is undefined. /// painting commands and the resulting behavior is undefined.
/// ///
/// Note: Custom paint calls its callback during paint, which means you cannot /// Note: Custom paint calls its painters during paint, which means you cannot
/// dirty layout or paint information during the callback. /// dirty layout or paint information during the callback.
class RenderCustomPaint extends RenderProxyBox { class RenderCustomPaint extends RenderProxyBox {
RenderCustomPaint({ RenderCustomPaint({
CustomPainter painter, CustomPainter painter,
CustomPainter foregroundPainter,
RenderBox child RenderBox child
}) : _painter = painter, super(child) { }) : _painter = painter, _foregroundPainter = foregroundPainter, super(child);
assert(painter != null);
}
CustomPainter get painter => _painter; CustomPainter get painter => _painter;
CustomPainter _painter; CustomPainter _painter;
void set painter (CustomPainter newPainter) { void set painter (CustomPainter newPainter) {
assert(newPainter != null || !attached);
if (_painter == newPainter) if (_painter == newPainter)
return; return;
CustomPainter oldPainter = _painter; CustomPainter oldPainter = _painter;
_painter = newPainter; _painter = newPainter;
if (newPainter == null) _checkForRepaint(_painter, oldPainter);
}
CustomPainter get foregroundPainter => _foregroundPainter;
CustomPainter _foregroundPainter;
void set foregroundPainter (CustomPainter newPainter) {
if (_foregroundPainter == newPainter)
return; return;
if (oldPainter == null CustomPainter oldPainter = _foregroundPainter;
|| newPainter.runtimeType != oldPainter.runtimeType _foregroundPainter = newPainter;
|| newPainter.shouldRepaint(oldPainter)) _checkForRepaint(_foregroundPainter, oldPainter);
}
void _checkForRepaint(CustomPainter newPainter, CustomPainter oldPainter) {
if (newPainter == null) {
assert(oldPainter != null); // We should be called only for changes.
markNeedsPaint();
} else if (oldPainter == null ||
newPainter.runtimeType != oldPainter.runtimeType ||
newPainter.shouldRepaint(oldPainter)) {
markNeedsPaint(); markNeedsPaint();
}
} }
void attach() { bool hitTestChildren(HitTestResult result, { Point position }) {
assert(_painter != null); if (_foregroundPainter != null && (_foregroundPainter.hitTest(position) ?? false))
super.attach(); return true;
return super.hitTestChildren(result, position: position);
} }
bool hitTestSelf(Point position) { bool hitTestSelf(Point position) {
return _painter.hitTest(position); return _painter != null && (_painter.hitTest(position) ?? true);
}
void _paintWithPainter(Canvas canvas, Offset offset, CustomPainter painter) {
canvas.translate(offset.dx, offset.dy);
painter.paint(canvas, size);
canvas.translate(-offset.dx, -offset.dy);
} }
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
assert(_painter != null); if (_painter != null)
context.canvas.translate(offset.dx, offset.dy); _paintWithPainter(context.canvas, offset, _painter);
_painter.paint(context.canvas, size);
context.canvas.translate(-offset.dx, -offset.dy);
super.paint(context, offset); super.paint(context, offset);
if (_foregroundPainter != null)
_paintWithPainter(context.canvas, offset, _foregroundPainter);
} }
} }
......
...@@ -137,21 +137,25 @@ class DecoratedBox extends OneChildRenderObjectWidget { ...@@ -137,21 +137,25 @@ class DecoratedBox extends OneChildRenderObjectWidget {
} }
class CustomPaint extends OneChildRenderObjectWidget { class CustomPaint extends OneChildRenderObjectWidget {
CustomPaint({ Key key, this.painter, Widget child }) CustomPaint({ Key key, this.painter, this.foregroundPainter, Widget child })
: super(key: key, child: child) { : super(key: key, child: child);
assert(painter != null);
}
final CustomPainter painter; final CustomPainter painter;
final CustomPainter foregroundPainter;
RenderCustomPaint createRenderObject() => new RenderCustomPaint(painter: painter); RenderCustomPaint createRenderObject() => new RenderCustomPaint(
painter: painter,
foregroundPainter: foregroundPainter
);
void updateRenderObject(RenderCustomPaint renderObject, CustomPaint oldWidget) { void updateRenderObject(RenderCustomPaint renderObject, CustomPaint oldWidget) {
renderObject.painter = painter; renderObject.painter = painter;
renderObject.foregroundPainter = foregroundPainter;
} }
void didUnmountRenderObject(RenderCustomPaint renderObject) { void didUnmountRenderObject(RenderCustomPaint renderObject) {
renderObject.painter = null; renderObject.painter = null;
renderObject.foregroundPainter = null;
} }
} }
...@@ -1382,4 +1386,4 @@ class StatefulBuilder extends StatefulComponent { ...@@ -1382,4 +1386,4 @@ class StatefulBuilder extends StatefulComponent {
} }
class _StatefulBuilderState extends State<StatefulBuilder> { class _StatefulBuilderState extends State<StatefulBuilder> {
Widget build(BuildContext context) => config.builder(context, setState); Widget build(BuildContext context) => config.builder(context, setState);
} }
\ No newline at end of file
...@@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart'; ...@@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
test('Can be placed in an infinte box', () { test('Can be placed in an infinite box', () {
testWidgets((WidgetTester tester) { testWidgets((WidgetTester tester) {
tester.pumpWidget(new Block(<Widget>[new Center()])); tester.pumpWidget(new Block(<Widget>[new Center()]));
}); });
......
// 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 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
import 'package:test/test.dart';
class TestCustomPainter extends CustomPainter {
TestCustomPainter({ this.log, this.name });
List<String> log;
String name;
void paint(Canvas canvas, Size size) {
log.add(name);
}
bool shouldRepaint(TestCustomPainter oldPainter) => true;
}
void main() {
test('Control test for custom painting', () {
testWidgets((WidgetTester tester) {
List<String> log = <String>[];
tester.pumpWidget(new CustomPaint(
painter: new TestCustomPainter(
log: log,
name: 'background'
),
foregroundPainter: new TestCustomPainter(
log: log,
name: 'foreground'
),
child: new CustomPaint(
painter: new TestCustomPainter(
log: log,
name: 'child'
)
)
));
expect(log, equals(['background', 'child', 'foreground']));
});
});
}
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