diff options
Diffstat (limited to 'Sources')
| -rw-r--r-- | Sources/phbar/Models/Block.swift | 48 | ||||
| -rw-r--r-- | Sources/phbar/Models/Cell.swift | 46 | ||||
| -rw-r--r-- | Sources/phbar/Views/BlockView.swift | 4 | ||||
| -rw-r--r-- | Sources/phbar/Views/CellView.swift | 10 |
4 files changed, 95 insertions, 13 deletions
diff --git a/Sources/phbar/Models/Block.swift b/Sources/phbar/Models/Block.swift index 7eca7c9..95c4ada 100644 --- a/Sources/phbar/Models/Block.swift +++ b/Sources/phbar/Models/Block.swift @@ -1,15 +1,46 @@ import Foundation +/// A named, self-updating unit of the bar. +/// +/// Each block runs a shell command on an optional timer, +/// splits its stdout into ``Cell`` values, and publishes +/// them for rendering. Blocks are identified by ``name`` +/// so they can be refreshed on demand via Darwin +/// notifications (see ``Notifier``). @MainActor final class Block: ObservableObject, Identifiable { + /// Stable identity for SwiftUI diffing. let id = UUID() + /// Identifier used for manual updates via Darwin + /// notifications. + /// + /// Multiple blocks can share the same name. + /// In such case, a single notification will refresh + /// all of them. let name: String + + /// Whether the block is anchored to the left or right + /// side of the bar. let placement: Config.Block.Placement + + /// The shell command to execute for producing the cells. + /// + /// Can be an inline expression or a path to an executable + /// script. Inherits the environment of the phbar process. let command: String + + /// Refresh interval in seconds, or `nil` for a one-shot block. + /// + /// A one-shot block can still be manually refreshed via + /// a Darwin notification (see ``Notifier``). let interval: Double? - @Published var cells: [String] = [] + /// The current cells produced by the last command execution. + /// + /// SwiftUI observes this `@Published` property and re-renders + /// the view tree automatically. + @Published var cells: [Cell] = [] private var tick: Task<Void, Never>? @@ -30,8 +61,8 @@ final class Block: ObservableObject, Identifiable { func runScript() { let command = self.command Task.detached(priority: .utility) { [weak self] in - let cells = Runner.run(command) - await self?.apply(cells) + let lines = Runner.run(command) + await self?.apply(lines) } } @@ -40,11 +71,18 @@ final class Block: ObservableObject, Identifiable { tick?.cancel() } + /// Replace the current cells with freshly parsed lines. + /// + /// Each line becomes a ``Cell``. This is always called + /// on the main actor so SwiftUI sees the update immediately. @MainActor - private func apply(_ cells: [String]) { - self.cells = cells + private func apply(_ lines: [String]) { + self.cells = lines.map { Cell(rawValue: $0) } } + /// Start the interval timer that re-runs the command periodically. + /// + /// Does nothing when ``interval`` is `nil` or ≤ 0. private func startTick() { guard let interval, interval > 0 else { return } tick = Task { [weak self] in diff --git a/Sources/phbar/Models/Cell.swift b/Sources/phbar/Models/Cell.swift new file mode 100644 index 0000000..789f615 --- /dev/null +++ b/Sources/phbar/Models/Cell.swift @@ -0,0 +1,46 @@ +import Foundation +import SwiftUI + +/// A thin wrapper around a single text line captured +/// by a block. +/// +/// When a block executes the command it captures stdout +/// and splits each line into a cell. Each cell is then +/// laid out horizontally respecting the order. +/// +/// Besides the underlying text value, a cell holds an +/// highlighted status that, if true, applies the special +/// background color defined in `Config.swift`. +/// You can set the highlight at runtime by prefixing the +/// text line with the special notation `!!`. +/// +/// E.g: `echo "!! $(date "+%a %d, %H:%M:%S") "` +/// +/// Highlight can have multiple use cases, but it was +/// imagined specifically for distinguishing a single cell +/// within a group. For example when you want to show which +/// workspace is currently active. +struct Cell: RawRepresentable, Identifiable { + /// Stable identity for SwiftUI diffing. + let id = UUID() + + /// The represented text content. + let rawValue: String + + /// Whether the cell is highlighted or not. + let highlighted: Bool + + /// The background color derived from the highlighted state. + /// + /// Returns `Config.highlight` when the cell is highlighted, + /// otherwise `.clear` so the underlying bar background + /// shows through. + var background: Color { + highlighted ? Config.highlight : .clear + } + + init(rawValue: String) { + self.highlighted = rawValue.hasPrefix("!!") + self.rawValue = highlighted ? String(rawValue.dropFirst(2)) : rawValue + } +} diff --git a/Sources/phbar/Views/BlockView.swift b/Sources/phbar/Views/BlockView.swift index b7c0e7f..4bbea7a 100644 --- a/Sources/phbar/Views/BlockView.swift +++ b/Sources/phbar/Views/BlockView.swift @@ -5,8 +5,8 @@ struct BlockView: View { var body: some View { HStack(spacing: 0) { - ForEach(block.cells.indices, id: \.self) { index in - CellView(text: block.cells[index]) + ForEach(block.cells) { cell in + CellView(cell: cell) } } } diff --git a/Sources/phbar/Views/CellView.swift b/Sources/phbar/Views/CellView.swift index 494667c..6b39eb1 100644 --- a/Sources/phbar/Views/CellView.swift +++ b/Sources/phbar/Views/CellView.swift @@ -1,13 +1,11 @@ import SwiftUI struct CellView: View { - var text: String + var cell: Cell var body: some View { - VStack { - Text(text) - } - .frame(height: Config.height) - .background(Color.cyan) + Text(cell.rawValue) + .frame(height: Config.height) + .background(cell.background) } } |
