mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #792 from LucasXu0/feat/mouse_cursor_style
Feat/mouse cursor style
This commit is contained in:
commit
b9de45a110
@ -183,7 +183,7 @@ class TextNode extends Node {
|
||||
super(children: children ?? LinkedList(), attributes: attributes ?? {});
|
||||
|
||||
TextNode.empty()
|
||||
: _delta = Delta([TextInsert(' ')]),
|
||||
: _delta = Delta([TextInsert('')]),
|
||||
super(
|
||||
type: 'text',
|
||||
children: LinkedList(),
|
||||
|
@ -77,7 +77,7 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
|
||||
debugPrint('[Checkbox] onTap...');
|
||||
TransactionBuilder(widget.editorState)
|
||||
..updateNode(widget.textNode, {
|
||||
'checkbox': !check,
|
||||
StyleKey.checkbox: !check,
|
||||
})
|
||||
..commit();
|
||||
},
|
||||
|
@ -35,7 +35,7 @@ class FlowyRichText extends StatefulWidget {
|
||||
this.cursorHeight,
|
||||
this.cursorWidth = 2.0,
|
||||
this.textSpanDecorator,
|
||||
this.placeholderText = 'Type \'/\' for commands',
|
||||
this.placeholderText = ' ',
|
||||
this.placeholderTextSpanDecorator,
|
||||
required this.textNode,
|
||||
required this.editorState,
|
||||
@ -138,11 +138,16 @@ class _FlowyRichTextState extends State<FlowyRichText> with Selectable {
|
||||
}
|
||||
|
||||
Widget _buildRichText(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
_buildPlaceholderText(context),
|
||||
_buildSingleRichText(context),
|
||||
],
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.text,
|
||||
child: widget.textNode.toRawString().isEmpty
|
||||
? Stack(
|
||||
children: [
|
||||
_buildPlaceholderText(context),
|
||||
_buildSingleRichText(context),
|
||||
],
|
||||
)
|
||||
: _buildSingleRichText(context),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
];
|
||||
|
@ -46,8 +46,14 @@ FlowyKeyEventHandler deleteTextHandler = (editorState, event) {
|
||||
if (textNode.previous is TextNode) {
|
||||
final previous = textNode.previous as TextNode;
|
||||
transactionBuilder
|
||||
..mergeText(previous, textNode)
|
||||
..deleteNode(textNode)
|
||||
..mergeText(previous, textNode);
|
||||
..afterSelection = Selection.collapsed(
|
||||
Position(
|
||||
path: previous.path,
|
||||
offset: previous.toRawString().length,
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
@ -0,0 +1,106 @@
|
||||
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 enterWithoutShiftInTextNodesHandler =
|
||||
(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)
|
||||
..insertNode(
|
||||
textNode.path.next,
|
||||
textNode.copyWith(
|
||||
attributes: needCopyAttributes ? textNode.attributes : {},
|
||||
delta: textNode.delta.slice(selection.end.offset),
|
||||
),
|
||||
)
|
||||
..deleteText(
|
||||
textNode,
|
||||
selection.start.offset,
|
||||
textNode.toRawString().length - selection.start.offset,
|
||||
)
|
||||
..afterSelection = afterSelection
|
||||
..commit();
|
||||
return KeyEventResult.handled;
|
||||
};
|
Loading…
Reference in New Issue
Block a user