From f2630b4e93d78ead251ebc4d2e5bb3c938743725 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Mon, 5 Jun 2023 13:16:20 +0800 Subject: [PATCH 1/5] fix: toolbar item size and scroll widget layout --- example/lib/pages/simple_editor.dart | 2 +- .../scroll/auto_scrollable_widget.dart | 23 ++++++++++++------- .../color/highlight_color_toolbar_item.dart | 2 ++ .../items/color/text_color_toolbar_item.dart | 2 ++ lib/src/service/editor_service.dart | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/example/lib/pages/simple_editor.dart b/example/lib/pages/simple_editor.dart index dfcdc78c8..51720172f 100644 --- a/example/lib/pages/simple_editor.dart +++ b/example/lib/pages/simple_editor.dart @@ -33,7 +33,7 @@ class SimpleEditor extends StatelessWidget { ); editorState.logConfiguration ..handler = debugPrint - ..level = LogLevel.off; + ..level = LogLevel.all; onEditorStateChange(editorState); final scrollController = ScrollController(); if (PlatformExtension.isDesktopOrWeb) { diff --git a/lib/src/editor/editor_component/service/scroll/auto_scrollable_widget.dart b/lib/src/editor/editor_component/service/scroll/auto_scrollable_widget.dart index 23c759b7e..37d9d00e3 100644 --- a/lib/src/editor/editor_component/service/scroll/auto_scrollable_widget.dart +++ b/lib/src/editor/editor_component/service/scroll/auto_scrollable_widget.dart @@ -24,14 +24,21 @@ class _AutoScrollableWidgetState extends State { @override Widget build(BuildContext context) { - return SingleChildScrollView( - controller: widget.scrollController, - child: Builder( - builder: (context) { - _scrollableState = Scrollable.of(context); - _initAutoScroller(); - return widget.builder(context, _autoScroller); - }, + return LayoutBuilder( + builder: (context, viewportConstraints) => SingleChildScrollView( + controller: widget.scrollController, + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Builder( + builder: (context) { + _scrollableState = Scrollable.of(context); + _initAutoScroller(); + return widget.builder(context, _autoScroller); + }, + ), + ), ), ); } diff --git a/lib/src/editor/toolbar/items/color/highlight_color_toolbar_item.dart b/lib/src/editor/toolbar/items/color/highlight_color_toolbar_item.dart index c95c5796d..62d4bce21 100644 --- a/lib/src/editor/toolbar/items/color/highlight_color_toolbar_item.dart +++ b/lib/src/editor/toolbar/items/color/highlight_color_toolbar_item.dart @@ -1,4 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; final highlightColorItem = ToolbarItem( id: 'editor.highlightColor', @@ -16,6 +17,7 @@ final highlightColorItem = ToolbarItem( }); return IconItemWidget( iconName: 'toolbar/highlight_color', + iconSize: const Size.square(14), isHighlight: isHighlight, tooltip: AppFlowyEditorLocalizations.current.highlightColor, onPressed: () { diff --git a/lib/src/editor/toolbar/items/color/text_color_toolbar_item.dart b/lib/src/editor/toolbar/items/color/text_color_toolbar_item.dart index 8310d858e..adcc85a43 100644 --- a/lib/src/editor/toolbar/items/color/text_color_toolbar_item.dart +++ b/lib/src/editor/toolbar/items/color/text_color_toolbar_item.dart @@ -1,4 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; final textColorItem = ToolbarItem( id: 'editor.textColor', @@ -16,6 +17,7 @@ final textColorItem = ToolbarItem( return IconItemWidget( iconName: 'toolbar/text_color', isHighlight: isHighlight, + iconSize: const Size.square(14), tooltip: AppFlowyEditorLocalizations.current.textColor, onPressed: () { showColorMenu( diff --git a/lib/src/service/editor_service.dart b/lib/src/service/editor_service.dart index 29f3c2a88..7b69a1643 100644 --- a/lib/src/service/editor_service.dart +++ b/lib/src/service/editor_service.dart @@ -204,7 +204,7 @@ class _AppFlowyEditorState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - widget.header ?? const SizedBox.shrink(), + if (widget.header != null) widget.header!, Container( padding: widget.editorStyle.padding, child: editorState.renderer.build( From 751485ca47438c912b009c4a355285549a6dac68 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Mon, 5 Jun 2023 13:27:34 +0800 Subject: [PATCH 2/5] fix: remove the use of document.empty --- lib/src/plugins/quill_delta/delta_document_encoder.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/plugins/quill_delta/delta_document_encoder.dart b/lib/src/plugins/quill_delta/delta_document_encoder.dart index c1940f4c9..750c5cd19 100644 --- a/lib/src/plugins/quill_delta/delta_document_encoder.dart +++ b/lib/src/plugins/quill_delta/delta_document_encoder.dart @@ -5,6 +5,7 @@ import 'package:appflowy_editor/src/core/document/text_delta.dart'; import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; import 'package:flutter/material.dart'; +@Deprecated('This class is outdated and will be removed in the next release.') class DeltaDocumentConvert { DeltaDocumentConvert(); @@ -19,7 +20,7 @@ class DeltaDocumentConvert { Document convertFromDelta(Delta delta) { final iter = delta.iterator; - final document = Document.empty(); + final document = Document.blank(); TextNode textNode = TextNode(delta: Delta()); int path = 0; From 22d699943ad5f604378265a320e2390f94078036 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Mon, 5 Jun 2023 13:36:45 +0800 Subject: [PATCH 3/5] fix: editor still editable even editable boolean is false when select on editor area --- lib/src/editor_state.dart | 3 +- lib/src/service/editor_service.dart | 44 ++++++++++++++++------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/lib/src/editor_state.dart b/lib/src/editor_state.dart index 7d1a83fef..0b545bb59 100644 --- a/lib/src/editor_state.dart +++ b/lib/src/editor_state.dart @@ -53,7 +53,6 @@ enum SelectionType { class EditorState { EditorState({ required this.document, - this.editable = true, }) { undoManager.state = this; } @@ -75,7 +74,7 @@ class EditorState { final Document document; /// Whether the editor is editable. - final bool editable; + bool editable = true; /// The style of the editor. late EditorStyle editorStyle; diff --git a/lib/src/service/editor_service.dart b/lib/src/service/editor_service.dart index 7b69a1643..2c639f556 100644 --- a/lib/src/service/editor_service.dart +++ b/lib/src/service/editor_service.dart @@ -150,6 +150,7 @@ class _AppFlowyEditorState extends State { editorState.editorStyle = widget.editorStyle; editorState.selectionMenuItems = widget.selectionMenuItems; editorState.renderer = _renderer; + editorState.editable = widget.editable; // auto focus WidgetsBinding.instance.addPostFrameCallback((timeStamp) { @@ -162,6 +163,7 @@ class _AppFlowyEditorState extends State { super.didUpdateWidget(oldWidget); editorState.editorStyle = widget.editorStyle; + editorState.editable = widget.editable; if (editorState.service != oldWidget.editorState.service) { editorState.selectionMenuItems = widget.selectionMenuItems; @@ -188,10 +190,22 @@ class _AppFlowyEditorState extends State { } Widget _buildServices(BuildContext context) { - return ScrollServiceWidget( - key: editorState.service.scrollServiceKey, - scrollController: widget.scrollController, - child: SelectionServiceWidget( + Widget child = Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.header != null) widget.header!, + Container( + padding: widget.editorStyle.padding, + child: editorState.renderer.build( + context, + editorState.document.root, + ), + ), + ], + ); + if (widget.editable) { + child = SelectionServiceWidget( key: editorState.service.selectionServiceKey, cursorColor: widget.editorStyle.cursorColor, selectionColor: widget.editorStyle.selectionColor, @@ -200,22 +214,14 @@ class _AppFlowyEditorState extends State { characterShortcutEvents: widget.characterShortcutEvents, commandShortcutEvents: widget.commandShortcutEvents, focusNode: widget.focusNode, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.header != null) widget.header!, - Container( - padding: widget.editorStyle.padding, - child: editorState.renderer.build( - context, - editorState.document.root, - ), - ), - ], - ), + child: child, ), - ), + ); + } + return ScrollServiceWidget( + key: editorState.service.scrollServiceKey, + scrollController: widget.scrollController, + child: child, ); } From 072f4922d16df6dad430b6bdf3602c0ccc2c5a7e Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Mon, 5 Jun 2023 14:43:23 +0800 Subject: [PATCH 4/5] fix: the image menu is overflow --- .../image_upload_widget.dart | 11 +++++++---- .../command_shortcut_events/copy_command.dart | 2 +- .../command_shortcut_events/paste_command.dart | 4 ++-- .../editor/toolbar/items/color/color_menu.dart | 2 +- .../toolbar/items/link/link_toolbar_item.dart | 12 +++++++----- .../selection_menu/selection_menu_service.dart | 17 ++++++++++++++++- 6 files changed, 34 insertions(+), 14 deletions(-) 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 34857baf4..6cf0c86cc 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 @@ -8,12 +8,15 @@ void showImageMenu( ) { menuService.dismiss(); - final topLeft = menuService.topLeft; + final (left, top, bottom) = menuService.getPosition( + MediaQuery.of(container.context).size.height * 2.0 / 3.0, + ); final imageMenuEntry = FullScreenOverlayEntry( - top: topLeft.dy, - left: topLeft.dx, + left: left, + top: top, + bottom: bottom, builder: (context) => UploadImageMenu( - backgroundColor: Colors.white, // TODO: customize the color + backgroundColor: menuService.style.selectionMenuBackgroundColor, width: MediaQuery.of(context).size.width * 0.5, onSubmitted: editorState.insertImageNode, onUpload: editorState.insertImageNode, diff --git a/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/copy_command.dart b/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/copy_command.dart index 75ddc0a55..6d117b8b4 100644 --- a/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/copy_command.dart +++ b/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/copy_command.dart @@ -40,7 +40,7 @@ CommandShortcutEventHandler _copyCommandHandler = (editorState) { AppFlowyClipboard.setData( text: text, - html: html, + html: html.isEmpty ? null : html, ); return KeyEventResult.handled; diff --git a/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/paste_command.dart b/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/paste_command.dart index 30d5c6c65..9d5fe43bf 100644 --- a/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/paste_command.dart +++ b/lib/src/editor/editor_component/service/shortcuts/command_shortcut_events/paste_command.dart @@ -43,12 +43,12 @@ CommandShortcutEventHandler _pasteCommandHandler = (editorState) { final data = await AppFlowyClipboard.getData(); final text = data.text; final html = data.html; - if (html != null) { + if (html != null && html.isNotEmpty) { final nodes = htmlToDocument(html).root.children; final transaction = editorState.transaction ..insertNodes(selection!.end.path, nodes); await editorState.apply(transaction); - } else if (text != null) { + } else if (text != null && text.isNotEmpty) { handlePastePlainText(editorState, data.text!); } }(); diff --git a/lib/src/editor/toolbar/items/color/color_menu.dart b/lib/src/editor/toolbar/items/color/color_menu.dart index c3a79aabe..a8bcac93c 100644 --- a/lib/src/editor/toolbar/items/color/color_menu.dart +++ b/lib/src/editor/toolbar/items/color/color_menu.dart @@ -20,7 +20,7 @@ void showColorMenu( overlay = FullScreenOverlayEntry( top: rect.bottom + 5, - left: rect.left + 10, + left: rect.left, builder: (context) { return ColorPicker( isTextColor: isTextColor, diff --git a/lib/src/editor/toolbar/items/link/link_toolbar_item.dart b/lib/src/editor/toolbar/items/link/link_toolbar_item.dart index 0e364fe2d..b86b81180 100644 --- a/lib/src/editor/toolbar/items/link/link_toolbar_item.dart +++ b/lib/src/editor/toolbar/items/link/link_toolbar_item.dart @@ -12,7 +12,7 @@ final linkItem = ToolbarItem( final nodes = editorState.getNodesInSelection(selection); final isHref = nodes.allSatisfyInSelection(selection, (delta) { return delta.everyAttributes( - (attributes) => attributes['href'] != null, + (attributes) => attributes[FlowyRichTextKeys.href] != null, ); }); @@ -39,9 +39,11 @@ void showLinkMenu( // get node, index and length for formatting text when the link is removed final node = editorState.getNodeAtPath(selection.end.path); - final index = - selection.isBackward ? selection.start.offset : selection.end.offset; - final length = (selection.start.offset - selection.end.offset).abs(); + if (node == null) { + return; + } + final index = selection.normalized.startIndex; + final length = selection.length; // get link address if the selection is already a link String? linkText; @@ -82,7 +84,7 @@ void showLinkMenu( onRemoveLink: () { final transaction = editorState.transaction ..formatText( - node!, + node, index, length, {BuiltInAttributeKey.href: null}, diff --git a/lib/src/render/selection_menu/selection_menu_service.dart b/lib/src/render/selection_menu/selection_menu_service.dart index 7e7f522a3..afba2aa9f 100644 --- a/lib/src/render/selection_menu/selection_menu_service.dart +++ b/lib/src/render/selection_menu/selection_menu_service.dart @@ -7,12 +7,26 @@ abstract class SelectionMenuService { Offset get topLeft; Offset get offset; Alignment get alignment; + SelectionMenuStyle get style; void show(); void dismiss(); + + (double left, double? top, double? bottom) getPosition(double threshold) { + final offset = this.offset; + final left = offset.dx; + double? top; + double? bottom; + if (topLeft.dy >= threshold) { + bottom = offset.dy; + } else { + top = offset.dy; + } + return (left, top, bottom); + } } -class SelectionMenu implements SelectionMenuService { +class SelectionMenu extends SelectionMenuService { SelectionMenu({ required this.context, required this.editorState, @@ -25,6 +39,7 @@ class SelectionMenu implements SelectionMenuService { final EditorState editorState; final List selectionMenuItems; final bool deleteSlashByDefault; + @override final SelectionMenuStyle style; OverlayEntry? _selectionMenuEntry; From 0a2b807c3064366c138a3b275b55d8d0b1a02508 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Mon, 5 Jun 2023 16:05:04 +0800 Subject: [PATCH 5/5] feat: add image align --- .../image_block_component.dart | 129 +++--------------- .../image_upload_widget.dart | 7 +- .../renderer/block_component_action.dart | 4 +- .../selection/desktop_selection_service.dart | 9 +- .../selection_menu_service.dart | 26 ++-- 5 files changed, 49 insertions(+), 126 deletions(-) diff --git a/lib/src/editor/block_component/image_block_component/image_block_component.dart b/lib/src/editor/block_component/image_block_component/image_block_component.dart index 57ac0d466..da9d200f5 100644 --- a/lib/src/editor/block_component/image_block_component/image_block_component.dart +++ b/lib/src/editor/block_component/image_block_component/image_block_component.dart @@ -50,7 +50,12 @@ Node imageNode({ } class ImageBlockComponentBuilder extends BlockComponentBuilder { - ImageBlockComponentBuilder(); + ImageBlockComponentBuilder({ + this.configuration = const BlockComponentConfiguration(), + }); + + @override + final BlockComponentConfiguration configuration; @override BlockComponentWidget build(BlockComponentContext blockComponentContext) { @@ -58,6 +63,7 @@ class ImageBlockComponentBuilder extends BlockComponentBuilder { return ImageBlockComponentWidget( node: node, showActions: showActions(node), + configuration: configuration, actionBuilder: (context, state) => actionBuilder( blockComponentContext, state, @@ -97,7 +103,7 @@ class _ImageBlockComponentWidgetState extends State { final align = attributes[ImageBlockKeys.align] ?? 'center'; final width = attributes[ImageBlockKeys.width]?.toDouble(); - return ImageNodeWidget( + Widget child = ImageNodeWidget( key: node.key, node: node, src: src, @@ -107,11 +113,21 @@ class _ImageBlockComponentWidgetState extends State { onResize: (width) { final transaction = editorState.transaction ..updateNode(node, { - 'width': width, + ImageBlockKeys.width: width, }); editorState.apply(transaction); }, ); + + if (widget.showActions && widget.actionBuilder != null) { + child = BlockComponentActionWrapper( + node: node, + actionBuilder: widget.actionBuilder!, + child: child, + ); + } + + return child; } Alignment _textToAlignment(String text) { @@ -123,110 +139,3 @@ class _ImageBlockComponentWidgetState extends State { return Alignment.center; } } - -// class ImageNodeBuilder extends NodeWidgetBuilder -// with ActionProvider { -// @override -// Widget build(NodeWidgetContext context) { -// final src = context.node.attributes['image_src']; -// final align = context.node.attributes['align']; -// double? width; -// if (context.node.attributes.containsKey('width')) { -// width = context.node.attributes['width'].toDouble(); -// } -// return ImageNodeWidget( -// key: context.node.key, -// node: context.node, -// src: src, -// width: width, -// editable: context.editorState.editable, -// alignment: _textToAlignment(align), -// onResize: (width) { -// final transaction = context.editorState.transaction -// ..updateNode(context.node, { -// 'width': width, -// }); -// context.editorState.apply(transaction); -// }, -// ); -// } - -// @override -// NodeValidator get nodeValidator => ((node) { -// return node.type == 'image' && -// node.attributes.containsKey('image_src') && -// node.attributes.containsKey('align'); -// }); - -// @override -// List actions(NodeWidgetContext context) { -// return [ -// ActionMenuItem.svg( -// name: 'image_toolbar/align_left', -// selected: () { -// final align = context.node.attributes['align']; -// return _textToAlignment(align) == Alignment.centerLeft; -// }, -// onPressed: () => _onAlign(context, Alignment.centerLeft), -// ), -// ActionMenuItem.svg( -// name: 'image_toolbar/align_center', -// selected: () { -// final align = context.node.attributes['align']; -// return _textToAlignment(align) == Alignment.center; -// }, -// onPressed: () => _onAlign(context, Alignment.center), -// ), -// ActionMenuItem.svg( -// name: 'image_toolbar/align_right', -// selected: () { -// final align = context.node.attributes['align']; -// return _textToAlignment(align) == Alignment.centerRight; -// }, -// onPressed: () => _onAlign(context, Alignment.centerRight), -// ), -// ActionMenuItem.separator(), -// ActionMenuItem.svg( -// name: 'image_toolbar/copy', -// onPressed: () { -// final src = context.node.attributes['image_src']; -// AppFlowyClipboard.setData(text: src); -// }, -// ), -// ActionMenuItem.svg( -// name: 'image_toolbar/delete', -// onPressed: () { -// final transaction = context.editorState.transaction -// ..deleteNode(context.node); -// context.editorState.apply(transaction); -// }, -// ), -// ]; -// } - -// Alignment _textToAlignment(String text) { -// if (text == 'left') { -// return Alignment.centerLeft; -// } else if (text == 'right') { -// return Alignment.centerRight; -// } -// return Alignment.center; -// } - -// String _alignmentToText(Alignment alignment) { -// if (alignment == Alignment.centerLeft) { -// return 'left'; -// } else if (alignment == Alignment.centerRight) { -// return 'right'; -// } -// return 'center'; -// } - -// void _onAlign(NodeWidgetContext context, Alignment alignment) { -// final transaction = context.editorState.transaction -// ..updateNode(context.node, { -// 'align': _alignmentToText(alignment), -// }); -// context.editorState.apply(transaction); -// } -// } 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 6cf0c86cc..bbe320252 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 @@ -17,6 +17,7 @@ void showImageMenu( bottom: bottom, builder: (context) => UploadImageMenu( backgroundColor: menuService.style.selectionMenuBackgroundColor, + headerColor: menuService.style.selectionMenuItemTextColor, width: MediaQuery.of(context).size.width * 0.5, onSubmitted: editorState.insertImageNode, onUpload: editorState.insertImageNode, @@ -29,12 +30,14 @@ class UploadImageMenu extends StatefulWidget { const UploadImageMenu({ Key? key, this.backgroundColor = Colors.white, + this.headerColor = Colors.black, this.width = 300, required this.onSubmitted, required this.onUpload, }) : super(key: key); final Color backgroundColor; + final Color headerColor; final double width; final void Function(String text) onSubmitted; final void Function(String text) onUpload; @@ -89,12 +92,12 @@ class _UploadImageMenuState extends State { } Widget _buildHeader(BuildContext context) { - return const Text( + return Text( 'URL Image', textAlign: TextAlign.left, style: TextStyle( fontSize: 14.0, - color: Colors.black, + color: widget.headerColor, fontWeight: FontWeight.w500, ), ); diff --git a/lib/src/editor/editor_component/service/renderer/block_component_action.dart b/lib/src/editor/editor_component/service/renderer/block_component_action.dart index 12d4afe52..40d6521eb 100644 --- a/lib/src/editor/editor_component/service/renderer/block_component_action.dart +++ b/lib/src/editor/editor_component/service/renderer/block_component_action.dart @@ -1,6 +1,8 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; +const double blockComponentActionContainerWidth = 50; + class BlockComponentActionContainer extends StatelessWidget { const BlockComponentActionContainer({ super.key, @@ -17,7 +19,7 @@ class BlockComponentActionContainer extends StatelessWidget { Widget build(BuildContext context) { return Container( alignment: Alignment.centerRight, - width: 50, + width: blockComponentActionContainerWidth, height: 25, // TODO: magic number, change it to the height of the block color: Colors .transparent, // have to set the color to transparent to make the MouseRegion work 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 8734ab5b7..aec440b41 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 @@ -1,4 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor/src/editor/editor_component/service/renderer/block_component_action.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:appflowy_editor/src/service/context_menu/context_menu.dart'; @@ -356,7 +357,13 @@ class _DesktopSelectionServiceWidgetState currentSelectedNodes = nodes; final node = nodes.first; - final rect = Offset.zero & node.rect.size; + var rect = Offset.zero & node.rect.size; + + final builder = editorState.renderer.blockComponentBuilder(node.type); + if (builder != null && builder.showActions(node)) { + rect = rect.translate(blockComponentActionContainerWidth, 0); + } + final overlay = OverlayEntry( builder: (context) => SelectionWidget( color: widget.selectionColor, diff --git a/lib/src/render/selection_menu/selection_menu_service.dart b/lib/src/render/selection_menu/selection_menu_service.dart index afba2aa9f..dcfe4c878 100644 --- a/lib/src/render/selection_menu/selection_menu_service.dart +++ b/lib/src/render/selection_menu/selection_menu_service.dart @@ -12,18 +12,7 @@ abstract class SelectionMenuService { void show(); void dismiss(); - (double left, double? top, double? bottom) getPosition(double threshold) { - final offset = this.offset; - final left = offset.dx; - double? top; - double? bottom; - if (topLeft.dy >= threshold) { - bottom = offset.dy; - } else { - top = offset.dy; - } - return (left, top, bottom); - } + (double left, double? top, double? bottom) getPosition(double threshold); } class SelectionMenu extends SelectionMenuService { @@ -159,6 +148,19 @@ class SelectionMenu extends SelectionMenuService { selectionService.currentSelection.addListener(_onSelectionChange); } + @override + (double, double?, double?) getPosition(double threshold) { + final left = _offset.dx; + double? top; + double? bottom; + if (topLeft.dy >= threshold) { + bottom = _offset.dy; + } else { + top = _offset.dy; + } + return (left, top, bottom); + } + @override Offset get topLeft { return _topLeft ?? Offset.zero;