diff --git a/lib/src/cef_web_view.dart b/lib/src/cef_web_view.dart index 671ed23..c81f5d0 100644 --- a/lib/src/cef_web_view.dart +++ b/lib/src/cef_web_view.dart @@ -64,6 +64,7 @@ class CefWebView extends StatefulWidget { this.agentControl = false, this.profile, this.renderScale, + this.onFind, }) : assert(!(enableCdp && !agentControl && profile != null && profile != ''), 'enableCdp cannot be combined with a named profile: CDP-over-TCP ' 'exposes an unauthenticated localhost port that could read the ' @@ -89,6 +90,13 @@ class CefWebView extends StatefulWidget { /// Shown until the first frame arrives. Defaults to a dark blank box. final Widget? placeholder; + /// Invoked when the user presses ⌘F in the focused view. The view has no + /// find-bar UI of its own — a host that wants find-in-page provides this to + /// open its own bar (which then drives [CefWebController.find] / `stopFind` + /// and reads `onFindResult`). When null, ⌘F falls through to the page as an + /// ordinary key (a page can implement its own find). + final VoidCallback? onFind; + /// If non-null, the page may only navigate to URLs whose scheme is in this /// set (case-insensitive) — every other navigation, including the initial /// load, programmatic [CefWebController.navigate], in-page clicks, and @@ -527,6 +535,15 @@ class _CefWebViewState extends State _applyZoom(0); return KeyEventResult.handled; } + // ⌘F opens the host's find bar (if it wired one); else fall through to the + // page. Key-down only. + if (event is KeyDownEvent && + !keys.isShiftPressed && + k == LogicalKeyboardKey.keyF && + widget.onFind != null) { + widget.onFind!(); + return KeyEventResult.handled; + } } final mods = _cefModifiers(); diff --git a/test/cef_web_view_test.dart b/test/cef_web_view_test.dart index f3530cb..3082fb9 100644 --- a/test/cef_web_view_test.dart +++ b/test/cef_web_view_test.dart @@ -520,4 +520,41 @@ void main() { expect((zooms.single.arguments as Map)['level'], expectLevel); }); } + + testWidgets('⌘F invokes onFind (host opens its own find bar)', (tester) async { + var finds = 0; + final focus = FocusNode(); + addTearDown(focus.dispose); + await tester.pumpWidget(boxed(CefWebView( + url: 'about:blank', + focusNode: focus, + onFind: () => finds++, + ))); + await tester.pumpAndSettle(); + focus.requestFocus(); + await tester.pump(); + log.clear(); + await tester.sendKeyDownEvent(LogicalKeyboardKey.meta); + await tester.sendKeyEvent(LogicalKeyboardKey.keyF); + await tester.sendKeyUpEvent(LogicalKeyboardKey.meta); + await tester.pump(); + expect(finds, 1); + // ⌘F is consumed — the letter isn't forwarded to the page as text. + expect(callsTo('imeCommitText'), isEmpty); + }); + + testWidgets('⌘F with no onFind falls through to the page', (tester) async { + await focusedView(tester); // no onFind wired + log.clear(); + await tester.sendKeyDownEvent(LogicalKeyboardKey.meta); + await tester.sendKeyEvent(LogicalKeyboardKey.keyF); + await tester.sendKeyUpEvent(LogicalKeyboardKey.meta); + await tester.pump(); + // The ⌘F key reaches the page (a 'key' call for F), not swallowed. + final fKeys = callsTo('key').where((c) { + final wkc = (c.arguments as Map)['windowsKeyCode'] as int?; + return wkc == 0x46; // 'F' + }); + expect(fKeys, isNotEmpty); + }); }