diff --git a/Sources/phbar/AppDelegate.swift b/Sources/phbar/AppDelegate.swift index 36b278d..017602a 100644 --- a/Sources/phbar/AppDelegate.swift +++ b/Sources/phbar/AppDelegate.swift @@ -17,8 +17,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate { super.init() for screen in screens { - let theme = try PHTheme.load(config.theme(for: screen)) - let blocks = try PHBlock.load(config.blocks(for: screen)) + let name = screen.localizedName + let index = NSScreen.screens.firstIndex(of: screen) + let theme = try PHTheme.load(config.theme(screenName: name, screenIndex: index)) + let blocks = try PHBlock.load(config.blocks(screenName: name, screenIndex: index)) controllers.append( BarController( diff --git a/Sources/phbar/Models/PHConfig/PHConfigMonitorOverride.swift b/Sources/phbar/Models/PHConfig/PHConfigMonitorOverride.swift index f5c3cdc..dc24db8 100644 --- a/Sources/phbar/Models/PHConfig/PHConfigMonitorOverride.swift +++ b/Sources/phbar/Models/PHConfig/PHConfigMonitorOverride.swift @@ -1,18 +1,28 @@ -import AppKit +import Foundation /// A per-monitor entry from the config's `[monitor]` table. /// /// Either field is optional; an unset field inherits the matching global -/// (`window` or `blocks`) so a monitor can override just one of the two. +/// (`theme`, `window`, or `blocks`) so a monitor can override just one of them. struct PHConfigMonitorOverride: Decodable { let theme: String? let window: String? let blocks: String? + + init(theme: String? = nil, window: String? = nil, blocks: String? = nil) { + self.theme = theme + self.window = window + self.blocks = blocks + } } extension PHConfig { - /// The override matching a screen identifier, if any. - /// Pure (no AppKit). Precedence: monitor name → monitor index. + /// The per-monitor override matching the given screen identity, if any. + /// + /// This is the single entry point for per-monitor resolution: the + /// `theme`/`window`/`blocks` helpers below all delegate to it. Kept free of + /// AppKit so the resolution logic is testable without an `NSScreen`. + /// Precedence: monitor name → monitor index. func monitorOverride(screenName: String, screenIndex: Int?) -> PHConfigMonitorOverride? { guard let monitors else { return nil } if let byName = monitors[screenName] { return byName } @@ -20,40 +30,21 @@ extension PHConfig { return nil } - /// Window definition name for a screen (pure resolver). - /// Override's `window` if set, else the global `window`. - func windowName(screenName: String, screenIndex: Int?) -> String { + /// Theme name for a screen: the override's `theme` if set, otherwise the + /// global `theme`. + func theme(screenName: String, screenIndex: Int?) -> String { + monitorOverride(screenName: screenName, screenIndex: screenIndex)?.theme ?? theme + } + + /// Window definition name for a screen: the override's `window` if set, + /// otherwise the global `window`. + func window(screenName: String, screenIndex: Int?) -> String { monitorOverride(screenName: screenName, screenIndex: screenIndex)?.window ?? window } - /// Block-set name for a screen (pure resolver). - /// Override's `blocks` if set, else the global `blocks` (default `"default"`). - func blocksName(screenName: String, screenIndex: Int?) -> String { + /// Block-set name for a screen: the override's `blocks` if set, otherwise + /// the global `blocks` (defaulting to `"default"`). + func blocks(screenName: String, screenIndex: Int?) -> String { monitorOverride(screenName: screenName, screenIndex: screenIndex)?.blocks ?? (blocks ?? "default") } - - /// The per-monitor override that applies to `screen`, if any. - /// Precedence: monitor name → monitor index. - func monitorOverride(for screen: NSScreen) -> PHConfigMonitorOverride? { - let index = NSScreen.screens.firstIndex(of: screen) - return monitorOverride(screenName: screen.localizedName, screenIndex: index) - } - - /// Window definition name for `screen`: the override's `window` if set, - /// otherwise the global `window`. - func windowName(for screen: NSScreen) -> String { - monitorOverride(for: screen)?.window ?? window - } - - /// Block-set name for `screen`: the override's `blocks` if set, otherwise - /// the global `blocks` (defaulting to `"default"`). - func blocks(for screen: NSScreen) -> String { - monitorOverride(for: screen)?.blocks ?? (blocks ?? "default") - } - - /// Theme name for `screen`: the override's `theme` if set, otherwise - /// the global `theme` (defaulting to `"default"`). - func theme(for screen: NSScreen) -> String { - monitorOverride(for: screen)?.theme ?? theme - } } diff --git a/Sources/phbar/Views/BarController.swift b/Sources/phbar/Views/BarController.swift index 56940d6..810499d 100644 --- a/Sources/phbar/Views/BarController.swift +++ b/Sources/phbar/Views/BarController.swift @@ -35,10 +35,15 @@ final class BarController: ObservableObject { extension BarController { /// Resolve the window definition for a given screen, applying any - /// per-monitor override from the config. Selection itself lives on - /// `PHConfig` (see `windowName(for:)`). + /// per-monitor override from the config. Selection lives on `PHConfig` + /// (see `window(screenName:screenIndex:)`); this looks the name up in the + /// theme, falling back to "default". static func window(for config: PHConfig, screen: NSScreen, from theme: PHTheme) -> PHThemeWindow { - resolve(window: config.windowName(for: screen), from: theme) + let name = config.window( + screenName: screen.localizedName, + screenIndex: NSScreen.screens.firstIndex(of: screen) + ) + return resolve(window: name, from: theme) } /// Look up a window definition by name, falling back to "default" then the