Skip to content

Commit 49ff1d7

Browse files
committed
Add macOS panel activation guard and focus fixes
1 parent d129358 commit 49ff1d7

3 files changed

Lines changed: 79 additions & 24 deletions

File tree

apps/desktop/src-tauri/src/permissions.rs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use objc2_app_kit::{NSApplicationActivationOptions, NSRunningApplication};
1010
use std::{
1111
future::Future,
1212
str::FromStr,
13-
sync::atomic::{AtomicU64, Ordering},
13+
sync::atomic::{AtomicU32, AtomicU64, Ordering},
1414
time::Duration,
1515
};
1616
#[cfg(target_os = "macos")]
@@ -19,6 +19,23 @@ use tracing::instrument;
1919

2020
#[cfg(target_os = "macos")]
2121
static MACOS_DOCK_VISIBILITY_SYNC_GENERATION: AtomicU64 = AtomicU64::new(0);
22+
#[cfg(target_os = "macos")]
23+
static MACOS_PENDING_PANEL_WINDOWS: AtomicU32 = AtomicU32::new(0);
24+
25+
#[cfg(target_os = "macos")]
26+
pub(crate) struct MacosPanelWindowActivationGuard {
27+
app: tauri::AppHandle,
28+
}
29+
30+
#[cfg(target_os = "macos")]
31+
impl Drop for MacosPanelWindowActivationGuard {
32+
fn drop(&mut self) {
33+
let pending = MACOS_PENDING_PANEL_WINDOWS.fetch_sub(1, Ordering::AcqRel);
34+
if pending == 1 {
35+
schedule_macos_dock_visibility_sync(&self.app);
36+
}
37+
}
38+
}
2239

2340
#[cfg(target_os = "macos")]
2441
#[link(name = "ApplicationServices", kind = "framework")]
@@ -142,25 +159,36 @@ fn macos_sync_activation_policy(app: &tauri::AppHandle, should_show_dock: bool)
142159
}
143160

