diff --git a/Cores/DeltaCore b/Cores/DeltaCore index b2af08a..35d634f 160000 --- a/Cores/DeltaCore +++ b/Cores/DeltaCore @@ -1 +1 @@ -Subproject commit b2af08a2db9e371b4fcfd8aa52b5106407af7c09 +Subproject commit 35d634fdeec4f89da7dfecacb90188d688caa177 diff --git a/Delta/Emulation/GameViewController.swift b/Delta/Emulation/GameViewController.swift index 1a8b888..520dd69 100644 --- a/Delta/Emulation/GameViewController.swift +++ b/Delta/Emulation/GameViewController.swift @@ -46,6 +46,8 @@ class GameViewController: DeltaCore.GameViewController let game = self.game as? Game NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.managedObjectContextDidChange(with:)), name: .NSManagedObjectContextObjectsDidChange, object: game?.managedObjectContext) + + self.updateControllerSkin() } } @@ -110,6 +112,7 @@ class GameViewController: DeltaCore.GameViewController NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidConnect, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidDisconnect, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.didEnterBackground(with:)), name: .UIApplicationDidEnterBackground, object: UIApplication.shared) + NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.preferredControllerSkinDidUpdate(with:)), name: .preferredControllerSkinDidUpdate, object: nil) } deinit @@ -186,9 +189,26 @@ extension GameViewController self.sustainButtonsContentView.topAnchor.constraint(equalTo: self.gameView.topAnchor).isActive = true self.sustainButtonsContentView.bottomAnchor.constraint(equalTo: self.gameView.bottomAnchor).isActive = true + self.updateControllerSkin() self.updateControllers() } + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) + { + super.viewWillTransition(to: size, with: coordinator) + + guard let game = self.game else { return } + + var traits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view) + traits.orientation = (size.width > size.height) ? .landscape : .portrait + + let controllerSkin = Settings.preferredControllerSkin(for: game.type, traits: traits) + + coordinator.animate(alongsideTransition: { (context) in + self.controllerView.controllerSkin = controllerSkin + }, completion: nil) + } + // MARK: - Segues /// KVO @@ -360,6 +380,16 @@ private extension GameViewController self.view.setNeedsLayout() } + + func updateControllerSkin() + { + guard let game = self.game else { return } + + let traits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view) + + let controllerSkin = Settings.preferredControllerSkin(for: game.type, traits: traits) + self.controllerView.controllerSkin = controllerSkin + } } //MARK: - Save States - @@ -731,4 +761,18 @@ private extension GameViewController self.game = nil } } + + @objc func preferredControllerSkinDidUpdate(with notification: Notification) + { + guard + let gameType = notification.userInfo?[Settings.NotificationUserInfoKey.gameType.rawValue] as? GameType, + let traits = notification.userInfo?[Settings.NotificationUserInfoKey.traits.rawValue] as? DeltaCore.ControllerSkin.Traits + else { return } + + let currentTraits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view) + if gameType == self.game?.type && traits == currentTraits + { + self.updateControllerSkin() + } + } } diff --git a/Delta/Settings/Controller Skins/ControllerSkinsViewController.swift b/Delta/Settings/Controller Skins/ControllerSkinsViewController.swift index 75a0ab6..0817199 100644 --- a/Delta/Settings/Controller Skins/ControllerSkinsViewController.swift +++ b/Delta/Settings/Controller Skins/ControllerSkinsViewController.swift @@ -178,6 +178,14 @@ extension ControllerSkinsViewController: UITableViewDataSourcePrefetching extension ControllerSkinsViewController { + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) + { + let controllerSkin = self.dataSource.fetchedResultsController.object(at: indexPath) + Settings.setPreferredControllerSkin(controllerSkin, for: self.gameType, traits: self.traits) + + _ = self.navigationController?.popViewController(animated: true) + } + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let controllerSkin = self.dataSource.fetchedResultsController.object(at: indexPath) diff --git a/Delta/Settings/Controller Skins/GameTypeControllerSkinsViewController.swift b/Delta/Settings/Controller Skins/GameTypeControllerSkinsViewController.swift index 591055d..f162cc7 100644 --- a/Delta/Settings/Controller Skins/GameTypeControllerSkinsViewController.swift +++ b/Delta/Settings/Controller Skins/GameTypeControllerSkinsViewController.swift @@ -102,12 +102,13 @@ private extension GameTypeControllerSkinsViewController { func updateControllerSkins() { - let controllerSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: self.gameType) - let portraitTraits = DeltaCore.ControllerSkin.Traits(deviceType: .iphone, displayMode: DeltaCore.ControllerSkin.DisplayMode.fullScreen, orientation: .portrait) - self.portraitImageView.image = controllerSkin?.image(for: portraitTraits, preferredSize: UIScreen.main.defaultControllerSkinSize) - let landscapeTraits = DeltaCore.ControllerSkin.Traits(deviceType: .iphone, displayMode: DeltaCore.ControllerSkin.DisplayMode.fullScreen, orientation: .landscape) - self.landscapeImageView.image = controllerSkin?.image(for: landscapeTraits, preferredSize: UIScreen.main.defaultControllerSkinSize) + + let portraitControllerSkin = Settings.preferredControllerSkin(for: self.gameType, traits: portraitTraits) + let landscapeControllerSkin = Settings.preferredControllerSkin(for: self.gameType, traits: landscapeTraits) + + self.portraitImageView.image = portraitControllerSkin?.image(for: portraitTraits, preferredSize: UIScreen.main.defaultControllerSkinSize) + self.landscapeImageView.image = landscapeControllerSkin?.image(for: landscapeTraits, preferredSize: UIScreen.main.defaultControllerSkinSize) } } diff --git a/Delta/Settings/Settings.swift b/Delta/Settings/Settings.swift index 6becfa9..5507e1e 100644 --- a/Delta/Settings/Settings.swift +++ b/Delta/Settings/Settings.swift @@ -8,9 +8,110 @@ import Foundation +import DeltaCore +import SNESDeltaCore +import GBADeltaCore + +import Roxas + +extension Notification.Name +{ + static let preferredControllerSkinDidUpdate = Notification.Name("PreferredControllerSkinDidUpdateNotification") +} + +extension Settings +{ + enum NotificationUserInfoKey: String + { + case gameType + case traits + } +} + struct Settings { /// Controllers static var localControllerPlayerIndex: Int? = 0 -} \ No newline at end of file + static func preferredControllerSkin(for gameType: GameType, traits: DeltaCore.ControllerSkin.Traits) -> ControllerSkin? + { + guard let userDefaultsKey = self.preferredControllerSkinKey(for: gameType, traits: traits) else { return nil } + + let identifier = UserDefaults.standard.string(forKey: userDefaultsKey) + + do + { + // Attempt to load preferred controller skin if it exists + + let fetchRequest: NSFetchRequest = ControllerSkin.fetchRequest() + + if let identifier = identifier + { + fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == %@", #keyPath(ControllerSkin.gameType), gameType.rawValue, #keyPath(ControllerSkin.identifier), identifier) + + if let controllerSkin = try DatabaseManager.shared.viewContext.fetch(fetchRequest).first + { + return controllerSkin + } + } + + // Controller skin doesn't exist, so fall back to standard controller skin + + fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == YES", #keyPath(ControllerSkin.gameType), gameType.rawValue, #keyPath(ControllerSkin.isStandard)) + + if let controllerSkin = try DatabaseManager.shared.viewContext.fetch(fetchRequest).first + { + Settings.setPreferredControllerSkin(controllerSkin, for: gameType, traits: traits) + return controllerSkin + } + } + catch + { + print(error) + } + + return nil + } + + static func setPreferredControllerSkin(_ controllerSkin: ControllerSkin, for gameType: GameType, traits: DeltaCore.ControllerSkin.Traits) + { + guard let userDefaultKey = self.preferredControllerSkinKey(for: gameType, traits: traits) else { return } + UserDefaults.standard.set(controllerSkin.identifier, forKey: userDefaultKey) + + NotificationCenter.default.post(name: .preferredControllerSkinDidUpdate, object: controllerSkin, userInfo: [NotificationUserInfoKey.gameType.rawValue: gameType, NotificationUserInfoKey.traits.rawValue: traits]) + } +} + +private extension Settings +{ + static func preferredControllerSkinKey(for gameType: GameType, traits: DeltaCore.ControllerSkin.Traits) -> String? + { + let systemName: String + + switch gameType + { + case GameType.snes: systemName = "snes" + case GameType.gba: systemName = "gba" + default: return nil + } + + let orientation: String + + switch traits.orientation + { + case .portrait: orientation = "portrait" + case .landscape: orientation = "landscape" + } + + let displayMode: String + + switch traits.displayMode + { + case .fullScreen: displayMode = "fullscreen" + case .splitView: displayMode = "splitview" + } + + let key = systemName + "-" + orientation + "-" + displayMode + "-controller" + return key + } +}