Commit fcd47f84 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

CustomPaint size (#6790)

parent 01a8d510
...@@ -1783,17 +1783,27 @@ abstract class CustomPainter { ...@@ -1783,17 +1783,27 @@ abstract class CustomPainter {
/// tree as needing a new layout during the callback (the layout for this frame /// tree as needing a new layout during the callback (the layout for this frame
/// has already happened). /// has already happened).
/// ///
/// Custom painters normally size themselves to their child. If they do not have
/// a child, they attempt to size themselves to the [preferredSize], which
/// defaults to [Size.zero].
///
/// See also: /// See also:
/// ///
/// * [CustomPainter] /// * [CustomPainter], the class that custom painter delegates should extend.
/// * [Canvas] /// * [Canvas], the API provided to custom painter delegates.
class RenderCustomPaint extends RenderProxyBox { class RenderCustomPaint extends RenderProxyBox {
/// Creates a render object that delegates its painting. /// Creates a render object that delegates its painting.
RenderCustomPaint({ RenderCustomPaint({
CustomPainter painter, CustomPainter painter,
CustomPainter foregroundPainter, CustomPainter foregroundPainter,
RenderBox child Size preferredSize: Size.zero,
}) : _painter = painter, _foregroundPainter = foregroundPainter, super(child); RenderBox child,
}) : _painter = painter,
_foregroundPainter = foregroundPainter,
_preferredSize = preferredSize,
super(child) {
assert(preferredSize != null);
}
/// The background custom paint delegate. /// The background custom paint delegate.
/// ///
...@@ -1860,6 +1870,23 @@ class RenderCustomPaint extends RenderProxyBox { ...@@ -1860,6 +1870,23 @@ class RenderCustomPaint extends RenderProxyBox {
} }
} }
/// The size that this [RenderCustomPaint] should aim for, given the layout
/// constraints, if there is no child.
///
/// Defaults to [Size.zero].
///
/// If there's a child, this is ignored, and the size of the child is used
/// instead.
Size get preferredSize => _preferredSize;
Size _preferredSize;
set preferredSize (Size value) {
assert(value != null);
if (preferredSize == value)
return;
_preferredSize = value;
markNeedsLayout();
}
@override @override
void attach(PipelineOwner owner) { void attach(PipelineOwner owner) {
super.attach(owner); super.attach(owner);
...@@ -1886,6 +1913,11 @@ class RenderCustomPaint extends RenderProxyBox { ...@@ -1886,6 +1913,11 @@ class RenderCustomPaint extends RenderProxyBox {
return _painter != null && (_painter.hitTest(position) ?? true); return _painter != null && (_painter.hitTest(position) ?? true);
} }
@override
void performResize() {
size = constraints.constrain(preferredSize);
}
void _paintWithPainter(Canvas canvas, Offset offset, CustomPainter painter) { void _paintWithPainter(Canvas canvas, Offset offset, CustomPainter painter) {
int debugPreviousCanvasSaveCount; int debugPreviousCanvasSaveCount;
canvas.save(); canvas.save();
......
...@@ -197,9 +197,13 @@ class BackdropFilter extends SingleChildRenderObjectWidget { ...@@ -197,9 +197,13 @@ class BackdropFilter extends SingleChildRenderObjectWidget {
/// ///
/// Painters are implemented by subclassing [CustomPainter]. /// Painters are implemented by subclassing [CustomPainter].
/// ///
/// Because custom paint calls its painters during paint, you cannot mark the /// Because custom paint calls its painters during paint, you cannot call
/// tree as needing a new layout during the callback (the layout for this frame /// `setState` or `markNeedsLayout` during the callback (the layout for this
/// has already happened). /// frame has already happened).
///
/// Custom painters normally size themselves to their child. If they do not have
/// a child, they attempt to size themselves to the [size], which defaults to
/// [Size.zero].
/// ///
/// See also: /// See also:
/// ///
...@@ -207,8 +211,10 @@ class BackdropFilter extends SingleChildRenderObjectWidget { ...@@ -207,8 +211,10 @@ class BackdropFilter extends SingleChildRenderObjectWidget {
/// * [Canvas]. /// * [Canvas].
class CustomPaint extends SingleChildRenderObjectWidget { class CustomPaint extends SingleChildRenderObjectWidget {
/// Creates a widget that delegates its painting. /// Creates a widget that delegates its painting.
CustomPaint({ Key key, this.painter, this.foregroundPainter, Widget child }) CustomPaint({ Key key, this.painter, this.foregroundPainter, this.size: Size.zero, Widget child })
: super(key: key, child: child); : super(key: key, child: child) {
assert(size != null);
}
/// The painter that paints before the children. /// The painter that paints before the children.
final CustomPainter painter; final CustomPainter painter;
...@@ -216,17 +222,28 @@ class CustomPaint extends SingleChildRenderObjectWidget { ...@@ -216,17 +222,28 @@ class CustomPaint extends SingleChildRenderObjectWidget {
/// The painter that paints after the children. /// The painter that paints after the children.
final CustomPainter foregroundPainter; final CustomPainter foregroundPainter;
/// The size that this [CustomPaint] should aim for, given the layout
/// constraints, if there is no child.
///
/// Defaults to [Size.zero].
///
/// If there's a child, this is ignored, and the size of the child is used
/// instead.
final Size size;
@override @override
RenderCustomPaint createRenderObject(BuildContext context) => new RenderCustomPaint( RenderCustomPaint createRenderObject(BuildContext context) => new RenderCustomPaint(
painter: painter, painter: painter,
foregroundPainter: foregroundPainter foregroundPainter: foregroundPainter,
preferredSize: size,
); );
@override @override
void updateRenderObject(BuildContext context, RenderCustomPaint renderObject) { void updateRenderObject(BuildContext context, RenderCustomPaint renderObject) {
renderObject renderObject
..painter = painter ..painter = painter
..foregroundPainter = foregroundPainter; ..foregroundPainter = foregroundPainter
..preferredSize = size;
} }
@override @override
......
...@@ -42,4 +42,39 @@ void main() { ...@@ -42,4 +42,39 @@ void main() {
expect(log, equals(<String>['background', 'child', 'foreground'])); expect(log, equals(<String>['background', 'child', 'foreground']));
}); });
testWidgets('CustomPaint sizing', (WidgetTester tester) async {
GlobalKey target = new GlobalKey();
await tester.pumpWidget(new Center(
child: new CustomPaint(key: target)
));
expect(target.currentContext.size, Size.zero);
await tester.pumpWidget(new Center(
child: new CustomPaint(key: target, child: new Container())
));
expect(target.currentContext.size, const Size(800.0, 600.0));
await tester.pumpWidget(new Center(
child: new CustomPaint(key: target, size: const Size(20.0, 20.0))
));
expect(target.currentContext.size, const Size(20.0, 20.0));
await tester.pumpWidget(new Center(
child: new CustomPaint(key: target, size: const Size(2000.0, 100.0))
));
expect(target.currentContext.size, const Size(800.0, 100.0));
await tester.pumpWidget(new Center(
child: new CustomPaint(key: target, size: Size.zero, child: new Container())
));
expect(target.currentContext.size, const Size(800.0, 600.0));
await tester.pumpWidget(new Center(
child: new CustomPaint(key: target, child: new Container(height: 0.0, width: 0.0))
));
expect(target.currentContext.size, Size.zero);
});
} }
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