// // PauseViewController.swift // Delta // // Created by Riley Testut on 1/30/16. // Copyright © 2016 Riley Testut. All rights reserved. // import UIKit import DeltaCore class PauseViewController: UIViewController, PauseInfoProviding { var emulatorCore: EmulatorCore? { didSet { self.updatePauseItems() } } var pauseItems: [MenuItem] { return [self.saveStateItem, self.loadStateItem, self.cheatCodesItem, self.fastForwardItem, self.sustainButtonsItem].compactMap { $0 } } /// Pause Items var saveStateItem: MenuItem? var loadStateItem: MenuItem? var cheatCodesItem: MenuItem? var fastForwardItem: MenuItem? var sustainButtonsItem: MenuItem? /// PauseInfoProviding var pauseText: String? /// Cheats weak var cheatsViewControllerDelegate: CheatsViewControllerDelegate? /// Save States weak var saveStatesViewControllerDelegate: SaveStatesViewControllerDelegate? private var saveStatesViewControllerMode = SaveStatesViewController.Mode.loading private var pauseNavigationController: UINavigationController! /// UIViewController override var preferredContentSize: CGSize { set { } get { var preferredContentSize = self.pauseNavigationController.topViewController?.preferredContentSize ?? CGSize.zero if preferredContentSize.height > 0 { preferredContentSize.height += self.pauseNavigationController.navigationBar.bounds.height } return preferredContentSize } } override var navigationController: UINavigationController? { return self.pauseNavigationController } override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() self.updateSafeAreaInsets() } } extension PauseViewController { override func targetViewController(forAction action: Selector, sender: Any?) -> UIViewController? { return self.pauseNavigationController } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { guard let identifier = segue.identifier else { return } switch identifier { case "embedNavigationController": self.pauseNavigationController = segue.destination as? UINavigationController self.pauseNavigationController.delegate = self self.pauseNavigationController.navigationBar.tintColor = UIColor.deltaPurple self.pauseNavigationController.view.backgroundColor = UIColor.clear let gridMenuViewController = self.pauseNavigationController.topViewController as! GridMenuViewController if #available(iOS 13.0, *) { let navigationBarAppearance = self.pauseNavigationController.navigationBar.standardAppearance.copy() navigationBarAppearance.backgroundEffect = UIBlurEffect(style: .dark) navigationBarAppearance.backgroundColor = UIColor.black.withAlphaComponent(0.2) navigationBarAppearance.shadowColor = UIColor.white.withAlphaComponent(0.2) navigationBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.white] self.pauseNavigationController.navigationBar.standardAppearance = navigationBarAppearance let transparentBarAppearance = navigationBarAppearance.copy() transparentBarAppearance.backgroundColor = nil transparentBarAppearance.backgroundEffect = nil gridMenuViewController.navigationItem.standardAppearance = transparentBarAppearance } gridMenuViewController.items = self.pauseItems case "saveStates": let saveStatesViewController = segue.destination as! SaveStatesViewController saveStatesViewController.delegate = self.saveStatesViewControllerDelegate saveStatesViewController.game = self.emulatorCore?.game as? Game saveStatesViewController.emulatorCore = self.emulatorCore saveStatesViewController.mode = self.saveStatesViewControllerMode case "cheats": let cheatsViewController = segue.destination as! CheatsViewController cheatsViewController.delegate = self.cheatsViewControllerDelegate cheatsViewController.game = self.emulatorCore?.game as? Game default: break } } } extension PauseViewController { func dismiss() { self.performSegue(withIdentifier: "unwindFromPauseMenu", sender: self) } } extension PauseViewController: UINavigationControllerDelegate { func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { let transitionCoordinator = PauseTransitionCoordinator(presentationController: self.presentationController!) transitionCoordinator.presenting = (operation == .push) return transitionCoordinator } func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { self.updateSafeAreaInsets() } } private extension PauseViewController { func updatePauseItems() { self.saveStateItem = nil self.loadStateItem = nil self.cheatCodesItem = nil self.sustainButtonsItem = nil self.fastForwardItem = nil guard let emulatorCore = self.emulatorCore else { return } self.saveStateItem = MenuItem(text: NSLocalizedString("Save State", comment: ""), image: #imageLiteral(resourceName: "SaveSaveState"), action: { [unowned self] _ in self.saveStatesViewControllerMode = .saving self.performSegue(withIdentifier: "saveStates", sender: self) }) self.loadStateItem = MenuItem(text: NSLocalizedString("Load State", comment: ""), image: #imageLiteral(resourceName: "LoadSaveState"), action: { [unowned self] _ in self.saveStatesViewControllerMode = .loading self.performSegue(withIdentifier: "saveStates", sender: self) }) self.cheatCodesItem = MenuItem(text: NSLocalizedString("Cheat Codes", comment: ""), image: #imageLiteral(resourceName: "CheatCodes"), action: { [unowned self] _ in self.performSegue(withIdentifier: "cheats", sender: self) }) self.fastForwardItem = MenuItem(text: NSLocalizedString("Fast Forward", comment: ""), image: #imageLiteral(resourceName: "FastForward"), action: { _ in }) self.sustainButtonsItem = MenuItem(text: NSLocalizedString("Hold Buttons", comment: ""), image: #imageLiteral(resourceName: "SustainButtons"), action: { _ in }) if ExperimentalFeatures.shared.variableFastForward.isEnabled { let menu = self.makeFastForwardMenu(for: emulatorCore.game) self.fastForwardItem?.menu = menu } } func updateSafeAreaInsets() { if self.navigationController?.topViewController == self.navigationController?.viewControllers.first { self.additionalSafeAreaInsets.left = self.view.window?.safeAreaInsets.left ?? 0 self.additionalSafeAreaInsets.right = self.view.window?.safeAreaInsets.right ?? 0 } else { self.additionalSafeAreaInsets.left = 0 self.additionalSafeAreaInsets.right = 0 } } func makeFastForwardMenu(for game: GameProtocol) -> UIMenu? { guard let deltaCore = Delta.core(for: game.type), #available(iOS 15, *) else { return nil } let menu = UIMenu(title: NSLocalizedString("Change the Fast Forward speed for this system.", comment: ""), options: [.singleSelection], children: [ UIDeferredMenuElement.uncached { [weak self] completion in let preferredSpeed = ExperimentalFeatures.shared.variableFastForward[game.type] let supportedSpeeds = FastForwardSpeed.speeds(in: deltaCore.supportedRates) var actions = zip(0..., supportedSpeeds).map { (index, speed) in let state: UIAction.State = (speed == preferredSpeed) ? .on : .off let action = UIAction(title: speed.description, state: state) { action in ExperimentalFeatures.shared.variableFastForward[game.type] = speed if let fastForwardItem = self?.fastForwardItem { fastForwardItem.isSelected = true // Always enable FF after selecting speed. fastForwardItem.action(fastForwardItem) } } if #available(iOS 16, *) { let configuration = UIImage.SymbolConfiguration(hierarchicalColor: .deltaPurple) let percentage = Double(index + 1) / Double(supportedSpeeds.count) action.image = UIImage(systemName: "timelapse", variableValue: percentage, configuration: configuration) } return action } let state: UIAction.State = (preferredSpeed == nil) ? .on : .off let action = UIAction(title: NSLocalizedString("Maximum", comment: ""), state: state) { action in ExperimentalFeatures.shared.variableFastForward[game.type] = nil if let fastForwardItem = self?.fastForwardItem { fastForwardItem.isSelected = true // Always enable FF after selecting speed. fastForwardItem.action(fastForwardItem) } } actions.append(action) completion(actions) } ]) return menu } }