feat: implement logic that pressing enter key without shift key in text nodes

This commit is contained in:
Lucas.Xu 2022-08-08 23:18:07 +08:00
parent 364a8e6ad2
commit af2f6a03d5
3 changed files with 107 additions and 96 deletions

View File

@ -13,7 +13,7 @@ import 'package:flowy_editor/service/internal_key_event_handlers/arrow_keys_hand
import 'package:flowy_editor/service/internal_key_event_handlers/copy_paste_handler.dart';
import 'package:flowy_editor/service/internal_key_event_handlers/delete_nodes_handler.dart';
import 'package:flowy_editor/service/internal_key_event_handlers/delete_text_handler.dart';
import 'package:flowy_editor/service/internal_key_event_handlers/enter_in_edge_of_text_node_handler.dart';
import 'package:flowy_editor/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart';
import 'package:flowy_editor/service/internal_key_event_handlers/slash_handler.dart';
import 'package:flowy_editor/service/internal_key_event_handlers/update_text_style_by_command_x_handler.dart';
import 'package:flowy_editor/service/internal_key_event_handlers/whitespace_handler.dart';
@ -39,7 +39,7 @@ List<FlowyKeyEventHandler> defaultKeyEventHandler = [
flowyDeleteNodesHandler,
arrowKeysHandler,
copyPasteKeysHandler,
enterInEdgeOfTextNodeHandler,
enterWithoutShiftInTextNodesHandler,
updateTextStyleByCommandXHandler,
whiteSpaceHandler,
];

View File

@ -1,94 +0,0 @@
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flowy_editor/document/node.dart';
import 'package:flowy_editor/document/position.dart';
import 'package:flowy_editor/document/selection.dart';
import 'package:flowy_editor/document/text_delta.dart';
import 'package:flowy_editor/extensions/node_extensions.dart';
import 'package:flowy_editor/extensions/path_extensions.dart';
import 'package:flowy_editor/operation/transaction_builder.dart';
import 'package:flowy_editor/render/rich_text/rich_text_style.dart';
import 'package:flowy_editor/service/keyboard_service.dart';
FlowyKeyEventHandler enterInEdgeOfTextNodeHandler = (editorState, event) {
if (event.logicalKey != LogicalKeyboardKey.enter) {
return KeyEventResult.ignored;
}
final nodes = editorState.service.selectionService.currentSelectedNodes;
final selection = editorState.service.selectionService.currentSelection.value;
if (selection == null ||
nodes.length != 1 ||
nodes.first is! TextNode ||
!selection.isCollapsed) {
return KeyEventResult.ignored;
}
final textNode = nodes.first as TextNode;
if (textNode.selectable!.end() == selection.end) {
if (textNode.subtype != null && textNode.delta.length == 0) {
TransactionBuilder(editorState)
..deleteNode(textNode)
..insertNode(
textNode.path,
textNode.copyWith(
children: LinkedList(),
delta: Delta([TextInsert('')]),
attributes: {},
),
)
..afterSelection = Selection.collapsed(
Position(
path: textNode.path,
offset: 0,
),
)
..commit();
} else {
final needCopyAttributes = StyleKey.globalStyleKeys
.where((key) => key != StyleKey.heading)
.contains(textNode.subtype);
TransactionBuilder(editorState)
..insertNode(
textNode.path.next,
textNode.copyWith(
children: LinkedList(),
delta: Delta([TextInsert('')]),
attributes: needCopyAttributes ? textNode.attributes : {},
),
)
..afterSelection = Selection.collapsed(
Position(
path: textNode.path.next,
offset: 0,
),
)
..commit();
}
return KeyEventResult.handled;
} else if (textNode.selectable!.start() == selection.start) {
TransactionBuilder(editorState)
..insertNode(
textNode.path,
textNode.copyWith(
children: LinkedList(),
delta: Delta([TextInsert('')]),
attributes: {},
),
)
..afterSelection = Selection.collapsed(
Position(
path: textNode.path.next,
offset: 0,
),
)
..commit();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
};

View File

@ -0,0 +1,105 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flowy_editor/document/node.dart';
import 'package:flowy_editor/document/position.dart';
import 'package:flowy_editor/document/selection.dart';
import 'package:flowy_editor/extensions/path_extensions.dart';
import 'package:flowy_editor/operation/transaction_builder.dart';
import 'package:flowy_editor/render/rich_text/rich_text_style.dart';
import 'package:flowy_editor/service/keyboard_service.dart';
/// Handle some cases where enter is pressed and shift is not pressed.
///
/// 1. Multiple selection and the selected nodes are [TextNode]
/// 1.1 delete the nodes expect for the first and the last,
/// and delete the text in the first and the last node by case.
/// 2. Single selection and the selected node is [TextNode]
/// 2.1 split the node into two nodes with style
/// 2.2 or insert a empty text node before.
FlowyKeyEventHandler enterWithoutShiftHandler = (editorState, event) {
if (event.logicalKey != LogicalKeyboardKey.enter || event.isShiftPressed) {
return KeyEventResult.ignored;
}
final nodes = editorState.service.selectionService.currentSelectedNodes;
final textNodes = nodes.whereType<TextNode>().toList(growable: false);
final selection = editorState.service.selectionService.currentSelection.value;
if (selection == null || nodes.length != textNodes.length) {
return KeyEventResult.ignored;
}
// Multiple selection
if (!selection.isSingle) {
final length = textNodes.length;
final List<TextNode> subTextNodes =
length >= 3 ? textNodes.sublist(1, textNodes.length - 2) : [];
final afterSelection = Selection.collapsed(
Position(path: textNodes.first.path.next, offset: 0),
);
TransactionBuilder(editorState)
..deleteText(
textNodes.first,
selection.start.offset,
textNodes.first.toRawString().length,
)
..deleteNodes(subTextNodes)
..deleteText(
textNodes.last,
0,
selection.end.offset,
)
..afterSelection = afterSelection
..commit();
return KeyEventResult.handled;
}
// Single selection and the selected node is [TextNode]
if (textNodes.length != 1) {
return KeyEventResult.ignored;
}
final textNode = textNodes.first;
// If selection is collapsed and position.start.offset == 0,
// insert a empty text node before.
if (selection.isCollapsed && selection.start.offset == 0) {
final afterSelection = Selection.collapsed(
Position(path: textNode.path.next, offset: 0),
);
TransactionBuilder(editorState)
..insertNode(
textNode.path,
TextNode.empty(),
)
..afterSelection = afterSelection
..commit();
return KeyEventResult.handled;
}
// Otherwise,
// split the node into two nodes with style
final needCopyAttributes = StyleKey.globalStyleKeys
.where((key) => key != StyleKey.heading)
.contains(textNode.subtype);
final afterSelection = Selection.collapsed(
Position(path: textNode.path.next, offset: 0),
);
TransactionBuilder(editorState)
..deleteText(
textNode,
selection.start.offset,
textNode.toRawString().length,
)
..insertNode(
textNode.path.next,
textNode.copyWith(
attributes: needCopyAttributes ? textNode.attributes : {},
delta: textNode.delta.slice(selection.end.offset),
),
)
..afterSelection = afterSelection
..commit();
return KeyEventResult.handled;
};