feat: add more gesture interceptors (#757)
This commit is contained in:
parent
b927ec0685
commit
c97842ae91
4 changed files with 59 additions and 14 deletions
|
|
@ -1,11 +1,13 @@
|
||||||
|
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_editor/src/editor/editor_component/service/selection/mobile_selection_service.dart';
|
import 'package:appflowy_editor/src/editor/editor_component/service/selection/mobile_selection_service.dart';
|
||||||
import 'package:appflowy_editor/src/editor/editor_component/service/selection/shared.dart';
|
import 'package:appflowy_editor/src/editor/editor_component/service/selection/shared.dart';
|
||||||
import 'package:appflowy_editor/src/flutter/overlay.dart';
|
import 'package:appflowy_editor/src/flutter/overlay.dart';
|
||||||
import 'package:appflowy_editor/src/service/selection/selection_gesture.dart';
|
import 'package:appflowy_editor/src/service/selection/selection_gesture.dart';
|
||||||
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
class DesktopSelectionServiceWidget extends StatefulWidget {
|
class DesktopSelectionServiceWidget extends StatefulWidget {
|
||||||
const DesktopSelectionServiceWidget({
|
const DesktopSelectionServiceWidget({
|
||||||
|
|
@ -235,6 +237,14 @@ class _DesktopSelectionServiceWidgetState
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDoubleTapDown(TapDownDetails details) {
|
void _onDoubleTapDown(TapDownDetails details) {
|
||||||
|
final canDoubleTap = _interceptors.every(
|
||||||
|
(interceptor) => interceptor.canDoubleTap?.call(details) ?? true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!canDoubleTap) {
|
||||||
|
return updateSelection(null);
|
||||||
|
}
|
||||||
|
|
||||||
final offset = details.globalPosition;
|
final offset = details.globalPosition;
|
||||||
final node = getNodeInOffset(offset);
|
final node = getNodeInOffset(offset);
|
||||||
final selection = node?.selectable?.getWordBoundaryInOffset(offset);
|
final selection = node?.selectable?.getWordBoundaryInOffset(offset);
|
||||||
|
|
@ -277,6 +287,13 @@ class _DesktopSelectionServiceWidgetState
|
||||||
void _onPanStart(DragStartDetails details) {
|
void _onPanStart(DragStartDetails details) {
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
|
||||||
|
final canPanStart = _interceptors
|
||||||
|
.every((interceptor) => interceptor.canPanStart?.call(details) ?? true);
|
||||||
|
|
||||||
|
if (!canPanStart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_panStartOffset = details.globalPosition.translate(-3.0, 0);
|
_panStartOffset = details.globalPosition.translate(-3.0, 0);
|
||||||
_panStartScrollDy = editorState.service.scrollService?.dy;
|
_panStartScrollDy = editorState.service.scrollService?.dy;
|
||||||
|
|
||||||
|
|
@ -286,6 +303,14 @@ class _DesktopSelectionServiceWidgetState
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onPanUpdate(DragUpdateDetails details) {
|
void _onPanUpdate(DragUpdateDetails details) {
|
||||||
|
final canPanUpdate = _interceptors.every(
|
||||||
|
(interceptor) => interceptor.canPanUpdate?.call(details) ?? true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!canPanUpdate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_panStartOffset == null ||
|
if (_panStartOffset == null ||
|
||||||
_panStartScrollDy == null ||
|
_panStartScrollDy == null ||
|
||||||
_panStartPosition == null) {
|
_panStartPosition == null) {
|
||||||
|
|
@ -316,6 +341,13 @@ class _DesktopSelectionServiceWidgetState
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onPanEnd(DragEndDetails details) {
|
void _onPanEnd(DragEndDetails details) {
|
||||||
|
final canPanEnd = _interceptors
|
||||||
|
.every((interceptor) => interceptor.canPanEnd?.call(details) ?? true);
|
||||||
|
|
||||||
|
if (!canPanEnd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_panStartPosition = null;
|
_panStartPosition = null;
|
||||||
|
|
||||||
editorState.service.scrollService?.stopAutoScroll();
|
editorState.service.scrollService?.stopAutoScroll();
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/core/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/core/location/position.dart';
|
import 'package:appflowy_editor/src/core/location/position.dart';
|
||||||
import 'package:appflowy_editor/src/core/location/selection.dart';
|
import 'package:appflowy_editor/src/core/location/selection.dart';
|
||||||
import 'package:appflowy_editor/src/editor/editor_component/service/selection/mobile_selection_service.dart';
|
import 'package:appflowy_editor/src/editor/editor_component/service/selection/mobile_selection_service.dart';
|
||||||
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
|
|
||||||
|
|
||||||
/// [AppFlowySelectionService] is responsible for processing
|
/// [AppFlowySelectionService] is responsible for processing
|
||||||
/// the [Selection] changes and updates.
|
/// the [Selection] changes and updates.
|
||||||
|
|
@ -87,9 +88,17 @@ class SelectionGestureInterceptor {
|
||||||
SelectionGestureInterceptor({
|
SelectionGestureInterceptor({
|
||||||
required this.key,
|
required this.key,
|
||||||
this.canTap,
|
this.canTap,
|
||||||
|
this.canDoubleTap,
|
||||||
|
this.canPanStart,
|
||||||
|
this.canPanUpdate,
|
||||||
|
this.canPanEnd,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String key;
|
final String key;
|
||||||
|
|
||||||
bool Function(TapDownDetails details)? canTap;
|
bool Function(TapDownDetails details)? canTap;
|
||||||
|
bool Function(TapDownDetails details)? canDoubleTap;
|
||||||
|
bool Function(DragStartDetails details)? canPanStart;
|
||||||
|
bool Function(DragUpdateDetails details)? canPanUpdate;
|
||||||
|
bool Function(DragEndDetails details)? canPanEnd;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
|
||||||
import '../../../../infra/clipboard_test.dart';
|
import '../../../../infra/clipboard_test.dart';
|
||||||
import '../../../infra/testable_editor.dart';
|
import '../../../infra/testable_editor.dart';
|
||||||
|
|
||||||
|
|
@ -40,12 +42,12 @@ void main() async {
|
||||||
(tester) async {
|
(tester) async {
|
||||||
await _testHandleCopy(tester, Document.fromJson(data));
|
await _testHandleCopy(tester, Document.fromJson(data));
|
||||||
});
|
});
|
||||||
testWidgets(
|
// TODO: fix this test
|
||||||
'Presses Command + A in nested document and copy text nestednode',
|
// testWidgets(
|
||||||
(tester) async {
|
// 'Presses Command + A in nested document and copy text nestednode',
|
||||||
// TODO: fix this test
|
// (tester) async {
|
||||||
// await _testNestedNodeCopyPaste(tester, Document.fromJson(exampledoc));
|
// await _testNestedNodeCopyPaste(tester, Document.fromJson(exampledoc));
|
||||||
});
|
// });
|
||||||
|
|
||||||
testWidgets('update selection and execute cut command', (tester) async {
|
testWidgets('update selection and execute cut command', (tester) async {
|
||||||
await _testCutHandle(tester, Document.fromJson(cutData));
|
await _testCutHandle(tester, Document.fromJson(cutData));
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
|
||||||
import '../../../infra/testable_editor.dart';
|
import '../../../infra/testable_editor.dart';
|
||||||
|
|
||||||
const _padding = 24.0;
|
const _padding = 24.0;
|
||||||
|
|
@ -103,8 +105,8 @@ void main() async {
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(.): The purpose of this test is to catch addPostFrameCallback from
|
// TODO(.): The purpose of this test is to catch addPostFrameCallback from
|
||||||
// calculateTextDirection but it doesn't catch it. Commenting the callback
|
// calculateTextDirection but it doesn't catch it. Commenting the callback
|
||||||
// out doesn't make this test fail.
|
// out doesn't make this test fail.
|
||||||
testWidgets(
|
testWidgets(
|
||||||
"indent AUTO line under AUTO line changing the second line calculated direction",
|
"indent AUTO line under AUTO line changing the second line calculated direction",
|
||||||
(tester) async {
|
(tester) async {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue