From 684f3718134ed582f13fc1aa89528654a7b0bca0 Mon Sep 17 00:00:00 2001 From: "Bat.bat" <45396585+williambj1@users.noreply.github.com> Date: Mon, 5 Aug 2024 02:44:06 +1000 Subject: [PATCH] Fix items not properly hidden prior to macOS 14 --- HeliPort.xcodeproj/project.pbxproj | 8 +++ .../Appearance/StatusMenu/HPMenuItem.swift | 45 ++++++++++++++++ .../MenuItemView/HidableMenuItemView.swift | 45 ++++++++++++++++ .../MenuItemView/KeyValueMenuItemView.swift | 19 ++++--- .../MenuItemView/SelectableMenuItemView.swift | 25 ++------- .../StatusMenu/StatusMenuBase.swift | 52 ++++++++++--------- .../StatusMenu/StatusMenuLegacy.swift | 4 +- .../StatusMenu/StatusMenuModern.swift | 14 ++--- 8 files changed, 149 insertions(+), 63 deletions(-) create mode 100644 HeliPort/Appearance/StatusMenu/HPMenuItem.swift create mode 100644 HeliPort/Appearance/StatusMenu/MenuItemView/HidableMenuItemView.swift diff --git a/HeliPort.xcodeproj/project.pbxproj b/HeliPort.xcodeproj/project.pbxproj index 5465726..a7bcef2 100644 --- a/HeliPort.xcodeproj/project.pbxproj +++ b/HeliPort.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ 505EC11D2C5BD89400F4E4EA /* StatusBarIconManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505EC11C2C5BD89400F4E4EA /* StatusBarIconManager.swift */; }; 505EC11F2C5BD8ED00F4E4EA /* StatusBarIconLegacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505EC11E2C5BD8ED00F4E4EA /* StatusBarIconLegacy.swift */; }; 505EC1212C5BD95700F4E4EA /* StatusBarIconModern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505EC1202C5BD95700F4E4EA /* StatusBarIconModern.swift */; }; + 50680FCA2C5FDB75008AF10F /* HidableMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50680FC92C5FDB75008AF10F /* HidableMenuItemView.swift */; }; + 5068784E2C5FE16C0046150A /* HPMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5068784D2C5FE16C0046150A /* HPMenuItem.swift */; }; 5088F70826BEA46F009E3A15 /* KextInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5088F70726BEA46F009E3A15 /* KextInfo.swift */; }; 50C5F2D027C23074009E3F68 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 50C5F2CF27C23074009E3F68 /* KeychainAccess */; }; 50C5F2D327C230D6009E3F68 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 50C5F2D227C230D6009E3F68 /* Sparkle */; }; @@ -93,6 +95,8 @@ 505EC11C2C5BD89400F4E4EA /* StatusBarIconManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarIconManager.swift; sourceTree = ""; }; 505EC11E2C5BD8ED00F4E4EA /* StatusBarIconLegacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarIconLegacy.swift; sourceTree = ""; }; 505EC1202C5BD95700F4E4EA /* StatusBarIconModern.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarIconModern.swift; sourceTree = ""; }; + 50680FC92C5FDB75008AF10F /* HidableMenuItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HidableMenuItemView.swift; sourceTree = ""; }; + 5068784D2C5FE16C0046150A /* HPMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HPMenuItem.swift; sourceTree = ""; }; 5088F70726BEA46F009E3A15 /* KextInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KextInfo.swift; sourceTree = ""; }; 50E7700E2C5A7CD600DB1160 /* UpdateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateManager.swift; sourceTree = ""; }; 50F4959724BDD26D00AE4C08 /* LoginItemManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginItemManager.swift; sourceTree = ""; }; @@ -159,6 +163,7 @@ isa = PBXGroup; children = ( 5013B9052C5E1ED5002C5006 /* MenuItemView */, + 5068784D2C5FE16C0046150A /* HPMenuItem.swift */, 5013B8FF2C5E1E00002C5006 /* StatusMenuBase.swift */, 5013B9012C5E1E36002C5006 /* StatusMenuLegacy.swift */, 5013B9032C5E1EA9002C5006 /* StatusMenuModern.swift */, @@ -169,6 +174,7 @@ 5013B9052C5E1ED5002C5006 /* MenuItemView */ = { isa = PBXGroup; children = ( + 50680FC92C5FDB75008AF10F /* HidableMenuItemView.swift */, 5013B9062C5E1EF2002C5006 /* KeyValueMenuItemView.swift */, 5013B9082C5E1FF4002C5006 /* SectionMenuItemView.swift */, 5013B90A2C5E2029002C5006 /* SelectableMenuItemView.swift */, @@ -447,6 +453,7 @@ 75FDF38C2481D25A00B2A601 /* Api.c in Sources */, F379276F24A0A4A50087FF2B /* CredentialsManager.swift in Sources */, F336D63C24B497B6004C98C4 /* NetworkManager+Data.swift in Sources */, + 5068784E2C5FE16C0046150A /* HPMenuItem.swift in Sources */, 5013B9102C5E2265002C5006 /* WiFiMenuItemViewLegacy.swift in Sources */, F33A1F3F24C8347F008ED2BD /* NSLocalizedString+Extensions.swift in Sources */, F379277124A0A52E0087FF2B /* Log.swift in Sources */, @@ -462,6 +469,7 @@ 5013B9002C5E1E00002C5006 /* StatusMenuBase.swift in Sources */, 505EC1212C5BD95700F4E4EA /* StatusBarIconModern.swift in Sources */, BCFA32EB2424D2BE00E23603 /* AppDelegate.swift in Sources */, + 50680FCA2C5FDB75008AF10F /* HidableMenuItemView.swift in Sources */, 5013B9072C5E1EF2002C5006 /* KeyValueMenuItemView.swift in Sources */, BCCB2AA4243708090005BB82 /* WiFiMenuItemView.swift in Sources */, 1380C36124D54BFD00A448CF /* PrefsSavedNetworksView.swift in Sources */, diff --git a/HeliPort/Appearance/StatusMenu/HPMenuItem.swift b/HeliPort/Appearance/StatusMenu/HPMenuItem.swift new file mode 100644 index 0000000..3656228 --- /dev/null +++ b/HeliPort/Appearance/StatusMenu/HPMenuItem.swift @@ -0,0 +1,45 @@ +// +// HPMenuItem.swift +// HeliPort +// +// Created by Bat.bat on 5/8/2024. +// Copyright © 2024 OpenIntelWireless. All rights reserved. +// + +/* + * This program and the accompanying materials are licensed and made available + * under the terms and conditions of the The 3-Clause BSD License + * which accompanies this distribution. The full text of the license may be found at + * https://opensource.org/licenses/BSD-3-Clause + */ + +import Cocoa + +/// Use this class instead of `NSMenuItem` when applying a custom view +class HPMenuItem: NSMenuItem { + + var highlightable: Bool = false + + convenience init(highlightable: Bool = false) { + self.init() + self.highlightable = highlightable + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override init(title string: String, action selector: Selector?, keyEquivalent charCode: String) { + super.init(title: string, action: selector, keyEquivalent: charCode) + } + + override var isHidden: Bool { + willSet { + (self.view as? HidableMenuItemView)?.visible = !newValue + } + } + + override func _canBeHighlighted() -> Bool { + return highlightable || super._canBeHighlighted() + } +} diff --git a/HeliPort/Appearance/StatusMenu/MenuItemView/HidableMenuItemView.swift b/HeliPort/Appearance/StatusMenu/MenuItemView/HidableMenuItemView.swift new file mode 100644 index 0000000..bb7bfb2 --- /dev/null +++ b/HeliPort/Appearance/StatusMenu/MenuItemView/HidableMenuItemView.swift @@ -0,0 +1,45 @@ +// +// HidableMenuItemView.swift +// HeliPort +// +// Created by Bat.bat on 5/8/2024. +// Copyright © 2024 OpenIntelWireless. All rights reserved. +// + +/* + * This program and the accompanying materials are licensed and made available + * under the terms and conditions of the The 3-Clause BSD License + * which accompanies this distribution. The full text of the license may be found at + * https://opensource.org/licenses/BSD-3-Clause + */ + +class HidableMenuItemView: NSView { + private let height: CGFloat + private var heightConstraint: NSLayoutConstraint! + + var visible: Bool = true { + willSet(visible) { + // Manually adjust visibility for item views prior to macOS 14 + if #unavailable(macOS 14) { + isHidden = !visible + heightConstraint.constant = visible ? height : 0 + layoutSubtreeIfNeeded() + } + } + } + + init(height: NSMenuItem.ItemHeight) { + self.height = height.rawValue + super.init(frame: .zero) + + translatesAutoresizingMaskIntoConstraints = false + + heightConstraint = heightAnchor.constraint(equalToConstant: height.rawValue) + heightConstraint.priority = NSLayoutConstraint.Priority(rawValue: 1000) + heightConstraint.isActive = true + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/HeliPort/Appearance/StatusMenu/MenuItemView/KeyValueMenuItemView.swift b/HeliPort/Appearance/StatusMenu/MenuItemView/KeyValueMenuItemView.swift index b27e3d8..7dfd49c 100644 --- a/HeliPort/Appearance/StatusMenu/MenuItemView/KeyValueMenuItemView.swift +++ b/HeliPort/Appearance/StatusMenu/MenuItemView/KeyValueMenuItemView.swift @@ -15,7 +15,7 @@ import Cocoa -class KeyValueMenuItemView: NSView { +class KeyValueMenuItemView: HidableMenuItemView { enum Inset: CGFloat { case standard = 14 @@ -34,7 +34,14 @@ class KeyValueMenuItemView: NSView { valueLabel.font = NSFont.systemFont(ofSize: 12, weight: .regular) valueLabel.textColor = .secondaryLabelColor - super.init(frame: .zero) + let height = { + if #available(macOS 11, *) { + return NSMenuItem.ItemHeight.textModern + } + return NSMenuItem.ItemHeight.textLegacy + }() + + super.init(height: height) addSubview(keyLabel) addSubview(valueLabel) @@ -45,15 +52,7 @@ class KeyValueMenuItemView: NSView { keyLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) valueLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) - let height = { - if #available(macOS 11, *) { - return NSMenuItem.ItemHeight.textModern - } - return NSMenuItem.ItemHeight.textLegacy - }() - NSLayoutConstraint.activate([ - heightAnchor.constraint(equalToConstant: height.rawValue), keyLabel.centerYAnchor.constraint(equalTo: centerYAnchor), keyLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: inset.rawValue), valueLabel.centerYAnchor.constraint(equalTo: centerYAnchor), diff --git a/HeliPort/Appearance/StatusMenu/MenuItemView/SelectableMenuItemView.swift b/HeliPort/Appearance/StatusMenu/MenuItemView/SelectableMenuItemView.swift index 3288bf9..1482078 100644 --- a/HeliPort/Appearance/StatusMenu/MenuItemView/SelectableMenuItemView.swift +++ b/HeliPort/Appearance/StatusMenu/MenuItemView/SelectableMenuItemView.swift @@ -15,13 +15,7 @@ import Cocoa -class SelectableMenuItem: NSMenuItem { - override func _canBeHighlighted() -> Bool { - return true - } -} - -class SelectableMenuItemView: NSView { +class SelectableMenuItemView: HidableMenuItemView { private class HoverView: NSView { override func draw(_ dirtyRect: NSRect) { @@ -34,8 +28,6 @@ class SelectableMenuItemView: NSView { // MARK: Initializers private var currentWindow: NSWindow? - private var heightConstraint: NSLayoutConstraint! - private let effectView: NSView? private let effectPadding: CGFloat = { @@ -45,10 +37,7 @@ class SelectableMenuItemView: NSView { return 0 }() - private let height: CGFloat - init(height: NSMenuItem.ItemHeight, hoverStyle: HoverStyle) { - self.height = height.rawValue switch hoverStyle { case .none: effectView = nil @@ -64,7 +53,7 @@ class SelectableMenuItemView: NSView { effectView = view } - super.init(frame: .zero) + super.init(height: height) if let view = effectView { self.addSubview(view) } } @@ -74,20 +63,18 @@ class SelectableMenuItemView: NSView { // MARK: Public - public enum HoverStyle { + enum HoverStyle { case none case selection case greytint } - public func checkHighlight() { + func checkHighlight() { if effectView != nil, let position = currentWindow?.mouseLocationOutsideOfEventStream { isMouseOver = bounds.contains(convert(position, from: nil)) } } - // MARK: Internal - var isMouseOver: Bool = false { willSet(hover) { if let view = effectView as? NSVisualEffectView { @@ -108,10 +95,6 @@ class SelectableMenuItemView: NSView { effectView?.layer?.masksToBounds = true } - heightConstraint = heightAnchor.constraint(equalToConstant: self.height) - heightConstraint.priority = NSLayoutConstraint.Priority(rawValue: 1000) - heightConstraint.isActive = true - effectView?.translatesAutoresizingMaskIntoConstraints = false effectView?.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } effectView?.leftAnchor.constraint(equalTo: self.leftAnchor, constant: effectPadding).isActive = true diff --git a/HeliPort/Appearance/StatusMenu/StatusMenuBase.swift b/HeliPort/Appearance/StatusMenu/StatusMenuBase.swift index 7d6d013..5717b1a 100644 --- a/HeliPort/Appearance/StatusMenu/StatusMenuBase.swift +++ b/HeliPort/Appearance/StatusMenu/StatusMenuBase.swift @@ -132,47 +132,47 @@ class StatusMenuBase: NSMenu, NSMenuDelegate { // - MARK: Common Menu items - let bsdItem = NSMenuItem(title: .interfaceName) - let macItem = NSMenuItem(title: .macAddress) - let itlwmVerItem = NSMenuItem(title: .itlwmVer) + let bsdItem = HPMenuItem(title: .interfaceName) + let macItem = HPMenuItem(title: .macAddress) + let itlwmVerItem = HPMenuItem(title: .itlwmVer) - let enableLoggingItem = NSMenuItem(title: .enableWiFiLog) - let createReportItem = NSMenuItem(title: .createReport) - let diagnoseItem = NSMenuItem(title: .openDiagnostics) + let enableLoggingItem = HPMenuItem(title: .enableWiFiLog) + let createReportItem = HPMenuItem(title: .createReport) + let diagnoseItem = HPMenuItem(title: .openDiagnostics) let hardwareInfoSeparator = NSMenuItem.separator() let networkItemListSeparator = NSMenuItem.separator() - let aboutItem = NSMenuItem(title: .aboutHeliport) + let aboutItem = HPMenuItem(title: .aboutHeliport) let checkUpdateItem = { - let item = NSMenuItem(title: .checkUpdates) + let item = HPMenuItem(title: .checkUpdates) item.target = UpdateManager.sharedController item.action = #selector(SPUStandardUpdaterController.checkForUpdates(_:)) return item }() let quitSeparator = NSMenuItem.separator() - let quitItem = NSMenuItem(title: .quitHeliport, + let quitItem = HPMenuItem(title: .quitHeliport, action: #selector(clickMenuItem(_:)), keyEquivalent: "q") - let toggleLaunchItem = NSMenuItem(title: .launchLogin, + let toggleLaunchItem = HPMenuItem(title: .launchLogin, action: #selector(clickMenuItem(_:))) // MARK: - WiFi connected items - let currentNetworkItem = SelectableMenuItem() - let ipAddresssItem = NSMenuItem(title: .ipAddr) - let routerItem = NSMenuItem(title: .routerStr) - let internetItem = NSMenuItem(title: .internetStr) - let securityItem = NSMenuItem(title: .securityStr) - let bssidItem = NSMenuItem(title: .bssidStr) - let channelItem = NSMenuItem(title: .channelStr) - let countryCodeItem = NSMenuItem(title: .countryCodeStr) - let rssiItem = NSMenuItem(title: .rssiStr) - let noiseItem = NSMenuItem(title: .noiseStr) - let txRateItem = NSMenuItem(title: .txRateStr) - let phyModeItem = NSMenuItem(title: .phyModeStr) - let mcsIndexItem = NSMenuItem(title: .mcsStr) - let nssItem = NSMenuItem(title: .nssStr) + let currentNetworkItem = HPMenuItem(highlightable: true) + let ipAddresssItem = HPMenuItem(title: .ipAddr) + let routerItem = HPMenuItem(title: .routerStr) + let internetItem = HPMenuItem(title: .internetStr) + let securityItem = HPMenuItem(title: .securityStr) + let bssidItem = HPMenuItem(title: .bssidStr) + let channelItem = HPMenuItem(title: .channelStr) + let countryCodeItem = HPMenuItem(title: .countryCodeStr) + let rssiItem = HPMenuItem(title: .rssiStr) + let noiseItem = HPMenuItem(title: .noiseStr) + let txRateItem = HPMenuItem(title: .txRateStr) + let phyModeItem = HPMenuItem(title: .phyModeStr) + let mcsIndexItem = HPMenuItem(title: .mcsStr) + let nssItem = HPMenuItem(title: .nssStr) // - MARK: Init @@ -245,7 +245,9 @@ class StatusMenuBase: NSMenu, NSMenuDelegate { addItem(item) } - func addNetworkItem(_ item: NSMenuItem = SelectableMenuItem(), insertAt: Int? = nil, hidden: Bool = false, + func addNetworkItem(_ item: NSMenuItem = HPMenuItem(highlightable: true), + insertAt: Int? = nil, + hidden: Bool = false, networkInfo: NetworkInfo = NetworkInfo(ssid: "placeholder")) -> NSMenuItem { item.isHidden = hidden diff --git a/HeliPort/Appearance/StatusMenu/StatusMenuLegacy.swift b/HeliPort/Appearance/StatusMenu/StatusMenuLegacy.swift index 124fee1..3105b80 100644 --- a/HeliPort/Appearance/StatusMenu/StatusMenuLegacy.swift +++ b/HeliPort/Appearance/StatusMenu/StatusMenuLegacy.swift @@ -198,7 +198,9 @@ final class StatusMenuLegacy: StatusMenuBase, StatusMenuItems { // - MARK: Overrides - override func addNetworkItem(_ item: NSMenuItem = SelectableMenuItem(), insertAt: Int? = nil, hidden: Bool = false, + override func addNetworkItem(_ item: NSMenuItem = HPMenuItem(highlightable: true), + insertAt: Int? = nil, + hidden: Bool = false, networkInfo: NetworkInfo = NetworkInfo(ssid: "placeholder")) -> NSMenuItem { item.view = WifiMenuItemViewLegacy(networkInfo: networkInfo) diff --git a/HeliPort/Appearance/StatusMenu/StatusMenuModern.swift b/HeliPort/Appearance/StatusMenu/StatusMenuModern.swift index f7c08da..36068ef 100644 --- a/HeliPort/Appearance/StatusMenu/StatusMenuModern.swift +++ b/HeliPort/Appearance/StatusMenu/StatusMenuModern.swift @@ -21,7 +21,7 @@ final class StatusMenuModern: StatusMenuBase, StatusMenuItems { // - MARK: Menu items private lazy var statusItem: NSMenuItem = { - let item = NSMenuItem() + let item = HPMenuItem() let view = StateSwitchMenuItemView(title: .Modern.wifi) { sender in _ = sender.state == .on ? power_on() : power_off() } @@ -30,13 +30,13 @@ final class StatusMenuModern: StatusMenuBase, StatusMenuItems { }() private let knownSectionItem: NSMenuItem = { - let item = NSMenuItem() + let item = HPMenuItem() item.view = SectionMenuItemView(title: .Modern.knownNetwork) return item }() private lazy var otherSectionItem: NSMenuItem = { - let item = SelectableMenuItem() + let item = HPMenuItem(highlightable: true) item.isHidden = true item.view = SectionMenuItemView(title: .Modern.otherNetworks) { expand in self.otherNetworkItemList.filter { $0.isEnabled } @@ -46,8 +46,8 @@ final class StatusMenuModern: StatusMenuBase, StatusMenuItems { return item }() - private let manuallyJoinItem = NSMenuItem(title: .Modern.joinNetworks) - private let networkPanelItem = NSMenuItem(title: .Modern.wifiSettings) + private let manuallyJoinItem = HPMenuItem(title: .Modern.joinNetworks) + private let networkPanelItem = HPMenuItem(title: .Modern.wifiSettings) lazy var enabledNetworkCardItems: [NSMenuItem] = [] @@ -257,7 +257,9 @@ final class StatusMenuModern: StatusMenuBase, StatusMenuItems { super.addClickItem(item) } - override func addNetworkItem(_ item: NSMenuItem = SelectableMenuItem(), insertAt: Int? = nil, hidden: Bool = false, + override func addNetworkItem(_ item: NSMenuItem = HPMenuItem(highlightable: true), + insertAt: Int? = nil, + hidden: Bool = false, networkInfo: NetworkInfo = NetworkInfo(ssid: "placeholder")) -> NSMenuItem { item.view = WifiMenuItemViewModern(networkInfo: networkInfo) return super.addNetworkItem(item, insertAt: insertAt, hidden: hidden, networkInfo: networkInfo)