mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Update Settings View
This commit is contained in:
parent
0d68bfff94
commit
105b0466a4
2 changed files with 163 additions and 2 deletions
|
|
@ -12419,6 +12419,10 @@
|
||||||
},
|
},
|
||||||
"shouldTranslate" : false
|
"shouldTranslate" : false
|
||||||
},
|
},
|
||||||
|
"HUD icon style" : {
|
||||||
|
"comment" : "A label describing the style of the 3D or symbol-based Bluetooth HUD icon.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"HUD Position" : {
|
"HUD Position" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"pt-BR" : {
|
"pt-BR" : {
|
||||||
|
|
|
||||||
|
|
@ -1776,6 +1776,7 @@ private struct HUDSelectionCard<Preview: View>: View {
|
||||||
|
|
||||||
private struct DevicesSettingsView: View {
|
private struct DevicesSettingsView: View {
|
||||||
@Default(.progressBarStyle) var progressBarStyle
|
@Default(.progressBarStyle) var progressBarStyle
|
||||||
|
@Default(.useBluetoothHUD3DIcon) private var useBluetoothHUD3DIcon
|
||||||
|
|
||||||
private func highlightID(_ title: String) -> String {
|
private func highlightID(_ title: String) -> String {
|
||||||
SettingsTab.devices.highlightID(for: title)
|
SettingsTab.devices.highlightID(for: title)
|
||||||
|
|
@ -1796,8 +1797,29 @@ private struct DevicesSettingsView: View {
|
||||||
.settingsHighlight(id: highlightID("Show battery percentage text in HUD"))
|
.settingsHighlight(id: highlightID("Show battery percentage text in HUD"))
|
||||||
Defaults.Toggle("Scroll device name in HUD", key: .showBluetoothDeviceNameMarquee)
|
Defaults.Toggle("Scroll device name in HUD", key: .showBluetoothDeviceNameMarquee)
|
||||||
.settingsHighlight(id: highlightID("Scroll device name in HUD"))
|
.settingsHighlight(id: highlightID("Scroll device name in HUD"))
|
||||||
Defaults.Toggle("Use 3D Bluetooth HUD icon", key: .useBluetoothHUD3DIcon)
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
.settingsHighlight(id: highlightID("Use 3D Bluetooth HUD icon"))
|
Text("HUD icon style")
|
||||||
|
.font(.system(size: 13, weight: .semibold))
|
||||||
|
.foregroundStyle(.primary)
|
||||||
|
|
||||||
|
HStack(spacing: 16) {
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
BluetoothHUDIconStyleCard(
|
||||||
|
style: .symbol,
|
||||||
|
isSelected: !useBluetoothHUD3DIcon
|
||||||
|
) {
|
||||||
|
useBluetoothHUD3DIcon = false
|
||||||
|
}
|
||||||
|
BluetoothHUDIconStyleCard(
|
||||||
|
style: .threeD,
|
||||||
|
isSelected: useBluetoothHUD3DIcon
|
||||||
|
) {
|
||||||
|
useBluetoothHUD3DIcon = true
|
||||||
|
}
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.settingsHighlight(id: highlightID("Use 3D Bluetooth HUD icon"))
|
||||||
} header: {
|
} header: {
|
||||||
Text("Bluetooth Audio Devices")
|
Text("Bluetooth Audio Devices")
|
||||||
} footer: {
|
} footer: {
|
||||||
|
|
@ -2669,6 +2691,141 @@ struct About: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension DevicesSettingsView {
|
||||||
|
enum BluetoothHUDIconStyle: String {
|
||||||
|
case symbol
|
||||||
|
case threeD
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .symbol:
|
||||||
|
return "Symbol"
|
||||||
|
case .threeD:
|
||||||
|
return "3D"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BluetoothHUDIconStyleCard: View {
|
||||||
|
let style: BluetoothHUDIconStyle
|
||||||
|
let isSelected: Bool
|
||||||
|
let action: () -> Void
|
||||||
|
|
||||||
|
@State private var isHovering = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
ZStack {
|
||||||
|
RoundedRectangle(cornerRadius: 12)
|
||||||
|
.fill(backgroundColor)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 12)
|
||||||
|
.strokeBorder(borderColor, lineWidth: isSelected ? 2 : 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
preview
|
||||||
|
}
|
||||||
|
.frame(width: 90, height: 64)
|
||||||
|
.onHover { hovering in
|
||||||
|
withAnimation(.easeInOut(duration: 0.2)) {
|
||||||
|
isHovering = hovering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(style.title)
|
||||||
|
.font(.caption)
|
||||||
|
.fontWeight(.medium)
|
||||||
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.onTapGesture { action() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private var preview: some View {
|
||||||
|
Group {
|
||||||
|
switch style {
|
||||||
|
case .symbol:
|
||||||
|
Image(systemName: BluetoothAudioDeviceType.airpods.sfSymbol)
|
||||||
|
.font(.system(size: 24, weight: .semibold))
|
||||||
|
.symbolRenderingMode(.hierarchical)
|
||||||
|
case .threeD:
|
||||||
|
if let url = Bundle.main.url(
|
||||||
|
forResource: BluetoothAudioDeviceType.airpods.inlineHUDAnimationBaseName,
|
||||||
|
withExtension: "mov"
|
||||||
|
) {
|
||||||
|
SettingsLoopingVideoIcon(url: url, size: CGSize(width: 28, height: 28))
|
||||||
|
.frame(width: 28, height: 28)
|
||||||
|
} else {
|
||||||
|
Image(systemName: BluetoothAudioDeviceType.airpods.sfSymbol)
|
||||||
|
.font(.system(size: 24, weight: .semibold))
|
||||||
|
.symbolRenderingMode(.hierarchical)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var backgroundColor: Color {
|
||||||
|
if isSelected { return Color.accentColor.opacity(0.12) }
|
||||||
|
if isHovering { return Color.primary.opacity(0.05) }
|
||||||
|
return Color(nsColor: .controlBackgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var borderColor: Color {
|
||||||
|
if isSelected { return Color.accentColor }
|
||||||
|
if isHovering { return Color.primary.opacity(0.1) }
|
||||||
|
return Color.clear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct SettingsLoopingVideoIcon: NSViewRepresentable {
|
||||||
|
let url: URL
|
||||||
|
let size: CGSize
|
||||||
|
|
||||||
|
func makeNSView(context: Context) -> NSView {
|
||||||
|
let view = NSView(frame: NSRect(origin: .zero, size: size))
|
||||||
|
view.wantsLayer = true
|
||||||
|
|
||||||
|
let layer = AVPlayerLayer()
|
||||||
|
layer.videoGravity = .resizeAspect
|
||||||
|
layer.frame = view.bounds
|
||||||
|
view.layer?.addSublayer(layer)
|
||||||
|
context.coordinator.attach(layer: layer, url: url)
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNSView(_ nsView: NSView, context: Context) {}
|
||||||
|
|
||||||
|
func makeCoordinator() -> Coordinator { Coordinator() }
|
||||||
|
|
||||||
|
final class Coordinator {
|
||||||
|
private var controller: SettingsLoopingPlayerController?
|
||||||
|
|
||||||
|
func attach(layer: AVPlayerLayer, url: URL) {
|
||||||
|
controller = SettingsLoopingPlayerController(url: url)
|
||||||
|
layer.player = controller?.player
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class SettingsLoopingPlayerController {
|
||||||
|
let player: AVQueuePlayer
|
||||||
|
private var looper: AVPlayerLooper?
|
||||||
|
|
||||||
|
init(url: URL) {
|
||||||
|
let item = AVPlayerItem(url: url)
|
||||||
|
player = AVQueuePlayer()
|
||||||
|
player.isMuted = true
|
||||||
|
player.actionAtItemEnd = .none
|
||||||
|
looper = AVPlayerLooper(player: player, templateItem: item)
|
||||||
|
player.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
player.pause()
|
||||||
|
looper = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Shelf: View {
|
struct Shelf: View {
|
||||||
@Default(.quickShareProvider) var quickShareProvider
|
@Default(.quickShareProvider) var quickShareProvider
|
||||||
@Default(.expandedDragDetection) var expandedDragDetection
|
@Default(.expandedDragDetection) var expandedDragDetection
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue