chore: adjust context menu visibility logic (#1158)
* chore: default example context menu * chore: adjust context menu visibility logic * chore: dart analyzer
This commit is contained in:
parent
aacec0801e
commit
15289103a2
6 changed files with 69 additions and 29 deletions
|
|
@ -97,6 +97,14 @@ class _DesktopEditorState extends State<DesktopEditor> {
|
|||
dropTargetStyle: const AppFlowyDropTargetStyle(
|
||||
color: Colors.red,
|
||||
),
|
||||
contextMenuBuilder: (context, position, editorState, onPressed) {
|
||||
return ContextMenu(
|
||||
position: position,
|
||||
editorState: editorState,
|
||||
items: standardContextMenuItems,
|
||||
onPressed: onPressed,
|
||||
);
|
||||
},
|
||||
header: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10.0),
|
||||
child: Image.asset(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export 'src/plugins/plugins.dart';
|
|||
export 'src/render/selection/selectable.dart';
|
||||
export 'src/render/toolbar/toolbar_item.dart';
|
||||
export 'src/service/context_menu/context_menu.dart';
|
||||
export 'src/service/context_menu/built_in_context_menu_item.dart';
|
||||
export 'src/service/default_text_operations/format_rich_text_style.dart';
|
||||
export 'src/service/internal_key_event_handlers/copy_paste_handler.dart';
|
||||
export 'src/service/shortcut_event/key_mapping.dart';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import 'dart:math';
|
|||
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/flutter/overlay.dart';
|
||||
import 'package:appflowy_editor/src/service/context_menu/built_in_context_menu_item.dart';
|
||||
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
|
@ -125,8 +124,7 @@ class AppFlowyEditor extends StatefulWidget {
|
|||
///
|
||||
/// It will be shown when the user right click on the editor.
|
||||
///
|
||||
/// Defaults to a standard implementation containing
|
||||
/// [standardContextMenuItems].
|
||||
/// See the built-in [ContextMenu] implementation.
|
||||
///
|
||||
final ContextMenuWidgetBuilder? contextMenuBuilder;
|
||||
|
||||
|
|
|
|||
|
|
@ -286,15 +286,45 @@ class _DesktopSelectionServiceWidgetState
|
|||
}
|
||||
|
||||
void _onSecondaryTapDown(TapDownDetails details) {
|
||||
// if selection is null, or
|
||||
// selection.isCollapsed and the selected node is TextNode.
|
||||
// try to select the word.
|
||||
final offset = details.globalPosition;
|
||||
final selection = editorState.selectionNotifier.value;
|
||||
if (selection == null ||
|
||||
(selection.isCollapsed == true &&
|
||||
currentSelectedNodes.first.delta != null)) {
|
||||
_onDoubleTapDown(details);
|
||||
final selectable = getNodeInOffset(offset)?.selectable;
|
||||
if (selectable == null) {
|
||||
clearSelection();
|
||||
return;
|
||||
}
|
||||
final wordBoundary = selectable.getWordBoundaryInOffset(offset);
|
||||
|
||||
final Selection? newSelection;
|
||||
|
||||
final isSelectionCollapsed = selection == null || selection.isCollapsed;
|
||||
|
||||
final isWordBoundaryWithinSelection = !isSelectionCollapsed &&
|
||||
wordBoundary != null &&
|
||||
(wordBoundary.start.path > selection.start.path ||
|
||||
wordBoundary.start.path == selection.start.path &&
|
||||
wordBoundary.start.offset >= selection.start.offset) &&
|
||||
(wordBoundary.end.path < selection.end.path ||
|
||||
wordBoundary.end.path == selection.end.path &&
|
||||
wordBoundary.end.offset <= selection.end.offset);
|
||||
|
||||
if (isWordBoundaryWithinSelection) {
|
||||
newSelection = selection;
|
||||
} else if (selectable.cursorStyle == CursorStyle.verticalLine) {
|
||||
newSelection = wordBoundary;
|
||||
} else {
|
||||
newSelection = Selection(
|
||||
start: selectable.start(),
|
||||
end: selectable.end(),
|
||||
);
|
||||
}
|
||||
|
||||
editorState.updateSelectionWithReason(
|
||||
newSelection,
|
||||
extraInfo: {
|
||||
selectionExtraInfoDisableToolbar: true,
|
||||
},
|
||||
);
|
||||
|
||||
_showContextMenu(details);
|
||||
}
|
||||
|
|
@ -436,25 +466,22 @@ class _DesktopSelectionServiceWidgetState
|
|||
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;
|
||||
}
|
||||
|
||||
final mask = OverlayEntry(
|
||||
builder: (_) => Listener(
|
||||
onPointerDown: (_) => _clearContextMenu(),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
),
|
||||
);
|
||||
_contextMenuAreas.add(mask);
|
||||
Overlay.of(context, rootOverlay: true).insert(mask);
|
||||
|
||||
final baseOffset =
|
||||
editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero;
|
||||
final offset = details.localPosition + const Offset(10, 10) + baseOffset;
|
||||
|
|
|
|||
|
|
@ -135,16 +135,23 @@ class _FloatingToolbarState extends State<FloatingToolbar>
|
|||
void _onSelectionChanged() {
|
||||
final selection = editorState.selection;
|
||||
final selectionType = editorState.selectionType;
|
||||
|
||||
final disableToolbar =
|
||||
editorState.selectionExtraInfo?[selectionExtraInfoDisableToolbar] ==
|
||||
true;
|
||||
|
||||
if (disableToolbar) {
|
||||
_clear();
|
||||
}
|
||||
|
||||
if (lastSelection == selection) return;
|
||||
lastSelection = selection;
|
||||
|
||||
if (selection == null ||
|
||||
selection.isCollapsed ||
|
||||
selectionType == SelectionType.block ||
|
||||
editorState.selectionExtraInfo?[selectionExtraInfoDisableToolbar] ==
|
||||
true) {
|
||||
selectionType == SelectionType.block) {
|
||||
_clear();
|
||||
} else {
|
||||
} else if (!disableToolbar) {
|
||||
// uses debounce to avoid the computing the rects too frequently.
|
||||
_showAfterDelay(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/editor/editor_component/service/ime/text_input_service.dart';
|
||||
import 'package:appflowy_editor/src/service/context_menu/built_in_context_menu_item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
|
|
|||
Loading…
Reference in a new issue