commit f2cbab62d8272d385cf5381078ea43842596f773
parent aa22d1916da51fa80e0e79a07018b26ca494e135
Author: William Casarin <jb55@jb55.com>
Date: Mon, 9 Feb 2026 16:09:56 -0800
dave: use NSApplication activate to bring app to front on macOS
ViewportCommand::Focus alone does not reliably bring the app window
in front of other applications on macOS. Add a direct call to
NSApplication::activate via objc2-app-kit to ensure the app
actually comes to the foreground when auto-steal focus triggers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
3 files changed, 83 insertions(+), 9 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -4151,6 +4151,8 @@ dependencies = [
"nostrdb",
"notedeck",
"notedeck_ui",
+ "objc2 0.6.1",
+ "objc2-app-kit 0.3.1",
"rand 0.9.2",
"rfd",
"serde",
@@ -4367,10 +4369,10 @@ dependencies = [
"block2 0.5.1",
"libc",
"objc2 0.5.2",
- "objc2-core-data",
- "objc2-core-image",
+ "objc2-core-data 0.2.2",
+ "objc2-core-image 0.2.2",
"objc2-foundation 0.2.2",
- "objc2-quartz-core",
+ "objc2-quartz-core 0.2.2",
]
[[package]]
@@ -4381,10 +4383,15 @@ checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
dependencies = [
"bitflags 2.9.1",
"block2 0.6.1",
+ "libc",
"objc2 0.6.1",
+ "objc2-cloud-kit 0.3.1",
+ "objc2-core-data 0.3.1",
"objc2-core-foundation",
"objc2-core-graphics",
+ "objc2-core-image 0.3.1",
"objc2-foundation 0.3.1",
+ "objc2-quartz-core 0.3.1",
]
[[package]]
@@ -4401,6 +4408,17 @@ dependencies = [
]
[[package]]
+name = "objc2-cloud-kit"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d"
+dependencies = [
+ "bitflags 2.9.1",
+ "objc2 0.6.1",
+ "objc2-foundation 0.3.1",
+]
+
+[[package]]
name = "objc2-contacts"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4424,6 +4442,17 @@ dependencies = [
]
[[package]]
+name = "objc2-core-data"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d"
+dependencies = [
+ "bitflags 2.9.1",
+ "objc2 0.6.1",
+ "objc2-foundation 0.3.1",
+]
+
+[[package]]
name = "objc2-core-foundation"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4460,6 +4489,16 @@ dependencies = [
]
[[package]]
+name = "objc2-core-image"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e"
+dependencies = [
+ "objc2 0.6.1",
+ "objc2-foundation 0.3.1",
+]
+
+[[package]]
name = "objc2-core-location"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4551,6 +4590,17 @@ dependencies = [
]
[[package]]
+name = "objc2-quartz-core"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5"
+dependencies = [
+ "bitflags 2.9.1",
+ "objc2 0.6.1",
+ "objc2-foundation 0.3.1",
+]
+
+[[package]]
name = "objc2-symbols"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4569,13 +4619,13 @@ dependencies = [
"bitflags 2.9.1",
"block2 0.5.1",
"objc2 0.5.2",
- "objc2-cloud-kit",
- "objc2-core-data",
- "objc2-core-image",
+ "objc2-cloud-kit 0.2.2",
+ "objc2-core-data 0.2.2",
+ "objc2-core-image 0.2.2",
"objc2-core-location",
"objc2-foundation 0.2.2",
"objc2-link-presentation",
- "objc2-quartz-core",
+ "objc2-quartz-core 0.2.2",
"objc2-symbols",
"objc2-uniform-type-identifiers",
"objc2-user-notifications",
diff --git a/crates/notedeck_dave/Cargo.toml b/crates/notedeck_dave/Cargo.toml
@@ -33,6 +33,10 @@ dirs = "5"
[target.'cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))'.dependencies]
rfd = { workspace = true }
+[target.'cfg(target_os = "macos")'.dependencies]
+objc2 = "0.6.1"
+objc2-app-kit = { version = "0.3.1", features = ["NSApplication", "NSResponder", "NSRunningApplication"] }
+
[dev-dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros", "test-util"] }
diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs
@@ -908,8 +908,7 @@ impl notedeck::App for Dave {
// Raise the OS window when auto-steal switches to a NeedsInput session
if stole_focus {
- ui.ctx()
- .send_viewport_cmd(egui::ViewportCommand::Focus);
+ activate_app(ui.ctx());
}
// Render UI and handle actions
@@ -927,3 +926,24 @@ impl notedeck::App for Dave {
AppResponse::action(app_action)
}
}
+
+/// Bring the application to the front.
+///
+/// On macOS, egui's ViewportCommand::Focus focuses the window but doesn't
+/// always activate the app (bring it in front of other apps), so we also
+/// call NSApplication::activateIgnoringOtherApps.
+fn activate_app(ctx: &egui::Context) {
+ ctx.send_viewport_cmd(egui::ViewportCommand::Focus);
+
+ #[cfg(target_os = "macos")]
+ {
+ use objc2::MainThreadMarker;
+ use objc2_app_kit::NSApplication;
+
+ // Safety: UI update runs on the main thread
+ if let Some(mtm) = MainThreadMarker::new() {
+ let app = NSApplication::sharedApplication(mtm);
+ unsafe { app.activate() };
+ }
+ }
+}