feat: motion control - direct mapping from gyro to mouse or controller sticks, with on-screen button "gyro" for toggling
This commit is contained in:
1 parent
99a2a4ca1a
commit
16a210efa2
29 files changed
+1932
-495
No files matched your search
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
180345E12E819877004AF36F /* libopus-sim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 180345E02E819877004AF36F /* libopus-sim.a */; };
|
180345E12E819877004AF36F /* libopus-sim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 180345E02E819877004AF36F /* libopus-sim.a */; };
|
||||||
|
180E6E372E93BFDB0067C73A /* CountdownAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180E6E362E93BFDB0067C73A /* CountdownAlertController.swift */; };
|
||||||
1817B61E2E6FD04D000D938D /* SafeTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1817B61D2E6FD04D000D938D /* SafeTimer.swift */; };
|
1817B61E2E6FD04D000D938D /* SafeTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1817B61D2E6FD04D000D938D /* SafeTimer.swift */; };
|
||||||
182760F72E83E2EA00EC1C5C /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = 182760F62E83E2EA00EC1C5C /* DequeModule */; };
|
182760F72E83E2EA00EC1C5C /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = 182760F62E83E2EA00EC1C5C /* DequeModule */; };
|
||||||
182760FA2E83E57D00EC1C5C /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = 182760F92E83E57D00EC1C5C /* DequeModule */; };
|
182760FA2E83E57D00EC1C5C /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = 182760F92E83E57D00EC1C5C /* DequeModule */; };
|
||||||
@@ -160,6 +161,7 @@
|
|||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
180345E02E819877004AF36F /* libopus-sim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopus-sim.a"; sourceTree = "<group>"; };
|
180345E02E819877004AF36F /* libopus-sim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopus-sim.a"; sourceTree = "<group>"; };
|
||||||
|
180E6E362E93BFDB0067C73A /* CountdownAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownAlertController.swift; sourceTree = "<group>"; };
|
||||||
1817B61D2E6FD04D000D938D /* SafeTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafeTimer.swift; sourceTree = "<group>"; };
|
1817B61D2E6FD04D000D938D /* SafeTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafeTimer.swift; sourceTree = "<group>"; };
|
||||||
1829CE3D2E2154DF0074A99A /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = "<group>"; };
|
1829CE3D2E2154DF0074A99A /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = "<group>"; };
|
||||||
1829CE3E2E2154DF0074A99A /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = "<group>"; };
|
1829CE3E2E2154DF0074A99A /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = "<group>"; };
|
||||||
@@ -635,6 +637,7 @@
|
|||||||
847D2FDA2DDA015F00937A62 /* MenuSectionView.m */,
|
847D2FDA2DDA015F00937A62 /* MenuSectionView.m */,
|
||||||
FBDE86E419F82297001C18A8 /* UIAppView.h */,
|
FBDE86E419F82297001C18A8 /* UIAppView.h */,
|
||||||
FBDE86E519F82297001C18A8 /* UIAppView.m */,
|
FBDE86E519F82297001C18A8 /* UIAppView.m */,
|
||||||
|
180E6E362E93BFDB0067C73A /* CountdownAlertController.swift */,
|
||||||
1888C1902E24C2C800108730 /* AboutView.swift */,
|
1888C1902E24C2C800108730 /* AboutView.swift */,
|
||||||
1888C1912E24C33100108730 /* AboutViewController.swift */,
|
1888C1912E24C33100108730 /* AboutViewController.swift */,
|
||||||
FB89463719F6473800339C8A /* Launch Screen.xib */,
|
FB89463719F6473800339C8A /* Launch Screen.xib */,
|
||||||
@@ -1054,6 +1057,7 @@
|
|||||||
840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */,
|
840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */,
|
||||||
187F4B282E04658A00EECC59 /* WidgetPanelStackView.m in Sources */,
|
187F4B282E04658A00EECC59 /* WidgetPanelStackView.m in Sources */,
|
||||||
FB4678FA1A55FFAD00377732 /* DiscoveryManager.m in Sources */,
|
FB4678FA1A55FFAD00377732 /* DiscoveryManager.m in Sources */,
|
||||||
|
180E6E372E93BFDB0067C73A /* CountdownAlertController.swift in Sources */,
|
||||||
841BAC922DE35D6B00DA639E /* ThemeManager.m in Sources */,
|
841BAC922DE35D6B00DA639E /* ThemeManager.m in Sources */,
|
||||||
98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */,
|
98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */,
|
||||||
FB89463519F646E200339C8A /* MainFrameViewController.m in Sources */,
|
FB89463519F646E200339C8A /* MainFrameViewController.m in Sources */,
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="XvO-F3-j4u"/>
|
<viewControllerLayoutGuide type="bottom" id="XvO-F3-j4u"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="WRy-3f-gEP" customClass="UIScrollView">
|
<view key="view" contentMode="scaleToFill" id="WRy-3f-gEP" customClass="UIScrollView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="2000" height="5000"/>
|
<rect key="frame" x="0.0" y="0.0" width="2165" height="5000"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="zpX-lo-MLA" userLabel="Resolutionstak">
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="zpX-lo-MLA" userLabel="Resolutionstak">
|
||||||
@@ -997,6 +997,31 @@
|
|||||||
<constraint firstAttribute="width" constant="380" id="cHp-hJ-PUQ"/>
|
<constraint firstAttribute="width" constant="380" id="cHp-hJ-PUQ"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</stackView>
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="6Lo-Vn-3fP" userLabel="mapGyroToStack">
|
||||||
|
<rect key="frame" x="24" y="2750" width="380" height="56.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Map Gyro to" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6gY-8I-NWq" userLabel="Show Local Keyboard Toolbar">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="gTZ-RS-VIl" userLabel="Show Local Keyboard Toolbar Selector">
|
||||||
|
<rect key="frame" x="0.0" y="25.5" width="380" height="32"/>
|
||||||
|
<segments>
|
||||||
|
<segment title="Mouse"/>
|
||||||
|
<segment title="Controller Stick"/>
|
||||||
|
<segment title="Drift Correction"/>
|
||||||
|
</segments>
|
||||||
|
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<color key="selectedSegmentTintColor" systemColor="linkColor"/>
|
||||||
|
</segmentedControl>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="56.5" id="KCs-Ma-Gkw"/>
|
||||||
|
<constraint firstAttribute="width" constant="380" id="bOe-T1-f1x"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="RrU-mx-9jP" userLabel="multiControllerStack">
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="RrU-mx-9jP" userLabel="multiControllerStack">
|
||||||
<rect key="frame" x="23" y="1649" width="318" height="31"/>
|
<rect key="frame" x="23" y="1649" width="318" height="31"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
@@ -1201,10 +1226,123 @@
|
|||||||
<constraint firstAttribute="height" constant="31" id="ucg-xc-5Iy"/>
|
<constraint firstAttribute="height" constant="31" id="ucg-xc-5Iy"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</stackView>
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="8eE-nU-sk5" userLabel="gyroToStickStack">
|
||||||
|
<rect key="frame" x="25" y="2835" width="336" height="67"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="RJa-fo-81z" userLabel="substack1">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="336" height="31"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Yaw&Pitch → Right Stick" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XU0-qu-2p4" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="186" height="31"/>
|
||||||
|
<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="tDm-kN-h78" userLabel="yawPitchToRightStickSwitch">
|
||||||
|
<rect key="frame" x="206" y="0.0" width="132" height="31"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="130" id="L2q-0I-zNv"/>
|
||||||
|
</constraints>
|
||||||
|
</switch>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="31" id="MVP-k4-wjG"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="fPh-yy-10O" userLabel="substack2">
|
||||||
|
<rect key="frame" x="0.0" y="36" width="336" height="31"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Roll → Left Stick" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EFR-Ee-KiK" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="186" height="31"/>
|
||||||
|
<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="Jr5-es-If6" userLabel="rollToLeftStickSwitch">
|
||||||
|
<rect key="frame" x="206" y="0.0" width="132" height="31"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="130" id="mon-AC-a0O"/>
|
||||||
|
</constraints>
|
||||||
|
</switch>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="31" id="2Fb-Z7-QOe"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="67" id="2rS-c5-xgi"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" axis="vertical" spacing="13.800000000000001" translatesAutoresizingMaskIntoConstraints="NO" id="9Cg-zf-Fjl" userLabel="yawPitchSensitivityStack">
|
||||||
|
<rect key="frame" x="22" y="2935" width="380" height="125"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="4Ee-fb-lEr" userLabel="yawSensitivityStack">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="55.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Yaw Sensitivity" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aol-mw-Kn1" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||||
|
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="100" minValue="0.0" maxValue="300" translatesAutoresizingMaskIntoConstraints="NO" id="vdl-D4-5vn">
|
||||||
|
<rect key="frame" x="-2" y="25.5" width="384" height="31"/>
|
||||||
|
<color key="tintColor" systemColor="linkColor"/>
|
||||||
|
</slider>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="55.5" id="FiZ-K1-sRp"/>
|
||||||
|
<constraint firstAttribute="width" constant="380" id="YVf-vn-1YY"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="gSn-tW-p9e" userLabel="pitchSensitivityStack">
|
||||||
|
<rect key="frame" x="0.0" y="69.5" width="380" height="55.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Pitch Sensitivity" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jcj-D4-VnY" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||||
|
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="100" minValue="0.0" maxValue="300" translatesAutoresizingMaskIntoConstraints="NO" id="HZZ-GL-j2Z">
|
||||||
|
<rect key="frame" x="-2" y="25.5" width="384" height="31"/>
|
||||||
|
<color key="tintColor" systemColor="linkColor"/>
|
||||||
|
</slider>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="380" id="4d0-pW-PNc"/>
|
||||||
|
<constraint firstAttribute="height" constant="55.5" id="tnC-IL-odd"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="iey-Nn-eFG" userLabel="rollSensitivityStack">
|
||||||
|
<rect key="frame" x="23" y="3084" width="380" height="55.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Roll Sensitivity" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="u9Q-wm-UBm" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||||
|
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="100" minValue="0.0" maxValue="300" translatesAutoresizingMaskIntoConstraints="NO" id="P7t-Q3-rrN">
|
||||||
|
<rect key="frame" x="-2" y="25.5" width="384" height="31"/>
|
||||||
|
<color key="tintColor" systemColor="linkColor"/>
|
||||||
|
</slider>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="380" id="BBp-mC-gCM"/>
|
||||||
|
<constraint firstAttribute="height" constant="55.5" id="zbp-4y-y0p"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
</view>
|
</view>
|
||||||
<size key="freeformSize" width="2000" height="5000"/>
|
<size key="freeformSize" width="2165" height="5000"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="audioConfigSelector" destination="iZL-dA-jk9" id="Yja-os-o6g"/>
|
<outlet property="audioConfigSelector" destination="iZL-dA-jk9" id="Yja-os-o6g"/>
|
||||||
<outlet property="audioConfigStack" destination="qnG-5F-2YY" id="kFX-VK-vuK"/>
|
<outlet property="audioConfigStack" destination="qnG-5F-2YY" id="kFX-VK-vuK"/>
|
||||||
@@ -1242,6 +1380,7 @@
|
|||||||
<outlet property="gyroModeStack" destination="kAD-mK-aP8" id="GvL-Yx-S2t"/>
|
<outlet property="gyroModeStack" destination="kAD-mK-aP8" id="GvL-Yx-S2t"/>
|
||||||
<outlet property="gyroSensitivitySlider" destination="3Ge-IB-PBj" id="Pdg-ke-KG3"/>
|
<outlet property="gyroSensitivitySlider" destination="3Ge-IB-PBj" id="Pdg-ke-KG3"/>
|
||||||
<outlet property="gyroSensitivityStack" destination="T3L-Kz-2yA" id="iQS-ce-h0z"/>
|
<outlet property="gyroSensitivityStack" destination="T3L-Kz-2yA" id="iQS-ce-h0z"/>
|
||||||
|
<outlet property="gyroToStickSwitchStack" destination="8eE-nU-sk5" id="oZB-AM-iHM"/>
|
||||||
<outlet property="hdrStack" destination="dH7-Sy-EdJ" id="cJm-bj-Aqo"/>
|
<outlet property="hdrStack" destination="dH7-Sy-EdJ" id="cJm-bj-Aqo"/>
|
||||||
<outlet property="hdrSwitch" destination="wIq-fF-X8l" id="Ozz-h0-nJL"/>
|
<outlet property="hdrSwitch" destination="wIq-fF-X8l" id="Ozz-h0-nJL"/>
|
||||||
<outlet property="liftStreamViewForKeyboardSelector" destination="MM5-O0-vK3" id="tSC-Rp-Qfg"/>
|
<outlet property="liftStreamViewForKeyboardSelector" destination="MM5-O0-vK3" id="tSC-Rp-Qfg"/>
|
||||||
@@ -1250,6 +1389,8 @@
|
|||||||
<outlet property="localMousePointerModeStack" destination="nE1-Al-qRc" id="2hC-Xi-icX"/>
|
<outlet property="localMousePointerModeStack" destination="nE1-Al-qRc" id="2hC-Xi-icX"/>
|
||||||
<outlet property="localVolumeSlider" destination="aDm-fu-eWR" id="hR5-TW-rBf"/>
|
<outlet property="localVolumeSlider" destination="aDm-fu-eWR" id="hR5-TW-rBf"/>
|
||||||
<outlet property="localVolumeStack" destination="a8q-4l-07i" id="cvW-nq-R7I"/>
|
<outlet property="localVolumeStack" destination="a8q-4l-07i" id="cvW-nq-R7I"/>
|
||||||
|
<outlet property="mapGyroToSelector" destination="gTZ-RS-VIl" id="f2p-Ie-lYM"/>
|
||||||
|
<outlet property="mapGyroToStack" destination="6Lo-Vn-3fP" id="HlD-Ke-O9D"/>
|
||||||
<outlet property="micVolumeSlider" destination="bfg-fA-aYb" id="qPV-vS-aPu"/>
|
<outlet property="micVolumeSlider" destination="bfg-fA-aYb" id="qPV-vS-aPu"/>
|
||||||
<outlet property="micVolumeStack" destination="xbj-CP-fhP" id="Bto-Me-9Qj"/>
|
<outlet property="micVolumeStack" destination="xbj-CP-fhP" id="Bto-Me-9Qj"/>
|
||||||
<outlet property="mousePointerVelocityFactorSlider" destination="Ei2-FJ-9oC" id="1T3-QA-Jua"/>
|
<outlet property="mousePointerVelocityFactorSlider" destination="Ei2-FJ-9oC" id="1T3-QA-Jua"/>
|
||||||
@@ -1266,6 +1407,8 @@
|
|||||||
<outlet property="performanceGraphStack" destination="H8p-G9-pnl" id="9RJ-uO-IpF"/>
|
<outlet property="performanceGraphStack" destination="H8p-G9-pnl" id="9RJ-uO-IpF"/>
|
||||||
<outlet property="pipStack" destination="12a-Wi-LlL" id="eTJ-0h-hRB"/>
|
<outlet property="pipStack" destination="12a-Wi-LlL" id="eTJ-0h-hRB"/>
|
||||||
<outlet property="pipSwitch" destination="VTC-Z9-IYM" id="gzC-rU-DoP"/>
|
<outlet property="pipSwitch" destination="VTC-Z9-IYM" id="gzC-rU-DoP"/>
|
||||||
|
<outlet property="pitchSensitivitySlider" destination="HZZ-GL-j2Z" id="4ji-Jt-WdT"/>
|
||||||
|
<outlet property="pitchSensitivityStack" destination="gSn-tW-p9e" id="Gbg-PK-dpf"/>
|
||||||
<outlet property="pointerVelocityDividerStack" destination="b1U-nD-gYE" id="ep1-K3-rKj"/>
|
<outlet property="pointerVelocityDividerStack" destination="b1U-nD-gYE" id="ep1-K3-rKj"/>
|
||||||
<outlet property="pointerVelocityFactorStack" destination="vdk-mK-ztu" id="Cik-Rh-pGS"/>
|
<outlet property="pointerVelocityFactorStack" destination="vdk-mK-ztu" id="Cik-Rh-pGS"/>
|
||||||
<outlet property="pointerVelocityModeDividerSlider" destination="AcZ-2i-GI4" id="dGf-ZU-w3S"/>
|
<outlet property="pointerVelocityModeDividerSlider" destination="AcZ-2i-GI4" id="dGf-ZU-w3S"/>
|
||||||
@@ -1279,6 +1422,9 @@
|
|||||||
<outlet property="resolutionStack" destination="zpX-lo-MLA" id="9Hu-I9-hjH"/>
|
<outlet property="resolutionStack" destination="zpX-lo-MLA" id="9Hu-I9-hjH"/>
|
||||||
<outlet property="reverseMouseWheelDirectionSelector" destination="mTM-NL-CcF" id="EYZ-Qg-OLu"/>
|
<outlet property="reverseMouseWheelDirectionSelector" destination="mTM-NL-CcF" id="EYZ-Qg-OLu"/>
|
||||||
<outlet property="reverseMouseWheelDirectionStack" destination="2P0-Tg-zWd" id="Zz1-VC-0hC"/>
|
<outlet property="reverseMouseWheelDirectionStack" destination="2P0-Tg-zWd" id="Zz1-VC-0hC"/>
|
||||||
|
<outlet property="rollSensitivitySlider" destination="P7t-Q3-rrN" id="pEv-Be-Dxe"/>
|
||||||
|
<outlet property="rollSensitivityStack" destination="iey-Nn-eFG" id="aco-Lz-SzN"/>
|
||||||
|
<outlet property="rollToLeftStickSwitch" destination="Jr5-es-If6" id="uQS-Rf-ETV"/>
|
||||||
<outlet property="scrollView" destination="WRy-3f-gEP" id="V8B-oF-27B"/>
|
<outlet property="scrollView" destination="WRy-3f-gEP" id="V8B-oF-27B"/>
|
||||||
<outlet property="sendDummyEventStack" destination="RGd-TV-mkU" id="87h-08-4Og"/>
|
<outlet property="sendDummyEventStack" destination="RGd-TV-mkU" id="87h-08-4Og"/>
|
||||||
<outlet property="sendDummyEventSwitch" destination="eE4-oT-Gzb" id="q51-ds-zJC"/>
|
<outlet property="sendDummyEventSwitch" destination="eE4-oT-Gzb" id="q51-ds-zJC"/>
|
||||||
@@ -1298,6 +1444,7 @@
|
|||||||
<outlet property="statsOverlayStack" destination="UcP-0b-nJD" id="UdI-NS-O6k"/>
|
<outlet property="statsOverlayStack" destination="UcP-0b-nJD" id="UdI-NS-O6k"/>
|
||||||
<outlet property="swapAbxyStack" destination="2rP-Ui-wVU" id="gUR-Vz-ORP"/>
|
<outlet property="swapAbxyStack" destination="2rP-Ui-wVU" id="gUR-Vz-ORP"/>
|
||||||
<outlet property="swapAbxySwitch" destination="hv7-6b-rez" id="iKX-qk-Dep"/>
|
<outlet property="swapAbxySwitch" destination="hv7-6b-rez" id="iKX-qk-Dep"/>
|
||||||
|
<outlet property="testStack" destination="aP7-M9-psT" id="xAv-s6-rea"/>
|
||||||
<outlet property="touchModeLabel" destination="MUY-2Y-bHi" id="6jD-aq-rmE"/>
|
<outlet property="touchModeLabel" destination="MUY-2Y-bHi" id="6jD-aq-rmE"/>
|
||||||
<outlet property="touchModeSelector" destination="bhC-da-VGd" id="hna-Cr-kyd"/>
|
<outlet property="touchModeSelector" destination="bhC-da-VGd" id="hna-Cr-kyd"/>
|
||||||
<outlet property="touchModeStack" destination="M78-Pw-jWx" id="mdl-y3-54B"/>
|
<outlet property="touchModeStack" destination="M78-Pw-jWx" id="mdl-y3-54B"/>
|
||||||
@@ -1310,6 +1457,10 @@
|
|||||||
<outlet property="unlockDisplayOrientationStack" destination="5dA-cw-gOa" id="HHx-qM-BZi"/>
|
<outlet property="unlockDisplayOrientationStack" destination="5dA-cw-gOa" id="HHx-qM-BZi"/>
|
||||||
<outlet property="useBuiltinMicStack" destination="jCF-si-KLC" id="AnY-Ob-jE9"/>
|
<outlet property="useBuiltinMicStack" destination="jCF-si-KLC" id="AnY-Ob-jE9"/>
|
||||||
<outlet property="useBuiltinMicSwitch" destination="H3f-qy-ySA" id="O76-df-ich"/>
|
<outlet property="useBuiltinMicSwitch" destination="H3f-qy-ySA" id="O76-df-ich"/>
|
||||||
|
<outlet property="yawPitchSensitivityStack" destination="9Cg-zf-Fjl" id="Iym-a8-nGn"/>
|
||||||
|
<outlet property="yawPitchToRightStickSwitch" destination="tDm-kN-h78" id="v97-60-O35"/>
|
||||||
|
<outlet property="yawSensitivitySlider" destination="vdl-D4-5vn" id="DqE-G7-9eQ"/>
|
||||||
|
<outlet property="yawSensitivityStack" destination="4Ee-fb-lEr" id="47a-rU-LQn"/>
|
||||||
<outlet property="yuv444Stack" destination="Qfd-bG-FSa" id="kpm-TK-Ngg"/>
|
<outlet property="yuv444Stack" destination="Qfd-bG-FSa" id="kpm-TK-Ngg"/>
|
||||||
<outlet property="yuv444Switch" destination="jIQ-jh-tvP" id="Qru-gO-haR"/>
|
<outlet property="yuv444Switch" destination="jIQ-jh-tvP" id="Qru-gO-haR"/>
|
||||||
</connections>
|
</connections>
|
||||||
@@ -1327,7 +1478,7 @@
|
|||||||
</items>
|
</items>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-503" y="1978"/>
|
<point key="canvasLocation" x="-503.59712230215831" y="1977.520661157025"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Reveal View Controller-->
|
<!--Reveal View Controller-->
|
||||||
<scene sceneID="rR7-ZT-bc7">
|
<scene sceneID="rR7-ZT-bc7">
|
||||||
@@ -1426,7 +1577,7 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="eaS-aj-VtA"/>
|
<viewControllerLayoutGuide type="bottom" id="eaS-aj-VtA"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="Acw-S4-f8a">
|
<view key="view" contentMode="scaleToFill" id="Acw-S4-f8a">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="834" height="1210"/>
|
<rect key="frame" x="0.0" y="0.0" width="1300" height="1194"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fZU-G0-fgM" userLabel="Toolbar Root View" customClass="ToolBarContainerView">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fZU-G0-fgM" userLabel="Toolbar Root View" customClass="ToolBarContainerView">
|
||||||
|
|||||||
@@ -796,7 +796,7 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="gDc-PK-wuq"/>
|
<viewControllerLayoutGuide type="bottom" id="gDc-PK-wuq"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="Xq6-9l-Oey" customClass="UIScrollView">
|
<view key="view" contentMode="scaleToFill" id="Xq6-9l-Oey" customClass="UIScrollView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1200" height="2500"/>
|
<rect key="frame" x="0.0" y="0.0" width="1200" height="5000"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="sbK-BT-6rK" userLabel="Resolutionstak">
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="sbK-BT-6rK" userLabel="Resolutionstak">
|
||||||
@@ -1932,10 +1932,149 @@
|
|||||||
<constraint firstAttribute="height" constant="31" id="FH8-Xv-wYq"/>
|
<constraint firstAttribute="height" constant="31" id="FH8-Xv-wYq"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</stackView>
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="NFm-mv-3qh" userLabel="mapGyroToStack">
|
||||||
|
<rect key="frame" x="15" y="2398" width="380" height="56.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Map Gyro to" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GX4-zV-pQ8" userLabel="Show Local Keyboard Toolbar">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" apportionsSegmentWidthsByContent="YES" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="0OC-rX-CHK" userLabel="Show Local Keyboard Toolbar Selector">
|
||||||
|
<rect key="frame" x="0.0" y="25.5" width="380" height="32"/>
|
||||||
|
<segments>
|
||||||
|
<segment title="Mouse"/>
|
||||||
|
<segment title="Controller Stick"/>
|
||||||
|
<segment title="Drift Correction"/>
|
||||||
|
</segments>
|
||||||
|
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<color key="selectedSegmentTintColor" systemColor="linkColor"/>
|
||||||
|
</segmentedControl>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="56.5" id="5S9-1U-k5K"/>
|
||||||
|
<constraint firstAttribute="width" constant="380" id="ffG-VJ-93D"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="xMx-7R-pmH" userLabel="gyroToStickStack">
|
||||||
|
<rect key="frame" x="18" y="2476" width="336" height="67"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="68R-uR-4PI" userLabel="substack1">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="336" height="31"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Yaw&Pitch → Right Stick" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AN9-U4-nUG" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="186" height="31"/>
|
||||||
|
<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="npQ-yh-2MA" userLabel="yawPitchToRightStickSwitch">
|
||||||
|
<rect key="frame" x="206" y="0.0" width="132" height="31"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="130" id="kQr-At-VS9"/>
|
||||||
|
</constraints>
|
||||||
|
</switch>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="31" id="8bw-pt-rsb"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="tvL-sE-fGi" userLabel="substack2">
|
||||||
|
<rect key="frame" x="0.0" y="36" width="336" height="31"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Roll → Left Stick" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3LT-qb-ZEB" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="186" height="31"/>
|
||||||
|
<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="caz-QH-4dS" userLabel="rollToLeftStickSwitch">
|
||||||
|
<rect key="frame" x="206" y="0.0" width="132" height="31"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="130" id="b3T-zI-mrq"/>
|
||||||
|
</constraints>
|
||||||
|
</switch>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="31" id="eI7-1J-KrE"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="67" id="bcM-Pz-UFh"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="13.800000000000001" id="1z6-G8-X45" userLabel="yawPitchSensitivityStack">
|
||||||
|
<rect key="frame" x="20" y="2557" width="380" height="125"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="pyS-Cc-IC3" userLabel="yawSensitivityStack">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="55.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Yaw Sensitivity" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aSq-2H-FPk" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||||
|
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="100" minValue="0.0" maxValue="800" translatesAutoresizingMaskIntoConstraints="NO" id="JBP-uF-88R">
|
||||||
|
<rect key="frame" x="-2" y="25.5" width="384" height="31"/>
|
||||||
|
<color key="tintColor" systemColor="linkColor"/>
|
||||||
|
</slider>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="55.5" id="9YP-eb-7uG"/>
|
||||||
|
<constraint firstAttribute="width" constant="380" id="yGK-yS-yMI"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="pYu-QR-UfH" userLabel="pitchSensitivityStack">
|
||||||
|
<rect key="frame" x="0.0" y="69.5" width="380" height="55.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Pitch Sensitivity" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yJv-xb-5HL" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||||
|
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="100" minValue="0.0" maxValue="800" translatesAutoresizingMaskIntoConstraints="NO" id="4AR-S2-QX8">
|
||||||
|
<rect key="frame" x="-2" y="25.5" width="384" height="31"/>
|
||||||
|
<color key="tintColor" systemColor="linkColor"/>
|
||||||
|
</slider>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="380" id="ccF-lv-vv4"/>
|
||||||
|
<constraint firstAttribute="height" constant="55.5" id="u97-Kt-elr"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="5Zq-ch-iZ4" userLabel="rollSensitivityStack">
|
||||||
|
<rect key="frame" x="20" y="2696" width="380" height="55.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Roll Sensitivity" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dxk-33-Nbx" userLabel="uiLabel">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||||
|
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="100" minValue="0.0" maxValue="800" translatesAutoresizingMaskIntoConstraints="NO" id="SQ5-oO-S7X">
|
||||||
|
<rect key="frame" x="-2" y="25.5" width="384" height="31"/>
|
||||||
|
<color key="tintColor" systemColor="linkColor"/>
|
||||||
|
</slider>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="380" id="1bD-h1-NW3"/>
|
||||||
|
<constraint firstAttribute="height" constant="55.5" id="9ho-dq-LZS"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
</view>
|
</view>
|
||||||
<size key="freeformSize" width="1200" height="2500"/>
|
<size key="freeformSize" width="1200" height="5000"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="audioConfigSelector" destination="hfR-K8-Ti4" id="4La-bI-jLq"/>
|
<outlet property="audioConfigSelector" destination="hfR-K8-Ti4" id="4La-bI-jLq"/>
|
||||||
<outlet property="audioConfigStack" destination="Rld-CY-qzs" id="dmf-m8-sor"/>
|
<outlet property="audioConfigStack" destination="Rld-CY-qzs" id="dmf-m8-sor"/>
|
||||||
@@ -1973,6 +2112,7 @@
|
|||||||
<outlet property="gyroModeStack" destination="5Ep-SA-IBh" id="2qw-Sb-8O6"/>
|
<outlet property="gyroModeStack" destination="5Ep-SA-IBh" id="2qw-Sb-8O6"/>
|
||||||
<outlet property="gyroSensitivitySlider" destination="mHC-z4-AWQ" id="GHN-po-QYw"/>
|
<outlet property="gyroSensitivitySlider" destination="mHC-z4-AWQ" id="GHN-po-QYw"/>
|
||||||
<outlet property="gyroSensitivityStack" destination="Nd5-0L-wpv" id="Qo0-Z3-gkm"/>
|
<outlet property="gyroSensitivityStack" destination="Nd5-0L-wpv" id="Qo0-Z3-gkm"/>
|
||||||
|
<outlet property="gyroToStickSwitchStack" destination="xMx-7R-pmH" id="LbK-ie-Hsl"/>
|
||||||
<outlet property="hdrStack" destination="kFw-d7-BTn" id="3aT-Gh-bDO"/>
|
<outlet property="hdrStack" destination="kFw-d7-BTn" id="3aT-Gh-bDO"/>
|
||||||
<outlet property="hdrSwitch" destination="Kgb-YU-AFW" id="ksG-A4-HhS"/>
|
<outlet property="hdrSwitch" destination="Kgb-YU-AFW" id="ksG-A4-HhS"/>
|
||||||
<outlet property="liftStreamViewForKeyboardSelector" destination="TJx-6E-kgr" id="KnL-15-Q6G"/>
|
<outlet property="liftStreamViewForKeyboardSelector" destination="TJx-6E-kgr" id="KnL-15-Q6G"/>
|
||||||
@@ -1981,6 +2121,8 @@
|
|||||||
<outlet property="localMousePointerModeStack" destination="IKt-3O-raJ" id="vkn-5N-4Jl"/>
|
<outlet property="localMousePointerModeStack" destination="IKt-3O-raJ" id="vkn-5N-4Jl"/>
|
||||||
<outlet property="localVolumeSlider" destination="7xn-es-SaH" id="au4-HZ-ff6"/>
|
<outlet property="localVolumeSlider" destination="7xn-es-SaH" id="au4-HZ-ff6"/>
|
||||||
<outlet property="localVolumeStack" destination="zmq-ah-Vfh" id="88M-AX-mh6"/>
|
<outlet property="localVolumeStack" destination="zmq-ah-Vfh" id="88M-AX-mh6"/>
|
||||||
|
<outlet property="mapGyroToSelector" destination="0OC-rX-CHK" id="YjK-lh-F2R"/>
|
||||||
|
<outlet property="mapGyroToStack" destination="NFm-mv-3qh" id="b87-SK-9SH"/>
|
||||||
<outlet property="micVolumeSlider" destination="49s-nP-71W" id="mbp-LG-jSE"/>
|
<outlet property="micVolumeSlider" destination="49s-nP-71W" id="mbp-LG-jSE"/>
|
||||||
<outlet property="micVolumeStack" destination="y2W-wf-sBM" id="UTS-Oa-fak"/>
|
<outlet property="micVolumeStack" destination="y2W-wf-sBM" id="UTS-Oa-fak"/>
|
||||||
<outlet property="mousePointerVelocityFactorSlider" destination="bXi-Xx-6MW" id="fEw-YV-154"/>
|
<outlet property="mousePointerVelocityFactorSlider" destination="bXi-Xx-6MW" id="fEw-YV-154"/>
|
||||||
@@ -1996,6 +2138,8 @@
|
|||||||
<outlet property="performanceGraphStack" destination="9r0-OX-NYn" id="wQE-tW-wtN"/>
|
<outlet property="performanceGraphStack" destination="9r0-OX-NYn" id="wQE-tW-wtN"/>
|
||||||
<outlet property="pipStack" destination="hvN-TZ-ziV" id="6oL-0b-Bto"/>
|
<outlet property="pipStack" destination="hvN-TZ-ziV" id="6oL-0b-Bto"/>
|
||||||
<outlet property="pipSwitch" destination="mgR-dj-4gu" id="b8b-7E-wrq"/>
|
<outlet property="pipSwitch" destination="mgR-dj-4gu" id="b8b-7E-wrq"/>
|
||||||
|
<outlet property="pitchSensitivitySlider" destination="4AR-S2-QX8" id="Ohj-qb-AUW"/>
|
||||||
|
<outlet property="pitchSensitivityStack" destination="pYu-QR-UfH" id="bB1-70-SpJ"/>
|
||||||
<outlet property="pointerVelocityDividerStack" destination="Jp2-5v-Zr0" id="0mB-RI-df8"/>
|
<outlet property="pointerVelocityDividerStack" destination="Jp2-5v-Zr0" id="0mB-RI-df8"/>
|
||||||
<outlet property="pointerVelocityFactorStack" destination="PAv-Ep-ZJo" id="fxb-uz-e1a"/>
|
<outlet property="pointerVelocityFactorStack" destination="PAv-Ep-ZJo" id="fxb-uz-e1a"/>
|
||||||
<outlet property="pointerVelocityModeDividerSlider" destination="OMw-Ew-fBX" id="Yki-z6-7bh"/>
|
<outlet property="pointerVelocityModeDividerSlider" destination="OMw-Ew-fBX" id="Yki-z6-7bh"/>
|
||||||
@@ -2009,6 +2153,9 @@
|
|||||||
<outlet property="resolutionStack" destination="sbK-BT-6rK" id="hEi-h6-te9"/>
|
<outlet property="resolutionStack" destination="sbK-BT-6rK" id="hEi-h6-te9"/>
|
||||||
<outlet property="reverseMouseWheelDirectionSelector" destination="Uca-fD-4AC" id="gLa-40-suX"/>
|
<outlet property="reverseMouseWheelDirectionSelector" destination="Uca-fD-4AC" id="gLa-40-suX"/>
|
||||||
<outlet property="reverseMouseWheelDirectionStack" destination="iS2-99-oox" id="dl2-8e-idU"/>
|
<outlet property="reverseMouseWheelDirectionStack" destination="iS2-99-oox" id="dl2-8e-idU"/>
|
||||||
|
<outlet property="rollSensitivitySlider" destination="SQ5-oO-S7X" id="UTL-nv-UwL"/>
|
||||||
|
<outlet property="rollSensitivityStack" destination="5Zq-ch-iZ4" id="zYv-nH-Ut1"/>
|
||||||
|
<outlet property="rollToLeftStickSwitch" destination="caz-QH-4dS" id="XPR-l0-M1L"/>
|
||||||
<outlet property="scrollView" destination="Xq6-9l-Oey" id="nif-2O-JNf"/>
|
<outlet property="scrollView" destination="Xq6-9l-Oey" id="nif-2O-JNf"/>
|
||||||
<outlet property="sendDummyEventStack" destination="6yl-Mb-0o6" id="KN5-ty-oTQ"/>
|
<outlet property="sendDummyEventStack" destination="6yl-Mb-0o6" id="KN5-ty-oTQ"/>
|
||||||
<outlet property="sendDummyEventSwitch" destination="OlN-GM-xLT" id="DQ8-Un-c5b"/>
|
<outlet property="sendDummyEventSwitch" destination="OlN-GM-xLT" id="DQ8-Un-c5b"/>
|
||||||
@@ -2040,6 +2187,10 @@
|
|||||||
<outlet property="unlockDisplayOrientationStack" destination="FKE-J4-bZB" id="Loj-SR-3XK"/>
|
<outlet property="unlockDisplayOrientationStack" destination="FKE-J4-bZB" id="Loj-SR-3XK"/>
|
||||||
<outlet property="useBuiltinMicStack" destination="lbL-2m-AO2" id="ZDm-aL-1cC"/>
|
<outlet property="useBuiltinMicStack" destination="lbL-2m-AO2" id="ZDm-aL-1cC"/>
|
||||||
<outlet property="useBuiltinMicSwitch" destination="YR4-0Z-D5Z" id="rMI-TK-FGJ"/>
|
<outlet property="useBuiltinMicSwitch" destination="YR4-0Z-D5Z" id="rMI-TK-FGJ"/>
|
||||||
|
<outlet property="yawPitchSensitivityStack" destination="1z6-G8-X45" id="9M6-Kg-Ms1"/>
|
||||||
|
<outlet property="yawPitchToRightStickSwitch" destination="npQ-yh-2MA" id="u31-YT-yXS"/>
|
||||||
|
<outlet property="yawSensitivitySlider" destination="JBP-uF-88R" id="57t-bo-3Y4"/>
|
||||||
|
<outlet property="yawSensitivityStack" destination="pyS-Cc-IC3" id="4Vr-ks-9hk"/>
|
||||||
<outlet property="yuv444Stack" destination="hnG-J3-IuI" id="whR-kg-UmO"/>
|
<outlet property="yuv444Stack" destination="hnG-J3-IuI" id="whR-kg-UmO"/>
|
||||||
<outlet property="yuv444Switch" destination="OZK-ba-DD9" id="SZA-F4-eo3"/>
|
<outlet property="yuv444Switch" destination="OZK-ba-DD9" id="SZA-F4-eo3"/>
|
||||||
</connections>
|
</connections>
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
//
|
||||||
|
// CountdownAlertController.swift
|
||||||
|
// VoidLink
|
||||||
|
//
|
||||||
|
// Created by Weimin on 2025/10/6.
|
||||||
|
// Copyright © 2025 True砖家 @ Bilibili. All rights reserved.
|
||||||
|
//
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
@objc class CountdownAlertController: NSObject {
|
||||||
|
|
||||||
|
/// 类方法,直接在任何 UIViewController 上显示倒计时弹窗
|
||||||
|
/// - Parameters:
|
||||||
|
/// - viewController: 要显示弹窗的控制器
|
||||||
|
/// - title: 弹窗标题
|
||||||
|
/// - message: 弹窗内容
|
||||||
|
/// - buttonTitle: 按钮初始文字(倒计时结束后显示)
|
||||||
|
/// - countdown: 倒计时秒数
|
||||||
|
/// - completion: 点击确认或倒计时结束的回调
|
||||||
|
|
||||||
|
@objc public static var actionCancelled:Bool = false
|
||||||
|
|
||||||
|
@objc class func showAlert(
|
||||||
|
in viewController: UIViewController,
|
||||||
|
title: String?,
|
||||||
|
message: String?,
|
||||||
|
withCancel: Bool,
|
||||||
|
buttonTitle: String,
|
||||||
|
countdown: Int,
|
||||||
|
completion: (() -> Void)? = nil
|
||||||
|
) {
|
||||||
|
var remainingSeconds = countdown
|
||||||
|
|
||||||
|
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||||
|
|
||||||
|
let confirmAction = UIAlertAction(title: "\(remainingSeconds)", style: .default) { _ in
|
||||||
|
actionCancelled = false
|
||||||
|
completion?()
|
||||||
|
}
|
||||||
|
|
||||||
|
let cancelAction = UIAlertAction(title: SwiftLocalizationHelper.localizedString(forKey: "Cancel"), style: .cancel) { _ in
|
||||||
|
actionCancelled = true
|
||||||
|
completion?()
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmAction.isEnabled = false
|
||||||
|
|
||||||
|
if withCancel {alert.addAction(cancelAction)}
|
||||||
|
alert.addAction(confirmAction)
|
||||||
|
|
||||||
|
viewController.present(alert, animated: true, completion: nil)
|
||||||
|
|
||||||
|
// 倒计时
|
||||||
|
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
|
||||||
|
timer.schedule(deadline: .now(), repeating: 1.0, leeway: .milliseconds(50))
|
||||||
|
timer.setEventHandler {
|
||||||
|
remainingSeconds -= 1
|
||||||
|
|
||||||
|
if remainingSeconds <= 0 {
|
||||||
|
timer.cancel()
|
||||||
|
confirmAction.isEnabled = true
|
||||||
|
confirmAction.setValue(buttonTitle, forKey: "title")
|
||||||
|
} else {
|
||||||
|
confirmAction.setValue("\(remainingSeconds)", forKey: "title")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,6 +42,9 @@
|
|||||||
@property (nonatomic, retain) NSNumber * resolutionSelected;
|
@property (nonatomic, retain) NSNumber * resolutionSelected;
|
||||||
@property (nonatomic, retain) NSNumber * externalDisplayMode;
|
@property (nonatomic, retain) NSNumber * externalDisplayMode;
|
||||||
@property (nonatomic, retain) NSNumber * localMousePointerMode;
|
@property (nonatomic, retain) NSNumber * localMousePointerMode;
|
||||||
|
@property (nonatomic, retain) NSNumber * gyroBiasX;
|
||||||
|
@property (nonatomic, retain) NSNumber * gyroBiasY;
|
||||||
|
@property (nonatomic, retain) NSNumber * gyroBiasZ;
|
||||||
@property (nonatomic, retain) NSNumber * backgroundSessionTimer;
|
@property (nonatomic, retain) NSNumber * backgroundSessionTimer;
|
||||||
@property (nonatomic) enum {
|
@property (nonatomic) enum {
|
||||||
CODEC_PREF_AUTO,
|
CODEC_PREF_AUTO,
|
||||||
|
|||||||
@@ -130,6 +130,9 @@
|
|||||||
self.framePacingMode = settings.framePacingMode;
|
self.framePacingMode = settings.framePacingMode;
|
||||||
self.sendDummyEvent = settings.sendDummyEvent;
|
self.sendDummyEvent = settings.sendDummyEvent;
|
||||||
self.rememberFoldState = settings.rememberFoldState;
|
self.rememberFoldState = settings.rememberFoldState;
|
||||||
|
self.gyroBiasX = settings.gyroBiasX;
|
||||||
|
self.gyroBiasY = settings.gyroBiasY;
|
||||||
|
self.gyroBiasZ = settings.gyroBiasZ;
|
||||||
self.backgroundSessionTimer = settings.backroundSessionTimer;
|
self.backgroundSessionTimer = settings.backroundSessionTimer;
|
||||||
#endif
|
#endif
|
||||||
self.uniqueId = settings.uniqueId;
|
self.uniqueId = settings.uniqueId;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "gyroscope.svg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Generator: Apple Native CoreSVG 341-->
|
||||||
|
<!DOCTYPE svg
|
||||||
|
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 22.7531 22.4167">
|
||||||
|
<g>
|
||||||
|
<rect height="22.4167" opacity="0" width="22.7531" x="0" y="0"/>
|
||||||
|
<path d="M4.66382 17.7636C6.51148 19.6044 10.4016 18.2265 14.3104 14.3211C18.2231 10.4016 19.6044 6.51148 17.7568 4.67065C15.8832 2.7976 12.0151 4.17898 8.08824 8.09898C4.17557 12.0224 2.79027 15.8832 4.66382 17.7636ZM5.99819 16.4151C4.94393 15.3574 6.33263 12.5482 9.42944 9.44409C12.5521 6.3214 15.3647 4.94734 16.4151 6.0016C17.4694 7.04854 16.0841 9.85382 12.9687 12.9833C9.86457 16.0841 7.04854 17.4728 5.99819 16.4151ZM17.7456 17.7602C19.6118 15.8832 18.2304 12.0258 14.3104 8.09507C10.4016 4.17898 6.52613 2.7976 4.64917 4.66724C2.80493 6.51148 4.19022 10.4016 8.1024 14.3138C12.0151 18.2265 15.8979 19.5971 17.7456 17.7602ZM16.4044 16.4151C15.3608 17.4728 12.5482 16.0841 9.42944 12.9687C6.31798 9.85724 4.94393 7.04854 5.99086 6.0016C7.04121 4.94734 9.85724 6.3214 12.9687 9.44018C16.0767 12.5516 17.4694 15.3608 16.4044 16.4151ZM19.0764 4.59024L21.8772 1.7895C22.2092 1.45357 22.2092 0.882298 21.8772 0.550279C21.5271 0.203607 20.97 0.210933 20.6272 0.550279L17.7762 3.39741ZM3.61603 17.5507L0.550279 20.6272C0.200191 20.9665 0.210933 21.5237 0.550279 21.863C0.878882 22.2058 1.43941 22.2097 1.7895 21.8737L5.03247 18.6342ZM11.203 22.406C17.3909 22.406 22.4094 17.3875 22.4094 11.203C22.4094 5.01849 17.3909 0 11.203 0C5.01849 0 0 5.01849 0 11.203C0 17.3875 5.01849 22.406 11.203 22.406ZM11.203 19.9444C6.36936 19.9444 2.46154 16.0366 2.46154 11.203C2.46154 6.36936 6.36936 2.46154 11.203 2.46154C16.0366 2.46154 19.9478 6.36936 19.9478 11.203C19.9478 16.0366 16.0366 19.9444 11.203 19.9444Z" fill="white" fill-opacity="0.85"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -16,15 +16,31 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
#define DEFAULT_TEMPLATE_NAME1 @"控件模板 / Widget templates"
|
#define DEFAULT_TEMPLATE_NAME1 @"控件模板 / Widget templates"
|
||||||
#define DEFAULT_TEMPLATE_NAME2 @"旧版控件 / Legacy widgets"
|
#define DEFAULT_TEMPLATE_NAME2 @"旧版控件 / Legacy widgets"
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, MapGyroTo) {
|
||||||
|
mapGyroToMouse,
|
||||||
|
mapGyroToControllerStick,
|
||||||
|
driftCorrection
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This object contains information pertaining to any of the user created, custom on screen controller layout configurations, or 'profiles.' The object contains a 'name' property for easy reference, as well as an 'isSelected' property which is used to determine whether this particular custom OSC layout should show on screen during game stream view. Only one 'OSCProfile' is set to 'isSelected' at any given time. The object also contains an array of 'OnScreenButtonStates' which provides information that allows us to move and hide/unhide each of the 19 on screen buttons. Note that the 'buttonStates' property should contain an NSMutableArray of ENCODED 'OnScreenButtonState' objects. This allows us to save the 'OSCProfile' object to NSUserDefaults.
|
This object contains information pertaining to any of the user created, custom on screen controller layout configurations, or 'profiles.' The object contains a 'name' property for easy reference, as well as an 'isSelected' property which is used to determine whether this particular custom OSC layout should show on screen during game stream view. Only one 'OSCProfile' is set to 'isSelected' at any given time. The object also contains an array of 'OnScreenButtonStates' which provides information that allows us to move and hide/unhide each of the 19 on screen buttons. Note that the 'buttonStates' property should contain an NSMutableArray of ENCODED 'OnScreenButtonState' objects. This allows us to save the 'OSCProfile' object to NSUserDefaults.
|
||||||
Additionally the 'OSCProfile' object adopts encoding and decoding protocols so that we can encode the object before saving it to NSUserDefaults. By saving this object to NSUserDefaults we allow the user to save and load their custom on screen controller button layouts between app launches
|
Additionally the 'OSCProfile' object adopts encoding and decoding protocols so that we can encode the object before saving it to NSUserDefaults. By saving this object to NSUserDefaults we allow the user to save and load their custom on screen controller button layouts between app launches
|
||||||
*/
|
*/
|
||||||
@interface OSCProfile : NSObject <NSCoding, NSSecureCoding>
|
@interface OSCProfile : NSObject <NSCoding, NSSecureCoding, NSMutableCopying>
|
||||||
|
|
||||||
@property NSString *name;
|
@property NSString *name;
|
||||||
@property NSMutableArray <NSData *> *buttonStatesEncoded;
|
@property NSMutableArray <NSData *> *buttonStatesEncoded;
|
||||||
@property BOOL isSelected;
|
@property BOOL isSelected;
|
||||||
|
@property (nonatomic, assign) MapGyroTo mapGyroTo;
|
||||||
|
@property (nonatomic, assign) bool yawPitchToRightStick;
|
||||||
|
@property (nonatomic, assign) bool rollToLeftStick;
|
||||||
|
@property (nonatomic, assign) CGFloat gyroSensitivityYaw;
|
||||||
|
@property (nonatomic, assign) CGFloat gyroSensitivityPitch;
|
||||||
|
@property (nonatomic, assign) CGFloat gyroSensitivityRoll;
|
||||||
|
@property (nonatomic, assign) CGFloat accelSensitivityX;
|
||||||
|
@property (nonatomic, assign) CGFloat accelSensitivityY;
|
||||||
|
@property (nonatomic, assign) CGFloat accelSensitivityZ;
|
||||||
|
|
||||||
|
|
||||||
- (id) initWithName:(NSString*)name buttonStates:(NSMutableArray*)buttonStates isSelected:(BOOL)isSelected;
|
- (id) initWithName:(NSString*)name buttonStates:(NSMutableArray*)buttonStates isSelected:(BOOL)isSelected;
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,16 @@
|
|||||||
[encoder encodeObject:self.name forKey:@"name"];
|
[encoder encodeObject:self.name forKey:@"name"];
|
||||||
[encoder encodeObject:self.buttonStatesEncoded forKey:@"buttonStates"];
|
[encoder encodeObject:self.buttonStatesEncoded forKey:@"buttonStates"];
|
||||||
[encoder encodeBool:self.isSelected forKey:@"isSelected"];
|
[encoder encodeBool:self.isSelected forKey:@"isSelected"];
|
||||||
|
[encoder encodeInt64:self.mapGyroTo forKey:@"mapGyroTo"];
|
||||||
|
[encoder encodeBool:self.yawPitchToRightStick forKey:@"yawPitchToRightStick"];
|
||||||
|
[encoder encodeBool:self.rollToLeftStick forKey:@"rollToLeftStick"];
|
||||||
|
[encoder encodeInt64:self.mapGyroTo forKey:@"mapGyroTo"];
|
||||||
|
[encoder encodeFloat:self.gyroSensitivityYaw forKey:@"gyroSensitivityYaw"];
|
||||||
|
[encoder encodeFloat:self.gyroSensitivityPitch forKey:@"gyroSensitivityPitch"];
|
||||||
|
[encoder encodeFloat:self.gyroSensitivityRoll forKey:@"gyroSensitivityRoll"];
|
||||||
|
[encoder encodeFloat:self.accelSensitivityX forKey:@"accelSensitivityX"];
|
||||||
|
[encoder encodeFloat:self.accelSensitivityY forKey:@"accelSensitivityY"];
|
||||||
|
[encoder encodeFloat:self.accelSensitivityZ forKey:@"accelSensitivityZ"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id) initWithCoder:(NSCoder*)decoder {
|
- (id) initWithCoder:(NSCoder*)decoder {
|
||||||
@@ -38,9 +48,35 @@
|
|||||||
self.name = [decoder decodeObjectForKey:@"name"];
|
self.name = [decoder decodeObjectForKey:@"name"];
|
||||||
self.buttonStatesEncoded = [decoder decodeObjectForKey:@"buttonStates"];
|
self.buttonStatesEncoded = [decoder decodeObjectForKey:@"buttonStates"];
|
||||||
self.isSelected = [decoder decodeBoolForKey:@"isSelected"];
|
self.isSelected = [decoder decodeBoolForKey:@"isSelected"];
|
||||||
|
self.mapGyroTo = [decoder containsValueForKey:@"mapGyroTo"] ? [decoder decodeInt64ForKey:@"mapGyroTo"] : mapGyroToMouse;
|
||||||
|
self.yawPitchToRightStick = [decoder containsValueForKey:@"yawPitchToRightStick"] ? [decoder decodeBoolForKey:@"yawPitchToRightStick"] : true;
|
||||||
|
self.rollToLeftStick = [decoder containsValueForKey:@"rollToLeftStick"] ? [decoder decodeBoolForKey:@"rollToLeftStick"] : false;
|
||||||
|
self.gyroSensitivityYaw = [decoder containsValueForKey:@"gyroSensitivityYaw"] ? [decoder decodeFloatForKey:@"gyroSensitivityYaw"] : 1.0;
|
||||||
|
self.gyroSensitivityPitch = [decoder containsValueForKey:@"gyroSensitivityPitch"] ? [decoder decodeFloatForKey:@"gyroSensitivityPitch"] : 1.0;
|
||||||
|
self.gyroSensitivityRoll = [decoder containsValueForKey:@"gyroSensitivityRoll"] ? [decoder decodeFloatForKey:@"gyroSensitivityRoll"] : 1.0;
|
||||||
|
self.accelSensitivityX = [decoder containsValueForKey:@"accelSensitivityX"] ? [decoder decodeFloatForKey:@"accelSensitivityX"] : 1.0;
|
||||||
|
self.accelSensitivityY = [decoder containsValueForKey:@"accelSensitivityY"] ? [decoder decodeFloatForKey:@"accelSensitivityY"] : 1.0;
|
||||||
|
self.accelSensitivityZ = [decoder containsValueForKey:@"accelSensitivityZ"] ? [decoder decodeFloatForKey:@"accelSensitivityZ"] : 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
|
||||||
|
OSCProfile *copy = [[[self class] allocWithZone:zone] init];
|
||||||
|
copy.name = [self.name mutableCopy]; // NSString → NSMutableString
|
||||||
|
copy.buttonStatesEncoded = [[NSMutableArray alloc] initWithArray:self.buttonStatesEncoded copyItems:YES];
|
||||||
|
copy.isSelected = self.isSelected;
|
||||||
|
copy.mapGyroTo = self.mapGyroTo;
|
||||||
|
copy.yawPitchToRightStick = self.yawPitchToRightStick;
|
||||||
|
copy.rollToLeftStick = self.rollToLeftStick;
|
||||||
|
copy.gyroSensitivityYaw = self.gyroSensitivityYaw;
|
||||||
|
copy.gyroSensitivityPitch = self.gyroSensitivityPitch;
|
||||||
|
copy.gyroSensitivityRoll = self.gyroSensitivityRoll;
|
||||||
|
copy.accelSensitivityX = self.accelSensitivityX;
|
||||||
|
copy.accelSensitivityY = self.accelSensitivityY;
|
||||||
|
copy.accelSensitivityZ = self.accelSensitivityZ;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -45,10 +45,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* Returns the index of the 'selected' profile within the array it's in
|
* Returns the index of the 'selected' profile within the array it's in
|
||||||
*/
|
*/
|
||||||
- (NSInteger) getIndexOfSelectedProfile;
|
- (NSInteger) getIndexOfSelectedProfile;
|
||||||
- (void)replaceSelectedProfileWith:(OSCProfile*)newProfile;
|
- (void)replaceSelectedProfileWith:(OSCProfile*)newProfile overwriteDefault:(bool)overwriteDefault;
|
||||||
|
|
||||||
- (NSMutableArray *) getEncodedProfiles;
|
- (NSMutableArray *) getEncodedProfiles;
|
||||||
- (NSData* )getSelectedEncodedProfile;
|
// - (NSData* )getSelectedEncodedProfile;
|
||||||
- (void) importEncodedProfiles:(NSMutableArray* )profilesEncoded;
|
- (void) importEncodedProfiles:(NSMutableArray* )profilesEncoded;
|
||||||
- (OnScreenButtonState *)unarchiveButtonStateEncoded:(NSData *)data;
|
- (OnScreenButtonState *)unarchiveButtonStateEncoded:(NSData *)data;
|
||||||
- (void) importDefaultTemplates;
|
- (void) importDefaultTemplates;
|
||||||
@@ -62,7 +62,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
/**
|
/**
|
||||||
* Saves a profile object with a particular 'name' and an array of button layers (the CALayer button layers are the objects currently visible on screen) to persistent storage
|
* Saves a profile object with a particular 'name' and an array of button layers (the CALayer button layers are the objects currently visible on screen) to persistent storage
|
||||||
*/
|
*/
|
||||||
- (void) saveProfileWithName:(NSString*)name andButtonLayers:(NSMutableArray *)buttonLayers;
|
- (void) duplicateSelectedProfileWithName:(NSString*)name;
|
||||||
|
|
||||||
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers;
|
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ static CGRect layoutViewBounds;
|
|||||||
+ (OSCProfilesManager *) sharedManager:(CGRect)viewBounds {
|
+ (OSCProfilesManager *) sharedManager:(CGRect)viewBounds {
|
||||||
static OSCProfilesManager *_sharedManager = nil;
|
static OSCProfilesManager *_sharedManager = nil;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
layoutViewBounds = viewBounds;
|
if(!CGRectEqualToRect(viewBounds, CGRectZero)) layoutViewBounds = viewBounds;
|
||||||
// NSLog(@"bounds width: %f, height: %f", layoutViewBounds.size.width, layoutViewBounds.size.height);
|
// NSLog(@"bounds width: %f, height: %f", layoutViewBounds.size.width, layoutViewBounds.size.height);
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
_sharedManager = [[self alloc] init];
|
_sharedManager = [[self alloc] init];
|
||||||
@@ -105,7 +105,7 @@ static CGRect layoutViewBounds;
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)replaceSelectedProfileWith:(OSCProfile*)newProfile{
|
- (void)replaceSelectedProfileWith:(OSCProfile*)newProfile overwriteDefault:(bool)overwriteDefault{
|
||||||
NSInteger index = 0;
|
NSInteger index = 0;
|
||||||
NSMutableArray *profiles = [self getAllProfiles];
|
NSMutableArray *profiles = [self getAllProfiles];
|
||||||
for (OSCProfile *profile in profiles) {
|
for (OSCProfile *profile in profiles) {
|
||||||
@@ -114,6 +114,7 @@ static CGRect layoutViewBounds;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(index>0) profiles[index] = newProfile;
|
if(index>0) profiles[index] = newProfile;
|
||||||
|
else if(overwriteDefault) profiles[index] = newProfile;
|
||||||
|
|
||||||
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles];
|
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles];
|
||||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
|
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
|
||||||
@@ -226,14 +227,16 @@ static CGRect layoutViewBounds;
|
|||||||
|
|
||||||
- (OSCProfile *) getSelectedProfile {
|
- (OSCProfile *) getSelectedProfile {
|
||||||
NSMutableArray *profiles = [self getAllProfiles];
|
NSMutableArray *profiles = [self getAllProfiles];
|
||||||
|
if(profiles.count == 0){
|
||||||
|
[self importDefaultTemplates];
|
||||||
|
profiles = [self getAllProfiles];
|
||||||
|
}
|
||||||
for (OSCProfile *profile in profiles) {
|
for (OSCProfile *profile in profiles) {
|
||||||
|
|
||||||
if (profile.isSelected) {
|
if (profile.isSelected) {
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil;
|
return profiles[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger) getIndexOfSelectedProfile {
|
- (NSInteger) getIndexOfSelectedProfile {
|
||||||
@@ -274,47 +277,40 @@ static CGRect layoutViewBounds;
|
|||||||
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers {
|
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers {
|
||||||
NSMutableArray* buttonStatesEncoded = [self convertOnScreenControllerAndWidgetsToButtonStates:oscButtonLayers];
|
NSMutableArray* buttonStatesEncoded = [self convertOnScreenControllerAndWidgetsToButtonStates:oscButtonLayers];
|
||||||
if([self getIndexOfSelectedProfile]==0) return false;
|
if([self getIndexOfSelectedProfile]==0) return false;
|
||||||
|
/*
|
||||||
OSCProfile *newProfile = [[OSCProfile alloc] initWithName:[self getSelectedProfile].name
|
OSCProfile *newProfile = [[OSCProfile alloc] initWithName:[self getSelectedProfile].name
|
||||||
buttonStates:buttonStatesEncoded isSelected:YES]; // create a new 'OSCProfile'. Set the array of encoded button states created above to the 'buttonStates' property of the new profile, along with a 'name'. Set 'isSelected' argument to YES which will set this saved profile as the one that will show up in the game stream view
|
buttonStates:buttonStatesEncoded isSelected:YES]; // create a new 'OSCProfile'. Set the array of encoded button states created above to the 'buttonStates' property of the new profile, along with a 'name'. Set 'isSelected' argument to YES which will set this saved profile as the one that will show up in the game stream view
|
||||||
|
*/
|
||||||
|
OSCProfile *selectedProfile = [self getSelectedProfile];
|
||||||
[self replaceSelectedProfileWith:newProfile];
|
selectedProfile.buttonStatesEncoded = buttonStatesEncoded;
|
||||||
|
[self replaceSelectedProfileWith:selectedProfile overwriteDefault:NO];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void) saveProfileWithName:(NSString*)name andButtonLayers:(NSMutableArray *)oscButtonLayers {
|
- (void) duplicateSelectedProfileWithName:(NSString*)name {
|
||||||
NSMutableArray* buttonStatesEncoded = [self convertOnScreenControllerAndWidgetsToButtonStates:oscButtonLayers];
|
if ([self profileNameAlreadyExist:name]) return;
|
||||||
OSCProfile *newProfile = [[OSCProfile alloc] initWithName:name
|
|
||||||
buttonStates:buttonStatesEncoded isSelected:YES]; // create a new 'OSCProfile'. Set the array of encoded button states created above to the 'buttonStates' property of the new profile, along with a 'name'. Set 'isSelected' argument to YES which will set this saved profile as the one that will show up in the game stream view
|
|
||||||
/* set all saved OSCProfiles 'isSelected' property to NO since the new profile you're adding will be set as the selected profile */
|
|
||||||
NSMutableArray *profiles = [self getAllProfiles];
|
|
||||||
for (OSCProfile *profile in profiles) {
|
|
||||||
|
|
||||||
profile.isSelected = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([self profileNameAlreadyExist:name]) { // if a saved profile with the same 'name' already exists in persistent storage then overwrite it and save the change to persistent storage
|
|
||||||
[self replaceProfile:[self OSCProfileWithName:name] withProfile:newProfile];
|
|
||||||
}
|
|
||||||
else { // otherwise encode then add the new profile to the end of the OSCProfiles array
|
else { // otherwise encode then add the new profile to the end of the OSCProfiles array
|
||||||
NSData *newProfileEncoded = [NSKeyedArchiver archivedDataWithRootObject:newProfile requiringSecureCoding:YES error:nil];
|
NSMutableArray *profiles = [self getAllProfiles];
|
||||||
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles];
|
OSCProfile* newProfile = nil;
|
||||||
[profilesEncoded addObject:newProfileEncoded];
|
for(OSCProfile* profile in profiles){
|
||||||
|
if(profile.isSelected){
|
||||||
|
profile.isSelected = false;
|
||||||
|
newProfile = [profile mutableCopy];
|
||||||
|
newProfile.isSelected = true;
|
||||||
|
newProfile.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(newProfile){
|
||||||
|
NSData *newProfileEncoded = [NSKeyedArchiver archivedDataWithRootObject:newProfile requiringSecureCoding:YES error:nil];
|
||||||
|
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles];
|
||||||
|
[profilesEncoded addObject:newProfileEncoded];
|
||||||
|
|
||||||
/* Encode the 'profilesEncoded' array itself, NOT the objects in the 'profilesEncoded' array, all of which are already encoded by this point */
|
/* Encode the 'profilesEncoded' array itself, NOT the objects in the 'profilesEncoded' array, all of which are already encoded by this point */
|
||||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
|
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"OSCProfiles"];
|
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"OSCProfiles"];
|
||||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||||
|
}
|
||||||
// saving test:
|
|
||||||
/*
|
|
||||||
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
|
|
||||||
NSString *path = [documentsPath stringByAppendingPathComponent:@"newDefault.bin"];
|
|
||||||
|
|
||||||
[profiles writeToFile:path atomically:YES];
|
|
||||||
NSLog(@"默认 Profile 数据已保存到: %@", path);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,10 +70,8 @@
|
|||||||
self.isHidden = [decoder decodeBoolForKey:@"isHidden"];
|
self.isHidden = [decoder decodeBoolForKey:@"isHidden"];
|
||||||
self.widthFactor = [decoder decodeFloatForKey:@"widthFactor"];
|
self.widthFactor = [decoder decodeFloatForKey:@"widthFactor"];
|
||||||
self.heightFactor = [decoder decodeFloatForKey:@"heightFactor"];
|
self.heightFactor = [decoder decodeFloatForKey:@"heightFactor"];
|
||||||
self.sensitivityFactorX = [decoder decodeFloatForKey:@"sensitivityFactorX"];
|
self.sensitivityFactorX = [decoder containsValueForKey:@"sensitivityFactorX"] ? [decoder decodeFloatForKey:@"sensitivityFactorX"] : 1.0;
|
||||||
self.sensitivityFactorX = self.sensitivityFactorX == 0 ? 1.0 : self.sensitivityFactorX;
|
self.sensitivityFactorY = [decoder containsValueForKey:@"sensitivityFactorY"] ? [decoder decodeFloatForKey:@"sensitivityFactorY"] : 1.0;
|
||||||
self.sensitivityFactorY = [decoder decodeFloatForKey:@"sensitivityFactorY"];
|
|
||||||
self.sensitivityFactorY = self.sensitivityFactorY == 0 ? 1.0 : self.sensitivityFactorY;
|
|
||||||
self.decelerationRate = [decoder decodeFloatForKey:@"decelerationRate"];
|
self.decelerationRate = [decoder decodeFloatForKey:@"decelerationRate"];
|
||||||
self.stickIndicatorOffset = [decoder decodeFloatForKey:@"stickIndicatorOffset"];
|
self.stickIndicatorOffset = [decoder decodeFloatForKey:@"stickIndicatorOffset"];
|
||||||
self.oscLayerSizeFactor = [decoder decodeFloatForKey:@"oscLayerSizeFactor"];
|
self.oscLayerSizeFactor = [decoder decodeFloatForKey:@"oscLayerSizeFactor"];
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import CoreMotion
|
|||||||
func gyroMixInputStarted() -> Bool
|
func gyroMixInputStarted() -> Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc class MotionHandler: NSObject, OscInstanceProviderDelegate, OnScreenWidgetStickMixedInputDelegate{
|
@objc class MotionHandler: NSObject, OscInstanceReceiverDelegate, OnScreenWidgetStickMixedInputDelegate{
|
||||||
func mixRightStickAndGyroInput(x: CGFloat, y: CGFloat) {
|
func mixRightStickAndGyroInput(x: CGFloat, y: CGFloat) {
|
||||||
rightStickTouchInputX = x
|
rightStickTouchInputX = x
|
||||||
rightStickTouchInputY = y
|
rightStickTouchInputY = y
|
||||||
@@ -27,7 +27,7 @@ import CoreMotion
|
|||||||
}
|
}
|
||||||
|
|
||||||
func gyroMixInputStarted() -> Bool {
|
func gyroMixInputStarted() -> Bool {
|
||||||
return gyroControlStarted
|
return gyroIsWorking
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOnScreenControlsInstance(_ sender: Any!) {
|
func getOnScreenControlsInstance(_ sender: Any!) {
|
||||||
@@ -42,10 +42,19 @@ import CoreMotion
|
|||||||
static let shared = MotionHandler()
|
static let shared = MotionHandler()
|
||||||
|
|
||||||
@objc class func sharedInstance() -> MotionHandler {
|
@objc class func sharedInstance() -> MotionHandler {
|
||||||
return MotionHandler.shared
|
let sharedInstance = MotionHandler.shared
|
||||||
|
sharedInstance.oscProfile = sharedInstance.oscProfileMan.getSelectedProfile()
|
||||||
|
sharedInstance.sensitvityYaw = sharedInstance.oscProfile.gyroSensitivityYaw
|
||||||
|
sharedInstance.sensitvityPitch = sharedInstance.oscProfile.gyroSensitivityPitch
|
||||||
|
sharedInstance.sensitvityRoll = sharedInstance.oscProfile.gyroSensitivityRoll
|
||||||
|
return sharedInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var oscProfileMan: OSCProfilesManager
|
||||||
|
private var oscProfile: OSCProfile
|
||||||
|
|
||||||
private var gyroControlStarted: Bool = false
|
private var gyroControlStarted: Bool = false
|
||||||
|
private var gyroIsWorking: Bool = false
|
||||||
private var accelControlStarted: Bool = false
|
private var accelControlStarted: Bool = false
|
||||||
private let motionManager = CMMotionManager()
|
private let motionManager = CMMotionManager()
|
||||||
public var sensitvityYaw:CGFloat = 1.0
|
public var sensitvityYaw:CGFloat = 1.0
|
||||||
@@ -57,15 +66,26 @@ import CoreMotion
|
|||||||
public let stickMaxOffset: CGFloat = 0x7FFE
|
public let stickMaxOffset: CGFloat = 0x7FFE
|
||||||
private var stickInputScale: CGFloat = 35
|
private var stickInputScale: CGFloat = 35
|
||||||
|
|
||||||
var yaw:Double = 0
|
private var yaw:Double = 0
|
||||||
var pitch:Double = 0
|
private var pitch:Double = 0
|
||||||
var roll:Double = 0
|
private var roll:Double = 0
|
||||||
|
|
||||||
var rightStickTouchInputX:Double = 0
|
private var isCalibrating: Bool = false
|
||||||
var rightStickTouchInputY:Double = 0
|
private var sumX: Double = 0
|
||||||
|
private var sumY: Double = 0
|
||||||
|
private var sumZ: Double = 0
|
||||||
|
@objc public var gyroBiasX: Double = 0
|
||||||
|
@objc public var gyroBiasY: Double = 0
|
||||||
|
@objc public var gyroBiasZ: Double = 0
|
||||||
|
private var yawBias:Double = 0
|
||||||
|
private var pitchBias:Double = 0
|
||||||
|
private var rollBias:Double = 0
|
||||||
|
|
||||||
var leftStickTouchInputX:Double = 0
|
private var rightStickTouchInputX:Double = 0
|
||||||
var leftStickTouchInputY:Double = 0
|
private var rightStickTouchInputY:Double = 0
|
||||||
|
|
||||||
|
private var leftStickTouchInputX:Double = 0
|
||||||
|
private var leftStickTouchInputY:Double = 0
|
||||||
|
|
||||||
var updateInterval: TimeInterval = 1.0 / 120.0 {
|
var updateInterval: TimeInterval = 1.0 / 120.0 {
|
||||||
didSet {
|
didSet {
|
||||||
@@ -75,6 +95,8 @@ import CoreMotion
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc public override init() {
|
@objc public override init() {
|
||||||
|
self.oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||||
|
self.oscProfile = oscProfileMan.getSelectedProfile()
|
||||||
super.init()
|
super.init()
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
|
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
|
||||||
@@ -112,13 +134,16 @@ import CoreMotion
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 停止更新
|
/// 停止更新
|
||||||
@objc public func stopGyroUpdate() {
|
private var needInterruption:Bool = false
|
||||||
|
|
||||||
|
@objc public func stopGyroUpdate(interruption:Bool=false) {
|
||||||
gyroControlStarted = false
|
gyroControlStarted = false
|
||||||
|
gyroIsWorking = false
|
||||||
|
needInterruption = interruption
|
||||||
if motionManager.isGyroActive{
|
if motionManager.isGyroActive{
|
||||||
motionManager.stopGyroUpdates()
|
motionManager.stopGyroUpdates()
|
||||||
}
|
}
|
||||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
self.clearGyroInput(interruption: needInterruption)
|
||||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func stopAccelUpdate() {
|
@objc public func stopAccelUpdate() {
|
||||||
@@ -126,14 +151,13 @@ import CoreMotion
|
|||||||
if motionManager.isAccelerometerActive {
|
if motionManager.isAccelerometerActive {
|
||||||
motionManager.stopAccelerometerUpdates()
|
motionManager.stopAccelerometerUpdates()
|
||||||
}
|
}
|
||||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
// self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
// self.onScreenControls.clearRightStickTouchPadFlag()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - 私有方法处理数据
|
// MARK: - 私有方法处理数据
|
||||||
private func handleAccelerometerData(x: Double, y: Double, z: Double) {
|
private func handleAccelerometerData(x: Double, y: Double, z: Double) {
|
||||||
// 在这里处理加速度数据,比如计算方向、存储或驱动逻辑
|
// 在这里处理加速度数据,比如计算方向、存储或驱动逻辑
|
||||||
//print("加速度计: x=\(x), y=\(y), z=\(z)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleGyroData(x: Double, y: Double, z: Double) {
|
private func handleGyroData(x: Double, y: Double, z: Double) {
|
||||||
@@ -142,67 +166,109 @@ import CoreMotion
|
|||||||
var pitchSource:Double = 0
|
var pitchSource:Double = 0
|
||||||
var rollSource:Double = 0
|
var rollSource:Double = 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
let correctedX:Double = x - gyroBiasX
|
||||||
|
let correctedY:Double = y - gyroBiasY
|
||||||
|
let correctedZ:Double = z - gyroBiasZ */
|
||||||
|
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
let orientation = (windowScene as! UIWindowScene).interfaceOrientation
|
let orientation = (windowScene as! UIWindowScene).interfaceOrientation
|
||||||
print("界面方向变化: \(orientation)")
|
|
||||||
switch orientation {
|
switch orientation {
|
||||||
case .landscapeLeft:
|
case .landscapeLeft:
|
||||||
yawSource = x
|
yawBias = gyroBiasX
|
||||||
pitchSource = -y
|
pitchBias = -gyroBiasY
|
||||||
rollSource = -z
|
rollBias = -gyroBiasZ
|
||||||
|
yawSource = x-yawBias
|
||||||
|
pitchSource = -y-pitchBias
|
||||||
|
rollSource = -z-rollBias
|
||||||
case .landscapeRight:
|
case .landscapeRight:
|
||||||
yawSource = -x
|
yawBias = -gyroBiasX
|
||||||
pitchSource = y
|
pitchBias = gyroBiasY
|
||||||
rollSource = -z
|
rollBias = -gyroBiasZ
|
||||||
|
yawSource = -x-yawBias
|
||||||
|
pitchSource = y-pitchBias
|
||||||
|
rollSource = -z-rollBias
|
||||||
case .portrait:
|
case .portrait:
|
||||||
yawSource = -y
|
yawBias = -gyroBiasY
|
||||||
pitchSource = -x
|
pitchBias = -gyroBiasX
|
||||||
rollSource = -z
|
rollBias = -gyroBiasZ
|
||||||
|
yawSource = -y-yawBias
|
||||||
|
pitchSource = -x-pitchBias
|
||||||
|
rollSource = -z-rollBias
|
||||||
case .portraitUpsideDown:
|
case .portraitUpsideDown:
|
||||||
yawSource = y
|
yawBias = gyroBiasY
|
||||||
pitchSource = x
|
pitchBias = gyroBiasX
|
||||||
rollSource = -z
|
rollBias = -gyroBiasZ
|
||||||
|
yawSource = y-yawBias
|
||||||
|
pitchSource = x-pitchBias
|
||||||
|
rollSource = -z-rollBias
|
||||||
default:
|
default:
|
||||||
yawSource = x
|
yawBias = gyroBiasX
|
||||||
pitchSource = -y
|
pitchBias = -gyroBiasY
|
||||||
rollSource = -z
|
rollBias = -gyroBiasZ
|
||||||
|
yawSource = x-yawBias
|
||||||
|
pitchSource = -y-pitchBias
|
||||||
|
rollSource = -z-rollBias
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
yawSource = x
|
let orientation = UIApplication.shared.statusBarOrientation
|
||||||
pitchSource = -y
|
if orientation.isLandscape {
|
||||||
rollSource = z
|
yawBias = gyroBiasX
|
||||||
|
pitchBias = -gyroBiasY
|
||||||
|
rollBias = -gyroBiasZ
|
||||||
|
yawSource = x-yawBias
|
||||||
|
pitchSource = -y-pitchBias
|
||||||
|
rollSource = -z-rollBias
|
||||||
|
} else if orientation.isPortrait {
|
||||||
|
yawBias = -gyroBiasY
|
||||||
|
pitchBias = -gyroBiasX
|
||||||
|
rollBias = -gyroBiasZ
|
||||||
|
yawSource = -y-yawBias
|
||||||
|
pitchSource = -x-pitchBias
|
||||||
|
rollSource = -z-rollBias
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !gyroControlStarted {
|
if !gyroControlStarted {
|
||||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
self.clearGyroInput(interruption: needInterruption)
|
||||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
|
||||||
print("Gyro: stopped")
|
print("Gyro: stopped")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if true {
|
gyroIsWorking = true
|
||||||
yaw = rightStickTouchInputX + gyroInputToStickInput(input:yawSource*sensitvityYaw*10)
|
|
||||||
yaw = self.clampStickInput(input: yaw)
|
|
||||||
pitch = rightStickTouchInputY - gyroInputToStickInput(input:pitchSource*sensitvityPitch*10)
|
|
||||||
pitch = self.clampStickInput(input: pitch)
|
|
||||||
self.onScreenControls.sendRightStickTouchPadEvent(yaw, pitch)
|
|
||||||
}
|
|
||||||
|
|
||||||
if true {
|
if oscProfile.mapGyroTo == MapGyroTo.mapGyroToMouse {
|
||||||
roll = leftStickTouchInputX + gyroInputToStickInput(input:rollSource*sensitvityRoll*10)
|
|
||||||
roll = self.clampStickInput(input: roll)
|
|
||||||
self.onScreenControls.sendLeftStickTouchPadEvent(roll, leftStickTouchInputY)
|
|
||||||
}
|
|
||||||
|
|
||||||
if false {
|
|
||||||
yaw = yawSource*sensitvityYaw*30
|
yaw = yawSource*sensitvityYaw*30
|
||||||
pitch = pitchSource*sensitvityPitch*30
|
pitch = pitchSource*sensitvityPitch*30
|
||||||
LiSendMouseMoveEvent(Int16(yaw),Int16(pitch))
|
LiSendMouseMoveEvent(Int16(yaw),Int16(pitch))
|
||||||
}
|
}
|
||||||
|
|
||||||
if false {
|
if oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick {
|
||||||
roll = rollSource*sensitvityPitch*100
|
if oscProfile.yawPitchToRightStick {
|
||||||
LiSendMouseMoveEvent(Int16(roll),0)
|
yaw = rightStickTouchInputX + gyroInputToStickInput(input:yawSource*sensitvityYaw*10)
|
||||||
|
yaw = self.clampStickInput(input: yaw)
|
||||||
|
pitch = rightStickTouchInputY - gyroInputToStickInput(input:pitchSource*sensitvityPitch*10)
|
||||||
|
pitch = self.clampStickInput(input: pitch)
|
||||||
|
self.onScreenControls.sendRightStickTouchPadEvent(yaw, pitch)
|
||||||
|
}
|
||||||
|
if oscProfile.rollToLeftStick {
|
||||||
|
roll = leftStickTouchInputX + gyroInputToStickInput(input:rollSource*sensitvityRoll*10)
|
||||||
|
roll = self.clampStickInput(input: roll)
|
||||||
|
self.onScreenControls.sendLeftStickTouchPadEvent(roll, leftStickTouchInputY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func clearGyroInput(interruption:Bool){
|
||||||
|
if oscProfile.yawPitchToRightStick{
|
||||||
|
self.onScreenControls.sendRightStickTouchPadEvent(rightStickTouchInputX-yawBias, rightStickTouchInputY-pitchBias)
|
||||||
|
}
|
||||||
|
if oscProfile.rollToLeftStick{
|
||||||
|
self.onScreenControls.sendLeftStickTouchPadEvent(leftStickTouchInputX-rollBias, leftStickTouchInputY)
|
||||||
|
}
|
||||||
|
if(interruption){
|
||||||
|
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||||
|
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,4 +279,33 @@ import CoreMotion
|
|||||||
private func gyroInputToStickInput(input: CGFloat) -> CGFloat{
|
private func gyroInputToStickInput(input: CGFloat) -> CGFloat{
|
||||||
return self.clampStickInput(input: stickMaxOffset * input / 8)
|
return self.clampStickInput(input: stickMaxOffset * input / 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc public func calibrateGyroBias(duration: TimeInterval = 5.0, completion: @escaping () -> Void) {
|
||||||
|
guard motionManager.isGyroAvailable else { return }
|
||||||
|
|
||||||
|
isCalibrating = true
|
||||||
|
var sumX = 0.0, sumY = 0.0, sumZ = 0.0
|
||||||
|
var sampleCount = 0
|
||||||
|
|
||||||
|
motionManager.startGyroUpdates(to: .main) { [weak self] gyroData, _ in
|
||||||
|
guard let self = self, self.isCalibrating, let data = gyroData else { return }
|
||||||
|
sumX += data.rotationRate.x
|
||||||
|
sumY += data.rotationRate.y
|
||||||
|
sumZ += data.rotationRate.z
|
||||||
|
sampleCount += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5秒后计算平均值
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + duration) { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.motionManager.stopGyroUpdates()
|
||||||
|
if sampleCount > 0 {
|
||||||
|
gyroBiasX = sumX / Double(sampleCount)
|
||||||
|
gyroBiasY = sumY / Double(sampleCount)
|
||||||
|
gyroBiasZ = sumZ / Double(sampleCount)
|
||||||
|
}
|
||||||
|
self.isCalibrating = false
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
@class StreamConfiguration;
|
@class StreamConfiguration;
|
||||||
|
|
||||||
// sending self as an instance to OnScreenWidgetView or other related classes
|
// sending self as an instance to OnScreenWidgetView or other related classes
|
||||||
@protocol OscInstanceProviderDelegate <NSObject>
|
@protocol OscInstanceReceiverDelegate <NSObject>
|
||||||
- (void)getOnScreenControlsInstance:(id)sender; // Renamed delegate method
|
- (void)getOnScreenControlsInstance:(id)sender; // Renamed delegate method
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ typedef NS_ENUM(NSInteger, OnScreenControlsLevel) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@property (nonatomic, weak) id<OscInstanceProviderDelegate> instanceReceiverDelegate; // Delegate property
|
@property (nonatomic, weak) id<OscInstanceReceiverDelegate> instanceReceiverDelegate; // Delegate property
|
||||||
- (void)sendInstance;
|
- (void)sendInstance;
|
||||||
|
|
||||||
// @property (nonatomic, assign) CustomTapGestureRecognizer* mouseRightClickTapRecognizer; // this object will be passed to onscreencontrols class for areVirtualControllerTaps flag setting
|
// @property (nonatomic, assign) CustomTapGestureRecognizer* mouseRightClickTapRecognizer; // this object will be passed to onscreencontrols class for areVirtualControllerTaps flag setting
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@objc class OnScreenWidgetView: UIView, OscInstanceProviderDelegate {
|
@objc class OnScreenWidgetView: UIView, OscInstanceReceiverDelegate {
|
||||||
// receiving the OnScreenControls instance from delegate
|
// receiving the OnScreenControls instance from delegate
|
||||||
@objc func getOnScreenControlsInstance(_ sender: Any) {
|
@objc func getOnScreenControlsInstance(_ sender: Any) {
|
||||||
if let controls = sender as? OnScreenControls {
|
if let controls = sender as? OnScreenControls {
|
||||||
@@ -35,8 +35,8 @@ import UIKit
|
|||||||
@objc protocol OnScreenFunctionalButtonDelegate: AnyObject {
|
@objc protocol OnScreenFunctionalButtonDelegate: AnyObject {
|
||||||
func startGyroUpdate()
|
func startGyroUpdate()
|
||||||
func startAccelUpdate()
|
func startAccelUpdate()
|
||||||
func stopGyroUpdate()
|
func stopGyroUpdate(interruption:Bool)
|
||||||
func stopAccelUpdate()
|
func stopAccelUpdate(interruption:Bool)
|
||||||
func openWidgetLayoutTool()
|
func openWidgetLayoutTool()
|
||||||
func switchWidgetProfile()
|
func switchWidgetProfile()
|
||||||
func bringUpSoftKeyboard()
|
func bringUpSoftKeyboard()
|
||||||
@@ -51,6 +51,9 @@ import UIKit
|
|||||||
case touchPad
|
case touchPad
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let oscProfileMan: OSCProfilesManager = OSCProfilesManager.sharedManager(CGRectZero)
|
||||||
|
private var oscProfile: OSCProfile
|
||||||
|
|
||||||
@objc public var widgetType: WidgetTypeEnum = WidgetTypeEnum.uninitialized
|
@objc public var widgetType: WidgetTypeEnum = WidgetTypeEnum.uninitialized
|
||||||
|
|
||||||
@objc static public var editMode: Bool = false
|
@objc static public var editMode: Bool = false
|
||||||
@@ -290,6 +293,7 @@ import UIKit
|
|||||||
self.pointerIdPool.insert(UInt32(i))
|
self.pointerIdPool.insert(UInt32(i))
|
||||||
}
|
}
|
||||||
self.activePointerIds = []
|
self.activePointerIds = []
|
||||||
|
self.oscProfile = oscProfileMan.getSelectedProfile()
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
|
|
||||||
upIndicator = createLrudDirectionLayer()
|
upIndicator = createLrudDirectionLayer()
|
||||||
@@ -1189,12 +1193,13 @@ import UIKit
|
|||||||
// vertical input must be inverted
|
// vertical input must be inverted
|
||||||
targetX = (targetX >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetX/self.stickMaxOffset)
|
targetX = (targetX >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetX/self.stickMaxOffset)
|
||||||
targetY = (targetY >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetY/self.stickMaxOffset)
|
targetY = (targetY >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetY/self.stickMaxOffset)
|
||||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
|
||||||
|
let mixRightStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick
|
||||||
|
&& oscProfile.yawPitchToRightStick)
|
||||||
|
if !mixRightStickInputToGyro || (self.mixInputDelegate?.gyroMixInputStarted() != true) {
|
||||||
self.onScreenControls.sendRightStickTouchPadEvent(targetX, targetY)
|
self.onScreenControls.sendRightStickTouchPadEvent(targetX, targetY)
|
||||||
}
|
}
|
||||||
else{
|
self.mixInputDelegate?.mixRightStickAndGyroInput(x: targetX, y: targetY)
|
||||||
self.mixInputDelegate?.mixRightStickAndGyroInput(x: targetX, y: targetY)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func sendLeftStickTouchPadEvent(inputX: CGFloat, inputY: CGFloat){
|
private func sendLeftStickTouchPadEvent(inputX: CGFloat, inputY: CGFloat){
|
||||||
@@ -1202,12 +1207,13 @@ import UIKit
|
|||||||
var targetY = -self.touchInputToStickInput(input: inputY)
|
var targetY = -self.touchInputToStickInput(input: inputY)
|
||||||
targetX = (targetX >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetX/self.stickMaxOffset)
|
targetX = (targetX >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetX/self.stickMaxOffset)
|
||||||
targetY = (targetY >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetY/self.stickMaxOffset)
|
targetY = (targetY >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetY/self.stickMaxOffset)
|
||||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
|
||||||
|
let mixLeftStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick
|
||||||
|
&& oscProfile.rollToLeftStick)
|
||||||
|
if !mixLeftStickInputToGyro || (self.mixInputDelegate?.gyroMixInputStarted() != true) {
|
||||||
self.onScreenControls.sendLeftStickTouchPadEvent(targetX, targetY)
|
self.onScreenControls.sendLeftStickTouchPadEvent(targetX, targetY)
|
||||||
}
|
}
|
||||||
else{
|
self.mixInputDelegate?.mixLeftStickAndGyroInput(x: targetX, y: targetY)
|
||||||
self.mixInputDelegate?.mixLeftStickAndGyroInput(x: targetX, y: targetY)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//==========================================================================================================
|
//==========================================================================================================
|
||||||
|
|
||||||
@@ -1656,33 +1662,33 @@ import UIKit
|
|||||||
if longPressed, buttonMode == ButtonMode.movable.rawValue {break}
|
if longPressed, buttonMode == ButtonMode.movable.rawValue {break}
|
||||||
NotificationCenter.default.post(name: Notification.Name("SettingsOverlayButtonPressedNotification"), object:nil) // inform layout tool controller to fetch button size factors. self will be passed as the object of the notification
|
NotificationCenter.default.post(name: Notification.Name("SettingsOverlayButtonPressedNotification"), object:nil) // inform layout tool controller to fetch button size factors. self will be passed as the object of the notification
|
||||||
case "GYRO":
|
case "GYRO":
|
||||||
self.functionalButtonDelegate?.stopGyroUpdate()
|
self.functionalButtonDelegate?.stopGyroUpdate(interruption: false)
|
||||||
case "ACCEL":
|
case "ACCEL":
|
||||||
self.functionalButtonDelegate?.stopAccelUpdate()
|
self.functionalButtonDelegate?.stopAccelUpdate(interruption: false)
|
||||||
case "MOTION":
|
case "MOTION":
|
||||||
self.functionalButtonDelegate?.stopGyroUpdate()
|
self.functionalButtonDelegate?.stopGyroUpdate(interruption: false)
|
||||||
self.functionalButtonDelegate?.stopAccelUpdate()
|
self.functionalButtonDelegate?.stopAccelUpdate(interruption: false)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func clearRightStickTouchPadFlag(){
|
private func clearRightStickTouchPadFlag(){
|
||||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
let mixRightStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick
|
||||||
|
&& oscProfile.yawPitchToRightStick)
|
||||||
|
if !mixRightStickInputToGyro || self.mixInputDelegate?.gyroMixInputStarted() != true {
|
||||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||||
}
|
}
|
||||||
else{
|
self.mixInputDelegate?.mixRightStickAndGyroInput(x: 0, y: 0)
|
||||||
self.mixInputDelegate?.mixRightStickAndGyroInput(x: 0, y: 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func clearLeftStickTouchPadFlag(){
|
private func clearLeftStickTouchPadFlag(){
|
||||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
let mixLeftStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick
|
||||||
|
&& oscProfile.rollToLeftStick)
|
||||||
|
if !mixLeftStickInputToGyro || self.mixInputDelegate?.gyroMixInputStarted() != true {
|
||||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||||
}
|
}
|
||||||
else{
|
self.mixInputDelegate?.mixLeftStickAndGyroInput(x: 0, y: 0)
|
||||||
self.mixInputDelegate?.mixLeftStickAndGyroInput(x: 0, y: 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||||
@@ -1844,11 +1850,11 @@ import UIKit
|
|||||||
override func didMoveToSuperview() {
|
override func didMoveToSuperview() {
|
||||||
super.didMoveToSuperview()
|
super.didMoveToSuperview()
|
||||||
if superview == nil {
|
if superview == nil {
|
||||||
if self.functionalButtonString == "GYRO" {self.functionalButtonDelegate?.stopGyroUpdate()}
|
if self.functionalButtonString == "GYRO" {self.functionalButtonDelegate?.stopGyroUpdate(interruption: true)}
|
||||||
if self.functionalButtonString == "ACCEL" {self.functionalButtonDelegate?.stopAccelUpdate()}
|
if self.functionalButtonString == "ACCEL" {self.functionalButtonDelegate?.stopAccelUpdate(interruption: true)}
|
||||||
if self.functionalButtonString == "MOTION" {
|
if self.functionalButtonString == "MOTION" {
|
||||||
self.functionalButtonDelegate?.stopGyroUpdate()
|
self.functionalButtonDelegate?.stopGyroUpdate(interruption: true)
|
||||||
self.functionalButtonDelegate?.stopAccelUpdate()
|
self.functionalButtonDelegate?.stopAccelUpdate(interruption: true)
|
||||||
}
|
}
|
||||||
buttonDownVisualEffectLayer.borderWidth = 0
|
buttonDownVisualEffectLayer.borderWidth = 0
|
||||||
buttonDownVisualEffectLayer.borderColor = defaultBorderColor
|
buttonDownVisualEffectLayer.borderColor = defaultBorderColor
|
||||||
|
|||||||
+11
-10
@@ -76,7 +76,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
UIKeyModifierFlags comboKeyModifierFlags;
|
UIKeyModifierFlags comboKeyModifierFlags;
|
||||||
|
|
||||||
WidgetSizeTransition _widgetSizeTransition;
|
WidgetSizeTransition _widgetSizeTransition;
|
||||||
OSCProfilesManager* profilesManager;
|
OSCProfilesManager* oscProfileMan;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setupStreamView:(ControllerSupport*)controllerSupport
|
- (void) setupStreamView:(ControllerSupport*)controllerSupport
|
||||||
@@ -499,21 +499,21 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
|
|
||||||
if(relocatedWidgetDict.allKeys.count == 0) return;
|
if(relocatedWidgetDict.allKeys.count == 0) return;
|
||||||
|
|
||||||
profilesManager = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
oscProfileMan = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
||||||
OSCProfile *newProfile = [profilesManager getSelectedProfile];
|
OSCProfile *newProfile = [oscProfileMan getSelectedProfile];
|
||||||
|
|
||||||
for (NSInteger i = 0; i < newProfile.buttonStatesEncoded.count; i++) {
|
for (NSInteger i = 0; i < newProfile.buttonStatesEncoded.count; i++) {
|
||||||
NSData* buttonStateEncoded = newProfile.buttonStatesEncoded[i];
|
NSData* buttonStateEncoded = newProfile.buttonStatesEncoded[i];
|
||||||
OnScreenButtonState *newButtonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
OnScreenButtonState *newButtonState = [oscProfileMan unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||||
if([relocatedWidgetDict.allKeys containsObject:newButtonState.identifier]){
|
if([relocatedWidgetDict.allKeys containsObject:newButtonState.identifier]){
|
||||||
OnScreenWidgetView* widget = [relocatedWidgetDict objectForKey:newButtonState.identifier];
|
OnScreenWidgetView* widget = [relocatedWidgetDict objectForKey:newButtonState.identifier];
|
||||||
newButtonState.position = [profilesManager normalizeWidgetPosition:widget.center];
|
newButtonState.position = [oscProfileMan normalizeWidgetPosition:widget.center];
|
||||||
NSData *newButtonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:newButtonState requiringSecureCoding:YES error:nil];
|
NSData *newButtonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:newButtonState requiringSecureCoding:YES error:nil];
|
||||||
newProfile.buttonStatesEncoded[i] = newButtonStateEncoded;
|
newProfile.buttonStatesEncoded[i] = newButtonStateEncoded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[profilesManager replaceSelectedProfileWith:newProfile];
|
[oscProfileMan replaceSelectedProfileWith:newProfile overwriteDefault:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) reloadOnScreenWidgetViews{
|
- (void) reloadOnScreenWidgetViews{
|
||||||
@@ -532,13 +532,14 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
|
|
||||||
OnScreenWidgetView.buttonVisualFeedbackEnabled = settings.buttonVisualFeedback;
|
OnScreenWidgetView.buttonVisualFeedbackEnabled = settings.buttonVisualFeedback;
|
||||||
|
|
||||||
if(!profilesManager) profilesManager = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
if(!oscProfileMan) oscProfileMan = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
||||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
OSCProfile *oscProfile = [oscProfileMan getSelectedProfile]; //returns the currently selected OSCProfile
|
||||||
|
MotionHandler* motionHandler = [MotionHandler sharedInstance];
|
||||||
|
|
||||||
if(!OnScreenWidgetView.editMode){ // in edit mode, keyboard widget view will be updated within layoutool view controller.
|
if(!OnScreenWidgetView.editMode){ // in edit mode, keyboard widget view will be updated within layoutool view controller.
|
||||||
|
|
||||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
OnScreenButtonState* buttonState = [oscProfileMan unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||||
if(buttonState.widgetType == CustomOnScreenWidget){
|
if(buttonState.widgetType == CustomOnScreenWidget){
|
||||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:buttonState.name buttonLabel:buttonState.alias shape:buttonState.widgetShape]; //reconstruct widgetView
|
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:buttonState.name buttonLabel:buttonState.alias shape:buttonState.widgetShape]; //reconstruct widgetView
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@@ -547,7 +548,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
widgetView.functionalButtonDelegate = (id<OnScreenFunctionalButtonDelegate>)streamFrameVC;
|
widgetView.functionalButtonDelegate = (id<OnScreenFunctionalButtonDelegate>)streamFrameVC;
|
||||||
widgetView.mixInputDelegate = (id<OnScreenWidgetStickMixedInputDelegate>) [MotionHandler sharedInstance];
|
widgetView.mixInputDelegate = (id<OnScreenWidgetStickMixedInputDelegate>)motionHandler;
|
||||||
|
|
||||||
widgetView.identifier = buttonState.identifier;
|
widgetView.identifier = buttonState.identifier;
|
||||||
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||||
|
|||||||
@@ -38,6 +38,9 @@
|
|||||||
<attribute name="frameQueueSize" optional="YES" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="frameQueueSize" optional="YES" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||||
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
|
||||||
<attribute name="graphOpacity" optional="YES" attributeType="Integer 32" defaultValueString="50" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="graphOpacity" optional="YES" attributeType="Integer 32" defaultValueString="50" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
<attribute name="gyroBiasX" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
<attribute name="gyroBiasY" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
<attribute name="gyroBiasZ" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||||
<attribute name="gyroMode" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="gyroMode" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||||
<attribute name="gyroSensitivity" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="gyroSensitivity" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||||
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
|||||||
@@ -1025,6 +1025,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Calibrating..." : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "正在校准…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "正在校準…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "正在校準…"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Cancel" : {
|
"Cancel" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -1583,6 +1606,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Drift Correction" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "漂移修正"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "漂移修正"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "漂移修正"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Edit" : {
|
"Edit" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -2027,6 +2073,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Finished!" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "完成!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "完成!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "完成!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"First Launch Tips" : {
|
"First Launch Tips" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -3013,6 +3082,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Motion Control" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "体感控制"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "體感控制"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "體感控制"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Mouse Pointer Velocity: %d%%" : {
|
"Mouse Pointer Velocity: %d%%" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -3843,6 +3935,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Place the device flat on a surface and keep it still, then tap ‘Start’." : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "将设备静置于平面上,再点“开始”。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "將裝置靜置於平面上,然後點「開始」。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "將裝置靜置於平面上,然後點「開始」。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Pofiles imported" : {
|
"Pofiles imported" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -5094,6 +5209,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Start" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "开始"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "開始"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "開始"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Stereo (surround sound available for iOS18+)" : {
|
"Stereo (surround sound available for iOS18+)" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -5169,6 +5307,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Tap 'OK' to set on-screen widget to 'Custom' and enable motion control." : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "点选 ‘确定’ 开启自定义屏显控件与体感控制。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "點選「確定」以開啟自訂螢幕小工具與體感控制。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "點選「確定」以開啟自訂螢幕小工具與體感控制。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Tap %d Fingers to Change OSC Layout in Stream View" : {
|
"Tap %d Fingers to Change OSC Layout in Stream View" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@@ -22,11 +22,14 @@
|
|||||||
@property (class, nonatomic, assign) BOOL overridePersistedFoldState;
|
@property (class, nonatomic, assign) BOOL overridePersistedFoldState;
|
||||||
|
|
||||||
// 外部可访问属性
|
// 外部可访问属性
|
||||||
|
@property (nonatomic, strong) UIStackView *rootStackView;
|
||||||
@property (nonatomic, assign) CGFloat leadingTrailingPadding;
|
@property (nonatomic, assign) CGFloat leadingTrailingPadding;
|
||||||
@property (nonatomic, assign) CGFloat separatorLinePadding;
|
@property (nonatomic, assign) CGFloat separatorLinePadding;
|
||||||
@property (nonatomic, copy) NSString *sectionTitle;
|
@property (nonatomic, copy) NSString *sectionTitle;
|
||||||
@property (nonatomic, copy) NSString *identifier;
|
@property (nonatomic, copy) NSString *identifier;
|
||||||
@property (nonatomic, assign) BOOL isExpanded;
|
@property (nonatomic, assign) BOOL isExpanded;
|
||||||
|
@property (nonatomic, assign) BOOL expandable;
|
||||||
|
@property (nonatomic, copy) void (^lockedSectionHandler)(void);
|
||||||
@property (nonatomic, strong) UIColor *backgroundColor;
|
@property (nonatomic, strong) UIColor *backgroundColor;
|
||||||
@property (nonatomic, assign) CGFloat rootStackViewSpacing;
|
@property (nonatomic, assign) CGFloat rootStackViewSpacing;
|
||||||
@property (nonatomic, assign) CGFloat headerViewHeight;
|
@property (nonatomic, assign) CGFloat headerViewHeight;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
@interface MenuSectionView ()
|
@interface MenuSectionView ()
|
||||||
|
|
||||||
@property (nonatomic, strong) UIStackView *rootStackView;
|
|
||||||
@property (nonatomic, strong) UILabel *titleLabel;
|
@property (nonatomic, strong) UILabel *titleLabel;
|
||||||
@property (nonatomic, strong) UIButton *toggleButton;
|
@property (nonatomic, strong) UIButton *toggleButton;
|
||||||
@property (nonatomic, strong) UIButton *toggleArea;
|
@property (nonatomic, strong) UIButton *toggleArea;
|
||||||
@@ -57,6 +56,7 @@ static BOOL overridePersistedFoldState = YES;
|
|||||||
_separatorLinePadding = 40;
|
_separatorLinePadding = 40;
|
||||||
_sectionTitle = @"Section";
|
_sectionTitle = @"Section";
|
||||||
_isExpanded = YES;
|
_isExpanded = YES;
|
||||||
|
_expandable = true;
|
||||||
_backgroundColor = [UIColor clearColor];
|
_backgroundColor = [UIColor clearColor];
|
||||||
_rootStackViewSpacing = [self isIPhone] ? 10 : 13.8;
|
_rootStackViewSpacing = [self isIPhone] ? 10 : 13.8;
|
||||||
_subStackViews = [NSMutableArray array];
|
_subStackViews = [NSMutableArray array];
|
||||||
@@ -235,7 +235,7 @@ static BOOL overridePersistedFoldState = YES;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)setExpanded:(BOOL)isExpanded {
|
- (void)setExpanded:(BOOL)isExpanded {
|
||||||
self.isExpanded = isExpanded;
|
self.isExpanded = !_expandable ? false : isExpanded;
|
||||||
[self updateViewForFoldState];
|
[self updateViewForFoldState];
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:self.isExpanded forKey:self.identifier];
|
[[NSUserDefaults standardUserDefaults] setBool:self.isExpanded forKey:self.identifier];
|
||||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||||
@@ -323,8 +323,9 @@ static BOOL overridePersistedFoldState = YES;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)toggleFold {
|
- (void)toggleFold {
|
||||||
self.isExpanded = !self.isExpanded;
|
self.isExpanded = !_expandable ? false : !self.isExpanded;
|
||||||
[self updateViewForFoldState];
|
[self updateViewForFoldState];
|
||||||
|
if(!_expandable) self.lockedSectionHandler();
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:self.isExpanded forKey:self.identifier];
|
[[NSUserDefaults standardUserDefaults] setBool:self.isExpanded forKey:self.identifier];
|
||||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -1270,10 +1270,12 @@
|
|||||||
self->_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
|
self->_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
|
||||||
|
|
||||||
//this part is just for registration, will not be immediately executed.
|
//this part is just for registration, will not be immediately executed.
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
self->_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC profile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
|
self->_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC profile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
|
||||||
NSLog(@"profile profile");
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||||
[self reloadLegacyOnScreenControls];
|
if (!strongSelf) return;
|
||||||
self->_oscProfilesTableViewController.currentOSCButtonLayers = self.layoutOSC.OSCButtonLayers; //pass updated OSCLayout to OSCProfileTableView again
|
[weakSelf reloadLegacyOnScreenControls];
|
||||||
|
strongSelf->_oscProfilesTableViewController.currentOSCButtonLayers = weakSelf.layoutOSC.OSCButtonLayers; //pass updated OSCLayout to OSCProfileTableView again
|
||||||
};
|
};
|
||||||
|
|
||||||
[self.oscProfilesTableViewController profileViewRefresh]; // execute this will make sure OSCLayout is updated from persisted profile, not any cache.
|
[self.oscProfilesTableViewController profileViewRefresh]; // execute this will make sure OSCLayout is updated from persisted profile, not any cache.
|
||||||
@@ -1298,8 +1300,9 @@
|
|||||||
_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
|
_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
|
||||||
_oscProfilesTableViewController.layoutViewBounds = self.view.bounds;
|
_oscProfilesTableViewController.layoutViewBounds = self.view.bounds;
|
||||||
|
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC ofile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the proffile and then hide/show and move each OSC button to their appropriate position
|
_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC ofile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the proffile and then hide/show and move each OSC button to their appropriate position
|
||||||
[self reloadLegacyOnScreenControls];
|
[weakSelf reloadLegacyOnScreenControls];
|
||||||
};
|
};
|
||||||
|
|
||||||
self.widgetPanelStack.hidden = YES;
|
self.widgetPanelStack.hidden = YES;
|
||||||
|
|||||||
@@ -159,8 +159,8 @@ const double NAV_BAR_HEIGHT = 50;
|
|||||||
[self presentViewController:savedAlertController animated:YES completion:nil];
|
[self presentViewController:savedAlertController animated:YES completion:nil];
|
||||||
}
|
}
|
||||||
else { // if user entered a valid name that doesn't already exist then save the profile to persistent storage
|
else { // if user entered a valid name that doesn't already exist then save the profile to persistent storage
|
||||||
[self->profilesManager saveProfileWithName: enteredProfileName andButtonLayers:self.currentOSCButtonLayers]; // the OSC layout here is passed from parent LayoutOSCViewController;
|
[self->profilesManager duplicateSelectedProfileWithName:enteredProfileName]; // the OSC layout here is passed from parent LayoutOSCViewController;
|
||||||
[self->profilesManager setProfileToSelected: [self->profilesManager getIndexOfLastProfile]];
|
// [self->profilesManager setProfileToSelected: [self->profilesManager getIndexOfLastProfile]];
|
||||||
|
|
||||||
UIAlertController * savedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Profile %@ duplicated from current layout", enteredProfileName] preferredStyle:UIAlertControllerStyleAlert]; // Let user know this profile has been duplicated & saved
|
UIAlertController * savedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Profile %@ duplicated from current layout", enteredProfileName] preferredStyle:UIAlertControllerStyleAlert]; // Let user know this profile has been duplicated & saved
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,24 @@
|
|||||||
@property (strong, nonatomic) IBOutlet UIStackView *rememberFoldStateStack;
|
@property (strong, nonatomic) IBOutlet UIStackView *rememberFoldStateStack;
|
||||||
@property (strong, nonatomic) IBOutlet UISwitch *rememberFoldStateSwitch;
|
@property (strong, nonatomic) IBOutlet UISwitch *rememberFoldStateSwitch;
|
||||||
|
|
||||||
|
@property (strong, nonatomic) IBOutlet UIStackView *mapGyroToStack;
|
||||||
|
@property (strong, nonatomic) IBOutlet UISegmentedControl *mapGyroToSelector;
|
||||||
|
|
||||||
|
@property (strong, nonatomic) IBOutlet UIStackView *gyroToStickSwitchStack;
|
||||||
|
@property (strong, nonatomic) IBOutlet UISwitch *yawPitchToRightStickSwitch;
|
||||||
|
@property (strong, nonatomic) IBOutlet UISwitch *rollToLeftStickSwitch;
|
||||||
|
|
||||||
|
@property (strong, nonatomic) IBOutlet UIStackView *yawPitchSensitivityStack;
|
||||||
|
@property (strong, nonatomic) IBOutlet UIStackView *yawSensitivityStack;
|
||||||
|
@property (strong, nonatomic) IBOutlet UISlider *yawSensitivitySlider;
|
||||||
|
@property (strong, nonatomic) IBOutlet UIStackView *pitchSensitivityStack;
|
||||||
|
@property (strong, nonatomic) IBOutlet UISlider *pitchSensitivitySlider;
|
||||||
|
|
||||||
|
@property (strong, nonatomic) IBOutlet UIStackView *rollSensitivityStack;
|
||||||
|
@property (strong, nonatomic) IBOutlet UISlider *rollSensitivitySlider;
|
||||||
|
|
||||||
|
@property (strong, nonatomic) IBOutlet UIStackView *testStack;
|
||||||
|
|
||||||
@property (nonatomic, strong) MicHandler *micHandler;
|
@property (nonatomic, strong) MicHandler *micHandler;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#import "ThemeManager.h"
|
#import "ThemeManager.h"
|
||||||
#import "Connection.h"
|
#import "Connection.h"
|
||||||
#import "Plot.h"
|
#import "Plot.h"
|
||||||
|
#import "OSCProfilesManager.h"
|
||||||
|
|
||||||
#import <UIKit/UIGestureRecognizerSubclass.h>
|
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||||
#import <VideoToolbox/VideoToolbox.h>
|
#import <VideoToolbox/VideoToolbox.h>
|
||||||
@@ -24,6 +25,8 @@
|
|||||||
@implementation SettingsViewController {
|
@implementation SettingsViewController {
|
||||||
TemporarySettings* tempSettings;
|
TemporarySettings* tempSettings;
|
||||||
DataManager* dataMan;
|
DataManager* dataMan;
|
||||||
|
OSCProfilesManager* oscProfileMan;
|
||||||
|
OSCProfile* oscProfile;
|
||||||
|
|
||||||
NSLayoutConstraint *parentStackLeadingConstraint;
|
NSLayoutConstraint *parentStackLeadingConstraint;
|
||||||
NSLayoutConstraint *parentStackWidthConstraint;
|
NSLayoutConstraint *parentStackWidthConstraint;
|
||||||
@@ -43,6 +46,7 @@
|
|||||||
CGFloat _scrollSpeed;
|
CGFloat _scrollSpeed;
|
||||||
CGFloat _currentRefreshRate;
|
CGFloat _currentRefreshRate;
|
||||||
MenuSectionView *touchAndControlSection;
|
MenuSectionView *touchAndControlSection;
|
||||||
|
MenuSectionView *motionControlSection;
|
||||||
MenuSectionView *videoSection;
|
MenuSectionView *videoSection;
|
||||||
MenuSectionView *otherSection;
|
MenuSectionView *otherSection;
|
||||||
MenuSectionView *experimentalSection;
|
MenuSectionView *experimentalSection;
|
||||||
@@ -342,6 +346,47 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
NSLog(@"Mic error: %@", error);
|
NSLog(@"Mic error: %@", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)reloadMotionControlConfigs{
|
||||||
|
oscProfile = [oscProfileMan getSelectedProfile];
|
||||||
|
[self.mapGyroToSelector setSelectedSegmentIndex:oscProfile.mapGyroTo];
|
||||||
|
[self mapGyroToChanged:self.mapGyroToSelector];
|
||||||
|
if(self.mapGyroToSelector.selectedSegmentIndex == mapGyroToControllerStick){
|
||||||
|
[self.yawPitchToRightStickSwitch setOn:oscProfile.yawPitchToRightStick];
|
||||||
|
[self yawPitchToRightStickSwitchFlipped:self.yawPitchToRightStickSwitch];
|
||||||
|
[self.rollToLeftStickSwitch setOn:oscProfile.rollToLeftStick];
|
||||||
|
[self rollToLeftStickSwitchFlipped:self.rollToLeftStickSwitch];
|
||||||
|
}
|
||||||
|
[self.yawSensitivitySlider setValue:[self map_SliderValue_fromVelocFactor:oscProfile.gyroSensitivityYaw]];
|
||||||
|
[self yawSensitivitySliderMoved:self.yawSensitivitySlider];
|
||||||
|
[self.pitchSensitivitySlider setValue:[self map_SliderValue_fromVelocFactor:oscProfile.gyroSensitivityPitch]];
|
||||||
|
[self pitchSensitivitySliderMoved:self.pitchSensitivitySlider];
|
||||||
|
[self.rollSensitivitySlider setValue:[self map_SliderValue_fromVelocFactor:oscProfile.gyroSensitivityRoll]];
|
||||||
|
[self rollSensitivitySliderMoved:self.rollSensitivitySlider];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)saveMotionControlConfigs{
|
||||||
|
|
||||||
|
CGFloat yawSensitivityPercent = [self map_velocFactorDisplay_fromSliderValue:self.yawSensitivitySlider.value];
|
||||||
|
CGFloat pitchSensitivityPercent = [self map_velocFactorDisplay_fromSliderValue:self.pitchSensitivitySlider.value];
|
||||||
|
CGFloat rollSensitivityPercent = [self map_velocFactorDisplay_fromSliderValue:self.rollSensitivitySlider.value];
|
||||||
|
|
||||||
|
bool configNotChanged = (oscProfile.mapGyroTo == self.mapGyroToSelector.selectedSegmentIndex
|
||||||
|
&& oscProfile.yawPitchToRightStick == self.yawPitchToRightStickSwitch.isOn
|
||||||
|
&& oscProfile.rollToLeftStick == self.rollToLeftStickSwitch.isOn
|
||||||
|
&& (int16_t)(oscProfile.gyroSensitivityYaw*100) == (int16_t)yawSensitivityPercent
|
||||||
|
&& (int16_t)(oscProfile.gyroSensitivityPitch*100) == (int16_t)pitchSensitivityPercent
|
||||||
|
&& (int16_t)(oscProfile.gyroSensitivityRoll*100) == (int16_t)rollSensitivityPercent);
|
||||||
|
if(!configNotChanged){
|
||||||
|
oscProfile.mapGyroTo = self.mapGyroToSelector.selectedSegmentIndex;
|
||||||
|
oscProfile.yawPitchToRightStick = self.yawPitchToRightStickSwitch.isOn;
|
||||||
|
oscProfile.rollToLeftStick = self.rollToLeftStickSwitch.isOn;
|
||||||
|
oscProfile.gyroSensitivityYaw = yawSensitivityPercent/100;
|
||||||
|
oscProfile.gyroSensitivityPitch = pitchSensitivityPercent/100;
|
||||||
|
oscProfile.gyroSensitivityRoll = rollSensitivityPercent/100;
|
||||||
|
[oscProfileMan replaceSelectedProfileWith:oscProfile overwriteDefault:YES];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated{
|
- (void)viewWillAppear:(BOOL)animated{
|
||||||
[super viewWillAppear:NO];
|
[super viewWillAppear:NO];
|
||||||
|
|
||||||
@@ -351,7 +396,6 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
[self.micHandler startTapping];
|
[self.micHandler startTapping];
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
[self updateParentStackHorizontalConstraints];
|
[self updateParentStackHorizontalConstraints];
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(deviceOrientationDidChange:) // handle orientation change since i made portrait mode available
|
selector:@selector(deviceOrientationDidChange:) // handle orientation change since i made portrait mode available
|
||||||
@@ -363,6 +407,11 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
name:ThemeDidChangeNotification
|
name:ThemeDidChangeNotification
|
||||||
object:nil];
|
object:nil];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(reloadMotionControlConfigs)
|
||||||
|
name:@"OscLayoutCloseNotification"
|
||||||
|
object:nil];
|
||||||
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||||
if(self.mainFrameViewController.settingsExpandedInStreamView){
|
if(self.mainFrameViewController.settingsExpandedInStreamView){
|
||||||
NSInteger responseCode = [self.mainFrameViewController requestForBitrate:self->_bitrate];
|
NSInteger responseCode = [self.mainFrameViewController requestForBitrate:self->_bitrate];
|
||||||
@@ -383,6 +432,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(![self manuallyChangedFPS]) [self framerateChanged];
|
if(![self manuallyChangedFPS]) [self framerateChanged];
|
||||||
|
|
||||||
|
[self reloadMotionControlConfigs];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidAppear:(BOOL)animated{
|
- (void)viewDidAppear:(BOOL)animated{
|
||||||
@@ -390,10 +441,10 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
[self updateResolutionTable];
|
[self updateResolutionTable];
|
||||||
[self.customResolutionSwitch addTarget:self action:@selector(customResolutionSwitched:) forControlEvents:UIControlEventValueChanged];
|
[self.customResolutionSwitch addTarget:self action:@selector(customResolutionSwitched:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
|
||||||
tempSettings = [dataMan getSettings];
|
self->tempSettings = [self->dataMan getSettings];
|
||||||
|
|
||||||
//CGSize currentResolution = CGSizeMake(currentSettings.width.intValue, currentSettings.height.intValue);
|
//CGSize currentResolution = CGSizeMake(currentSettings.width.intValue, currentSettings.height.intValue);
|
||||||
[self.customResolutionSwitch setOn: isCustomResolution(tempSettings.resolutionSelected.intValue)];
|
[self.customResolutionSwitch setOn: isCustomResolution(self->tempSettings.resolutionSelected.intValue)];
|
||||||
[self.resolutionSelector setEnabled:!self.customResolutionSwitch.isOn];
|
[self.resolutionSelector setEnabled:!self.customResolutionSwitch.isOn];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,6 +700,22 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
[touchAndControlSection addToParentStack:_parentStack];
|
[touchAndControlSection addToParentStack:_parentStack];
|
||||||
// [touchAndControlSection setExpanded:NO];
|
// [touchAndControlSection setExpanded:NO];
|
||||||
|
|
||||||
|
motionControlSection = [[MenuSectionView alloc] init];
|
||||||
|
motionControlSection.delegate = self;
|
||||||
|
motionControlSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Motion Control"];
|
||||||
|
motionControlSection.identifier = @"SettingsSectionMotionControl";
|
||||||
|
if (@available(iOS 13.0, *)) {
|
||||||
|
// [motionControlSection setSectionWithIcon:[UIImage imageNamed:@"gyroscope"] andSize:23];
|
||||||
|
[motionControlSection setSectionWithIcon:[UIImage imageNamed:@"gyroscope"] andSize:23];
|
||||||
|
}
|
||||||
|
[self addSetting:self.mapGyroToStack ofId:@"mapGyroToStack" withInfoTag:NO withDynamicLabel:NO to:motionControlSection];
|
||||||
|
[self addSetting:self.gyroToStickSwitchStack ofId:@"gyroToStickStack" withInfoTag:NO withDynamicLabel:NO to:motionControlSection];
|
||||||
|
[self addSetting:self.yawPitchSensitivityStack ofId:@"yawPitchSensitivityStack" withInfoTag:NO withDynamicLabel:NO to:motionControlSection];
|
||||||
|
[self addDynamicLabelForStack:self.yawSensitivityStack];
|
||||||
|
[self addDynamicLabelForStack:self.pitchSensitivityStack];
|
||||||
|
[self addSetting:self.rollSensitivityStack ofId:@"rollSensitivityStack" withInfoTag:NO withDynamicLabel:YES to:motionControlSection];
|
||||||
|
[motionControlSection addToParentStack:_parentStack];
|
||||||
|
|
||||||
MenuSectionView *gesturesSection = [[MenuSectionView alloc] init];
|
MenuSectionView *gesturesSection = [[MenuSectionView alloc] init];
|
||||||
gesturesSection.delegate = self;
|
gesturesSection.delegate = self;
|
||||||
gesturesSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Gestures"];
|
gesturesSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Gestures"];
|
||||||
@@ -1399,347 +1466,387 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
settingStackWillBeRelocatedToLowestPosition = false;
|
|
||||||
hiddenStacks = [[NSMutableSet alloc] init];
|
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"_UIConstraintBasedLayoutLogUnsatisfiable"];
|
[UIView animateWithDuration:0 animations:^{
|
||||||
|
|
||||||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
self->oscProfileMan = [OSCProfilesManager sharedManager:CGRectZero];
|
||||||
[self.view addGestureRecognizer:longPress];
|
|
||||||
|
|
||||||
_settingStackDict = [[NSMutableDictionary alloc] init];
|
self->settingStackWillBeRelocatedToLowestPosition = false;
|
||||||
|
self->hiddenStacks = [[NSMutableSet alloc] init];
|
||||||
|
|
||||||
|
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"_UIConstraintBasedLayoutLogUnsatisfiable"];
|
||||||
|
|
||||||
|
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||||
|
[self.view addGestureRecognizer:longPress];
|
||||||
|
|
||||||
|
self->_settingStackDict = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
|
|
||||||
for(UIView* view in self.view.subviews){
|
for(UIView* view in self.view.subviews){
|
||||||
[view removeFromSuperview];
|
[view removeFromSuperview];
|
||||||
}
|
|
||||||
|
|
||||||
[self initParentStack];
|
|
||||||
|
|
||||||
// load rememberFoldState before section layout
|
|
||||||
dataMan = [[DataManager alloc] init];
|
|
||||||
tempSettings = [dataMan getSettings];
|
|
||||||
[self.rememberFoldStateSwitch setOn:tempSettings.rememberFoldState];// Load old setting
|
|
||||||
MenuSectionView.overridePersistedFoldState = !tempSettings.rememberFoldState;
|
|
||||||
[self.rememberFoldStateSwitch addTarget:self action:@selector(rememberFoldStateSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
|
||||||
|
|
||||||
[self layoutSections];
|
|
||||||
|
|
||||||
|
|
||||||
// [self swi];
|
|
||||||
|
|
||||||
self->slideToCloseSettingsViewRecognizer = [[CustomEdgeSlideGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)];
|
|
||||||
slideToCloseSettingsViewRecognizer.edges = UIRectEdgeLeft;
|
|
||||||
slideToCloseSettingsViewRecognizer.normalizedThresholdDistance = 0.0;
|
|
||||||
slideToCloseSettingsViewRecognizer.EDGE_TOLERANCE = 10;
|
|
||||||
slideToCloseSettingsViewRecognizer.immediateTriggering = true;
|
|
||||||
slideToCloseSettingsViewRecognizer.delaysTouchesBegan = NO;
|
|
||||||
slideToCloseSettingsViewRecognizer.delaysTouchesEnded = NO;
|
|
||||||
[self.view addGestureRecognizer:slideToCloseSettingsViewRecognizer];
|
|
||||||
|
|
||||||
settingsViewJustLoaded = true;
|
|
||||||
|
|
||||||
// Always run settings in dark mode because we want the light fonts
|
|
||||||
if (@available(iOS 13.0, tvOS 13.0, *)) {
|
|
||||||
self.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
currentSettingsMenuMode = tempSettings.settingsMenuMode.intValue;
|
|
||||||
[self loadFavoriteSettingStackIdentifiers];
|
|
||||||
if(tempSettings.settingsMenuMode.intValue == FavoriteSettings) [self switchToFavoriteSettings];
|
|
||||||
|
|
||||||
// Ensure we pick a bitrate that falls exactly onto a slider notch
|
|
||||||
_bitrate = bitrateTable[[self getSliderValueForBitrate:[tempSettings.bitrate intValue]]];
|
|
||||||
|
|
||||||
// Get the size of the screen with and without safe area insets
|
|
||||||
// UIWindow *window = UIApplication.sharedApplication.windows.firstObject;
|
|
||||||
// CGFloat screenScale = window.screen.scale;
|
|
||||||
// CGFloat safeAreaWidth = (window.frame.size.width - window.safeAreaInsets.left - window.safeAreaInsets.right) * screenScale;
|
|
||||||
// CGFloat fullScreenWidth = window.frame.size.width * screenScale;
|
|
||||||
// CGFloat fullScreenHeight = window.frame.size.height * screenScale;
|
|
||||||
|
|
||||||
[self.resolutionSelector removeSegmentAtIndex:0 animated:NO]; // remove 360p
|
|
||||||
[self.resolutionSelector removeSegmentAtIndex:5 animated:NO]; // remove custom segment
|
|
||||||
// iOS12 compatibility:
|
|
||||||
self.resolutionSelector.selectedSegmentIndex = 3;
|
|
||||||
[self.resolutionSelector setNeedsLayout];
|
|
||||||
|
|
||||||
resolutionTable[5] = CGSizeMake([tempSettings.width integerValue], [tempSettings.height integerValue]); // custom initial value
|
|
||||||
[self updateResolutionTable];
|
|
||||||
|
|
||||||
|
|
||||||
NSInteger framerate;
|
|
||||||
switch ([tempSettings.framerate integerValue]) {
|
|
||||||
case 30:
|
|
||||||
framerate = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
case 60:
|
|
||||||
framerate = 1;
|
|
||||||
break;
|
|
||||||
case 120:
|
|
||||||
framerate = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSInteger resolution = tempSettings.resolutionSelected.integerValue;
|
|
||||||
if(resolution >= RESOLUTION_TABLE_SIZE){
|
|
||||||
resolution = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only show the 120 FPS option if we have a > 60-ish Hz display
|
|
||||||
bool enable120Fps = false;
|
|
||||||
if (@available(iOS 10.3, tvOS 10.3, *)) {
|
|
||||||
if ([UIScreen mainScreen].maximumFramesPerSecond > 62) {
|
|
||||||
enable120Fps = true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!enable120Fps) {
|
|
||||||
[self.framerateSelector removeSegmentAtIndex:2 animated:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable codec selector segments for unsupported codecs
|
[self initParentStack];
|
||||||
#if defined(__IPHONE_16_0) || defined(__TVOS_16_0)
|
|
||||||
if (!VTIsHardwareDecodeSupported(kCMVideoCodecType_AV1))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
[self.codecSelector removeSegmentAtIndex:2 animated:NO];
|
|
||||||
}
|
|
||||||
if (!VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC)) {
|
|
||||||
[self.codecSelector removeSegmentAtIndex:1 animated:NO];
|
|
||||||
|
|
||||||
// Only enable the 4K option for "recent" devices. We'll judge that by whether
|
// load rememberFoldState before section layout
|
||||||
// they support HEVC decoding (A9 or later).
|
self->dataMan = [[DataManager alloc] init];
|
||||||
[self.resolutionSelector setEnabled:NO forSegmentAtIndex:2];
|
self->tempSettings = [self->dataMan getSettings];
|
||||||
}
|
[self.rememberFoldStateSwitch setOn:self->tempSettings.rememberFoldState];// Load old setting
|
||||||
switch (tempSettings.preferredCodec) {
|
MenuSectionView.overridePersistedFoldState = !self->tempSettings.rememberFoldState;
|
||||||
case CODEC_PREF_AUTO:
|
[self.rememberFoldStateSwitch addTarget:self action:@selector(rememberFoldStateSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||||
[self.codecSelector setSelectedSegmentIndex:self.codecSelector.numberOfSegments - 1];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CODEC_PREF_AV1:
|
[self layoutSections];
|
||||||
[self.codecSelector setSelectedSegmentIndex:2];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CODEC_PREF_HEVC:
|
|
||||||
[self.codecSelector setSelectedSegmentIndex:1];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CODEC_PREF_H264:
|
// [self swi];
|
||||||
[self.codecSelector setSelectedSegmentIndex:0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (![self hdrSupported]) {
|
self->slideToCloseSettingsViewRecognizer = [[CustomEdgeSlideGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)];
|
||||||
[self.hdrSwitch setOn:NO];
|
self->slideToCloseSettingsViewRecognizer.edges = UIRectEdgeLeft;
|
||||||
[self.hdrSwitch setEnabled:NO];
|
self->slideToCloseSettingsViewRecognizer.normalizedThresholdDistance = 0.0;
|
||||||
}
|
self->slideToCloseSettingsViewRecognizer.EDGE_TOLERANCE = 10;
|
||||||
else {
|
self->slideToCloseSettingsViewRecognizer.immediateTriggering = true;
|
||||||
[self.hdrSwitch setOn:tempSettings.enableHdr];
|
self->slideToCloseSettingsViewRecognizer.delaysTouchesBegan = NO;
|
||||||
}
|
self->slideToCloseSettingsViewRecognizer.delaysTouchesEnded = NO;
|
||||||
|
[self.view addGestureRecognizer:self->slideToCloseSettingsViewRecognizer];
|
||||||
|
|
||||||
[self.yuv444Switch setOn:tempSettings.enableYUV444];
|
self->settingsViewJustLoaded = true;
|
||||||
|
|
||||||
[self.pipSwitch setOn:tempSettings.enablePIP];
|
// Always run settings in dark mode because we want the light fonts
|
||||||
if(@available(iOS 15.0, *)) [self.pipSwitch setEnabled:true];
|
if (@available(iOS 13.0, tvOS 13.0, *)) {
|
||||||
else{
|
self.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
|
||||||
[self.pipSwitch setOn:false];
|
}
|
||||||
[self.pipSwitch setEnabled:false];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self.statsOverlaySelector setSelectedSegmentIndex:tempSettings.statsOverlayLevel.intValue];
|
|
||||||
|
|
||||||
NSInteger renderingBackend = [tempSettings.renderingBackend integerValue];
|
self->currentSettingsMenuMode = self->tempSettings.settingsMenuMode.intValue;
|
||||||
[self.renderingBackendSelector setSelectedSegmentIndex:renderingBackend];
|
[self loadFavoriteSettingStackIdentifiers];
|
||||||
[self.renderingBackendSelector addTarget:self action:@selector(renderingBackendChanged:) forControlEvents:UIControlEventValueChanged];
|
if(self->tempSettings.settingsMenuMode.intValue == FavoriteSettings) [self switchToFavoriteSettings];
|
||||||
|
|
||||||
NSInteger framePacingMode = [tempSettings.framePacingMode integerValue];
|
// Ensure we pick a bitrate that falls exactly onto a slider notch
|
||||||
[self.framePacingModeSelector setSelectedSegmentIndex:framePacingMode];
|
self->_bitrate = bitrateTable[[self getSliderValueForBitrate:[self->tempSettings.bitrate intValue]]];
|
||||||
[self.framePacingModeSelector addTarget:self action:@selector(framePacingModeChanged:) forControlEvents:UIControlEventValueChanged];
|
|
||||||
[self framePacingModeChanged:self.framePacingModeSelector];
|
|
||||||
|
|
||||||
[self renderingBackendChanged:self.renderingBackendSelector]; // Update PiP and frame pacing state based on current selection
|
// Get the size of the screen with and without safe area insets
|
||||||
|
// UIWindow *window = UIApplication.sharedApplication.windows.firstObject;
|
||||||
|
// CGFloat screenScale = window.screen.scale;
|
||||||
|
// CGFloat safeAreaWidth = (window.frame.size.width - window.safeAreaInsets.left - window.safeAreaInsets.right) * screenScale;
|
||||||
|
// CGFloat fullScreenWidth = window.frame.size.width * screenScale;
|
||||||
|
// CGFloat fullScreenHeight = window.frame.size.height * screenScale;
|
||||||
|
|
||||||
[self.citrixX1MouseSwitch setOn:tempSettings.btMouseSupport];
|
[self.resolutionSelector removeSegmentAtIndex:0 animated:NO]; // remove 360p
|
||||||
[self.optimizeGamesSwitch setOn: tempSettings.optimizeGames];
|
[self.resolutionSelector removeSegmentAtIndex:5 animated:NO]; // remove custom segment
|
||||||
[self.multiControllerSwitch setOn:tempSettings.multiController];
|
// iOS12 compatibility:
|
||||||
[self.swapAbxySwitch setOn:tempSettings.swapABXYButtons];
|
self.resolutionSelector.selectedSegmentIndex = 3;
|
||||||
[self.buttonVisualFeedbackSwitch setOn:tempSettings.buttonVisualFeedback];
|
[self.resolutionSelector setNeedsLayout];
|
||||||
|
|
||||||
[self.gyroModeSelector setSelectedSegmentIndex:tempSettings.gyroMode.intValue];
|
resolutionTable[5] = CGSizeMake([self->tempSettings.width integerValue], [self->tempSettings.height integerValue]); // custom initial value
|
||||||
[self.gyroSensitivitySlider setValue: (uint16_t)(tempSettings.gyroSensitivity.floatValue * 100) animated:YES]; // Load old setting.
|
[self updateResolutionTable];
|
||||||
[self.gyroSensitivitySlider addTarget:self action:@selector(gyroSensitivitySliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
|
||||||
[self gyroSensitivitySliderMoved:self.gyroSensitivitySlider];
|
|
||||||
|
|
||||||
if (@available(iOS 14.0, tvOS 14.0, *)) nil;
|
|
||||||
else{
|
|
||||||
[self.gyroModeSelector setEnabled:false forSegmentAtIndex:1];
|
|
||||||
[self.gyroModeSelector setEnabled:false forSegmentAtIndex:3];
|
|
||||||
}
|
|
||||||
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
|
|
||||||
[self.gyroModeSelector setEnabled:[motionManager isGyroAvailable] forSegmentAtIndex:2];
|
|
||||||
|
|
||||||
[self.emulatedControllerTypeSelector setSelectedSegmentIndex:[self controllerTypeToSegmentIndex:tempSettings.emulatedControllerType.intValue]];
|
NSInteger framerate;
|
||||||
[self.emulatedControllerTypeSelector addTarget:self action:@selector(emulatedControllerTypeChanged:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
switch ([self->tempSettings.framerate integerValue]) {
|
||||||
[self emulatedControllerTypeChanged:self.emulatedControllerTypeSelector];
|
case 30:
|
||||||
|
framerate = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case 60:
|
||||||
|
framerate = 1;
|
||||||
|
break;
|
||||||
|
case 120:
|
||||||
|
framerate = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
[self.audioOnPcSwitch setOn:tempSettings.playAudioOnPC];
|
NSInteger resolution = self->tempSettings.resolutionSelected.integerValue;
|
||||||
|
if(resolution >= RESOLUTION_TABLE_SIZE){
|
||||||
|
resolution = 0;
|
||||||
|
}
|
||||||
|
|
||||||
[self.localVolumeSlider setValue:tempSettings.localVolume.floatValue*100];
|
// Only show the 120 FPS option if we have a > 60-ish Hz display
|
||||||
[self.localVolumeSlider addTarget:self action:@selector(localVolumeSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
bool enable120Fps = false;
|
||||||
[self localVolumeSliderMoved:self.localVolumeSlider];
|
if (@available(iOS 10.3, tvOS 10.3, *)) {
|
||||||
|
if ([UIScreen mainScreen].maximumFramesPerSecond > 62) {
|
||||||
|
enable120Fps = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!enable120Fps) {
|
||||||
|
[self.framerateSelector removeSegmentAtIndex:2 animated:NO];
|
||||||
|
}
|
||||||
|
|
||||||
[self.micVolumeSlider setValue:tempSettings.micVolume.floatValue*100];
|
[UIView animateWithDuration:0 animations:^{
|
||||||
[self.micVolumeSlider addTarget:self action:@selector(micVolumeSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
// Disable codec selector segments for unsupported codecs
|
||||||
[self micVolumeSliderMoved:self.self.micVolumeSlider];
|
#if defined(__IPHONE_16_0) || defined(__TVOS_16_0)
|
||||||
|
if (!VTIsHardwareDecodeSupported(kCMVideoCodecType_AV1))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
[self.codecSelector removeSegmentAtIndex:2 animated:NO];
|
||||||
|
}
|
||||||
|
if (!VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC)) {
|
||||||
|
[self.codecSelector removeSegmentAtIndex:1 animated:NO];
|
||||||
|
|
||||||
[self.redirectMicSwitch setOn:tempSettings.redirectMic];
|
// Only enable the 4K option for "recent" devices. We'll judge that by whether
|
||||||
[self.redirectMicSwitch addTarget:self action:@selector(redirectMicSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
// they support HEVC decoding (A9 or later).
|
||||||
[self redirectMicSwitchFlipped:self.redirectMicSwitch];
|
[self.resolutionSelector setEnabled:NO forSegmentAtIndex:2];
|
||||||
|
}
|
||||||
|
|
||||||
[self.useBuiltinMicSwitch setOn:tempSettings.useBuiltinMic];
|
switch (self->tempSettings.preferredCodec) {
|
||||||
|
case CODEC_PREF_AUTO:
|
||||||
|
[self.codecSelector setSelectedSegmentIndex:self.codecSelector.numberOfSegments - 1];
|
||||||
|
break;
|
||||||
|
|
||||||
_lastSelectedResolutionIndex = resolution;
|
case CODEC_PREF_AV1:
|
||||||
[self.resolutionSelector setSelectedSegmentIndex:resolution];
|
[self.codecSelector setSelectedSegmentIndex:2];
|
||||||
[self.resolutionSelector addTarget:self action:@selector(newResolutionChosen) forControlEvents:UIControlEventValueChanged];
|
break;
|
||||||
|
|
||||||
[self.framerateSelector setSelectedSegmentIndex:framerate];
|
case CODEC_PREF_HEVC:
|
||||||
[self.framerateSelector addTarget:self action:@selector(framerateChanged) forControlEvents:UIControlEventValueChanged];
|
[self.codecSelector setSelectedSegmentIndex:1];
|
||||||
|
break;
|
||||||
|
|
||||||
[self.bitrateSlider setMinimumValue:0];
|
case CODEC_PREF_H264:
|
||||||
[self.bitrateSlider setMaximumValue:(sizeof(bitrateTable) / sizeof(*bitrateTable)) - 1];
|
[self.codecSelector setSelectedSegmentIndex:0];
|
||||||
[self.bitrateSlider setValue:[self getSliderValueForBitrate:_bitrate] animated:YES];
|
break;
|
||||||
[self.bitrateSlider addTarget:self action:@selector(bitrateSliderMoved) forControlEvents:UIControlEventValueChanged];
|
}
|
||||||
[self updateBitrateText];
|
}];
|
||||||
[self updateResolutionDisplayLabel];
|
|
||||||
|
|
||||||
[self.frameQueueSizeSlider setMinimumValue:1];
|
if (![self hdrSupported]) {
|
||||||
[self.frameQueueSizeSlider setMaximumValue:5];
|
[self.hdrSwitch setOn:NO];
|
||||||
[self.frameQueueSizeSlider setValue:tempSettings.frameQueueSize.intValue];
|
[self.hdrSwitch setEnabled:NO];
|
||||||
[self.frameQueueSizeSlider addTarget:self action:@selector(frameQueueSizeSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
}
|
||||||
[self frameQueueSizeSliderMoved:self.frameQueueSizeSlider];
|
else {
|
||||||
|
[self.hdrSwitch setOn:self->tempSettings.enableHdr];
|
||||||
|
}
|
||||||
|
|
||||||
[self.enableGraphsSwitch setOn:tempSettings.enableGraphs animated:NO]; // Add this line
|
[self.yuv444Switch setOn:self->tempSettings.enableYUV444];
|
||||||
[self.enableGraphsSwitch addTarget:self action:@selector(enableGraphsChanged:) forControlEvents:UIControlEventValueChanged];
|
|
||||||
[self enableGraphsChanged:self.enableGraphsSwitch];
|
|
||||||
[self.graphOpacityStepper setMinimumValue:0];
|
|
||||||
[self.graphOpacityStepper setMaximumValue:100];
|
|
||||||
[self.graphOpacityStepper setValue:(int)tempSettings.graphOpacity.intValue];
|
|
||||||
[self.graphOpacityStepper addTarget:self action:@selector(graphOpacityStepperTapped:) forControlEvents:UIControlEventValueChanged];
|
|
||||||
[self graphOpacityStepperTapped:self.graphOpacityStepper];
|
|
||||||
|
|
||||||
if (@available(iOS 18.0, tvOS 18.0, *)) {}else{
|
[self.pipSwitch setOn:self->tempSettings.enablePIP];
|
||||||
[self.audioConfigSelector removeSegmentAtIndex:1 animated:false];
|
if(@available(iOS 15.0, *)) [self.pipSwitch setEnabled:true];
|
||||||
[self.audioConfigSelector removeSegmentAtIndex:1 animated:false]; // segment 2 goes away when you remove index 2
|
else{
|
||||||
[self.audioConfigSelector setTitle:[LocalizationHelper localizedStringForKey:@"Stereo (surround sound available for iOS18+)"] forSegmentAtIndex:0];
|
[self.pipSwitch setOn:false];
|
||||||
[self.audioConfigSelector setEnabled:NO];
|
[self.pipSwitch setEnabled:false];
|
||||||
}
|
}
|
||||||
switch ([tempSettings.audioConfig integerValue]) {
|
|
||||||
case 2:
|
|
||||||
[self.audioConfigSelector setSelectedSegmentIndex:0];
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
[self.audioConfigSelector setSelectedSegmentIndex:1];
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
[self.audioConfigSelector setSelectedSegmentIndex:2];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock Display Orientation setting
|
[self.statsOverlaySelector setSelectedSegmentIndex:self->tempSettings.statsOverlayLevel.intValue];
|
||||||
bool unlockDisplayOrientationSelectorEnabled = [self isFullScreenRequired] || [self isIPhone];//need "requires fullscreen" enabled in the app bunddle to make runtime orientation limitation working
|
|
||||||
if(unlockDisplayOrientationSelectorEnabled) [self.unlockDisplayOrientationSelector setSelectedSegmentIndex:tempSettings.unlockDisplayOrientation ? 1 : 0];
|
|
||||||
else [self.unlockDisplayOrientationSelector setSelectedSegmentIndex:1]; // can't lock screen orientation in this mode = Display Orientation always unlocked
|
|
||||||
[self.unlockDisplayOrientationSelector setEnabled:unlockDisplayOrientationSelectorEnabled];
|
|
||||||
|
|
||||||
[self.backgroundSessionTimerSlider setValue:(uint32_t)tempSettings.backgroundSessionTimer.floatValue];
|
NSInteger renderingBackend = [self->tempSettings.renderingBackend integerValue];
|
||||||
[self.backgroundSessionTimerSlider addTarget:self action:@selector(backgroundSessionTimerSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
[self.renderingBackendSelector setSelectedSegmentIndex:renderingBackend];
|
||||||
[self backgroundSessionTimerSliderMoved:self.backgroundSessionTimerSlider];
|
[self.renderingBackendSelector addTarget:self action:@selector(renderingBackendChanged:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
|
||||||
// lift streamview setting
|
NSInteger framePacingMode = [self->tempSettings.framePacingMode integerValue];
|
||||||
[self.liftStreamViewForKeyboardSelector setSelectedSegmentIndex:tempSettings.liftStreamViewForKeyboard ? 1 : 0];// Load old setting
|
[self.framePacingModeSelector setSelectedSegmentIndex:framePacingMode];
|
||||||
|
[self.framePacingModeSelector addTarget:self action:@selector(framePacingModeChanged:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self framePacingModeChanged:self.framePacingModeSelector];
|
||||||
|
|
||||||
// showkeyboard toolbar setting
|
[self renderingBackendChanged:self.renderingBackendSelector]; // Update PiP and frame pacing state based on current selection
|
||||||
[self.softKeyboardToolbarSwitch setOn:tempSettings.showKeyboardToolbar];// Load old setting
|
|
||||||
|
|
||||||
// reverse mouse wheel direction setting
|
[self.citrixX1MouseSwitch setOn:self->tempSettings.btMouseSupport];
|
||||||
[self.reverseMouseWheelDirectionSelector setSelectedSegmentIndex:tempSettings.reverseMouseWheelDirection ? 1 : 0];// Load old setting
|
[self.optimizeGamesSwitch setOn: self->tempSettings.optimizeGames];
|
||||||
|
[self.multiControllerSwitch setOn:self->tempSettings.multiController];
|
||||||
|
[self.swapAbxySwitch setOn:self->tempSettings.swapABXYButtons];
|
||||||
|
[self.buttonVisualFeedbackSwitch setOn:self->tempSettings.buttonVisualFeedback];
|
||||||
|
|
||||||
// slide to menu settings
|
[self.gyroModeSelector setSelectedSegmentIndex:self->tempSettings.gyroMode.intValue];
|
||||||
[self.slideToSettingsScreenEdgeSelector setSelectedSegmentIndex:[self getSelectorIndexFromScreenEdge:(uint32_t)tempSettings.slideToSettingsScreenEdge.integerValue]];
|
[self.gyroSensitivitySlider setValue: (uint16_t)(self->tempSettings.gyroSensitivity.floatValue * 100) animated:YES]; // Load old setting.
|
||||||
// Load old setting
|
[self.gyroSensitivitySlider addTarget:self action:@selector(gyroSensitivitySliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||||
[self.slideToToolboxScreenEdgeSelector setEnabled:false];
|
[self gyroSensitivitySliderMoved:self.gyroSensitivitySlider];
|
||||||
[self.slideToSettingsScreenEdgeSelector addTarget:self action:@selector(slideToSettingsScreenEdgeChanged) forControlEvents:UIControlEventValueChanged];
|
|
||||||
[self slideToSettingsScreenEdgeChanged];
|
|
||||||
|
|
||||||
[self.slideToMenuDistanceSlider setValue:tempSettings.slideToSettingsDistance.floatValue];
|
if (@available(iOS 14.0, tvOS 14.0, *)) nil;
|
||||||
[self.slideToMenuDistanceSlider addTarget:self action:@selector(slideToMenuDistanceSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
else{
|
||||||
[self slideToMenuDistanceSliderMoved:self.slideToMenuDistanceSlider];
|
[self.gyroModeSelector setEnabled:false forSegmentAtIndex:1];
|
||||||
|
[self.gyroModeSelector setEnabled:false forSegmentAtIndex:3];
|
||||||
|
}
|
||||||
|
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
|
||||||
|
[self.gyroModeSelector setEnabled:[motionManager isGyroAvailable] forSegmentAtIndex:2];
|
||||||
|
|
||||||
|
[self.emulatedControllerTypeSelector setSelectedSegmentIndex:[self controllerTypeToSegmentIndex:self->tempSettings.emulatedControllerType.intValue]];
|
||||||
|
[self.emulatedControllerTypeSelector addTarget:self action:@selector(emulatedControllerTypeChanged:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||||
|
[self emulatedControllerTypeChanged:self.emulatedControllerTypeSelector];
|
||||||
|
|
||||||
|
[self.audioOnPcSwitch setOn:self->tempSettings.playAudioOnPC];
|
||||||
|
|
||||||
|
[self.localVolumeSlider setValue:self->tempSettings.localVolume.floatValue*100];
|
||||||
|
[self.localVolumeSlider addTarget:self action:@selector(localVolumeSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self localVolumeSliderMoved:self.localVolumeSlider];
|
||||||
|
|
||||||
|
[self.micVolumeSlider setValue:self->tempSettings.micVolume.floatValue*100];
|
||||||
|
[self.micVolumeSlider addTarget:self action:@selector(micVolumeSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self micVolumeSliderMoved:self.self.micVolumeSlider];
|
||||||
|
|
||||||
|
[self.redirectMicSwitch setOn:self->tempSettings.redirectMic];
|
||||||
|
[self.redirectMicSwitch addTarget:self action:@selector(redirectMicSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self redirectMicSwitchFlipped:self.redirectMicSwitch];
|
||||||
|
|
||||||
|
[self.useBuiltinMicSwitch setOn:self->tempSettings.useBuiltinMic];
|
||||||
|
|
||||||
|
self->_lastSelectedResolutionIndex = resolution;
|
||||||
|
[self.resolutionSelector setSelectedSegmentIndex:resolution];
|
||||||
|
[self.resolutionSelector addTarget:self action:@selector(newResolutionChosen) forControlEvents:UIControlEventValueChanged];
|
||||||
|
|
||||||
|
[self.framerateSelector setSelectedSegmentIndex:framerate];
|
||||||
|
[self.framerateSelector addTarget:self action:@selector(framerateChanged) forControlEvents:UIControlEventValueChanged];
|
||||||
|
|
||||||
|
[self.bitrateSlider setMinimumValue:0];
|
||||||
|
[self.bitrateSlider setMaximumValue:(sizeof(bitrateTable) / sizeof(*bitrateTable)) - 1];
|
||||||
|
[self.bitrateSlider setValue:[self getSliderValueForBitrate:self->_bitrate] animated:YES];
|
||||||
|
[self.bitrateSlider addTarget:self action:@selector(bitrateSliderMoved) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self updateBitrateText];
|
||||||
|
[self updateResolutionDisplayLabel];
|
||||||
|
|
||||||
|
[self.frameQueueSizeSlider setMinimumValue:1];
|
||||||
|
[self.frameQueueSizeSlider setMaximumValue:5];
|
||||||
|
[self.frameQueueSizeSlider setValue:self->tempSettings.frameQueueSize.intValue];
|
||||||
|
[self.frameQueueSizeSlider addTarget:self action:@selector(frameQueueSizeSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self frameQueueSizeSliderMoved:self.frameQueueSizeSlider];
|
||||||
|
|
||||||
|
[self.enableGraphsSwitch setOn:self->tempSettings.enableGraphs animated:NO]; // Add this line
|
||||||
|
[self.enableGraphsSwitch addTarget:self action:@selector(enableGraphsChanged:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self enableGraphsChanged:self.enableGraphsSwitch];
|
||||||
|
[self.graphOpacityStepper setMinimumValue:0];
|
||||||
|
[self.graphOpacityStepper setMaximumValue:100];
|
||||||
|
[self.graphOpacityStepper setValue:(int)self->tempSettings.graphOpacity.intValue];
|
||||||
|
[self.graphOpacityStepper addTarget:self action:@selector(graphOpacityStepperTapped:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self graphOpacityStepperTapped:self.graphOpacityStepper];
|
||||||
|
|
||||||
|
if (@available(iOS 18.0, tvOS 18.0, *)) {}else{
|
||||||
|
[self.audioConfigSelector removeSegmentAtIndex:1 animated:false];
|
||||||
|
[self.audioConfigSelector removeSegmentAtIndex:1 animated:false]; // segment 2 goes away when you remove index 2
|
||||||
|
[self.audioConfigSelector setTitle:[LocalizationHelper localizedStringForKey:@"Stereo (surround sound available for iOS18+)"] forSegmentAtIndex:0];
|
||||||
|
[self.audioConfigSelector setEnabled:NO];
|
||||||
|
}
|
||||||
|
switch ([self->tempSettings.audioConfig integerValue]) {
|
||||||
|
case 2:
|
||||||
|
[self.audioConfigSelector setSelectedSegmentIndex:0];
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
[self.audioConfigSelector setSelectedSegmentIndex:1];
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
[self.audioConfigSelector setSelectedSegmentIndex:2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock Display Orientation setting
|
||||||
|
bool unlockDisplayOrientationSelectorEnabled = [self isFullScreenRequired] || [self isIPhone];//need "requires fullscreen" enabled in the app bunddle to make runtime orientation limitation working
|
||||||
|
if(unlockDisplayOrientationSelectorEnabled) [self.unlockDisplayOrientationSelector setSelectedSegmentIndex:self->tempSettings.unlockDisplayOrientation ? 1 : 0];
|
||||||
|
else [self.unlockDisplayOrientationSelector setSelectedSegmentIndex:1]; // can't lock screen orientation in this mode = Display Orientation always unlocked
|
||||||
|
[self.unlockDisplayOrientationSelector setEnabled:unlockDisplayOrientationSelectorEnabled];
|
||||||
|
|
||||||
|
[self.backgroundSessionTimerSlider setValue:(uint32_t)self->tempSettings.backgroundSessionTimer.floatValue];
|
||||||
|
[self.backgroundSessionTimerSlider addTarget:self action:@selector(backgroundSessionTimerSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self backgroundSessionTimerSliderMoved:self.backgroundSessionTimerSlider];
|
||||||
|
|
||||||
|
// lift streamview setting
|
||||||
|
[self.liftStreamViewForKeyboardSelector setSelectedSegmentIndex:self->tempSettings.liftStreamViewForKeyboard ? 1 : 0];// Load old setting
|
||||||
|
|
||||||
|
// showkeyboard toolbar setting
|
||||||
|
[self.softKeyboardToolbarSwitch setOn:self->tempSettings.showKeyboardToolbar];// Load old setting
|
||||||
|
|
||||||
|
// reverse mouse wheel direction setting
|
||||||
|
[self.reverseMouseWheelDirectionSelector setSelectedSegmentIndex:self->tempSettings.reverseMouseWheelDirection ? 1 : 0];// Load old setting
|
||||||
|
|
||||||
|
// slide to menu settings
|
||||||
|
[self.slideToSettingsScreenEdgeSelector setSelectedSegmentIndex:[self getSelectorIndexFromScreenEdge:(uint32_t)self->tempSettings.slideToSettingsScreenEdge.integerValue]];
|
||||||
|
// Load old setting
|
||||||
|
[self.slideToToolboxScreenEdgeSelector setEnabled:false];
|
||||||
|
[self.slideToSettingsScreenEdgeSelector addTarget:self action:@selector(slideToSettingsScreenEdgeChanged) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self slideToSettingsScreenEdgeChanged];
|
||||||
|
|
||||||
|
[self.slideToMenuDistanceSlider setValue:self->tempSettings.slideToSettingsDistance.floatValue];
|
||||||
|
[self.slideToMenuDistanceSlider addTarget:self action:@selector(slideToMenuDistanceSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||||
|
[self slideToMenuDistanceSliderMoved:self.slideToMenuDistanceSlider];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//TouchMode & OSC Related Settings:
|
//TouchMode & OSC Related Settings:
|
||||||
|
|
||||||
// pointer veloc setting, will be enable/disabled by touchMode
|
// pointer veloc setting, will be enable/disabled by touchMode
|
||||||
[self.pointerVelocityModeDividerSlider setValue: (uint8_t)(tempSettings.pointerVelocityModeDivider.floatValue * 100) animated:YES]; // Load old setting.
|
[self.pointerVelocityModeDividerSlider setValue: (uint8_t)(self->tempSettings.pointerVelocityModeDivider.floatValue * 100) animated:YES]; // Load old setting.
|
||||||
[self.pointerVelocityModeDividerSlider addTarget:self action:@selector(pointerVelocityModeDividerSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
[self.pointerVelocityModeDividerSlider addTarget:self action:@selector(pointerVelocityModeDividerSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||||
[self pointerVelocityModeDividerSliderMoved:self.pointerVelocityModeDividerSlider];
|
[self pointerVelocityModeDividerSliderMoved:self.pointerVelocityModeDividerSlider];
|
||||||
|
|
||||||
// init pointer veloc setting, will be enable/disabled by touchMode
|
// init pointer veloc setting, will be enable/disabled by touchMode
|
||||||
[self.touchPointerVelocityFactorSlider setValue: [self map_SliderValue_fromVelocFactor: tempSettings.touchPointerVelocityFactor.floatValue] animated:YES]; // Load old setting.
|
[self.touchPointerVelocityFactorSlider setValue: [self map_SliderValue_fromVelocFactor: self->tempSettings.touchPointerVelocityFactor.floatValue] animated:YES]; // Load old setting.
|
||||||
[self.touchPointerVelocityFactorSlider addTarget:self action:@selector(touchPointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
[self.touchPointerVelocityFactorSlider addTarget:self action:@selector(touchPointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||||
[self touchPointerVelocityFactorSliderMoved:self.touchPointerVelocityFactorSlider];
|
[self touchPointerVelocityFactorSliderMoved:self.touchPointerVelocityFactorSlider];
|
||||||
|
|
||||||
// async native touch event
|
// async native touch event
|
||||||
// [self.asyncNativeTouchPrioritySelector setSelectedSegmentIndex:currentSettings.asyncNativeTouchPriority.intValue]; // load old setting of asyncNativeTouchPriority
|
// [self.asyncNativeTouchPrioritySelector setSelectedSegmentIndex:currentSettings.asyncNativeTouchPriority.intValue]; // load old setting of asyncNativeTouchPriority
|
||||||
// [self.asyncNativeTouchPrioritySelector addTarget:self action:@selector(asyncNativeTouchPriorityChanged) forControlEvents:UIControlEventValueChanged];
|
// [self.asyncNativeTouchPrioritySelector addTarget:self action:@selector(asyncNativeTouchPriorityChanged) forControlEvents:UIControlEventValueChanged];
|
||||||
|
|
||||||
// init relative touch mouse pointer veloc setting, will be enable/disabled by touchMode
|
// init relative touch mouse pointer veloc setting, will be enable/disabled by touchMode
|
||||||
[self.mousePointerVelocityFactorSlider setValue:[self map_SliderValue_fromVelocFactor: tempSettings.mousePointerVelocityFactor.floatValue] animated:YES]; // Load old setting.
|
[self.mousePointerVelocityFactorSlider setValue:[self map_SliderValue_fromVelocFactor: self->tempSettings.mousePointerVelocityFactor.floatValue] animated:YES]; // Load old setting.
|
||||||
[self.mousePointerVelocityFactorSlider addTarget:self action:@selector(mousePointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
[self.mousePointerVelocityFactorSlider addTarget:self action:@selector(mousePointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||||
[self mousePointerVelocityFactorSliderMoved:self.mousePointerVelocityFactorSlider];
|
[self mousePointerVelocityFactorSliderMoved:self.mousePointerVelocityFactorSlider];
|
||||||
|
|
||||||
|
|
||||||
// these settings will be affected by onscreenControl & touchMode, must be loaded before them.
|
// these settings will be affected by onscreenControl & touchMode, must be loaded before them.
|
||||||
// NSLog(@"osc tool fingers setting test: %d", currentSettings.oscLayoutToolFingers.intValue);
|
// NSLog(@"osc tool fingers setting test: %d", currentSettings.oscLayoutToolFingers.intValue);
|
||||||
self->oswLayoutFingers = (uint16_t)tempSettings.oscLayoutToolFingers.intValue; // load old setting of oscLayoutFingers
|
self->oswLayoutFingers = (uint16_t)self->tempSettings.oscLayoutToolFingers.intValue; // load old setting of oscLayoutFingers
|
||||||
uint8_t keyboardToggleFingers = tempSettings.keyboardToggleFingers.intValue;
|
uint8_t keyboardToggleFingers = self->tempSettings.keyboardToggleFingers.intValue;
|
||||||
|
|
||||||
[self.softKeyboardGestureSelector setSelectedSegmentIndex:keyboardToggleFingers>=5 ? 3 : keyboardToggleFingers-3];
|
[self.softKeyboardGestureSelector setSelectedSegmentIndex:keyboardToggleFingers>=5 ? 3 : keyboardToggleFingers-3];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// this setting will be affected by touchMode, must be loaded before them.
|
// this setting will be affected by touchMode, must be loaded before them.
|
||||||
NSInteger onscreenControlsLevel = [tempSettings.onscreenControls integerValue];
|
NSInteger onscreenControlsLevel = [self->tempSettings.onscreenControls integerValue];
|
||||||
[self.onScreenWidgetSelector setSelectedSegmentIndex:onscreenControlsLevel];
|
[self.onScreenWidgetSelector setSelectedSegmentIndex:onscreenControlsLevel];
|
||||||
[self.onScreenWidgetSelector addTarget:self action:@selector(onScreenWidgetChanged) forControlEvents:UIControlEventValueChanged];
|
[self.onScreenWidgetSelector addTarget:self action:@selector(onScreenWidgetChanged) forControlEvents:UIControlEventValueChanged];
|
||||||
[self onScreenWidgetChanged];
|
[self onScreenWidgetChanged];
|
||||||
|
|
||||||
// touch move event interval for native-touch.
|
// touch move event interval for native-touch.
|
||||||
[self.touchMoveEventIntervalSlider setValue:tempSettings.touchMoveEventInterval.intValue animated:YES]; // Load old setting.
|
[self.touchMoveEventIntervalSlider setValue:self->tempSettings.touchMoveEventInterval.intValue animated:YES]; // Load old setting.
|
||||||
[self.touchMoveEventIntervalSlider addTarget:self action:@selector(touchMoveEventIntervalSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
[self.touchMoveEventIntervalSlider addTarget:self action:@selector(touchMoveEventIntervalSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||||
[self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
|
[self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
|
||||||
|
|
||||||
|
|
||||||
// this part will enable/disable oscSelector & the asyncNativeTouchPriority selector
|
// this part will enable/disable oscSelector & the asyncNativeTouchPriority selector
|
||||||
uint8_t touchModeSelectorIndex = tempSettings.touchMode.intValue == NativeTouchOnly ? NativeTouch : tempSettings.touchMode.intValue;
|
uint8_t touchModeSelectorIndex = self->tempSettings.touchMode.intValue == NativeTouchOnly ? NativeTouch : self->tempSettings.touchMode.intValue;
|
||||||
[self.touchModeSelector setSelectedSegmentIndex:touchModeSelectorIndex]; //Load old touchMode setting
|
[self.touchModeSelector setSelectedSegmentIndex:touchModeSelectorIndex]; //Load old touchMode setting
|
||||||
[self.touchModeSelector addTarget:self action:@selector(touchModeChanged:) forControlEvents:UIControlEventValueChanged];
|
[self.touchModeSelector addTarget:self action:@selector(touchModeChanged:) forControlEvents:UIControlEventValueChanged];
|
||||||
[self touchModeChanged:self.touchModeSelector];
|
[self touchModeChanged:self.touchModeSelector];
|
||||||
|
|
||||||
self.enableOswSwitchStack.hidden = !(tempSettings.touchMode.intValue == NativeTouch || tempSettings.touchMode.intValue == NativeTouchOnly); // do not use setHidden to stack wrapped by a settingStack
|
self.enableOswSwitchStack.hidden = !(self->tempSettings.touchMode.intValue == NativeTouch || self->tempSettings.touchMode.intValue == NativeTouchOnly); // do not use setHidden to stack wrapped by a settingStack
|
||||||
|
|
||||||
[self.enableOswForNativeTouchSwitch setOn:tempSettings.touchMode.intValue != NativeTouchOnly];
|
[self.enableOswForNativeTouchSwitch setOn:self->tempSettings.touchMode.intValue != NativeTouchOnly];
|
||||||
[self.enableOswForNativeTouchSwitch addTarget:self action:@selector(enableOswForNativeTouchSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
[self.enableOswForNativeTouchSwitch addTarget:self action:@selector(enableOswForNativeTouchSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||||
[self enableOswForNativeTouchSwitchFlipped:self.enableOswForNativeTouchSwitch];
|
[self enableOswForNativeTouchSwitchFlipped:self.enableOswForNativeTouchSwitch];
|
||||||
|
|
||||||
[self.externalDisplayModeSelector setSelectedSegmentIndex:tempSettings.externalDisplayMode.integerValue];
|
[self.externalDisplayModeSelector setSelectedSegmentIndex:self->tempSettings.externalDisplayMode.integerValue];
|
||||||
[self.localMousePointerModeSelector setSelectedSegmentIndex:tempSettings.localMousePointerMode.integerValue];
|
[self.localMousePointerModeSelector setSelectedSegmentIndex:self->tempSettings.localMousePointerMode.integerValue];
|
||||||
|
|
||||||
[self.sendDummyEventSwitch setOn:tempSettings.sendDummyEvent];// Load old setting
|
[self.sendDummyEventSwitch setOn:self->tempSettings.sendDummyEvent];// Load old setting
|
||||||
|
|
||||||
|
|
||||||
|
self->motionControlSection.expandable = [self isCustomOswEnabled];
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
|
self->motionControlSection.lockedSectionHandler = ^{
|
||||||
|
[CountdownAlertController showAlertIn:weakSelf
|
||||||
|
title:[LocalizationHelper localizedStringForKey:@"Tips"]
|
||||||
|
message:[LocalizationHelper localizedStringForKey:@"Tap 'OK' to set on-screen widget to 'Custom' and enable motion control."]
|
||||||
|
withCancel:YES
|
||||||
|
buttonTitle:[LocalizationHelper localizedStringForKey:@"OK"]
|
||||||
|
countdown:0
|
||||||
|
completion:^{
|
||||||
|
if(!CountdownAlertController.actionCancelled){
|
||||||
|
[weakSelf.onScreenWidgetSelector setSelectedSegmentIndex:OnScreenControlsLevelCustom];
|
||||||
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) return;
|
||||||
|
strongSelf->motionControlSection.expandable = true;
|
||||||
|
[strongSelf->motionControlSection setExpanded:YES];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
[self.mapGyroToSelector addTarget:self action:@selector(mapGyroToChanged:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self.yawPitchToRightStickSwitch addTarget:self action:@selector(yawPitchToRightStickSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self.rollToLeftStickSwitch addTarget:self action:@selector(rollToLeftStickSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
|
||||||
|
[self.yawSensitivitySlider addTarget:self action:@selector(yawSensitivitySliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self.pitchSensitivitySlider addTarget:self action:@selector(pitchSensitivitySliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
[self.rollSensitivitySlider addTarget:self action:@selector(rollSensitivitySliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
|
||||||
|
//Motion control settings
|
||||||
|
|
||||||
|
self->settingsViewJustLoaded = false;
|
||||||
|
}];
|
||||||
|
|
||||||
settingsViewJustLoaded = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)slideToSettingsScreenEdgeChanged{
|
- (void)slideToSettingsScreenEdgeChanged{
|
||||||
@@ -1793,7 +1900,11 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (bool)isCustomOswEnabled{
|
- (bool)isCustomOswEnabled{
|
||||||
return [self isOswEnabled] && self.onScreenWidgetSelector.selectedSegmentIndex == OnScreenControlsLevelCustom;
|
bool customOswEnabled = [self isOswEnabled] && self.onScreenWidgetSelector.selectedSegmentIndex == OnScreenControlsLevelCustom;
|
||||||
|
motionControlSection.expandable = customOswEnabled;
|
||||||
|
if(!settingsViewJustLoaded) [motionControlSection setExpanded:customOswEnabled];
|
||||||
|
NSLog(@"isCustomOswEnabled %d, %f",customOswEnabled, CACurrentMediaTime());
|
||||||
|
return customOswEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (bool)isNotNativeTouchOnly{
|
- (bool)isNotNativeTouchOnly{
|
||||||
@@ -1926,6 +2037,76 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)mapGyroToChanged:(UISegmentedControl* )sender{
|
||||||
|
[self setHidden:sender.selectedSegmentIndex != mapGyroToControllerStick forStack:_gyroToStickSwitchStack];
|
||||||
|
bool mapGyroToMouseEnabled = sender.selectedSegmentIndex == mapGyroToMouse;
|
||||||
|
bool mapGyroToControllerStickEnabled = sender.selectedSegmentIndex == mapGyroToControllerStick;
|
||||||
|
if(mapGyroToMouseEnabled){
|
||||||
|
[self setHidden:false forStack:_yawPitchSensitivityStack];
|
||||||
|
[self setHidden:true forStack:_rollSensitivityStack];
|
||||||
|
}
|
||||||
|
if(mapGyroToControllerStickEnabled){
|
||||||
|
[self yawPitchToRightStickSwitchFlipped:self.yawPitchToRightStickSwitch];
|
||||||
|
[self rollToLeftStickSwitchFlipped:self.rollToLeftStickSwitch];
|
||||||
|
}
|
||||||
|
if(sender.selectedSegmentIndex == driftCorrection){
|
||||||
|
|
||||||
|
[CountdownAlertController showAlertIn:self
|
||||||
|
title:[LocalizationHelper localizedStringForKey:@"Drift Correction"]
|
||||||
|
message:[LocalizationHelper localizedStringForKey:@"Place the device flat on a surface and keep it still, then tap ‘Start’."]
|
||||||
|
withCancel:YES
|
||||||
|
buttonTitle:[LocalizationHelper localizedStringForKey:@"Start"]
|
||||||
|
countdown:0
|
||||||
|
completion:^{
|
||||||
|
if(CountdownAlertController.actionCancelled){
|
||||||
|
[self.mapGyroToSelector setSelectedSegmentIndex:self->oscProfile.mapGyroTo];
|
||||||
|
NSLog(@"actionCancelled");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
MotionHandler* motionHandler = [MotionHandler sharedInstance];
|
||||||
|
[motionHandler calibrateGyroBiasWithDuration:5 completion:^{
|
||||||
|
Settings* currentSettings = [self->dataMan retrieveSettings];
|
||||||
|
currentSettings.gyroBiasX = [NSNumber numberWithDouble:motionHandler.gyroBiasX];
|
||||||
|
currentSettings.gyroBiasY = [NSNumber numberWithDouble:motionHandler.gyroBiasY];
|
||||||
|
currentSettings.gyroBiasZ = [NSNumber numberWithDouble:motionHandler.gyroBiasZ];
|
||||||
|
[self->dataMan saveData];
|
||||||
|
}];
|
||||||
|
[CountdownAlertController showAlertIn:self
|
||||||
|
title:[LocalizationHelper localizedStringForKey:@"Drift Correction"]
|
||||||
|
message:[LocalizationHelper localizedStringForKey:@"Calibrating..."]
|
||||||
|
withCancel:NO
|
||||||
|
buttonTitle:[LocalizationHelper localizedStringForKey:@"Finished!"]
|
||||||
|
countdown:6
|
||||||
|
completion:^{
|
||||||
|
[self.mapGyroToSelector setSelectedSegmentIndex:self->oscProfile.mapGyroTo];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rollToLeftStickSwitchFlipped:(UISwitch* )sender{
|
||||||
|
[self setHidden:!sender.isOn forStack:_rollSensitivityStack];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)yawPitchToRightStickSwitchFlipped:(UISwitch* )sender{
|
||||||
|
[self setHidden:!sender.isOn forStack:_yawPitchSensitivityStack];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)yawSensitivitySliderMoved:(UISlider* )sender{
|
||||||
|
[self findDynamicLabelFromStack:_yawSensitivityStack].text = [NSString stringWithFormat:@" %d%% ", (int16_t)[self map_velocFactorDisplay_fromSliderValue:sender.value]];
|
||||||
|
[_pitchSensitivitySlider setValue:sender.value];
|
||||||
|
[self pitchSensitivitySliderMoved:self.pitchSensitivitySlider];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pitchSensitivitySliderMoved:(UISlider* )sender{
|
||||||
|
[self findDynamicLabelFromStack:_pitchSensitivityStack].text = [NSString stringWithFormat:@" %d%% ", (int16_t)[self map_velocFactorDisplay_fromSliderValue:sender.value]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rollSensitivitySliderMoved:(UISlider* )sender{
|
||||||
|
[self findDynamicLabelFromStack:_rollSensitivityStack].text = [NSString stringWithFormat:@" %d%% ", (int16_t)[self map_velocFactorDisplay_fromSliderValue:sender.value]];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)invokeOscLayout{
|
- (void)invokeOscLayout{
|
||||||
// init CustomOSC stuff
|
// init CustomOSC stuff
|
||||||
/* sets a reference to the correct 'LayoutOnScreenControlsViewController' depending on whether the user is on an iPhone or iPad */
|
/* sets a reference to the correct 'LayoutOnScreenControlsViewController' depending on whether the user is on an iPhone or iPad */
|
||||||
@@ -1950,7 +2131,7 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void) touchPointerVelocityFactorSliderMoved:(UISlider* )sender {
|
- (void) touchPointerVelocityFactorSliderMoved:(UISlider* )sender {
|
||||||
[self findDynamicLabelFromStack:self.pointerVelocityFactorStack].text = [NSString stringWithFormat:@" %d%% ", [self map_velocFactorDisplay_fromSliderValue:sender.value]]; // Update label display
|
[self findDynamicLabelFromStack:self.pointerVelocityFactorStack].text = [NSString stringWithFormat:@" %d%% ", (uint16_t)[self map_velocFactorDisplay_fromSliderValue:sender.value]]; // Update label display
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) gyroSensitivitySliderMoved:(UISlider* )sender {
|
- (void) gyroSensitivitySliderMoved:(UISlider* )sender {
|
||||||
@@ -1977,10 +2158,10 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
|
|
||||||
|
|
||||||
// veloc factor upto 700%
|
// veloc factor upto 700%
|
||||||
- (uint16_t) map_velocFactorDisplay_fromSliderValue:(CGFloat)sliderValue{
|
- (CGFloat) map_velocFactorDisplay_fromSliderValue:(CGFloat)sliderValue{
|
||||||
uint16_t velocFactorDisplay = 0;
|
CGFloat velocFactorDisplay = 0;
|
||||||
if(sliderValue > 200) velocFactorDisplay = 200 + ((uint16_t)sliderValue % 200) * 5;
|
if(sliderValue > 200) velocFactorDisplay = 200 + ((uint16_t)sliderValue % 200) * 5;
|
||||||
else velocFactorDisplay = (uint16_t) sliderValue;
|
else velocFactorDisplay = sliderValue;
|
||||||
return velocFactorDisplay;
|
return velocFactorDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1994,7 +2175,7 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void) mousePointerVelocityFactorSliderMoved:(UISlider* )sender {
|
- (void) mousePointerVelocityFactorSliderMoved:(UISlider* )sender {
|
||||||
[self findDynamicLabelFromStack:_mousePointerVelocityStack].text = [NSString stringWithFormat:@" %d%% ",[self map_velocFactorDisplay_fromSliderValue:sender.value]];
|
[self findDynamicLabelFromStack:_mousePointerVelocityStack].text = [NSString stringWithFormat:@" %d%% ",(uint16_t)[self map_velocFactorDisplay_fromSliderValue:sender.value]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (uint32_t) getScreenEdgeFromSelector {
|
- (uint32_t) getScreenEdgeFromSelector {
|
||||||
@@ -2516,6 +2697,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(![MicHandler permissionGranted]) [self.redirectMicSwitch setOn:false];
|
if(![MicHandler permissionGranted]) [self.redirectMicSwitch setOn:false];
|
||||||
|
|
||||||
|
[self saveMotionControlConfigs];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) saveSettings {
|
- (void) saveSettings {
|
||||||
|
|||||||
@@ -434,6 +434,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
_motionHandler = [MotionHandler sharedInstance];
|
_motionHandler = [MotionHandler sharedInstance];
|
||||||
|
_motionHandler.gyroBiasX = _settings.gyroBiasX.doubleValue;
|
||||||
|
_motionHandler.gyroBiasY = _settings.gyroBiasY.doubleValue;
|
||||||
|
_motionHandler.gyroBiasZ = _settings.gyroBiasZ.doubleValue;
|
||||||
|
|
||||||
_streamView.onScreenControls.instanceReceiverDelegate = _motionHandler;
|
_streamView.onScreenControls.instanceReceiverDelegate = _motionHandler;
|
||||||
[_streamView.onScreenControls sendInstance];
|
[_streamView.onScreenControls sendInstance];
|
||||||
|
|
||||||
@@ -534,64 +538,22 @@
|
|||||||
|
|
||||||
- (void)popFirstStreamingTip {
|
- (void)popFirstStreamingTip {
|
||||||
// 初始化倒计时秒数
|
// 初始化倒计时秒数
|
||||||
__block NSInteger remainingSeconds = 16;
|
|
||||||
|
|
||||||
NSString* settingsEdgeSide = _settings.slideToSettingsScreenEdge.intValue == UIRectEdgeLeft ? [LocalizationHelper localizedStringForKey:@"left"] : [LocalizationHelper localizedStringForKey:@"right"];
|
NSString* settingsEdgeSide = _settings.slideToSettingsScreenEdge.intValue == UIRectEdgeLeft ? [LocalizationHelper localizedStringForKey:@"left"] : [LocalizationHelper localizedStringForKey:@"right"];
|
||||||
NSString* cmdToolEdgeSide = _settings.slideToSettingsScreenEdge.intValue == UIRectEdgeLeft ? [LocalizationHelper localizedStringForKey:@"right"] : [LocalizationHelper localizedStringForKey:@"left"];
|
NSString* cmdToolEdgeSide = _settings.slideToSettingsScreenEdge.intValue == UIRectEdgeLeft ? [LocalizationHelper localizedStringForKey:@"right"] : [LocalizationHelper localizedStringForKey:@"left"];
|
||||||
uint8_t slideDist = (uint8_t)(_settings.slideToSettingsDistance.floatValue * 100);
|
uint8_t slideDist = (uint8_t)(_settings.slideToSettingsDistance.floatValue * 100);
|
||||||
// 创建弹窗
|
// 创建弹窗
|
||||||
|
|
||||||
NSString* tipText = [LocalizationHelper localizedStringForKey:@"firstLaunchTip", settingsEdgeSide, slideDist, cmdToolEdgeSide, slideDist];
|
NSString* tipText = [LocalizationHelper localizedStringForKey:@"firstLaunchTip", settingsEdgeSide, slideDist, cmdToolEdgeSide, slideDist];
|
||||||
|
|
||||||
UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"First Launch Tips"] message: [LocalizationHelper localizedStringForKey:@"%@", tipText] preferredStyle:UIAlertControllerStyleAlert];
|
[CountdownAlertController showAlertIn:self
|
||||||
|
title:[LocalizationHelper localizedStringForKey:@"First Launch Tips"]
|
||||||
|
message:tipText
|
||||||
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
withCancel:NO
|
||||||
paragraphStyle.alignment = NSTextAlignmentLeft;
|
buttonTitle:[LocalizationHelper localizedStringForKey:@"Got it!"]
|
||||||
|
countdown:16
|
||||||
NSDictionary *attributes = @{
|
completion:^{}];
|
||||||
NSParagraphStyleAttributeName: paragraphStyle,
|
|
||||||
NSFontAttributeName: [UIFont systemFontOfSize:14]
|
|
||||||
};
|
|
||||||
|
|
||||||
NSAttributedString *attributedMessage = [[NSAttributedString alloc] initWithString:tipText
|
|
||||||
attributes:attributes];
|
|
||||||
|
|
||||||
// 使用 KVC 设置 attributedMessage(注意审核风险)
|
|
||||||
[tipsAlertController setValue:attributedMessage forKey:@"attributedMessage"];
|
|
||||||
|
|
||||||
// 添加确认按钮(初始禁用)
|
|
||||||
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Got it! (15)"]
|
|
||||||
style:UIAlertActionStyleDefault
|
|
||||||
handler:^(UIAlertAction * _Nonnull action) {
|
|
||||||
}];
|
|
||||||
confirmAction.enabled = NO;
|
|
||||||
[tipsAlertController addAction:confirmAction];
|
|
||||||
|
|
||||||
// 显示弹窗
|
|
||||||
[self presentViewController:tipsAlertController animated:YES completion:nil];
|
|
||||||
|
|
||||||
// 使用dispatch_source_t实现精确倒计时
|
|
||||||
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
|
|
||||||
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
|
|
||||||
|
|
||||||
dispatch_source_set_event_handler(timer, ^{
|
|
||||||
remainingSeconds--;
|
|
||||||
|
|
||||||
if (remainingSeconds <= 0) {
|
|
||||||
// 倒计时结束
|
|
||||||
dispatch_source_cancel(timer);
|
|
||||||
// 启用确认按钮
|
|
||||||
confirmAction.enabled = YES;
|
|
||||||
[confirmAction setValue:[LocalizationHelper localizedStringForKey:@"Got it!"] forKey:@"title"];
|
|
||||||
} else {
|
|
||||||
// 更新按钮标题和消息
|
|
||||||
[confirmAction setValue:[NSString stringWithFormat:[LocalizationHelper localizedStringForKey:@"Got it! (%ld)", remainingSeconds], (long)remainingSeconds] forKey:@"title"];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch_resume(timer);
|
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateTheme {
|
- (void)updateTheme {
|
||||||
@@ -1570,8 +1532,8 @@
|
|||||||
[_motionHandler startAccelUpdate];
|
[_motionHandler startAccelUpdate];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopGyroUpdate{
|
- (void)stopGyroUpdateWithInterruption:(BOOL)interruption{
|
||||||
[_motionHandler stopGyroUpdate];
|
[_motionHandler stopGyroUpdateWithInterruption:interruption];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopAccelUpdate{
|
- (void)stopAccelUpdate{
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <Limelight.h>
|
#include <Limelight.h>
|
||||||
#import "OnScreenControls.h"
|
#import "OnScreenControls.h"
|
||||||
#import "OnScreenButtonState.h"
|
#import "OnScreenButtonState.h"
|
||||||
|
#import "OSCProfile.h"
|
||||||
|
#import "OSCProfilesManager.h"
|
||||||
#import "opus.h"
|
#import "opus.h"
|
||||||
#import "opus_defines.h"
|
#import "opus_defines.h"
|
||||||
// #import "RelativeTouchHandler.h"
|
// #import "RelativeTouchHandler.h"
|
||||||
@@ -313,6 +313,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"6gY-8I-NWq.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Map Gyro to\"; ObjectID = \"6gY-8I-NWq\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Map Gyro to"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "陀螺仪映射为"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "陀螺儀映射為"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "陀螺儀映射為"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"6w5-Md-NpV.text" : {
|
"6w5-Md-NpV.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Preferred Codec\"; ObjectID = \"6w5-Md-NpV\";",
|
"comment" : "Class = \"UILabel\"; text = \"Preferred Codec\"; ObjectID = \"6w5-Md-NpV\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -865,6 +895,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"aol-mw-Kn1.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Yaw Sensitivity\"; ObjectID = \"aol-mw-Kn1\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Yaw Sensitivity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航灵敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航靈敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航靈敏度"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"aup-5k-z6P.text" : {
|
"aup-5k-z6P.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Show Local Keyboard Toolbar \"; ObjectID = \"aup-5k-z6P\";",
|
"comment" : "Class = \"UILabel\"; text = \"Show Local Keyboard Toolbar \"; ObjectID = \"aup-5k-z6P\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -1657,6 +1717,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"EFR-Ee-KiK.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Roll → Left Stick\"; ObjectID = \"EFR-Ee-KiK\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Roll → Left Stick"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滚转→左摇杆"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滾轉→左搖桿"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滾轉→左搖桿"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"egA-7n-Dpe.text" : {
|
"egA-7n-Dpe.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Touch Pointer Velocity\"; ObjectID = \"egA-7n-Dpe\";",
|
"comment" : "Class = \"UILabel\"; text = \"Touch Pointer Velocity\"; ObjectID = \"egA-7n-Dpe\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -1837,6 +1927,96 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"gTZ-RS-VIl.segmentTitles[0]" : {
|
||||||
|
"comment" : "Class = \"UISegmentedControl\"; gTZ-RS-VIl.segmentTitles[0] = \"Mouse\"; ObjectID = \"gTZ-RS-VIl\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Mouse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "鼠标"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滑鼠"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滑鼠"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gTZ-RS-VIl.segmentTitles[1]" : {
|
||||||
|
"comment" : "Class = \"UISegmentedControl\"; gTZ-RS-VIl.segmentTitles[1] = \"Controller Stick\"; ObjectID = \"gTZ-RS-VIl\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Controller Stick"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "手柄摇杆"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "手把搖桿"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "手把搖桿"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gTZ-RS-VIl.segmentTitles[2]" : {
|
||||||
|
"comment" : "Class = \"UISegmentedControl\"; gTZ-RS-VIl.segmentTitles[2] = \"Drift Correction\"; ObjectID = \"gTZ-RS-VIl\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Drift Correction"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "漂移修正"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "漂移修正"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "漂移修正"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Gus-CK-wpc.text" : {
|
"Gus-CK-wpc.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Send Dummy Event\"; ObjectID = \"Gus-CK-wpc\";",
|
"comment" : "Class = \"UILabel\"; text = \"Send Dummy Event\"; ObjectID = \"Gus-CK-wpc\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -2293,6 +2473,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jcj-D4-VnY.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Pitch Sensitivity\"; ObjectID = \"jcj-D4-VnY\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Pitch Sensitivity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "俯仰灵敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "俯仰靈敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "俯仰靈敏度"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"JTl-un-QJJ.text" : {
|
"JTl-un-QJJ.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Emulated Controller Type\"; ObjectID = \"JTl-un-QJJ\";",
|
"comment" : "Class = \"UILabel\"; text = \"Emulated Controller Type\"; ObjectID = \"JTl-un-QJJ\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -3709,6 +3919,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"u9Q-wm-UBm.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Roll Sensitivity\"; ObjectID = \"u9Q-wm-UBm\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Roll Sensitivity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滚转灵敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滾轉靈敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滾轉靈敏度"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"v8l-hf-kiR.text" : {
|
"v8l-hf-kiR.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Rendering Mode (Experimental)\"; ObjectID = \"v8l-hf-kiR\";",
|
"comment" : "Class = \"UILabel\"; text = \"Rendering Mode (Experimental)\"; ObjectID = \"v8l-hf-kiR\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -3841,6 +4081,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"XU0-qu-2p4.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Yaw&Pitch → Right Stick\"; ObjectID = \"XU0-qu-2p4\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Yaw&Pitch → Right Stick"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航/俯仰→右摇杆"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航/俯仰→右搖桿"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "偏航/俯仰→右搖桿"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Ylu-hT-Cuz.text" : {
|
"Ylu-hT-Cuz.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"The recommended value is 2. 1 frame provides the lowest latency, but may require an Ethernet connection.\"; ObjectID = \"Ylu-hT-Cuz\";",
|
"comment" : "Class = \"UILabel\"; text = \"The recommended value is 2. 1 frame provides the lowest latency, but may require an Ethernet connection.\"; ObjectID = \"Ylu-hT-Cuz\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
|
|||||||
@@ -61,6 +61,96 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"0OC-rX-CHK.segmentTitles[0]" : {
|
||||||
|
"comment" : "Class = \"UISegmentedControl\"; 0OC-rX-CHK.segmentTitles[0] = \"Mouse\"; ObjectID = \"0OC-rX-CHK\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Mouse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "鼠标"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滑鼠"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滑鼠"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0OC-rX-CHK.segmentTitles[1]" : {
|
||||||
|
"comment" : "Class = \"UISegmentedControl\"; 0OC-rX-CHK.segmentTitles[1] = \"Controller Stick\"; ObjectID = \"0OC-rX-CHK\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Controller Stick"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "手柄摇杆"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "手把搖桿"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "手把搖桿"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0OC-rX-CHK.segmentTitles[2]" : {
|
||||||
|
"comment" : "Class = \"UISegmentedControl\"; 0OC-rX-CHK.segmentTitles[2] = \"Drift Correction\"; ObjectID = \"0OC-rX-CHK\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Drift Correction"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "漂移修正"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "漂移修正"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "漂移修正"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"3av-lc-tcF.text" : {
|
"3av-lc-tcF.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Enable On-Screen Widget & Peripherals\"; ObjectID = \"3av-lc-tcF\";",
|
"comment" : "Class = \"UILabel\"; text = \"Enable On-Screen Widget & Peripherals\"; ObjectID = \"3av-lc-tcF\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -121,6 +211,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"3LT-qb-ZEB.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Roll → Left Stick\"; ObjectID = \"3LT-qb-ZEB\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Roll → Left Stick"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滚转→左摇杆"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滾轉→左搖桿"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滾轉→左搖桿"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"4Ul-P6-eUk.text" : {
|
"4Ul-P6-eUk.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Enable PiP\"; ObjectID = \"4Ul-P6-eUk\";",
|
"comment" : "Class = \"UILabel\"; text = \"Enable PiP\"; ObjectID = \"4Ul-P6-eUk\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -907,6 +1027,66 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"AN9-U4-nUG.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Yaw&Pitch → Right Stick\"; ObjectID = \"AN9-U4-nUG\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Yaw&Pitch → Right Stick"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航/俯仰→右摇杆"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航/俯仰→右搖桿"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航/俯仰→右搖桿"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aSq-2H-FPk.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Yaw Sensitivity\"; ObjectID = \"aSq-2H-FPk\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Yaw Sensitivity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航灵敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航靈敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "偏航靈敏度"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"awK-Ei-T9i.text" : {
|
"awK-Ei-T9i.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Open Toolbox in Streaming\"; ObjectID = \"awK-Ei-T9i\";",
|
"comment" : "Class = \"UILabel\"; text = \"Open Toolbox in Streaming\"; ObjectID = \"awK-Ei-T9i\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -1435,6 +1615,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dxk-33-Nbx.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Roll Sensitivity\"; ObjectID = \"dxk-33-Nbx\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Roll Sensitivity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滚转灵敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滾轉靈敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "滾轉靈敏度"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"E7F-vx-pnM.text" : {
|
"E7F-vx-pnM.text" : {
|
||||||
"comment" : "Class = \"UILabel\"; text = \"Frame Rate\"; ObjectID = \"E7F-vx-pnM\";",
|
"comment" : "Class = \"UILabel\"; text = \"Frame Rate\"; ObjectID = \"E7F-vx-pnM\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -1681,6 +1891,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"GX4-zV-pQ8.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Map Gyro to\"; ObjectID = \"GX4-zV-pQ8\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Map Gyro to"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "陀螺仪映射为"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "陀螺儀映射為"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "陀螺儀映射為"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"hfR-K8-Ti4.segmentTitles[0]" : {
|
"hfR-K8-Ti4.segmentTitles[0]" : {
|
||||||
"comment" : "Class = \"UISegmentedControl\"; hfR-K8-Ti4.segmentTitles[0] = \"Stereo\"; ObjectID = \"hfR-K8-Ti4\";",
|
"comment" : "Class = \"UISegmentedControl\"; hfR-K8-Ti4.segmentTitles[0] = \"Stereo\"; ObjectID = \"hfR-K8-Ti4\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
@@ -3931,6 +4171,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"yJv-xb-5HL.text" : {
|
||||||
|
"comment" : "Class = \"UILabel\"; text = \"Pitch Sensitivity\"; ObjectID = \"yJv-xb-5HL\";",
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Pitch Sensitivity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "俯仰灵敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "俯仰靈敏度"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "俯仰靈敏度"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Z9M-yq-an2.title" : {
|
"Z9M-yq-an2.title" : {
|
||||||
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"Z9M-yq-an2\";",
|
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"Z9M-yq-an2\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
|
|||||||
Reference in new issue
Block a user