mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Working stuff
This commit is contained in:
parent
a75497bbbf
commit
9ea2cce485
4 changed files with 104 additions and 33 deletions
|
|
@ -87,6 +87,13 @@ extension ColorCodedProgressBar {
|
||||||
}
|
}
|
||||||
return AnyShapeStyle(ColorCodedPalette.discreteColor(for: intensity))
|
return AnyShapeStyle(ColorCodedPalette.discreteColor(for: intensity))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the palette color that matches the configured thresholds. Useful for non-linear indicators.
|
||||||
|
static func paletteColor(for value: CGFloat, mode: ColorCodingMode, smoothGradient: Bool) -> Color {
|
||||||
|
let normalized = min(max(value, 0), 1)
|
||||||
|
let intensity = mode.intensity(for: normalized)
|
||||||
|
return ColorCodedPalette.color(for: intensity, smooth: smoothGradient)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Palette Helpers
|
// MARK: - Palette Helpers
|
||||||
|
|
@ -95,6 +102,13 @@ private enum ColorCodedPalette {
|
||||||
private static let greenComponents: (CGFloat, CGFloat, CGFloat) = (0.2, 0.78, 0.35)
|
private static let greenComponents: (CGFloat, CGFloat, CGFloat) = (0.2, 0.78, 0.35)
|
||||||
private static let yellowComponents: (CGFloat, CGFloat, CGFloat) = (0.93, 0.75, 0.2)
|
private static let yellowComponents: (CGFloat, CGFloat, CGFloat) = (0.93, 0.75, 0.2)
|
||||||
private static let redComponents: (CGFloat, CGFloat, CGFloat) = (0.95, 0.36, 0.3)
|
private static let redComponents: (CGFloat, CGFloat, CGFloat) = (0.95, 0.36, 0.3)
|
||||||
|
|
||||||
|
static func color(for intensity: CGFloat, smooth: Bool) -> Color {
|
||||||
|
if smooth {
|
||||||
|
return smoothColor(for: intensity)
|
||||||
|
}
|
||||||
|
return discreteColor(for: intensity)
|
||||||
|
}
|
||||||
|
|
||||||
static func discreteColor(for intensity: CGFloat) -> Color {
|
static func discreteColor(for intensity: CGFloat) -> Color {
|
||||||
let clamped = min(max(intensity, 0), 1)
|
let clamped = min(max(intensity, 0), 1)
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,32 @@ struct InlineHUD: View {
|
||||||
@Default(.useSmoothColorGradient) var useSmoothColorGradient
|
@Default(.useSmoothColorGradient) var useSmoothColorGradient
|
||||||
@Default(.progressBarStyle) var progressBarStyle
|
@Default(.progressBarStyle) var progressBarStyle
|
||||||
@Default(.showProgressPercentages) var showProgressPercentages
|
@Default(.showProgressPercentages) var showProgressPercentages
|
||||||
|
@Default(.showBluetoothBatteryPercentageText) var showBluetoothBatteryPercentageText
|
||||||
|
@Default(.showBluetoothDeviceNameMarquee) var showBluetoothDeviceNameMarquee
|
||||||
@ObservedObject var bluetoothManager = BluetoothAudioManager.shared
|
@ObservedObject var bluetoothManager = BluetoothAudioManager.shared
|
||||||
|
|
||||||
@State private var displayName: String = ""
|
@State private var displayName: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
let baseInfoWidth: CGFloat = {
|
||||||
|
if type == .bluetoothAudio {
|
||||||
|
return showBluetoothDeviceNameMarquee ? 100 : 58
|
||||||
|
}
|
||||||
|
return 100
|
||||||
|
}()
|
||||||
|
let infoWidth = baseInfoWidth - (hoverAnimation ? 0 : 12) + gestureProgress / 2
|
||||||
|
let baseTrailingWidth: CGFloat = {
|
||||||
|
if type == .bluetoothAudio {
|
||||||
|
if showBluetoothDeviceNameMarquee {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
return showBluetoothBatteryPercentageText ? 88 : 64
|
||||||
|
}
|
||||||
|
return 100
|
||||||
|
}()
|
||||||
|
let trailingWidth = baseTrailingWidth - (hoverAnimation ? 0 : 12) + gestureProgress / 2
|
||||||
|
|
||||||
|
return HStack {
|
||||||
HStack(spacing: 5) {
|
HStack(spacing: 5) {
|
||||||
Group {
|
Group {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
@ -78,22 +98,15 @@ struct InlineHUD: View {
|
||||||
|
|
||||||
// Use marquee text for device names to handle long names
|
// Use marquee text for device names to handle long names
|
||||||
if type == .bluetoothAudio {
|
if type == .bluetoothAudio {
|
||||||
MarqueeText(
|
if showBluetoothDeviceNameMarquee {
|
||||||
$displayName,
|
MarqueeText(
|
||||||
font: .system(size: 13, weight: .medium),
|
$displayName,
|
||||||
nsFont: .body,
|
font: .system(size: 13, weight: .medium),
|
||||||
textColor: .white,
|
nsFont: .body,
|
||||||
minDuration: 0.5,
|
textColor: .white,
|
||||||
frameWidth: 85 - (hoverAnimation ? 0 : 12) + gestureProgress / 2
|
minDuration: 0.5,
|
||||||
)
|
frameWidth: infoWidth
|
||||||
.onAppear {
|
)
|
||||||
displayName = Type2Name(type)
|
|
||||||
}
|
|
||||||
.onChange(of: type) { _, _ in
|
|
||||||
displayName = Type2Name(type)
|
|
||||||
}
|
|
||||||
.onChange(of: bluetoothManager.lastConnectedDevice?.name) { _, _ in
|
|
||||||
displayName = Type2Name(type)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text(Type2Name(type))
|
Text(Type2Name(type))
|
||||||
|
|
@ -104,7 +117,7 @@ struct InlineHUD: View {
|
||||||
.contentTransition(.numericText())
|
.contentTransition(.numericText())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(width: 100 - (hoverAnimation ? 0 : 12) + gestureProgress / 2, height: vm.notchSize.height - (hoverAnimation ? 0 : 12), alignment: .leading)
|
.frame(width: infoWidth, height: vm.notchSize.height - (hoverAnimation ? 0 : 12), alignment: .leading)
|
||||||
|
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.fill(.black)
|
.fill(.black)
|
||||||
|
|
@ -129,22 +142,22 @@ struct InlineHUD: View {
|
||||||
.contentTransition(.interpolate)
|
.contentTransition(.interpolate)
|
||||||
} else if (type == .bluetoothAudio) {
|
} else if (type == .bluetoothAudio) {
|
||||||
// Bluetooth device battery display
|
// Bluetooth device battery display
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 8) {
|
||||||
if value > 0 {
|
if value > 0 {
|
||||||
Group {
|
CircularBatteryIndicator(
|
||||||
if useColorCodedBatteryDisplay && progressBarStyle != .segmented {
|
value: value,
|
||||||
DraggableProgressBar(value: .constant(value), colorMode: .battery)
|
useColorCoding: useColorCodedBatteryDisplay && progressBarStyle != .segmented,
|
||||||
} else {
|
smoothGradient: useSmoothColorGradient
|
||||||
DraggableProgressBar(value: .constant(value))
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(width: 60)
|
|
||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
Text("\(Int(value * 100))%")
|
|
||||||
.font(.caption)
|
if showBluetoothBatteryPercentageText {
|
||||||
.fontWeight(.medium)
|
Text("\(Int(value * 100))%")
|
||||||
.foregroundStyle(.white)
|
.font(.caption)
|
||||||
.lineLimit(1)
|
.fontWeight(.medium)
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// No battery info available
|
// No battery info available
|
||||||
Text("Connected")
|
Text("Connected")
|
||||||
|
|
@ -189,11 +202,51 @@ struct InlineHUD: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.trailing, 4)
|
.padding(.trailing, 4)
|
||||||
.frame(width: 100 - (hoverAnimation ? 0 : 12) + gestureProgress / 2, height: vm.closedNotchSize.height - (hoverAnimation ? 0 : 12), alignment: .center)
|
.frame(width: trailingWidth, height: vm.closedNotchSize.height - (hoverAnimation ? 0 : 12), alignment: .center)
|
||||||
}
|
}
|
||||||
.frame(height: vm.closedNotchSize.height + (hoverAnimation ? 8 : 0), alignment: .center)
|
.frame(height: vm.closedNotchSize.height + (hoverAnimation ? 8 : 0), alignment: .center)
|
||||||
|
.onAppear {
|
||||||
|
displayName = Type2Name(type)
|
||||||
|
}
|
||||||
|
.onChange(of: type) { _, _ in
|
||||||
|
displayName = Type2Name(type)
|
||||||
|
}
|
||||||
|
.onChange(of: bluetoothManager.lastConnectedDevice?.name) { _, _ in
|
||||||
|
displayName = Type2Name(type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct CircularBatteryIndicator: View {
|
||||||
|
let value: CGFloat
|
||||||
|
let useColorCoding: Bool
|
||||||
|
let smoothGradient: Bool
|
||||||
|
|
||||||
|
private var clampedValue: CGFloat {
|
||||||
|
min(max(value, 0), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var indicatorColor: Color {
|
||||||
|
if useColorCoding {
|
||||||
|
return ColorCodedProgressBar.paletteColor(for: clampedValue, mode: .battery, smoothGradient: smoothGradient)
|
||||||
|
}
|
||||||
|
return .white
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
Circle()
|
||||||
|
.stroke(Color.white.opacity(0.18), lineWidth: 2.6)
|
||||||
|
|
||||||
|
Circle()
|
||||||
|
.trim(from: 0, to: max(clampedValue, 0.015))
|
||||||
|
.rotation(.degrees(-90))
|
||||||
|
.stroke(indicatorColor, style: StrokeStyle(lineWidth: 2.8, lineCap: .round))
|
||||||
|
}
|
||||||
|
.frame(width: 22, height: 22)
|
||||||
|
.animation(.smooth(duration: 0.18), value: clampedValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SpeakerSymbol(_ value: CGFloat) -> String {
|
func SpeakerSymbol(_ value: CGFloat) -> String {
|
||||||
switch(value) {
|
switch(value) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
|
||||||
|
|
@ -675,6 +675,8 @@ struct HUD: View {
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Defaults.Toggle("Show Bluetooth device connections", key: .showBluetoothDeviceConnections)
|
Defaults.Toggle("Show Bluetooth device connections", key: .showBluetoothDeviceConnections)
|
||||||
|
Defaults.Toggle("Show battery percentage text in HUD", key: .showBluetoothBatteryPercentageText)
|
||||||
|
Defaults.Toggle("Scroll device name in HUD", key: .showBluetoothDeviceNameMarquee)
|
||||||
} header: {
|
} header: {
|
||||||
Text("Bluetooth Audio Devices")
|
Text("Bluetooth Audio Devices")
|
||||||
} footer: {
|
} footer: {
|
||||||
|
|
|
||||||
|
|
@ -391,6 +391,8 @@ extension Defaults.Keys {
|
||||||
static let useColorCodedBatteryDisplay = Key<Bool>("useColorCodedBatteryDisplay", default: true)
|
static let useColorCodedBatteryDisplay = Key<Bool>("useColorCodedBatteryDisplay", default: true)
|
||||||
static let useColorCodedVolumeDisplay = Key<Bool>("useColorCodedVolumeDisplay", default: false)
|
static let useColorCodedVolumeDisplay = Key<Bool>("useColorCodedVolumeDisplay", default: false)
|
||||||
static let useSmoothColorGradient = Key<Bool>("useSmoothColorGradient", default: true)
|
static let useSmoothColorGradient = Key<Bool>("useSmoothColorGradient", default: true)
|
||||||
|
static let showBluetoothBatteryPercentageText = Key<Bool>("showBluetoothBatteryPercentageText", default: true)
|
||||||
|
static let showBluetoothDeviceNameMarquee = Key<Bool>("showBluetoothDeviceNameMarquee", default: true)
|
||||||
|
|
||||||
// MARK: Stats Feature
|
// MARK: Stats Feature
|
||||||
static let enableStatsFeature = Key<Bool>("enableStatsFeature", default: false)
|
static let enableStatsFeature = Key<Bool>("enableStatsFeature", default: false)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue