Remapping functionality

This commit is contained in:
Hariharan Mudaliar 2025-12-14 12:11:30 +05:30
commit 96dde9d555
No known key found for this signature in database
GPG key ID: D04F1C2B9194DDFC
9 changed files with 687 additions and 399 deletions

View file

@ -1360,13 +1360,9 @@ struct Media: View {
@Default(.sneakPeekStyles) var sneakPeekStyles
@Default(.enableMinimalisticUI) var enableMinimalisticUI
@Default(.showShuffleAndRepeat) private var showShuffleAndRepeat
@Default(.musicAuxLeftControl) private var musicAuxLeftControl
@Default(.musicAuxRightControl) private var musicAuxRightControl
@Default(.musicSkipBehavior) private var musicSkipBehavior
@Default(.musicControlWindowEnabled) private var musicControlWindowEnabled
@Default(.enableLockScreenMediaWidget) private var enableLockScreenMediaWidget
@State private var previousLeftAuxControl: MusicAuxiliaryControl = Defaults[.musicAuxLeftControl]
@State private var previousRightAuxControl: MusicAuxiliaryControl = Defaults[.musicAuxRightControl]
private func highlightID(_ title: String) -> String {
SettingsTab.media.highlightID(for: title)
@ -1408,16 +1404,21 @@ struct Media: View {
Section {
Defaults.Toggle(key: .showShuffleAndRepeat) {
HStack {
Text("Show shuffle and repeat buttons")
Text("Enable customizable controls")
customBadge(text: "Beta")
}
}
Defaults.Toggle(key: .showMediaOutputControl) {
Text("Show media output control in other layouts")
Text("Allow media output control in layouts")
}
.disabled(!showShuffleAndRepeat)
if showShuffleAndRepeat {
mediaControlsGrid
MusicSlotConfigurationView()
} else {
Text("Turn on customizable controls to rearrange media buttons.")
.font(.caption)
.foregroundStyle(.secondary)
.padding(.vertical, 4)
}
} header: {
Text("Media controls")
@ -1512,142 +1513,9 @@ struct Media: View {
Defaults[.enableFullscreenMediaDetection] = hideNotchOption != .never
}
}
.onAppear {
ensureAuxControlsUnique()
previousLeftAuxControl = musicAuxLeftControl
previousRightAuxControl = musicAuxRightControl
}
.onChange(of: musicAuxLeftControl) { newValue in
if newValue == musicAuxRightControl {
let fallback = MusicAuxiliaryControl.alternative(
excluding: newValue,
preferring: previousLeftAuxControl
)
if fallback != musicAuxRightControl {
musicAuxRightControl = fallback
}
}
previousLeftAuxControl = newValue
}
.onChange(of: musicAuxRightControl) { newValue in
if newValue == musicAuxLeftControl {
let fallback = MusicAuxiliaryControl.alternative(
excluding: newValue,
preferring: previousRightAuxControl
)
if fallback != musicAuxLeftControl {
musicAuxLeftControl = fallback
}
}
previousRightAuxControl = newValue
}
.onChange(of: showShuffleAndRepeat) { isEnabled in
if isEnabled {
ensureAuxControlsUnique()
}
}
.navigationTitle("Media")
}
private var mediaControlsGrid: some View {
VStack(alignment: .leading, spacing: 16) {
mediaControlsRow(title: "Left button", selection: $musicAuxLeftControl, otherSelection: musicAuxRightControl)
mediaControlsRow(title: "Right button", selection: $musicAuxRightControl, otherSelection: musicAuxLeftControl)
}
.padding(.vertical, 12)
.frame(maxWidth: .infinity, alignment: .leading)
}
private func mediaControlsRow(title: String, selection: Binding<MusicAuxiliaryControl>, otherSelection: MusicAuxiliaryControl) -> some View {
VStack(alignment: .leading, spacing: 12) {
Text(title)
.font(.system(size: 13, weight: .semibold))
.foregroundStyle(.white)
HStack(spacing: 12) {
ForEach([MusicAuxiliaryControl.mediaOutput, MusicAuxiliaryControl.repeatMode, MusicAuxiliaryControl.lyrics, MusicAuxiliaryControl.shuffle], id: \.self) { control in
MediaControlButton(
control: control,
isSelected: selection.wrappedValue == control,
disabled: control == otherSelection
) {
selection.wrappedValue = control
}
}
}
}
}
struct MediaControlButton: View {
let control: MusicAuxiliaryControl
let isSelected: Bool
let disabled: 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)
)
Image(systemName: control.symbolName)
.font(.system(size: 22, weight: .medium))
.foregroundStyle(iconColor)
}
.frame(width: 68, height: 52)
.onHover { hovering in
if !disabled {
withAnimation(.easeInOut(duration: 0.2)) {
isHovering = hovering
}
}
}
Text(control.displayName)
.font(.caption)
.fontWeight(.medium)
.lineLimit(2)
.multilineTextAlignment(.center)
.frame(width: 80)
.foregroundStyle(disabled ? .secondary : .primary)
}
.contentShape(Rectangle())
.onTapGesture {
if !disabled {
action()
}
}
.opacity(disabled ? 0.5 : 1.0)
}
private var backgroundColor: Color {
if disabled { return Color(nsColor: .controlBackgroundColor) }
if isSelected { return Color.accentColor.opacity(0.1) }
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 var iconColor: Color {
if disabled { return .secondary }
if isSelected { return .accentColor }
return .primary
}
}
// Only show controller options that are available on this macOS version
private var availableMediaControllers: [MediaControllerType] {
if MusicManager.shared.isNowPlayingDeprecated {
@ -1656,15 +1524,6 @@ struct Media: View {
return MediaControllerType.allCases
}
}
private func ensureAuxControlsUnique() {
guard showShuffleAndRepeat, musicAuxLeftControl == musicAuxRightControl else { return }
let fallback = MusicAuxiliaryControl.alternative(excluding: musicAuxLeftControl)
if fallback != musicAuxRightControl {
musicAuxRightControl = fallback
}
}
}
struct CalendarSettings: View {