diff --git a/lib/appflowy_editor.dart b/lib/appflowy_editor.dart index b023dcd09..470f8b226 100644 --- a/lib/appflowy_editor.dart +++ b/lib/appflowy_editor.dart @@ -9,6 +9,7 @@ export 'src/editor/block_component/rich_text/appflowy_rich_text_keys.dart'; export 'src/editor/block_component/rich_text/default_selectable_mixin.dart'; // editor part, including editor component, block component, etc. export 'src/editor/editor.dart'; +export 'src/editor/find_replace_menu/find_and_replace.dart'; export 'src/editor/selection_menu/selection_menu.dart'; // editor state export 'src/editor_state.dart'; diff --git a/lib/src/editor/editor_component/service/scroll/desktop_scroll_service.dart b/lib/src/editor/editor_component/service/scroll/desktop_scroll_service.dart index ee33c46ce..4f1107fba 100644 --- a/lib/src/editor/editor_component/service/scroll/desktop_scroll_service.dart +++ b/lib/src/editor/editor_component/service/scroll/desktop_scroll_service.dart @@ -80,7 +80,7 @@ class _DesktopScrollServiceState extends State @override void jumpTo(int index) { - throw UnimplementedError(); + editorScrollController.jumpTo(offset: index.toDouble()); } @override diff --git a/lib/src/editor/editor_component/service/scroll/editor_scroll_controller.dart b/lib/src/editor/editor_component/service/scroll/editor_scroll_controller.dart index a269ab028..dbc7f6804 100644 --- a/lib/src/editor/editor_component/service/scroll/editor_scroll_controller.dart +++ b/lib/src/editor/editor_component/service/scroll/editor_scroll_controller.dart @@ -181,7 +181,10 @@ class EditorScrollController { ); } - scrollOffsetController.jumpTo(offset: max(0, offset)); + itemScrollController.jumpTo( + index: max(0, offset.toInt()), + alignment: 0.5, + ); } void jumpToTop() { diff --git a/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/find_replace_command.dart b/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/find_replace_command.dart index 77a91c05e..569e00801 100644 --- a/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/find_replace_command.dart +++ b/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/find_replace_command.dart @@ -24,12 +24,23 @@ class FindReplaceStyle { FindReplaceStyle({ this.selectedHighlightColor = const Color(0xFFFFB931), this.unselectedHighlightColor = const Color(0x60ECBC5F), + this.findMenuBuilder, }); //selected highlight color is used as background color on the selected found pattern. final Color selectedHighlightColor; //unselected highlight color is used on every other found pattern which can be selected. final Color unselectedHighlightColor; + + // find menu builder + Widget Function( + BuildContext context, + EditorState editorState, + FindReplaceLocalizations? localizations, + FindReplaceStyle style, + bool showReplaceMenu, + VoidCallback onDismiss, + )? findMenuBuilder; } class FindReplaceLocalizations { @@ -108,7 +119,7 @@ KeyEventResult _showFindAndReplaceDialog( _findReplaceService = FindReplaceMenu( context: context, editorState: editorState, - replaceFlag: openReplace, + showReplaceMenu: openReplace, localizations: localizations, style: style, ); diff --git a/lib/src/editor/find_replace_menu/find_and_replace.dart b/lib/src/editor/find_replace_menu/find_and_replace.dart new file mode 100644 index 000000000..b55f0c952 --- /dev/null +++ b/lib/src/editor/find_replace_menu/find_and_replace.dart @@ -0,0 +1 @@ +export 'search_service_v2.dart'; diff --git a/lib/src/editor/find_replace_menu/find_menu_service.dart b/lib/src/editor/find_replace_menu/find_menu_service.dart index cf593d311..4d6095bbd 100644 --- a/lib/src/editor/find_replace_menu/find_menu_service.dart +++ b/lib/src/editor/find_replace_menu/find_menu_service.dart @@ -13,14 +13,14 @@ class FindReplaceMenu implements FindReplaceService { FindReplaceMenu({ required this.context, required this.editorState, - required this.replaceFlag, + required this.showReplaceMenu, this.localizations, required this.style, }); final BuildContext context; final EditorState editorState; - final bool replaceFlag; + final bool showReplaceMenu; final FindReplaceLocalizations? localizations; final FindReplaceStyle style; @@ -68,29 +68,37 @@ class FindReplaceMenu implements FindReplaceService { return Positioned( top: topOffset, right: rightOffset, - child: Material( - borderRadius: BorderRadius.circular(8.0), - child: DecoratedBox( - decoration: BoxDecoration( - color: editorState.editorStyle.selectionColor, - boxShadow: [ - BoxShadow( - blurRadius: 5, - spreadRadius: 1, - color: Colors.black.withOpacity(0.1), + child: style.findMenuBuilder?.call( + context, + editorState, + localizations, + style, + showReplaceMenu, + dismiss, + ) ?? + Material( + borderRadius: BorderRadius.circular(8.0), + child: DecoratedBox( + decoration: BoxDecoration( + color: editorState.editorStyle.selectionColor, + boxShadow: [ + BoxShadow( + blurRadius: 5, + spreadRadius: 1, + color: Colors.black.withOpacity(0.1), + ), + ], + borderRadius: BorderRadius.circular(6.0), ), - ], - borderRadius: BorderRadius.circular(6.0), - ), - child: FindAndReplaceMenuWidget( - onDismiss: dismiss, - editorState: editorState, - showReplaceMenu: replaceFlag, - localizations: localizations, - style: style, + child: FindAndReplaceMenuWidget( + onDismiss: dismiss, + editorState: editorState, + showReplaceMenu: showReplaceMenu, + localizations: localizations, + style: style, + ), + ), ), - ), - ), ); }, ); diff --git a/lib/src/editor/find_replace_menu/find_replace_widget.dart b/lib/src/editor/find_replace_menu/find_replace_widget.dart index 4388268fd..e6b51d3c3 100644 --- a/lib/src/editor/find_replace_menu/find_replace_widget.dart +++ b/lib/src/editor/find_replace_menu/find_replace_widget.dart @@ -1,6 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/editor/find_replace_menu/find_replace_menu_icon_button.dart'; -import 'package:appflowy_editor/src/editor/find_replace_menu/search_service_v2.dart'; import 'package:flutter/material.dart'; const double _iconButtonSize = 30; diff --git a/lib/src/editor/find_replace_menu/search_service_v2.dart b/lib/src/editor/find_replace_menu/search_service_v2.dart index 146baa114..ffcccd72c 100644 --- a/lib/src/editor/find_replace_menu/search_service_v2.dart +++ b/lib/src/editor/find_replace_menu/search_service_v2.dart @@ -27,12 +27,16 @@ class SearchServiceV2 { int _selectedIndex = 0; int get selectedIndex => _selectedIndex; set selectedIndex(int index) { + _prevSelectedIndex = _selectedIndex; _selectedIndex = matchedPositions.value.isEmpty ? -1 : index.clamp(0, matchedPositions.value.length - 1); currentSelectedIndex.value = _selectedIndex; } + // only used for scrolling to the first or the last match. + int _prevSelectedIndex = 0; + ValueNotifier currentSelectedIndex = ValueNotifier(0); void dispose() { @@ -101,6 +105,18 @@ class SearchServiceV2 { path: start.path, offset: start.offset + pattern.length, ); + + // https://github.com/google/flutter.widgets/issues/151 + // there's a bug in the scrollable_positioned_list package + // we can't scroll to the index without animation. + // so we just scroll the position if the index is the first or the last. + final length = matchedPositions.value.length - 1; + if (_prevSelectedIndex != selectedIndex && + ((_prevSelectedIndex == length && selectedIndex == 0) || + (_prevSelectedIndex == 0 && selectedIndex == length))) { + editorState.scrollService?.jumpTo(start.path.first); + } + editorState.updateSelectionWithReason( Selection(start: start, end: end), extraInfo: { diff --git a/lib/src/editor/toolbar/desktop/floating_toolbar.dart b/lib/src/editor/toolbar/desktop/floating_toolbar.dart index 4270f61b0..473966dbd 100644 --- a/lib/src/editor/toolbar/desktop/floating_toolbar.dart +++ b/lib/src/editor/toolbar/desktop/floating_toolbar.dart @@ -1,7 +1,6 @@ import 'dart:math'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/editor/find_replace_menu/search_service_v2.dart'; import 'package:flutter/material.dart'; class FloatingToolbarStyle {