From e74f5e84dc0db1293f4a478988a16e589798d0a1 Mon Sep 17 00:00:00 2001 From: Vincent Chan Date: Wed, 27 Jul 2022 15:46:43 +0800 Subject: [PATCH] feat: handle arrow keys --- .../example/lib/plugin/text_node_widget.dart | 2 +- .../flowy_editor/lib/editor_state.dart | 19 +++++- .../arrow_keys_handler.dart | 59 +++++++++++++++++++ .../lib/service/selection_service.dart | 21 +++---- 4 files changed, 88 insertions(+), 13 deletions(-) diff --git a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart index a67ebcd2ad..53c33cd295 100644 --- a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart +++ b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart @@ -126,7 +126,7 @@ class __TextNodeWidgetState extends State<_TextNodeWidget> textCapitalization: TextCapitalization.sentences, ), ); - editorState.cursorSelection = _localSelectionToGlobal(node, selection); + editorState.updateCursorSelection(_localSelectionToGlobal(node, selection)); _textInputConnection ?..show() ..setEditingState( diff --git a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart b/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart index cd503843c2..a69b053c90 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart @@ -31,7 +31,22 @@ class EditorState { final service = FlowyService(); final UndoManager undoManager = UndoManager(); - Selection? cursorSelection; + Selection? _cursorSelection; + + Selection? get cursorSelection { + return _cursorSelection; + } + + /// add the set reason in the future, don't use setter + updateCursorSelection(Selection? cursorSelection) { + // broadcast to other users here + if (cursorSelection == null) { + service.selectionService.clearSelection(); + } else { + service.selectionService.updateSelection(cursorSelection); + } + _cursorSelection = cursorSelection; + } Timer? _debouncedSealHistoryItemTimer; @@ -58,7 +73,7 @@ class EditorState { for (final op in transaction.operations) { _applyOperation(op); } - cursorSelection = transaction.afterSelection; + updateCursorSelection(transaction.afterSelection); if (options.recordUndo) { final undoItem = undoManager.getUndoHistoryItem(); diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/arrow_keys_handler.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/arrow_keys_handler.dart index 95496db2ea..bdb473042d 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/arrow_keys_handler.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/arrow_keys_handler.dart @@ -1,7 +1,17 @@ +import 'package:flowy_editor/document/node.dart'; +import 'package:flowy_editor/document/position.dart'; import 'package:flowy_editor/service/keyboard_service.dart'; +import 'package:flowy_editor/document/selection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +int _endOffsetOfNode(Node node) { + if (node is TextNode) { + return node.delta.length; + } + return 0; +} + FlowyKeyEventHandler arrowKeysHandler = (editorState, event) { if (event.logicalKey != LogicalKeyboardKey.arrowUp && event.logicalKey != LogicalKeyboardKey.arrowDown && @@ -10,5 +20,54 @@ FlowyKeyEventHandler arrowKeysHandler = (editorState, event) { return KeyEventResult.ignored; } + final currentSelection = editorState.cursorSelection; + if (currentSelection == null) { + return KeyEventResult.ignored; + } + + if (event.logicalKey == LogicalKeyboardKey.arrowLeft) { + // turn left + if (currentSelection.isCollapsed) { + final end = currentSelection.end; + final offset = end.offset; + if (offset == 0) { + final node = editorState.document.nodeAtPath(end.path)!; + final prevNode = node.previous; + if (prevNode != null) { + editorState.updateCursorSelection(Selection.collapsed(Position( + path: prevNode.path, offset: _endOffsetOfNode(prevNode)))); + } + return KeyEventResult.handled; + } + editorState.updateCursorSelection( + Selection.collapsed(Position(path: end.path, offset: offset - 1))); + } else { + editorState + .updateCursorSelection(currentSelection.collapse(atStart: true)); + } + return KeyEventResult.handled; + } else if (event.logicalKey == LogicalKeyboardKey.arrowRight) { + if (currentSelection.isCollapsed) { + final end = currentSelection.end; + final offset = end.offset; + final node = editorState.document.nodeAtPath(end.path)!; + final lengthOfNode = _endOffsetOfNode(node); + if (offset >= lengthOfNode) { + final nextNode = node.next; + if (nextNode != null) { + editorState.updateCursorSelection( + Selection.collapsed(Position(path: nextNode.path, offset: 0))); + } + return KeyEventResult.handled; + } + + editorState.updateCursorSelection( + Selection.collapsed(Position(path: end.path, offset: offset + 1))); + } else { + editorState.updateCursorSelection(currentSelection.collapse()); + } + return KeyEventResult.handled; + } + return KeyEventResult.ignored; }; diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart index 19604b0227..e3c262a360 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart @@ -1,4 +1,3 @@ -import 'package:flowy_editor/document/path.dart'; import 'package:flowy_editor/document/node.dart'; import 'package:flowy_editor/document/position.dart'; import 'package:flowy_editor/document/selection.dart'; @@ -49,7 +48,7 @@ mixin FlowySelectionService on State { /// [start] is the offset under the global coordinate system. Node? computeNodeInOffset(Node node, Offset offset); - /// Return the [Node]s in multiple selection. Emtpy list would be returned + /// Return the [Node]s in multiple selection. Empty list would be returned /// if no nodes are in range. /// /// [start] is the offset under the global coordinate system. @@ -136,8 +135,8 @@ class _FlowySelectionState extends State TapGestureRecognizer: GestureRecognizerFactoryWithHandlers( () => TapGestureRecognizer(), - (recongizer) { - recongizer.onTapDown = _onTapDown; + (recognizer) { + recognizer.onTapDown = _onTapDown; }, ) }, @@ -167,9 +166,9 @@ class _FlowySelectionState extends State if (end != null) { return computeNodesInRange(editorState.document.root, start, end); } else { - final reuslt = computeNodeInOffset(editorState.document.root, start); - if (reuslt != null) { - return [reuslt]; + final result = computeNodeInOffset(editorState.document.root, start); + if (result != null) { + return [result]; } } return []; @@ -253,8 +252,10 @@ class _FlowySelectionState extends State if (selectable != null) { final position = selectable.getPositionInOffset(tapOffset!); final selection = Selection.collapsed(position); - updateSelection(selection); + editorState.updateCursorSelection(selection); } + } else { + editorState.updateCursorSelection(null); } } @@ -283,7 +284,7 @@ class _FlowySelectionState extends State final selection = Selection( start: isDownward ? start : end, end: isDownward ? end : start); debugPrint('[_onPanUpdate] $selection'); - updateSelection(selection); + editorState.updateCursorSelection(selection); } } @@ -302,7 +303,7 @@ class _FlowySelectionState extends State _cursorOverlays ..forEach((overlay) => overlay.remove()) ..clear(); - // clear floating shortcusts + // clear floating shortcuts editorState.service.floatingShortcutServiceKey.currentState ?.unwrapOrNull() ?.hide();