diff --git a/Delta.xcodeproj/project.pbxproj b/Delta.xcodeproj/project.pbxproj index e4031ff..3eb413f 100644 --- a/Delta.xcodeproj/project.pbxproj +++ b/Delta.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */; }; BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF5E7F451B9A652600AE44F8 /* Settings.storyboard */; }; BF65E8631CEE5C6A00CD3247 /* Cheat.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF65E8621CEE5C6A00CD3247 /* Cheat.swift */; }; + BF696B801D9B2B02009639E0 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF696B7F1D9B2B02009639E0 /* Theme.swift */; }; BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; }; BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BF762EAB1BC1B076002C8866 /* NSManagedObject+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF762EAA1BC1B076002C8866 /* NSManagedObject+Conveniences.swift */; }; @@ -124,6 +125,7 @@ BF5E7F451B9A652600AE44F8 /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; BF63BDE91D389EEB00FCB040 /* GameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; BF65E8621CEE5C6A00CD3247 /* Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cheat.swift; sourceTree = ""; }; + BF696B7F1D9B2B02009639E0 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Theme.swift; path = Theming/Theme.swift; sourceTree = ""; }; BF6BB2451BB73FE800CCF94A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; BF70798B1B6B464B0019077C /* ZipZap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ZipZap.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BF762EAA1BC1B076002C8866 /* NSManagedObject+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+Conveniences.swift"; sourceTree = ""; }; @@ -257,6 +259,14 @@ path = "Game Selection"; sourceTree = ""; }; + BF696B7E1D9B2AE6009639E0 /* Theming */ = { + isa = PBXGroup; + children = ( + BF696B7F1D9B2B02009639E0 /* Theme.swift */, + ); + name = Theming; + sourceTree = ""; + }; BF762EA91BC1B044002C8866 /* Extensions */ = { isa = PBXGroup; children = ( @@ -394,6 +404,7 @@ BFFB709D1AF99ACA00DE56FE /* Emulation */, BF7AE7FA1C2E851F00B1B5BC /* Pause Menu */, BFAA1FEB1B8AA4E800495943 /* Settings */, + BF696B7E1D9B2AE6009639E0 /* Theming */, BF090CEE1B490C1A00DCAB45 /* Extensions */, BFFA71DA1AAC406100EE9DD1 /* Supporting Files */, ); @@ -614,6 +625,7 @@ BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */, BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */, BFDB28451BC9DA7B001D0C83 /* GamePickerController.swift in Sources */, + BF696B801D9B2B02009639E0 /* Theme.swift in Sources */, BF7AE8081C2E858400B1B5BC /* PauseMenuViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Delta/Game Selection/GameCollectionViewController.swift b/Delta/Game Selection/GameCollectionViewController.swift index 935892a..5be616b 100644 --- a/Delta/Game Selection/GameCollectionViewController.swift +++ b/Delta/Game Selection/GameCollectionViewController.swift @@ -21,7 +21,7 @@ class GameCollectionViewController: UICollectionViewController } } - var theme: GamesViewController.Theme = .light { + var theme: Theme = .light { didSet { self.collectionView?.reloadData() } @@ -197,11 +197,15 @@ private extension GameCollectionViewController { let cancelAction = Action(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, action: nil) + let saveStatesAction = Action(title: NSLocalizedString("Save States", comment: ""), style: .default, action: { [unowned self] action in + self.viewSaveStates(for: game) + }) + let deleteAction = Action(title: NSLocalizedString("Delete", comment: ""), style: .destructive, action: { [unowned self] action in self.delete(game) }) - return [cancelAction, deleteAction] + return [cancelAction, saveStatesAction, deleteAction] } func delete(_ game: Game) @@ -222,6 +226,21 @@ private extension GameCollectionViewController self.present(confirmationAlertController, animated: true, completion: nil) } + func viewSaveStates(for game: Game) + { + let storyboard = UIStoryboard(name: "PauseMenu", bundle: nil) + + let saveStatesViewController = storyboard.instantiateViewController(withIdentifier: "saveStatesViewController") as! SaveStatesViewController + saveStatesViewController.delegate = self + saveStatesViewController.game = game + saveStatesViewController.mode = .loading + saveStatesViewController.theme = .light + saveStatesViewController.showsDoneButton = true + + let navigationController = UINavigationController(rootViewController: saveStatesViewController) + self.present(navigationController, animated: true, completion: nil) + } + @objc func handleLongPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) { guard gestureRecognizer.state == .began else { return } @@ -295,6 +314,27 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate } } +//MARK: - SaveStatesViewControllerDelegate - +/// SaveStatesViewControllerDelegate +extension GameCollectionViewController: SaveStatesViewControllerDelegate +{ + func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState) + { + } + + func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveStateProtocol) + { + self.activeSaveState = saveState + + self.dismiss(animated: true) { + let indexPath = self.dataSource.fetchedResultsController.indexPath(forObject: saveStatesViewController.game)! + let cell = self.collectionView?.cellForItem(at: indexPath) + + self.launchGame(withSender: cell, clearScreen: false) + } + } +} + //MARK: - UICollectionViewDelegate - /// UICollectionViewDelegate extension GameCollectionViewController diff --git a/Delta/Game Selection/GamesViewController.swift b/Delta/Game Selection/GamesViewController.swift index 3ca7f03..ba84659 100644 --- a/Delta/Game Selection/GamesViewController.swift +++ b/Delta/Game Selection/GamesViewController.swift @@ -13,15 +13,6 @@ import DeltaCore import Roxas -extension GamesViewController -{ - enum Theme - { - case light - case dark - } -} - class GamesViewController: UIViewController { var theme: Theme = .light { diff --git a/Delta/Pause Menu/Save States/SaveStatesCollectionHeaderView.swift b/Delta/Pause Menu/Save States/SaveStatesCollectionHeaderView.swift index 45ecaa5..554c945 100644 --- a/Delta/Pause Menu/Save States/SaveStatesCollectionHeaderView.swift +++ b/Delta/Pause Menu/Save States/SaveStatesCollectionHeaderView.swift @@ -12,6 +12,21 @@ class SaveStatesCollectionHeaderView: UICollectionReusableView { let textLabel = UILabel() + fileprivate let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark))) + + var isTextLabelVibrancyEnabled = true { + didSet { + if self.isTextLabelVibrancyEnabled + { + self.vibrancyView.contentView.addSubview(self.textLabel) + } + else + { + self.addSubview(self.textLabel) + } + } + } + override init(frame: CGRect) { super.init(frame: frame) @@ -28,10 +43,9 @@ class SaveStatesCollectionHeaderView: UICollectionReusableView private func initialize() { - let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark))) - vibrancyView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - vibrancyView.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height) - self.addSubview(vibrancyView) + self.vibrancyView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + self.vibrancyView.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height) + self.addSubview(self.vibrancyView) self.textLabel.translatesAutoresizingMaskIntoConstraints = false self.textLabel.textColor = UIColor.white @@ -41,10 +55,12 @@ class SaveStatesCollectionHeaderView: UICollectionReusableView self.textLabel.font = UIFont(descriptor: fontDescriptor, size: 0.0) self.textLabel.textAlignment = .center - vibrancyView.contentView.addSubview(self.textLabel) + self.vibrancyView.contentView.addSubview(self.textLabel) // Auto Layout - NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[textLabel]-20-|", options: [], metrics: nil, views: ["textLabel": self.textLabel])) - NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-10-[textLabel]|", options: [], metrics: nil, views: ["textLabel": self.textLabel])) + self.textLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true + self.textLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20).isActive = true + self.textLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true + self.textLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true } } diff --git a/Delta/Pause Menu/Save States/SaveStatesViewController.swift b/Delta/Pause Menu/Save States/SaveStatesViewController.swift index 37b34ce..e34ea4c 100644 --- a/Delta/Pause Menu/Save States/SaveStatesViewController.swift +++ b/Delta/Pause Menu/Save States/SaveStatesViewController.swift @@ -49,6 +49,18 @@ class SaveStatesViewController: UICollectionViewController var mode = Mode.loading + var theme = Theme.dark { + didSet { + if self.isViewLoaded + { + self.updateTheme() + } + } + } + + var showsDoneButton = false + + fileprivate var vibrancyView: UIVisualEffectView! fileprivate var backgroundView: RSTBackgroundView! fileprivate var prototypeCell = GridCollectionViewCell() @@ -81,10 +93,10 @@ extension SaveStatesViewController { super.viewDidLoad() - let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark))) - vibrancyView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - vibrancyView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height) - self.view.insertSubview(vibrancyView, at: 0) + self.vibrancyView = UIVisualEffectView(effect: nil) + self.vibrancyView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + self.vibrancyView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height) + self.view.insertSubview(self.vibrancyView, at: 0) self.backgroundView = RSTBackgroundView(frame: CGRect(x: 0, y: 0, width: vibrancyView.bounds.width, height: vibrancyView.bounds.height)) self.backgroundView.isHidden = true @@ -92,7 +104,7 @@ extension SaveStatesViewController self.backgroundView.textLabel.text = NSLocalizedString("No Save States", comment: "") self.backgroundView.textLabel.textColor = UIColor.white self.backgroundView.detailTextLabel.textColor = UIColor.white - vibrancyView.contentView.addSubview(self.backgroundView) + self.vibrancyView.contentView.addSubview(self.backgroundView) let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout let averageHorizontalInset = (collectionViewLayout.sectionInset.left + collectionViewLayout.sectionInset.right) / 2 @@ -114,6 +126,12 @@ extension SaveStatesViewController self.navigationItem.rightBarButtonItem = nil } + if self.showsDoneButton + { + let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(SaveStatesViewController.handleDoneButton)) + self.navigationItem.rightBarButtonItem = doneButton + } + // Manually update prototype cell properties self.prototypeCellWidthConstraint = self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: collectionViewLayout.itemWidth) self.prototypeCellWidthConstraint.isActive = true @@ -126,6 +144,7 @@ extension SaveStatesViewController self.registerForPreviewing(with: self, sourceView: self.collectionView!) self.updateBackgroundView() + self.updateTheme() } override func viewWillAppear(_ animated: Bool) @@ -177,6 +196,41 @@ private extension SaveStatesViewController } } + func updateTheme() + { + switch self.theme + { + case .light: + self.view.backgroundColor = UIColor.white + + self.navigationController?.navigationBar.barStyle = .default + self.navigationController?.toolbar.barStyle = .default + + self.vibrancyView.effect = nil + + self.backgroundView.textLabel.textColor = UIColor.gray + self.backgroundView.detailTextLabel.textColor = UIColor.gray + + case .dark: + self.view.backgroundColor = nil + + self.navigationController?.navigationBar.barStyle = .blackTranslucent + self.navigationController?.toolbar.barStyle = .blackTranslucent + + self.vibrancyView.effect = UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)) + + self.backgroundView.textLabel.textColor = UIColor.white + self.backgroundView.detailTextLabel.textColor = UIColor.white + } + } + + //MARK: - Navigation - + + @objc func handleDoneButton() + { + self.presentingViewController?.dismiss(animated: true, completion: nil) + } + //MARK: - Configure Views - func configureCollectionViewCell(_ cell: GridCollectionViewCell, forIndexPath indexPath: IndexPath, ignoreExpensiveOperations ignoreOperations: Bool = false) @@ -186,8 +240,20 @@ private extension SaveStatesViewController cell.imageView.backgroundColor = UIColor.white cell.imageView.image = UIImage(named: "DeltaPlaceholder") - cell.isTextLabelVibrancyEnabled = true - cell.isImageViewVibrancyEnabled = true + switch self.theme + { + case .light: + cell.isTextLabelVibrancyEnabled = false + cell.isImageViewVibrancyEnabled = false + + cell.textLabel.textColor = UIColor.gray + + case .dark: + cell.isTextLabelVibrancyEnabled = true + cell.isImageViewVibrancyEnabled = true + + cell.textLabel.textColor = UIColor.white + } if !ignoreOperations { @@ -218,7 +284,6 @@ private extension SaveStatesViewController let dimensions = deltaCore.emulatorConfiguration.videoBufferInfo.outputDimensions cell.maximumImageSize = CGSize(width: self.prototypeCellWidthConstraint.constant, height: (self.prototypeCellWidthConstraint.constant / dimensions.width) * dimensions.height) - cell.textLabel.textColor = UIColor.white cell.textLabel.font = UIFont.preferredFont(forTextStyle: .subheadline) let name = saveState.name ?? self.dateFormatter.string(from: saveState.modifiedDate) @@ -239,6 +304,17 @@ private extension SaveStatesViewController } headerView.textLabel.text = title + + switch self.theme + { + case .light: + headerView.textLabel.textColor = UIColor.gray + headerView.isTextLabelVibrancyEnabled = false + + case .dark: + headerView.textLabel.textColor = UIColor.white + headerView.isTextLabelVibrancyEnabled = true + } } //MARK: - Gestures - @@ -549,10 +625,10 @@ extension SaveStatesViewController: UIViewControllerPreviewingDelegate { guard let indexPath = self.collectionView?.indexPathForItem(at: location), - let layoutAttributes = self.collectionViewLayout.layoutAttributesForItem(at: indexPath), - self.emulatorCoreSaveState != nil + let layoutAttributes = self.collectionViewLayout.layoutAttributesForItem(at: indexPath) else { return nil } + guard self.emulatorCore == nil || (self.emulatorCore != nil && self.emulatorCoreSaveState != nil) else { return nil } previewingContext.sourceRect = layoutAttributes.frame diff --git a/Delta/Theming/Theme.swift b/Delta/Theming/Theme.swift new file mode 100644 index 0000000..8dae4c6 --- /dev/null +++ b/Delta/Theming/Theme.swift @@ -0,0 +1,15 @@ +// +// Theme.swift +// Delta +// +// Created by Riley Testut on 9/27/16. +// Copyright © 2016 Riley Testut. All rights reserved. +// + +import Foundation + +enum Theme +{ + case light + case dark +}