feat(apple): tabbed macOS Settings + stats-overlay placement/toggle + Stream menu
ci / rust (push) Failing after 42s
apple / swift (push) Successful in 54s
ci / web (push) Successful in 29s
ci / docs-site (push) Successful in 32s
android / android (push) Successful in 1m47s
ci / bench (push) Successful in 1m35s
decky / build-publish (push) Successful in 11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
deb / build-publish (push) Successful in 2m21s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 5m27s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 5m28s
docker / deploy-docs (push) Successful in 20s
ci / rust (push) Failing after 42s
apple / swift (push) Successful in 54s
ci / web (push) Successful in 29s
ci / docs-site (push) Successful in 32s
android / android (push) Successful in 1m47s
ci / bench (push) Successful in 1m35s
decky / build-publish (push) Successful in 11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
deb / build-publish (push) Successful in 2m21s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 5m27s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 5m28s
docker / deploy-docs (push) Successful in 20s
The macOS Settings window had outgrown one scrolling pane — split it into a tabbed preferences window (General / Display / Audio / Controllers / Advanced). Each settings group is now a shared @ViewBuilder section, so iOS keeps its single grouped Form and tvOS its pushed-picker layout, each defined once. No setting moved or dropped. New statistics-overlay controls (Settings → Display → Statistics): a show/hide toggle (DefaultsKey.hudEnabled) and a corner picker (HUDPlacement / DefaultsKey.hudPlacement) — the HUD moves to the chosen corner and aligns its text to that edge. A Scene-level "Stream" menu (StreamCommands) carries Show/Hide Statistics (⌘⇧S) and Disconnect (⌘D). Disconnect moved off the HUD button into the menu so it survives the overlay being hidden, wired via .focusedSceneValue. On iOS a material-backed exit chip appears when the HUD is hidden (touch users have no menu/⌘D); tvOS disconnect is unchanged (Siri-Remote Menu button). Builds on macOS/iOS/tvOS; swift test green. Adversarially reviewed (8 findings refuted, 2 minor — the iOS exit-chip contrast fix is included here). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,8 @@ struct ContentView: View {
|
||||
@AppStorage(DefaultsKey.gamepadType) private var gamepadType = 0
|
||||
@AppStorage(DefaultsKey.bitrateKbps) private var bitrateKbps = 0
|
||||
@AppStorage(DefaultsKey.fullscreenWhileStreaming) private var fullscreenWhileStreaming = true
|
||||
@AppStorage(DefaultsKey.hudEnabled) private var hudEnabled = true
|
||||
@AppStorage(DefaultsKey.hudPlacement) private var hudPlacement = HUDPlacement.topTrailing.rawValue
|
||||
@State private var showAddHost = false
|
||||
@State private var pairingTarget: StoredHost?
|
||||
@State private var speedTestTarget: StoredHost?
|
||||
@@ -59,6 +61,13 @@ struct ContentView: View {
|
||||
}
|
||||
}
|
||||
.onDisappear { model.disconnect() } // window closed mid-session (Cmd+N spawns more)
|
||||
// Expose the session to the Scene-level Stream menu (Disconnect ⌘D works even when
|
||||
// the HUD is hidden). tvOS has no such menu.
|
||||
#if !os(tvOS)
|
||||
.focusedSceneValue(\.sessionFocus, SessionFocus(
|
||||
isStreaming: model.connection != nil,
|
||||
disconnect: { model.disconnect() }))
|
||||
#endif
|
||||
#if os(macOS)
|
||||
// Fullscreen only while a session is up (incl. the trust prompt over the blurred stream),
|
||||
// windowed on the host list — so the picker isn't forced fullscreen. Opt-out in Settings.
|
||||
@@ -155,7 +164,8 @@ struct ContentView: View {
|
||||
}
|
||||
|
||||
private func stream(captureEnabled: Bool) -> some View {
|
||||
Group {
|
||||
let placement = HUDPlacement(rawValue: hudPlacement) ?? .topTrailing
|
||||
return Group {
|
||||
if let conn = model.connection {
|
||||
StreamView(
|
||||
connection: conn,
|
||||
@@ -172,9 +182,30 @@ struct ContentView: View {
|
||||
},
|
||||
presentMeter: model.presentLatency
|
||||
)
|
||||
.overlay(alignment: .topTrailing) {
|
||||
if captureEnabled { StreamHUDView(model: model, connection: conn) }
|
||||
.overlay(alignment: placement.alignment) {
|
||||
if captureEnabled && hudEnabled {
|
||||
StreamHUDView(model: model, connection: conn, placement: placement)
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
// Touch users have no menu / ⌘D, so when the HUD (and its Disconnect button)
|
||||
// is hidden, keep a minimal always-reachable exit in a corner. It rides a
|
||||
// material disc (like the HUD) so the glyph stays legible over a bright frame
|
||||
// — this is the sole touch disconnect path when stats are off.
|
||||
.overlay(alignment: .topLeading) {
|
||||
if captureEnabled && !hudEnabled {
|
||||
Button { model.disconnect() } label: {
|
||||
Image(systemName: "xmark")
|
||||
.font(.headline.weight(.semibold))
|
||||
.frame(width: 36, height: 36)
|
||||
.background(.regularMaterial, in: Circle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(12)
|
||||
.accessibilityLabel("Disconnect")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user