prole/prole-app/Sources/StatusItemController.swift
chrisfu 8a6e8738db ProleStatus: app-window default, light splash restored, status bar controls; endpoint config via properties; build + docs
- Default to Application Window mode with full app menu; status bar overlay remains available
- Restore clickable startup Splash Tip (5s) with animated GIF and live counter; use light theme (Aqua)
- Status bar item: monospaced "P" icon; left click shows overlay (status mode) or main window (app mode)
- Overlay: add Maximize button (□) to toggle back to main window; keep click‑through elsewhere
- Global hotkey Cmd+Opt+Shift+P toggles modes; Prole menu item mirrors the same toggle and updates title dynamically
- Application menu (Prole): Show Status Bar / Show Main Window (contextual), Refresh Now, Quit
- Application Window layout: non‑scrolling, vertical 1‑line rows (svc, k3s aggregate, local) with top‑right timestamp; bottom‑left controls (⟳ Refresh, _ Minimize)
- Parameterize endpoints via `prole.properties` (bundled + user override). Replace legacy raspberry with retropie defaults
- ServiceChecker + UI read endpoints from Config loader; tooltips/labels reflect configured hosts/ports
- Build script: generate `Info.plist` with `LSUIElement=false`; bundle resources (`prole-type.gif`, `prole.properties`); ad‑hoc codesign. Universal build supported
- Documentation: rewrite README with technical build/run/config details and operational posture

Files:
- proleStatus/Sources/: AppDelegate.swift, OverlayWindow.swift, StatusView.swift, StatusItemController.swift,
  SplashTipWindowController.swift, MainWindowController.swift, AppStatusView.swift, ServiceChecker.swift, Config.swift
- proleStatus/build.sh
- proleStatus/prole.properties
- proleStatus/README.md

Notes:
- Build verified via `./build.sh build` (arm64). App starts in App Window mode with light theme; menu + hotkey + overlay toggle operate as intended
2025-12-01 21:08:30 -08:00

103 lines
3.4 KiB
Swift

import AppKit
final class StatusItemController {
struct Actions {
let onLeftClick: () -> Void
let onToggleStatusBar: () -> Void
let onToggleAppWindow: () -> Void
let onRefresh: () -> Void
let onQuit: () -> Void
}
private var statusItem: NSStatusItem?
private let actions: Actions
private var stateStatusBarVisible: Bool = true
private var stateAppWindowVisible: Bool = false
init(actions: Actions) {
self.actions = actions
createStatusItemIfNeeded()
}
func setState(statusBarVisible: Bool, appWindowVisible: Bool) {
self.stateStatusBarVisible = statusBarVisible
self.stateAppWindowVisible = appWindowVisible
}
func hideStatusItem() {
if let item = statusItem {
NSStatusBar.system.removeStatusItem(item)
statusItem = nil
}
}
func showStatusItem() {
createStatusItemIfNeeded()
}
var isVisible: Bool { statusItem != nil }
private func createStatusItemIfNeeded() {
guard statusItem == nil else { return }
let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem = item
if let button = item.button {
let font = NSFont.monospacedSystemFont(ofSize: 13, weight: .bold)
button.image = nil
button.attributedTitle = NSAttributedString(string: "P", attributes: [ .font: font ])
button.target = self
button.action = #selector(handleClick)
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
button.toolTip = "ProleStatus"
}
}
@objc private func handleClick(_ sender: Any?) {
guard let event = NSApp.currentEvent else {
actions.onLeftClick(); return
}
switch event.type {
case .rightMouseUp:
showMenu()
default:
actions.onLeftClick()
}
}
private func showMenu() {
guard let button = statusItem?.button else { return }
let menu = NSMenu(title: "ProleStatus")
let statusBarTitle = stateStatusBarVisible ? "Hide Status Bar Icon" : "Show Status Bar Icon"
let appWindowTitle = stateAppWindowVisible ? "Hide Application Window" : "Show Application Window"
let sbItem = NSMenuItem(title: statusBarTitle, action: #selector(toggleStatusBar), keyEquivalent: "")
sbItem.target = self
menu.addItem(sbItem)
let winItem = NSMenuItem(title: appWindowTitle, action: #selector(toggleAppWindow), keyEquivalent: "")
winItem.target = self
menu.addItem(winItem)
menu.addItem(.separator())
let refreshItem = NSMenuItem(title: "Refresh Now", action: #selector(refreshNow), keyEquivalent: "r")
refreshItem.target = self
menu.addItem(refreshItem)
menu.addItem(.separator())
let quitItem = NSMenuItem(title: "Quit ProleStatus", action: #selector(quitApp), keyEquivalent: "q")
quitItem.target = self
menu.addItem(quitItem)
let point = NSPoint(x: 0, y: button.bounds.minY - 3)
menu.popUp(positioning: nil, at: point, in: button)
}
@objc private func toggleStatusBar() { actions.onToggleStatusBar() }
@objc private func toggleAppWindow() { actions.onToggleAppWindow() }
@objc private func refreshNow() { actions.onRefresh() }
@objc private func quitApp() { actions.onQuit() }
}