Adds Nintendo DS AirPlay settings to customize how games appear on TV

* Top Screen Only = Shows just the top screen on TV
* Layout Horizontally = Places screens side-by-side on TV (instead of stacked vertically)
This commit is contained in:
Riley Testut 2023-04-26 16:07:17 -05:00
parent 32e7c1f93e
commit 1137189b57
9 changed files with 256 additions and 28 deletions

@ -1 +1 @@
Subproject commit 4b5084c75149dc0fc7b4abb4e6ab4f41df025ecd
Subproject commit 3ce43f3103c637dfdb27f85fc0d0041c8a37292b

View File

@ -196,6 +196,8 @@
D5A9C00329DDED6D00A8D610 /* ExperimentalFeaturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9C00229DDED6D00A8D610 /* ExperimentalFeaturesView.swift */; };
D5A9C01D29DE058C00A8D610 /* VariableFastForward.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9C01C29DE058C00A8D610 /* VariableFastForward.swift */; };
D5AAF27729884F8600F21ACF /* CheatDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AAF27629884F8600F21ACF /* CheatDevice.swift */; };
D5ADD12229F33FBF00CE0560 /* Features.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADD12129F33FBF00CE0560 /* Features.swift */; };
D5D78AE529F9BC3700E064F0 /* DSAirPlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D78AE429F9BC3700E064F0 /* DSAirPlay.swift */; };
D5D797E6298D946200738869 /* Contributor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D797E5298D946200738869 /* Contributor.swift */; };
D5D797E9298DCC7300738869 /* cheatbase.zip in Resources */ = {isa = PBXBuildFile; fileRef = D5D797E7298DC9E200738869 /* cheatbase.zip */; };
D5D7C1F929E60EA500663793 /* UserDefaults+OptionValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58F39C829E0A702008B4100 /* UserDefaults+OptionValues.swift */; };
@ -453,6 +455,8 @@
D5A9C01929DDFBDD00A8D610 /* SettingsName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsName.swift; sourceTree = "<group>"; };
D5A9C01C29DE058C00A8D610 /* VariableFastForward.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariableFastForward.swift; sourceTree = "<group>"; };
D5AAF27629884F8600F21ACF /* CheatDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatDevice.swift; sourceTree = "<group>"; };
D5ADD12129F33FBF00CE0560 /* Features.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Features.swift; sourceTree = "<group>"; };
D5D78AE429F9BC3700E064F0 /* DSAirPlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DSAirPlay.swift; sourceTree = "<group>"; };
D5D797E5298D946200738869 /* Contributor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contributor.swift; sourceTree = "<group>"; };
D5D797E7298DC9E200738869 /* cheatbase.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = cheatbase.zip; sourceTree = "<group>"; };
D5D7C1E629E5F90200663793 /* OptionValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionValue.swift; sourceTree = "<group>"; };
@ -845,6 +849,7 @@
BF11734E1DA32CEC00047DF8 /* Controllers */,
BF1DAD5B1D9F574900E752A7 /* Controller Skins */,
BF48F74C219A16C100BC2FC1 /* Syncing */,
D5D78AE329F9BC0200E064F0 /* Features */,
D5A9BFFF29DDECF500A8D610 /* Experimental Features */,
D5D797E4298D944C00738869 /* Contributors */,
);
@ -1092,6 +1097,15 @@
path = "Experimental Features";
sourceTree = "<group>";
};
D5D78AE329F9BC0200E064F0 /* Features */ = {
isa = PBXGroup;
children = (
D5ADD12129F33FBF00CE0560 /* Features.swift */,
D5D78AE429F9BC3700E064F0 /* DSAirPlay.swift */,
);
path = Features;
sourceTree = "<group>";
};
D5D797E4298D944C00738869 /* Contributors */ = {
isa = PBXGroup;
children = (
@ -1519,11 +1533,13 @@
D560BD8629EDC45600289847 /* ExternalDisplaySceneDelegate.swift in Sources */,
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */,
BF15AF841F54B43B009B6AAB /* ActionInput.swift in Sources */,
D5D78AE529F9BC3700E064F0 /* DSAirPlay.swift in Sources */,
BF4828841F9027B600028B97 /* Delta.xcdatamodeld in Sources */,
BF5942951E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift in Sources */,
BFC3628021ADE2BA00EF2BE6 /* UIAlertController+Error.swift in Sources */,
BF353FF91C5D870B00C1184C /* MenuItem.swift in Sources */,
D5D797E6298D946200738869 /* Contributor.swift in Sources */,
D5ADD12229F33FBF00CE0560 /* Features.swift in Sources */,
BFDB3418219E4B1700595A62 /* SyncStatusViewController.swift in Sources */,
BF18B61F1E2985F900F70067 /* UIAlertController+Importing.swift in Sources */,
D586497229774ABD0081477E /* CheatBase.swift in Sources */,

View File

@ -1956,10 +1956,80 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="AirPlay" id="Bci-L3-OeH">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="SwitchCell" id="Jq5-oz-Tdi" customClass="SwitchTableViewCell">
<rect key="frame" x="0.0" y="291.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Jq5-oz-Tdi" id="rg7-TQ-4w8">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Show Top Screen Only" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="leX-Hk-FtK">
<rect key="frame" x="16" y="11.5" width="286" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="erK-Wq-1Lx">
<rect key="frame" x="310" y="6.5" width="51" height="31"/>
<color key="onTintColor" name="Purple"/>
<connections>
<action selector="toggleTopScreenOnly:" destination="OwL-4c-EEA" eventType="valueChanged" id="Muw-Xa-Wy1"/>
</connections>
</switch>
</subviews>
<constraints>
<constraint firstItem="erK-Wq-1Lx" firstAttribute="leading" secondItem="leX-Hk-FtK" secondAttribute="trailing" constant="8" symbolic="YES" id="3bQ-fs-dhn"/>
<constraint firstItem="leX-Hk-FtK" firstAttribute="centerY" secondItem="rg7-TQ-4w8" secondAttribute="centerY" id="DdR-vJ-3hz"/>
<constraint firstAttribute="trailingMargin" secondItem="erK-Wq-1Lx" secondAttribute="trailing" id="Yn5-qu-rZ7"/>
<constraint firstItem="leX-Hk-FtK" firstAttribute="leading" secondItem="rg7-TQ-4w8" secondAttribute="leadingMargin" id="gaA-Aj-Y45"/>
<constraint firstItem="erK-Wq-1Lx" firstAttribute="centerY" secondItem="rg7-TQ-4w8" secondAttribute="centerY" id="tsz-wz-mVz"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="switchView" destination="erK-Wq-1Lx" id="55W-CO-LbT"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="SwitchCell" id="Wfb-f8-j84" customClass="SwitchTableViewCell">
<rect key="frame" x="0.0" y="335.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Wfb-f8-j84" id="bVY-An-mBJ">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Arrange Screens Horizontally" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="agM-Sz-8Jc">
<rect key="frame" x="16" y="11.5" width="286" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="F3O-5s-93H">
<rect key="frame" x="310" y="6.5" width="51" height="31"/>
<color key="onTintColor" name="Purple"/>
<connections>
<action selector="toggleLayoutHorizontally:" destination="OwL-4c-EEA" eventType="valueChanged" id="Wnh-tj-Z01"/>
</connections>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="F3O-5s-93H" secondAttribute="trailing" id="4A1-e1-ByX"/>
<constraint firstItem="F3O-5s-93H" firstAttribute="centerY" secondItem="bVY-An-mBJ" secondAttribute="centerY" id="Ede-Sv-5JJ"/>
<constraint firstItem="agM-Sz-8Jc" firstAttribute="leading" secondItem="bVY-An-mBJ" secondAttribute="leadingMargin" id="lcT-KV-Mes"/>
<constraint firstItem="agM-Sz-8Jc" firstAttribute="centerY" secondItem="bVY-An-mBJ" secondAttribute="centerY" id="oOf-ri-9Rb"/>
<constraint firstItem="F3O-5s-93H" firstAttribute="leading" secondItem="agM-Sz-8Jc" secondAttribute="trailing" constant="8" symbolic="YES" id="rXK-nP-M6x"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="switchView" destination="F3O-5s-93H" id="ZiQ-7p-Qb8"/>
</connections>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Performance" footerTitle="When enabled, Delta will automatically enable JIT when on the same WiFi network as AltServer." id="MyM-jI-qtY" userLabel="Performance">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="SwitchCell" id="uXJ-qB-E2e" customClass="SwitchTableViewCell">
<rect key="frame" x="0.0" y="284" width="375" height="44"/>
<rect key="frame" x="0.0" y="443" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uXJ-qB-E2e" id="mkz-9w-FlW">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
@ -1997,7 +2067,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
<tableViewSection headerTitle="DS BIOS Files" footerTitle="Delta requires these BIOS files in order to play Nintendo DS games. Tap one to import it from Files." id="ObZ-cl-5sV">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="Bfp-iA-wWE" detailTextLabel="Xpe-sd-GRy" style="IBUITableViewCellStyleValue1" id="ian-1X-SOi">
<rect key="frame" x="0.0" y="408" width="375" height="44"/>
<rect key="frame" x="0.0" y="578.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ian-1X-SOi" id="nE9-5Y-G2k">
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
@ -2020,7 +2090,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="fgD-y0-55T" detailTextLabel="b8s-cx-IgW" style="IBUITableViewCellStyleValue1" id="dwT-0T-gFD">
<rect key="frame" x="0.0" y="452" width="375" height="44"/>
<rect key="frame" x="0.0" y="622.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="dwT-0T-gFD" id="KEg-18-WYZ">
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
@ -2043,7 +2113,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="SIu-zD-qfE" detailTextLabel="CW8-h0-YST" style="IBUITableViewCellStyleValue1" id="Tll-tX-97T">
<rect key="frame" x="0.0" y="496" width="375" height="44"/>
<rect key="frame" x="0.0" y="666.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Tll-tX-97T" id="4ca-AM-jBE">
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
@ -2070,7 +2140,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
<tableViewSection headerTitle="DSi BIOS Files" footerTitle="Delta requires these BIOS files in order to play Nintendo DSi games. Tap one to import it from Files." id="SAz-hG-O4G">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="QKd-rm-101" detailTextLabel="pJb-OR-TGs" style="IBUITableViewCellStyleValue1" id="jwN-8n-xgM">
<rect key="frame" x="0.0" y="620" width="375" height="44"/>
<rect key="frame" x="0.0" y="802" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jwN-8n-xgM" id="b29-V1-qt3">
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
@ -2093,7 +2163,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="6OX-32-izR" detailTextLabel="2bc-8r-epB" style="IBUITableViewCellStyleValue1" id="3ka-mn-QqR">
<rect key="frame" x="0.0" y="664" width="375" height="44"/>
<rect key="frame" x="0.0" y="846" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3ka-mn-QqR" id="4ya-QS-s5w">
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
@ -2116,7 +2186,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="Czu-qN-Luq" detailTextLabel="eVw-7r-BEf" style="IBUITableViewCellStyleValue1" id="4FF-9a-jq2">
<rect key="frame" x="0.0" y="708" width="375" height="44"/>
<rect key="frame" x="0.0" y="890" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4FF-9a-jq2" id="emb-d3-TgY">
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
@ -2139,7 +2209,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="VWi-nu-uMv" detailTextLabel="8JD-uT-eoA" style="IBUITableViewCellStyleValue1" id="we3-0h-uKq">
<rect key="frame" x="0.0" y="752" width="375" height="44"/>
<rect key="frame" x="0.0" y="934" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="we3-0h-uKq" id="cG1-a2-Nez">
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
@ -2166,7 +2236,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
<tableViewSection headerTitle="" footerTitle="Changing cores may improve performance at the cost of additional features." id="hbM-mL-bIr">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" indentationWidth="10" reuseIdentifier="ChangeCell" textLabel="Jmx-Jw-278" style="IBUITableViewCellStyleDefault" id="p1T-4d-fC3">
<rect key="frame" x="0.0" y="861.5" width="375" height="44"/>
<rect key="frame" x="0.0" y="1053.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="p1T-4d-fC3" id="3bs-Bp-nd4">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>

View File

@ -90,6 +90,11 @@ extension ControllerSkin: ControllerSkinProtocol
{
return self.controllerSkin?.aspectRatio(for: traits)
}
public func contentSize(for traits: DeltaCore.ControllerSkin.Traits) -> CGSize?
{
return self.controllerSkin?.contentSize(for: traits)
}
}
extension ControllerSkin: Syncable

View File

@ -670,6 +670,12 @@ private extension GameViewController
{
var touchControllerSkin = TouchControllerSkin(controllerSkin: controllerSkin)
if UIApplication.shared.isExternalDisplayConnected
{
// Only show touch screen if external display is connected.
touchControllerSkin.screenPredicate = { $0.isTouchScreen }
}
if self.view.bounds.width > self.view.bounds.height
{
touchControllerSkin.screenLayoutAxis = .horizontal
@ -1119,8 +1125,10 @@ private extension GameViewController
// We need to receive gameViewController(_:didUpdateGameViews) callback.
scene.gameViewController.delegate = self
self.updateGameViews()
self.updateExternalDisplay()
self.updateControllerSkin()
// Implicitly called from updateControllerSkin()
// self.updateExternalDisplay()
}
func updateExternalDisplay()
@ -1132,7 +1140,37 @@ private extension GameViewController
scene.game = self.game
}
self.updateExternalDisplayGameViews()
var controllerSkin: ControllerSkinProtocol?
if let game = self.game, let traits = scene.gameViewController.controllerView.controllerSkinTraits
{
if let standardSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: game.type), standardSkin.supports(traits)
{
if standardSkin.hasTouchScreen(for: traits)
{
// Only use TouchControllerSkin for standard controller skins with touch screens.
var touchControllerSkin = DeltaCore.TouchControllerSkin(controllerSkin: standardSkin)
touchControllerSkin.screenLayoutAxis = Settings.features.dsAirPlay.layoutAxis
if Settings.features.dsAirPlay.topScreenOnly
{
touchControllerSkin.screenPredicate = { !$0.isTouchScreen }
}
controllerSkin = touchControllerSkin
}
else
{
controllerSkin = standardSkin
}
}
}
scene.gameViewController.controllerView.controllerSkin = controllerSkin
// Implicitly called when assigning controllerSkin.
// self.updateExternalDisplayGameViews()
}
func updateExternalDisplayGameViews()
@ -1154,7 +1192,7 @@ private extension GameViewController
self.emulatorCore?.remove(gameView)
}
self.updateGameViews()
self.updateControllerSkin() // Reset TouchControllerSkin + GameViews
}
}
@ -1317,6 +1355,10 @@ private extension GameViewController
case .syncingService, .isAltJITEnabled: break
case Settings.features.dsAirPlay.$topScreenOnly.settingsKey: fallthrough
case Settings.features.dsAirPlay.$layoutAxis.settingsKey:
self.updateExternalDisplay()
default: break
}
}

