@@ -18,6 +18,7 @@ import 'package:super_editor/src/default_editor/text_tools.dart';
18
18
import 'package:super_editor/src/document_operations/selection_operations.dart' ;
19
19
import 'package:super_editor/src/infrastructure/_logging.dart' ;
20
20
import 'package:super_editor/src/infrastructure/content_layers.dart' ;
21
+ import 'package:super_editor/src/infrastructure/flutter/eager_pan_gesture_recognizer.dart' ;
21
22
import 'package:super_editor/src/infrastructure/flutter/build_context.dart' ;
22
23
import 'package:super_editor/src/infrastructure/flutter/flutter_scheduler.dart' ;
23
24
import 'package:super_editor/src/infrastructure/multi_tap_gesture.dart' ;
@@ -308,6 +309,7 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
308
309
final _magnifierFocalPointInDocumentSpace = ValueNotifier <Offset ?>(null );
309
310
Offset ? _dragEndInInteractor;
310
311
DragMode ? _dragMode;
312
+ DragStartDetails ? _dragStartDetails;
311
313
// TODO: HandleType is the wrong type here, we need collapsed/base/extent,
312
314
// not collapsed/upstream/downstream. Change the type once it's working.
313
315
HandleType ? _dragHandleType;
@@ -896,6 +898,9 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
896
898
}
897
899
898
900
void _onPanStart (DragStartDetails details) {
901
+ // Store the gesture start details to disambiguate horizontal vs vertical dragging, later.
902
+ _dragStartDetails = details;
903
+
899
904
// Stop waiting for a long-press to start, if a long press isn't already in-progress.
900
905
_globalTapDownOffset = null ;
901
906
_tapDownLongPressTimer? .cancel ();
@@ -905,8 +910,11 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
905
910
// bit of slop might be the problem.
906
911
final selection = widget.selection.value;
907
912
if (selection == null ) {
908
- // There isn't a selection, the user is dragging to scroll the document.
909
- _startDragScrolling (details);
913
+ // There isn't a selection, but we still don't know if the user is dragging
914
+ // vertically or horizontally. Wait until the onPanUpdate event is fired
915
+ // to decide whether or not we should scroll the document.
916
+ _dragMode = DragMode .waitingForScrollDirection;
917
+ _updateDragStartLocation (details.globalPosition);
910
918
return ;
911
919
}
912
920
@@ -924,9 +932,11 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
924
932
_dragMode = DragMode .extent;
925
933
_dragHandleType = HandleType .downstream;
926
934
} else {
927
- // The user isn't dragging over a handle.
928
- // Start scrolling the document.
929
- _startDragScrolling (details);
935
+ // The user isn't dragging over a handle, but we still don't know if the user is dragging
936
+ // vertically or horizontally. Wait until the onPanUpdate event is fired
937
+ // to decide whether or not we should scroll the document.
938
+ _dragMode = DragMode .waitingForScrollDirection;
939
+ _updateDragStartLocation (details.globalPosition);
930
940
931
941
return ;
932
942
}
@@ -936,31 +946,7 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
936
946
..hideToolbar ()
937
947
..showMagnifier ();
938
948
939
- _globalStartDragOffset = details.globalPosition;
940
- final interactorBox = context.findRenderObject () as RenderBox ;
941
- final handleOffsetInInteractor = interactorBox.globalToLocal (details.globalPosition);
942
- _dragStartInDoc = _interactorOffsetToDocumentOffset (handleOffsetInInteractor);
943
-
944
- if (_dragHandleType != null ) {
945
- _startDragPositionOffset = _docLayout
946
- .getRectForPosition (
947
- _dragHandleType == HandleType .upstream ? selection.base : selection.extent,
948
- )!
949
- .center;
950
- } else {
951
- // User is long-press dragging, which is why there's no drag handle type.
952
- // In this case, the start drag offset is wherever the user touched.
953
- _startDragPositionOffset = _dragStartInDoc! ;
954
- }
955
-
956
- // We need to record the scroll offset at the beginning of
957
- // a drag for the case that this interactor is embedded
958
- // within an ancestor Scrollable. We need to use this value
959
- // to calculate a scroll delta on every scroll frame to
960
- // account for the fact that this interactor is moving within
961
- // the ancestor scrollable, despite the fact that the user's
962
- // finger/mouse position hasn't changed.
963
- _dragStartScrollOffset = scrollPosition.pixels;
949
+ _updateDragStartLocation (details.globalPosition);
964
950
965
951
widget.dragHandleAutoScroller.value? .startAutoScrollHandleMonitoring ();
966
952
@@ -1011,9 +997,26 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
1011
997
}
1012
998
1013
999
void _onPanUpdate (DragUpdateDetails details) {
1000
+ if (_dragMode == DragMode .waitingForScrollDirection) {
1001
+ if (_globalStartDragOffset != null && (details.globalPosition.dy - _globalStartDragOffset! .dy).abs () > kPanSlop) {
1002
+ // The user is dragging vertically. Start scrolling the document.
1003
+ _startDragScrolling (_dragStartDetails! );
1004
+ }
1005
+ }
1006
+
1014
1007
if (_dragMode == DragMode .scroll) {
1015
1008
// The user is trying to scroll the document. Scroll it, accordingly.
1016
- _scrollingDrag! .update (details);
1009
+ _scrollingDrag! .update (
1010
+ DragUpdateDetails (
1011
+ globalPosition: details.globalPosition,
1012
+ localPosition: details.localPosition,
1013
+ primaryDelta: details.delta.dy,
1014
+ // Having a primary delta requires that one of the
1015
+ // offset dimensions is zero.
1016
+ delta: Offset (0.0 , details.delta.dy),
1017
+ ),
1018
+ );
1019
+
1017
1020
return ;
1018
1021
}
1019
1022
@@ -1109,6 +1112,9 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
1109
1112
// or with a long-press. Finish that interaction.
1110
1113
_onDragSelectionEnd ();
1111
1114
break ;
1115
+ case DragMode .waitingForScrollDirection:
1116
+ _dragMode = null ;
1117
+ break ;
1112
1118
case null :
1113
1119
// The user wasn't dragging over a selection. Do nothing.
1114
1120
break ;
@@ -1341,6 +1347,35 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
1341
1347
_magnifierFocalPointInDocumentSpace.value = centerOfContentAtOffset;
1342
1348
}
1343
1349
1350
+ void _updateDragStartLocation (Offset globalOffset) {
1351
+ _globalStartDragOffset = globalOffset;
1352
+ final interactorBox = context.findRenderObject () as RenderBox ;
1353
+ final handleOffsetInInteractor = interactorBox.globalToLocal (globalOffset);
1354
+ _dragStartInDoc = _interactorOffsetToDocumentOffset (handleOffsetInInteractor);
1355
+
1356
+ final selection = widget.selection.value;
1357
+ if (_dragHandleType != null && selection != null ) {
1358
+ _startDragPositionOffset = _docLayout
1359
+ .getRectForPosition (
1360
+ _dragHandleType == HandleType .upstream ? selection.base : selection.extent,
1361
+ )!
1362
+ .center;
1363
+ } else {
1364
+ // User is long-press dragging, which is why there's no drag handle type.
1365
+ // In this case, the start drag offset is wherever the user touched.
1366
+ _startDragPositionOffset = _dragStartInDoc! ;
1367
+ }
1368
+
1369
+ // We need to record the scroll offset at the beginning of
1370
+ // a drag for the case that this interactor is embedded
1371
+ // within an ancestor Scrollable. We need to use this value
1372
+ // to calculate a scroll delta on every scroll frame to
1373
+ // account for the fact that this interactor is moving within
1374
+ // the ancestor scrollable, despite the fact that the user's
1375
+ // finger/mouse position hasn't changed.
1376
+ _dragStartScrollOffset = scrollPosition.pixels;
1377
+ }
1378
+
1344
1379
@override
1345
1380
Widget build (BuildContext context) {
1346
1381
if (widget.scrollController.hasClients) {
@@ -1374,27 +1409,9 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
1374
1409
..gestureSettings = gestureSettings;
1375
1410
},
1376
1411
),
1377
- // We use a combination of a VerticalDragGestureRecognizer and a HorizontalDragGestureRecognizer
1378
- // instead of a PanGestureRecognizer because `Scrollable` also uses a VerticalDragGestureRecognizer
1379
- // and we need to beat out any ancestor `Scrollable` in the gesture arena.
1380
- // Without the HorizontalDragGestureRecognizer, horizontal drags aren't reported here
1381
- // when the editor has an ancestor `Scrollable`.
1382
- VerticalDragGestureRecognizer : GestureRecognizerFactoryWithHandlers <VerticalDragGestureRecognizer >(
1383
- () => VerticalDragGestureRecognizer (),
1384
- (VerticalDragGestureRecognizer instance) {
1385
- instance
1386
- ..dragStartBehavior = DragStartBehavior .down
1387
- ..onDown = _onPanDown
1388
- ..onStart = _onPanStart
1389
- ..onUpdate = _onPanUpdate
1390
- ..onEnd = _onPanEnd
1391
- ..onCancel = _onPanCancel
1392
- ..gestureSettings = gestureSettings;
1393
- },
1394
- ),
1395
- HorizontalDragGestureRecognizer : GestureRecognizerFactoryWithHandlers <HorizontalDragGestureRecognizer >(
1396
- () => HorizontalDragGestureRecognizer (),
1397
- (HorizontalDragGestureRecognizer instance) {
1412
+ EagerPanGestureRecognizer : GestureRecognizerFactoryWithHandlers <EagerPanGestureRecognizer >(
1413
+ () => EagerPanGestureRecognizer (),
1414
+ (EagerPanGestureRecognizer instance) {
1398
1415
instance
1399
1416
..dragStartBehavior = DragStartBehavior .down
1400
1417
..onDown = _onPanDown
@@ -1450,7 +1467,9 @@ enum DragMode {
1450
1467
// around the selected word.
1451
1468
longPress,
1452
1469
// Dragging to scroll the document.
1453
- scroll
1470
+ scroll,
1471
+ // We still don't know if the user is dragging vertically or horizontally.
1472
+ waitingForScrollDirection,
1454
1473
}
1455
1474
1456
1475
/// Adds and removes an iOS-style editor toolbar, as dictated by an ancestor
0 commit comments