From 5f9c821b84c1980758db693c0121ce2df8a64d06 Mon Sep 17 00:00:00 2001 From: wenkaifan0720 Date: Thu, 2 Jul 2026 21:57:20 -0700 Subject: [PATCH] =?UTF-8?q?feat(input):=20=E2=8C=98F=20opens=20the=20host'?= =?UTF-8?q?s=20find=20bar=20(onFind=20callback)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The find-in-page backend already exists end-to-end (CefWebController.find/stopFind + onFindResult down to native CefFindHandler); the only gap was ⌘F, which the focused view consumed. Add an optional onFind VoidCallback to CefWebView: ⌘F invokes it (the host opens its own find bar, which drives the existing find API) and consumes the key; when null, ⌘F falls through to the page as before. Dart-only — no native/protocol change, so no host republish. Tests: ⌘F→onFind (consumed) and ⌘F-without-onFind (falls through to the page); suite 143 green. Co-Authored-By: Claude Fable 5 --- lib/src/cef_web_view.dart | 17 +++++++++++++++++ test/cef_web_view_test.dart | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) 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); + }); }