Skip to content

Commit

Permalink
feat: enable customizing editor's focus
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasXu0 committed Jun 15, 2023
1 parent 23bc6d2 commit ed6b02f
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class FullScreenOverlayEntry {
this.right,
required this.builder,
this.tapToDismiss = true,
this.dismissCallback,
});

final double? top;
Expand All @@ -17,6 +18,7 @@ class FullScreenOverlayEntry {
final double? right;
final WidgetBuilder builder;
final bool tapToDismiss;
final VoidCallback? dismissCallback;

OverlayEntry? _entry;

Expand All @@ -34,6 +36,7 @@ class FullScreenOverlayEntry {
// remove this from the overlay when tapped the opaque layer
_entry?.remove();
_entry = null;
dismissCallback?.call();
}
},
child: Stack(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ void showImageMenu(
editorState.insertImageNode(text);
menuService.dismiss();
imageMenuEntry.remove();
keepEditorFocusNotifier.value -= 1;
}

keepEditorFocusNotifier.value += 1;
imageMenuEntry = FullScreenOverlayEntry(
left: left,
top: top,
bottom: bottom,
dismissCallback: () => keepEditorFocusNotifier.value -= 1,
builder: (context) => UploadImageMenu(
backgroundColor: menuService.style.selectionMenuBackgroundColor,
headerColor: menuService.style.selectionMenuItemTextColor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>

focusNode = widget.focusNode ?? FocusNode(debugLabel: 'keyboard service');
focusNode.addListener(_onFocusChanged);

keepEditorFocusNotifier.addListener(_onKeepEditorFocusChanged);
}

@override
Expand All @@ -85,6 +87,7 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
if (widget.focusNode == null) {
focusNode.dispose();
}
keepEditorFocusNotifier.removeListener(_onKeepEditorFocusChanged);
super.dispose();
}

Expand Down Expand Up @@ -232,6 +235,9 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>

// clear the selection when the focus is lost.
if (PlatformExtension.isDesktop && !focusNode.hasFocus) {
if (keepEditorFocusNotifier.value > 0) {
return;
}
final children =
WidgetsBinding.instance.focusManager.primaryFocus?.children;
if (children != null && !children.contains(focusNode)) {
Expand All @@ -240,6 +246,16 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
}
}

void _onKeepEditorFocusChanged() {
Log.editor.debug(
'keyboard service - on keep editor focus changed: ${keepEditorFocusNotifier.value}}',
);

if (keepEditorFocusNotifier.value == 0) {
focusNode.requestFocus();
}
}

// only verify on macOS.
void _updateCaretPosition(Selection? selection) {
if (selection == null || !selection.isCollapsed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,17 @@ void showLinkMenu(
OverlayEntry? overlay;

void dismissOverlay() {
keepEditorFocusNotifier.value -= 1;
overlay?.remove();
overlay = null;
editorState.service.keyboardService?.enable();
}

keepEditorFocusNotifier.value += 1;
overlay = FullScreenOverlayEntry(
top: top,
bottom: bottom,
left: left,
dismissCallback: () => keepEditorFocusNotifier.value -= 1,
builder: (context) {
return LinkMenu(
linkText: linkText,
Expand Down Expand Up @@ -114,5 +116,4 @@ void showLinkMenu(
).build();

Overlay.of(context).insert(overlay!);
editorState.service.keyboardService?.disable();
}
6 changes: 1 addition & 5 deletions lib/src/render/image/image_upload_widget.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import 'package:appflowy_editor/src/core/document/node.dart';
import 'package:appflowy_editor/src/editor_state.dart';
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart';
import 'package:appflowy_editor/src/render/style/editor_style.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';

// void showImageMenu(
Expand Down
2 changes: 2 additions & 0 deletions lib/src/render/selection_menu/selection_menu_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {

_showingItems = widget.items;

keepEditorFocusNotifier.value += 1;
WidgetsBinding.instance.addPostFrameCallback((_) {
_focusNode.requestFocus();
});
Expand All @@ -262,6 +263,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
@override
void dispose() {
_focusNode.dispose();
keepEditorFocusNotifier.value -= 1;

super.dispose();
}
Expand Down
9 changes: 9 additions & 0 deletions lib/src/service/editor_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import 'package:appflowy_editor/src/flutter/overlay.dart';
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
import 'package:provider/provider.dart';

// workaround for the issue:
// the popover will grab the focus even if it's inside the editor
// setup a global value to indicate whether the focus should be grabbed
// increase the value when the popover is opened
// decrease the value when the popover is closed
// only grab the focus when the value is 0
// the operation must be paired
ValueNotifier<int> keepEditorFocusNotifier = ValueNotifier(0);

class AppFlowyEditor extends StatefulWidget {
@Deprecated('Use AppFlowyEditor.custom or AppFlowyEditor.standard instead')
const AppFlowyEditor({
Expand Down
14 changes: 11 additions & 3 deletions test/render/image/image_upload_widget_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/src/editor/block_component/image_block_component/image_upload_widget.dart';
import '../../new/infra/testable_editor.dart';

void main() {
Expand All @@ -9,9 +10,8 @@ void main() {
..addParagraph(initialText: 'Welcome to AppFlowy');
await editor.startTesting();

await editor.updateSelection(
Selection.single(path: [0], startOffset: 19),
);
final selection = Selection.single(path: [0], startOffset: 19);
await editor.updateSelection(selection);

await editor.pressKey(character: '/');
await tester.pumpAndSettle();
Expand All @@ -23,6 +23,14 @@ void main() {

await tester.tap(imageMenuItemFinder);
await tester.pumpAndSettle();
expect(find.byType(UploadImageMenu), findsOneWidget);

expect(editor.selection, selection);

await tester.tapAt(Offset.zero);
await tester.pumpAndSettle();
expect(find.byType(UploadImageMenu), findsNothing);
expect(editor.selection, selection);

await editor.dispose();
});
Expand Down

0 comments on commit ed6b02f

Please sign in to comment.