mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Update Notch slider for volume/brightness feedback by kryoscopic
This commit is contained in:
parent
40e805a785
commit
166c053621
3 changed files with 253 additions and 5 deletions
|
|
@ -583,7 +583,7 @@ struct ContentView: View {
|
||||||
.frame(width: 76, alignment: .trailing)
|
.frame(width: 76, alignment: .trailing)
|
||||||
}
|
}
|
||||||
.frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center)
|
.frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center)
|
||||||
} else if coordinator.sneakPeek.show && Defaults[.inlineHUD] && (coordinator.sneakPeek.type != .music) && (coordinator.sneakPeek.type != .battery) && (coordinator.sneakPeek.type != .timer) && (coordinator.sneakPeek.type != .reminder) && !coordinator.sneakPeek.type.isExtensionPayload && (coordinator.sneakPeek.type != .volume || vm.notchState == .closed) {
|
} else if coordinator.sneakPeek.show && Defaults[.inlineHUD] && (coordinator.sneakPeek.type != .music) && (coordinator.sneakPeek.type != .battery) && (coordinator.sneakPeek.type != .timer) && (coordinator.sneakPeek.type != .reminder) && !coordinator.sneakPeek.type.isExtensionPayload && ((coordinator.sneakPeek.type != .volume && coordinator.sneakPeek.type != .brightness && coordinator.sneakPeek.type != .backlight) || vm.notchState == .closed) {
|
||||||
InlineHUD(type: $coordinator.sneakPeek.type, value: $coordinator.sneakPeek.value, icon: $coordinator.sneakPeek.icon, hoverAnimation: $isHovering, gestureProgress: $gestureProgress)
|
InlineHUD(type: $coordinator.sneakPeek.type, value: $coordinator.sneakPeek.value, icon: $coordinator.sneakPeek.icon, hoverAnimation: $isHovering, gestureProgress: $gestureProgress)
|
||||||
.transition(
|
.transition(
|
||||||
coordinator.sneakPeek.type == .capsLock
|
coordinator.sneakPeek.type == .capsLock
|
||||||
|
|
@ -631,7 +631,7 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if coordinator.sneakPeek.show {
|
if coordinator.sneakPeek.show {
|
||||||
if (coordinator.sneakPeek.type != .music) && (coordinator.sneakPeek.type != .battery) && (coordinator.sneakPeek.type != .timer) && !Defaults[.inlineHUD] && (coordinator.sneakPeek.type != .volume || vm.notchState == .closed) {
|
if (coordinator.sneakPeek.type != .music) && (coordinator.sneakPeek.type != .battery) && (coordinator.sneakPeek.type != .timer) && !Defaults[.inlineHUD] && ((coordinator.sneakPeek.type != .volume && coordinator.sneakPeek.type != .brightness && coordinator.sneakPeek.type != .backlight) || vm.notchState == .closed) {
|
||||||
SystemEventIndicatorModifier(eventType: $coordinator.sneakPeek.type, value: $coordinator.sneakPeek.value, icon: $coordinator.sneakPeek.icon, sendEventBack: { _ in
|
SystemEventIndicatorModifier(eventType: $coordinator.sneakPeek.type, value: $coordinator.sneakPeek.value, icon: $coordinator.sneakPeek.icon, sendEventBack: { _ in
|
||||||
//
|
//
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@ struct MinimalisticMusicPlayerView: View {
|
||||||
@ObservedObject private var reminderManager = ReminderLiveActivityManager.shared
|
@ObservedObject private var reminderManager = ReminderLiveActivityManager.shared
|
||||||
@ObservedObject private var timerManager = TimerManager.shared
|
@ObservedObject private var timerManager = TimerManager.shared
|
||||||
@ObservedObject private var coordinator = DynamicIslandViewCoordinator.shared
|
@ObservedObject private var coordinator = DynamicIslandViewCoordinator.shared
|
||||||
|
@State private var hudValue: Double = 0
|
||||||
|
@State private var hudDragging: Bool = false
|
||||||
|
@State private var hudLastDragged: Date = .distantPast
|
||||||
@Default(.enableReminderLiveActivity) private var enableReminderLiveActivity
|
@Default(.enableReminderLiveActivity) private var enableReminderLiveActivity
|
||||||
@Default(.enableLyrics) private var enableLyrics
|
@Default(.enableLyrics) private var enableLyrics
|
||||||
@Default(.timerPresets) private var timerPresets
|
@Default(.timerPresets) private var timerPresets
|
||||||
|
|
@ -85,8 +88,13 @@ struct MinimalisticMusicPlayerView: View {
|
||||||
.padding(.top, 6)
|
.padding(.top, 6)
|
||||||
|
|
||||||
// Compact playback controls
|
// Compact playback controls
|
||||||
playbackControls
|
if shouldShowControlHUDRow {
|
||||||
.padding(.top, 4)
|
controlHUDRow
|
||||||
|
.padding(.top, 4)
|
||||||
|
} else {
|
||||||
|
playbackControls
|
||||||
|
.padding(.top, 4)
|
||||||
|
}
|
||||||
|
|
||||||
if enableLyrics {
|
if enableLyrics {
|
||||||
lyricsView
|
lyricsView
|
||||||
|
|
@ -672,6 +680,122 @@ private struct MinimalisticReminderDetailsView: View {
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.padding(.top, 2)
|
.padding(.top, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var shouldShowControlHUDRow: Bool {
|
||||||
|
guard vm.notchState == .open else { return false }
|
||||||
|
guard coordinator.sneakPeek.show else { return false }
|
||||||
|
guard Defaults[.enableSystemHUD] else { return false }
|
||||||
|
guard !Defaults[.enableCustomOSD] && !Defaults[.enableVerticalHUD] && !Defaults[.enableCircularHUD] else { return false }
|
||||||
|
|
||||||
|
switch coordinator.sneakPeek.type {
|
||||||
|
case .volume:
|
||||||
|
return Defaults[.enableVolumeHUD]
|
||||||
|
case .brightness:
|
||||||
|
return Defaults[.enableBrightnessHUD]
|
||||||
|
case .backlight:
|
||||||
|
return Defaults[.enableKeyboardBacklightHUD]
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controlHUDRow: some View {
|
||||||
|
HStack(alignment: .center, spacing: 10) {
|
||||||
|
if !controlLeftIconName.isEmpty {
|
||||||
|
Image(systemName: controlLeftIconName)
|
||||||
|
.font(.system(size: 16, weight: .semibold))
|
||||||
|
.frame(width: 22, height: 22, alignment: .center)
|
||||||
|
}
|
||||||
|
|
||||||
|
controlHUDSlider
|
||||||
|
|
||||||
|
if !controlRightIconName.isEmpty {
|
||||||
|
Image(systemName: controlRightIconName)
|
||||||
|
.font(.system(size: 16, weight: .semibold))
|
||||||
|
.frame(width: 22, height: 22, alignment: .center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
.frame(height: 60, alignment: .center)
|
||||||
|
.onAppear { syncHUDValueIfNeeded(force: true) }
|
||||||
|
.onChange(of: coordinator.sneakPeek.value) { _, _ in
|
||||||
|
syncHUDValueIfNeeded(force: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controlHUDSlider: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
CustomSlider(
|
||||||
|
value: Binding(
|
||||||
|
get: { hudValue },
|
||||||
|
set: { newValue in
|
||||||
|
hudValue = newValue
|
||||||
|
updateControlHUDValue(newValue)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
range: 0...1,
|
||||||
|
color: .white,
|
||||||
|
dragging: $hudDragging,
|
||||||
|
lastDragged: $hudLastDragged,
|
||||||
|
onValueChange: { newValue in
|
||||||
|
updateControlHUDValue(newValue)
|
||||||
|
},
|
||||||
|
thumbSize: 10,
|
||||||
|
restingTrackHeight: 4,
|
||||||
|
draggingTrackHeight: 7
|
||||||
|
)
|
||||||
|
.frame(height: 7)
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
}
|
||||||
|
.frame(height: 22)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func syncHUDValueIfNeeded(force: Bool) {
|
||||||
|
guard shouldShowControlHUDRow else { return }
|
||||||
|
guard force || !hudDragging else { return }
|
||||||
|
hudValue = Double(coordinator.sneakPeek.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateControlHUDValue(_ newValue: Double) {
|
||||||
|
let clamped = max(0, min(1, newValue))
|
||||||
|
switch coordinator.sneakPeek.type {
|
||||||
|
case .volume:
|
||||||
|
SystemVolumeController.shared.setVolume(Float(clamped))
|
||||||
|
case .brightness:
|
||||||
|
SystemBrightnessController.shared.setBrightness(Float(clamped))
|
||||||
|
case .backlight:
|
||||||
|
SystemKeyboardBacklightController.shared.setLevel(Float(clamped))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controlLeftIconName: String {
|
||||||
|
switch coordinator.sneakPeek.type {
|
||||||
|
case .volume:
|
||||||
|
return SystemVolumeController.shared.isMuted ? "speaker.slash" : "speaker.wave.1"
|
||||||
|
case .brightness:
|
||||||
|
return "sun.min.fill"
|
||||||
|
case .backlight:
|
||||||
|
return "light.min"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controlRightIconName: String {
|
||||||
|
switch coordinator.sneakPeek.type {
|
||||||
|
case .volume:
|
||||||
|
return SystemVolumeController.shared.isMuted ? "" : "speaker.wave.3"
|
||||||
|
case .brightness:
|
||||||
|
return "sun.max.fill"
|
||||||
|
case .backlight:
|
||||||
|
return "light.max"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var playPauseButton: some View {
|
private var playPauseButton: some View {
|
||||||
MinimalisticSquircircleButton(
|
MinimalisticSquircircleButton(
|
||||||
|
|
|
||||||
|
|
@ -129,10 +129,15 @@ struct AlbumArtView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MusicControlsView: View {
|
struct MusicControlsView: View {
|
||||||
|
@EnvironmentObject var vm: DynamicIslandViewModel
|
||||||
@ObservedObject var musicManager = MusicManager.shared
|
@ObservedObject var musicManager = MusicManager.shared
|
||||||
|
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
||||||
@State private var sliderValue: Double = 0
|
@State private var sliderValue: Double = 0
|
||||||
@State private var dragging: Bool = false
|
@State private var dragging: Bool = false
|
||||||
@State private var lastDragged: Date = .distantPast
|
@State private var lastDragged: Date = .distantPast
|
||||||
|
@State private var hudValue: Double = 0
|
||||||
|
@State private var hudDragging: Bool = false
|
||||||
|
@State private var hudLastDragged: Date = .distantPast
|
||||||
@Default(.showShuffleAndRepeat) private var showCustomControls
|
@Default(.showShuffleAndRepeat) private var showCustomControls
|
||||||
@Default(.musicControlSlots) private var slotConfig
|
@Default(.musicControlSlots) private var slotConfig
|
||||||
@Default(.showMediaOutputControl) private var showMediaOutputControl
|
@Default(.showMediaOutputControl) private var showMediaOutputControl
|
||||||
|
|
@ -144,7 +149,11 @@ struct MusicControlsView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
songInfoAndSlider
|
songInfoAndSlider
|
||||||
playbackControls
|
if shouldShowControlHUDRow {
|
||||||
|
controlHUDRow
|
||||||
|
} else {
|
||||||
|
playbackControls
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
@ -238,6 +247,121 @@ struct MusicControlsView: View {
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var shouldShowControlHUDRow: Bool {
|
||||||
|
guard vm.notchState == .open else { return false }
|
||||||
|
guard coordinator.sneakPeek.show else { return false }
|
||||||
|
guard Defaults[.enableSystemHUD] else { return false }
|
||||||
|
guard !Defaults[.enableCustomOSD] && !Defaults[.enableVerticalHUD] && !Defaults[.enableCircularHUD] else { return false }
|
||||||
|
|
||||||
|
switch coordinator.sneakPeek.type {
|
||||||
|
case .volume:
|
||||||
|
return Defaults[.enableVolumeHUD]
|
||||||
|
case .brightness:
|
||||||
|
return Defaults[.enableBrightnessHUD]
|
||||||
|
case .backlight:
|
||||||
|
return Defaults[.enableKeyboardBacklightHUD]
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controlHUDRow: some View {
|
||||||
|
HStack(alignment: .center, spacing: 10) {
|
||||||
|
if !controlLeftIconName.isEmpty {
|
||||||
|
Image(systemName: controlLeftIconName)
|
||||||
|
.font(.system(size: 16, weight: .semibold))
|
||||||
|
.frame(width: 22, height: 22, alignment: .center)
|
||||||
|
}
|
||||||
|
|
||||||
|
controlHUDSlider
|
||||||
|
|
||||||
|
if !controlRightIconName.isEmpty {
|
||||||
|
Image(systemName: controlRightIconName)
|
||||||
|
.font(.system(size: 16, weight: .semibold))
|
||||||
|
.frame(width: 22, height: 22, alignment: .center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
.onAppear { syncHUDValueIfNeeded(force: true) }
|
||||||
|
.onChange(of: coordinator.sneakPeek.value) { _, _ in
|
||||||
|
syncHUDValueIfNeeded(force: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controlHUDSlider: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
CustomSlider(
|
||||||
|
value: Binding(
|
||||||
|
get: { hudValue },
|
||||||
|
set: { newValue in
|
||||||
|
hudValue = newValue
|
||||||
|
updateControlHUDValue(newValue)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
range: 0...1,
|
||||||
|
color: .white,
|
||||||
|
dragging: $hudDragging,
|
||||||
|
lastDragged: $hudLastDragged,
|
||||||
|
onValueChange: { newValue in
|
||||||
|
updateControlHUDValue(newValue)
|
||||||
|
},
|
||||||
|
thumbSize: 10,
|
||||||
|
restingTrackHeight: 4,
|
||||||
|
draggingTrackHeight: 7
|
||||||
|
)
|
||||||
|
.frame(height: 7)
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
}
|
||||||
|
.frame(height: 22)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func syncHUDValueIfNeeded(force: Bool) {
|
||||||
|
guard shouldShowControlHUDRow else { return }
|
||||||
|
guard force || !hudDragging else { return }
|
||||||
|
hudValue = Double(coordinator.sneakPeek.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateControlHUDValue(_ newValue: Double) {
|
||||||
|
let clamped = max(0, min(1, newValue))
|
||||||
|
switch coordinator.sneakPeek.type {
|
||||||
|
case .volume:
|
||||||
|
SystemVolumeController.shared.setVolume(Float(clamped))
|
||||||
|
case .brightness:
|
||||||
|
SystemBrightnessController.shared.setBrightness(Float(clamped))
|
||||||
|
case .backlight:
|
||||||
|
SystemKeyboardBacklightController.shared.setLevel(Float(clamped))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controlLeftIconName: String {
|
||||||
|
switch coordinator.sneakPeek.type {
|
||||||
|
case .volume:
|
||||||
|
return SystemVolumeController.shared.isMuted ? "speaker.slash" : "speaker.wave.1"
|
||||||
|
case .brightness:
|
||||||
|
return "sun.min.fill"
|
||||||
|
case .backlight:
|
||||||
|
return "light.min"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controlRightIconName: String {
|
||||||
|
switch coordinator.sneakPeek.type {
|
||||||
|
case .volume:
|
||||||
|
return SystemVolumeController.shared.isMuted ? "" : "speaker.wave.3"
|
||||||
|
case .brightness:
|
||||||
|
return "sun.max.fill"
|
||||||
|
case .backlight:
|
||||||
|
return "light.max"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var brandAccentColor: Color {
|
private var brandAccentColor: Color {
|
||||||
musicManager.brandAccentColor
|
musicManager.brandAccentColor
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue