Restore FractionalOffset operators (#12368)

These now act the way they used to act if both operands are
FractionalOffsets.  Once you mix in some other AlignmentGeometry
objects, everything gets converted to the AlignmentGeometry coordinate
system.
This commit is contained in:
Adam Barth 2017-10-02 21:48:24 -07:00 committed by GitHub
parent 7bfa3c5676
commit 31b6ac049c
3 changed files with 85 additions and 7 deletions

View File

@ -540,7 +540,7 @@ class AlignmentDirectional extends AlignmentGeometry {
if (start == 1.0 && y == 1.0) if (start == 1.0 && y == 1.0)
return 'AlignmentDirectional.bottomEnd'; return 'AlignmentDirectional.bottomEnd';
return 'AlignmentDirectional(${start.toStringAsFixed(1)}, ' return 'AlignmentDirectional(${start.toStringAsFixed(1)}, '
'${y.toStringAsFixed(1)})'; '${y.toStringAsFixed(1)})';
} }
@override @override

View File

@ -21,6 +21,31 @@ import 'basic_types.dart';
/// The [FractionalOffset] class specifies offsets in terms of a distance from /// The [FractionalOffset] class specifies offsets in terms of a distance from
/// the top left, regardless of the [TextDirection]. /// the top left, regardless of the [TextDirection].
/// ///
/// ## Design discussion
///
/// [FractionalOffset] and [Alignment] are two different representations of the
/// same information: the location within a rectangle relative to the size of
/// the rectangle. The difference between the two classes is in the coordinate
/// system they use to represent the location.
///
/// [FractionalOffset] uses a coordinate system with an origin in the top-left
/// corner of the rectangle whereas [Alignment] uses a coordinate system with an
/// origin in the center of the rectangle.
///
/// Historically, [FractionalOffset] predates [Alignment]. When we attempted to
/// make a version of [FractionalOffset] that adapted to the [TextDirection], we
/// ran into difficulty because placing the origin in the top-left corner
/// introduced a left-to-right bias that was hard to remove.
///
/// By placing the origin in the center, [Alignment] and [AlignmentDirectional]
/// are able to use the same origin, which means we can use a linear function to
/// resolve an [AlignmentDirectional] into an [Alignment] in both
/// [TextDirection.rtl] and [TextDirection.ltr].
///
/// [Alignment] is better for most purposes than [FractionalOffset] and should
/// be used instead of [FractionalOffset]. We continue to implement
/// [FractionalOffset] to support code that predates [Alignment].
///
/// See also: /// See also:
/// ///
/// * [Alignment], which uses a coordinate system based on the center of the /// * [Alignment], which uses a coordinate system based on the center of the
@ -108,6 +133,47 @@ class FractionalOffset extends Alignment {
/// The bottom right corner. /// The bottom right corner.
static const FractionalOffset bottomRight = const FractionalOffset(1.0, 1.0); static const FractionalOffset bottomRight = const FractionalOffset(1.0, 1.0);
@override
Alignment operator -(Alignment other) {
if (other is! FractionalOffset)
return super - other;
final FractionalOffset typedOther = other;
return new FractionalOffset(dx - typedOther.dx, dy - typedOther.dy);
}
@override
Alignment operator +(Alignment other) {
if (other is! FractionalOffset)
return super + other;
final FractionalOffset typedOther = other;
return new FractionalOffset(dx + typedOther.dx, dy + typedOther.dy);
}
@override
FractionalOffset operator -() {
return new FractionalOffset(-dx, -dy);
}
@override
FractionalOffset operator *(double other) {
return new FractionalOffset(dx * other, dy * other);
}
@override
FractionalOffset operator /(double other) {
return new FractionalOffset(dx / other, dy / other);
}
@override
FractionalOffset operator ~/(double other) {
return new FractionalOffset((dx ~/ other).toDouble(), (dy ~/ other).toDouble());
}
@override
FractionalOffset operator %(double other) {
return new FractionalOffset(dx % other, dy % other);
}
/// Linearly interpolate between two [FractionalOffset]s. /// Linearly interpolate between two [FractionalOffset]s.
/// ///
/// If either is null, this function interpolates from [FractionalOffset.center]. /// If either is null, this function interpolates from [FractionalOffset.center].
@ -120,4 +186,10 @@ class FractionalOffset extends Alignment {
return new FractionalOffset(ui.lerpDouble(a.dx, 0.5, t), ui.lerpDouble(a.dy, 0.5, t)); return new FractionalOffset(ui.lerpDouble(a.dx, 0.5, t), ui.lerpDouble(a.dy, 0.5, t));
return new FractionalOffset(ui.lerpDouble(a.dx, b.dx, t), ui.lerpDouble(a.dy, b.dy, t)); return new FractionalOffset(ui.lerpDouble(a.dx, b.dx, t), ui.lerpDouble(a.dy, b.dy, t));
} }
@override
String toString() {
return 'FractionalOffset(${dx.toStringAsFixed(1)}, '
'${dy.toStringAsFixed(1)})';
}
} }

View File

@ -7,14 +7,20 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
test('FractionalOffset control test', () { test('FractionalOffset control test', () {
const FractionalOffset offset = const FractionalOffset(0.5, 0.25); const FractionalOffset a = const FractionalOffset(0.5, 0.25);
const FractionalOffset b = const FractionalOffset(1.25, 0.75);
expect(offset, hasOneLineDescription); expect(a, hasOneLineDescription);
expect(offset.hashCode, equals(const FractionalOffset(0.5, 0.25).hashCode)); expect(a.hashCode, equals(const FractionalOffset(0.5, 0.25).hashCode));
expect(a.toString(), equals('FractionalOffset(0.5, 0.3)'));
expect(offset / 2.0, const Alignment(0.0, -0.25)); expect(-a, const FractionalOffset(-0.5, -0.25));
expect(offset ~/ 2.0, Alignment.center); expect(a - b, const FractionalOffset(-0.75, -0.5));
expect(offset % 5.0, const Alignment(0.0, 4.5)); expect(a + b, const FractionalOffset(1.75, 1.0));
expect(a * 2.0, const FractionalOffset(1.0, 0.5));
expect(a / 2.0, const FractionalOffset(0.25, 0.125));
expect(a ~/ 2.0, const FractionalOffset(0.0, 0.0));
expect(a % 5.0, const FractionalOffset(0.5, 0.25));
}); });
test('FractionalOffset.lerp()', () { test('FractionalOffset.lerp()', () {