mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
* fix: [FR] The text formatting toolbar should appear after the selection #1778 * chore: format code
This commit is contained in:
parent
77ff2e987a
commit
b89c69f294
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/flutter/overlay.dart';
|
import 'package:appflowy_editor/src/flutter/overlay.dart';
|
||||||
import 'package:appflowy_editor/src/infra/log.dart';
|
import 'package:appflowy_editor/src/infra/log.dart';
|
||||||
import 'package:appflowy_editor/src/service/context_menu/built_in_context_menu_item.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;
|
EditorState get editorState => widget.editorState;
|
||||||
|
|
||||||
|
// Toolbar
|
||||||
|
Timer? _toolbarTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -144,6 +149,7 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
|
|||||||
clearSelection();
|
clearSelection();
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
currentSelection.removeListener(_onSelectionChange);
|
currentSelection.removeListener(_onSelectionChange);
|
||||||
|
_clearToolbar();
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
@ -236,7 +242,7 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
|
|||||||
// clear cursor areas
|
// clear cursor areas
|
||||||
|
|
||||||
// hide toolbar
|
// hide toolbar
|
||||||
editorState.service.toolbarService?.hide();
|
// editorState.service.toolbarService?.hide();
|
||||||
|
|
||||||
// clear context menu
|
// clear context menu
|
||||||
_clearContextMenu();
|
_clearContextMenu();
|
||||||
@ -482,13 +488,8 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
|
|||||||
|
|
||||||
Overlay.of(context)?.insertAll(_selectionAreas);
|
Overlay.of(context)?.insertAll(_selectionAreas);
|
||||||
|
|
||||||
if (toolbarOffset != null && layerLink != null) {
|
// show toolbar
|
||||||
editorState.service.toolbarService?.showInOffset(
|
_showToolbarWithDelay(toolbarOffset, layerLink, alignment!);
|
||||||
toolbarOffset,
|
|
||||||
alignment!,
|
|
||||||
layerLink,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateCursorAreas(Position position) {
|
void _updateCursorAreas(Position position) {
|
||||||
@ -502,6 +503,7 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
|
|||||||
currentSelectedNodes = [node];
|
currentSelectedNodes = [node];
|
||||||
|
|
||||||
_showCursor(node, position);
|
_showCursor(node, position);
|
||||||
|
_clearToolbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showCursor(Node node, Position position) {
|
void _showCursor(Node node, Position position) {
|
||||||
@ -628,6 +630,40 @@ class _AppFlowySelectionState extends State<AppFlowySelection>
|
|||||||
_scrollUpOrDownIfNeeded();
|
_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}) {
|
void _showDebugLayerIfNeeded({Offset? offset}) {
|
||||||
// remove false to show debug overlay.
|
// remove false to show debug overlay.
|
||||||
// if (kDebugMode && false) {
|
// if (kDebugMode && false) {
|
||||||
|
@ -7,7 +7,11 @@ import 'package:appflowy_editor/src/extensions/object_extensions.dart';
|
|||||||
|
|
||||||
abstract class AppFlowyToolbarService {
|
abstract class AppFlowyToolbarService {
|
||||||
/// Show the toolbar widget beside the offset.
|
/// 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.
|
/// Hide the toolbar widget.
|
||||||
void hide();
|
void hide();
|
||||||
@ -45,7 +49,11 @@ class _FlowyToolbarState extends State<FlowyToolbar>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void showInOffset(Offset offset, Alignment alignment, LayerLink layerLink) {
|
void showInOffset(
|
||||||
|
Offset offset,
|
||||||
|
Alignment alignment,
|
||||||
|
LayerLink layerLink,
|
||||||
|
) {
|
||||||
hide();
|
hide();
|
||||||
final items = _filterItems(toolbarItems);
|
final items = _filterItems(toolbarItems);
|
||||||
if (items.isEmpty) {
|
if (items.isEmpty) {
|
||||||
|
@ -25,6 +25,7 @@ void main() async {
|
|||||||
|
|
||||||
await editor.updateSelection(h1);
|
await editor.updateSelection(h1);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
|
|
||||||
final h1Button = find.byWidgetPredicate((widget) {
|
final h1Button = find.byWidgetPredicate((widget) {
|
||||||
@ -52,6 +53,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: singleLineText.length));
|
end: Position(path: [0], offset: singleLineText.length));
|
||||||
|
|
||||||
await editor.updateSelection(h2);
|
await editor.updateSelection(h2);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
|
|
||||||
final h2Button = find.byWidgetPredicate((widget) {
|
final h2Button = find.byWidgetPredicate((widget) {
|
||||||
@ -77,6 +79,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: singleLineText.length));
|
end: Position(path: [0], offset: singleLineText.length));
|
||||||
|
|
||||||
await editor.updateSelection(h3);
|
await editor.updateSelection(h3);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
|
|
||||||
final h3Button = find.byWidgetPredicate((widget) {
|
final h3Button = find.byWidgetPredicate((widget) {
|
||||||
@ -104,6 +107,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: singleLineText.length));
|
end: Position(path: [0], offset: singleLineText.length));
|
||||||
|
|
||||||
await editor.updateSelection(underline);
|
await editor.updateSelection(underline);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
final underlineButton = find.byWidgetPredicate((widget) {
|
final underlineButton = find.byWidgetPredicate((widget) {
|
||||||
if (widget is ToolbarItemWidget) {
|
if (widget is ToolbarItemWidget) {
|
||||||
@ -132,6 +136,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: singleLineText.length));
|
end: Position(path: [0], offset: singleLineText.length));
|
||||||
|
|
||||||
await editor.updateSelection(bold);
|
await editor.updateSelection(bold);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
final boldButton = find.byWidgetPredicate((widget) {
|
final boldButton = find.byWidgetPredicate((widget) {
|
||||||
if (widget is ToolbarItemWidget) {
|
if (widget is ToolbarItemWidget) {
|
||||||
@ -159,6 +164,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: singleLineText.length));
|
end: Position(path: [0], offset: singleLineText.length));
|
||||||
|
|
||||||
await editor.updateSelection(italic);
|
await editor.updateSelection(italic);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
final italicButton = find.byWidgetPredicate((widget) {
|
final italicButton = find.byWidgetPredicate((widget) {
|
||||||
if (widget is ToolbarItemWidget) {
|
if (widget is ToolbarItemWidget) {
|
||||||
@ -187,6 +193,7 @@ void main() async {
|
|||||||
|
|
||||||
await editor.updateSelection(strikeThrough);
|
await editor.updateSelection(strikeThrough);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
final strikeThroughButton = find.byWidgetPredicate((widget) {
|
final strikeThroughButton = find.byWidgetPredicate((widget) {
|
||||||
if (widget is ToolbarItemWidget) {
|
if (widget is ToolbarItemWidget) {
|
||||||
@ -214,6 +221,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: singleLineText.length));
|
end: Position(path: [0], offset: singleLineText.length));
|
||||||
|
|
||||||
await editor.updateSelection(code);
|
await editor.updateSelection(code);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
final codeButton = find.byWidgetPredicate((widget) {
|
final codeButton = find.byWidgetPredicate((widget) {
|
||||||
if (widget is ToolbarItemWidget) {
|
if (widget is ToolbarItemWidget) {
|
||||||
@ -250,6 +258,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: singleLineText.length));
|
end: Position(path: [0], offset: singleLineText.length));
|
||||||
|
|
||||||
await editor.updateSelection(quote);
|
await editor.updateSelection(quote);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
final quoteButton = find.byWidgetPredicate((widget) {
|
final quoteButton = find.byWidgetPredicate((widget) {
|
||||||
if (widget is ToolbarItemWidget) {
|
if (widget is ToolbarItemWidget) {
|
||||||
@ -276,6 +285,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: singleLineText.length));
|
end: Position(path: [0], offset: singleLineText.length));
|
||||||
|
|
||||||
await editor.updateSelection(bulletList);
|
await editor.updateSelection(bulletList);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
final bulletListButton = find.byWidgetPredicate((widget) {
|
final bulletListButton = find.byWidgetPredicate((widget) {
|
||||||
if (widget is ToolbarItemWidget) {
|
if (widget is ToolbarItemWidget) {
|
||||||
@ -306,6 +316,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: singleLineText.length));
|
end: Position(path: [0], offset: singleLineText.length));
|
||||||
|
|
||||||
await editor.updateSelection(selection);
|
await editor.updateSelection(selection);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
final highlightButton = find.byWidgetPredicate((widget) {
|
final highlightButton = find.byWidgetPredicate((widget) {
|
||||||
if (widget is ToolbarItemWidget) {
|
if (widget is ToolbarItemWidget) {
|
||||||
@ -343,6 +354,7 @@ void main() async {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await editor.updateSelection(selection);
|
await editor.updateSelection(selection);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
final colorButton = find.byWidgetPredicate((widget) {
|
final colorButton = find.byWidgetPredicate((widget) {
|
||||||
if (widget is ToolbarItemWidget) {
|
if (widget is ToolbarItemWidget) {
|
||||||
|
@ -245,6 +245,7 @@ Future<void> _testLinkMenuInSingleTextSelection(WidgetTester tester) async {
|
|||||||
await editor.updateSelection(selection);
|
await editor.updateSelection(selection);
|
||||||
|
|
||||||
// show toolbar
|
// show toolbar
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
|
|
||||||
// trigger the link menu
|
// trigger the link menu
|
||||||
|
@ -24,6 +24,7 @@ void main() async {
|
|||||||
);
|
);
|
||||||
await editor.updateSelection(selection);
|
await editor.updateSelection(selection);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
|
|
||||||
// no link item
|
// no link item
|
||||||
@ -72,6 +73,7 @@ void main() async {
|
|||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [0], startOffset: 0, endOffset: text.length),
|
Selection.single(path: [0], startOffset: 0, endOffset: text.length),
|
||||||
);
|
);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
|
|
||||||
void testHighlight(bool expectedValue) {
|
void testHighlight(bool expectedValue) {
|
||||||
@ -138,6 +140,7 @@ void main() async {
|
|||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [0], startOffset: 0, endOffset: text.length),
|
Selection.single(path: [0], startOffset: 0, endOffset: text.length),
|
||||||
);
|
);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
var itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.h1');
|
var itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.h1');
|
||||||
expect(itemWidget.isHighlight, true);
|
expect(itemWidget.isHighlight, true);
|
||||||
@ -145,6 +148,7 @@ void main() async {
|
|||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [1], startOffset: 0, endOffset: text.length),
|
Selection.single(path: [1], startOffset: 0, endOffset: text.length),
|
||||||
);
|
);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.quote');
|
itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.quote');
|
||||||
expect(itemWidget.isHighlight, true);
|
expect(itemWidget.isHighlight, true);
|
||||||
@ -152,6 +156,7 @@ void main() async {
|
|||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [2], startOffset: 0, endOffset: text.length),
|
Selection.single(path: [2], startOffset: 0, endOffset: text.length),
|
||||||
);
|
);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.bulleted_list');
|
itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.bulleted_list');
|
||||||
expect(itemWidget.isHighlight, true);
|
expect(itemWidget.isHighlight, true);
|
||||||
@ -183,6 +188,7 @@ void main() async {
|
|||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [2], startOffset: text.length, endOffset: 0),
|
Selection.single(path: [2], startOffset: text.length, endOffset: 0),
|
||||||
);
|
);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
expect(
|
expect(
|
||||||
_itemWidgetForId(tester, 'appflowy.toolbar.h1').isHighlight,
|
_itemWidgetForId(tester, 'appflowy.toolbar.h1').isHighlight,
|
||||||
@ -199,6 +205,7 @@ void main() async {
|
|||||||
end: Position(path: [1], offset: 0),
|
end: Position(path: [1], offset: 0),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
expect(
|
expect(
|
||||||
_itemWidgetForId(tester, 'appflowy.toolbar.bold').isHighlight,
|
_itemWidgetForId(tester, 'appflowy.toolbar.bold').isHighlight,
|
||||||
@ -211,6 +218,7 @@ void main() async {
|
|||||||
end: Position(path: [0], offset: 0),
|
end: Position(path: [0], offset: 0),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
expect(find.byType(ToolbarWidget), findsOneWidget);
|
expect(find.byType(ToolbarWidget), findsOneWidget);
|
||||||
expect(
|
expect(
|
||||||
_itemWidgetForId(tester, 'appflowy.toolbar.bold').isHighlight,
|
_itemWidgetForId(tester, 'appflowy.toolbar.bold').isHighlight,
|
||||||
|
Loading…
Reference in New Issue
Block a user