From 0ff1664974cbe2abc8ac4f7d8c0f200c482fbbfd Mon Sep 17 00:00:00 2001 From: Tommaso Negri Date: Sun, 12 Jul 2026 15:59:04 +0200 Subject: [PATCH] make Theme monitor dependent --- Sources/phbar/AppDelegate.swift | 5 +- Sources/phbar/CLI/cli+start.swift | 2 - Sources/phbar/Models/PHConfig.swift | 37 +----------- .../Models/PHConfig/PHConfig+Screen.swift | 22 ------- .../PHConfig/PHConfigMonitorOverride.swift | 59 +++++++++++++++++++ Sources/phbar/Models/PHConfig/config.toml | 6 +- Sources/phbar/Models/PHTheme/theme.toml | 5 +- 7 files changed, 72 insertions(+), 64 deletions(-) delete mode 100644 Sources/phbar/Models/PHConfig/PHConfig+Screen.swift create mode 100644 Sources/phbar/Models/PHConfig/PHConfigMonitorOverride.swift diff --git a/Sources/phbar/AppDelegate.swift b/Sources/phbar/AppDelegate.swift index b92c14d..36b278d 100644 --- a/Sources/phbar/AppDelegate.swift +++ b/Sources/phbar/AppDelegate.swift @@ -7,18 +7,19 @@ final class AppDelegate: NSObject, NSApplicationDelegate { private var observers: [IPCObserver] = [] /// Create one bar controller per screen. Each screen resolves its own - /// window and block set from the config, so refresh state and layout stay + /// theme, window and block set from the config, so refresh state and layout stay /// independent across monitors. init( config: PHConfig, screens: [NSScreen], - theme: PHTheme, debug: Bool ) throws { super.init() for screen in screens { + let theme = try PHTheme.load(config.theme(for: screen)) let blocks = try PHBlock.load(config.blocks(for: screen)) + controllers.append( BarController( config: config, diff --git a/Sources/phbar/CLI/cli+start.swift b/Sources/phbar/CLI/cli+start.swift index 7b08c70..c1b77ed 100644 --- a/Sources/phbar/CLI/cli+start.swift +++ b/Sources/phbar/CLI/cli+start.swift @@ -14,7 +14,6 @@ extension phbar { mutating func run() throws { let config = try PHConfig.load() - let theme = try PHTheme.load("voltage") // NOTE: Make sure NSApp.run() runs in the main thread dispatchPrecondition(condition: .onQueue(.main)) @@ -28,7 +27,6 @@ extension phbar { let delegate = try AppDelegate( config: config, screens: screens, - theme: theme, debug: debug ) let app = NSApplication.shared diff --git a/Sources/phbar/Models/PHConfig.swift b/Sources/phbar/Models/PHConfig.swift index 7a79f6d..b996ec0 100644 --- a/Sources/phbar/Models/PHConfig.swift +++ b/Sources/phbar/Models/PHConfig.swift @@ -1,43 +1,12 @@ struct PHConfig: Decodable { + let theme: String let window: String let blocks: String? let env: [String: String]? - let monitors: [String: PHMonitorOverride]? + let monitors: [String: PHConfigMonitorOverride]? private enum CodingKeys: String, CodingKey { - case window, blocks, env + case theme, window, blocks, env case monitors = "monitor" } } - -/// 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. -struct PHMonitorOverride: Decodable { - let window: String? - let blocks: String? -} - -extension PHConfig { - /// The override matching a screen identifier, if any. - /// Pure (no AppKit). Precedence: monitor name → monitor index. - func monitorOverride(screenName: String, screenIndex: Int?) -> PHMonitorOverride? { - guard let monitors else { return nil } - if let byName = monitors[screenName] { return byName } - if let index = screenIndex, let byIndex = monitors[String(index)] { return byIndex } - 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 { - 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 { - monitorOverride(screenName: screenName, screenIndex: screenIndex)?.blocks ?? (blocks ?? "default") - } -} diff --git a/Sources/phbar/Models/PHConfig/PHConfig+Screen.swift b/Sources/phbar/Models/PHConfig/PHConfig+Screen.swift deleted file mode 100644 index 0b516ed..0000000 --- a/Sources/phbar/Models/PHConfig/PHConfig+Screen.swift +++ /dev/null @@ -1,22 +0,0 @@ -import AppKit - -extension PHConfig { - /// The per-monitor override that applies to `screen`, if any. - /// Precedence: monitor name → monitor index. - func monitorOverride(for screen: NSScreen) -> PHMonitorOverride? { - 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") - } -} diff --git a/Sources/phbar/Models/PHConfig/PHConfigMonitorOverride.swift b/Sources/phbar/Models/PHConfig/PHConfigMonitorOverride.swift new file mode 100644 index 0000000..f5c3cdc --- /dev/null +++ b/Sources/phbar/Models/PHConfig/PHConfigMonitorOverride.swift @@ -0,0 +1,59 @@ +import AppKit + +/// 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. +struct PHConfigMonitorOverride: Decodable { + let theme: String? + let window: String? + let blocks: String? +} + +extension PHConfig { + /// The override matching a screen identifier, if any. + /// Pure (no AppKit). 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 } + if let index = screenIndex, let byIndex = monitors[String(index)] { return byIndex } + 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 { + 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 { + 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/Models/PHConfig/config.toml b/Sources/phbar/Models/PHConfig/config.toml index 2e99ede..17b9e6f 100644 --- a/Sources/phbar/Models/PHConfig/config.toml +++ b/Sources/phbar/Models/PHConfig/config.toml @@ -1,19 +1,23 @@ # Default window and block set for every monitor. +# - theme: a named theme at ~/.config/phbar/themes/.toml # - window: a [[window]] definition from the theme file # - blocks: a named set at ~/.config/phbar/blocks/.toml +theme = "default" window = "default" blocks = "default" -# Optional: override the window and/or block set per monitor. +# Optional: override the theme, window, and/or block set per monitor. # - quoted numeric key → NSScreen index # - string key → NSScreen.localizedName (stable across replugs) # Precedence: monitor name → monitor index → the globals above. # Either field may be omitted to inherit its global value. # # [monitor."1"] +# theme = "default" # window = "bottom" # blocks = "laptop" # # [monitor."DELL U2723QE"] +# theme = "external" # window = "clock" # blocks = "external" diff --git a/Sources/phbar/Models/PHTheme/theme.toml b/Sources/phbar/Models/PHTheme/theme.toml index a499af3..c110940 100644 --- a/Sources/phbar/Models/PHTheme/theme.toml +++ b/Sources/phbar/Models/PHTheme/theme.toml @@ -1,13 +1,12 @@ -# phbar theme file ~ Voltage +# phbar theme file # -# Place at ~/.config/phbar/themes/voltage.toml +# Place at ~/.config/phbar/themes/default.toml [[window]] name = "default" anchor = "top" height = 30 width = "100%" -margin = { leading = 30, top = 30, trailing = 30, bottom = 30 } [[window]] name = "bottom"