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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a1ff5cb7bb
commit
0815432efd
@@ -0,0 +1,125 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user