From 863a1c5d20e6cb64ce3e8965b9333f9f3df49121 Mon Sep 17 00:00:00 2001 From: keiya01 Date: Sun, 22 May 2022 11:32:53 +0900 Subject: [PATCH 1/5] feat: add find-in-page, closes #585 --- .changes/add-find-in-page.md | 5 +++ examples/find_in_page.rs | 75 ++++++++++++++++++++++++++++++++++++ src/webview/android/mod.rs | 8 +++- src/webview/mod.rs | 27 +++++++++++++ src/webview/webkitgtk/mod.rs | 43 ++++++++++++++++++++- src/webview/webview2/mod.rs | 8 +++- src/webview/wkwebview/mod.rs | 19 ++++++++- 7 files changed, 180 insertions(+), 5 deletions(-) create mode 100644 .changes/add-find-in-page.md create mode 100644 examples/find_in_page.rs diff --git a/.changes/add-find-in-page.md b/.changes/add-find-in-page.md new file mode 100644 index 000000000..a37075f92 --- /dev/null +++ b/.changes/add-find-in-page.md @@ -0,0 +1,5 @@ +--- +"wry": minor +--- + +Add find-in-page feature. diff --git a/examples/find_in_page.rs b/examples/find_in_page.rs new file mode 100644 index 000000000..bb77bc73c --- /dev/null +++ b/examples/find_in_page.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use wry::webview::FindInPageOption; + +fn main() -> wry::Result<()> { + use wry::{ + application::{ + event::{Event, StartCause, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, + }, + webview::WebViewBuilder, + }; + + #[derive(Debug)] + enum UserEvent { + FindInPage(String), + } + + let event_loop: EventLoop = EventLoop::with_user_event(); + let proxy = event_loop.create_proxy(); + let window = WindowBuilder::new() + .with_title("Hello World") + .build(&event_loop)?; + let webview = WebViewBuilder::new(window)? + .with_html( + r#" + + +

Tauri is a toolkit that helps developers make applications for the major desktop platforms - using virtually any frontend framework in existence. The core is built with Rust, and the CLI leverages Node.js making Tauri a genuinely polyglot approach to creating and maintaining great apps. If you want to know more about the technical details, then please visit the Introduction. If you want to know more about this project's philosophy - then keep reading.

