|
@@ -1,4 +1,5 @@
|
|
|
import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart';
|
|
|
+import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart';
|
|
|
import 'package:flutter/material.dart';
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
|
@@ -25,10 +26,11 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) {
|
|
|
nodes = selection.isBackward ? nodes : nodes.reversed.toList(growable: false);
|
|
|
selection = selection.isBackward ? selection : selection.reversed;
|
|
|
final textNodes = nodes.whereType<TextNode>().toList();
|
|
|
- final nonTextNodes =
|
|
|
+ final List<Node> nonTextNodes =
|
|
|
nodes.where((node) => node is! TextNode).toList(growable: false);
|
|
|
|
|
|
final transactionBuilder = TransactionBuilder(editorState);
|
|
|
+ List<int>? cancelNumberListPath;
|
|
|
|
|
|
if (nonTextNodes.isNotEmpty) {
|
|
|
transactionBuilder.deleteNodes(nonTextNodes);
|
|
@@ -40,6 +42,9 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) {
|
|
|
if (index < 0 && selection.isCollapsed) {
|
|
|
// 1. style
|
|
|
if (textNode.subtype != null) {
|
|
|
+ if (textNode.subtype == StyleKey.numberList) {
|
|
|
+ cancelNumberListPath = textNode.path;
|
|
|
+ }
|
|
|
transactionBuilder
|
|
|
..updateNode(textNode, {
|
|
|
StyleKey.subtype: null,
|
|
@@ -54,23 +59,13 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) {
|
|
|
} else {
|
|
|
// 2. non-style
|
|
|
// find previous text node.
|
|
|
- var previous = textNode.previous;
|
|
|
- while (previous != null) {
|
|
|
- if (previous is TextNode) {
|
|
|
- transactionBuilder
|
|
|
- ..mergeText(previous, textNode)
|
|
|
- ..deleteNode(textNode)
|
|
|
- ..afterSelection = Selection.collapsed(
|
|
|
- Position(
|
|
|
- path: previous.path,
|
|
|
- offset: previous.toRawString().length,
|
|
|
- ),
|
|
|
- );
|
|
|
- break;
|
|
|
- } else {
|
|
|
- previous = previous.previous;
|
|
|
- }
|
|
|
- }
|
|
|
+ return _backDeleteToPreviousTextNode(
|
|
|
+ editorState,
|
|
|
+ textNode,
|
|
|
+ transactionBuilder,
|
|
|
+ nonTextNodes,
|
|
|
+ selection,
|
|
|
+ );
|
|
|
}
|
|
|
} else {
|
|
|
if (selection.isCollapsed) {
|
|
@@ -88,9 +83,22 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) {
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
- if (textNodes.isNotEmpty) {
|
|
|
- _deleteTextNodes(transactionBuilder, textNodes, selection);
|
|
|
+ if (textNodes.isEmpty) {
|
|
|
+ return KeyEventResult.handled;
|
|
|
+ }
|
|
|
+ final startPosition = selection.start;
|
|
|
+ final nodeAtStart = editorState.document.nodeAtPath(startPosition.path)!;
|
|
|
+ _deleteTextNodes(transactionBuilder, textNodes, selection);
|
|
|
+ transactionBuilder.commit();
|
|
|
+
|
|
|
+ if (nodeAtStart is TextNode && nodeAtStart.subtype == StyleKey.numberList) {
|
|
|
+ makeFollowingNodesIncremental(
|
|
|
+ editorState,
|
|
|
+ startPosition.path,
|
|
|
+ transactionBuilder.afterSelection!,
|
|
|
+ );
|
|
|
}
|
|
|
+ return KeyEventResult.handled;
|
|
|
}
|
|
|
|
|
|
if (transactionBuilder.operations.isNotEmpty) {
|
|
@@ -100,6 +108,59 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) {
|
|
|
transactionBuilder.commit();
|
|
|
}
|
|
|
|
|
|
+ if (cancelNumberListPath != null) {
|
|
|
+ makeFollowingNodesIncremental(
|
|
|
+ editorState,
|
|
|
+ cancelNumberListPath,
|
|
|
+ Selection.collapsed(selection.start),
|
|
|
+ beginNum: 0,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return KeyEventResult.handled;
|
|
|
+}
|
|
|
+
|
|
|
+KeyEventResult _backDeleteToPreviousTextNode(
|
|
|
+ EditorState editorState,
|
|
|
+ TextNode textNode,
|
|
|
+ TransactionBuilder transactionBuilder,
|
|
|
+ List<Node> nonTextNodes,
|
|
|
+ Selection selection) {
|
|
|
+ var previous = textNode.previous;
|
|
|
+ bool prevIsNumberList = false;
|
|
|
+ while (previous != null) {
|
|
|
+ if (previous is TextNode) {
|
|
|
+ if (previous.subtype == StyleKey.numberList) {
|
|
|
+ prevIsNumberList = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ transactionBuilder
|
|
|
+ ..mergeText(previous, textNode)
|
|
|
+ ..deleteNode(textNode)
|
|
|
+ ..afterSelection = Selection.collapsed(
|
|
|
+ Position(
|
|
|
+ path: previous.path,
|
|
|
+ offset: previous.toRawString().length,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ previous = previous.previous;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (transactionBuilder.operations.isNotEmpty) {
|
|
|
+ if (nonTextNodes.isNotEmpty) {
|
|
|
+ transactionBuilder.afterSelection = Selection.collapsed(selection.start);
|
|
|
+ }
|
|
|
+ transactionBuilder.commit();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (prevIsNumberList) {
|
|
|
+ makeFollowingNodesIncremental(
|
|
|
+ editorState, previous!.path, transactionBuilder.afterSelection!);
|
|
|
+ }
|
|
|
+
|
|
|
return KeyEventResult.handled;
|
|
|
}
|
|
|
|
|
@@ -120,37 +181,65 @@ KeyEventResult _handleDelete(EditorState editorState, RawKeyEvent event) {
|
|
|
final transactionBuilder = TransactionBuilder(editorState);
|
|
|
if (textNodes.length == 1) {
|
|
|
final textNode = textNodes.first;
|
|
|
+ // The cursor is at the end of the line,
|
|
|
+ // merge next line into this line.
|
|
|
if (selection.start.offset >= textNode.delta.length) {
|
|
|
- final nextNode = textNode.next;
|
|
|
- if (nextNode == null) {
|
|
|
- return KeyEventResult.ignored;
|
|
|
- }
|
|
|
- if (nextNode is TextNode) {
|
|
|
- transactionBuilder.mergeText(textNode, nextNode);
|
|
|
- }
|
|
|
- transactionBuilder.deleteNode(nextNode);
|
|
|
+ return _mergeNextLineIntoThisLine(
|
|
|
+ editorState,
|
|
|
+ textNode,
|
|
|
+ transactionBuilder,
|
|
|
+ selection,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ final index = textNode.delta.nextRunePosition(selection.start.offset);
|
|
|
+ if (selection.isCollapsed) {
|
|
|
+ transactionBuilder.deleteText(
|
|
|
+ textNode,
|
|
|
+ selection.start.offset,
|
|
|
+ index - selection.start.offset,
|
|
|
+ );
|
|
|
} else {
|
|
|
- final index = textNode.delta.nextRunePosition(selection.start.offset);
|
|
|
- if (selection.isCollapsed) {
|
|
|
- transactionBuilder.deleteText(
|
|
|
- textNode,
|
|
|
- selection.start.offset,
|
|
|
- index - selection.start.offset,
|
|
|
- );
|
|
|
- } else {
|
|
|
- transactionBuilder.deleteText(
|
|
|
- textNode,
|
|
|
- selection.start.offset,
|
|
|
- selection.end.offset - selection.start.offset,
|
|
|
- );
|
|
|
- }
|
|
|
+ transactionBuilder.deleteText(
|
|
|
+ textNode,
|
|
|
+ selection.start.offset,
|
|
|
+ selection.end.offset - selection.start.offset,
|
|
|
+ );
|
|
|
}
|
|
|
+ transactionBuilder.commit();
|
|
|
} else {
|
|
|
+ final startPosition = selection.start;
|
|
|
+ final nodeAtStart = editorState.document.nodeAtPath(startPosition.path)!;
|
|
|
_deleteTextNodes(transactionBuilder, textNodes, selection);
|
|
|
+ transactionBuilder.commit();
|
|
|
+
|
|
|
+ if (nodeAtStart is TextNode && nodeAtStart.subtype == StyleKey.numberList) {
|
|
|
+ makeFollowingNodesIncremental(
|
|
|
+ editorState, startPosition.path, transactionBuilder.afterSelection!);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ return KeyEventResult.handled;
|
|
|
+}
|
|
|
+
|
|
|
+KeyEventResult _mergeNextLineIntoThisLine(
|
|
|
+ EditorState editorState,
|
|
|
+ TextNode textNode,
|
|
|
+ TransactionBuilder transactionBuilder,
|
|
|
+ Selection selection) {
|
|
|
+ final nextNode = textNode.next;
|
|
|
+ if (nextNode == null) {
|
|
|
+ return KeyEventResult.ignored;
|
|
|
+ }
|
|
|
+ if (nextNode is TextNode) {
|
|
|
+ transactionBuilder.mergeText(textNode, nextNode);
|
|
|
+ }
|
|
|
+ transactionBuilder.deleteNode(nextNode);
|
|
|
transactionBuilder.commit();
|
|
|
|
|
|
+ if (textNode.subtype == StyleKey.numberList) {
|
|
|
+ makeFollowingNodesIncremental(editorState, textNode.path, selection);
|
|
|
+ }
|
|
|
+
|
|
|
return KeyEventResult.handled;
|
|
|
}
|
|
|
|