Unverified Commit c4cdbf86 authored by Hans Muller's avatar Hans Muller Committed by GitHub

Transform Widget alignment is AlignmentGeometry (#12870)



* a => an
parent 16c363ac
...@@ -1618,13 +1618,15 @@ class RenderTransform extends RenderProxyBox { ...@@ -1618,13 +1618,15 @@ class RenderTransform extends RenderProxyBox {
RenderTransform({ RenderTransform({
@required Matrix4 transform, @required Matrix4 transform,
Offset origin, Offset origin,
Alignment alignment, AlignmentGeometry alignment,
TextDirection textDirection,
this.transformHitTests: true, this.transformHitTests: true,
RenderBox child RenderBox child
}) : assert(transform != null), }) : assert(transform != null),
super(child) { super(child) {
this.transform = transform; this.transform = transform;
this.alignment = alignment; this.alignment = alignment;
this.textDirection = textDirection;
this.origin = origin; this.origin = origin;
} }
...@@ -1646,16 +1648,35 @@ class RenderTransform extends RenderProxyBox { ...@@ -1646,16 +1648,35 @@ class RenderTransform extends RenderProxyBox {
/// ///
/// This is equivalent to setting an origin based on the size of the box. /// This is equivalent to setting an origin based on the size of the box.
/// If it is specified at the same time as an offset, both are applied. /// If it is specified at the same time as an offset, both are applied.
Alignment get alignment => _alignment; ///
Alignment _alignment; /// An [AlignmentDirectional.start] value is the same as an [Alignment]
set alignment(Alignment value) { /// whose [Alignment.x] value is `-1.0` if [textDirection] is
assert(value == null || (value.x != null && value.y != null)); /// [TextDirection.ltr], and `1.0` if [textDirection] is [TextDirection.rtl].
/// Similarly [AlignmentDirectional.end] is the same as an [Alignment]
/// whose [Alignment.x] value is `1.0` if [textDirection] is
/// [TextDirection.ltr], and `-1.0` if [textDirection] is [TextDirection.rtl].
AlignmentGeometry get alignment => _alignment;
AlignmentGeometry _alignment;
set alignment(AlignmentGeometry value) {
if (_alignment == value) if (_alignment == value)
return; return;
_alignment = value; _alignment = value;
markNeedsPaint(); markNeedsPaint();
} }
/// 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;
markNeedsPaint();
}
/// When set to true, hit tests are performed based on the position of the /// When set to true, hit tests are performed based on the position of the
/// child as it is painted. When set to false, hit tests are performed /// child as it is painted. When set to false, hit tests are performed
/// ignoring the transformation. /// ignoring the transformation.
...@@ -1713,18 +1734,19 @@ class RenderTransform extends RenderProxyBox { ...@@ -1713,18 +1734,19 @@ class RenderTransform extends RenderProxyBox {
} }
Matrix4 get _effectiveTransform { Matrix4 get _effectiveTransform {
if (_origin == null && _alignment == null) final Alignment resolvedAlignment = alignment?.resolve(textDirection);
if (_origin == null && resolvedAlignment == null)
return _transform; return _transform;
final Matrix4 result = new Matrix4.identity(); final Matrix4 result = new Matrix4.identity();
if (_origin != null) if (_origin != null)
result.translate(_origin.dx, _origin.dy); result.translate(_origin.dx, _origin.dy);
Offset translation; Offset translation;
if (_alignment != null) { if (resolvedAlignment != null) {
translation = _alignment.alongSize(size); translation = resolvedAlignment.alongSize(size);
result.translate(translation.dx, translation.dy); result.translate(translation.dx, translation.dy);
} }
result.multiply(_transform); result.multiply(_transform);
if (_alignment != null) if (resolvedAlignment != null)
result.translate(-translation.dx, -translation.dy); result.translate(-translation.dx, -translation.dy);
if (_origin != null) if (_origin != null)
result.translate(-_origin.dx, -_origin.dy); result.translate(-_origin.dx, -_origin.dy);
...@@ -1770,6 +1792,7 @@ class RenderTransform extends RenderProxyBox { ...@@ -1770,6 +1792,7 @@ class RenderTransform extends RenderProxyBox {
description.add(new TransformProperty('transform matrix', _transform)); description.add(new TransformProperty('transform matrix', _transform));
description.add(new DiagnosticsProperty<Offset>('origin', origin)); description.add(new DiagnosticsProperty<Offset>('origin', origin));
description.add(new DiagnosticsProperty<Alignment>('alignment', alignment)); description.add(new DiagnosticsProperty<Alignment>('alignment', alignment));
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
description.add(new DiagnosticsProperty<bool>('transformHitTests', transformHitTests)); description.add(new DiagnosticsProperty<bool>('transformHitTests', transformHitTests));
} }
} }
...@@ -1822,7 +1845,7 @@ class RenderFittedBox extends RenderProxyBox { ...@@ -1822,7 +1845,7 @@ class RenderFittedBox extends RenderProxyBox {
/// parent's bounds. An alignment of (1.0, 0.5) aligns the child to the middle /// 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. /// of the right edge of its parent's bounds.
/// ///
/// If this is set to a [AlignmentDirectional] object, then /// If this is set to an [AlignmentDirectional] object, then
/// [textDirection] must not be null. /// [textDirection] must not be null.
AlignmentGeometry get alignment => _alignment; AlignmentGeometry get alignment => _alignment;
AlignmentGeometry _alignment; AlignmentGeometry _alignment;
...@@ -1951,7 +1974,7 @@ class RenderFittedBox extends RenderProxyBox { ...@@ -1951,7 +1974,7 @@ class RenderFittedBox extends RenderProxyBox {
/// Applies a translation transformation before painting its child. /// Applies a translation transformation before painting its child.
/// ///
/// The translation is expressed as a [Offset] scaled to the child's size. For /// The translation is expressed as an [Offset] scaled to the child's size. For
/// example, an [Offset] with a `dx` of 0.25 will result in a horizontal /// example, an [Offset] with a `dx` of 0.25 will result in a horizontal
/// translation of one quarter the width of the child. /// translation of one quarter the width of the child.
/// ///
......
...@@ -794,10 +794,17 @@ class Transform extends SingleChildRenderObjectWidget { ...@@ -794,10 +794,17 @@ class Transform extends SingleChildRenderObjectWidget {
/// The alignment of the origin, relative to the size of the box. /// The alignment of the origin, relative to the size of the box.
/// ///
/// This is equivalent to setting an origin based on the size of the box. /// This is equivalent to setting an origin based on the size of the box.
/// If it is specified at the same time as an offset, both are applied. /// If it is specified at the same time as the [origin], both are applied.
final Alignment alignment; ///
/// An [AlignmentDirectional.start] value is the same as an [Alignment]
/// whose [Alignment.x] value is `-1.0` if [textDirection] is
/// [TextDirection.ltr], and `1.0` if [textDirection] is [TextDirection.rtl].
/// Similarly [AlignmentDirectional.end] is the same as an [Alignment]
/// whose [Alignment.x] value is `1.0` if [textDirection] is
/// [TextDirection.ltr], and `-1.0` if [textDirection] is [TextDirection.rtl].
final AlignmentGeometry alignment;
/// Whether to apply the translation when performing hit tests. /// Whether to apply the transformation when performing hit tests.
final bool transformHitTests; final bool transformHitTests;
@override @override
...@@ -806,6 +813,7 @@ class Transform extends SingleChildRenderObjectWidget { ...@@ -806,6 +813,7 @@ class Transform extends SingleChildRenderObjectWidget {
transform: transform, transform: transform,
origin: origin, origin: origin,
alignment: alignment, alignment: alignment,
textDirection: Directionality.of(context),
transformHitTests: transformHitTests transformHitTests: transformHitTests
); );
} }
...@@ -816,6 +824,7 @@ class Transform extends SingleChildRenderObjectWidget { ...@@ -816,6 +824,7 @@ class Transform extends SingleChildRenderObjectWidget {
..transform = transform ..transform = transform
..origin = origin ..origin = origin
..alignment = alignment ..alignment = alignment
..textDirection = Directionality.of(context)
..transformHitTests = transformHitTests; ..transformHitTests = transformHitTests;
} }
} }
...@@ -1195,7 +1204,7 @@ class Padding extends SingleChildRenderObjectWidget { ...@@ -1195,7 +1204,7 @@ class Padding extends SingleChildRenderObjectWidget {
/// * [Center], which is the same as [Align] but with the [alignment] always /// * [Center], which is the same as [Align] but with the [alignment] always
/// set to [Alignment.center]. /// set to [Alignment.center].
/// * [FractionallySizedBox], which sizes its child based on a fraction of its own /// * [FractionallySizedBox], which sizes its child based on a fraction of its own
/// size and positions the child according to a [Alignment] value. /// size and positions the child according to an [Alignment] value.
class Align extends SingleChildRenderObjectWidget { class Align extends SingleChildRenderObjectWidget {
/// Creates an alignment widget. /// Creates an alignment widget.
/// ///
...@@ -1291,9 +1300,9 @@ class Center extends Align { ...@@ -1291,9 +1300,9 @@ class Center extends Align {
/// ///
/// * [SingleChildLayoutDelegate], which controls the layout of the child. /// * [SingleChildLayoutDelegate], which controls the layout of the child.
/// * [Align], which sizes itself based on its child's size and positions /// * [Align], which sizes itself based on its child's size and positions
/// the child according to a [Alignment] value. /// the child according to an [Alignment] value.
/// * [FractionallySizedBox], which sizes its child based on a fraction of its own /// * [FractionallySizedBox], which sizes its child based on a fraction of its own
/// size and positions the child according to a [Alignment] value. /// size and positions the child according to an [Alignment] value.
/// * [CustomMultiChildLayout], which uses a delegate to position multiple /// * [CustomMultiChildLayout], which uses a delegate to position multiple
/// children. /// children.
class CustomSingleChildLayout extends SingleChildRenderObjectWidget { class CustomSingleChildLayout extends SingleChildRenderObjectWidget {
...@@ -1568,7 +1577,7 @@ class ConstrainedBox extends SingleChildRenderObjectWidget { ...@@ -1568,7 +1577,7 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
/// See also: /// See also:
/// ///
/// * [Align] (which sizes itself based on its child's size and positions /// * [Align] (which sizes itself based on its child's size and positions
/// the child according to a [Alignment] value) /// the child according to an [Alignment] value)
/// * [OverflowBox] /// * [OverflowBox]
class FractionallySizedBox extends SingleChildRenderObjectWidget { class FractionallySizedBox extends SingleChildRenderObjectWidget {
/// Creates a widget that sizes its child to a fraction of the total available space. /// Creates a widget that sizes its child to a fraction of the total available space.
...@@ -2269,7 +2278,7 @@ class ListBody extends MultiChildRenderObjectWidget { ...@@ -2269,7 +2278,7 @@ class ListBody extends MultiChildRenderObjectWidget {
/// See also: /// See also:
/// ///
/// * [Align], which sizes itself based on its child's size and positions /// * [Align], which sizes itself based on its child's size and positions
/// the child according to a [Alignment] value. /// the child according to an [Alignment] value.
/// * [CustomSingleChildLayout], which uses a delegate to control the layout of /// * [CustomSingleChildLayout], which uses a delegate to control the layout of
/// a single child. /// a single child.
/// * [CustomMultiChildLayout], which uses a delegate to position multiple /// * [CustomMultiChildLayout], which uses a delegate to position multiple
...@@ -4023,7 +4032,7 @@ class RawImage extends LeafRenderObjectWidget { ...@@ -4023,7 +4032,7 @@ class RawImage extends LeafRenderObjectWidget {
/// How to align the image within its bounds. /// How to align the image within its bounds.
/// ///
/// The alignment aligns the given position in the image to the given position /// The alignment aligns the given position in the image to the given position
/// in the layout bounds. For example, a [Alignment] alignment of (-1.0, /// in the layout bounds. For example, an [Alignment] alignment of (-1.0,
/// -1.0) aligns the image to the top-left corner of its layout bounds, while a /// -1.0) aligns the image to the top-left corner of its layout bounds, while a
/// [Alignment] alignment of (1.0, 1.0) aligns the bottom right of the /// [Alignment] alignment of (1.0, 1.0) aligns the bottom right of the
/// image with the bottom right corner of its layout bounds. Similarly, an /// image with the bottom right corner of its layout bounds. Similarly, an
......
...@@ -106,6 +106,77 @@ void main() { ...@@ -106,6 +106,77 @@ void main() {
expect(didReceiveTap, isTrue); expect(didReceiveTap, isTrue);
}); });
testWidgets('Transform AlignmentDirectional alignment', (WidgetTester tester) async {
bool didReceiveTap = false;
Widget buildFrame(TextDirection textDirection, AlignmentGeometry alignment) {
return new Directionality(
textDirection: textDirection,
child: new Stack(
children: <Widget>[
new Positioned(
top: 100.0,
left: 100.0,
child: new Container(
width: 100.0,
height: 100.0,
color: const Color(0xFF0000FF),
),
),
new Positioned(
top: 100.0,
left: 100.0,
child: new Container(
width: 100.0,
height: 100.0,
child: new Transform(
transform: new Matrix4.diagonal3Values(0.5, 0.5, 1.0),
alignment: alignment,
child: new GestureDetector(
onTap: () {
didReceiveTap = true;
},
child: new Container(
color: const Color(0xFF00FFFF),
),
),
),
),
),
],
),
);
}
await tester.pumpWidget(buildFrame(TextDirection.ltr, AlignmentDirectional.centerEnd));
didReceiveTap = false;
await tester.tapAt(const Offset(110.0, 110.0));
expect(didReceiveTap, isFalse);
await tester.tapAt(const Offset(190.0, 150.0));
expect(didReceiveTap, isTrue);
await tester.pumpWidget(buildFrame(TextDirection.rtl, AlignmentDirectional.centerStart));
didReceiveTap = false;
await tester.tapAt(const Offset(110.0, 110.0));
expect(didReceiveTap, isFalse);
await tester.tapAt(const Offset(190.0, 150.0));
expect(didReceiveTap, isTrue);
await tester.pumpWidget(buildFrame(TextDirection.ltr, AlignmentDirectional.centerStart));
didReceiveTap = false;
await tester.tapAt(const Offset(190.0, 150.0));
expect(didReceiveTap, isFalse);
await tester.tapAt(const Offset(110.0, 150.0));
expect(didReceiveTap, isTrue);
await tester.pumpWidget(buildFrame(TextDirection.rtl, AlignmentDirectional.centerEnd));
didReceiveTap = false;
await tester.tapAt(const Offset(190.0, 150.0));
expect(didReceiveTap, isFalse);
await tester.tapAt(const Offset(110.0, 150.0));
expect(didReceiveTap, isTrue);
});
testWidgets('Transform offset + alignment', (WidgetTester tester) async { testWidgets('Transform offset + alignment', (WidgetTester tester) async {
bool didReceiveTap = false; bool didReceiveTap = false;
await tester.pumpWidget( await tester.pumpWidget(
......
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