* fix: [FR] The text formatting toolbar should appear after the selection #1778

* chore: format code
This commit is contained in:
Lucas.Xu 2023-03-09 13:32:49 +07:00 committed by GitHub
parent 77ff2e987a
commit b89c69f294
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 10 deletions

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:appflowy_editor/src/flutter/overlay.dart';
import 'package:appflowy_editor/src/infra/log.dart';
import 'package:appflowy_editor/src/service/context_menu/built_in_context_menu_item.dart';
@ -121,6 +123,9 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
EditorState get editorState => widget.editorState;
// Toolbar
Timer? _toolbarTimer;
@override
void initState() {
super.initState();
@ -144,6 +149,7 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
clearSelection();
WidgetsBinding.instance.removeObserver(this);
currentSelection.removeListener(_onSelectionChange);
_clearToolbar();
super.dispose();
}
@ -236,7 +242,7 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
// clear cursor areas
// hide toolbar
editorState.service.toolbarService?.hide();
// editorState.service.toolbarService?.hide();
// clear context menu
_clearContextMenu();
@ -482,13 +488,8 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
Overlay.of(context)?.insertAll(_selectionAreas);
if (toolbarOffset != null && layerLink != null) {
editorState.service.toolbarService?.showInOffset(
toolbarOffset,
alignment!,
layerLink,
);
}
// show toolbar
_showToolbarWithDelay(toolbarOffset, layerLink, alignment!);
}
void _updateCursorAreas(Position position) {
@ -502,6 +503,7 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
currentSelectedNodes = [node];
_showCursor(node, position);
_clearToolbar();
}
void _showCursor(Node node, Position position) {
@ -628,6 +630,40 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
_scrollUpOrDownIfNeeded();
}
void _showToolbarWithDelay(
Offset? toolbarOffset,
LayerLink? layerLink,
Alignment alignment, {
Duration delay = const Duration(milliseconds: 400),
}) {
if (toolbarOffset == null && layerLink == null) {
_clearToolbar();
return;
}
if (_toolbarTimer?.isActive ?? false) {
_toolbarTimer?.cancel();
}
_toolbarTimer = Timer(
delay,
() {
if (toolbarOffset != null && layerLink != null) {
editorState.service.toolbarService?.showInOffset(
toolbarOffset,
alignment,
layerLink,
);
}
},
);
}
void _clearToolbar() {
editorState.service.toolbarService?.hide();
if (_toolbarTimer?.isActive ?? false) {
_toolbarTimer?.cancel();
}
}
void _showDebugLayerIfNeeded({Offset? offset}) {
// remove false to show debug overlay.
// if (kDebugMode && false) {

View File

@ -7,7 +7,11 @@ import 'package:appflowy_editor/src/extensions/object_extensions.dart';
abstract class AppFlowyToolbarService {
/// Show the toolbar widget beside the offset.
void showInOffset(Offset offset, Alignment alignment, LayerLink layerLink);
void showInOffset(
Offset offset,
Alignment alignment,
LayerLink layerLink,
);
/// Hide the toolbar widget.
void hide();
@ -45,7 +49,11 @@ class _FlowyToolbarState extends State<FlowyToolbar>
}
@override
void showInOffset(Offset offset, Alignment alignment, LayerLink layerLink) {
void showInOffset(
Offset offset,
Alignment alignment,
LayerLink layerLink,
) {
hide();
final items = _filterItems(toolbarItems);
if (items.isEmpty) {

View File

@ -25,6 +25,7 @@ void main() async {
await editor.updateSelection(h1);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final h1Button = find.byWidgetPredicate((widget) {
@ -52,6 +53,7 @@ void main() async {
end: Position(path: [0], offset: singleLineText.length));
await editor.updateSelection(h2);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final h2Button = find.byWidgetPredicate((widget) {
@ -77,6 +79,7 @@ void main() async {
end: Position(path: [0], offset: singleLineText.length));
await editor.updateSelection(h3);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final h3Button = find.byWidgetPredicate((widget) {
@ -104,6 +107,7 @@ void main() async {
end: Position(path: [0], offset: singleLineText.length));
await editor.updateSelection(underline);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final underlineButton = find.byWidgetPredicate((widget) {
if (widget is ToolbarItemWidget) {
@ -132,6 +136,7 @@ void main() async {
end: Position(path: [0], offset: singleLineText.length));
await editor.updateSelection(bold);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final boldButton = find.byWidgetPredicate((widget) {
if (widget is ToolbarItemWidget) {
@ -159,6 +164,7 @@ void main() async {
end: Position(path: [0], offset: singleLineText.length));
await editor.updateSelection(italic);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final italicButton = find.byWidgetPredicate((widget) {
if (widget is ToolbarItemWidget) {
@ -187,6 +193,7 @@ void main() async {
await editor.updateSelection(strikeThrough);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final strikeThroughButton = find.byWidgetPredicate((widget) {
if (widget is ToolbarItemWidget) {
@ -214,6 +221,7 @@ void main() async {
end: Position(path: [0], offset: singleLineText.length));
await editor.updateSelection(code);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final codeButton = find.byWidgetPredicate((widget) {
if (widget is ToolbarItemWidget) {
@ -250,6 +258,7 @@ void main() async {
end: Position(path: [0], offset: singleLineText.length));
await editor.updateSelection(quote);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final quoteButton = find.byWidgetPredicate((widget) {
if (widget is ToolbarItemWidget) {
@ -276,6 +285,7 @@ void main() async {
end: Position(path: [0], offset: singleLineText.length));
await editor.updateSelection(bulletList);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final bulletListButton = find.byWidgetPredicate((widget) {
if (widget is ToolbarItemWidget) {
@ -306,6 +316,7 @@ void main() async {
end: Position(path: [0], offset: singleLineText.length));
await editor.updateSelection(selection);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final highlightButton = find.byWidgetPredicate((widget) {
if (widget is ToolbarItemWidget) {
@ -343,6 +354,7 @@ void main() async {
);
await editor.updateSelection(selection);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
final colorButton = find.byWidgetPredicate((widget) {
if (widget is ToolbarItemWidget) {

View File

@ -245,6 +245,7 @@ Future<void> _testLinkMenuInSingleTextSelection(WidgetTester tester) async {
await editor.updateSelection(selection);
// show toolbar
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
// trigger the link menu

View File

@ -24,6 +24,7 @@ void main() async {
);
await editor.updateSelection(selection);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
// no link item
@ -72,6 +73,7 @@ void main() async {
await editor.updateSelection(
Selection.single(path: [0], startOffset: 0, endOffset: text.length),
);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
void testHighlight(bool expectedValue) {
@ -138,6 +140,7 @@ void main() async {
await editor.updateSelection(
Selection.single(path: [0], startOffset: 0, endOffset: text.length),
);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
var itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.h1');
expect(itemWidget.isHighlight, true);
@ -145,6 +148,7 @@ void main() async {
await editor.updateSelection(
Selection.single(path: [1], startOffset: 0, endOffset: text.length),
);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.quote');
expect(itemWidget.isHighlight, true);
@ -152,6 +156,7 @@ void main() async {
await editor.updateSelection(
Selection.single(path: [2], startOffset: 0, endOffset: text.length),
);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.bulleted_list');
expect(itemWidget.isHighlight, true);
@ -183,6 +188,7 @@ void main() async {
await editor.updateSelection(
Selection.single(path: [2], startOffset: text.length, endOffset: 0),
);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
expect(
_itemWidgetForId(tester, 'appflowy.toolbar.h1').isHighlight,
@ -199,6 +205,7 @@ void main() async {
end: Position(path: [1], offset: 0),
),
);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
expect(
_itemWidgetForId(tester, 'appflowy.toolbar.bold').isHighlight,
@ -211,6 +218,7 @@ void main() async {
end: Position(path: [0], offset: 0),
),
);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.byType(ToolbarWidget), findsOneWidget);
expect(
_itemWidgetForId(tester, 'appflowy.toolbar.bold').isHighlight,