Add per-page element customization and panel settings polish

Phase 5 of the player panels redesign:
- Per-page element toggles now rendered and editable for every page:
  comments (author avatars), related (thumbnails), chapters (thumbnails),
  queue (previously played section), tracks (video/audio/subtitles/speed
  sections)
- Page bar labels toggle (icons-only bar shows the selected page's name)
- Layout mode schematic diagram in the details panel settings
- NEW badge machinery for future page types (versionAdded on PanelPageType)
- Typed per-page settings accessors on PanelPageConfiguration and VM update
  helpers for each page kind
- Unit tests for per-page settings round-trip and accessor fallbacks

The standalone quality selector sheet is unaffected: section gating defaults
to everything visible and only the embedded tracks page passes preset
settings through.
This commit is contained in:
Arkadiusz Fal
2026-09-22 09:37:10 +02:00
parent 052cb76702
commit c289e7576e
19 changed files with 618 additions and 68 deletions

View File

@@ -78,7 +78,11 @@ extension QualitySelectorView {
#endif
}
.onAppear {
selectedTab = initialTab
// Fall back to the first visible tab when the requested one is
// hidden by the tracks page section settings.
selectedTab = availableTabs.contains(initialTab)
? initialTab
: (availableTabs.first ?? .video)
}
}
@@ -90,13 +94,15 @@ extension QualitySelectorView {
// tvOS: each row is its own rounded card with vertical spacing so the
// system focus hover effect has clean bounds (no clipped dividers).
VStack(spacing: 8) {
mediaSelectionRow(
destination: .video,
label: String(localized: "player.quality.video"),
systemImage: "film",
value: currentVideoDisplayValue
)
.focused($inlinePanelInitialFocus)
if availableTabs.contains(.video) {
mediaSelectionRow(
destination: .video,
label: String(localized: "player.quality.video"),
systemImage: "film",
value: currentVideoDisplayValue
)
.focused($inlinePanelInitialFocus)
}
if availableTabs.contains(.audio) {
mediaSelectionRow(
destination: .audio,
@@ -116,14 +122,18 @@ extension QualitySelectorView {
}
#else
VStack(spacing: 0) {
mediaSelectionRow(
destination: .video,
label: String(localized: "player.quality.video"),
systemImage: "film",
value: currentVideoDisplayValue
)
if availableTabs.contains(.video) {
mediaSelectionRow(
destination: .video,
label: String(localized: "player.quality.video"),
systemImage: "film",
value: currentVideoDisplayValue
)
}
if availableTabs.contains(.audio) {
Divider()
if availableTabs.contains(.video) {
Divider()
}
mediaSelectionRow(
destination: .audio,
label: String(localized: "stream.audio"),
@@ -132,7 +142,9 @@ extension QualitySelectorView {
)
}
if availableTabs.contains(.subtitles) {
Divider()
if availableTabs.contains(.video) || availableTabs.contains(.audio) {
Divider()
}
mediaSelectionRow(
destination: .subtitles,
label: String(localized: "stream.subtitles"),
@@ -334,15 +346,19 @@ extension QualitySelectorView {
#if os(tvOS)
// tvOS rows are styled individually to match the Settings rows.
VStack(spacing: 8) {
playbackSpeedRow
if visibleSections.effectiveShowRate {
playbackSpeedRow
}
audioModeRow
}
#else
VStack(spacing: 0) {
playbackSpeedRow
if visibleSections.effectiveShowRate {
playbackSpeedRow
Divider()
Divider()
}
audioModeRow