Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable customizing editor's focus #198

Merged
merged 1 commit into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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