diff --git a/packages/fleather/lib/src/rendering/editor.dart b/packages/fleather/lib/src/rendering/editor.dart index 0966a10f..120b3401 100644 --- a/packages/fleather/lib/src/rendering/editor.dart +++ b/packages/fleather/lib/src/rendering/editor.dart @@ -186,7 +186,7 @@ class RenderEditor extends RenderEditableContainerBox markNeedsSemanticsUpdate(); } - Offset get _paintOffset => Offset(0.0, -(offset?.pixels ?? 0.0)); + Offset get paintOffset => Offset(0.0, -(offset?.pixels ?? 0.0)); ViewportOffset? get offset => _offset; ViewportOffset? _offset; @@ -633,7 +633,7 @@ class RenderEditor extends RenderEditableContainerBox _paintFloatingCursor(context, offset); } defaultPaint(context, offset); - _updateSelectionExtentsVisibility(offset + _paintOffset); + _updateSelectionExtentsVisibility(offset + paintOffset); _paintHandleLayers(context, getEndpointsForSelection(selection)); if (hasFocus && diff --git a/packages/fleather/lib/src/widgets/editor.dart b/packages/fleather/lib/src/widgets/editor.dart index 0219ba44..c34cb2c1 100644 --- a/packages/fleather/lib/src/widgets/editor.dart +++ b/packages/fleather/lib/src/widgets/editor.dart @@ -2025,21 +2025,24 @@ class RawEditorState extends EditorState final smallestLineHeight = math.min(baseLineHeight, extentLineHeight); return _textSelectionToolbarAnchorsFromSelection( - renderEditor: renderEditor, startGlyphHeight: smallestLineHeight, endGlyphHeight: smallestLineHeight, selectionEndpoints: endpoints); } TextSelectionToolbarAnchors _textSelectionToolbarAnchorsFromSelection({ - required RenderEditor renderEditor, required double startGlyphHeight, required double endGlyphHeight, required List selectionEndpoints, }) { + // If editor is scrollable, the editing region is only the viewport + // otherwise use editor as editing region + final viewport = RenderAbstractViewport.maybeOf(renderEditor); + final visualSizeRenderer = (viewport ?? renderEditor) as RenderBox; final Rect editingRegion = Rect.fromPoints( - renderEditor.localToGlobal(Offset.zero), - renderEditor.localToGlobal(renderEditor.size.bottomRight(Offset.zero)), + visualSizeRenderer.localToGlobal(Offset.zero), + visualSizeRenderer + .localToGlobal(visualSizeRenderer.size.bottomRight(Offset.zero)), ); if (editingRegion.left.isNaN || @@ -2056,12 +2059,21 @@ class RawEditorState extends EditorState final Rect selectionRect = Rect.fromLTRB( isMultiline ? editingRegion.left - : editingRegion.left + selectionEndpoints.first.point.dx, - editingRegion.top + selectionEndpoints.first.point.dy - startGlyphHeight, + : editingRegion.left + + selectionEndpoints.first.point.dx + + renderEditor.paintOffset.dx, + editingRegion.top + + selectionEndpoints.first.point.dy + + renderEditor.paintOffset.dy - + startGlyphHeight, isMultiline ? editingRegion.right - : editingRegion.left + selectionEndpoints.last.point.dx, - editingRegion.top + selectionEndpoints.last.point.dy, + : editingRegion.left + + selectionEndpoints.last.point.dx + + renderEditor.paintOffset.dx, + editingRegion.top + + selectionEndpoints.last.point.dy + + renderEditor.paintOffset.dy, ); return TextSelectionToolbarAnchors( diff --git a/packages/fleather/test/widgets/editor_test.dart b/packages/fleather/test/widgets/editor_test.dart index 9cd055f3..64d0b51b 100644 --- a/packages/fleather/test/widgets/editor_test.dart +++ b/packages/fleather/test/widgets/editor_test.dart @@ -106,6 +106,48 @@ void main() { tester.view.viewInsets = FakeViewPadding.zero; }); + testWidgets('Keep selectiontoolbar with editor bounds', (tester) async { + final delta = Delta(); + for (int i = 0; i < 30; i++) { + delta.insert('Test\n'); + } + final scrollController = ScrollController(); + final controller = + FleatherController(document: ParchmentDocument.fromDelta(delta)); + final editor = MaterialApp( + home: Scaffold( + body: Column( + children: [ + const SizedBox(width: 300, height: 150), + Expanded( + child: FleatherEditor( + controller: controller, + scrollController: scrollController, + ), + ), + ], + ), + ), + ); + await tester.pumpWidget(editor); + // Double tap to show toolbar + await tester.tapAt( + tester.getTopLeft(find.byType(RawEditor)) + const Offset(1, 1)); + await tester.tapAt( + tester.getTopLeft(find.byType(RawEditor)) + const Offset(1, 1)); + await tester.pumpAndSettle(); + expect(find.byType(TextSelectionToolbar), findsOneWidget); + // Scroll extent is > 500, toolbar position should be around -400 if not + // capped + scrollController.jumpTo(scrollController.position.maxScrollExtent); + await tester.pumpAndSettle(); + final renderToolbarTextButton = + tester.renderObject(find.byType(TextSelectionToolbarTextButton).first) + as RenderBox; + final toolbarTop = renderToolbarTextButton.localToGlobal(Offset.zero); + expect(toolbarTop.dy, greaterThan(90)); + }); + testWidgets('allows merging attribute theme data', (tester) async { var delta = Delta() ..insert(