Commit 05a22fe0 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

FittedBox RTL (#12662)

parent 662b6684
......@@ -1764,15 +1764,30 @@ class RenderFittedBox extends RenderProxyBox {
///
/// The [fit] and [alignment] arguments must not be null.
RenderFittedBox({
RenderBox child,
BoxFit fit: BoxFit.contain,
Alignment alignment: Alignment.center
AlignmentGeometry alignment: Alignment.center,
TextDirection textDirection,
RenderBox child,
}) : assert(fit != null),
assert(alignment != null),
_fit = fit,
_alignment = alignment,
_textDirection = textDirection,
super(child);
Alignment _resolvedAlignment;
void _resolve() {
if (_resolvedAlignment != null)
return;
_resolvedAlignment = alignment.resolve(textDirection);
}
void _markNeedResolution() {
_resolvedAlignment = null;
markNeedsPaint();
}
/// How to inscribe the child into the space allocated during layout.
BoxFit get fit => _fit;
BoxFit _fit;
......@@ -1790,17 +1805,36 @@ class RenderFittedBox extends RenderProxyBox {
/// An alignment of (0.0, 0.0) aligns the child to the top-left corner of its
/// parent's bounds. An alignment of (1.0, 0.5) aligns the child to the middle
/// of the right edge of its parent's bounds.
Alignment get alignment => _alignment;
Alignment _alignment;
set alignment(Alignment value) {
assert(value != null && value.x != null && value.y != null);
///
/// If this is set to a [AlignmentDirectional] object, then
/// [textDirection] must not be null.
AlignmentGeometry get alignment => _alignment;
AlignmentGeometry _alignment;
set alignment(AlignmentGeometry value) {
assert(value != null);
if (_alignment == value)
return;
_alignment = value;
_clearPaintData();
markNeedsPaint();
_markNeedResolution();
}
/// The text direction with which to resolve [alignment].
///
/// This may be changed to null, but only after [alignment] has been changed
/// to a value that does not depend on the direction.
TextDirection get textDirection => _textDirection;
TextDirection _textDirection;
set textDirection(TextDirection value) {
if (_textDirection == value)
return;
_textDirection = value;
_clearPaintData();
_markNeedResolution();
}
// TODO(ianh): The intrinsic dimensions of this box are wrong.
@override
void performLayout() {
if (child != null) {
......@@ -1828,12 +1862,13 @@ class RenderFittedBox extends RenderProxyBox {
_hasVisualOverflow = false;
_transform = new Matrix4.identity();
} else {
_resolve();
final Size childSize = child.size;
final FittedSizes sizes = applyBoxFit(_fit, childSize, size);
final double scaleX = sizes.destination.width / sizes.source.width;
final double scaleY = sizes.destination.height / sizes.source.height;
final Rect sourceRect = _alignment.inscribe(sizes.source, Offset.zero & childSize);
final Rect destinationRect = _alignment.inscribe(sizes.destination, Offset.zero & size);
final Rect sourceRect = _resolvedAlignment.inscribe(sizes.source, Offset.zero & childSize);
final Rect destinationRect = _resolvedAlignment.inscribe(sizes.destination, Offset.zero & size);
_hasVisualOverflow = sourceRect.width < childSize.width || sourceRect.height < childSize.width;
_transform = new Matrix4.translationValues(destinationRect.left, destinationRect.top, 0.0)
..scale(scaleX, scaleY, 1.0)
......@@ -1894,6 +1929,7 @@ class RenderFittedBox extends RenderProxyBox {
super.debugFillProperties(description);
description.add(new EnumProperty<BoxFit>('fit', fit));
description.add(new DiagnosticsProperty<Alignment>('alignment', alignment));
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
}
}
......
......@@ -229,7 +229,7 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
/// The [alignment] argument must not be null.
RenderAligningShiftedBox({
AlignmentGeometry alignment: Alignment.center,
TextDirection textDirection,
@required TextDirection textDirection,
RenderBox child,
}) : assert(alignment != null),
_alignment = alignment,
......@@ -265,7 +265,7 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
AlignmentGeometry _alignment;
/// Sets the alignment to a new value, and triggers a layout update.
///
/// The new alignment must not be null or have any null properties.
/// The new alignment must not be null.
set alignment(AlignmentGeometry value) {
assert(value != null);
if (_alignment == value)
......
......@@ -961,7 +961,7 @@ class FittedBox extends SingleChildRenderObjectWidget {
Key key,
this.fit: BoxFit.contain,
this.alignment: Alignment.center,
Widget child
Widget child,
}) : assert(fit != null),
assert(alignment != null),
super(key: key, child: child);
......@@ -974,16 +974,30 @@ class FittedBox extends SingleChildRenderObjectWidget {
/// An alignment of (-1.0, -1.0) aligns the child to the top-left corner of its
/// parent's bounds. An alignment of (1.0, 0.0) aligns the child to the middle
/// of the right edge of its parent's bounds.
final Alignment alignment;
final AlignmentGeometry alignment;
@override
RenderFittedBox createRenderObject(BuildContext context) => new RenderFittedBox(fit: fit, alignment: alignment);
RenderFittedBox createRenderObject(BuildContext context) {
return new RenderFittedBox(
fit: fit,
alignment: alignment,
textDirection: Directionality.of(context),
);
}
@override
void updateRenderObject(BuildContext context, RenderFittedBox renderObject) {
renderObject
..fit = fit
..alignment = alignment;
..alignment = alignment
..textDirection = Directionality.of(context);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
description.add(new EnumProperty<BoxFit>('fit', fit));
description.add(new DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
}
}
......
......@@ -112,4 +112,230 @@ void main() {
expect(insidePoint, equals(outsidePoint));
});
testWidgets('FittedBox with no child', (WidgetTester tester) async {
final Key key = new UniqueKey();
await tester.pumpWidget(
new Center(
child: new FittedBox(
key: key,
fit: BoxFit.cover,
),
),
);
final RenderBox box = tester.firstRenderObject(find.byKey(key));
expect(box.size.width, 0.0);
expect(box.size.height, 0.0);
});
testWidgets('Child can be aligned multiple ways in a row', (WidgetTester tester) async {
final Key outside = new UniqueKey();
final Key inside = new UniqueKey();
{ // align RTL
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.rtl,
child: new Center(
child: new Container(
width: 100.0,
height: 100.0,
child: new FittedBox(
key: outside,
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.bottomEnd,
child: new Container(
key: inside,
width: 10.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 10.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(0.0, 90.0));
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(10.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(10.0, 100.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
{ // change direction
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new Container(
width: 100.0,
height: 100.0,
child: new FittedBox(
key: outside,
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.bottomEnd,
child: new Container(
key: inside,
width: 10.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 10.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(90.0, 90.0));
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(10.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(100.0, 100.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
{ // change alignment
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new Container(
width: 100.0,
height: 100.0,
child: new FittedBox(
key: outside,
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.center,
child: new Container(
key: inside,
width: 10.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 10.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(45.0, 45.0));
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(10.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(55.0, 55.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
{ // change size
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new Container(
width: 100.0,
height: 100.0,
child: new FittedBox(
key: outside,
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.center,
child: new Container(
key: inside,
width: 30.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 30.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(35.0, 45.0));
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(30.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(65.0, 55.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
{ // change fit
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new Container(
width: 100.0,
height: 100.0,
child: new FittedBox(
key: outside,
fit: BoxFit.fill,
alignment: AlignmentDirectional.center,
child: new Container(
key: inside,
width: 30.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 30.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(0.0, 0.0));
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(30.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(100.0, 100.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
});
}
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