Add liquid glass to osd

This commit is contained in:
Hariharan Mudaliar 2026-02-21 09:49:33 +05:30
commit e553ccf667
No known key found for this signature in database
GPG key ID: 025583EB0BE5640E
5 changed files with 208 additions and 13 deletions

View file

@ -7617,6 +7617,9 @@
}
}
}
},
"Custom Liquid is available on macOS 26 or later." : {
},
"Custom Liquid settings require the Liquid Glass material." : {
"localizations" : {
@ -7627,6 +7630,9 @@
}
}
}
},
"Custom liquid variant" : {
},
"Custom music live activity animation" : {
"localizations" : {

View file

@ -28,6 +28,8 @@ struct CustomOSDView: View {
@Binding var icon: String
@Default(.osdMaterial) var osdMaterial
@Default(.osdLiquidGlassCustomizationMode) var osdLiquidGlassCustomizationMode
@Default(.osdLiquidGlassVariant) var osdLiquidGlassVariant
@Default(.osdIconColorStyle) var osdIconColorStyle
@Environment(\.colorScheme) var colorScheme
@ -160,11 +162,23 @@ struct CustomOSDView: View {
.fill(.ultraThinMaterial)
case .liquid:
if #available(macOS 26.0, *) {
RoundedRectangle(cornerRadius: 18, style: .continuous)
.glassEffect(
.clear.interactive(),
in: .rect(cornerRadius: 18)
)
if osdLiquidGlassCustomizationMode == .customLiquid {
TimelineView(.periodic(from: .now, by: 1.0 / 30.0)) { context in
LiquidGlassBackground(
variant: osdLiquidGlassVariant,
cornerRadius: 18,
trigger: context.date.timeIntervalSinceReferenceDate
) {
Color.white.opacity(0.04)
}
}
} else {
RoundedRectangle(cornerRadius: 18, style: .continuous)
.glassEffect(
.clear.interactive(),
in: .rect(cornerRadius: 18)
)
}
} else {
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill(.ultraThinMaterial)

View file

@ -46,6 +46,9 @@ struct VerticalHUDView: View {
@Default(.verticalHUDHeight) var hudHeight
@Default(.verticalHUDWidth) var hudWidth
@Default(.verticalHUDUseAccentColor) var useAccentColor
@Default(.verticalHUDMaterial) var verticalHUDMaterial
@Default(.verticalHUDLiquidGlassCustomizationMode) var verticalHUDLiquidGlassCustomizationMode
@Default(.verticalHUDLiquidGlassVariant) var verticalHUDLiquidGlassVariant
@Default(.verticalHUDInteractive) var isInteractive
@Environment(\.colorScheme) var colorScheme
@ -68,8 +71,8 @@ struct VerticalHUDView: View {
// The Actual HUD Pill
ZStack {
// Background
Capsule()
.fill(.ultraThinMaterial)
verticalBackground
.clipShape(Capsule())
.overlay {
Capsule()
.stroke(.white.opacity(0.1), lineWidth: 1)
@ -253,5 +256,42 @@ struct VerticalHUDView: View {
}
return .secondary
}
@ViewBuilder
private var verticalBackground: some View {
switch verticalHUDMaterial {
case .frosted:
Capsule().fill(.ultraThinMaterial)
case .liquid:
if #available(macOS 26.0, *) {
if verticalHUDLiquidGlassCustomizationMode == .customLiquid {
TimelineView(.periodic(from: .now, by: 1.0 / 30.0)) { context in
LiquidGlassBackground(
variant: verticalHUDLiquidGlassVariant,
cornerRadius: max(currentWidth, hudWidth),
trigger: context.date.timeIntervalSinceReferenceDate
) {
Color.white.opacity(0.04)
}
}
} else {
Capsule()
.fill(.clear)
.glassEffect(
.clear.interactive(),
in: .capsule
)
}
} else {
Capsule().fill(.ultraThinMaterial)
}
case .solidDark:
Capsule().fill(Color.black.opacity(0.85))
case .solidLight:
Capsule().fill(Color.white.opacity(0.85))
case .solidAuto:
Capsule().fill((colorScheme == .dark ? Color.black : Color.white).opacity(0.85))
}
}
}
#endif

View file

@ -1369,6 +1369,9 @@ private struct HUDAndOSDSettingsView: View {
@Default(.verticalHUDWidth) var verticalHUDWidth
@Default(.verticalHUDPadding) var verticalHUDPadding
@Default(.verticalHUDUseAccentColor) var verticalHUDUseAccentColor
@Default(.verticalHUDMaterial) var verticalHUDMaterial
@Default(.verticalHUDLiquidGlassCustomizationMode) var verticalHUDLiquidGlassCustomizationMode
@Default(.verticalHUDLiquidGlassVariant) var verticalHUDLiquidGlassVariant
// Circular HUD Props
@Default(.circularHUDShowValue) var circularHUDShowValue
@ -1391,6 +1394,27 @@ private struct HUDAndOSDSettingsView: View {
Color(nsColor: .controlBackgroundColor)
}
private var liquidVariantRange: ClosedRange<Double> {
Double(LiquidGlassVariant.supportedRange.lowerBound)...Double(LiquidGlassVariant.supportedRange.upperBound)
}
private var availableVerticalMaterials: [OSDMaterial] {
if #available(macOS 26.0, *) {
return OSDMaterial.allCases
}
return OSDMaterial.allCases.filter { $0 != .liquid }
}
private var verticalLiquidVariantBinding: Binding<Double> {
Binding(
get: { Double(verticalHUDLiquidGlassVariant.rawValue) },
set: { newValue in
let raw = Int(newValue.rounded())
verticalHUDLiquidGlassVariant = LiquidGlassVariant.clamped(raw)
}
)
}
var body: some View {
VStack(spacing: 20) {
HStack(spacing: 16) {
@ -1608,6 +1632,39 @@ private struct HUDAndOSDSettingsView: View {
Toggle("Show Percentage", isOn: $verticalHUDShowValue)
Toggle("Use Accent Color", isOn: $verticalHUDUseAccentColor)
Toggle("Interactive (Drag to Change)", isOn: $verticalHUDInteractive)
Picker("Material", selection: $verticalHUDMaterial) {
ForEach(availableVerticalMaterials, id: \.self) { material in
Text(material.rawValue).tag(material)
}
}
if verticalHUDMaterial == .liquid {
if #available(macOS 26.0, *) {
Picker("Glass mode", selection: $verticalHUDLiquidGlassCustomizationMode) {
ForEach(LockScreenGlassCustomizationMode.allCases) { mode in
Text(mode.rawValue).tag(mode)
}
}
.pickerStyle(.segmented)
if verticalHUDLiquidGlassCustomizationMode == .customLiquid {
VStack(alignment: .leading, spacing: 6) {
HStack {
Text("Custom liquid variant")
Spacer()
Text("v\(verticalHUDLiquidGlassVariant.rawValue)")
.font(.caption)
.foregroundStyle(.secondary)
}
Slider(value: verticalLiquidVariantBinding, in: liquidVariantRange, step: 1)
}
}
} else {
Text("Custom Liquid is available on macOS 26 or later.")
.font(.caption)
.foregroundStyle(.secondary)
}
}
Defaults.Toggle("Color-coded Volume", key: .useColorCodedVolumeDisplay)
if Defaults[.useColorCodedVolumeDisplay] {
Defaults.Toggle("Smooth color transitions", key: .useSmoothColorGradient)
@ -1719,6 +1776,12 @@ private struct HUDAndOSDSettingsView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.background(paneBackgroundColor)
.navigationTitle("Controls")
.onAppear {
if #unavailable(macOS 26.0), verticalHUDMaterial == .liquid {
verticalHUDMaterial = .frosted
verticalHUDLiquidGlassCustomizationMode = .standard
}
}
}
}
@ -5608,34 +5671,34 @@ struct TimerSettings: View {
let nextIndex = timerPresets.count + 1
let defaultColor = Defaults[.accentColor]
let newPreset = TimerPreset(name: "Preset \(nextIndex)", duration: 5 * 60, color: defaultColor)
withAnimation(.smooth) {
_ = withAnimation(.smooth) {
timerPresets.append(newPreset)
}
}
private func movePresetUp(_ index: Int) {
guard index > timerPresets.startIndex else { return }
withAnimation(.smooth) {
_ = withAnimation(.smooth) {
timerPresets.swapAt(index, index - 1)
}
}
private func movePresetDown(_ index: Int) {
guard index < timerPresets.index(before: timerPresets.endIndex) else { return }
withAnimation(.smooth) {
_ = withAnimation(.smooth) {
timerPresets.swapAt(index, index + 1)
}
}
private func removePreset(_ index: Int) {
guard timerPresets.indices.contains(index) else { return }
withAnimation(.smooth) {
_ = withAnimation(.smooth) {
timerPresets.remove(at: index)
}
}
private func resetPresets() {
withAnimation(.smooth) {
_ = withAnimation(.smooth) {
timerPresets = TimerPreset.defaultPresets
}
}
@ -6550,6 +6613,8 @@ struct CustomOSDSettings: View {
@Default(.enableOSDBrightness) var enableOSDBrightness
@Default(.enableOSDKeyboardBacklight) var enableOSDKeyboardBacklight
@Default(.osdMaterial) var osdMaterial
@Default(.osdLiquidGlassCustomizationMode) var osdLiquidGlassCustomizationMode
@Default(.osdLiquidGlassVariant) var osdLiquidGlassVariant
@Default(.osdIconColorStyle) var osdIconColorStyle
@Default(.enableSystemHUD) var enableSystemHUD
@ObservedObject private var accessibilityPermission = AccessibilityPermissionStore.shared
@ -6565,6 +6630,27 @@ struct CustomOSDSettings: View {
private var hasAccessibilityPermission: Bool {
accessibilityPermission.isAuthorized
}
private var availableOSDMaterials: [OSDMaterial] {
if #available(macOS 26.0, *) {
return OSDMaterial.allCases
}
return OSDMaterial.allCases.filter { $0 != .liquid }
}
private var liquidVariantRange: ClosedRange<Double> {
Double(LiquidGlassVariant.supportedRange.lowerBound)...Double(LiquidGlassVariant.supportedRange.upperBound)
}
private var osdLiquidVariantBinding: Binding<Double> {
Binding(
get: { Double(osdLiquidGlassVariant.rawValue) },
set: { newValue in
let raw = Int(newValue.rounded())
osdLiquidGlassVariant = LiquidGlassVariant.clamped(raw)
}
)
}
var body: some View {
Form {
@ -6598,7 +6684,7 @@ struct CustomOSDSettings: View {
Section {
Picker("Material", selection: $osdMaterial) {
ForEach(OSDMaterial.allCases, id: \.self) { material in
ForEach(availableOSDMaterials, id: \.self) { material in
Text(material.rawValue).tag(material)
}
}
@ -6606,6 +6692,34 @@ struct CustomOSDSettings: View {
.onChange(of: osdMaterial) { _, _ in
previewValue = previewValue == 0.65 ? 0.651 : 0.65
}
if osdMaterial == .liquid {
if #available(macOS 26.0, *) {
Picker("Glass mode", selection: $osdLiquidGlassCustomizationMode) {
ForEach(LockScreenGlassCustomizationMode.allCases) { mode in
Text(mode.rawValue).tag(mode)
}
}
.pickerStyle(.segmented)
if osdLiquidGlassCustomizationMode == .customLiquid {
VStack(alignment: .leading, spacing: 6) {
HStack {
Text("Custom liquid variant")
Spacer()
Text("v\(osdLiquidGlassVariant.rawValue)")
.font(.caption)
.foregroundStyle(.secondary)
}
Slider(value: osdLiquidVariantBinding, in: liquidVariantRange, step: 1)
}
}
} else {
Text("Custom Liquid is available on macOS 26 or later.")
.font(.caption)
.foregroundStyle(.secondary)
}
}
Picker("Icon & Progress Color", selection: $osdIconColorStyle) {
ForEach(OSDIconColorStyle.allCases, id: \.self) { style in
@ -6682,6 +6796,10 @@ struct CustomOSDSettings: View {
.navigationTitle("Custom OSD")
.onAppear {
accessibilityPermission.refreshStatus()
if #unavailable(macOS 26.0), osdMaterial == .liquid {
osdMaterial = .frosted
osdLiquidGlassCustomizationMode = .standard
}
}
.onChange(of: accessibilityPermission.isAuthorized) { _, granted in
if !granted {

View file

@ -973,6 +973,15 @@ extension Defaults.Keys {
static let verticalHUDWidth = Key<CGFloat>("verticalHUDWidth", default: 36)
static let verticalHUDPadding = Key<CGFloat>("verticalHUDPadding", default: 24)
static let verticalHUDUseAccentColor = Key<Bool>("verticalHUDUseAccentColor", default: false)
static let verticalHUDMaterial = Key<OSDMaterial>("verticalHUDMaterial", default: .frosted)
static let verticalHUDLiquidGlassCustomizationMode = Key<LockScreenGlassCustomizationMode>(
"verticalHUDLiquidGlassCustomizationMode",
default: .standard
)
static let verticalHUDLiquidGlassVariant = Key<LiquidGlassVariant>(
"verticalHUDLiquidGlassVariant",
default: .defaultVariant
)
// Circular HUD Customization
static let circularHUDShowValue = Key<Bool>("circularHUDShowValue", default: true)
@ -985,6 +994,14 @@ extension Defaults.Keys {
static let enableOSDBrightness = Key<Bool>("enableOSDBrightness", default: true)
static let enableOSDKeyboardBacklight = Key<Bool>("enableOSDKeyboardBacklight", default: true)
static let osdMaterial = Key<OSDMaterial>("osdMaterial", default: .frosted)
static let osdLiquidGlassCustomizationMode = Key<LockScreenGlassCustomizationMode>(
"osdLiquidGlassCustomizationMode",
default: .standard
)
static let osdLiquidGlassVariant = Key<LiquidGlassVariant>(
"osdLiquidGlassVariant",
default: .defaultVariant
)
static let osdIconColorStyle = Key<OSDIconColorStyle>("osdIconColorStyle", default: .white)
// MARK: Screen Recording Detection Feature