Add corner player layout on iPad and macOS

Preset-selected two-region layout: video in the top-left, a resizable
side column, and an optional region below the video. Pages are assigned
to regions per preset (Location picker in the page editor); the layout
falls back to the stacked widescreen layout below 900pt width, so Split
View and narrow windows degrade automatically.

- CornerPlayerLayout hosts the shared playerAreaForWidescreen builder,
  so the MPV hosting path is identical to the stacked layout
- Layout resolver in ExpandedPlayerSheet picks corner per layout pass;
  NavigationCoordinator.isCornerLayoutActive feeds the macOS window
  manager, which drops the aspect-ratio lock and auto-fit while corner
  is active (and restores them on exit, incl. fullscreen replay and
  window re-mounts)
- Side column width persisted via new cornerPanelColumnWidth setting
  (280-560pt, video column kept >= 480pt)
- Settings: layout mode picker (iPad/macOS) with automatic-fallback
  footer, per-page region picker when corner is on, region shown in
  the pages list
This commit is contained in:
Arkadiusz Fal
2026-09-21 23:52:09 +02:00
parent c33d125553
commit a6d989c7af
14 changed files with 582 additions and 12 deletions

View File

@@ -15,6 +15,7 @@ struct PanelPageEditorView: View {
// Local state for immediate UI updates
@State private var isEnabled: Bool = true
@State private var region: PanelRegion = .primary
@State private var showDescription: Bool = true
@State private var showStatsRow: Bool = true
@State private var showChannelRow: Bool = true
@@ -32,6 +33,10 @@ struct PanelPageEditorView: View {
Form {
enabledSection
if PanelSettingsView.supportsCornerLayout, viewModel.panelLayoutMode == .corner {
regionSection
}
if pageType == .info {
infoElementsSection
}
@@ -73,6 +78,28 @@ struct PanelPageEditorView: View {
}
}
// MARK: - Region Section (corner layout)
private var regionSection: some View {
Section {
Picker(
String(localized: "panel.settings.page.region"),
selection: $region
) {
ForEach(PanelRegion.allCases, id: \.self) { region in
Text(region.displayName).tag(region)
}
}
.disabled(!viewModel.canEditActivePreset)
.onChange(of: region) { _, newValue in
guard newValue != configuration?.region else { return }
viewModel.setPanelPageRegion(newValue, for: pageType)
}
} footer: {
Text(String(localized: "panel.settings.page.region.footer"))
}
}
// MARK: - Info Elements Section
private var infoElementsSection: some View {
@@ -117,6 +144,7 @@ struct PanelPageEditorView: View {
private func syncLocalState() {
isEnabled = configuration?.isEnabled ?? true
region = configuration?.region ?? .primary
let info = infoSettings
showDescription = info.effectiveShowDescription
showStatsRow = info.effectiveShowStatsRow

View File

@@ -14,9 +14,22 @@ struct PanelSettingsView: View {
// Local state for immediate UI updates
@State private var defaultVisibility: PanelDefaultVisibility = .hidden
@State private var layoutMode: PanelLayoutMode = .stacked
/// Corner layout exists on iPad and macOS only.
static var supportsCornerLayout: Bool {
#if os(macOS)
return true
#else
return UIDevice.current.userInterfaceIdiom == .pad
#endif
}
var body: some View {
List {
if Self.supportsCornerLayout {
layoutSection
}
visibilitySection
pagesSection
}
@@ -40,6 +53,28 @@ struct PanelSettingsView: View {
}
}
// MARK: - Layout Section
private var layoutSection: some View {
Section {
Picker(
String(localized: "panel.settings.layoutMode"),
selection: $layoutMode
) {
ForEach(PanelLayoutMode.allCases, id: \.self) { mode in
Text(mode.displayName).tag(mode)
}
}
.disabled(!viewModel.canEditActivePreset)
.onChange(of: layoutMode) { _, newValue in
guard newValue != viewModel.panelLayoutMode else { return }
viewModel.syncPanelLayoutMode(newValue)
}
} footer: {
Text(String(localized: "panel.settings.layoutMode.footer"))
}
}
// MARK: - Visibility Section
private var visibilitySection: some View {
@@ -92,10 +127,15 @@ struct PanelSettingsView: View {
HStack {
Label(page.pageType.displayName, systemImage: page.pageType.systemImage)
Spacer()
Text(page.isEnabled
? String(localized: "common.on")
: String(localized: "common.off"))
.foregroundStyle(.secondary)
if page.isEnabled, layoutMode == .corner, Self.supportsCornerLayout {
Text(page.region.displayName)
.foregroundStyle(.secondary)
} else {
Text(page.isEnabled
? String(localized: "common.on")
: String(localized: "common.off"))
.foregroundStyle(.secondary)
}
}
}
}
@@ -104,6 +144,7 @@ struct PanelSettingsView: View {
private func syncLocalState() {
defaultVisibility = viewModel.panelDefaultVisibility
layoutMode = viewModel.panelLayoutMode
}
}