108 lines
2.9 KiB
Swift
108 lines
2.9 KiB
Swift
import SwiftUI
|
|
|
|
@MainActor
|
|
final class BarController: ObservableObject {
|
|
let config: PHConfig
|
|
let screen: NSScreen
|
|
let theme: PHTheme
|
|
let window: PHThemeWindow
|
|
@Published var blocks: [PHBlock]
|
|
|
|
var arrangedBlocks: [PHBlock] {
|
|
blocks.filter { $0.centered != true }
|
|
}
|
|
|
|
var centeredBlocks: [PHBlock] {
|
|
blocks.filter { $0.centered == true }
|
|
}
|
|
|
|
init(config: PHConfig, screen: NSScreen, theme: PHTheme, blocks: [PHBlock], environment: PHEnvironment = .process, debug: Bool) {
|
|
self.config = config
|
|
self.screen = screen
|
|
self.theme = theme
|
|
self.window = Self.window(for: config, screen: screen, from: theme)
|
|
self.blocks = blocks
|
|
|
|
for block in blocks {
|
|
block.text = text(for: block)
|
|
block.style = style(for: block)
|
|
block.environment = environment
|
|
block.debug = debug
|
|
}
|
|
}
|
|
}
|
|
|
|
// Theming
|
|
|
|
extension BarController {
|
|
/// Resolve the window definition for a given screen, applying any
|
|
/// 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 {
|
|
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
|
|
/// built-in default.
|
|
static func resolve(window name: String, from theme: PHTheme) -> PHThemeWindow {
|
|
guard let window = theme.windows?.first(where: { $0.name == name }) else {
|
|
guard let defaultWindow = theme.windows?.first(where: { $0.name == "default" }) else {
|
|
return PHThemeWindow.default
|
|
}
|
|
return defaultWindow
|
|
}
|
|
return window
|
|
}
|
|
|
|
func text(for block: PHBlock) -> PHThemeText {
|
|
guard let text = theme.texts?.first(where: { $0.name == block.textName }) else {
|
|
guard let defaultText = theme.texts?.first(where: { $0.name == "default" }) else {
|
|
return PHThemeText.default
|
|
}
|
|
return defaultText
|
|
}
|
|
return text
|
|
}
|
|
|
|
func style(for block: PHBlock) -> PHThemeStyle {
|
|
guard let style = theme.styles?.first(where: { $0.name == block.styleName }) else {
|
|
guard let defaultStyle = theme.styles?.first(where: { $0.name == "default" }) else {
|
|
return PHThemeStyle.default
|
|
}
|
|
return defaultStyle
|
|
}
|
|
return style
|
|
}
|
|
}
|
|
|
|
// Refresh
|
|
|
|
extension BarController {
|
|
/// Start auto-refresh for every block.
|
|
func startAutoRefresh() {
|
|
for block in blocks {
|
|
block.startAutoRefresh()
|
|
}
|
|
}
|
|
|
|
/// Stop auto-refresh for every block.
|
|
func stopAutoRefresh() {
|
|
for block in blocks {
|
|
block.stopAutoRefresh()
|
|
}
|
|
}
|
|
|
|
/// Force every block to recompute its label immediately (manual refresh).
|
|
/// Each block updates concurrently; the view refreshes as results arrive.
|
|
func refresh() {
|
|
for block in blocks {
|
|
Task { await block.update() }
|
|
}
|
|
}
|
|
}
|