Files
Flaro/Flixy/Views/Settings/SubtitleAppearanceView.swift
T
ScarriffleandClaude Opus 5 0815432efd Untertitel lassen sich jetzt einstellen
Groesse in vier Stufen, Schrift und Hintergrund als fuenf feste Paare —
Weiss, Weiss auf Schwarz, Schwarz auf Weiss, Gelb, Gelb auf Schwarz. Feste
Paare statt zweier Regler, weil unabhaengige Achsen unlesbare Mischungen
zulassen: Schwarz auf durchsichtig ueber einer Nachtszene ist eine
gueltige Einstellung und eine wertlose.

Die Uebersetzung nach AVFoundation ist eine Tabelle aus Schluesseln, bei
der jeder Fehler lautlos ist — ein falscher Schluessel laesst den
Initialisierer `nil` zurueckgeben, und dann gibt es gar keine Regel. Auf
dem Bildschirm sieht „keine Regel\" genauso aus wie „Regel ohne Wirkung\".
Zehn Tests decken das ab, darunter die drei Fallen: Alpha zuerst in der
Farbe, Prozentwert statt Punktgroesse, und der Hintergrund auch fuer
„durchsichtig\" ausdruecklich gesetzt statt weggelassen — weggelassen gilt
sonst, was Jellyfins WebVTT selbst mitbringt, und das ist oft ein
schwarzer Kasten.

Als Protokollmethode, nicht als Feld an `PlaybackSource`: die wird einmal
beim Oeffnen gelesen, und eine Einstellung, die erst beim naechsten Film
wirkt, haelt jeder fuer kaputt. Angewandt wird an zwei Stellen — am
laufenden Gegenstand und am frisch angelegten. Nur das erste zu tun hiesse,
dass die naechste Folge wieder in der Vorgabe erscheint.

VLC erbt die leere Vorgabe. Die Einstellungsseite sagt beides: dass es fuer
den Standardplayer gilt, und dass eingebrannte Bilduntertitel von Blu-rays
sich nicht gestalten lassen, weil es dort keinen Text gibt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-28 21:22:22 +02:00

126 lines
4.3 KiB
Swift

import SwiftUI
import FlixyDesign
import FlixyPlayback
/// Wie Untertitel aussehen sollen.
struct SubtitleAppearanceView: View {
@Environment(SubtitleAppearance.self) private var appearance
@Environment(\.flixyAccent) private var accent
var body: some View {
@Bindable var appearance = appearance
Form {
SettingsHeader(
symbol: "textformat.size",
colour: SettingsPalette.subtitles,
title: "subtitleStyle.title",
explanation: "subtitleStyle.headerExplanation"
)
Section {
preview
} header: {
Text("subtitleStyle.previewHeader")
}
Section {
Picker("subtitleStyle.size", selection: $appearance.size) {
ForEach(SubtitleStyle.Size.allCases) { size in
Text(size.label).tag(size)
}
}
} footer: {
Text("subtitleStyle.sizeExplanation")
}
Section {
Picker("subtitleStyle.palette", selection: $appearance.palette) {
ForEach(SubtitleStyle.Palette.allCases) { palette in
Text(palette.label).tag(palette)
}
}
} header: {
Text("subtitleStyle.paletteHeader")
} footer: {
// Die Grenze gehört hierher, nicht nur in einen Kommentar:
// bei eingebrannten Untertiteln tut jede dieser Einstellungen
// sichtbar nichts, und ohne Erklärung sieht das nach einem
// Fehler aus.
Text("subtitleStyle.paletteExplanation")
}
}
.tint(accent)
.flixyForm()
.scrollContentBackground(.hidden)
.background(Color.flixyCanvas)
.navigationTitle(Text("subtitleStyle.title"))
.flixyInlineTitle()
}
/// Ein Beispielsatz über grauer Fläche.
///
/// **Eine Nachbildung, nicht AVFoundations Renderer.** Sie zeigt die
/// Wirkung der Einstellung, nicht das Bild auf den Punkt genau — wer
/// später zwei Pixel „repariert", repariert die falsche Sache.
private var preview: some View {
ZStack {
// Mittelgrau: auf Weiss wäre weisse Schrift unsichtbar, auf
// Schwarz schwarze. Ein Grauton zeigt beide ehrlich.
LinearGradient(
colors: [Color(white: 0.35), Color(white: 0.18)],
startPoint: .top,
endPoint: .bottom
)
Text("subtitleStyle.previewText")
.font(.system(size: 15 * CGFloat(appearance.size.rawValue) / 100, weight: .medium))
.foregroundStyle(appearance.style.foreground.color)
.padding(.horizontal, 8)
.padding(.vertical, 3)
.background(appearance.style.background.color)
.shadow(
color: appearance.style.wantsEdgeShadow ? .black.opacity(0.9) : .clear,
radius: 2,
y: 1
)
.padding(FlixySpacing.regular)
}
.frame(height: 96)
.clipShape(.rect(cornerRadius: FlixyRadius.card))
.listRowInsets(EdgeInsets())
.listRowBackground(Color.clear)
}
}
extension SubtitleStyle.RGBA {
var color: Color {
Color(.sRGB, red: red, green: green, blue: blue, opacity: alpha)
}
}
/// Die Namen der Stufen stehen neben der Ansicht, nicht im Modell — dasselbe
/// Muster wie bei `PlaybackSettings.Quality`.
extension SubtitleStyle.Size {
var label: LocalizedStringKey {
switch self {
case .small: "subtitleStyle.size.small"
case .normal: "subtitleStyle.size.normal"
case .large: "subtitleStyle.size.large"
case .huge: "subtitleStyle.size.huge"
}
}
}
extension SubtitleStyle.Palette {
var label: LocalizedStringKey {
switch self {
case .whiteOnClear: "subtitleStyle.palette.whiteOnClear"
case .whiteOnBlack: "subtitleStyle.palette.whiteOnBlack"
case .blackOnWhite: "subtitleStyle.palette.blackOnWhite"
case .yellowOnClear: "subtitleStyle.palette.yellowOnClear"
case .yellowOnBlack: "subtitleStyle.palette.yellowOnBlack"
}
}
}