From 4be4c36bb476fc4d9c02691d7054a4dd1ca06677 Mon Sep 17 00:00:00 2001 From: delli Date: Sat, 20 Dec 2025 12:43:21 +0100 Subject: [PATCH 1/3] fixed bugs and made a better version of the circular hud --- .../SystemEventIndicatorModifier.swift | 9 ++++ .../components/OSD/CircularHUDView.swift | 53 ++++++++++--------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/DynamicIsland/components/Live activities/SystemEventIndicatorModifier.swift b/DynamicIsland/components/Live activities/SystemEventIndicatorModifier.swift index a7b93a5..bdda58e 100644 --- a/DynamicIsland/components/Live activities/SystemEventIndicatorModifier.swift +++ b/DynamicIsland/components/Live activities/SystemEventIndicatorModifier.swift @@ -56,6 +56,13 @@ struct SystemEventIndicatorModifier: View { .contentTransition(.interpolate) .frame(width: 20, height: 15) .foregroundStyle(.white) + case .bluetoothAudio: + if !icon.isEmpty { + Image(systemName: icon) + .contentTransition(.interpolate) + .frame(width: 20, height: 15) + .foregroundStyle(.white) + } default: EmptyView() } @@ -72,6 +79,8 @@ struct SystemEventIndicatorModifier: View { ProgressSection(value: $value, showPercentages: showProgressPercentages) case .backlight: ProgressSection(value: $value, showPercentages: showProgressPercentages) + case .bluetoothAudio: + ProgressSection(value: $value, showPercentages: showProgressPercentages, colorMode: .battery) default: ProgressSection(value: $value, showPercentages: showProgressPercentages) } diff --git a/DynamicIsland/components/OSD/CircularHUDView.swift b/DynamicIsland/components/OSD/CircularHUDView.swift index 098a8b6..978abe6 100644 --- a/DynamicIsland/components/OSD/CircularHUDView.swift +++ b/DynamicIsland/components/OSD/CircularHUDView.swift @@ -28,36 +28,39 @@ struct CircularHUDView: View { .stroke(.white.opacity(0.1), lineWidth: 1) } - // Progress ring background - Circle() - .stroke(Color.white.opacity(0.2), lineWidth: strokeWidth) - .padding(strokeWidth / 2) + // Native Gauge Ring + Gauge(value: value) { + Text("") + } + .gaugeStyle(.accessoryCircular) + .labelsHidden() + .tint(strokeStyle) + .scaleEffect(size / 60) + .animation(.spring(response: 0.4, dampingFraction: 0.7, blendDuration: 0), value: value) - // Progress ring - Circle() - .trim(from: 0, to: value) - .stroke( - strokeStyle, - style: StrokeStyle(lineWidth: strokeWidth, lineCap: .round) - ) - .rotationEffect(.degrees(-90)) - .padding(strokeWidth / 2) - .animation(.interactiveSpring(response: 0.35, dampingFraction: 0.8, blendDuration: 0), value: value) + // Central Icon + Image(systemName: symbolName) + .font(.system(size: size * 0.32, weight: .bold)) + .symbolRenderingMode(.hierarchical) + .foregroundStyle(.white) + .contentTransition(.symbolEffect(.replace)) // Smooth icon switching + .animation(.spring(response: 0.3, dampingFraction: 0.6), value: symbolName) + - // Content - VStack(spacing: 2) { - Image(systemName: symbolName) - .font(.system(size: size * 0.35, weight: .semibold)) - .foregroundStyle(useAccentColor ? (value > 0.9 ? .white : .primary) : .white) - .symbolRenderingMode(.hierarchical) - - if showValue { + // Bottom Value Label + if showValue { + VStack { + Spacer() Text("\(Int(value * 100))") - .font(.system(size: size * 0.18, weight: .semibold, design: .rounded)) - .foregroundStyle(.white.opacity(0.8)) + .font(.system(size: size * 0.15, weight: .bold, design: .rounded)) + .foregroundStyle(.white.opacity(0.9)) .contentTransition(.numericText()) + .padding(.bottom, size * 0.03) // Adjusted to align perfectly with the bottom gap } } + + + } .frame(width: size, height: size) .shadow(color: .black.opacity(0.2), radius: 15, x: 0, y: 5) @@ -82,7 +85,7 @@ struct CircularHUDView: View { if deviceInfo.isAirPods { // Use AirPods icon when AirPods are connected - if value < 0.01 { return "airpods" } + if value < 0.01 { return "headphones.slash" } else { return "airpods" } } else if deviceInfo.isHeadphones { // Use headphone icons when other headphones are connected From ada9c10f9f69289d9084ac3049d41493e8d1cc5d Mon Sep 17 00:00:00 2001 From: delli Date: Sun, 21 Dec 2025 13:52:17 +0100 Subject: [PATCH 2/3] new circular hud --- .../components/OSD/CircularHUDView.swift | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/DynamicIsland/components/OSD/CircularHUDView.swift b/DynamicIsland/components/OSD/CircularHUDView.swift index 978abe6..26e02df 100644 --- a/DynamicIsland/components/OSD/CircularHUDView.swift +++ b/DynamicIsland/components/OSD/CircularHUDView.swift @@ -20,32 +20,55 @@ struct CircularHUDView: View { var body: some View { ZStack { - // Background circle + // Background circle (SOLO OPAQUE - No transparency) Circle() - .fill(.ultraThinMaterial) + .fill(Color(white: 0.1)) .overlay { Circle() - .stroke(.white.opacity(0.1), lineWidth: 1) + .stroke(Color(white: 0.2), lineWidth: 1) } - // Native Gauge Ring - Gauge(value: value) { - Text("") + // 1. Static Background Track (Empty binario) + Circle() + .trim(from: 0.15, to: 0.85) + .stroke(Color(white: 0.15), style: StrokeStyle(lineWidth: strokeWidth * 1.5, lineCap: .round)) + .rotationEffect(.degrees(90)) + .frame(width: size, height: size) + + // 2. Animated Progress Arc (Filled part) + Circle() + .trim(from: 0.15, to: 0.15 + (0.7 * value)) + .stroke(strokeStyle, style: StrokeStyle(lineWidth: strokeWidth * 1.5, lineCap: .round)) + .rotationEffect(.degrees(90)) + .frame(width: size, height: size) + .animation(.interactiveSpring(response: 0.35, dampingFraction: 0.85, blendDuration: 0), value: value) + + // 3. The "Ball" Indicator (Oversized White Pallino) + GeometryReader { geometry in + let radius = size / 2 + let trackWidth = strokeWidth * 1.5 + let startAngle = 144.0 + let currentAngle = startAngle + (252.0 * value) + + let x = radius + radius * cos(currentAngle * .pi / 180) + let y = radius + radius * sin(currentAngle * .pi / 180) + + Circle() + .fill(.white) + .frame(width: trackWidth * 1.45, height: trackWidth * 1.45) + .shadow(color: .black.opacity(0.45), radius: 2, x: 0, y: 1.2) + .position(x: x, y: y) + .animation(.interactiveSpring(response: 0.35, dampingFraction: 0.85, blendDuration: 0), value: value) } - .gaugeStyle(.accessoryCircular) - .labelsHidden() - .tint(strokeStyle) - .scaleEffect(size / 60) - .animation(.spring(response: 0.4, dampingFraction: 0.7, blendDuration: 0), value: value) + .frame(width: size, height: size) // Central Icon Image(systemName: symbolName) .font(.system(size: size * 0.32, weight: .bold)) .symbolRenderingMode(.hierarchical) .foregroundStyle(.white) - .contentTransition(.symbolEffect(.replace)) // Smooth icon switching + .contentTransition(.symbolEffect(.replace)) .animation(.spring(response: 0.3, dampingFraction: 0.6), value: symbolName) - // Bottom Value Label if showValue { @@ -53,9 +76,9 @@ struct CircularHUDView: View { Spacer() Text("\(Int(value * 100))") .font(.system(size: size * 0.15, weight: .bold, design: .rounded)) - .foregroundStyle(.white.opacity(0.9)) + .foregroundStyle(.white.opacity(0.65)) .contentTransition(.numericText()) - .padding(.bottom, size * 0.03) // Adjusted to align perfectly with the bottom gap + .padding(.bottom, size * 0.03) } } @@ -72,7 +95,12 @@ struct CircularHUDView: View { return ColorCodedProgressBar.shapeStyle(for: value, mode: .volume, smoothGradient: useSmoothGradient) } - return AnyShapeStyle(useAccentColor ? Color.accentColor : Color.white) + if useAccentColor { + return AnyShapeStyle(Color.accentColor) + } + + // Default to the approved solid dark gray to match reference when not colored + return AnyShapeStyle(Color(white: 0.25)) } private var symbolName: String { From 8942e1f8a5e905291a5fb77196231d8098786bf3 Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Tue, 23 Dec 2025 01:52:05 +0530 Subject: [PATCH 3/3] Update Numeric Text transition --- .../components/OSD/CircularHUDView.swift | 11 ++--- .../components/OSD/HUDNumericLabel.swift | 40 +++++++++++++++++++ .../components/OSD/VerticalHUDView.swift | 20 +++++++--- 3 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 DynamicIsland/components/OSD/HUDNumericLabel.swift diff --git a/DynamicIsland/components/OSD/CircularHUDView.swift b/DynamicIsland/components/OSD/CircularHUDView.swift index 26e02df..4b48d7d 100644 --- a/DynamicIsland/components/OSD/CircularHUDView.swift +++ b/DynamicIsland/components/OSD/CircularHUDView.swift @@ -74,11 +74,12 @@ struct CircularHUDView: View { if showValue { VStack { Spacer() - Text("\(Int(value * 100))") - .font(.system(size: size * 0.15, weight: .bold, design: .rounded)) - .foregroundStyle(.white.opacity(0.65)) - .contentTransition(.numericText()) - .padding(.bottom, size * 0.03) + HUDNumericLabel( + value: value, + font: .system(size: size * 0.15, weight: .bold, design: .rounded), + color: .white.opacity(0.65) + ) + .padding(.bottom, size * 0.03) } } diff --git a/DynamicIsland/components/OSD/HUDNumericLabel.swift b/DynamicIsland/components/OSD/HUDNumericLabel.swift new file mode 100644 index 0000000..7464008 --- /dev/null +++ b/DynamicIsland/components/OSD/HUDNumericLabel.swift @@ -0,0 +1,40 @@ +#if os(macOS) +import SwiftUI + +/// Shared numeric label for HUD/OSD styles with rolling value animation. +struct HUDNumericLabel: View { + let value: CGFloat + var font: Font + var color: Color + var alignment: Alignment = .center + var width: CGFloat? = nil + var minimumDigits: Int = 3 + var animation: Animation = .interactiveSpring(response: 0.35, dampingFraction: 0.75, blendDuration: 0.15) + + @State private var animatedValue: CGFloat = 0 + + var body: some View { + Text(formattedValue) + .font(font) + .monospacedDigit() + .foregroundStyle(color) + .contentTransition(.numericText()) + .frame(width: width, alignment: alignment) + .onAppear { + animatedValue = value + } + .onChange(of: value) { newValue in + withAnimation(animation) { + animatedValue = newValue + } + } + } + + private var formattedValue: String { + let clamped = max(0, min(100, Int(round(animatedValue * 100)))) + let digits = max(1, minimumDigits) + let format = "%\(digits)d" + return String(format: format, clamped) + } +} +#endif diff --git a/DynamicIsland/components/OSD/VerticalHUDView.swift b/DynamicIsland/components/OSD/VerticalHUDView.swift index 4b6f88f..29b13f3 100644 --- a/DynamicIsland/components/OSD/VerticalHUDView.swift +++ b/DynamicIsland/components/OSD/VerticalHUDView.swift @@ -73,11 +73,14 @@ struct VerticalHUDView: View { // Icon & Text overlay VStack { if showValue { - Text("\(Int(state.value * 100))%") - .font(.system(size: 10, weight: .bold)) - .foregroundStyle(state.value > 0.85 ? (useAccentColor ? .white : .black) : .secondary) - .padding(.top, 8 + (stretchOffset < 0 ? abs(stretchOffset/4) : 0)) - .contentTransition(.numericText()) + HUDNumericLabel( + value: state.value, + font: .system(size: 10, weight: .bold), + color: valueLabelColor, + alignment: .center, + width: hudWidth * 0.8 + ) + .padding(.top, 8 + (stretchOffset < 0 ? abs(stretchOffset/4) : 0)) } Spacer() @@ -225,5 +228,12 @@ struct VerticalHUDView: View { return AnyShapeStyle(useAccentColor ? Color.accentColor : Color.white) } + + private var valueLabelColor: Color { + if state.value > 0.85 { + return useAccentColor ? .white : .black + } + return .secondary + } } #endif