Skip to content

Commit

Permalink
feat: paste plaintext shortcut (#338)
Browse files Browse the repository at this point in the history
* feat: shortcut for pasting plaintext

* test: paste without formatting

* chore: use text directly

* refactor: add cut command
  • Loading branch information
MayurSMahajan authored Aug 2, 2023
1 parent c3f62b3 commit 2b517b2
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 2 deletions.
4 changes: 2 additions & 2 deletions lib/src/editor/block_component/standard_block_components.dart
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,6 @@ final List<CommandShortcutEvent> standardCommandShortcutEvents = [

// copy paste and cut
copyCommand,
pasteCommand,
cutCommand
...pasteCommands,
cutCommand,
];
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';

final List<CommandShortcutEvent> pasteCommands = [
pasteCommand,
pasteTextWithoutFormattingCommand,
];

/// Paste.
///
/// - support
Expand All @@ -14,6 +19,52 @@ final CommandShortcutEvent pasteCommand = CommandShortcutEvent(
handler: _pasteCommandHandler,
);

final CommandShortcutEvent pasteTextWithoutFormattingCommand =
CommandShortcutEvent(
key: 'paste the content',
command: 'ctrl+shift+v',
macOSCommand: 'cmd+shift+v',
handler: _pasteTextWithoutFormattingCommandHandler,
);

CommandShortcutEventHandler _pasteTextWithoutFormattingCommandHandler =
(editorState) {
if (PlatformExtension.isMobile) {
assert(
false,
'pasteTextWithoutFormattingCommand is not supported on mobile platform.',
);
return KeyEventResult.ignored;
}

var selection = editorState.selection;
if (selection == null) {
return KeyEventResult.ignored;
}

// delete the selection first.
if (!selection.isCollapsed) {
editorState.deleteSelection(selection);
}

// fetch selection again.
selection = editorState.selection;
if (selection == null) {
return KeyEventResult.skipRemainingHandlers;
}
assert(selection.isCollapsed);

() async {
final data = await AppFlowyClipboard.getData();
final text = data.text;
if (text != null && text.isNotEmpty) {
handlePastePlainText(editorState, text);
}
}();

return KeyEventResult.handled;
};

CommandShortcutEventHandler _pasteCommandHandler = (editorState) {
if (PlatformExtension.isMobile) {
assert(false, 'pasteCommand is not supported on mobile platform.');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import 'dart:io';

import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';

import '../../../infra/testable_editor.dart';

const text = 'Welcome to AppFlowy Editor 🔥!';

void main() async {
setUp(() {
TestWidgetsFlutterBinding.ensureInitialized();
});

group('paste_command_test.dart paste_plaintext', () {
testWidgets('works with formatted text', (tester) async {
final editor = tester.editor..addParagraph(initialText: text);
await editor.startTesting();

await _applyFormatting(
editor,
BuiltInAttributeKey.underline,
LogicalKeyboardKey.keyU,
);

await _applyFormatting(
editor,
BuiltInAttributeKey.italic,
LogicalKeyboardKey.keyI,
);

await _applyFormatting(
editor,
BuiltInAttributeKey.bold,
LogicalKeyboardKey.keyB,
);
await tester.pumpAndSettle();

final selection = Selection.single(
path: [0],
startOffset: 0,
endOffset: 7,
);

await editor.updateSelection(selection);
await editor.pressKey(
key: LogicalKeyboardKey.keyC,
isMetaPressed: Platform.isMacOS,
isControlPressed: Platform.isWindows || Platform.isLinux,
);

await editor.updateSelection(selection);

await editor.pressKey(
key: LogicalKeyboardKey.keyV,
isShiftPressed: true,
isMetaPressed: Platform.isMacOS,
isControlPressed: Platform.isWindows || Platform.isLinux,
);

await editor.updateSelection(selection);
final node = editor.nodeAtPath([0]);

_checkSelectionNotFormatted(
node!,
selection,
BuiltInAttributeKey.bold,
);

_checkSelectionNotFormatted(
node,
selection,
BuiltInAttributeKey.underline,
);

_checkSelectionNotFormatted(
node,
selection,
BuiltInAttributeKey.italic,
);

await editor.dispose();
});
});
}

Future<void> _applyFormatting(
TestableEditor editor,
String matchStyle,
LogicalKeyboardKey key,
) async {
final selection = Selection.single(
path: [0],
startOffset: 0,
endOffset: 7,
);
await editor.updateSelection(selection);
await editor.pressKey(
key: key,
isMetaPressed: Platform.isMacOS,
isControlPressed: Platform.isWindows || Platform.isLinux,
);
final node = editor.nodeAtPath([0]);

expect(
node!.allSatisfyInSelection(selection, (delta) {
return delta.whereType<TextInsert>().every(
(el) => el.attributes?[matchStyle] == true,
);
}),
true,
);
}

void _checkSelectionNotFormatted(
Node node,
Selection selection,
String matchStyle,
) {
expect(
node.allSatisfyInSelection(selection, (delta) {
return delta.whereType<TextInsert>().every(
(el) => el.attributes?[matchStyle] != true,
);
}),
true,
);
}

0 comments on commit 2b517b2

Please sign in to comment.