+ +"#, + )? + .with_ipc_handler(move |_, text: String| { + proxy + .send_event(UserEvent::FindInPage(text.clone())) + .unwrap(); + }); + + #[cfg(debug_assertions)] + let webview = webview.with_devtools(true); + + let webview = webview.build()?; + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + + match event { + Event::NewEvents(StartCause::Init) => println!("Wry has started!"), + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => *control_flow = ControlFlow::Exit, + Event::UserEvent(UserEvent::FindInPage(text)) => { + webview.find_in_page( + text, + FindInPageOption { + case_sensitive: true, + max_match_count: 100, + ..FindInPageOption::default() + }, + |found| println!("Is found: {}", found), + ); + } + _ => (), + } + }); +} diff --git a/src/webview/android/mod.rs b/src/webview/android/mod.rs index 10d131621..f6a87c979 100644 --- a/src/webview/android/mod.rs +++ b/src/webview/android/mod.rs @@ -1,6 +1,6 @@ use std::{collections::HashSet, ffi::c_void, ptr::null_mut, rc::Rc, sync::RwLock}; -use crate::{application::window::Window, Result}; +use crate::{application::window::Window, webview::FindInPageOption, Result}; use super::{WebContext, WebViewAttributes}; @@ -120,6 +120,12 @@ impl InnerWebView { } pub fn zoom(&self, scale_factor: f64) {} + + pub fn find_in_page(&self, _string: String, _option: FindInPageOption, _f: F) + where + F: Fn(bool) + 'static, + { + } } pub struct UnsafeIpc(*mut c_void); diff --git a/src/webview/mod.rs b/src/webview/mod.rs index ce24d3e2f..d9c7ec4b0 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -517,6 +517,20 @@ impl WebView { self.webview.zoom(scale_factor); } + /// Searches for the specified string in the WebView content. + /// + /// ## Platform-specific: + /// + /// - **Windows / Android**: Not supported. + /// - **macOS**: available on macOS 10.15.4+ only. + /// - **iOS**: available on iOS 13.4+ only. + pub fn find_in_page(&self, string: String, option: FindInPageOption, f: F) + where + F: Fn(bool) + 'static, + { + self.webview.find_in_page(string, option, f); + } + #[cfg(target_os = "android")] pub fn run(self, env: JNIEnv, jclass: JClass, jobject: JObject) -> jobject { self.webview.run(env, jclass, jobject).unwrap() @@ -528,6 +542,19 @@ impl WebView { } } +/// A configuration for `find_in_page`. +#[derive(Default)] +pub struct FindInPageOption { + /// Indicate the search direction. + pub backwards: bool, + /// Match the search string in a case-sensitive. + pub case_sensitive: bool, + /// Wrap around to the other side of the page. + pub wraps: bool, + /// For Linux only, Maximum number of search strings to highlight. + pub max_match_count: u32, +} + /// An event enumeration sent to [`FileDropHandler`]. #[non_exhaustive] #[derive(Debug, Serialize, Clone)] diff --git a/src/webview/webkitgtk/mod.rs b/src/webview/webkitgtk/mod.rs index d74087afa..b1d0e1965 100644 --- a/src/webview/webkitgtk/mod.rs +++ b/src/webview/webkitgtk/mod.rs @@ -18,7 +18,8 @@ use gio::Cancellable; use glib::signal::Inhibit; use gtk::prelude::*; use webkit2gtk::{ - traits::*, NavigationPolicyDecision, PolicyDecisionType, UserContentInjectedFrames, UserScript, + traits::*, FindController, FindControllerBuilder, FindControllerExt, FindOptions, + NavigationPolicyDecision, PolicyDecisionType, UserContentInjectedFrames, UserScript, UserScriptInjectionTime, WebView, WebViewBuilder, }; use webkit2gtk_sys::{ @@ -31,7 +32,7 @@ pub use web_context::WebContextImpl; use crate::{ application::{platform::unix::*, window::Window}, - webview::{web_context::WebContext, WebViewAttributes}, + webview::{web_context::WebContext, FindInPageOption, WebViewAttributes}, Error, Result, }; @@ -42,6 +43,7 @@ pub struct InnerWebView { pub(crate) webview: Rc, #[cfg(any(debug_assertions, feature = "devtools"))] is_inspector_open: Arc, + find_controller: Rc, } impl InnerWebView { @@ -294,10 +296,17 @@ impl InnerWebView { is_inspector_open }; + let find_controller = { + let builder = FindControllerBuilder::new(); + let controller = builder.web_view(&*webview).build(); + Rc::new(controller) + }; + let w = Self { webview, #[cfg(any(debug_assertions, feature = "devtools"))] is_inspector_open, + find_controller, }; // Initialize message handler @@ -390,6 +399,36 @@ impl InnerWebView { pub fn zoom(&self, scale_factor: f64) { WebViewExt::set_zoom_level(&*self.webview, scale_factor); } + + pub fn find_in_page(&self, string: String, option: FindInPageOption, f: F) + where + F: Fn(bool) + 'static, + { + let mut flags = FindOptions::NONE; + if option.backwards { + flags |= FindOptions::BACKWARDS; + } + if !option.case_sensitive { + flags |= FindOptions::CASE_INSENSITIVE; + } + if option.wraps { + flags |= FindOptions::WRAP_AROUND; + } + + self + .find_controller + .search(&string, flags.bits(), option.max_match_count); + + let handler = Rc::new(Box::new(f)); + let found = handler.clone(); + + self + .find_controller + .connect_failed_to_find_text(move |_| (*handler)(false)); + self + .find_controller + .connect_found_text(move |_, _| (*found)(true)); + } } pub fn platform_webview_version() -> Result { diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index 8418c5630..94a01a508 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -5,7 +5,7 @@ mod file_drop; use crate::{ - webview::{WebContext, WebViewAttributes}, + webview::{FindInPageOption, WebContext, WebViewAttributes}, Error, Result, }; @@ -621,6 +621,12 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ pub fn zoom(&self, scale_factor: f64) { let _ = unsafe { self.controller.SetZoomFactor(scale_factor) }; } + + pub fn find_in_page(&self, _string: String, _option: FindInPageOption, _f: F) + where + F: Fn(bool) + 'static, + { + } } pub fn platform_webview_version() -> Result { diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index c1e21ce31..b3e210450 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -6,6 +6,7 @@ mod file_drop; mod web_context; +use block::ConcreteBlock; pub use web_context::WebContextImpl; #[cfg(target_os = "macos")] @@ -43,7 +44,7 @@ use crate::{ dpi::{LogicalSize, PhysicalSize}, window::Window, }, - webview::{FileDropEvent, WebContext, WebViewAttributes}, + webview::{FileDropEvent, FindInPageOption, WebContext, WebViewAttributes}, Result, }; @@ -608,6 +609,22 @@ r#"Object.defineProperty(window, 'ipc', { let _: () = msg_send![self.webview, setPageZoom: scale_factor]; } } + + pub fn find_in_page(&self, string: String, option: FindInPageOption, f: F) + where + F: Fn(bool) + 'static, + { + unsafe { + let config: id = msg_send![class!(WKFindConfiguration), new]; + let _: () = msg_send![config, setBackwards: option.backwards]; + let _: () = msg_send![config, setCaseSensitive: option.case_sensitive]; + let _: () = msg_send![config, setWraps: option.wraps]; + let _: () = msg_send![self.webview, findString: NSString::new(&string) withConfiguration: config completionHandler:ConcreteBlock::new(|result: id| { + let match_found: BOOL = msg_send![result, matchFound]; + f(match_found == YES); + })]; + } + } } pub fn platform_webview_version() -> Result { From ce2af08bd5a6ce253529ea9002904b2290c0c32e Mon Sep 17 00:00:00 2001 From: keiya sasaki <34934510+keiya01@users.noreply.github.com> Date: Mon, 23 May 2022 08:54:18 +0900 Subject: [PATCH 2/5] Update .changes/add-find-in-page.md Co-authored-by: Amr Bashir --- .changes/add-find-in-page.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/add-find-in-page.md b/.changes/add-find-in-page.md index a37075f92..47d20365c 100644 --- a/.changes/add-find-in-page.md +++ b/.changes/add-find-in-page.md @@ -2,4 +2,4 @@ "wry": minor --- -Add find-in-page feature. +Add `WebView::find_in_page`. From d29325bcb7f04da1cb77550066ad52ba7c8435b6 Mon Sep 17 00:00:00 2001 From: keiya sasaki <34934510+keiya01@users.noreply.github.com> Date: Mon, 23 May 2022 08:54:26 +0900 Subject: [PATCH 3/5] Update src/webview/mod.rs Co-authored-by: Amr Bashir --- src/webview/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webview/mod.rs b/src/webview/mod.rs index d9c7ec4b0..e1e1f4ea9 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -521,7 +521,7 @@ impl WebView { /// /// ## Platform-specific: /// - /// - **Windows / Android**: Not supported. + /// - **Windows / Android**: Unsupported. /// - **macOS**: available on macOS 10.15.4+ only. /// - **iOS**: available on iOS 13.4+ only. pub fn find_in_page(&self, string: String, option: FindInPageOption, f: F) From 7b856a2f4779035f829455e41b7657a93386b57c Mon Sep 17 00:00:00 2001 From: keiya sasaki <34934510+keiya01@users.noreply.github.com> Date: Mon, 23 May 2022 08:54:32 +0900 Subject: [PATCH 4/5] Update src/webview/mod.rs Co-authored-by: Amr Bashir --- src/webview/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webview/mod.rs b/src/webview/mod.rs index e1e1f4ea9..4ffbc5697 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -542,7 +542,7 @@ impl WebView { } } -/// A configuration for `find_in_page`. +/// A configuration for [`WebView::find_in_page`]. #[derive(Default)] pub struct FindInPageOption { /// Indicate the search direction. From 95712dbf17424452c46b63e0cd3d6304e7acb1e1 Mon Sep 17 00:00:00 2001 From: keiya sasaki <34934510+keiya01@users.noreply.github.com> Date: Mon, 23 May 2022 08:54:40 +0900 Subject: [PATCH 5/5] Update src/webview/mod.rs Co-authored-by: Amr Bashir --- src/webview/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 4ffbc5697..d1d2a7c7c 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -551,7 +551,11 @@ pub struct FindInPageOption { pub case_sensitive: bool, /// Wrap around to the other side of the page. pub wraps: bool, - /// For Linux only, Maximum number of search strings to highlight. + /// Maximum number of search strings to highlight. + /// + /// ## Platform-specific: + /// + /// **Windows / macOS / Android / iOS**: Unsupported. pub max_match_count: u32, }