Skip to content

Commit

Permalink
Merge branch 'feature/DeltaSkins' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeMatt committed Jan 28, 2025
2 parents e604473 + 9b0a2a3 commit 13eff4e
Show file tree
Hide file tree
Showing 63 changed files with 10,170 additions and 60 deletions.
6 changes: 6 additions & 0 deletions PVFeatureFlags/Sources/PVFeatureFlags/PVFeatureFlags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ public struct FeatureFlag: Codable, Sendable {
self.allowedAppTypes = allowedAppTypes
self.description = description
}

/// Enables advanced skin features like filters and debug mode
public static let advancedSkinFeatures = FeatureFlag(
enabled: true,
description: "Enables advanced skin features like filters and debug mode"
)
}

/// Root structure for feature flags JSON
Expand Down
91 changes: 91 additions & 0 deletions PVSettings/Sources/PVSettings/Settings/Model/PVSettingsModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,97 @@ public extension Defaults.Keys {
static let controllerOpacity = Key<Double>("controllerOpacity", default: 0.8)

static let pauseButtonIsMenuButton = Key<Bool>("pauseButtonIsMenuButton", default: false)
static let hapticFeedback = Key<Bool>("hapticFeedback", default: true)

static let buttonPressEffect = Key<ButtonPressEffect>("buttonPressEffect", default: .glow)
static let buttonSound = Key<ButtonSound>("buttonSound", default: .click)
}

public enum ButtonPressEffect: String, Codable, Equatable, UserDefaultsRepresentable, Defaults.Serializable, CaseIterable {
case bubble = "bubble"
case ring = "ring"
case glow = "glow"

public var description: String {
switch self {
case .bubble:
return "Bubble + Ring"
case .ring:
return "Ring Only"
case .glow:
return "Radial Glow"
}
}

public var subtitle: String {
switch self {
case .bubble:
return "Shows both a gradient bubble and ring outline"
case .ring:
return "Shows only the ring outline effect"
case .glow:
return "Shows a soft radial glow effect"
}
}
}

public enum ButtonSound: String, Codable, Equatable, UserDefaultsRepresentable, Defaults.Serializable, CaseIterable {
case none = "none"
case generated = "generated"
case click = "click"
case tap = "tap"
case pop = "pop"

public var description: String {
switch self {
case .none:
return "No Sound"
case .generated:
return "Generated"
case .click:
return "Click"
case .tap:
return "Tap"
case .pop:
return "Pop"
}
}

public var subtitle: String {
switch self {
case .none:
return "Disable button press sounds"
case .generated:
return "Classic synthesized tone"
case .click:
return "Mechanical click sound"
case .tap:
return "Soft tap sound"
case .pop:
return "Bubble pop sound"
}
}

/// The sound file name in the bundle
public var filename: String {
switch self {
case .none, .generated:
return ""
case .click:
return "button-click"
case .tap:
return "button-tap"
case .pop:
return "button-pop"
}
}

public var hasReleaseSample: Bool {
switch self {
case .click, .pop: return true
default: return false
}
}
}

// MARK: File syste
Expand Down
1 change: 1 addition & 0 deletions PVUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ let package = Package(
resources: [
.copy("Resources/Shaders/fsh"),
.copy("Resources/Shaders/GLES"),
.process("Resources/Sounds"),
.process("Resources/Shaders/Metal"),
.process("Resources/SystemIcons.xcassets"),
.process("Resources/Assets.xcassets"),
Expand Down
95 changes: 93 additions & 2 deletions PVUI/Sources/PVSwiftUI/Settings/SettingsSwiftUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,9 @@ private struct VideoSection: View {
private struct ControllerSection: View {
@Default(.use8BitdoM30) var use8BitdoM30
@Default(.pauseButtonIsMenuButton) var pauseButtonIsMenuButton
@Default(.hapticFeedback) var hapticFeedback
@Default(.buttonPressEffect) var buttonPressEffect
@Default(.buttonSound) var buttonSound

var body: some View {
Group {
Expand All @@ -530,11 +533,21 @@ private struct ControllerSection: View {
icon: .sfSymbol("pause.rectangle"))
}
}
#if !os(tvOS)

#if !os(tvOS)
ThemedToggle(isOn: $hapticFeedback) {
SettingsRow(title: "Haptic Feedback",
subtitle: "Vibrate when pressing buttons.",
icon: .sfSymbol("iphone.radiowaves.left.and.right"))
}
OnScreenControllerSection()
#endif
#endif
}
}

private func playButtonSound(_ sound: ButtonSound) {
PVUIBase.ButtonSoundGenerator.shared.playSound(sound, pan: 0, volume: 1.0)
}
}

#if !os(tvOS)
Expand All @@ -543,6 +556,8 @@ private struct OnScreenControllerSection: View {
@Default(.buttonTints) var buttonTints
@Default(.allRightShoulders) var allRightShoulders
@Default(.buttonVibration) var buttonVibration
@Default(.buttonSound) var buttonSound
@Default(.buttonPressEffect) var buttonPressEffect
@Default(.missingButtonsAlwaysOn) var missingButtonsAlwaysOn
@Default(.onscreenJoypad) var onscreenJoypad
@Default(.onscreenJoypadWithKeyboard) var onscreenJoypadWithKeyboard
Expand Down Expand Up @@ -591,8 +606,84 @@ private struct OnScreenControllerSection: View {
icon: .sfSymbol("keyboard.badge.eye"))
}

if FeatureFlag.advancedSkinFeatures.enabled {
// Button Sound Effect Picker
NavigationLink {
Form {
Section(header: Text("Button Sound Effect")) {
ForEach(ButtonSound.allCases, id: \.self) { sound in
Button {
buttonSound = sound
// Play sample sound when selected
if sound != .none {
playButtonSound(sound)
}
} label: {
HStack {
VStack(alignment: .leading) {
Text(sound.description)
.foregroundColor(.primary)
Text(sound.subtitle)
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
if buttonSound == sound {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
}
}
}
}
}
}
.navigationTitle("Button Sound Effect")
} label: {
SettingsRow(title: "Button Sound Effect",
subtitle: buttonSound.description,
icon: .sfSymbol("speaker.wave.2"))
}

// Button Press Effect Picker
NavigationLink {
Form {
Section(header: Text("Button Press Effect")) {
ForEach(ButtonPressEffect.allCases, id: \.self) { effect in
Button {
buttonPressEffect = effect
} label: {
HStack {
VStack(alignment: .leading) {
Text(effect.description)
.foregroundColor(.primary)
Text(effect.subtitle)
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
if buttonPressEffect == effect {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
}
}
}
}
}
}
.navigationTitle("Button Effect Style")
} label: {
SettingsRow(title: "Button Effect Style",
subtitle: buttonPressEffect.description,
icon: .sfSymbol("circle.circle"))
}

}
}
}

private func playButtonSound(_ sound: ButtonSound) {
PVUIBase.ButtonSoundGenerator.shared.playSound(sound, pan: 0, volume: 1.0)
}
}
#endif

Expand Down
Loading

0 comments on commit 13eff4e

Please sign in to comment.