Skip to content

Commit 07953fb

Browse files
authored
fix(tauri-runtime-wry): send focused events on multiwebview for Windows (#13222)
* fix(tauri-runtime-wry): send focused events on multiwebview for Windows closes #9755 follow-up for #12014 * fix mobile build
1 parent 8d994f6 commit 07953fb

File tree

2 files changed

+117
-43
lines changed

2 files changed

+117
-43
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-runtime-wry": patch:bug
3+
---
4+
5+
Emit `WindowEvent::Focused` events when using the multiwebview (unstable feature flag) mode on Windows.

crates/tauri-runtime-wry/src/lib.rs

Lines changed: 112 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -363,14 +363,15 @@ impl<T: UserEvent> Context<T> {
363363
self,
364364
Message::CreateWebview(
365365
window_id,
366-
Box::new(move |window| {
366+
Box::new(move |window, options| {
367367
create_webview(
368368
WebviewKind::WindowChild,
369369
window,
370370
window_id_wrapper_,
371371
webview_id,
372372
&context,
373373
pending,
374+
options.focused_webview,
374375
)
375376
}),
376377
),
@@ -485,6 +486,59 @@ impl TryFrom<Icon<'_>> for TaoIcon {
485486
pub struct WindowEventWrapper(pub Option<WindowEvent>);
486487

487488
impl WindowEventWrapper {
489+
fn map_from_tao(
490+
event: &TaoWindowEvent<'_>,
491+
#[allow(unused_variables)] window: &WindowWrapper,
492+
) -> Self {
493+
let event = match event {
494+
TaoWindowEvent::Resized(size) => WindowEvent::Resized(PhysicalSizeWrapper(*size).into()),
495+
TaoWindowEvent::Moved(position) => {
496+
WindowEvent::Moved(PhysicalPositionWrapper(*position).into())
497+
}
498+
TaoWindowEvent::Destroyed => WindowEvent::Destroyed,
499+
TaoWindowEvent::ScaleFactorChanged {
500+
scale_factor,
501+
new_inner_size,
502+
} => WindowEvent::ScaleFactorChanged {
503+
scale_factor: *scale_factor,
504+
new_inner_size: PhysicalSizeWrapper(**new_inner_size).into(),
505+
},
506+
TaoWindowEvent::Focused(focused) => {
507+
#[cfg(not(windows))]
508+
return Self(Some(WindowEvent::Focused(*focused)));
509+
// on multiwebview mode, if there's no focused webview, it means we're receiving a direct window focus change
510+
// (without receiving a webview focus, such as when clicking the taskbar app icon or using Alt + Tab)
511+
// in this case we must send the focus change event here
512+
#[cfg(windows)]
513+
if window.has_children.load(Ordering::Relaxed) {
514+
const FOCUSED_WEBVIEW_MARKER: &str = "__tauriWindow?";
515+
let mut focused_webview = window.focused_webview.lock().unwrap();
516+
// when we focus a webview and the window was previously focused, we get a blur event here
517+
// so on blur we should only send events if the current focus is owned by the window
518+
if !*focused
519+
&& focused_webview
520+
.as_deref()
521+
.map_or(false, |w| w != FOCUSED_WEBVIEW_MARKER)
522+
{
523+
return Self(None);
524+
}
525+
526+
// reset focused_webview on blur, or set to a dummy value on focus
527+
// (to prevent double focus event when we click a webview after focusing a window)
528+
*focused_webview = (*focused).then(|| FOCUSED_WEBVIEW_MARKER.to_string());
529+
530+
return Self(Some(WindowEvent::Focused(*focused)));
531+
} else {
532+
// when not on multiwebview mode, we handle focus change events on the webview (add_GotFocus and add_LostFocus)
533+
return Self(None);
534+
}
535+
}
536+
TaoWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)),
537+
_ => return Self(None),
538+
};
539+
Self(Some(event))
540+
}
541+
488542
fn parse(window: &WindowWrapper, event: &TaoWindowEvent<'_>) -> Self {
489543
match event {
490544
// resized event from tao doesn't include a reliable size on macOS
@@ -501,7 +555,7 @@ impl WindowEventWrapper {
501555
Self(None)
502556
}
503557
}
504-
e => e.into(),
558+
e => Self::map_from_tao(e, window),
505559
}
506560
}
507561
}
@@ -524,30 +578,6 @@ fn tao_activation_policy(activation_policy: ActivationPolicy) -> TaoActivationPo
524578
}
525579
}
526580

