Check if a double is NaN before converting to it int (#61940)
This commit is contained in:
parent
056e455e94
commit
64173f75c3
@ -127,6 +127,13 @@ class MatrixUtils {
|
|||||||
///
|
///
|
||||||
/// This function assumes the given point has a z-coordinate of 0.0. The
|
/// This function assumes the given point has a z-coordinate of 0.0. The
|
||||||
/// z-coordinate of the result is ignored.
|
/// z-coordinate of the result is ignored.
|
||||||
|
///
|
||||||
|
/// While not common, this method may return (NaN, NaN), iff the given `point`
|
||||||
|
/// results in a "point at infinity" in homogeneous coordinates after applying
|
||||||
|
/// the `transform`. For example, a [RenderObject] may set its transform to
|
||||||
|
/// the zero matrix to indicate its content is currently not visible. Trying
|
||||||
|
/// to convert an `Offset` to its coordinate space always results in
|
||||||
|
/// (NaN, NaN).
|
||||||
static Offset transformPoint(Matrix4 transform, Offset point) {
|
static Offset transformPoint(Matrix4 transform, Offset point) {
|
||||||
final Float64List storage = transform.storage;
|
final Float64List storage = transform.storage;
|
||||||
final double x = point.dx;
|
final double x = point.dx;
|
||||||
|
@ -2293,7 +2293,9 @@ abstract class RenderBox extends RenderObject {
|
|||||||
/// coordinate system of `ancestor` (which must be an ancestor of this render
|
/// coordinate system of `ancestor` (which must be an ancestor of this render
|
||||||
/// object) instead of to the global coordinate system.
|
/// object) instead of to the global coordinate system.
|
||||||
///
|
///
|
||||||
/// This method is implemented in terms of [getTransformTo].
|
/// This method is implemented in terms of [getTransformTo]. If the transform
|
||||||
|
/// matrix puts the given `point` on the line at infinity (for instance, when
|
||||||
|
/// the transform matrix is the zero matrix), this method returns (NaN, NaN).
|
||||||
Offset localToGlobal(Offset point, { RenderObject ancestor }) {
|
Offset localToGlobal(Offset point, { RenderObject ancestor }) {
|
||||||
return MatrixUtils.transformPoint(getTransformTo(ancestor), point);
|
return MatrixUtils.transformPoint(getTransformTo(ancestor), point);
|
||||||
}
|
}
|
||||||
|
@ -1936,10 +1936,12 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
|||||||
Offset _getPixelPerfectCursorOffset(Rect caretRect) {
|
Offset _getPixelPerfectCursorOffset(Rect caretRect) {
|
||||||
final Offset caretPosition = localToGlobal(caretRect.topLeft);
|
final Offset caretPosition = localToGlobal(caretRect.topLeft);
|
||||||
final double pixelMultiple = 1.0 / _devicePixelRatio;
|
final double pixelMultiple = 1.0 / _devicePixelRatio;
|
||||||
final int quotientX = (caretPosition.dx / pixelMultiple).round();
|
final double pixelPerfectOffsetX = caretPosition.dx.isFinite
|
||||||
final int quotientY = (caretPosition.dy / pixelMultiple).round();
|
? (caretPosition.dx / pixelMultiple).round() * pixelMultiple - caretPosition.dx
|
||||||
final double pixelPerfectOffsetX = quotientX * pixelMultiple - caretPosition.dx;
|
: caretPosition.dx;
|
||||||
final double pixelPerfectOffsetY = quotientY * pixelMultiple - caretPosition.dy;
|
final double pixelPerfectOffsetY = caretPosition.dy.isFinite
|
||||||
|
? (caretPosition.dy / pixelMultiple).round() * pixelMultiple - caretPosition.dy
|
||||||
|
: caretPosition.dy;
|
||||||
return Offset(pixelPerfectOffsetX, pixelPerfectOffsetY);
|
return Offset(pixelPerfectOffsetX, pixelPerfectOffsetY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4321,6 +4321,30 @@ void main() {
|
|||||||
expect(scrollController.offset, 0);
|
expect(scrollController.offset, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('getLocalRectForCaret does not throw when it sees an infinite point', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: SkipPainting(
|
||||||
|
child: Transform(
|
||||||
|
transform: Matrix4.zero(),
|
||||||
|
child: EditableText(
|
||||||
|
controller: TextEditingController(),
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
style: textStyle,
|
||||||
|
cursorColor: Colors.blue,
|
||||||
|
backgroundCursorColor: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||||
|
final Rect rect = state.renderEditable.getLocalRectForCaret(const TextPosition(offset: 0));
|
||||||
|
expect(rect.isFinite, false);
|
||||||
|
expect(tester.takeException(), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('obscured multiline fields throw an exception', (WidgetTester tester) async {
|
testWidgets('obscured multiline fields throw an exception', (WidgetTester tester) async {
|
||||||
final TextEditingController controller = TextEditingController();
|
final TextEditingController controller = TextEditingController();
|
||||||
expect(
|
expect(
|
||||||
@ -5350,3 +5374,15 @@ class NoImplicitScrollPhysics extends AlwaysScrollableScrollPhysics {
|
|||||||
return NoImplicitScrollPhysics(parent: buildParent(ancestor));
|
return NoImplicitScrollPhysics(parent: buildParent(ancestor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SkipPainting extends SingleChildRenderObjectWidget {
|
||||||
|
const SkipPainting({ Key key, Widget child }): super(key: key, child: child);
|
||||||
|
|
||||||
|
@override
|
||||||
|
SkipPaintingRenderObject createRenderObject(BuildContext context) => SkipPaintingRenderObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
class SkipPaintingRenderObject extends RenderProxyBox {
|
||||||
|
@override
|
||||||
|
void paint(PaintingContext context, Offset offset) { }
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user