196 lines
6.5 KiB
Swift
196 lines
6.5 KiB
Swift
import AppKit
|
|
import Foundation
|
|
import OSLog
|
|
import SwiftUI
|
|
import MacToolsPluginKit
|
|
|
|
protocol MenuBarCommandRunning {
|
|
func setMenuBarAutohide(_ isEnabled: Bool) throws
|
|
}
|
|
|
|
struct ProcessMenuBarCommandRunner: MenuBarCommandRunning {
|
|
private let localization: PluginLocalization
|
|
|
|
init(localization: PluginLocalization = PluginLocalization(bundle: .main)) {
|
|
self.localization = localization
|
|
}
|
|
|
|
func setMenuBarAutohide(_ isEnabled: Bool) throws {
|
|
let script = """
|
|
tell application "System Events"
|
|
tell dock preferences
|
|
set autohide menu bar to \(isEnabled ? "true" : "false")
|
|
end tell
|
|
end tell
|
|
"""
|
|
|
|
let appleScript = NSAppleScript(source: script)
|
|
var error: NSDictionary?
|
|
appleScript?.executeAndReturnError(&error)
|
|
|
|
if let error {
|
|
let message = (error[NSAppleScript.errorMessage] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
throw NSError(
|
|
domain: "AutoHideMenuBarPlugin",
|
|
code: (error[NSAppleScript.errorNumber] as? Int) ?? 1,
|
|
userInfo: [
|
|
NSLocalizedDescriptionKey: message?.isEmpty == false
|
|
? message!
|
|
: localization.string(
|
|
"error.toggleFailed",
|
|
defaultValue: "切换菜单栏自动隐藏失败"
|
|
)
|
|
]
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
public final class AutoHideMenuBarPluginFactory: NSObject, MacToolsPluginBundleFactory {
|
|
public static func makeProvider(context: PluginRuntimeContext) throws -> any PluginProvider {
|
|
AutoHideMenuBarPluginProvider(context: context)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private struct AutoHideMenuBarPluginProvider: PluginProvider {
|
|
let context: PluginRuntimeContext
|
|
|
|
func makePlugins() -> [any MacToolsPlugin] {
|
|
[AutoHideMenuBarPlugin(localization: PluginLocalization(bundle: context.resourceBundle))]
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
final class AutoHideMenuBarPlugin: MacToolsPlugin, PluginPrimaryPanel {
|
|
let metadata: PluginMetadata
|
|
|
|
let primaryPanelDescriptor = PluginPrimaryPanelDescriptor(
|
|
controlStyle: .switch,
|
|
menuActionBehavior: .keepPresented
|
|
)
|
|
|
|
var onStateChange: (() -> Void)?
|
|
var requestPermissionGuidance: ((String) -> Void)?
|
|
var shortcutBindingResolver: ((String) -> ShortcutBinding?)?
|
|
|
|
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "cc.ggbond.mactools", category: "AutoHideMenuBarPlugin")
|
|
private let commandRunner: any MenuBarCommandRunning
|
|
private let stateReader: () -> Bool
|
|
private let localization: PluginLocalization
|
|
|
|
private var isMenuBarHidden: Bool
|
|
private var lastErrorMessage: String?
|
|
|
|
init(
|
|
commandRunner: (any MenuBarCommandRunning)? = nil,
|
|
stateReader: @escaping () -> Bool = { AutoHideMenuBarPlugin.readMenuBarAutohideState() },
|
|
localization: PluginLocalization = PluginLocalization(bundle: .main)
|
|
) {
|
|
self.localization = localization
|
|
self.commandRunner = commandRunner ?? ProcessMenuBarCommandRunner(localization: localization)
|
|
self.stateReader = stateReader
|
|
self.metadata = PluginMetadata(
|
|
id: "auto-hide-menu-bar",
|
|
title: localization.string("metadata.title", defaultValue: "自动隐藏菜单栏"),
|
|
iconName: "menubar.rectangle",
|
|
iconTint: Color(nsColor: .systemIndigo),
|
|
order: 42,
|
|
defaultDescription: localization.string(
|
|
"metadata.description",
|
|
defaultValue: "自动隐藏菜单栏,提供更完整的屏幕显示空间"
|
|
)
|
|
)
|
|
self.isMenuBarHidden = stateReader()
|
|
}
|
|
|
|
var primaryPanelState: PluginPanelState {
|
|
PluginPanelState(
|
|
subtitle: isMenuBarHidden
|
|
? localization.string("panel.subtitle.enabled", defaultValue: "已开启")
|
|
: localization.string("panel.subtitle.disabled", defaultValue: "已关闭"),
|
|
isOn: isMenuBarHidden,
|
|
isExpanded: false,
|
|
isEnabled: true,
|
|
isVisible: true,
|
|
detail: nil,
|
|
errorMessage: lastErrorMessage
|
|
)
|
|
}
|
|
|
|
var permissionRequirements: [PluginPermissionRequirement] { [] }
|
|
var settingsSections: [PluginSettingsSection] { [] }
|
|
var shortcutDefinitions: [PluginShortcutDefinition] { [] }
|
|
|
|
func refresh() {
|
|
let latestState = stateReader()
|
|
if latestState != isMenuBarHidden {
|
|
isMenuBarHidden = latestState
|
|
onStateChange?()
|
|
}
|
|
}
|
|
|
|
func handleAction(_ action: PluginPanelAction) {
|
|
guard case let .setSwitch(isEnabled) = action else {
|
|
return
|
|
}
|
|
|
|
setMenuBarHidden(isEnabled)
|
|
}
|
|
|
|
func permissionState(for permissionID: String) -> PluginPermissionState {
|
|
PluginPermissionState(isGranted: true, footnote: nil)
|
|
}
|
|
|
|
func handlePermissionAction(id: String) {}
|
|
func handleSettingsAction(id: String) {}
|
|
func handleShortcutAction(id: String) {}
|
|
|
|
private func setMenuBarHidden(_ isEnabled: Bool) {
|
|
do {
|
|
try commandRunner.setMenuBarAutohide(isEnabled)
|
|
isMenuBarHidden = isEnabled
|
|
lastErrorMessage = nil
|
|
onStateChange?()
|
|
} catch {
|
|
logger.error("Failed to update menu bar auto-hide: \(error.localizedDescription, privacy: .public)")
|
|
lastErrorMessage = error.localizedDescription
|
|
refresh()
|
|
onStateChange?()
|
|
}
|
|
}
|
|
|
|
nonisolated static func readMenuBarAutohideState(
|
|
globalDefaults: UserDefaults = .standard,
|
|
dockDefaults: UserDefaults? = UserDefaults(suiteName: "com.apple.dock")
|
|
) -> Bool {
|
|
resolvedMenuBarAutohideState(
|
|
globalValue: globalDefaults.object(forKey: "_HIHideMenuBar"),
|
|
dockValue: dockDefaults?.object(forKey: "autohide-menubar")
|
|
)
|
|
}
|
|
|
|
nonisolated static func resolvedMenuBarAutohideState(globalValue: Any?, dockValue: Any?) -> Bool {
|
|
if let value = boolValue(from: globalValue) {
|
|
return value
|
|
}
|
|
|
|
if let value = boolValue(from: dockValue) {
|
|
return value
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
private nonisolated static func boolValue(from value: Any?) -> Bool? {
|
|
switch value {
|
|
case let value as Bool:
|
|
value
|
|
case let value as NSNumber:
|
|
value.boolValue
|
|
default:
|
|
nil
|
|
}
|
|
}
|
|
}
|