feat: enable toggling and canceling of the formatting with shortcuts (#528)

This commit is contained in:
Lucas.Xu 2023-10-11 13:10:37 +08:00 • committed by GitHub
commit b17bddceb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 153 additions and 16 deletions

View file

@ -195,6 +195,7 @@ extension TextTransaction on Transaction {
int index,
String text, {
Attributes? attributes,
Attributes? toggledAttributes,
}) {
final delta = node.delta;
if (delta == null) {
@ -207,7 +208,11 @@ extension TextTransaction on Transaction {
'The index($index) is out of range or negative.',
);
final newAttributes = attributes ?? delta.sliceAttributes(index);
final newAttributes = attributes ?? delta.sliceAttributes(index) ?? {};
if (toggledAttributes != null) {
newAttributes.addAll(toggledAttributes);
}
final insert = Delta()
..retain(index)
@ -316,6 +321,7 @@ extension TextTransaction on Transaction {
return;
}
afterSelection = beforeSelection;
final format = Delta()
..retain(index)
..retain(length, attributes: attributes);

View file

@ -19,4 +19,13 @@ class AppFlowyRichTextKeys {
textColor,
highlightColor,
];
// The values supported toggled even if the selection is collapsed.
static List<String> supportToggled = [
bold,
italic,
underline,
strikethrough,
code,
];
}

View file

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:appflowy_editor/appflowy_editor.dart';
extension TextTransforms on EditorState {
@ -185,18 +187,37 @@ extension TextTransforms on EditorState {
if (selection == null) {
return;
}
final nodes = getNodesInSelection(selection);
final isHighlight = nodes.allSatisfyInSelection(selection, (delta) {
return delta.everyAttributes(
(attributes) => attributes[key] == true,
if (selection.isCollapsed) {
// get the attributes from the previous one character.
selection = selection.copyWith(
start: selection.start.copyWith(
offset: max(
selection.startIndex - 1,
0,
),
),
);
});
await formatDelta(
selection,
{
key: !isHighlight,
},
);
final toggled = nodes.allSatisfyInSelection(selection, (delta) {
return delta.everyAttributes(
(attributes) => attributes[key] == true,
);
});
toggledStyle[key] = !toggled;
} else {
final isHighlight = nodes.allSatisfyInSelection(selection, (delta) {
return delta.everyAttributes(
(attributes) => attributes[key] == true,
);
});
await formatDelta(
selection,
{
key: !isHighlight,
},
);
}
}
/// format the node at the given selection.

View file

@ -1,5 +1,6 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/ime/character_shortcut_event_helper.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
Future<void> onInsert(
@ -42,11 +43,21 @@ Future<void> onInsert(
}
assert(node.delta != null);
if (kDebugMode) {
// verify the toggled keys are supported.
assert(
editorState.toggledStyle.keys.every(
(element) => AppFlowyRichTextKeys.supportToggled.contains(element),
),
);
}
final transaction = editorState.transaction
..insertText(
node,
selection.startIndex,
insertion.textInserted,
toggledAttributes: editorState.toggledStyle,
);
return editorState.apply(transaction);
}

View file

@ -24,35 +24,50 @@ final CommandShortcutEvent toggleBoldCommand = CommandShortcutEvent(
key: 'toggle bold',
command: 'ctrl+b',
macOSCommand: 'cmd+b',
handler: (editorState) => _toggleAttribute(editorState, 'bold'),
handler: (editorState) => _toggleAttribute(
editorState,
AppFlowyRichTextKeys.bold,
),
);
final CommandShortcutEvent toggleItalicCommand = CommandShortcutEvent(
key: 'toggle italic',
command: 'ctrl+i',
macOSCommand: 'cmd+i',
handler: (editorState) => _toggleAttribute(editorState, 'italic'),
handler: (editorState) => _toggleAttribute(
editorState,
AppFlowyRichTextKeys.italic,
),
);
final CommandShortcutEvent toggleUnderlineCommand = CommandShortcutEvent(
key: 'toggle underline',
command: 'ctrl+u',
macOSCommand: 'cmd+u',
handler: (editorState) => _toggleAttribute(editorState, 'underline'),
handler: (editorState) => _toggleAttribute(
editorState,
AppFlowyRichTextKeys.underline,
),
);
final CommandShortcutEvent toggleStrikethroughCommand = CommandShortcutEvent(
key: 'toggle strikethrough',
command: 'ctrl+shift+s',
macOSCommand: 'cmd+shift+s',
handler: (editorState) => _toggleAttribute(editorState, 'strikethrough'),
handler: (editorState) => _toggleAttribute(
editorState,
AppFlowyRichTextKeys.strikethrough,
),
);
final CommandShortcutEvent toggleCodeCommand = CommandShortcutEvent(
key: 'toggle code',
command: 'ctrl+e',
macOSCommand: 'cmd+e',
handler: (editorState) => _toggleAttribute(editorState, 'code'),
handler: (editorState) => _toggleAttribute(
editorState,
AppFlowyRichTextKeys.code,
),
);
KeyEventResult _toggleAttribute(

View file

@ -100,6 +100,9 @@ class EditorState {
/// Sets the selection of the editor.
set selection(Selection? value) {
// clear the toggled style when the selection is changed.
toggledStyle.clear();
selectionNotifier.value = value;
}
@ -145,6 +148,13 @@ class EditorState {
sync: true,
);
/// Store the toggled format style, like bold, italic, etc.
/// All the values must be the key from [AppFlowyRichTextKeys.supportToggled].
///
/// NOTES: It only works once;
/// after the selection is changed, the toggled style will be cleared.
final toggledStyle = <String, bool>{};
final UndoManager undoManager = UndoManager();
Transaction get transaction {