From e416af98a27d30c4a27d186e8ed6e9007ab5e534 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 11:42:49 +0800 Subject: [PATCH 01/11] Remove unwraps --- crates/tauri-runtime-wry/src/lib.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index aef855ca7e62..0874f81f3047 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -5284,12 +5284,13 @@ You may have it installed on another user account, but it is not available for t #[cfg(windows)] { let controller = webview.controller(); - let proxy_clone = context.proxy.clone(); - let window_id_ = window_id.clone(); let mut token = 0; - unsafe { - let label_ = label.clone(); - let focused_webview_ = focused_webview.clone(); + + let label_ = label.clone(); + let window_id_ = window_id.clone(); + let proxy_clone = context.proxy.clone(); + let focused_webview_ = focused_webview.clone(); + if let Err(error) = unsafe { controller.add_GotFocus( &FocusChangedEventHandler::create(Box::new(move |_, _| { let mut focused_webview = focused_webview_.lock().unwrap(); @@ -5309,12 +5310,14 @@ You may have it installed on another user account, but it is not available for t })), &mut token, ) + } { + log::error!("Failed to attach WebView2 `add_GotFocus` handler, `WindowEvent::Focused` will not be sent: {error}"); } - .unwrap(); - unsafe { - let label_ = label.clone(); - let window_id_ = window_id.clone(); - let proxy_clone = context.proxy.clone(); + + let label_ = label.clone(); + let window_id_ = window_id.clone(); + let proxy_clone = context.proxy.clone(); + if let Err(error) = unsafe { controller.add_LostFocus( &FocusChangedEventHandler::create(Box::new(move |_, _| { let mut focused_webview = focused_webview.lock().unwrap(); @@ -5342,8 +5345,9 @@ You may have it installed on another user account, but it is not available for t })), &mut token, ) + } { + log::error!("Failed to attach WebView2 `add_LostFocus` handler, `WindowEvent::Focused` will not be sent: {error}"); } - .unwrap(); if let Ok(webview) = unsafe { controller.CoreWebView2() } { let proxy_clone = context.proxy.clone(); From 7d3a78ebf761796380d6bd6cd94bb08d2e843bbd Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 13:14:39 +0800 Subject: [PATCH 02/11] Restore multi-webview focus --- crates/tauri-runtime-wry/src/lib.rs | 122 ++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 0874f81f3047..eaa91eba77ed 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -528,29 +528,53 @@ impl WindowEventWrapper { #[cfg(windows)] #[allow(clippy::collapsible_match)] if window.has_children.load(Ordering::Relaxed) { - const FOCUSED_WEBVIEW_MARKER: &str = "__tauriWindow?"; let mut focused_webview = window.focused_webview.lock().unwrap(); // when we focus a webview and the window was previously focused, we get a blur event here // so on blur we should only send events if the current focus is owned by the window - if !*focused - && focused_webview - .as_deref() - .is_some_and(|w| w != FOCUSED_WEBVIEW_MARKER) - { - return Self(None); - } - - // reset focused_webview on blur, or set to a dummy value on focus - // (to prevent double focus event when we click a webview after focusing a window) - *focused_webview = if *focused { - Some(FOCUSED_WEBVIEW_MARKER.to_owned()) + // if !*focused + // && focused_webview + // .as_deref() + // .is_some_and(|w| w != FOCUSED_WEBVIEW_MARKER) + // { + // return Self(None); + // } + + // // reset focused_webview on blur, or set to a dummy value on focus + // // (to prevent double focus event when we click a webview after focusing a window) + // *focused_webview = if *focused { + // Some(FOCUSED_WEBVIEW_MARKER.to_owned()) + // } else { + // None + // }; + + if *focused { + if let FocusState::Blured { + last_focused_webview_label: Some(last_focused_webview_label), + } = &*focused_webview + { + if let Some(should_focus_webview) = window + .webviews + .iter() + .find(|w| &w.label == last_focused_webview_label) + { + *focused_webview = FocusState::WindowFocused; + drop(focused_webview); + let _ = should_focus_webview.focus(); + } + } } else { - None - }; + if let FocusState::WebviewFocused { ref webview_label } = *focused_webview { + // only reset when we lost window focus - otherwise some other webview is focused + *focused_webview = FocusState::Blured { + last_focused_webview_label: Some(webview_label.clone()), + }; + } + } WindowEvent::Focused(*focused) } else { - // when not on multiwebview mode, we handle focus change events on the webview (add_GotFocus and add_LostFocus) + // when not on multiwebview mode, wry will set focus to the webview, + // and we will handle focus change events on the webview (add_GotFocus and add_LostFocus) return Self(None); } } @@ -1557,7 +1581,7 @@ pub type CreateWebviewClosure = Box Result + Send>; pub struct CreateWebviewOptions { - pub focused_webview: Arc>>, + pub focused_webview: Arc>, } pub enum Message { @@ -2593,6 +2617,25 @@ impl Drop for WebviewWrapper { } } +#[derive(Debug)] +pub enum FocusState { + WindowFocused, + WebviewFocused { + webview_label: String, + }, + Blured { + last_focused_webview_label: Option, + }, +} + +impl Default for FocusState { + fn default() -> Self { + Self::Blured { + last_focused_webview_label: None, + } + } +} + pub struct WindowWrapper { label: String, inner: Option>, @@ -2607,7 +2650,7 @@ pub struct WindowWrapper { is_window_transparent: bool, #[cfg(windows)] surface: Option, Arc>>, - focused_webview: Arc>>, + focused_webview: Arc>, } impl WindowWrapper { @@ -4677,7 +4720,7 @@ fn create_window( let mut webviews = Vec::new(); - let focused_webview = Arc::new(Mutex::new(None)); + let focused_webview = Arc::new(Mutex::new(FocusState::default())); if let Some(webview) = webview { webviews.push(create_webview( @@ -4752,7 +4795,7 @@ fn create_webview( id: WebviewId, context: &Context, pending: PendingWebview>, - #[allow(unused_variables)] focused_webview: Arc>>, + #[allow(unused_variables)] focused_webview: Arc>, ) -> Result { if !context.webview_runtime_installed { #[cfg(all(not(debug_assertions), windows))] @@ -5293,11 +5336,17 @@ You may have it installed on another user account, but it is not available for t if let Err(error) = unsafe { controller.add_GotFocus( &FocusChangedEventHandler::create(Box::new(move |_, _| { + dbg!("GotFocus"); let mut focused_webview = focused_webview_.lock().unwrap(); // when using multiwebview mode, we should check if the focus change is actually a "webview focus change" // instead of a window focus change (here we're patching window events, so we only care about the actual window changing focus) - let already_focused = focused_webview.is_some(); - focused_webview.replace(label_.clone()); + let already_focused = matches!( + *focused_webview, + FocusState::WindowFocused | FocusState::WebviewFocused { .. } + ); + *focused_webview = FocusState::WebviewFocused { + webview_label: label_.clone(), + }; if !already_focused { let _ = proxy_clone.send_event(Message::Webview( @@ -5328,19 +5377,24 @@ You may have it installed on another user account, but it is not available for t // on multiwebview mode if we change focus to a different webview // we get the gotFocus event of the other webview before the lostFocus // so this check makes sense - let lost_window_focus = focused_webview.as_ref().map_or(true, |w| w == &label_); - // TODO: Use `is_none_or` instead when MSRV gets raised above 1.82 - // let lost_window_focus = focused_webview.as_ref().is_none_or(|t| t == &label_); - - if lost_window_focus { - // only reset when we lost window focus - otherwise some other webview is focused - *focused_webview = None; - let _ = proxy_clone.send_event(Message::Webview( - *window_id_.lock().unwrap(), - id, - WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), - )); + match *focused_webview { + FocusState::WebviewFocused { ref webview_label } => { + let lost_window_focus = webview_label == &label_; + if lost_window_focus { + // only reset when we lost window focus - otherwise some other webview is focused + *focused_webview = FocusState::Blured { + last_focused_webview_label: Some(label_.clone()), + }; + let _ = proxy_clone.send_event(Message::Webview( + *window_id_.lock().unwrap(), + id, + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), + )); + } + } + _ => {} } + Ok(()) })), &mut token, From 6efcd060a44dcddb7621ee0ee449b3a47c36175b Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 13:21:49 +0800 Subject: [PATCH 03/11] Cleanup --- crates/tauri-runtime-wry/src/lib.rs | 46 ++++++++--------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index eaa91eba77ed..9514083a4678 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -529,23 +529,6 @@ impl WindowEventWrapper { #[allow(clippy::collapsible_match)] if window.has_children.load(Ordering::Relaxed) { let mut focused_webview = window.focused_webview.lock().unwrap(); - // when we focus a webview and the window was previously focused, we get a blur event here - // so on blur we should only send events if the current focus is owned by the window - // if !*focused - // && focused_webview - // .as_deref() - // .is_some_and(|w| w != FOCUSED_WEBVIEW_MARKER) - // { - // return Self(None); - // } - - // // reset focused_webview on blur, or set to a dummy value on focus - // // (to prevent double focus event when we click a webview after focusing a window) - // *focused_webview = if *focused { - // Some(FOCUSED_WEBVIEW_MARKER.to_owned()) - // } else { - // None - // }; if *focused { if let FocusState::Blured { @@ -564,7 +547,6 @@ impl WindowEventWrapper { } } else { if let FocusState::WebviewFocused { ref webview_label } = *focused_webview { - // only reset when we lost window focus - otherwise some other webview is focused *focused_webview = FocusState::Blured { last_focused_webview_label: Some(webview_label.clone()), }; @@ -5336,7 +5318,6 @@ You may have it installed on another user account, but it is not available for t if let Err(error) = unsafe { controller.add_GotFocus( &FocusChangedEventHandler::create(Box::new(move |_, _| { - dbg!("GotFocus"); let mut focused_webview = focused_webview_.lock().unwrap(); // when using multiwebview mode, we should check if the focus change is actually a "webview focus change" // instead of a window focus change (here we're patching window events, so we only care about the actual window changing focus) @@ -5377,22 +5358,19 @@ You may have it installed on another user account, but it is not available for t // on multiwebview mode if we change focus to a different webview // we get the gotFocus event of the other webview before the lostFocus // so this check makes sense - match *focused_webview { - FocusState::WebviewFocused { ref webview_label } => { - let lost_window_focus = webview_label == &label_; - if lost_window_focus { - // only reset when we lost window focus - otherwise some other webview is focused - *focused_webview = FocusState::Blured { - last_focused_webview_label: Some(label_.clone()), - }; - let _ = proxy_clone.send_event(Message::Webview( - *window_id_.lock().unwrap(), - id, - WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), - )); - } + if let FocusState::WebviewFocused { ref webview_label } = *focused_webview { + let lost_window_focus = webview_label == &label_; + if lost_window_focus { + // only reset when we lost window focus - otherwise some other webview is focused + *focused_webview = FocusState::Blured { + last_focused_webview_label: Some(label_.clone()), + }; + let _ = proxy_clone.send_event(Message::Webview( + *window_id_.lock().unwrap(), + id, + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), + )); } - _ => {} } Ok(()) From 5de146c30fc79b48bf0f10eaf9d3be6b86d02254 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 13:30:26 +0800 Subject: [PATCH 04/11] Skip if already focused --- crates/tauri-runtime-wry/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 9514083a4678..d92f938b3e12 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -544,11 +544,14 @@ impl WindowEventWrapper { drop(focused_webview); let _ = should_focus_webview.focus(); } + } else { + // Already focused + return Self(None); } } else { - if let FocusState::WebviewFocused { ref webview_label } = *focused_webview { + if let FocusState::WindowFocused = *focused_webview { *focused_webview = FocusState::Blured { - last_focused_webview_label: Some(webview_label.clone()), + last_focused_webview_label: None, }; } } From 497a313a6a5bb64410b94d7a34df45c0f51c4264 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 14:27:09 +0800 Subject: [PATCH 05/11] `has_children` is set to true for webview window on unstable feature --- crates/tauri-runtime-wry/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index d92f938b3e12..6af593bdf22d 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -4707,6 +4707,11 @@ fn create_window( let focused_webview = Arc::new(Mutex::new(FocusState::default())); + #[cfg(feature = "unstable")] + let has_children = webview.is_some(); + #[cfg(not(feature = "unstable"))] + let has_children = false; + if let Some(webview) = webview { webviews.push(create_webview( #[cfg(feature = "unstable")] @@ -4742,7 +4747,7 @@ fn create_window( Ok(WindowWrapper { label, - has_children: AtomicBool::new(false), + has_children: AtomicBool::new(has_children), inner: Some(window), webviews, window_event_listeners, From ff681c58de7b6e8eb62e020e890882813d2b1086 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 14:31:28 +0800 Subject: [PATCH 06/11] Forward the event for raw tao window --- crates/tauri-runtime-wry/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 6af593bdf22d..69ea424de082 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -556,6 +556,9 @@ impl WindowEventWrapper { } } + WindowEvent::Focused(*focused) + } else if window.webviews.is_empty() { + // Raw tao window without webviews, forward the event WindowEvent::Focused(*focused) } else { // when not on multiwebview mode, wry will set focus to the webview, From bfec0245abc3580b47c0f810bcb07e0de494c215 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 15:49:38 +0800 Subject: [PATCH 07/11] Always handle focused events in `add_LostFocus` --- crates/tauri-runtime-wry/src/lib.rs | 50 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 69ea424de082..1641b2a8d2fe 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -526,37 +526,37 @@ impl WindowEventWrapper { // (without receiving a webview focus, such as when clicking the taskbar app icon or using Alt + Tab) // in this case we must send the focus change event here #[cfg(windows)] - #[allow(clippy::collapsible_match)] if window.has_children.load(Ordering::Relaxed) { let mut focused_webview = window.focused_webview.lock().unwrap(); - if *focused { - if let FocusState::Blured { - last_focused_webview_label: Some(last_focused_webview_label), - } = &*focused_webview - { - if let Some(should_focus_webview) = window - .webviews - .iter() - .find(|w| &w.label == last_focused_webview_label) - { - *focused_webview = FocusState::WindowFocused; - drop(focused_webview); - let _ = should_focus_webview.focus(); - } - } else { - // Already focused - return Self(None); + if !*focused { + // Blur events are handled in the webview side (add_LostFocus) + return Self(None); + } + + if let FocusState::Blured { + last_focused_webview_label, + } = &*focused_webview + { + let should_focus_webview = + last_focused_webview_label + .as_deref() + .and_then(|last_focused_webview_label| { + window + .webviews + .iter() + .find(|w| w.label == last_focused_webview_label) + }); + *focused_webview = FocusState::WindowFocused; + if let Some(should_focus_webview) = should_focus_webview { + drop(focused_webview); + let _ = should_focus_webview.focus(); } + WindowEvent::Focused(*focused) } else { - if let FocusState::WindowFocused = *focused_webview { - *focused_webview = FocusState::Blured { - last_focused_webview_label: None, - }; - } + // Already focused + return Self(None); } - - WindowEvent::Focused(*focused) } else if window.webviews.is_empty() { // Raw tao window without webviews, forward the event WindowEvent::Focused(*focused) From 125c2e8d4f5b344b6d9efd46998936afa96322a5 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 16:01:22 +0800 Subject: [PATCH 08/11] Clean up --- crates/tauri-runtime-wry/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 1641b2a8d2fe..96cac86339fe 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -527,13 +527,12 @@ impl WindowEventWrapper { // in this case we must send the focus change event here #[cfg(windows)] if window.has_children.load(Ordering::Relaxed) { - let mut focused_webview = window.focused_webview.lock().unwrap(); - if !*focused { // Blur events are handled in the webview side (add_LostFocus) return Self(None); } + let mut focused_webview = window.focused_webview.lock().unwrap(); if let FocusState::Blured { last_focused_webview_label, } = &*focused_webview @@ -552,7 +551,7 @@ impl WindowEventWrapper { drop(focused_webview); let _ = should_focus_webview.focus(); } - WindowEvent::Focused(*focused) + WindowEvent::Focused(true) } else { // Already focused return Self(None); From a1c9322f6f5be93732563ba8daf8b13295e9a60b Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 16:06:39 +0800 Subject: [PATCH 09/11] Add change file --- .changes/unstable-webview-focus.md | 6 ++++++ .changes/webview-focus-on-move.md | 6 ++++++ .changes/wry-focused-webview.md | 5 +++++ 3 files changed, 17 insertions(+) create mode 100644 .changes/unstable-webview-focus.md create mode 100644 .changes/webview-focus-on-move.md create mode 100644 .changes/wry-focused-webview.md diff --git a/.changes/unstable-webview-focus.md b/.changes/unstable-webview-focus.md new file mode 100644 index 000000000000..6854f716e884 --- /dev/null +++ b/.changes/unstable-webview-focus.md @@ -0,0 +1,6 @@ +--- +tauri: minor:bug +tauri-runtime-wry: minor:bug +--- + +Fix webview don't get focus when Alt-Tab back to the window if `unstable` feature is enabled on Windows diff --git a/.changes/webview-focus-on-move.md b/.changes/webview-focus-on-move.md new file mode 100644 index 000000000000..fb5ccc5be258 --- /dev/null +++ b/.changes/webview-focus-on-move.md @@ -0,0 +1,6 @@ +--- +tauri: minor:bug +tauri-runtime-wry: minor:bug +--- + +Fix `WindowEvent::Focused` events emitted when dragging the window on Windows diff --git a/.changes/wry-focused-webview.md b/.changes/wry-focused-webview.md new file mode 100644 index 000000000000..45a16428cec1 --- /dev/null +++ b/.changes/wry-focused-webview.md @@ -0,0 +1,5 @@ +--- +tauri-runtime-wry: minor:breaking +--- + +`CreateWebviewOptions::focused_webview` now takes `Arc>` instead of `Arc>>` From 8ca2b4f220831d8fd195660786212211a4fa83c1 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 19:04:17 +0800 Subject: [PATCH 10/11] Move out to a `add_focus_change_listeners` fn --- crates/tauri-runtime-wry/src/lib.rs | 164 ++++++++++++++++------------ 1 file changed, 94 insertions(+), 70 deletions(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 96cac86339fe..6b07bd4f9c05 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -50,7 +50,10 @@ use tao::platform::unix::{WindowBuilderExtUnix, WindowExtUnix}; #[cfg(windows)] use tao::platform::windows::{WindowBuilderExtWindows, WindowExtWindows}; #[cfg(windows)] -use webview2_com::{ContainsFullScreenElementChangedEventHandler, FocusChangedEventHandler}; +use webview2_com::{ + ContainsFullScreenElementChangedEventHandler, FocusChangedEventHandler, + Microsoft::Web::WebView2::Win32::ICoreWebView2Controller, +}; #[cfg(windows)] use windows::Win32::Foundation::HWND; #[cfg(target_os = "ios")] @@ -5321,75 +5324,15 @@ You may have it installed on another user account, but it is not available for t let controller = webview.controller(); let mut token = 0; - let label_ = label.clone(); - let window_id_ = window_id.clone(); - let proxy_clone = context.proxy.clone(); - let focused_webview_ = focused_webview.clone(); - if let Err(error) = unsafe { - controller.add_GotFocus( - &FocusChangedEventHandler::create(Box::new(move |_, _| { - let mut focused_webview = focused_webview_.lock().unwrap(); - // when using multiwebview mode, we should check if the focus change is actually a "webview focus change" - // instead of a window focus change (here we're patching window events, so we only care about the actual window changing focus) - let already_focused = matches!( - *focused_webview, - FocusState::WindowFocused | FocusState::WebviewFocused { .. } - ); - *focused_webview = FocusState::WebviewFocused { - webview_label: label_.clone(), - }; - - if !already_focused { - let _ = proxy_clone.send_event(Message::Webview( - *window_id_.lock().unwrap(), - id, - WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)), - )); - } - Ok(()) - })), - &mut token, - ) - } { - log::error!("Failed to attach WebView2 `add_GotFocus` handler, `WindowEvent::Focused` will not be sent: {error}"); - } - - let label_ = label.clone(); - let window_id_ = window_id.clone(); - let proxy_clone = context.proxy.clone(); - if let Err(error) = unsafe { - controller.add_LostFocus( - &FocusChangedEventHandler::create(Box::new(move |_, _| { - let mut focused_webview = focused_webview.lock().unwrap(); - // when using multiwebview mode, we should handle webview focus changes - // so we check is the currently focused webview matches this webview's - // (in this case, it means we lost the window focus) - // - // on multiwebview mode if we change focus to a different webview - // we get the gotFocus event of the other webview before the lostFocus - // so this check makes sense - if let FocusState::WebviewFocused { ref webview_label } = *focused_webview { - let lost_window_focus = webview_label == &label_; - if lost_window_focus { - // only reset when we lost window focus - otherwise some other webview is focused - *focused_webview = FocusState::Blured { - last_focused_webview_label: Some(label_.clone()), - }; - let _ = proxy_clone.send_event(Message::Webview( - *window_id_.lock().unwrap(), - id, - WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), - )); - } - } - - Ok(()) - })), - &mut token, - ) - } { - log::error!("Failed to attach WebView2 `add_LostFocus` handler, `WindowEvent::Focused` will not be sent: {error}"); - } + add_focus_change_listeners( + window_id.clone(), + id, + context.proxy.clone(), + focused_webview, + label.clone(), + &controller, + &mut token, + ); if let Ok(webview) = unsafe { controller.CoreWebView2() } { let proxy_clone = context.proxy.clone(); @@ -5427,6 +5370,87 @@ You may have it installed on another user account, but it is not available for t }) } +/// Used to prevent duplicated [`WindowEvent::Focused`] events, +/// and to track last focused webview in multi-webview mode for us to restore webview focuses +#[cfg(windows)] +fn add_focus_change_listeners( + window_id: Arc>, + id: u32, + proxy: TaoEventLoopProxy>, + focused_webview: Arc>, + label: String, + controller: &ICoreWebView2Controller, + token: &mut i64, +) { + let label_ = label.clone(); + let window_id_ = window_id.clone(); + let proxy_clone = proxy.clone(); + let focused_webview_ = focused_webview.clone(); + if let Err(error) = unsafe { + controller.add_GotFocus( + &FocusChangedEventHandler::create(Box::new(move |_, _| { + let mut focused_webview = focused_webview_.lock().unwrap(); + // when using multiwebview mode, we should check if the focus change is actually a "webview focus change" + // instead of a window focus change (here we're patching window events, so we only care about the actual window changing focus) + let already_focused = matches!( + *focused_webview, + FocusState::WindowFocused | FocusState::WebviewFocused { .. } + ); + *focused_webview = FocusState::WebviewFocused { + webview_label: label_.clone(), + }; + + if !already_focused { + let _ = proxy_clone.send_event(Message::Webview( + *window_id_.lock().unwrap(), + id, + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)), + )); + } + Ok(()) + })), + token, + ) + } { + log::error!("Failed to attach WebView2 `add_GotFocus` handler, `WindowEvent::Focused` will not be sent: {error}"); + return; + } + + if let Err(error) = unsafe { + controller.add_LostFocus( + &FocusChangedEventHandler::create(Box::new(move |_, _| { + let mut focused_webview = focused_webview.lock().unwrap(); + // when using multiwebview mode, we should handle webview focus changes + // so we check is the currently focused webview matches this webview's + // (in this case, it means we lost the window focus) + // + // on multiwebview mode if we change focus to a different webview + // we get the gotFocus event of the other webview before the lostFocus + // so this check makes sense + if let FocusState::WebviewFocused { ref webview_label } = *focused_webview { + let lost_window_focus = webview_label == &label; + if lost_window_focus { + // only reset when we lost window focus - otherwise some other webview is focused + *focused_webview = FocusState::Blured { + last_focused_webview_label: Some(label.clone()), + }; + let _ = proxy.send_event(Message::Webview( + *window_id.lock().unwrap(), + id, + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), + )); + } + } + + Ok(()) + })), + token, + ) + } { + log::error!("Failed to attach WebView2 `add_LostFocus` handler, `WindowEvent::Focused` will not be sent: {error}"); + } +} + /// Create a wry ipc handler from a tauri ipc handler. fn create_ipc_handler( window_id: Arc>, From a4c387e67dd0e672d22e6471413bfaa2cdce7c29 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 1 Jul 2026 19:11:47 +0800 Subject: [PATCH 11/11] move `add_focus_change_listeners` down to bottom --- crates/tauri-runtime-wry/src/lib.rs | 122 ++++++++++++++-------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 6b07bd4f9c05..86fbce0867f5 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -5370,6 +5370,67 @@ You may have it installed on another user account, but it is not available for t }) } +/// Create a wry ipc handler from a tauri ipc handler. +fn create_ipc_handler( + window_id: Arc>, + webview_id: WebviewId, + context: Context, + label: String, + ipc_handler: Option>>, +) -> Box { + Box::new(move |request| { + if let Some(handler) = &ipc_handler { + handler( + DetachedWebview { + label: label.clone(), + dispatcher: WryWebviewDispatcher { + window_id: window_id.clone(), + webview_id, + context: context.clone(), + }, + }, + request, + ); + } + }) +} + +#[cfg(target_os = "macos")] +fn inner_size( + window: &Window, + webviews: &[WebviewWrapper], + has_children: bool, +) -> TaoPhysicalSize { + if !has_children && !webviews.is_empty() { + use wry::WebViewExtMacOS; + let webview = webviews.first().unwrap(); + let view = unsafe { Retained::cast_unchecked::(webview.webview()) }; + let view_frame = view.frame(); + let logical: TaoLogicalSize = (view_frame.size.width, view_frame.size.height).into(); + return logical.to_physical(window.scale_factor()); + } + + window.inner_size() +} + +#[cfg(not(target_os = "macos"))] +#[allow(unused_variables)] +fn inner_size( + window: &Window, + webviews: &[WebviewWrapper], + has_children: bool, +) -> TaoPhysicalSize { + window.inner_size() +} + +fn to_tao_theme(theme: Option) -> Option { + match theme { + Some(Theme::Light) => Some(TaoTheme::Light), + Some(Theme::Dark) => Some(TaoTheme::Dark), + _ => None, + } +} + /// Used to prevent duplicated [`WindowEvent::Focused`] events, /// and to track last focused webview in multi-webview mode for us to restore webview focuses #[cfg(windows)] @@ -5450,64 +5511,3 @@ fn add_focus_change_listeners( log::error!("Failed to attach WebView2 `add_LostFocus` handler, `WindowEvent::Focused` will not be sent: {error}"); } } - -/// Create a wry ipc handler from a tauri ipc handler. -fn create_ipc_handler( - window_id: Arc>, - webview_id: WebviewId, - context: Context, - label: String, - ipc_handler: Option>>, -) -> Box { - Box::new(move |request| { - if let Some(handler) = &ipc_handler { - handler( - DetachedWebview { - label: label.clone(), - dispatcher: WryWebviewDispatcher { - window_id: window_id.clone(), - webview_id, - context: context.clone(), - }, - }, - request, - ); - } - }) -} - -#[cfg(target_os = "macos")] -fn inner_size( - window: &Window, - webviews: &[WebviewWrapper], - has_children: bool, -) -> TaoPhysicalSize { - if !has_children && !webviews.is_empty() { - use wry::WebViewExtMacOS; - let webview = webviews.first().unwrap(); - let view = unsafe { Retained::cast_unchecked::(webview.webview()) }; - let view_frame = view.frame(); - let logical: TaoLogicalSize = (view_frame.size.width, view_frame.size.height).into(); - return logical.to_physical(window.scale_factor()); - } - - window.inner_size() -} - -#[cfg(not(target_os = "macos"))] -#[allow(unused_variables)] -fn inner_size( - window: &Window, - webviews: &[WebviewWrapper], - has_children: bool, -) -> TaoPhysicalSize { - window.inner_size() -} - -fn to_tao_theme(theme: Option) -> Option { - match theme { - Some(Theme::Light) => Some(TaoTheme::Light), - Some(Theme::Dark) => Some(TaoTheme::Dark), - _ => None, - } -}