diff --git a/lib/src/editor/block_component/divider_block_component/divider_block_component.dart b/lib/src/editor/block_component/divider_block_component/divider_block_component.dart index d61fe224f..b7982356b 100644 --- a/lib/src/editor/block_component/divider_block_component/divider_block_component.dart +++ b/lib/src/editor/block_component/divider_block_component/divider_block_component.dart @@ -124,7 +124,7 @@ class _DividerBlockComponentWidgetState @override Rect getBlockRect() { - return getCursorRectInPosition(Position.invalid()) ?? Rect.zero; + return getRectsInSelection(Selection.invalid()).first; } @override diff --git a/lib/src/editor/block_component/image_block_component/image_upload_widget.dart b/lib/src/editor/block_component/image_block_component/image_upload_widget.dart index 5b963a73c..0393b7222 100644 --- a/lib/src/editor/block_component/image_block_component/image_upload_widget.dart +++ b/lib/src/editor/block_component/image_block_component/image_upload_widget.dart @@ -1,9 +1,10 @@ import 'dart:io'; import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:file_picker/file_picker.dart' as fp; import 'package:flutter/material.dart'; + import '../../util/file_picker/file_picker_impl.dart'; -import 'package:file_picker/file_picker.dart' as fp; enum ImageFromFileStatus { notSelected, @@ -47,7 +48,7 @@ void showImageMenu( builder: (context) => UploadImageMenu( backgroundColor: menuService.style.selectionMenuBackgroundColor, headerColor: menuService.style.selectionMenuItemTextColor, - width: MediaQuery.of(context).size.width * 0.4, + width: MediaQuery.of(context).size.width * 0.3, onSubmitted: insertImage, onUpload: insertImage, ), @@ -103,8 +104,8 @@ class _UploadImageMenuState extends State { Widget build(BuildContext context) { return Container( width: widget.width, - height: 300, - padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 10.0), + height: 240, + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 5.0), decoration: BoxDecoration( color: widget.backgroundColor, boxShadow: [ @@ -123,7 +124,8 @@ class _UploadImageMenuState extends State { Align( alignment: Alignment.centerLeft, child: SizedBox( - width: 300, + width: 260, + height: 36, child: TabBar( tabs: const [ Tab(text: 'Upload Image'), @@ -207,7 +209,7 @@ class _UploadImageMenuState extends State { ) { return SizedBox( width: 170, - height: 48, + height: 36, child: TextButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(const Color(0xFF00BCF0)), @@ -296,7 +298,7 @@ class _UploadImageMenuState extends State { } }, child: Container( - height: 80, + height: 60, margin: const EdgeInsets.all(10.0), decoration: BoxDecoration( border: Border.all(color: const Color(0xff00BCF0)), diff --git a/lib/src/editor/editor_component/service/selection/desktop_selection_service.dart b/lib/src/editor/editor_component/service/selection/desktop_selection_service.dart index b774ee479..fb01189ae 100644 --- a/lib/src/editor/editor_component/service/selection/desktop_selection_service.dart +++ b/lib/src/editor/editor_component/service/selection/desktop_selection_service.dart @@ -529,6 +529,25 @@ class _DesktopSelectionServiceWidgetState void _showContextMenu(TapDownDetails details) { _clearContextMenu(); + // only shows around the selection area. + if (selectionRects.isEmpty) { + return; + } + + final isHitSelectionAreas = currentSelection.value?.isCollapsed == true || + selectionRects.any((element) { + const threshold = 20; + final scaledArea = Rect.fromCenter( + center: element.center, + width: element.width + threshold, + height: element.height + threshold, + ); + return scaledArea.contains(details.globalPosition); + }); + if (!isHitSelectionAreas) { + return; + } + // For now, only support the text node. if (!currentSelectedNodes.every((element) => element.delta != null)) { return; diff --git a/test/service/context_menu_test.dart b/test/service/context_menu_test.dart index fdfe7ac1a..06f63f2af 100644 --- a/test/service/context_menu_test.dart +++ b/test/service/context_menu_test.dart @@ -1,7 +1,7 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:appflowy_editor/appflowy_editor.dart'; import '../infra/clipboard_test.dart'; import '../new/infra/testable_editor.dart'; @@ -26,9 +26,10 @@ void main() async { }); }); group('context menu test', () { - void rightClick() { + void rightClickAt(Offset position) { GestureBinding.instance.handlePointerEvent( - const PointerDownEvent( + PointerDownEvent( + position: position, buttons: kSecondaryMouseButton, ), ); @@ -39,22 +40,26 @@ void main() async { } testWidgets('context menu test', (tester) async { - final editor = tester.editor - ..addParagraph(initialText: 'Welcome to AppFlowy'); + const text = 'Welcome to AppFlowy'; + final editor = tester.editor..addParagraph(initialText: text); await editor.startTesting(); expect(find.byType(ContextMenu), findsNothing); - rightClick(); + await editor.updateSelection( + Selection.single(path: [0], startOffset: 0, endOffset: text.length), + ); + final position = tester.getCenter(find.text(text, findRichText: true)); + rightClickAt(position); await tester.pump(); expect(find.byType(ContextMenu), findsOneWidget); await editor.dispose(); }); testWidgets('context menu cut test ', (tester) async { - final editor = tester.editor - ..addParagraph(initialText: 'Welcome to AppFlowy'); + const text = 'Welcome to AppFlowy'; + final editor = tester.editor..addParagraph(initialText: text); await editor.startTesting(); expect( - find.text('Welcome to AppFlowy', findRichText: true), + find.text(text, findRichText: true), findsOneWidget, ); await editor.updateSelection( @@ -63,9 +68,10 @@ void main() async { end: Position(path: [0], offset: 18), ), ); - final text = + final copiedText = editor.editorState.getTextInSelection(editor.selection).join('/n'); - rightClick(); + final position = tester.getCenter(find.text(text, findRichText: true)); + rightClickAt(position); await tester.pump(); final cutButton = find.text('Cut'); expect(cutButton, findsOneWidget); @@ -76,17 +82,18 @@ void main() async { findsNothing, ); final clipBoardData = await AppFlowyClipboard.getData(); - expect(clipBoardData.text, text); + expect(clipBoardData.text, copiedText); await editor.dispose(); }); testWidgets('context menu copy and paste test', (tester) async { + const text = 'Welcome to AppFlowy'; final editor = tester.editor - ..addParagraph(initialText: 'Welcome to AppFlowy'); - editor.addParagraph(initialText: 'Hello'); + ..addParagraph(initialText: text) + ..addParagraph(initialText: 'Hello'); await editor.startTesting(); expect( - find.text('Welcome to AppFlowy', findRichText: true), + find.text(text, findRichText: true), findsOneWidget, ); await editor.updateSelection( @@ -95,9 +102,10 @@ void main() async { end: Position(path: [1], offset: 5), ), ); - final text = + final copiedText = editor.editorState.getTextInSelection(editor.selection).join('/n'); - rightClick(); + final position = tester.getCenter(find.text('Hello', findRichText: true)); + rightClickAt(position); await tester.pump(); final copyButton = find.text('Copy'); expect(copyButton, findsOneWidget); @@ -108,14 +116,16 @@ void main() async { findsOneWidget, ); final clipBoardData = await AppFlowyClipboard.getData(); - expect(clipBoardData.text, text); + expect(clipBoardData.text, copiedText); await editor.updateSelection( Selection( start: Position(path: [0], offset: 0), end: Position(path: [0], offset: 7), ), ); - rightClick(); + final newPosition = + tester.getTopLeft(find.text(text, findRichText: true)); + rightClickAt(newPosition); await tester.pump(); final pasteButton = find.text('Paste'); expect(pasteButton, findsOneWidget);