From a354fdecc89623f3fc8489e32ecb91aeeca01899 Mon Sep 17 00:00:00 2001 From: lwouis Date: Tue, 26 May 2026 20:37:48 +0200 Subject: [PATCH] fix: always persist menubar icon removal from dragging (#5654) --- src/Menubar.swift | 15 +++++++++++++ .../settings-window/tabs/GeneralTab.swift | 21 ++++--------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/Menubar.swift b/src/Menubar.swift index f0588c09..1d085da5 100644 --- a/src/Menubar.swift +++ b/src/Menubar.swift @@ -8,6 +8,7 @@ class Menubar { private static var supportProjectMenuItem: NSMenuItem! private static var myAccountMenuItem: NSMenuItem! private static let menuDelegate = MenubarMenuDelegate() + private static var isVisibleObserver: NSKeyValueObservation? @discardableResult static func addMenuItem(_ title: String, _ action: Selector, _ keyEquivalent: String, _ symbolName: String?, _ color: NSColor? = nil, _ target: AnyObject? = nil) -> NSMenuItem { @@ -55,6 +56,7 @@ class Menubar { // the WindowServer has already laid the menubar out at its imageless default size, then // invalidates NSStatusBarContentView mid-FBS-scene-update — `_NSDetectedLayoutRecursion`. applyMenubarIconPreferences() + observeRemovalFromMenubar() #if DEBUG installQAMenuMiddleClickMonitor() #endif @@ -152,6 +154,19 @@ class Menubar { } } + // The user can ⌘-drag the icon off the menubar (enabled by `.removalAllowed`). When that + // happens, `isVisible` flips true→false and we persist the preference. Observing here in + // `Menubar` rather than in `GeneralTab` means we react whether or not Settings is open. + static private func observeRemovalFromMenubar() { + statusItem.behavior = .removalAllowed + isVisibleObserver = statusItem.observe(\.isVisible, options: [.old, .new]) { _, change in + if change.oldValue == true && change.newValue == false { + Preferences.set("menubarIconShown", "false") + GeneralTab.menuIconShownToggle?.setSilently(.off) + } + } + } + private static var badgeDotLayer: CALayer? static private func loadPreferredIcon() { diff --git a/src/preferences/settings-window/tabs/GeneralTab.swift b/src/preferences/settings-window/tabs/GeneralTab.swift index 4bfa9021..ab64ea9c 100644 --- a/src/preferences/settings-window/tabs/GeneralTab.swift +++ b/src/preferences/settings-window/tabs/GeneralTab.swift @@ -3,20 +3,20 @@ import Sparkle class GeneralTab { static var menubarIconDropdown: NSPopUpButton? + static var menuIconShownToggle: Switch? static var updatesPolicyDropdown: NSPopUpButton? static var crashPolicyDropdown: NSPopUpButton? static var policyLock = false - private static var menubarIsVisibleObserver: NSKeyValueObservation? static func initTab() -> NSView { let startAtLogin = TableGroupView.Row(leftTitle: NSLocalizedString("Start at login", comment: ""), rightViews: [LabelAndControl.makeSwitch("startAtLogin")]) menubarIconDropdown = LabelAndControl.makeDropdown("menubarIcon", MenubarIconPreference.allCases) - let menuIconShownToggle = LabelAndControl.makeSwitch("menubarIconShown") + menuIconShownToggle = LabelAndControl.makeSwitch("menubarIconShown") let menubarIcon = TableGroupView.Row(leftTitle: NSLocalizedString("Menubar icon", comment: ""), rightViews: [ menubarIconDropdown!, - menuIconShownToggle, + menuIconShownToggle!, ]) let language = TableGroupView.Row(leftTitle: NSLocalizedString("Language", comment: ""), rightViews: [LabelAndControl.makeDropdown("language", LanguagePreference.allCases, extraAction: setLanguageCallback)]) @@ -36,7 +36,6 @@ class GeneralTab { cell.bezelStyle = .regularSquare cell.arrowPosition = .arrowAtBottom cell.imagePosition = .imageOverlaps - enableDraggingOffMenubarIcon(menuIconShownToggle) let captureWindowsInBackground = TableGroupView.Row(leftTitle: NSLocalizedString("Capture windows in the background", comment: ""), subTitle: NSLocalizedString("When disabled, avoids the macOS purple screen-recording indicator, and avoids flickers when playing DRM video. Thumbnails will be less up-to-date.", comment: ""), rightViews: [LabelAndControl.makeSwitch("captureWindowsInBackground")]) @@ -67,9 +66,8 @@ class GeneralTab { } static func cleanup() { - // KVO observer is invalidated by nil-out - menubarIsVisibleObserver = nil menubarIconDropdown = nil + menuIconShownToggle = nil updatesPolicyDropdown = nil crashPolicyDropdown = nil } @@ -81,17 +79,6 @@ class GeneralTab { crashPolicyDropdown?.selectItem(at: CachedUserDefaults.intFromMacroPref("crashPolicy", CrashPolicyPreference.allCases)) } - private static func enableDraggingOffMenubarIcon(_ menuIconShownToggle: Switch) { - Menubar.statusItem.behavior = .removalAllowed - menubarIsVisibleObserver = Menubar.statusItem.observe(\.isVisible, options: [.old, .new]) { _, change in - Logger.debug { "---- \(change)" } - if change.oldValue == true && change.newValue == false { - menuIconShownToggle.state = .off - LabelAndControl.controlWasChanged(menuIconShownToggle, nil) - } - } - } - @objc static func resetPreferences() { let alert = NSAlert() alert.alertStyle = .critical