Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/macos-autosave-name.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tray-icon": minor
---

Add `TrayIconBuilder::with_autosave_name` (and corresponding `TrayIconAttributes::autosave_name` field) so callers can hand AppKit an `NSStatusItem.setAutosaveName` key on macOS. With the autosave name set, AppKit persists the user's ⌘+drag position across launches via `NSUserDefaults`. Without it, the status item resets to the default leftmost slot every launch — which on notched MacBooks (M1+ Pro/Max/14"/16") collides with the camera cutout. No-op on Linux / Windows.
35 changes: 35 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,22 @@ pub struct TrayIconAttributes {
/// on the user's panel. This may not be shown in all visualizations.
/// - **Windows:** Unsupported.
pub title: Option<String>,

/// Autosave name passed to `NSStatusItem.setAutosaveName`. AppKit
/// uses this key to persist the user's ⌘+drag position across
/// app launches via NSUserDefaults. Without it, the status item
/// snaps back to the default leftmost slot every launch — which
/// on notched MacBooks (M1+ Pro/Max/14"/16") collides with the
/// camera cutout.
///
/// Pick a stable, app-unique string (typically a reverse-DNS
/// identifier like `"com.example.app.tray"`); changing it
/// invalidates the saved position.
///
/// ## Platform-specific
///
/// - **macOS only.** No-op on Linux / Windows.
pub autosave_name: Option<String>,
}

impl Default for TrayIconAttributes {
Expand All @@ -216,6 +232,7 @@ impl Default for TrayIconAttributes {
menu_on_left_click: true,
menu_on_right_click: true,
title: None,
autosave_name: None,
}
}
}
Expand Down Expand Up @@ -305,6 +322,24 @@ impl TrayIconBuilder {
self
}

/// Set the autosave name handed to `NSStatusItem.setAutosaveName`.
/// AppKit uses this key to persist the user's ⌘+drag position
/// across launches via NSUserDefaults. Without it, the status
/// item snaps back to the default leftmost slot every launch —
/// which on notched MacBooks collides with the camera cutout.
///
/// Pick a stable, app-unique string (typically a reverse-DNS
/// identifier like `"com.example.app.tray"`); changing it
/// invalidates the saved position.
///
/// ## Platform-specific
///
/// - **macOS only.** No-op on Linux / Windows.
pub fn with_autosave_name<S: AsRef<str>>(mut self, name: S) -> Self {
self.attrs.autosave_name = Some(name.as_ref().to_string());
self
}

/// Whether to show the tray menu on left click or not, default is `true`.
///
/// ## Platform-specific:
Expand Down
12 changes: 12 additions & 0 deletions src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ impl TrayIcon {
NSStatusBar::systemStatusBar().statusItemWithLength(NSVariableStatusItemLength)
};

// When the caller supplied an autosave name, hand it to
// AppKit so the user's ⌘+drag position survives app
// launches via NSUserDefaults. Without this, every launch
// the status item resets to the default leftmost slot,
// which on notched MacBooks (M1+ Pro/Max/14"/16") collides
// with the camera cutout.
if let Some(name) = attrs.autosave_name.as_deref() {
unsafe {
ns_status_item.setAutosaveName(Some(&NSString::from_str(name)));
}
}

set_icon_for_ns_status_item_button(
&ns_status_item,
attrs.icon.clone(),
Expand Down