prole/prole-app/Sources/MainWindowController.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

94 lines
3.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import AppKit
// MainWindowController builds the regular window you can move/resize.
// It embeds an AppStatusView (3 simple rows, no scrolling) and a tiny
// control bar in the bottomleft with:
// - Refresh (forces a status check)
// - _ Minimize to Status Bar (switches to overlay mode)
final class MainWindowController: NSWindowController {
struct Actions {
let onRefresh: () -> Void
let onMinimizeToStatusBar: () -> Void
}
private let statusView = AppStatusView()
private let actions: Actions
init(serviceChecker: ServiceChecker, actions: Actions) {
// Initialize stored properties before calling super.init
self.actions = actions
let style: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable]
let initialRect = NSRect(x: 0, y: 0, width: 720, height: 420)
let window = NSWindow(contentRect: initialRect, styleMask: style, backing: .buffered, defer: false)
super.init(window: window)
window.isReleasedWhenClosed = false
window.title = "ProleStatus"
window.center()
window.level = .normal
window.collectionBehavior = [.canJoinAllSpaces]
window.appearance = NSAppearance(named: .aqua)
// A plain NSView acts as the content container
let content = NSView()
content.translatesAutoresizingMaskIntoConstraints = false
window.contentView = content
statusView.translatesAutoresizingMaskIntoConstraints = false
content.addSubview(statusView)
// Bottom-left control bar with Refresh and Minimize buttons
let controls = NSStackView()
controls.orientation = .horizontal
controls.spacing = 8
controls.alignment = .centerY
controls.translatesAutoresizingMaskIntoConstraints = false
content.addSubview(controls)
let refreshButton = NSButton(title: "", target: nil, action: nil)
refreshButton.bezelStyle = .texturedRounded
refreshButton.toolTip = "Refresh Now"
refreshButton.target = self
refreshButton.action = #selector(didTapRefresh)
let minimizeButton = NSButton(title: "_", target: nil, action: nil)
minimizeButton.bezelStyle = .texturedRounded
minimizeButton.toolTip = "Minimize to Status Bar"
minimizeButton.target = self
minimizeButton.action = #selector(didTapMinimize)
controls.addArrangedSubview(refreshButton)
controls.addArrangedSubview(minimizeButton)
NSLayoutConstraint.activate([
content.widthAnchor.constraint(equalToConstant: initialRect.width),
content.heightAnchor.constraint(equalToConstant: initialRect.height),
// AppStatusView pinned to top/leading/trailing so timestamp can sit top-right
statusView.leadingAnchor.constraint(equalTo: content.leadingAnchor),
statusView.trailingAnchor.constraint(equalTo: content.trailingAnchor),
statusView.topAnchor.constraint(equalTo: content.topAnchor),
controls.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 8),
controls.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -8)
])
statusView.bindTo(serviceChecker: serviceChecker)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
func show() {
guard let window = window else { return }
window.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: false)
}
func hide() {
window?.orderOut(nil)
}
var isVisible: Bool { window?.isVisible ?? false }
// MARK: - Actions
@objc private func didTapRefresh() { actions.onRefresh() }
@objc private func didTapMinimize() { actions.onMinimizeToStatusBar() }
}