527-
impl<'a> From<&TaoWindowEvent<'a>> for WindowEventWrapper {
528-
fn from(event: &TaoWindowEvent<'a>) -> Self {
529-
let event = match event {
530-
TaoWindowEvent::Resized(size) => WindowEvent::Resized(PhysicalSizeWrapper(*size).into()),
531-
TaoWindowEvent::Moved(position) => {
532-
WindowEvent::Moved(PhysicalPositionWrapper(*position).into())
533-
}
534-
TaoWindowEvent::Destroyed => WindowEvent::Destroyed,
535-
TaoWindowEvent::ScaleFactorChanged {
536-
scale_factor,
537-
new_inner_size,
538-
} => WindowEvent::ScaleFactorChanged {
539-
scale_factor: *scale_factor,
540-
new_inner_size: PhysicalSizeWrapper(**new_inner_size).into(),
541-
},
542-
#[cfg(any(target_os = "linux", target_os = "macos"))]
543-
TaoWindowEvent::Focused(focused) => WindowEvent::Focused(*focused),
544-
TaoWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)),
545-
_ => return Self(None),
546-
};
547-
Self(Some(event))
548-
}
549-
}
550-
551581
pub struct MonitorHandleWrapper(pub MonitorHandle);
552582

553583
impl From<MonitorHandleWrapper> for Monitor {
@@ -1402,7 +1432,12 @@ pub enum EventLoopWindowTargetMessage {
14021432
pub type CreateWindowClosure<T> =
14031433
Box<dyn FnOnce(&EventLoopWindowTarget<Message<T>>) -> Result<WindowWrapper> + Send>;
14041434

1405-
pub type CreateWebviewClosure = Box<dyn FnOnce(&Window) -> Result<WebviewWrapper> + Send>;
1435+
pub type CreateWebviewClosure =
1436+
Box<dyn FnOnce(&Window, CreateWebviewOptions) -> Result<WebviewWrapper> + Send>;
1437+
1438+
pub struct CreateWebviewOptions {
1439+
pub focused_webview: Arc<Mutex<Option<String>>>,
1440+
}
14061441

14071442
pub enum Message<T: 'static> {
14081443
Task(Box<dyn FnOnce() + Send>),
@@ -2349,6 +2384,7 @@ pub struct WindowWrapper {
23492384
is_window_transparent: bool,
23502385
#[cfg(windows)]
23512386
surface: Option<softbuffer::Surface<Arc<Window>, Arc<Window>>>,
2387+
focused_webview: Arc<Mutex<Option<String>>>,
23522388
}
23532389

23542390
impl fmt::Debug for WindowWrapper {
@@ -2805,8 +2841,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
28052841
.0
28062842
.borrow()
28072843
.get(&window_id)
2808-
.and_then(|w| w.inner.clone());
2809-
if let Some(window) = window {
2844+
.map(|w| (w.inner.clone(), w.focused_webview.clone()));
2845+
if let Some((Some(window), focused_webview)) = window {
28102846
let window_id_wrapper = Arc::new(Mutex::new(window_id));
28112847

28122848
let webview_id = self.context.next_webview_id();
@@ -2818,6 +2854,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
28182854
webview_id,
28192855
&self.context,
28202856
pending,
2857+
focused_webview,
28212858
)?;
28222859

28232860
#[allow(unknown_lints, clippy::manual_inspect)]
@@ -3750,9 +3787,9 @@ fn handle_user_message<T: UserEvent>(
37503787
.0
37513788
.borrow()
37523789
.get(&window_id)
3753-
.and_then(|w| w.inner.clone());
3754-
if let Some(window) = window {
3755-
match handler(&window) {
3790+
.map(|w| (w.inner.clone(), w.focused_webview.clone()));
3791+
if let Some((Some(window), focused_webview)) = window {
3792+
match handler(&window, CreateWebviewOptions { focused_webview }) {
37563793
Ok(webview) => {
37573794
#[allow(unknown_lints, clippy::manual_inspect)]
37583795
windows.0.borrow_mut().get_mut(&window_id).map(|w| {
@@ -3818,6 +3855,7 @@ fn handle_user_message<T: UserEvent>(
38183855
is_window_transparent,
38193856
#[cfg(windows)]
38203857
surface,
3858+
focused_webview: Default::default(),
38213859
},
38223860
);
38233861
sender.send(Ok(Arc::downgrade(&window))).unwrap();
@@ -4307,6 +4345,8 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
43074345

43084346
let mut webviews = Vec::new();
43094347

4348+
let focused_webview = Arc::new(Mutex::new(None));
4349+
43104350
if let Some(webview) = webview {
43114351
webviews.push(create_webview(
43124352
#[cfg(feature = "unstable")]
@@ -4318,6 +4358,7 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
43184358
webview_id,
43194359
context,
43204360
webview,
4361+
focused_webview.clone(),
43214362
)?);
43224363
}
43234364

@@ -4351,6 +4392,7 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
43514392
is_window_transparent,
43524393
#[cfg(windows)]
43534394
surface,
4395+
focused_webview,
43544396
})
43554397
}
43564398

@@ -4378,6 +4420,7 @@ fn create_webview<T: UserEvent>(
43784420
id: WebviewId,
43794421
context: &Context<T>,
43804422
pending: PendingWebview<T, Wry<T>>,
4423+
#[allow(unused_variables)] focused_webview: Arc<Mutex<Option<String>>>,
43814424
) -> Result<WebviewWrapper> {
43824425
#[allow(unused_mut)]
43834426
let PendingWebview {
@@ -4766,34 +4809,60 @@ fn create_webview<T: UserEvent>(
47664809
}
47674810

47684811
#[cfg(windows)]
4769-
if kind == WebviewKind::WindowContent {
4812+
{
47704813
let controller = webview.controller();
47714814
let proxy = context.proxy.clone();
47724815
let proxy_ = proxy.clone();
47734816
let window_id_ = window_id.clone();
47744817
let mut token = 0;
47754818
unsafe {
4819+
let label_ = label.clone();
4820+
let focused_webview_ = focused_webview.clone();
47764821
controller.add_GotFocus(
47774822
&FocusChangedEventHandler::create(Box::new(move |_, _| {
4778-
let _ = proxy.send_event(Message::Webview(
4779-
*window_id_.lock().unwrap(),
4780-
id,
4781-
WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)),
4782-
));
4823+
let mut focused_webview = focused_webview_.lock().unwrap();
4824+
// when using multiwebview mode, we should check if the focus change is actually a "webview focus change"
4825+
// instead of a window focus change (here we're patching window events, so we only care about the actual window changing focus)
4826+
let already_focused = focused_webview.is_some();
4827+
focused_webview.replace(label_.clone());
4828+
4829+
if !already_focused {
4830+
let _ = proxy.send_event(Message::Webview(
4831+
*window_id_.lock().unwrap(),
4832+
id,
4833+
WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)),
4834+
));
4835+
}
47834836
Ok(())
47844837
})),
47854838
&mut token,
47864839
)
47874840
}
47884841
.unwrap();
47894842
unsafe {
4843+
let label_ = label.clone();
4844+
let focused_webview_ = focused_webview.clone();
47904845
controller.add_LostFocus(
47914846
&FocusChangedEventHandler::create(Box::new(move |_, _| {
4792-
let _ = proxy_.send_event(Message::Webview(
4793-
*window_id.lock().unwrap(),
4794-
id,
4795-
WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)),
4796-
));
4847+
let mut focused_webview = focused_webview_.lock().unwrap();
4848+
// when using multiwebview mode, we should handle webview focus changes
4849+
// so we check is the currently focused webview matches this webview's
4850+
// (in this case, it means we lost the window focus)
4851+
//
4852+
// on multiwebview mode if we change focus to a different webview
4853+
// we get the gotFocus event of the other webview before the lostFocus
4854+
// so this check makes sense
4855+
let lost_window_focus = focused_webview.as_ref().map_or(true, |w| w == &label_);
4856+
4857+
if lost_window_focus {
4858+
// only reset when we lost window focus - otherwise some other webview is focused
4859+
*focused_webview = None;
4860+
let _ = proxy_.send_event(Message::Webview(
4861+
*window_id.lock().unwrap(),
4862+
id,
4863+
WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)),
4864+
));
4865+
}
47974866
Ok(())
47984867
})),
47994868
&mut token,

0 commit comments

Comments
 (0)