144161
#[cfg(target_os = "macos")]
145-
pub(crate) fn prepare_macos_panel_window(app: &tauri::AppHandle) {
162+
pub(crate) fn prepare_macos_panel_window(
163+
app: &tauri::AppHandle,
164+
) -> MacosPanelWindowActivationGuard {
165+
MACOS_PENDING_PANEL_WINDOWS.fetch_add(1, Ordering::AcqRel);
166+
146167
if let Err(err) = app.set_activation_policy(tauri::ActivationPolicy::Accessory) {
147168
tracing::warn!("Failed to prepare macOS panel activation policy: {err}");
148169
}
170+
171+
MacosPanelWindowActivationGuard { app: app.clone() }
149172
}
150173

151174
#[cfg(target_os = "macos")]
152175
pub(crate) fn sync_macos_dock_visibility(app: &tauri::AppHandle) {
176+
if MACOS_PENDING_PANEL_WINDOWS.load(Ordering::Acquire) > 0 {
177+
return;
178+
}
179+
153180
let should_hide_dock = GeneralSettingsStore::get(app)
154181
.ok()
155182
.flatten()
156183
.is_some_and(|settings| settings.hide_dock_icon);
157184

158-
let should_show_dock = !should_hide_dock
159-
|| app.webview_windows().keys().any(|label| {
160-
CapWindowId::from_str(label)
161-
.map(|window_id| window_id.activates_dock())
162-
.unwrap_or(false)
163-
});
185+
let has_visible_dock_window = app.webview_windows().iter().any(|(label, window)| {
186+
CapWindowId::from_str(label)
187+
.map(|window_id| window_id.activates_dock() && window.is_visible().unwrap_or(false))
188+
.unwrap_or(false)
189+
});
190+
191+
let should_show_dock = !should_hide_dock || has_visible_dock_window;
164192

165193
macos_sync_activation_policy(app, should_show_dock);
166194

@@ -171,11 +199,11 @@ pub(crate) fn sync_macos_dock_visibility(app: &tauri::AppHandle) {
171199

172200
#[cfg(target_os = "macos")]
173201
pub(crate) fn schedule_macos_dock_visibility_sync(app: &tauri::AppHandle) {
174-
let generation = MACOS_DOCK_VISIBILITY_SYNC_GENERATION.fetch_add(1, Ordering::Relaxed) + 1;
202+
let generation = MACOS_DOCK_VISIBILITY_SYNC_GENERATION.fetch_add(1, Ordering::AcqRel) + 1;
175203
let app = app.clone();
176204
tauri::async_runtime::spawn(async move {
177205
tokio::time::sleep(Duration::from_millis(100)).await;
178-
if MACOS_DOCK_VISIBILITY_SYNC_GENERATION.load(Ordering::Relaxed) == generation {
206+
if MACOS_DOCK_VISIBILITY_SYNC_GENERATION.load(Ordering::Acquire) == generation {
179207
sync_macos_dock_visibility(&app);
180208
}
181209
});

apps/desktop/src-tauri/src/target_select_overlay.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ pub async fn open_target_select_overlays(
115115
show_overlay(&window);
116116

117117
if should_focus {
118-
window.set_focus().ok();
118+
focus_target_select_overlay(&window);
119119
}
120120

121121
state.spawn(display_id, window.clone());
@@ -127,10 +127,7 @@ pub async fn open_target_select_overlays(
127127
.show(&app)
128128
.await
129129
{
130-
window.show().ok();
131-
if should_focus {
132-
window.set_focus().ok();
133-
}
130+
finish_created_target_select_overlay(&window, should_focus);
134131
}
135132
} else {
136133
let app_clone = app.clone();
@@ -143,10 +140,7 @@ pub async fn open_target_select_overlays(
143140
.show(&app_clone)
144141
.await
145142
{
146-
window.show().ok();
147-
if should_focus {
148-
window.set_focus().ok();
149-
}
143+
finish_created_target_select_overlay(&window, should_focus);
150144
}
151145
});
152146
}
@@ -158,7 +152,7 @@ pub async fn open_target_select_overlays(
158152
.get(&app);
159153

160154
if let Some(window) = focus_window {
161-
window.set_focus().ok();
155+
focus_target_select_overlay(&window);
162156
}
163157

164158
let window_exclusions = general_settings::GeneralSettingsStore::get(&app)
@@ -225,6 +219,27 @@ pub async fn open_target_select_overlays(
225219
Ok(())
226220
}
227221

222+
fn finish_created_target_select_overlay(window: &WebviewWindow, should_focus: bool) {
223+
#[cfg(target_os = "macos")]
224+
let _ = (window, should_focus);
225+
226+
#[cfg(not(target_os = "macos"))]
227+
{
228+
window.show().ok();
229+
if should_focus {
230+
focus_target_select_overlay(window);
231+
}
232+
}
233+
}
234+
235+
fn focus_target_select_overlay(window: &WebviewWindow) {
236+
#[cfg(target_os = "macos")]
237+
let _ = window;
238+
239+
#[cfg(not(target_os = "macos"))]
240+
window.set_focus().ok();
241+
}
242+
228243
fn should_skip_window(window: &Window, exclusions: &[WindowExclusion]) -> bool {
229244
if exclusions.is_empty() {
230245
return false;

apps/desktop/src-tauri/src/windows.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,7 @@ impl ShowCapWindow {
13681368
let should_protect = should_protect_window(app, &title);
13691369

13701370
#[cfg(target_os = "macos")]
1371-
permissions::prepare_macos_panel_window(app);
1371+
let panel_activation_guard = permissions::prepare_macos_panel_window(app);
13721372

13731373
let window = self
13741374
.window_builder(app, "/")
@@ -1408,7 +1408,9 @@ impl ShowCapWindow {
14081408
app.run_on_main_thread({
14091409
let window = window.clone();
14101410
let app = app.clone();
1411+
let panel_activation_guard = panel_activation_guard;
14111412
move || {
1413+
let _panel_activation_guard = panel_activation_guard;
14121414
use tauri_nspanel::cocoa::appkit::NSWindowCollectionBehavior;
14131415
use tauri_nspanel::panel_delegate;
14141416
use crate::panel_manager::try_to_panel;
@@ -1508,7 +1510,7 @@ impl ShowCapWindow {
15081510
};
15091511

15101512
#[cfg(target_os = "macos")]
1511-
permissions::prepare_macos_panel_window(app);
1513+
let panel_activation_guard = permissions::prepare_macos_panel_window(app);
15121514

15131515
let mut window_builder = self
15141516
.window_builder(
@@ -1576,7 +1578,9 @@ impl ShowCapWindow {
15761578
app.run_on_main_thread({
15771579
let window = window.clone();
15781580
let app = app.clone();
1581+
let panel_activation_guard = panel_activation_guard;
15791582
move || {
1583+
let _panel_activation_guard = panel_activation_guard;
15801584
use tauri_nspanel::cocoa::appkit::NSWindowCollectionBehavior;
15811585
use tauri_nspanel::panel_delegate;
15821586
use tauri_nspanel::WebviewWindowExt as NSPanelWebviewWindowExt;
@@ -1612,6 +1616,10 @@ impl ShowCapWindow {
16121616

16131617
panel.set_delegate(delegate);
16141618

1619+
#[allow(non_upper_case_globals)]
1620+
const NSWindowStyleMaskNonActivatingPanel: i32 = 1 << 7;
1621+
panel.set_style_mask(NSWindowStyleMaskNonActivatingPanel);
1622+
16151623
let max_level = unsafe { CGWindowLevelForKey(kCGMaximumWindowLevelKey) };
16161624
panel.set_level(max_level - 1);
16171625

@@ -1920,7 +1928,7 @@ impl ShowCapWindow {
19201928
let should_protect = should_protect_window(app, &title);
19211929

19221930
#[cfg(target_os = "macos")]
1923-
permissions::prepare_macos_panel_window(app);
1931+
let panel_activation_guard = permissions::prepare_macos_panel_window(app);
19241932

19251933
let label = camera_window_label
19261934
.clone()
@@ -2049,7 +2057,9 @@ impl ShowCapWindow {
20492057
app.run_on_main_thread({
20502058
let window = window.clone();
20512059
let app = app.clone();
2060+
let panel_activation_guard = panel_activation_guard;
20522061
move || {
2062+
let _panel_activation_guard = panel_activation_guard;
20532063
use tauri_nspanel::cocoa::appkit::NSWindowCollectionBehavior;
20542064
use tauri_nspanel::panel_delegate;
20552065
use crate::panel_manager::try_to_panel;
@@ -2250,7 +2260,7 @@ impl ShowCapWindow {
22502260
let should_protect = should_protect_window(app, &title);
22512261

22522262
#[cfg(target_os = "macos")]
2253-
permissions::prepare_macos_panel_window(app);
2263+
let panel_activation_guard = permissions::prepare_macos_panel_window(app);
22542264

22552265
#[cfg(target_os = "macos")]
22562266
let window = {
@@ -2323,7 +2333,9 @@ impl ShowCapWindow {
23232333
app.run_on_main_thread({
23242334
let window = window.clone();
23252335
let app = app.clone();
2336+
let panel_activation_guard = panel_activation_guard;
23262337
move || {
2338+
let _panel_activation_guard = panel_activation_guard;
23272339
use tauri_nspanel::cocoa::appkit::NSWindowCollectionBehavior;
23282340
use tauri_nspanel::panel_delegate;
23292341
use tauri_nspanel::WebviewWindowExt as NSPanelWebviewWindowExt;

0 commit comments

Comments
 (0)