Unverified Commit 9ffa1c51 authored by liyuqian's avatar liyuqian Committed by GitHub

Add Clip enum to Material and related widgets (#18576)

See details in our proposal for this breaking API change and #18057. This PR setup all code paths to allow the change but doesn't change the clip behavior by itself. We'll change `defaultClipBehavior` from `Clip.antiAlias` to `Clip.none` in the following PR to change the clip behavior and update tests.
parent ad163749
......@@ -28,6 +28,7 @@ export 'src/painting/box_decoration.dart';
export 'src/painting/box_fit.dart';
export 'src/painting/box_shadow.dart';
export 'src/painting/circle_border.dart';
export 'src/painting/clip.dart';
export 'src/painting/colors.dart';
export 'src/painting/debug.dart';
export 'src/painting/decoration.dart';
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show Clip, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
......@@ -87,8 +89,9 @@ abstract class MaterialInkController {
///
/// The Material widget is responsible for:
///
/// 1. Clipping: Material clips its widget sub-tree to the shape specified by
/// [shape], [type], and [borderRadius].
/// 1. Clipping: If [clipBehavior] is not [Clip.none], Material clips its widget
/// sub-tree to the shape specified by [shape], [type], and [borderRadius].
/// By default, [clipBehavior] is [Clip.none] for performance considerations.
/// 2. Elevation: Material elevates its widget sub-tree on the Z axis by
/// [elevation] pixels, and draws the appropriate shadow.
/// 3. Ink effects: Material shows ink effects implemented by [InkFeature]s
......@@ -171,6 +174,7 @@ class Material extends StatefulWidget {
this.textStyle,
this.borderRadius,
this.shape,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
this.animationDuration = kThemeChangeDuration,
this.child,
}) : assert(type != null),
......@@ -179,6 +183,7 @@ class Material extends StatefulWidget {
assert(!(shape != null && borderRadius != null)),
assert(animationDuration != null),
assert(!(identical(type, MaterialType.circle) && (borderRadius != null || shape != null))),
assert(clipBehavior != null),
super(key: key);
/// The widget below this widget in the tree.
......@@ -226,6 +231,14 @@ class Material extends StatefulWidget {
/// zero.
final ShapeBorder shape;
/// {@template flutter.widgets.Clip}
/// The content will be clipped (or not) according to this option.
///
/// See the enum [Clip] for details of all possible options and their common
/// use cases.
/// {@endtemplate}
final Clip clipBehavior;
/// Defines the duration of animated changes for [shape], [elevation],
/// and [shadowColor].
///
......@@ -329,6 +342,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
curve: Curves.fastOutSlowIn,
duration: widget.animationDuration,
shape: BoxShape.rectangle,
clipBehavior: widget.clipBehavior,
borderRadius: BorderRadius.zero,
elevation: widget.elevation,
color: backgroundColor,
......@@ -341,12 +355,13 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
final ShapeBorder shape = _getShape();
if (widget.type == MaterialType.transparency)
return _transparentInterior(shape: shape, contents: contents);
return _transparentInterior(shape: shape, clipBehavior: widget.clipBehavior, contents: contents);
return new _MaterialInterior(
curve: Curves.fastOutSlowIn,
duration: widget.animationDuration,
shape: shape,
clipBehavior: widget.clipBehavior,
elevation: widget.elevation,
color: backgroundColor,
shadowColor: widget.shadowColor,
......@@ -354,7 +369,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
);
}
static Widget _transparentInterior({ShapeBorder shape, Widget contents}) {
static Widget _transparentInterior({ShapeBorder shape, Clip clipBehavior, Widget contents}) {
return new ClipPath(
child: new _ShapeBorderPaint(
child: contents,
......@@ -363,6 +378,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
clipper: new ShapeBorderClipper(
shape: shape,
),
clipBehavior: clipBehavior,
);
}
......@@ -582,6 +598,7 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget {
Key key,
@required this.child,
@required this.shape,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
@required this.elevation,
@required this.color,
@required this.shadowColor,
......@@ -589,6 +606,7 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget {
@required Duration duration,
}) : assert(child != null),
assert(shape != null),
assert(clipBehavior != null),
assert(elevation != null),
assert(color != null),
assert(shadowColor != null),
......@@ -605,6 +623,9 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget {
/// determines the physical shape.
final ShapeBorder shape;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// The target z-coordinate at which to place this physical object.
final double elevation;
......@@ -651,6 +672,7 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior>
shape: shape,
textDirection: Directionality.of(context)
),
clipBehavior: widget.clipBehavior,
elevation: _elevation.evaluate(animation),
color: widget.color,
shadowColor: _shadowColor.evaluate(animation),
......
// Copyright 2018 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 'dart:ui' show Canvas, Clip, Path, Paint, Rect, RRect;
/// Clip utilities used by [PaintingContext] and [TestRecordingPaintingContext].
abstract class ClipContext {
/// The canvas on which to paint.
Canvas get canvas;
void _clipAndPaint(void canvasClipCall(bool doAntiAlias), Clip clipBehavior, Rect bounds, void painter()) {
assert(canvasClipCall != null);
canvas.save();
switch (clipBehavior) {
case Clip.none:
break;
case Clip.hardEdge:
canvasClipCall(false);
break;
case Clip.antiAlias:
canvasClipCall(true);
break;
case Clip.antiAliasWithSaveLayer:
canvasClipCall(true);
canvas.saveLayer(bounds, new Paint());
break;
}
painter();
if (clipBehavior == Clip.antiAliasWithSaveLayer) {
canvas.restore();
}
canvas.restore();
}
/// Clip [canvas] with [Path] according to [Clip] and then paint. [canvas] is
/// restored to the pre-clip status afterwards.
///
/// `bounds` is the saveLayer bounds used for [Clip.antiAliasWithSaveLayer].
void clipPathAndPaint(Path path, Clip clipBehavior, Rect bounds, void painter()) {
_clipAndPaint((bool doAntiAias) => canvas.clipPath(path, doAntiAlias: doAntiAias), clipBehavior, bounds, painter);
}
/// Clip [canvas] with [Path] according to [RRect] and then paint. [canvas] is
/// restored to the pre-clip status afterwards.
///
/// `bounds` is the saveLayer bounds used for [Clip.antiAliasWithSaveLayer].
void clipRRectAndPaint(RRect rrect, Clip clipBehavior, Rect bounds, void painter()) {
_clipAndPaint((bool doAntiAias) => canvas.clipRRect(rrect, doAntiAlias: doAntiAias), clipBehavior, bounds, painter);
}
/// Clip [canvas] with [Path] according to [Rect] and then paint. [canvas] is
/// restored to the pre-clip status afterwards.
///
/// `bounds` is the saveLayer bounds used for [Clip.antiAliasWithSaveLayer].
void clipRectAndPaint(Rect rect, Clip clipBehavior, Rect bounds, void painter()) {
_clipAndPaint((bool doAntiAias) => canvas.clipRect(rect, doAntiAlias: doAntiAias), clipBehavior, bounds, painter);
}
}
......@@ -5,7 +5,7 @@
import 'dart:async';
import 'dart:collection';
import 'dart:ui' as ui show Image, ImageFilter, Picture, Scene, SceneBuilder;
import 'dart:ui' show Offset;
import 'dart:ui' show Clip, Offset, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
......@@ -605,7 +605,8 @@ class ClipRectLayer extends ContainerLayer {
///
/// The [clipRect] property must be non-null before the compositing phase of
/// the pipeline.
ClipRectLayer({ this.clipRect });
ClipRectLayer({ this.clipRect, Clip clipBehavior = Clip.antiAlias }) :
_clipBehavior = clipBehavior, assert(clipBehavior != null), assert(clipBehavior != Clip.none);
/// The rectangle to clip in the parent's coordinate system.
///
......@@ -613,6 +614,15 @@ class ClipRectLayer extends ContainerLayer {
/// (as described at [Layer]).
Rect clipRect;
/// {@macro flutter.clipper.clipBehavior}
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior;
set clipBehavior(Clip value) {
assert(value != null);
assert(value != Clip.none);
_clipBehavior = value;
}
@override
S find<S>(Offset regionOffset) {
if (!clipRect.contains(regionOffset))
......@@ -628,7 +638,7 @@ class ClipRectLayer extends ContainerLayer {
return true;
}());
if (enabled)
builder.pushClipRect(clipRect.shift(layerOffset));
builder.pushClipRect(clipRect.shift(layerOffset), clipBehavior: clipBehavior);
addChildrenToScene(builder, layerOffset);
if (enabled)
builder.pop();
......@@ -651,7 +661,8 @@ class ClipRRectLayer extends ContainerLayer {
///
/// The [clipRRect] property must be non-null before the compositing phase of
/// the pipeline.
ClipRRectLayer({ this.clipRRect });
ClipRRectLayer({ this.clipRRect, Clip clipBehavior = Clip.antiAlias }) :
_clipBehavior = clipBehavior, assert(clipBehavior != null), assert(clipBehavior != Clip.none);
/// The rounded-rect to clip in the parent's coordinate system.
///
......@@ -659,6 +670,15 @@ class ClipRRectLayer extends ContainerLayer {
/// (as described at [Layer]).
RRect clipRRect;
/// {@macro flutter.clipper.clipBehavior}
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior;
set clipBehavior(Clip value) {
assert(value != null);
assert(value != Clip.none);
_clipBehavior = value;
}
@override
S find<S>(Offset regionOffset) {
if (!clipRRect.contains(regionOffset))
......@@ -674,7 +694,7 @@ class ClipRRectLayer extends ContainerLayer {
return true;
}());
if (enabled)
builder.pushClipRRect(clipRRect.shift(layerOffset));
builder.pushClipRRect(clipRRect.shift(layerOffset), clipBehavior: clipBehavior);
addChildrenToScene(builder, layerOffset);
if (enabled)
builder.pop();
......@@ -697,7 +717,8 @@ class ClipPathLayer extends ContainerLayer {
///
/// The [clipPath] property must be non-null before the compositing phase of
/// the pipeline.
ClipPathLayer({ this.clipPath });
ClipPathLayer({ this.clipPath, Clip clipBehavior = Clip.antiAlias }) :
_clipBehavior = clipBehavior, assert(clipBehavior != null), assert(clipBehavior != Clip.none);
/// The path to clip in the parent's coordinate system.
///
......@@ -705,6 +726,15 @@ class ClipPathLayer extends ContainerLayer {
/// (as described at [Layer]).
Path clipPath;
/// {@macro flutter.clipper.clipBehavior}
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior;
set clipBehavior(Clip value) {
assert(value != null);
assert(value != Clip.none);
_clipBehavior = value;
}
@override
S find<S>(Offset regionOffset) {
if (!clipPath.contains(regionOffset))
......@@ -720,7 +750,7 @@ class ClipPathLayer extends ContainerLayer {
return true;
}());
if (enabled)
builder.pushClipPath(clipPath.shift(layerOffset));
builder.pushClipPath(clipPath.shift(layerOffset), clipBehavior: clipBehavior);
addChildrenToScene(builder, layerOffset);
if (enabled)
builder.pop();
......@@ -925,10 +955,12 @@ class PhysicalModelLayer extends ContainerLayer {
/// The [clipPath], [elevation], and [color] arguments must not be null.
PhysicalModelLayer({
@required this.clipPath,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
@required this.elevation,
@required this.color,
@required this.shadowColor,
}) : assert(clipPath != null),
assert(clipBehavior != null),
assert(elevation != null),
assert(color != null),
assert(shadowColor != null);
......@@ -939,6 +971,9 @@ class PhysicalModelLayer extends ContainerLayer {
/// (as described at [Layer]).
Path clipPath;
/// {@macro flutter.widgets.Clip}
Clip clipBehavior;
/// The z-coordinate at which to place this physical object.
///
/// The scene must be explicitly recomposited after this property is changed
......@@ -980,6 +1015,7 @@ class PhysicalModelLayer extends ContainerLayer {
elevation: elevation,
color: color,
shadowColor: shadowColor,
clipBehavior: clipBehavior,
);
}
addChildrenToScene(builder, layerOffset);
......
......@@ -4,6 +4,7 @@
import 'dart:developer';
import 'dart:ui' as ui show PictureRecorder;
import 'dart:ui' show Clip;
import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart';
......@@ -59,7 +60,7 @@ typedef void PaintingContextCallback(PaintingContext context, Offset offset);
///
/// New [PaintingContext] objects are created automatically when using
/// [PaintingContext.repaintCompositedChild] and [pushLayer].
class PaintingContext {
class PaintingContext extends ClipContext {
PaintingContext._(this._containerLayer, this.estimatedBounds)
: assert(_containerLayer != null),
assert(estimatedBounds != null);
......@@ -195,6 +196,7 @@ class PaintingContext {
/// The current canvas can change whenever you paint a child using this
/// context, which means it's fragile to hold a reference to the canvas
/// returned by this getter.
@override
Canvas get canvas {
if (_canvas == null)
_startRecording();
......@@ -315,17 +317,13 @@ class PaintingContext {
/// clip the painting done by [painter].
/// * `painter` is a callback that will paint with the [clipRect] applied. This
/// function calls the [painter] synchronously.
void pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter) {
/// * `clipBehavior` controls how the rectangle is clipped.
void pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter, {Clip clipBehavior = Clip.antiAlias}) {
final Rect offsetClipRect = clipRect.shift(offset);
if (needsCompositing) {
pushLayer(new ClipRectLayer(clipRect: offsetClipRect), painter, offset, childPaintBounds: offsetClipRect);
pushLayer(new ClipRectLayer(clipRect: offsetClipRect, clipBehavior: clipBehavior), painter, offset, childPaintBounds: offsetClipRect);
} else {
canvas
..save()
..clipRect(offsetClipRect);
painter(this, offset);
canvas
..restore();
clipRectAndPaint(offsetClipRect, clipBehavior, offsetClipRect, () => painter(this, offset));
}
}
......@@ -341,18 +339,16 @@ class PaintingContext {
/// to use to clip the painting done by `painter`.
/// * `painter` is a callback that will paint with the `clipRRect` applied. This
/// function calls the `painter` synchronously.
void pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, RRect clipRRect, PaintingContextCallback painter) {
/// * `clipBehavior` controls how the path is clipped.
// ignore: deprecated_member_use
void pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, RRect clipRRect, PaintingContextCallback painter, {Clip clipBehavior = Clip.antiAlias}) {
assert(clipBehavior != null);
final Rect offsetBounds = bounds.shift(offset);
final RRect offsetClipRRect = clipRRect.shift(offset);
if (needsCompositing) {
pushLayer(new ClipRRectLayer(clipRRect: offsetClipRRect), painter, offset, childPaintBounds: offsetBounds);
pushLayer(new ClipRRectLayer(clipRRect: offsetClipRRect, clipBehavior: clipBehavior), painter, offset, childPaintBounds: offsetBounds);
} else {
canvas
..save()
..clipRRect(offsetClipRRect);
painter(this, offset);
canvas
..restore();
clipRRectAndPaint(offsetClipRRect, clipBehavior, offsetBounds, () => painter(this, offset));
}
}
......@@ -368,18 +364,16 @@ class PaintingContext {
/// clip the painting done by `painter`.
/// * `painter` is a callback that will paint with the `clipPath` applied. This
/// function calls the `painter` synchronously.
void pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter) {
/// * `clipBehavior` controls how the rounded rectangle is clipped.
// ignore: deprecated_member_use
void pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter, {Clip clipBehavior = Clip.antiAlias}) {
assert(clipBehavior != null);
final Rect offsetBounds = bounds.shift(offset);
final Path offsetClipPath = clipPath.shift(offset);
if (needsCompositing) {
pushLayer(new ClipPathLayer(clipPath: offsetClipPath), painter, offset, childPaintBounds: offsetBounds);
pushLayer(new ClipPathLayer(clipPath: offsetClipPath, clipBehavior: clipBehavior), painter, offset, childPaintBounds: offsetBounds);
} else {
canvas
..save()
..clipPath(clipPath.shift(offset));
painter(this, offset);
canvas
..restore();
clipPathAndPaint(offsetClipPath, clipBehavior, offsetBounds, () => painter(this, offset));
}
}
......
......@@ -5,6 +5,7 @@
import 'dart:async';
import 'dart:ui' as ui show ImageFilter, Gradient, Image;
import 'dart:ui' show Clip, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart';
......@@ -1116,8 +1117,9 @@ class ShapeBorderClipper extends CustomClipper<Path> {
abstract class _RenderCustomClip<T> extends RenderProxyBox {
_RenderCustomClip({
RenderBox child,
CustomClipper<T> clipper
}) : _clipper = clipper, super(child);
CustomClipper<T> clipper,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
}) : _clipper = clipper, assert(clipBehavior != null), assert(clipBehavior != Clip.none), super(child);
/// If non-null, determines which clip to use on the child.
CustomClipper<T> get clipper => _clipper;
......@@ -1160,6 +1162,13 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox {
T get _defaultClip;
T _clip;
/// {@template flutter.clipper.clipBehavior}
/// Controls how to clip (default to [Clip.antiAlias]).
///
/// [Clip.none] is not allowed here.
/// {@endtemplate}
final Clip clipBehavior;
@override
void performLayout() {
final Size oldSize = hasSize ? size : null;
......@@ -1220,8 +1229,9 @@ class RenderClipRect extends _RenderCustomClip<Rect> {
/// the child.
RenderClipRect({
RenderBox child,
CustomClipper<Rect> clipper
}) : super(child: child, clipper: clipper);
CustomClipper<Rect> clipper,
Clip clipBehavior = Clip.antiAlias,
}) : super(child: child, clipper: clipper, clipBehavior: clipBehavior);
@override
Rect get _defaultClip => Offset.zero & size;
......@@ -1241,7 +1251,7 @@ class RenderClipRect extends _RenderCustomClip<Rect> {
void paint(PaintingContext context, Offset offset) {
if (child != null) {
_updateClip();
context.pushClipRect(needsCompositing, offset, _clip, super.paint);
context.pushClipRect(needsCompositing, offset, _clip, super.paint, clipBehavior: clipBehavior);
}
}
......@@ -1274,7 +1284,8 @@ class RenderClipRRect extends _RenderCustomClip<RRect> {
RenderBox child,
BorderRadius borderRadius = BorderRadius.zero,
CustomClipper<RRect> clipper,
}) : _borderRadius = borderRadius, super(child: child, clipper: clipper) {
Clip clipBehavior = Clip.antiAlias,
}) : _borderRadius = borderRadius, super(child: child, clipper: clipper, clipBehavior: clipBehavior) {
assert(_borderRadius != null || clipper != null);
}
......@@ -1312,7 +1323,7 @@ class RenderClipRRect extends _RenderCustomClip<RRect> {
void paint(PaintingContext context, Offset offset) {
if (child != null) {
_updateClip();
context.pushClipRRect(needsCompositing, offset, _clip.outerRect, _clip, super.paint);
context.pushClipRRect(needsCompositing, offset, _clip.outerRect, _clip, super.paint, clipBehavior: clipBehavior);
}
}
......@@ -1341,8 +1352,9 @@ class RenderClipOval extends _RenderCustomClip<Rect> {
/// position of the child.
RenderClipOval({
RenderBox child,
CustomClipper<Rect> clipper
}) : super(child: child, clipper: clipper);
CustomClipper<Rect> clipper,
Clip clipBehavior = Clip.antiAlias,
}) : super(child: child, clipper: clipper, clipBehavior: clipBehavior);
Rect _cachedRect;
Path _cachedPath;
......@@ -1376,7 +1388,7 @@ class RenderClipOval extends _RenderCustomClip<Rect> {
void paint(PaintingContext context, Offset offset) {
if (child != null) {
_updateClip();
context.pushClipPath(needsCompositing, offset, _clip, _getClipPath(_clip), super.paint);
context.pushClipPath(needsCompositing, offset, _clip, _getClipPath(_clip), super.paint, clipBehavior: clipBehavior);
}
}
......@@ -1413,8 +1425,9 @@ class RenderClipPath extends _RenderCustomClip<Path> {
/// efficiently.
RenderClipPath({
RenderBox child,
CustomClipper<Path> clipper
}) : super(child: child, clipper: clipper);
CustomClipper<Path> clipper,
Clip clipBehavior = Clip.antiAlias,
}) : super(child: child, clipper: clipper, clipBehavior: clipBehavior);
@override
Path get _defaultClip => new Path()..addRect(Offset.zero & size);
......@@ -1434,7 +1447,7 @@ class RenderClipPath extends _RenderCustomClip<Path> {
void paint(PaintingContext context, Offset offset) {
if (child != null) {
_updateClip();
context.pushClipPath(needsCompositing, offset, Offset.zero & size, _clip, super.paint);
context.pushClipPath(needsCompositing, offset, Offset.zero & size, _clip, super.paint, clipBehavior: clipBehavior);
}
}
......@@ -1462,14 +1475,16 @@ abstract class _RenderPhysicalModelBase<T> extends _RenderCustomClip<T> {
@required double elevation,
@required Color color,
@required Color shadowColor,
Clip clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
CustomClipper<T> clipper,
}) : assert(elevation != null),
assert(color != null),
assert(shadowColor != null),
assert(clipBehavior != null),
_elevation = elevation,
_color = color,
_shadowColor = shadowColor,
super(child: child, clipper: clipper);
super(child: child, clipBehavior: clipBehavior, clipper: clipper);
/// The z-coordinate at which to place this material.
///
......@@ -1539,17 +1554,20 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
RenderPhysicalModel({
RenderBox child,
BoxShape shape = BoxShape.rectangle,
Clip clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
BorderRadius borderRadius,
double elevation = 0.0,
@required Color color,
Color shadowColor = const Color(0xFF000000),
}) : assert(shape != null),
assert(clipBehavior != null),
assert(elevation != null),
assert(color != null),
assert(shadowColor != null),
_shape = shape,
_borderRadius = borderRadius,
super(
clipBehavior: clipBehavior,
child: child,
elevation: elevation,
color: color,
......@@ -1638,6 +1656,7 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
if (needsCompositing) {
final PhysicalModelLayer physicalModel = new PhysicalModelLayer(
clipPath: offsetRRectAsPath,
clipBehavior: clipBehavior,
elevation: paintShadows ? elevation : 0.0,
color: color,
shadowColor: shadowColor,
......@@ -1662,10 +1681,7 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
);
}
canvas.drawRRect(offsetRRect, new Paint()..color = color);
canvas.save();
canvas.clipRRect(offsetRRect);
super.paint(context, offset);
canvas.restore();
context.clipRRectAndPaint(offsetRRect, clipBehavior, offsetBounds, () => super.paint(context, offset));
assert(context.canvas == canvas, 'canvas changed even though needsCompositing was false');
}
}
......@@ -1697,6 +1713,7 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
RenderPhysicalShape({
RenderBox child,
@required CustomClipper<Path> clipper,
Clip clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
double elevation = 0.0,
@required Color color,
Color shadowColor = const Color(0xFF000000),
......@@ -1710,6 +1727,7 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
color: color,
shadowColor: shadowColor,
clipper: clipper,
clipBehavior: clipBehavior
);
@override
......@@ -1751,6 +1769,7 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
if (needsCompositing) {
final PhysicalModelLayer physicalModel = new PhysicalModelLayer(
clipPath: offsetPath,
clipBehavior: clipBehavior,
elevation: paintShadows ? elevation : 0.0,
color: color,
shadowColor: shadowColor,
......@@ -1775,10 +1794,7 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
);
}
canvas.drawPath(offsetPath, new Paint()..color = color..style = PaintingStyle.fill);
canvas.save();
canvas.clipPath(offsetPath);
super.paint(context, offset);
canvas.restore();
context.clipPathAndPaint(offsetPath, clipBehavior, offsetBounds, () => super.paint(context, offset));
assert(context.canvas == canvas, 'canvas changed even though needsCompositing was false');
}
}
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:ui' as ui show Image, ImageFilter;
import 'dart:ui' show Clip, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
......@@ -476,13 +477,16 @@ class ClipRect extends SingleChildRenderObjectWidget {
///
/// If [clipper] is null, the clip will match the layout size and position of
/// the child.
const ClipRect({ Key key, this.clipper, Widget child }) : super(key: key, child: child);
const ClipRect({ Key key, this.clipper, this.clipBehavior = Clip.antiAlias, Widget child }) : super(key: key, child: child);
/// If non-null, determines which clip to use.
final CustomClipper<Rect> clipper;
/// {@macro flutter.widget.clipper.clipBehavior}
final Clip clipBehavior;
@override
RenderClipRect createRenderObject(BuildContext context) => new RenderClipRect(clipper: clipper);
RenderClipRect createRenderObject(BuildContext context) => new RenderClipRect(clipper: clipper, clipBehavior: clipBehavior);
@override
void updateRenderObject(BuildContext context, RenderClipRect renderObject) {
......@@ -524,8 +528,10 @@ class ClipRRect extends SingleChildRenderObjectWidget {
Key key,
this.borderRadius,
this.clipper,
this.clipBehavior = Clip.antiAlias,
Widget child,
}) : assert(borderRadius != null || clipper != null),
assert(clipBehavior != null),
super(key: key, child: child);
/// The border radius of the rounded corners.
......@@ -539,8 +545,11 @@ class ClipRRect extends SingleChildRenderObjectWidget {
/// If non-null, determines which clip to use.
final CustomClipper<RRect> clipper;
/// {@macro flutter.widget.clipper.clipBehavior}
final Clip clipBehavior;
@override
RenderClipRRect createRenderObject(BuildContext context) => new RenderClipRRect(borderRadius: borderRadius, clipper: clipper);
RenderClipRRect createRenderObject(BuildContext context) => new RenderClipRRect(borderRadius: borderRadius, clipper: clipper, clipBehavior: clipBehavior);
@override
void updateRenderObject(BuildContext context, RenderClipRRect renderObject) {
......@@ -575,7 +584,7 @@ class ClipOval extends SingleChildRenderObjectWidget {
///
/// If [clipper] is null, the oval will be inscribed into the layout size and
/// position of the child.
const ClipOval({ Key key, this.clipper, Widget child }) : super(key: key, child: child);
const ClipOval({ Key key, this.clipper, this.clipBehavior = Clip.antiAlias, Widget child }) : super(key: key, child: child);
/// If non-null, determines which clip to use.
///
......@@ -588,8 +597,11 @@ class ClipOval extends SingleChildRenderObjectWidget {
/// object) instead.
final CustomClipper<Rect> clipper;
/// {@macro flutter.widget.clipper.clipBehavior}
final Clip clipBehavior;
@override
RenderClipOval createRenderObject(BuildContext context) => new RenderClipOval(clipper: clipper);
RenderClipOval createRenderObject(BuildContext context) => new RenderClipOval(clipper: clipper, clipBehavior: clipBehavior);
@override
void updateRenderObject(BuildContext context, RenderClipOval renderObject) {
......@@ -627,7 +639,7 @@ class ClipPath extends SingleChildRenderObjectWidget {
/// size and location of the child. However, rather than use this default,
/// consider using a [ClipRect], which can achieve the same effect more
/// efficiently.
const ClipPath({ Key key, this.clipper, Widget child }) : super(key: key, child: child);
const ClipPath({ Key key, this.clipper, this.clipBehavior = Clip.antiAlias, Widget child }) : super(key: key, child: child);
/// If non-null, determines which clip to use.
///
......@@ -636,8 +648,11 @@ class ClipPath extends SingleChildRenderObjectWidget {
/// efficient way of obtaining that effect.
final CustomClipper<Path> clipper;
/// {@macro flutter.widget.clipper.clipBehavior}
final Clip clipBehavior;
@override
RenderClipPath createRenderObject(BuildContext context) => new RenderClipPath(clipper: clipper);
RenderClipPath createRenderObject(BuildContext context) => new RenderClipPath(clipper: clipper, clipBehavior: clipBehavior);
@override
void updateRenderObject(BuildContext context, RenderClipPath renderObject) {
......@@ -677,6 +692,7 @@ class PhysicalModel extends SingleChildRenderObjectWidget {
const PhysicalModel({
Key key,
this.shape = BoxShape.rectangle,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
this.borderRadius,
this.elevation = 0.0,
@required this.color,
......@@ -691,6 +707,9 @@ class PhysicalModel extends SingleChildRenderObjectWidget {
/// The type of shape.
final BoxShape shape;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// The border radius of the rounded corners.
///
/// Values are clamped so that horizontal and vertical radii sums do not
......@@ -712,6 +731,7 @@ class PhysicalModel extends SingleChildRenderObjectWidget {
RenderPhysicalModel createRenderObject(BuildContext context) {
return new RenderPhysicalModel(
shape: shape,
clipBehavior: clipBehavior,
borderRadius: borderRadius,
elevation: elevation, color: color,
shadowColor: shadowColor,
......@@ -760,11 +780,13 @@ class PhysicalShape extends SingleChildRenderObjectWidget {
const PhysicalShape({
Key key,
@required this.clipper,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
this.elevation = 0.0,
@required this.color,
this.shadowColor = const Color(0xFF000000),
Widget child,
}) : assert(clipper != null),
assert(clipBehavior != null),
assert(elevation != null),
assert(color != null),
assert(shadowColor != null),
......@@ -777,6 +799,9 @@ class PhysicalShape extends SingleChildRenderObjectWidget {
/// shape for use with this widget.
final CustomClipper<Path> clipper;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// The z-coordinate at which to place this physical object.
final double elevation;
......@@ -790,6 +815,7 @@ class PhysicalShape extends SingleChildRenderObjectWidget {
RenderPhysicalShape createRenderObject(BuildContext context) {
return new RenderPhysicalShape(
clipper: clipper,
clipBehavior: clipBehavior,
elevation: elevation,
color: color,
shadowColor: shadowColor
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show Clip, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
......@@ -1181,6 +1183,7 @@ class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
Key key,
@required this.child,
@required this.shape,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
this.borderRadius = BorderRadius.zero,
@required this.elevation,
@required this.color,
......@@ -1191,6 +1194,7 @@ class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
@required Duration duration,
}) : assert(child != null),
assert(shape != null),
assert(clipBehavior != null),
assert(borderRadius != null),
assert(elevation != null),
assert(color != null),
......@@ -1209,6 +1213,9 @@ class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
/// This property is not animated.
final BoxShape shape;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// The target border radius of the rounded corners for a rectangle shape.
final BorderRadius borderRadius;
......@@ -1262,6 +1269,7 @@ class _AnimatedPhysicalModelState extends AnimatedWidgetBaseState<AnimatedPhysic
return new PhysicalModel(
child: widget.child,
shape: widget.shape,
clipBehavior: widget.clipBehavior,
borderRadius: _borderRadius.evaluate(animation),
elevation: _elevation.evaluate(animation),
color: widget.animateColor ? _color.evaluate(animation) : widget.color,
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show Clip;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
......@@ -90,7 +92,7 @@ class TestRecordingCanvas implements Canvas {
}
/// A [PaintingContext] for tests that use [TestRecordingCanvas].
class TestRecordingPaintingContext implements PaintingContext {
class TestRecordingPaintingContext extends ClipContext implements PaintingContext {
/// Creates a [PaintingContext] for tests that use [TestRecordingCanvas].
TestRecordingPaintingContext(this.canvas);
......@@ -103,28 +105,19 @@ class TestRecordingPaintingContext implements PaintingContext {
}
@override
void pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter) {
canvas.save();
canvas.clipRect(clipRect.shift(offset));
painter(this, offset);
canvas.restore();
void pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter, {Clip clipBehavior = Clip.antiAlias}) {
clipRectAndPaint(clipRect.shift(offset), clipBehavior, clipRect.shift(offset), () => painter(this, offset));
}
@override
void pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, RRect clipRRect, PaintingContextCallback painter) {
canvas.save();
canvas.clipRRect(clipRRect.shift(offset));
painter(this, offset);
canvas.restore();
void pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, RRect clipRRect, PaintingContextCallback painter, {Clip clipBehavior = Clip.antiAlias}) {
assert(clipBehavior != null);
clipRRectAndPaint(clipRRect.shift(offset), clipBehavior, bounds.shift(offset), () => painter(this, offset));
}
@override
void pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter) {
canvas
..save()
..clipPath(clipPath.shift(offset));
painter(this, offset);
canvas.restore();
void pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter, {Clip clipBehavior = Clip.antiAlias}) {
clipPathAndPaint(clipPath.shift(offset), clipBehavior, bounds.shift(offset), () => painter(this, offset));
}
@override
......
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