mirror of
https://github.com/yattee/yattee.git
synced 2026-07-20 14:22:02 +00:00
- Replace the full-width material overlay bar with a compact, centered capsule (Apple Music style) via a macOS-specific macOSCapsuleLayout - Extend glassBackground() to use real Liquid Glass on macOS 26 with a material fallback on macOS 15-25 (mirrors the PlayerOverlayButton pattern) - Remove the duplicate close button (close now comes only from the configurable button set) - Keep the capsule visible in the main window while the separate player window is open (window mode), still hidden in sheet mode
194 lines
5.9 KiB
Swift
194 lines
5.9 KiB
Swift
//
|
|
// GlassBackground.swift
|
|
// Yattee
|
|
//
|
|
// Adaptive glass background that uses liquid glass on iOS 26+ and falls back to materials on older versions.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
// MARK: - Glass Style
|
|
|
|
/// Style options for the glass background effect
|
|
enum GlassStyle {
|
|
/// Clear glass with minimal blur - subtle transparency
|
|
case clear
|
|
/// Regular glass with standard blur and reflection
|
|
case regular
|
|
/// Tinted glass with a color overlay
|
|
case tinted(Color)
|
|
}
|
|
|
|
// MARK: - Glass Shape
|
|
|
|
/// Shape options for the glass background
|
|
enum GlassShape {
|
|
/// Rectangle with corner radius
|
|
case rect(cornerRadius: CGFloat)
|
|
/// Capsule shape
|
|
case capsule
|
|
/// Circle shape
|
|
case circle
|
|
}
|
|
|
|
// MARK: - Fallback Material
|
|
|
|
/// Material to use on iOS versions before 26
|
|
enum GlassFallbackMaterial {
|
|
case ultraThinMaterial
|
|
case thinMaterial
|
|
case regularMaterial
|
|
case thickMaterial
|
|
case ultraThickMaterial
|
|
}
|
|
|
|
// MARK: - Glass Background Modifier
|
|
|
|
/// A view modifier that applies liquid glass effect on iOS 26+ and falls back to materials on older versions
|
|
struct GlassBackgroundModifier: ViewModifier {
|
|
let style: GlassStyle
|
|
let shape: GlassShape
|
|
let fallbackMaterial: GlassFallbackMaterial
|
|
let colorScheme: ColorScheme?
|
|
|
|
init(
|
|
style: GlassStyle = .clear,
|
|
shape: GlassShape = .rect(cornerRadius: 12),
|
|
fallbackMaterial: GlassFallbackMaterial = .ultraThinMaterial,
|
|
colorScheme: ColorScheme? = nil
|
|
) {
|
|
self.style = style
|
|
self.shape = shape
|
|
self.fallbackMaterial = fallbackMaterial
|
|
self.colorScheme = colorScheme
|
|
}
|
|
|
|
func body(content: Content) -> some View {
|
|
#if os(iOS) || os(macOS)
|
|
if #available(iOS 26.0, macOS 26.0, *) {
|
|
content.modifier(LiquidGlassModifier(style: style, shape: shape, colorScheme: colorScheme))
|
|
} else {
|
|
content.modifier(FallbackGlassModifier(shape: shape, material: fallbackMaterial, colorScheme: colorScheme))
|
|
}
|
|
#else
|
|
// tvOS doesn't have glassEffect, always use fallback material
|
|
content.modifier(FallbackGlassModifier(shape: shape, material: fallbackMaterial, colorScheme: colorScheme))
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// MARK: - iOS 26+ / macOS 26+ Liquid Glass
|
|
|
|
#if os(iOS) || os(macOS)
|
|
@available(iOS 26.0, macOS 26.0, *)
|
|
private struct LiquidGlassModifier: ViewModifier {
|
|
let style: GlassStyle
|
|
let shape: GlassShape
|
|
let colorScheme: ColorScheme?
|
|
|
|
func body(content: Content) -> some View {
|
|
switch shape {
|
|
case .rect(let cornerRadius):
|
|
applyGlassEffect(to: content, shape: .rect(cornerRadius: cornerRadius))
|
|
case .capsule:
|
|
applyGlassEffect(to: content, shape: .capsule)
|
|
case .circle:
|
|
applyGlassEffect(to: content, shape: .circle)
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func applyGlassEffect<S: Shape>(to content: Content, shape: S) -> some View {
|
|
if let scheme = colorScheme {
|
|
switch style {
|
|
case .clear:
|
|
content.glassEffect(.clear, in: shape).environment(\.colorScheme, scheme)
|
|
case .regular:
|
|
content.glassEffect(.regular, in: shape).environment(\.colorScheme, scheme)
|
|
case .tinted(let color):
|
|
content.glassEffect(.regular.tint(color), in: shape).environment(\.colorScheme, scheme)
|
|
}
|
|
} else {
|
|
switch style {
|
|
case .clear:
|
|
content.glassEffect(.clear, in: shape)
|
|
case .regular:
|
|
content.glassEffect(.regular, in: shape)
|
|
case .tinted(let color):
|
|
content.glassEffect(.regular.tint(color), in: shape)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// MARK: - Fallback for older iOS / macOS
|
|
|
|
private struct FallbackGlassModifier: ViewModifier {
|
|
let shape: GlassShape
|
|
let material: GlassFallbackMaterial
|
|
let colorScheme: ColorScheme?
|
|
|
|
func body(content: Content) -> some View {
|
|
switch shape {
|
|
case .rect(let cornerRadius):
|
|
content
|
|
.background(materialBackground)
|
|
.clipShape(RoundedRectangle(cornerRadius: cornerRadius))
|
|
case .capsule:
|
|
content
|
|
.background(materialBackground)
|
|
.clipShape(Capsule())
|
|
case .circle:
|
|
content
|
|
.background(materialBackground)
|
|
.clipShape(Circle())
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var materialBackground: some View {
|
|
if let colorScheme {
|
|
rawMaterialBackground.environment(\.colorScheme, colorScheme)
|
|
} else {
|
|
rawMaterialBackground
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var rawMaterialBackground: some View {
|
|
switch material {
|
|
case .ultraThinMaterial:
|
|
Rectangle().fill(.ultraThinMaterial)
|
|
case .thinMaterial:
|
|
Rectangle().fill(.thinMaterial)
|
|
case .regularMaterial:
|
|
Rectangle().fill(.regularMaterial)
|
|
case .thickMaterial:
|
|
Rectangle().fill(.thickMaterial)
|
|
case .ultraThickMaterial:
|
|
Rectangle().fill(.ultraThickMaterial)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - View Extension
|
|
|
|
extension View {
|
|
/// Applies a glass background effect that adapts to the platform
|
|
/// - Parameters:
|
|
/// - style: The glass style (clear, regular, or tinted)
|
|
/// - shape: The shape of the glass background
|
|
/// - fallbackMaterial: The material to use on iOS < 26
|
|
/// - colorScheme: Optional color scheme to force (affects glass appearance on iOS 26+)
|
|
/// - Returns: A view with the glass background applied
|
|
func glassBackground(
|
|
_ style: GlassStyle = .clear,
|
|
in shape: GlassShape = .rect(cornerRadius: 12),
|
|
fallback: GlassFallbackMaterial = .ultraThinMaterial,
|
|
colorScheme: ColorScheme? = nil
|
|
) -> some View {
|
|
modifier(GlassBackgroundModifier(style: style, shape: shape, fallbackMaterial: fallback, colorScheme: colorScheme))
|
|
}
|
|
}
|