Der Mac ist nicht immer Catalyst
Die Bedienung mit Zeiger und Tastatur hing an `#if targetEnvironment(macCatalyst)`. Das ist in dem Bau, der tatsächlich benutzt wird, **falsch**: „für iPad angepasst" ist buchstäblich der iOS-Bau, der auf Apple Silicon startet, und dort ist die Abfrage nicht erfüllt. Klick-zum-Anhalten, Leertaste und die Pfeiltasten waren damit genau dort abgeschaltet, wo sie gebraucht werden. Jetzt beantwortet `FlixyPlatform.isMac` die Frage zur Laufzeit und kennt beide Wege. Die Ansicht bleibt dabei eine einzige: `focusable(_:)` nimmt den Schalter entgegen, die Tastenhandler geben sonst `.ignored` zurück. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9a4b6b1e06
commit
37a6c2648f
@@ -32,6 +32,8 @@ struct PlayerView: View {
|
|||||||
@State private var hintTask: Task<Void, Never>?
|
@State private var hintTask: Task<Void, Never>?
|
||||||
/// Ob die Tastatur bei diesem Bildschirm ankommt. Nur auf dem Mac gesetzt.
|
/// Ob die Tastatur bei diesem Bildschirm ankommt. Nur auf dem Mac gesetzt.
|
||||||
@FocusState private var keyboardFocus: Bool
|
@FocusState private var keyboardFocus: Bool
|
||||||
|
/// Ob Zeiger und Tastatur gelten. Einmal beim Start ermittelt.
|
||||||
|
private var isMac: Bool { FlixyPlatform.isMac }
|
||||||
/// Wann der Zeiger zuletzt die Bedienelemente geholt hat.
|
/// Wann der Zeiger zuletzt die Bedienelemente geholt hat.
|
||||||
@State private var lastPointerReveal: Date = .distantPast
|
@State private var lastPointerReveal: Date = .distantPast
|
||||||
|
|
||||||
@@ -124,36 +126,28 @@ struct PlayerView: View {
|
|||||||
//
|
//
|
||||||
// Dort ist beides selbstverständlich: Leertaste hält an, die Pfeile
|
// Dort ist beides selbstverständlich: Leertaste hält an, die Pfeile
|
||||||
// springen, und die Bedienelemente kommen von allein, sobald sich der
|
// springen, und die Bedienelemente kommen von allein, sobald sich der
|
||||||
// Zeiger regt. Auf dem iPhone gibt es keines von beidem, und
|
// Zeiger regt. Auf dem iPhone gibt es keines von beidem.
|
||||||
// `focusable` legte dort nur einen Fokusrahmen über den Film.
|
//
|
||||||
#if targetEnvironment(macCatalyst)
|
// Zur Laufzeit entschieden, nicht beim Übersetzen: „für iPad
|
||||||
.focusable()
|
// angepasst" ist der iOS-Bau und läuft trotzdem auf dem Mac. Ein
|
||||||
|
// `#if` hätte das Ganze genau dort abgeschaltet. `focusable(_:)`
|
||||||
|
// nimmt den Schalter entgegen, die Tastenhandler geben sonst
|
||||||
|
// `.ignored` zurück — so bleibt es **eine** Ansicht, und der Player
|
||||||
|
// wird nicht neu aufgebaut, nur weil die Frage vom Gerät abhängt.
|
||||||
|
.focusable(isMac)
|
||||||
.focusEffectDisabled()
|
.focusEffectDisabled()
|
||||||
.focused($keyboardFocus)
|
.focused($keyboardFocus)
|
||||||
// Von Hand gesetzt, weil sonst niemand den Fokus hat: der Player
|
// Von Hand gesetzt, weil sonst niemand den Fokus hat: der Player
|
||||||
// besteht aus Knöpfen, und solange keiner angeklickt wurde, ginge
|
// besteht aus Knöpfen, und solange keiner angeklickt wurde, ginge
|
||||||
// jeder Tastendruck ins Leere.
|
// jeder Tastendruck ins Leere.
|
||||||
.onAppear { keyboardFocus = true }
|
.onAppear { if isMac { keyboardFocus = true } }
|
||||||
.onKeyPress(.space) {
|
.onKeyPress(.space) { onMac { model.togglePlayPause() } }
|
||||||
model.togglePlayPause()
|
.onKeyPress(.leftArrow) { onMac { seek(by: -Double(settings.rewindSeconds)) } }
|
||||||
revealControls()
|
.onKeyPress(.rightArrow) { onMac { seek(by: Double(settings.forwardSeconds)) } }
|
||||||
return .handled
|
|
||||||
}
|
|
||||||
.onKeyPress(.leftArrow) {
|
|
||||||
seek(by: -Double(settings.rewindSeconds))
|
|
||||||
revealControls()
|
|
||||||
return .handled
|
|
||||||
}
|
|
||||||
.onKeyPress(.rightArrow) {
|
|
||||||
seek(by: Double(settings.forwardSeconds))
|
|
||||||
revealControls()
|
|
||||||
return .handled
|
|
||||||
}
|
|
||||||
.onContinuousHover { phase in
|
.onContinuousHover { phase in
|
||||||
guard case .active = phase else { return }
|
guard isMac, case .active = phase else { return }
|
||||||
pointerMoved()
|
pointerMoved()
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
.sheet(item: $trackPicker) { kind in
|
.sheet(item: $trackPicker) { kind in
|
||||||
TrackPickerSheet(model: model, kind: kind)
|
TrackPickerSheet(model: model, kind: kind)
|
||||||
}
|
}
|
||||||
@@ -602,12 +596,20 @@ struct PlayerView: View {
|
|||||||
/// ein Tipp, der sofort anhält, nähme den einzigen Weg zu Scrubber,
|
/// ein Tipp, der sofort anhält, nähme den einzigen Weg zu Scrubber,
|
||||||
/// Tonspur und Qualität.
|
/// Tonspur und Qualität.
|
||||||
private func primaryTap() {
|
private func primaryTap() {
|
||||||
#if targetEnvironment(macCatalyst)
|
guard isMac else {
|
||||||
|
toggleControls()
|
||||||
|
return
|
||||||
|
}
|
||||||
model.togglePlayPause()
|
model.togglePlayPause()
|
||||||
revealControls()
|
revealControls()
|
||||||
#else
|
}
|
||||||
toggleControls()
|
|
||||||
#endif
|
/// Führt eine Tastenaktion aus — aber nur dort, wo es eine Tastatur gibt.
|
||||||
|
private func onMac(_ action: () -> Void) -> KeyPress.Result {
|
||||||
|
guard isMac else { return .ignored }
|
||||||
|
action()
|
||||||
|
revealControls()
|
||||||
|
return .handled
|
||||||
}
|
}
|
||||||
|
|
||||||
private func toggleControls() {
|
private func toggleControls() {
|
||||||
|
|||||||
@@ -12,6 +12,30 @@ import AppKit
|
|||||||
/// Systeme verschiedene Namen dafür haben, steht der Unterschied hier und
|
/// Systeme verschiedene Namen dafür haben, steht der Unterschied hier und
|
||||||
/// nirgends sonst. Sonst wandert er in zwanzig Dateien und wird beim
|
/// nirgends sonst. Sonst wandert er in zwanzig Dateien und wird beim
|
||||||
/// nächsten Bildschirm vergessen.
|
/// nächsten Bildschirm vergessen.
|
||||||
|
/// Auf was für einem Gerät läuft das hier gerade?
|
||||||
|
public enum FlixyPlatform {
|
||||||
|
/// Läuft die App auf einem Mac?
|
||||||
|
///
|
||||||
|
/// **Zwei Wege führen dorthin, und für den Übersetzer sehen sie
|
||||||
|
/// verschieden aus.** Catalyst ist ein eigener Bau und meldet sich als
|
||||||
|
/// `targetEnvironment(macCatalyst)`. „Für iPad angepasst" ist dagegen
|
||||||
|
/// buchstäblich der iOS-Bau, der auf Apple Silicon startet — dort ist
|
||||||
|
/// dieselbe Abfrage schlicht falsch.
|
||||||
|
///
|
||||||
|
/// Wer nur die erste Hälfte prüft, schreibt Code für den Mac, der auf dem
|
||||||
|
/// Mac nicht läuft: übersetzt sauber, ist aber genau dort abgeschaltet,
|
||||||
|
/// wo er gebraucht wird. Deshalb hier beide Wege, an einer Stelle.
|
||||||
|
public static let isMac: Bool = {
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
true
|
||||||
|
#elseif os(iOS)
|
||||||
|
ProcessInfo.processInfo.isiOSAppOnMac
|
||||||
|
#else
|
||||||
|
true
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
/// Eine schmale Titelzeile — auf dem Mac gibt es sie nicht.
|
/// Eine schmale Titelzeile — auf dem Mac gibt es sie nicht.
|
||||||
public func flixyInlineTitle() -> some View {
|
public func flixyInlineTitle() -> some View {
|
||||||
|
|||||||
Reference in New Issue
Block a user