Skip to content

Commit

Permalink
Merge branch 'feature/CustomIcon' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeMatt committed Nov 27, 2024
2 parents f3f77fd + 7932bed commit 3f421f2
Show file tree
Hide file tree
Showing 337 changed files with 3,247 additions and 51 deletions.
8 changes: 6 additions & 2 deletions PVLibrary/Sources/PVLibrary/Database/PVGameLibrary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,14 @@ public final class ROMLocationMigrator {

/// Old paths that need migration
private var oldPaths: [(source: URL, destination: URL)] {
guard let sharedContainer = fileManager.containerURL(forSecurityApplicationGroupIdentifier: PVAppGroupId)?
.appendingPathComponent("Documents") else {
ELOG("Could not load appgroup \(PVAppGroupId)")
return []
}

let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let documentsURL = URL(fileURLWithPath: documentsPath)
let sharedContainer = fileManager.containerURL(forSecurityApplicationGroupIdentifier: PVAppGroupId)!
.appendingPathComponent("Documents")

return [
(documentsURL.appendingPathComponent("ROMs"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,9 @@ public final class RomDatabase {
// and RomDatabase would just exist to provide context instances and init the initial database - jm
public static var sharedInstance: RomDatabase {
// Make sure real shared is inited first
let shared = RomDatabase._sharedInstance!
guard let shared = RomDatabase._sharedInstance else {
return try! RomDatabase()
}

if Thread.isMainThread {
return shared
Expand Down
73 changes: 57 additions & 16 deletions PVUI/Sources/PVSwiftUI/Components/NeumorphismView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,71 @@

import SwiftUI

struct NeumorphismView: View {
struct NeumorphismView<Content: View>: View {
let content: () -> Content
let baseColor: Color

/// Initialize with content and optional base color
init(baseColor: Color = Color(red: 0.16290172934532166, green: 0.16290172934532166, blue: 0.16290172934532166),
@ViewBuilder content: @escaping () -> Content) {
self.content = content
self.baseColor = baseColor
}

var body: some View {
ZStack {
color.BaseColor
.edgesIgnoringSafeArea(.all)
RoundedRectangle(cornerRadius: 10)
.frame(width: 150, height: 150)
.modifier(NeumorphismModifier())
}
content()
.modifier(NeumorphismModifier(baseColor: baseColor))
}
}

struct NeumorphismModifier: ViewModifier {
let baseColor: Color

/// Calculate shadow colors based on base color
private var lightShadowColor: Color {
baseColor.adjustBrightness(by: -0.48) /// Darken by 48%
}

private var darkShadowColor: Color {
baseColor.adjustBrightness(by: 0.48) /// Lighten by 48%
}

private var foregroundGradient: LinearGradient {
LinearGradient(
gradient: Gradient(colors: [baseColor, baseColor]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
}

func body(content: Content) -> some View {
content
.foregroundStyle(color.ForegroundColor)
.shadow(color: color.LightShadowColor, radius: 2.9914936266447367, x: 2.7381441885964914, y: 2.7381441885964914)
.shadow(color: color.DarkShadowColor, radius: 2.9914936266447367, x: -2.7381441885964914, y: -2.7381441885964914)
.foregroundStyle(foregroundGradient)
.shadow(color: lightShadowColor, radius: 2.99, x: 2.74, y: 2.74)
.shadow(color: darkShadowColor, radius: 2.99, x: -2.74, y: -2.74)
}
}

class color {
static let BaseColor: Color = Color(red: 0.16290172934532166, green: 0.16290172934532166, blue: 0.16290172934532166)
static let LightShadowColor: Color = Color(red: 0.08447035402059555, green: 0.08447035402059555, blue: 0.08447035402059555)
static let DarkShadowColor: Color = Color(red: 0.24133309721946716, green: 0.24133309721946716, blue: 0.24133309721946716)
static let ForegroundColor: LinearGradient = LinearGradient(gradient: Gradient(colors: [Color(red: 0.16290172934532166, green: 0.16290172934532166, blue: 0.16290172934532166), Color(red: 0.16290172934532166, green: 0.16290172934532166, blue: 0.16290172934532166)]), startPoint: .topLeading, endPoint: .bottomTrailing)
/// Helper extension to adjust color brightness
extension Color {
/// Adjusts the brightness of a color by a given percentage
/// - Parameter percentage: Amount to adjust (-1.0 to 1.0, where negative darkens and positive lightens)
/// - Returns: Adjusted color
func adjustBrightness(by percentage: Double) -> Color {
let uiColor = UIColor(self)
var hue: CGFloat = 0
var saturation: CGFloat = 0
var brightness: CGFloat = 0
var alpha: CGFloat = 0

uiColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)

/// Adjust brightness while keeping it within 0...1
let newBrightness = max(0, min(1, brightness * (1 + percentage)))

return Color(hue: Double(hue),
saturation: Double(saturation),
brightness: Double(newBrightness),
opacity: Double(alpha))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ public class ContinuesMagementViewModel: ObservableObject {
systemTitle: String,
numberOfSaves: Int,
gameUIImage: UIImage? = nil,
onLoadSave: ((String) -> Void)?
onLoadSave: ((String) -> Void)? = nil
) {
self.driver = driver
self.gameUIImage = gameUIImage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public struct SaveStateRowView: View {
@State private var showingLoadAlert = false
@State private var editText: String = ""
@Binding var currentUserInteractionCellID: String?

/// Computed property for display title
private var displayTitle: String {
viewModel.description?.isEmpty == false ? viewModel.description! : viewModel.gameTitle
Expand All @@ -103,6 +103,7 @@ public struct SaveStateRowView: View {
.aspectRatio(contentMode: .fill)
.frame(width: 60, height: 60)
.clipShape(RoundedRectangle(cornerRadius: 9))
.padding(.leading, 9)
.onTapGesture {
showingLoadAlert = true
}
Expand Down
8 changes: 8 additions & 0 deletions PVUI/Sources/PVSwiftUI/PVSwiftUI.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

import SwiftUI
import UIKit
#if canImport(FreemiumKit)
import FreemiumKit
#endif
import PVThemes

struct PremiumThemedToggle<Label: View>: View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct ThemeSelectionView: View {

var body: some View {
List {

/// Standard theme options section
Section(header: Text("Standard Themes")) {
ForEach(ThemeOptionsStandard.allCases, id: \.self) { option in
Expand Down
31 changes: 31 additions & 0 deletions PVUI/Sources/PVSwiftUI/Settings/IconManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import SwiftUI

/// Observable object to track app icon changes
class IconManager: ObservableObject {
static let shared = IconManager()

@Published var currentIconName: String? {
didSet {
/// Update UI when icon changes
objectWillChange.send()
}
}

private init() {
/// Initialize with current icon name
currentIconName = UIApplication.shared.alternateIconName

/// Setup notification observer for icon changes
NotificationCenter.default.addObserver(
self,
selector: #selector(iconDidChange),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
}

@objc private func iconDidChange() {
/// Update current icon name when app becomes active
currentIconName = UIApplication.shared.alternateIconName
}
}
75 changes: 58 additions & 17 deletions PVUI/Sources/PVSwiftUI/Settings/SettingsSwiftUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import PVUIKit
import RxRealm
import RxSwift
import RealmSwift
import Perception

#if canImport(FreemiumKit)
import FreemiumKit
Expand Down Expand Up @@ -109,8 +110,14 @@ public struct PVSettingsView: View {
}
.onAppear {
viewModel.setupConflictsObserver()
Task { @MainActor in
await AppState.shared.libraryUpdatesController?.updateConflicts()
withPerceptionTracking {
Task { @MainActor in
await AppState.shared.libraryUpdatesController?.updateConflicts()
}
} onChange: {
Task { @MainActor in
viewModel.numberOfConflicts = AppState.shared.libraryUpdatesController?.conflicts.count ?? 0
}
}
}
}
Expand Down Expand Up @@ -155,6 +162,7 @@ struct SettingsRow: View {
private struct AppSection: View {
@ObservedObject var viewModel: PVSettingsViewModel
@ObservedObject private var themeManager = ThemeManager.shared
@ObservedObject private var iconManager = IconManager.shared

var body: some View {
Section(header: Text("App")) {
Expand Down Expand Up @@ -185,10 +193,38 @@ private struct AppSection: View {
icon: .sfSymbol("lock.fill"))
}

NavigationLink(destination: AppearanceView()) {
SettingsRow(title: "Appearance",
subtitle: "Visual options for Game Library",
icon: .sfSymbol("eye"))

/// App icon selection section
PaidFeatureView {
NavigationLink(destination: AppIconSelectorView()) {
HStack {
Image(systemName: "app")
.resizable()
.scaledToFit()
.frame(width: 22, height: 22)
.foregroundColor(.accentColor)
Text("Change App Icon")
Spacer()
IconImage(
iconName: iconManager.currentIconName ?? "AppIcon",
size: 24
)
}
}
} lockedView: {
HStack {
Image(systemName: "lock.fill")
.resizable()
.scaledToFit()
.frame(width: 22, height: 22)
.foregroundColor(.accentColor)
Text("Change App Icon")
Spacer()
IconImage(
iconName: iconManager.currentIconName ?? "AppIcon",
size: 24
)
}
}
}
}
Expand Down Expand Up @@ -399,13 +435,13 @@ private struct AudioSection: View {
PaidFeatureView {
NavigationLink(destination: AudioEngineSettingsView()) {
SettingsRow(title: "Audio Engine",
subtitle: "Configure audio engine, buffer and latency settings.",
icon: .sfSymbol("waveform.circle"))
subtitle: "Configure audio engine, buffer and latency settings.",
icon: .sfSymbol("waveform.circle"))
}
} lockedView: {
SettingsRow(title: "Audio Engine",
subtitle: "Unlock to configure advanced audio settings.",
icon: .sfSymbol("lock.fill"))
subtitle: "Unlock to configure advanced audio settings.",
icon: .sfSymbol("lock.fill"))
}
}
}
Expand Down Expand Up @@ -554,13 +590,18 @@ private struct LibrarySection: View {

var body: some View {
Section(header: Text("Library")) {
//#if canImport(PVWebServer)
// Button(action: viewModel.launchWebServer) {
// SettingsRow(title: "Launch Web Server",
// subtitle: "Transfer ROMs and saves over WiFi.",
// icon: .sfSymbol("xserve"))
// }
//#endif
//#if canImport(PVWebServer)
// Button(action: viewModel.launchWebServer) {
// SettingsRow(title: "Launch Web Server",
// subtitle: "Transfer ROMs and saves over WiFi.",
// icon: .sfSymbol("xserve"))
// }
//#endif
NavigationLink(destination: AppearanceView()) {
SettingsRow(title: "Appearance",
subtitle: "Visual options for Game Library",
icon: .sfSymbol("eye"))
}

NavigationLink(destination: ConflictsView().environmentObject(viewModel)) {
SettingsRow(title: "Manage Conflicts",
Expand Down
Loading

0 comments on commit 3f421f2

Please sign in to comment.