View File

@ -23,12 +23,19 @@ private extension MelonDSCoreSettingsViewController
enum Section: Int
{
case general
case airPlay
case performance
case dsBIOS
case dsiBIOS
case changeCore
}
enum AirPlayRow: Int, CaseIterable
{
case topScreenOnly
case layoutHorizontally
}
@available(iOS 13, *)
enum BIOSError: LocalizedError
{
@ -265,6 +272,32 @@ private extension MelonDSCoreSettingsViewController
Settings.isAltJITEnabled = sender.isOn
}
@IBAction func toggleTopScreenOnly(_ sender: UISwitch)
{
Settings.features.dsAirPlay.topScreenOnly = sender.isOn
self.tableView.performBatchUpdates({
let layoutHorizontallyIndexPath = IndexPath(row: AirPlayRow.layoutHorizontally.rawValue, section: Section.airPlay.rawValue)
if sender.isOn
{
self.tableView.deleteRows(at: [layoutHorizontallyIndexPath], with: .automatic)
}
else
{
self.tableView.insertRows(at: [layoutHorizontallyIndexPath], with: .automatic)
}
}) { _ in
self.tableView.reloadSections([Section.airPlay.rawValue], with: .none)
}
}
@IBAction func toggleLayoutHorizontally(_ sender: UISwitch)
{
Settings.features.dsAirPlay.layoutAxis = sender.isOn ? .horizontal : .vertical
self.tableView.reloadSections([Section.airPlay.rawValue], with: .none)
}
@objc func willEnterForeground(_ notification: Notification)
{
self.tableView.reloadData()
@ -277,13 +310,11 @@ extension MelonDSCoreSettingsViewController
{
let section = Section(rawValue: sectionIndex)!
if isSectionHidden(section)
switch section
{
return 0
}
else
{
return super.tableView(tableView, numberOfRowsInSection: sectionIndex)
case _ where isSectionHidden(section): return 0
case .airPlay where Settings.features.dsAirPlay.topScreenOnly: return 1 // Layout axis is irrelevant if only AirPlaying top screen.
default: return super.tableView(tableView, numberOfRowsInSection: sectionIndex)
}
}
@ -313,6 +344,16 @@ extension MelonDSCoreSettingsViewController
cell.contentView.isHidden = (item == nil)
case .airPlay:
let cell = cell as! SwitchTableViewCell
let row = AirPlayRow.allCases[indexPath.row]
switch row
{
case .topScreenOnly: cell.switchView.isOn = Settings.features.dsAirPlay.topScreenOnly
case .layoutHorizontally: cell.switchView.isOn = (Settings.features.dsAirPlay.layoutAxis == .horizontal)
}
case .performance:
let cell = cell as! SwitchTableViewCell
cell.switchView.isOn = Settings.isAltJITEnabled
@ -396,7 +437,7 @@ extension MelonDSCoreSettingsViewController
case .changeCore:
self.changeCore()
case .performance: break
case .airPlay, .performance: break
}
}
@ -417,14 +458,18 @@ extension MelonDSCoreSettingsViewController
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String?
{
let section = Section(rawValue: section)!
if isSectionHidden(section)
switch section
{
return nil
case _ where isSectionHidden(section): return nil
case .airPlay:
switch (Settings.features.dsAirPlay.topScreenOnly, Settings.features.dsAirPlay.layoutAxis)
{
case (true, _): return NSLocalizedString("When AirPlaying DS games, only the top screen will appear on the external display.", comment: "")
case (false, .vertical): return NSLocalizedString("When AirPlaying DS games, both screens will be stacked vertically on the external display.", comment: "")
case (false, .horizontal): return NSLocalizedString("When AirPlaying DS games, both screens will be placed side-by-side on the external display.", comment: "")
}
else
{
return super.tableView(tableView, titleForFooterInSection: section.rawValue)
default: return super.tableView(tableView, titleForFooterInSection: section.rawValue)
}
}

View File

@ -0,0 +1,23 @@
//
// DSAirPlay.swift
// Delta
//
// Created by Riley Testut on 4/26/23.
// Copyright © 2023 Riley Testut. All rights reserved.
//
import SwiftUI
import DeltaFeatures
import DeltaCore
extension TouchControllerSkin.LayoutAxis: OptionValue {}
struct DSAirPlayOptions
{
@Option
var topScreenOnly: Bool = true
@Option
var layoutAxis: TouchControllerSkin.LayoutAxis = .vertical
}

View File

@ -0,0 +1,25 @@
//
// Features.swift
// Delta
//
// Created by Riley Testut on 4/21/23.
// Copyright © 2023 Riley Testut. All rights reserved.
//
import DeltaFeatures
extension Settings
{
struct Features: FeatureContainer
{
static let shared = Features()
@Feature(name: "DS AirPlay", options: DSAirPlayOptions())
var dsAirPlay
private init()
{
self.prepareFeatures()
}
}
}

View File

@ -49,6 +49,8 @@ extension Settings
struct Settings
{
static let features = Features.shared
static func registerDefaults()
{
let defaults = [#keyPath(UserDefaults.translucentControllerSkinOpacity): 0.7,