Default the built-in presets to the corner layout

The built-in presets now use the corner layout, which still falls back
to stacked where it doesn't fit (iPhone, iPad portrait, narrow windows).
In the corner layout the landscape panels open visible unless the preset
says otherwise, on devices that can show it: iPad, macOS, and foldable
iPhones, whose hinge is now remembered once reported. Custom presets and
presets saved without panel settings keep the stacked layout.
This commit is contained in:
Arkadiusz Fal
2026-10-12 18:24:46 +02:00
parent 5026c094be
commit e71bb76f13
4 changed files with 72 additions and 7 deletions

View File

@@ -11,7 +11,7 @@ extension LayoutPreset {
/// Bump this version whenever any built-in preset definition changes.
/// On launch, the app compares this against the last-applied version
/// and replaces stale built-in presets with fresh copies from code.
static let builtInPresetsVersion = 9
static let builtInPresetsVersion = 10
// MARK: - Built-in Preset IDs
@@ -132,7 +132,7 @@ extension LayoutPreset {
gesturesSettings: gesturesSettings,
playerPillSettings: playerPillSettings,
miniPlayerSettings: miniPlayerSettings,
panelSettings: .default
panelSettings: .builtInDefault
)
}
@@ -194,7 +194,7 @@ extension LayoutPreset {
gesturesSettings: nil,
playerPillSettings: nil,
miniPlayerSettings: MiniPlayerSettings(),
panelSettings: .default
panelSettings: .builtInDefault
)
}
@@ -288,7 +288,7 @@ extension LayoutPreset {
gesturesSettings: gesturesSettings,
playerPillSettings: playerPillSettings,
miniPlayerSettings: miniPlayerSettings,
panelSettings: .default
panelSettings: .builtInDefault
)
}
@@ -340,7 +340,7 @@ extension LayoutPreset {
gesturesSettings: nil,
playerPillSettings: nil,
miniPlayerSettings: MiniPlayerSettings(),
panelSettings: .default
panelSettings: .builtInDefault
)
}

View File

@@ -478,16 +478,56 @@ struct PanelSettings: Codable, Hashable, Sendable {
PanelPageConfiguration(pageType: .queue, region: .primary),
])
/// Panel settings of the built-in presets: the default pages in the
/// corner layout. The player still falls back to the stacked layout where
/// the corner layout doesn't fit (iPhone, iPad portrait, narrow windows).
/// `default` stays stacked so presets saved without panel settings keep
/// the layout they had.
static let builtInDefault: PanelSettings = {
var settings = PanelSettings.default
settings.layoutMode = .corner
return settings
}()
var effectiveLayoutMode: PanelLayoutMode { layoutMode ?? .stacked }
/// The default visibility for an orientation: its own value, else the
/// legacy single value, else portrait visible / landscape hidden.
/// legacy single value, else portrait visible / landscape hidden — except
/// landscape in the corner layout on a device that can show it, where the
/// panels are the point of the layout and open visible.
func effectiveDefaultVisibility(for orientation: PanelOrientation) -> PanelDefaultVisibility {
switch orientation {
case .portrait:
return portraitDefaultVisibility ?? defaultVisibility ?? .visible
case .landscape:
return landscapeDefaultVisibility ?? defaultVisibility ?? .hidden
let fallback: PanelDefaultVisibility =
effectiveLayoutMode == .corner && Self.canHostCornerLayout ? .visible : .hidden
return landscapeDefaultVisibility ?? defaultVisibility ?? fallback
}
}
/// Whether this device has a screen the corner layout fits on: iPad,
/// macOS, and foldable iPhones (interior display).
static var canHostCornerLayout: Bool {
#if os(macOS)
return true
#elseif os(iOS)
return UIDevice.current.userInterfaceIdiom == .pad || deviceHasHinge
#else
return false
#endif
}
private static let deviceHasHingeKey = "deviceHasHinge"
/// Whether a hinge has ever been reported on this device (foldable
/// iPhone). Hinge state only arrives through views, so the hinge
/// observers record it here for code that runs without one. Local only.
static var deviceHasHinge: Bool {
get { UserDefaults.standard.bool(forKey: deviceHasHingeKey) }
set {
guard newValue != deviceHasHinge else { return }
UserDefaults.standard.set(newValue, forKey: deviceHasHingeKey)
}
}