feat: open emoji selection menu with keyboard short (#4133)

* feat: Implement emoji shortcut event (#2038)

* test: emoji shortcut integration test (#2038)

* test: Fixed testcase for emoji_shortcut_test (#2038)

* fix: remove _showEmojiPickerMenu (#4133)

* fix: change local variables with underscore

---------

Co-authored-by: jazima <jazim.a.29@gmail.com>
This commit is contained in:
Hassam Sulehria 2024-02-16 12:47:23 -05:00 committed by GitHub
parent d690ca2751
commit e6aa57275a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 137 additions and 6 deletions

View File

@ -0,0 +1,41 @@
import 'dart:io';
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/editor.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/keyboard.dart';
import 'util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// May be better to move this to an existing test but unsure what it fits with
group('Keyboard shortcuts related to emojis', () {
testWidgets('cmd/ctrl+alt+e shortcut opens the emoji picker',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
final Finder editor = find.byType(AppFlowyEditor);
await tester.tap(editor);
await tester.pumpAndSettle();
expect(find.byType(EmojiSelectionMenu), findsNothing);
await FlowyTestKeyboard.simulateKeyDownEvent(
[
Platform.isMacOS
? LogicalKeyboardKey.meta
: LogicalKeyboardKey.control,
LogicalKeyboardKey.alt,
LogicalKeyboardKey.keyE,
],
tester: tester,
);
expect(find.byType(EmojiSelectionMenu), findsOneWidget);
});
});
}

View File

@ -23,6 +23,7 @@ import 'share_markdown_test.dart' as share_markdown_test;
import 'sidebar/sidebar_test_runner.dart' as sidebar_test_runner;
import 'switch_folder_test.dart' as switch_folder_test;
import 'tabs_test.dart' as tabs_test;
import 'emoji_shortcut_test.dart' as emoji_shortcut_test;
// import 'auth/supabase_auth_test.dart' as supabase_auth_test_runner;
/// The main task runner for all integration tests in AppFlowy.
@ -69,6 +70,7 @@ Future<void> main() async {
// Others
hotkeys_test.main();
emoji_shortcut_test.main();
// Appearance integration test
appearance_test_runner.main();

View File

@ -32,6 +32,7 @@ final List<CommandShortcutEvent> commandShortcutEvents = [
customCutCommand,
...customTextAlignCommands,
...standardCommandShortcutEvents,
emojiShortcutEvent,
];
final List<CommandShortcutEvent> defaultCommandShortcutEvents = [
@ -93,6 +94,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
customCutCommand,
...customTextAlignCommands,
...standardCommandShortcutEvents,
emojiShortcutEvent,
..._buildFindAndReplaceCommands(),
];

View File

@ -17,10 +17,12 @@ SelectionMenuItem emojiMenuItem = SelectionMenuItem(
keywords: ['emoji'],
handler: (editorState, menuService, context) {
final container = Overlay.of(context);
menuService.dismiss();
showEmojiPickerMenu(
container,
editorState,
menuService,
menuService.alignment,
menuService.offset,
);
},
);
@ -28,12 +30,9 @@ SelectionMenuItem emojiMenuItem = SelectionMenuItem(
void showEmojiPickerMenu(
OverlayState container,
EditorState editorState,
SelectionMenuService menuService,
Alignment alignment,
Offset offset,
) {
menuService.dismiss();
final alignment = menuService.alignment;
final offset = menuService.offset;
final top = alignment == Alignment.topLeft ? offset.dy : null;
final bottom = alignment == Alignment.bottomLeft ? offset.dy : null;

View File

@ -1,4 +1,5 @@
export 'emoji_menu_item.dart';
export 'emoji_shortcut_event.dart';
export 'src/emji_picker_config.dart';
export 'src/emoji_picker.dart';
export 'src/emoji_picker_builder.dart';

View File

@ -0,0 +1,86 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
final CommandShortcutEvent emojiShortcutEvent = CommandShortcutEvent(
key: 'show emoji picker',
command: 'ctrl+alt+e',
macOSCommand: 'cmd+alt+e',
handler: _emojiShortcutHandler,
);
CommandShortcutEventHandler _emojiShortcutHandler = (editorState) {
final selection = editorState.selection;
if (selection == null) {
return KeyEventResult.ignored;
}
final context = editorState.getNodeAtPath(selection.start.path)?.context;
if (context == null) {
return KeyEventResult.ignored;
}
final container = Overlay.of(context);
Alignment alignment = Alignment.topLeft;
Offset offset = Offset.zero;
final selectionService = editorState.service.selectionService;
final selectionRects = selectionService.selectionRects;
if (selectionRects.isEmpty) {
return KeyEventResult.ignored;
}
final rect = selectionRects.first;
// Calculate the offset and alignment
// Don't like these values being hardcoded but unsure how to grab the
// values dynamically to match the /emoji command.
const menuHeight = 200.0;
const menuOffset = Offset(10, 10); // Tried (0, 10) but that looked off
final editorOffset =
editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero;
final editorHeight = editorState.renderBox!.size.height;
final editorWidth = editorState.renderBox!.size.width;
// show below default
alignment = Alignment.topLeft;
final bottomRight = rect.bottomRight;
final topRight = rect.topRight;
var newOffset = bottomRight + menuOffset;
offset = Offset(
newOffset.dx,
newOffset.dy,
);
// show above
if (newOffset.dy + menuHeight >= editorOffset.dy + editorHeight) {
offset = topRight - menuOffset;
alignment = Alignment.bottomLeft;
offset = Offset(
newOffset.dx,
MediaQuery.of(context).size.height - newOffset.dy,
);
}
// show on left
if (offset.dx - editorOffset.dx > editorWidth / 2) {
alignment = _alignment == Alignment.topLeft
? Alignment.topRight
: Alignment.bottomRight;
offset = Offset(
editorWidth - offset.dx + editorOffset.dx,
offset.dy,
);
}
showEmojiPickerMenu(
container,
editorState,
alignment,
offset,
);
return KeyEventResult.handled;
};