BoxDecoration.borderRadius and RoundedRectangleBorder.borderRadius RTL (#12603)

This commit is contained in:
Ian Hickson 2017-10-18 11:24:54 -07:00 committed by GitHub
parent f6135107bc
commit 0790891600
8 changed files with 84 additions and 27 deletions

View file

@ -117,8 +117,9 @@ abstract class BorderRadiusGeometry {
/// Linearly interpolate between two [BorderRadiusGeometry] objects.
///
/// If either is null, this function interpolates from [BorderRadius.zero], and
/// the result is an object of the same type as the non-null argument.
/// If either is null, this function interpolates from [BorderRadius.zero],
/// and the result is an object of the same type as the non-null argument. (If
/// both are null, this returns null.)
///
/// If [lerp] is applied to two objects of the same type ([BorderRadius] or
/// [BorderRadiusDirectional]), an object of that type will be returned (though
@ -126,6 +127,8 @@ abstract class BorderRadiusGeometry {
/// representing a combination of both is returned. That object can be turned
/// into a concrete [BorderRadius] using [resolve].
static BorderRadiusGeometry lerp(BorderRadiusGeometry a, BorderRadiusGeometry b, double t) {
if (a == null && b == null)
return null;
a ??= BorderRadius.zero;
b ??= BorderRadius.zero;
return a.add((b.subtract(a)) * t);

View file

@ -123,7 +123,7 @@ class BoxDecoration extends Decoration {
///
/// Applies only to boxes with rectangular shapes; ignored if [shape] is not
/// [BoxShape.rectangle].
final BorderRadius borderRadius;
final BorderRadiusGeometry borderRadius;
/// A list of shadows cast by this box behind the box.
///
@ -159,7 +159,7 @@ class BoxDecoration extends Decoration {
color: Color.lerp(null, color, factor),
image: image, // TODO(ianh): fade the image from transparent
border: BoxBorder.lerp(null, border, factor),
borderRadius: BorderRadius.lerp(null, borderRadius, factor),
borderRadius: BorderRadiusGeometry.lerp(null, borderRadius, factor),
boxShadow: BoxShadow.lerpList(null, boxShadow, factor),
gradient: gradient?.scale(factor),
shape: shape,
@ -223,7 +223,7 @@ class BoxDecoration extends Decoration {
color: Color.lerp(a.color, b.color, t),
image: t < 0.5 ? a.image : b.image, // TODO(ianh): cross-fade the image
border: BoxBorder.lerp(a.border, b.border, t),
borderRadius: BorderRadius.lerp(a.borderRadius, b.borderRadius, t),
borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, b.borderRadius, t),
boxShadow: BoxShadow.lerpList(a.boxShadow, b.boxShadow, t),
gradient: Gradient.lerp(a.gradient, b.gradient, t),
shape: t < 0.5 ? a.shape : b.shape,
@ -269,20 +269,20 @@ class BoxDecoration extends Decoration {
properties.add(new DiagnosticsProperty<Color>('color', color, defaultValue: null));
properties.add(new DiagnosticsProperty<DecorationImage>('image', image, defaultValue: null));
properties.add(new DiagnosticsProperty<BoxBorder>('border', border, defaultValue: null));
properties.add(new DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius, defaultValue: null));
properties.add(new DiagnosticsProperty<BorderRadiusGeometry>('borderRadius', borderRadius, defaultValue: null));
properties.add(new IterableProperty<BoxShadow>('boxShadow', boxShadow, defaultValue: null, style: DiagnosticsTreeStyle.whitespace));
properties.add(new DiagnosticsProperty<Gradient>('gradient', gradient, defaultValue: null));
properties.add(new EnumProperty<BoxShape>('shape', shape, defaultValue: BoxShape.rectangle));
}
@override
bool hitTest(Size size, Offset position) {
bool hitTest(Size size, Offset position, { TextDirection textDirection }) {
assert(shape != null);
assert((Offset.zero & size).contains(position));
switch (shape) {
case BoxShape.rectangle:
if (borderRadius != null) {
final RRect bounds = borderRadius.toRRect(Offset.zero & size);
final RRect bounds = borderRadius.resolve(textDirection).toRRect(Offset.zero & size);
return bounds.contains(position);
}
return true;
@ -332,7 +332,7 @@ class _BoxDecorationPainter extends BoxPainter {
return _cachedBackgroundPaint;
}
void _paintBox(Canvas canvas, Rect rect, Paint paint) {
void _paintBox(Canvas canvas, Rect rect, Paint paint, TextDirection textDirection) {
switch (_decoration.shape) {
case BoxShape.circle:
assert(_decoration.borderRadius == null);
@ -344,13 +344,13 @@ class _BoxDecorationPainter extends BoxPainter {
if (_decoration.borderRadius == null) {
canvas.drawRect(rect, paint);
} else {
canvas.drawRRect(_decoration.borderRadius.toRRect(rect), paint);
canvas.drawRRect(_decoration.borderRadius.resolve(textDirection).toRRect(rect), paint);
}
break;
}
}
void _paintShadows(Canvas canvas, Rect rect) {
void _paintShadows(Canvas canvas, Rect rect, TextDirection textDirection) {
if (_decoration.boxShadow == null)
return;
for (BoxShadow boxShadow in _decoration.boxShadow) {
@ -358,13 +358,13 @@ class _BoxDecorationPainter extends BoxPainter {
..color = boxShadow.color
..maskFilter = new MaskFilter.blur(BlurStyle.normal, boxShadow.blurSigma);
final Rect bounds = rect.shift(boxShadow.offset).inflate(boxShadow.spreadRadius);
_paintBox(canvas, bounds, paint);
_paintBox(canvas, bounds, paint, textDirection);
}
}
void _paintBackgroundColor(Canvas canvas, Rect rect) {
void _paintBackgroundColor(Canvas canvas, Rect rect, TextDirection textDirection) {
if (_decoration.color != null || _decoration.gradient != null)
_paintBox(canvas, rect, _getBackgroundPaint(rect));
_paintBox(canvas, rect, _getBackgroundPaint(rect), textDirection);
}
ImageStream _imageStream;
@ -399,7 +399,7 @@ class _BoxDecorationPainter extends BoxPainter {
if (_decoration.shape == BoxShape.circle)
clipPath = new Path()..addOval(rect);
else if (_decoration.borderRadius != null)
clipPath = new Path()..addRRect(_decoration.borderRadius.toRRect(rect));
clipPath = new Path()..addRRect(_decoration.borderRadius.resolve(configuration.textDirection).toRRect(rect));
if (clipPath != null) {
canvas.save();
canvas.clipPath(clipPath);
@ -444,8 +444,9 @@ class _BoxDecorationPainter extends BoxPainter {
assert(configuration != null);
assert(configuration.size != null);
final Rect rect = offset & configuration.size;
_paintShadows(canvas, rect);
_paintBackgroundColor(canvas, rect);
final TextDirection textDirection = configuration.textDirection;
_paintShadows(canvas, rect, textDirection);
_paintBackgroundColor(canvas, rect, textDirection);
_paintBackgroundImage(canvas, rect, configuration);
_decoration.border?.paint(
canvas,

View file

@ -122,7 +122,12 @@ abstract class Decoration extends Diagnosticable {
/// if the decoration only draws a circle, this function might
/// return true if the point was inside the circle and false
/// otherwise.
bool hitTest(Size size, Offset position) => true;
///
/// The decoration may be sensitive to the [TextDirection]. The
/// `textDirection` argument should therefore be provided. If it is known that
/// the decoration is not affected by the text direction, then the argument
/// may be ommitted or set to null.
bool hitTest(Size size, Offset position, { TextDirection textDirection }) => true;
/// Returns a [BoxPainter] that will paint this decoration.
///

View file

@ -195,7 +195,7 @@ class FlutterLogoDecoration extends Decoration {
@override
// TODO(ianh): better hit testing
bool hitTest(Size size, Offset position) => true;
bool hitTest(Size size, Offset position, { TextDirection textDirection }) => true;
@override
BoxPainter createBoxPainter([VoidCallback onChanged]) {

View file

@ -38,7 +38,7 @@ class RoundedRectangleBorder extends ShapeBorder {
final BorderSide side;
/// The radii for each corner.
final BorderRadius borderRadius;
final BorderRadiusGeometry borderRadius;
@override
EdgeInsetsGeometry get dimensions {
@ -92,13 +92,13 @@ class RoundedRectangleBorder extends ShapeBorder {
@override
Path getInnerPath(Rect rect, { TextDirection textDirection }) {
return new Path()
..addRRect(borderRadius.toRRect(rect).deflate(side.width));
..addRRect(borderRadius.resolve(textDirection).toRRect(rect).deflate(side.width));
}
@override
Path getOuterPath(Rect rect, { TextDirection textDirection }) {
return new Path()
..addRRect(borderRadius.toRRect(rect));
..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
}
@override
@ -109,9 +109,9 @@ class RoundedRectangleBorder extends ShapeBorder {
case BorderStyle.solid:
final double width = side.width;
if (width == 0.0) {
canvas.drawRRect(borderRadius.toRRect(rect), side.toPaint());
canvas.drawRRect(borderRadius.resolve(textDirection).toRRect(rect), side.toPaint());
} else {
final RRect outer = borderRadius.toRRect(rect);
final RRect outer = borderRadius.resolve(textDirection).toRRect(rect);
final RRect inner = outer.deflate(width);
final Paint paint = new Paint()
..color = side.color;

View file

@ -1482,7 +1482,7 @@ class RenderDecoratedBox extends RenderProxyBox {
@required Decoration decoration,
DecorationPosition position: DecorationPosition.background,
ImageConfiguration configuration: ImageConfiguration.empty,
RenderBox child
RenderBox child,
}) : assert(decoration != null),
assert(position != null),
assert(configuration != null),
@ -1522,6 +1522,9 @@ class RenderDecoratedBox extends RenderProxyBox {
/// The settings to pass to the decoration when painting, so that it can
/// resolve images appropriately. See [ImageProvider.resolve] and
/// [BoxPainter.paint].
///
/// The [ImageConfiguration.textDirection] field is also used by
/// direction-sensitive [Decoration]s for painting and hit-testing.
ImageConfiguration get configuration => _configuration;
ImageConfiguration _configuration;
set configuration(ImageConfiguration value) {
@ -1541,7 +1544,7 @@ class RenderDecoratedBox extends RenderProxyBox {
@override
bool hitTestSelf(Offset position) {
return _decoration.hitTest(size, position);
return _decoration.hitTest(size, position, textDirection: configuration.textDirection);
}
@override

View file

@ -0,0 +1,45 @@
// Copyright 2017 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/painting.dart';
import '../rendering/mock_canvas.dart';
void main() {
test('BoxDecoration with BorderRadiusDirectional', () {
final BoxDecoration decoration = const BoxDecoration(
color: const Color(0xFF000000),
borderRadius: const BorderRadiusDirectional.only(topStart: const Radius.circular(100.0)),
);
final BoxPainter painter = decoration.createBoxPainter();
const Size size = const Size(1000.0, 1000.0);
expect(
(Canvas canvas) {
painter.paint(
canvas,
const Offset(0.0, 0.0),
const ImageConfiguration(size: size, textDirection: TextDirection.rtl),
);
},
paints
..rrect(rrect: new RRect.fromRectAndCorners(Offset.zero & size, topRight: const Radius.circular(100.0)))
);
expect(decoration.hitTest(size, const Offset(10.0, 10.0), textDirection: TextDirection.rtl), isTrue);
expect(decoration.hitTest(size, const Offset(990.0, 10.0), textDirection: TextDirection.rtl), isFalse);
expect(
(Canvas canvas) {
painter.paint(
canvas,
const Offset(0.0, 0.0),
const ImageConfiguration(size: size, textDirection: TextDirection.ltr),
);
},
paints
..rrect(rrect: new RRect.fromRectAndCorners(Offset.zero & size, topLeft: const Radius.circular(100.0)))
);
expect(decoration.hitTest(size, const Offset(10.0, 10.0), textDirection: TextDirection.ltr), isFalse);
expect(decoration.hitTest(size, const Offset(990.0, 10.0), textDirection: TextDirection.ltr), isTrue);
});
}

View file

@ -138,7 +138,7 @@ void main() {
expect(border.left.width, closeTo(1.9, 0.1));
expect(border.left.style, BorderStyle.solid);
expect(border.left.color, const Color(0xFF151515));
expect(actualDecoration.borderRadius.topLeft.x, closeTo(6.8, 0.1));
expect(actualDecoration.borderRadius.resolve(TextDirection.ltr).topLeft.x, closeTo(6.8, 0.1));
expect(actualDecoration.shape, BoxShape.rectangle);
expect(actualDecoration.boxShadow[0].blurRadius, closeTo(3.1, 0.1));
expect(actualDecoration.boxShadow[0].spreadRadius, closeTo(1.2, 0.1));