aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Sources/phbar/Models/Block.swift48
-rw-r--r--Sources/phbar/Models/Cell.swift46
-rw-r--r--Sources/phbar/Views/BlockView.swift4
-rw-r--r--Sources/phbar/Views/CellView.swift10
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)
}
}