Unverified Commit 64173f75 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Check if a double is NaN before converting to it int (#61940)

parent 056e455e
...@@ -127,6 +127,13 @@ class MatrixUtils { ...@@ -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 { ...@@ -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 { ...@@ -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() { ...@@ -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 { ...@@ -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) { }
}
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