feat: motion control - direct mapping from gyro to mouse or controller sticks, with on-screen button "gyro" for toggling
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
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 */; };
|
||||
182760F72E83E2EA00EC1C5C /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = 182760F62E83E2EA00EC1C5C /* DequeModule */; };
|
||||
182760FA2E83E57D00EC1C5C /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = 182760F92E83E57D00EC1C5C /* DequeModule */; };
|
||||
@@ -160,6 +161,7 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -635,6 +637,7 @@
|
||||
847D2FDA2DDA015F00937A62 /* MenuSectionView.m */,
|
||||
FBDE86E419F82297001C18A8 /* UIAppView.h */,
|
||||
FBDE86E519F82297001C18A8 /* UIAppView.m */,
|
||||
180E6E362E93BFDB0067C73A /* CountdownAlertController.swift */,
|
||||
1888C1902E24C2C800108730 /* AboutView.swift */,
|
||||
1888C1912E24C33100108730 /* AboutViewController.swift */,
|
||||
FB89463719F6473800339C8A /* Launch Screen.xib */,
|
||||
@@ -1054,6 +1057,7 @@
|
||||
840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */,
|
||||
187F4B282E04658A00EECC59 /* WidgetPanelStackView.m in Sources */,
|
||||
FB4678FA1A55FFAD00377732 /* DiscoveryManager.m in Sources */,
|
||||
180E6E372E93BFDB0067C73A /* CountdownAlertController.swift in Sources */,
|
||||
841BAC922DE35D6B00DA639E /* ThemeManager.m in Sources */,
|
||||
98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */,
|
||||
FB89463519F646E200339C8A /* MainFrameViewController.m in Sources */,
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<viewControllerLayoutGuide type="bottom" id="XvO-F3-j4u"/>
|
||||
</layoutGuides>
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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"/>
|
||||
</constraints>
|
||||
</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">
|
||||
<rect key="frame" x="23" y="1649" width="318" height="31"/>
|
||||
<subviews>
|
||||
@@ -1201,10 +1226,123 @@
|
||||
<constraint firstAttribute="height" constant="31" id="ucg-xc-5Iy"/>
|
||||
</constraints>
|
||||
</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>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<size key="freeformSize" width="2000" height="5000"/>
|
||||
<size key="freeformSize" width="2165" height="5000"/>
|
||||
<connections>
|
||||
<outlet property="audioConfigSelector" destination="iZL-dA-jk9" id="Yja-os-o6g"/>
|
||||
<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="gyroSensitivitySlider" destination="3Ge-IB-PBj" id="Pdg-ke-KG3"/>
|
||||
<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="hdrSwitch" destination="wIq-fF-X8l" id="Ozz-h0-nJL"/>
|
||||
<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="localVolumeSlider" destination="aDm-fu-eWR" id="hR5-TW-rBf"/>
|
||||
<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="micVolumeStack" destination="xbj-CP-fhP" id="Bto-Me-9Qj"/>
|
||||
<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="pipStack" destination="12a-Wi-LlL" id="eTJ-0h-hRB"/>
|
||||
<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="pointerVelocityFactorStack" destination="vdk-mK-ztu" id="Cik-Rh-pGS"/>
|
||||
<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="reverseMouseWheelDirectionSelector" destination="mTM-NL-CcF" id="EYZ-Qg-OLu"/>
|
||||
<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="sendDummyEventStack" destination="RGd-TV-mkU" id="87h-08-4Og"/>
|
||||
<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="swapAbxyStack" destination="2rP-Ui-wVU" id="gUR-Vz-ORP"/>
|
||||
<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="touchModeSelector" destination="bhC-da-VGd" id="hna-Cr-kyd"/>
|
||||
<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="useBuiltinMicStack" destination="jCF-si-KLC" id="AnY-Ob-jE9"/>
|
||||
<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="yuv444Switch" destination="jIQ-jh-tvP" id="Qru-gO-haR"/>
|
||||
</connections>
|
||||
@@ -1327,7 +1478,7 @@
|
||||
</items>
|
||||
</navigationBar>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-503" y="1978"/>
|
||||
<point key="canvasLocation" x="-503.59712230215831" y="1977.520661157025"/>
|
||||
</scene>
|
||||
<!--Reveal View Controller-->
|
||||
<scene sceneID="rR7-ZT-bc7">
|
||||
@@ -1426,7 +1577,7 @@
|
||||
<viewControllerLayoutGuide type="bottom" id="eaS-aj-VtA"/>
|
||||
</layoutGuides>
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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"/>
|
||||
</layoutGuides>
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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"/>
|
||||
</constraints>
|
||||
</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>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<size key="freeformSize" width="1200" height="2500"/>
|
||||
<size key="freeformSize" width="1200" height="5000"/>
|
||||
<connections>
|
||||
<outlet property="audioConfigSelector" destination="hfR-K8-Ti4" id="4La-bI-jLq"/>
|
||||
<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="gyroSensitivitySlider" destination="mHC-z4-AWQ" id="GHN-po-QYw"/>
|
||||
<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="hdrSwitch" destination="Kgb-YU-AFW" id="ksG-A4-HhS"/>
|
||||
<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="localVolumeSlider" destination="7xn-es-SaH" id="au4-HZ-ff6"/>
|
||||
<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="micVolumeStack" destination="y2W-wf-sBM" id="UTS-Oa-fak"/>
|
||||
<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="pipStack" destination="hvN-TZ-ziV" id="6oL-0b-Bto"/>
|
||||
<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="pointerVelocityFactorStack" destination="PAv-Ep-ZJo" id="fxb-uz-e1a"/>
|
||||
<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="reverseMouseWheelDirectionSelector" destination="Uca-fD-4AC" id="gLa-40-suX"/>
|
||||
<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="sendDummyEventStack" destination="6yl-Mb-0o6" id="KN5-ty-oTQ"/>
|
||||
<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="useBuiltinMicStack" destination="lbL-2m-AO2" id="ZDm-aL-1cC"/>
|
||||
<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="yuv444Switch" destination="OZK-ba-DD9" id="SZA-F4-eo3"/>
|
||||
</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 * externalDisplayMode;
|
||||
@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) enum {
|
||||
CODEC_PREF_AUTO,
|
||||
|
||||
@@ -130,6 +130,9 @@
|
||||
self.framePacingMode = settings.framePacingMode;
|
||||
self.sendDummyEvent = settings.sendDummyEvent;
|
||||
self.rememberFoldState = settings.rememberFoldState;
|
||||
self.gyroBiasX = settings.gyroBiasX;
|
||||
self.gyroBiasY = settings.gyroBiasY;
|
||||
self.gyroBiasZ = settings.gyroBiasZ;
|
||||
self.backgroundSessionTimer = settings.backroundSessionTimer;
|
||||
#endif
|
||||
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_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.
|
||||
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 NSMutableArray <NSData *> *buttonStatesEncoded;
|
||||
@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;
|
||||
|
||||
|
||||
@@ -31,6 +31,16 @@
|
||||
[encoder encodeObject:self.name forKey:@"name"];
|
||||
[encoder encodeObject:self.buttonStatesEncoded forKey:@"buttonStates"];
|
||||
[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 {
|
||||
@@ -38,9 +48,35 @@
|
||||
self.name = [decoder decodeObjectForKey:@"name"];
|
||||
self.buttonStatesEncoded = [decoder decodeObjectForKey:@"buttonStates"];
|
||||
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;
|
||||
}
|
||||
|
||||
- (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
|
||||
|
||||
@@ -45,10 +45,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* Returns the index of the 'selected' profile within the array it's in
|
||||
*/
|
||||
- (NSInteger) getIndexOfSelectedProfile;
|
||||
- (void)replaceSelectedProfileWith:(OSCProfile*)newProfile;
|
||||
- (void)replaceSelectedProfileWith:(OSCProfile*)newProfile overwriteDefault:(bool)overwriteDefault;
|
||||
|
||||
- (NSMutableArray *) getEncodedProfiles;
|
||||
- (NSData* )getSelectedEncodedProfile;
|
||||
// - (NSData* )getSelectedEncodedProfile;
|
||||
- (void) importEncodedProfiles:(NSMutableArray* )profilesEncoded;
|
||||
- (OnScreenButtonState *)unarchiveButtonStateEncoded:(NSData *)data;
|
||||
- (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
|
||||
*/
|
||||
- (void) saveProfileWithName:(NSString*)name andButtonLayers:(NSMutableArray *)buttonLayers;
|
||||
- (void) duplicateSelectedProfileWithName:(NSString*)name;
|
||||
|
||||
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers;
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ static CGRect layoutViewBounds;
|
||||
+ (OSCProfilesManager *) sharedManager:(CGRect)viewBounds {
|
||||
static OSCProfilesManager *_sharedManager = nil;
|
||||
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);
|
||||
dispatch_once(&onceToken, ^{
|
||||
_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;
|
||||
NSMutableArray *profiles = [self getAllProfiles];
|
||||
for (OSCProfile *profile in profiles) {
|
||||
@@ -114,6 +114,7 @@ static CGRect layoutViewBounds;
|
||||
}
|
||||
}
|
||||
if(index>0) profiles[index] = newProfile;
|
||||
else if(overwriteDefault) profiles[index] = newProfile;
|
||||
|
||||
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles];
|
||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
|
||||
@@ -226,14 +227,16 @@ static CGRect layoutViewBounds;
|
||||
|
||||
- (OSCProfile *) getSelectedProfile {
|
||||
NSMutableArray *profiles = [self getAllProfiles];
|
||||
|
||||
if(profiles.count == 0){
|
||||
[self importDefaultTemplates];
|
||||
profiles = [self getAllProfiles];
|
||||
}
|
||||
for (OSCProfile *profile in profiles) {
|
||||
|
||||
if (profile.isSelected) {
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
return profiles[0];
|
||||
}
|
||||
|
||||
- (NSInteger) getIndexOfSelectedProfile {
|
||||
@@ -274,47 +277,40 @@ static CGRect layoutViewBounds;
|
||||
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers {
|
||||
NSMutableArray* buttonStatesEncoded = [self convertOnScreenControllerAndWidgetsToButtonStates:oscButtonLayers];
|
||||
if([self getIndexOfSelectedProfile]==0) return false;
|
||||
/*
|
||||
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
|
||||
|
||||
|
||||
[self replaceSelectedProfileWith:newProfile];
|
||||
*/
|
||||
OSCProfile *selectedProfile = [self getSelectedProfile];
|
||||
selectedProfile.buttonStatesEncoded = buttonStatesEncoded;
|
||||
[self replaceSelectedProfileWith:selectedProfile overwriteDefault:NO];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
- (void) saveProfileWithName:(NSString*)name andButtonLayers:(NSMutableArray *)oscButtonLayers {
|
||||
NSMutableArray* buttonStatesEncoded = [self convertOnScreenControllerAndWidgetsToButtonStates:oscButtonLayers];
|
||||
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];
|
||||
}
|
||||
- (void) duplicateSelectedProfileWithName:(NSString*)name {
|
||||
if ([self profileNameAlreadyExist:name]) return;
|
||||
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 *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 */
|
||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"OSCProfiles"];
|
||||
[[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);
|
||||
*/
|
||||
NSMutableArray *profiles = [self getAllProfiles];
|
||||
OSCProfile* newProfile = nil;
|
||||
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 */
|
||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"OSCProfiles"];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,10 +70,8 @@
|
||||
self.isHidden = [decoder decodeBoolForKey:@"isHidden"];
|
||||
self.widthFactor = [decoder decodeFloatForKey:@"widthFactor"];
|
||||
self.heightFactor = [decoder decodeFloatForKey:@"heightFactor"];
|
||||
self.sensitivityFactorX = [decoder decodeFloatForKey:@"sensitivityFactorX"];
|
||||
self.sensitivityFactorX = self.sensitivityFactorX == 0 ? 1.0 : self.sensitivityFactorX;
|
||||
self.sensitivityFactorY = [decoder decodeFloatForKey:@"sensitivityFactorY"];
|
||||
self.sensitivityFactorY = self.sensitivityFactorY == 0 ? 1.0 : self.sensitivityFactorY;
|
||||
self.sensitivityFactorX = [decoder containsValueForKey:@"sensitivityFactorX"] ? [decoder decodeFloatForKey:@"sensitivityFactorX"] : 1.0;
|
||||
self.sensitivityFactorY = [decoder containsValueForKey:@"sensitivityFactorY"] ? [decoder decodeFloatForKey:@"sensitivityFactorY"] : 1.0;
|
||||
self.decelerationRate = [decoder decodeFloatForKey:@"decelerationRate"];
|
||||
self.stickIndicatorOffset = [decoder decodeFloatForKey:@"stickIndicatorOffset"];
|
||||
self.oscLayerSizeFactor = [decoder decodeFloatForKey:@"oscLayerSizeFactor"];
|
||||
|
||||
@@ -15,7 +15,7 @@ import CoreMotion
|
||||
func gyroMixInputStarted() -> Bool
|
||||
}
|
||||
|
||||
@objc class MotionHandler: NSObject, OscInstanceProviderDelegate, OnScreenWidgetStickMixedInputDelegate{
|
||||
@objc class MotionHandler: NSObject, OscInstanceReceiverDelegate, OnScreenWidgetStickMixedInputDelegate{
|
||||
func mixRightStickAndGyroInput(x: CGFloat, y: CGFloat) {
|
||||
rightStickTouchInputX = x
|
||||
rightStickTouchInputY = y
|
||||
@@ -27,7 +27,7 @@ import CoreMotion
|
||||
}
|
||||
|
||||
func gyroMixInputStarted() -> Bool {
|
||||
return gyroControlStarted
|
||||
return gyroIsWorking
|
||||
}
|
||||
|
||||
func getOnScreenControlsInstance(_ sender: Any!) {
|
||||
@@ -42,10 +42,19 @@ import CoreMotion
|
||||
static let shared = 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 gyroIsWorking: Bool = false
|
||||
private var accelControlStarted: Bool = false
|
||||
private let motionManager = CMMotionManager()
|
||||
public var sensitvityYaw:CGFloat = 1.0
|
||||
@@ -57,15 +66,26 @@ import CoreMotion
|
||||
public let stickMaxOffset: CGFloat = 0x7FFE
|
||||
private var stickInputScale: CGFloat = 35
|
||||
|
||||
var yaw:Double = 0
|
||||
var pitch:Double = 0
|
||||
var roll:Double = 0
|
||||
private var yaw:Double = 0
|
||||
private var pitch:Double = 0
|
||||
private var roll:Double = 0
|
||||
|
||||
var rightStickTouchInputX:Double = 0
|
||||
var rightStickTouchInputY:Double = 0
|
||||
private var isCalibrating: Bool = false
|
||||
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
|
||||
|
||||
private var rightStickTouchInputX:Double = 0
|
||||
private var rightStickTouchInputY:Double = 0
|
||||
|
||||
var leftStickTouchInputX:Double = 0
|
||||
var leftStickTouchInputY:Double = 0
|
||||
private var leftStickTouchInputX:Double = 0
|
||||
private var leftStickTouchInputY:Double = 0
|
||||
|
||||
var updateInterval: TimeInterval = 1.0 / 120.0 {
|
||||
didSet {
|
||||
@@ -75,6 +95,8 @@ import CoreMotion
|
||||
}
|
||||
|
||||
@objc public override init() {
|
||||
self.oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
self.oscProfile = oscProfileMan.getSelectedProfile()
|
||||
super.init()
|
||||
if #available(iOS 13.0, *) {
|
||||
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
|
||||
gyroIsWorking = false
|
||||
needInterruption = interruption
|
||||
if motionManager.isGyroActive{
|
||||
motionManager.stopGyroUpdates()
|
||||
}
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
self.clearGyroInput(interruption: needInterruption)
|
||||
}
|
||||
|
||||
@objc public func stopAccelUpdate() {
|
||||
@@ -126,14 +151,13 @@ import CoreMotion
|
||||
if motionManager.isAccelerometerActive {
|
||||
motionManager.stopAccelerometerUpdates()
|
||||
}
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
// self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
// self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
}
|
||||
|
||||
// MARK: - 私有方法处理数据
|
||||
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) {
|
||||
@@ -142,67 +166,109 @@ import CoreMotion
|
||||
var pitchSource: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, *) {
|
||||
let orientation = (windowScene as! UIWindowScene).interfaceOrientation
|
||||
print("界面方向变化: \(orientation)")
|
||||
switch orientation {
|
||||
case .landscapeLeft:
|
||||
yawSource = x
|
||||
pitchSource = -y
|
||||
rollSource = -z
|
||||
yawBias = gyroBiasX
|
||||
pitchBias = -gyroBiasY
|
||||
rollBias = -gyroBiasZ
|
||||
yawSource = x-yawBias
|
||||
pitchSource = -y-pitchBias
|
||||
rollSource = -z-rollBias
|
||||
case .landscapeRight:
|
||||
yawSource = -x
|
||||
pitchSource = y
|
||||
rollSource = -z
|
||||
yawBias = -gyroBiasX
|
||||
pitchBias = gyroBiasY
|
||||
rollBias = -gyroBiasZ
|
||||
yawSource = -x-yawBias
|
||||
pitchSource = y-pitchBias
|
||||
rollSource = -z-rollBias
|
||||
case .portrait:
|
||||
yawSource = -y
|
||||
pitchSource = -x
|
||||
rollSource = -z
|
||||
yawBias = -gyroBiasY
|
||||
pitchBias = -gyroBiasX
|
||||
rollBias = -gyroBiasZ
|
||||
yawSource = -y-yawBias
|
||||
pitchSource = -x-pitchBias
|
||||
rollSource = -z-rollBias
|
||||
case .portraitUpsideDown:
|
||||
yawSource = y
|
||||
pitchSource = x
|
||||
rollSource = -z
|
||||
yawBias = gyroBiasY
|
||||
pitchBias = gyroBiasX
|
||||
rollBias = -gyroBiasZ
|
||||
yawSource = y-yawBias
|
||||
pitchSource = x-pitchBias
|
||||
rollSource = -z-rollBias
|
||||
default:
|
||||
yawSource = x
|
||||
pitchSource = -y
|
||||
rollSource = -z
|
||||
yawBias = gyroBiasX
|
||||
pitchBias = -gyroBiasY
|
||||
rollBias = -gyroBiasZ
|
||||
yawSource = x-yawBias
|
||||
pitchSource = -y-pitchBias
|
||||
rollSource = -z-rollBias
|
||||
}
|
||||
} else {
|
||||
yawSource = x
|
||||
pitchSource = -y
|
||||
rollSource = z
|
||||
let orientation = UIApplication.shared.statusBarOrientation
|
||||
if orientation.isLandscape {
|
||||
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 {
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
self.clearGyroInput(interruption: needInterruption)
|
||||
print("Gyro: stopped")
|
||||
return
|
||||
}
|
||||
|
||||
if 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 {
|
||||
roll = leftStickTouchInputX + gyroInputToStickInput(input:rollSource*sensitvityRoll*10)
|
||||
roll = self.clampStickInput(input: roll)
|
||||
self.onScreenControls.sendLeftStickTouchPadEvent(roll, leftStickTouchInputY)
|
||||
}
|
||||
gyroIsWorking = true
|
||||
|
||||
if false {
|
||||
if oscProfile.mapGyroTo == MapGyroTo.mapGyroToMouse {
|
||||
yaw = yawSource*sensitvityYaw*30
|
||||
pitch = pitchSource*sensitvityPitch*30
|
||||
LiSendMouseMoveEvent(Int16(yaw),Int16(pitch))
|
||||
}
|
||||
|
||||
if false {
|
||||
roll = rollSource*sensitvityPitch*100
|
||||
LiSendMouseMoveEvent(Int16(roll),0)
|
||||
if oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick {
|
||||
if oscProfile.yawPitchToRightStick {
|
||||
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{
|
||||
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;
|
||||
|
||||
// sending self as an instance to OnScreenWidgetView or other related classes
|
||||
@protocol OscInstanceProviderDelegate <NSObject>
|
||||
@protocol OscInstanceReceiverDelegate <NSObject>
|
||||
- (void)getOnScreenControlsInstance:(id)sender; // Renamed delegate method
|
||||
@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;
|
||||
|
||||
// @property (nonatomic, assign) CustomTapGestureRecognizer* mouseRightClickTapRecognizer; // this object will be passed to onscreencontrols class for areVirtualControllerTaps flag setting
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc class OnScreenWidgetView: UIView, OscInstanceProviderDelegate {
|
||||
@objc class OnScreenWidgetView: UIView, OscInstanceReceiverDelegate {
|
||||
// receiving the OnScreenControls instance from delegate
|
||||
@objc func getOnScreenControlsInstance(_ sender: Any) {
|
||||
if let controls = sender as? OnScreenControls {
|
||||
@@ -35,8 +35,8 @@ import UIKit
|
||||
@objc protocol OnScreenFunctionalButtonDelegate: AnyObject {
|
||||
func startGyroUpdate()
|
||||
func startAccelUpdate()
|
||||
func stopGyroUpdate()
|
||||
func stopAccelUpdate()
|
||||
func stopGyroUpdate(interruption:Bool)
|
||||
func stopAccelUpdate(interruption:Bool)
|
||||
func openWidgetLayoutTool()
|
||||
func switchWidgetProfile()
|
||||
func bringUpSoftKeyboard()
|
||||
@@ -51,6 +51,9 @@ import UIKit
|
||||
case touchPad
|
||||
}
|
||||
|
||||
private let oscProfileMan: OSCProfilesManager = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
private var oscProfile: OSCProfile
|
||||
|
||||
@objc public var widgetType: WidgetTypeEnum = WidgetTypeEnum.uninitialized
|
||||
|
||||
@objc static public var editMode: Bool = false
|
||||
@@ -290,6 +293,7 @@ import UIKit
|
||||
self.pointerIdPool.insert(UInt32(i))
|
||||
}
|
||||
self.activePointerIds = []
|
||||
self.oscProfile = oscProfileMan.getSelectedProfile()
|
||||
super.init(frame: .zero)
|
||||
|
||||
upIndicator = createLrudDirectionLayer()
|
||||
@@ -1189,12 +1193,13 @@ import UIKit
|
||||
// vertical input must be inverted
|
||||
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)
|
||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
||||
|
||||
let mixRightStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick
|
||||
&& oscProfile.yawPitchToRightStick)
|
||||
if !mixRightStickInputToGyro || (self.mixInputDelegate?.gyroMixInputStarted() != true) {
|
||||
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){
|
||||
@@ -1202,12 +1207,13 @@ import UIKit
|
||||
var targetY = -self.touchInputToStickInput(input: inputY)
|
||||
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)
|
||||
if self.mixInputDelegate?.gyroMixInputStarted() != true {
|
||||
|
||||
let mixLeftStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick
|
||||
&& oscProfile.rollToLeftStick)
|
||||
if !mixLeftStickInputToGyro || (self.mixInputDelegate?.gyroMixInputStarted() != true) {
|
||||
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}
|
||||
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":
|
||||
self.functionalButtonDelegate?.stopGyroUpdate()
|
||||
self.functionalButtonDelegate?.stopGyroUpdate(interruption: false)
|
||||
case "ACCEL":
|
||||
self.functionalButtonDelegate?.stopAccelUpdate()
|
||||
self.functionalButtonDelegate?.stopAccelUpdate(interruption: false)
|
||||
case "MOTION":
|
||||
self.functionalButtonDelegate?.stopGyroUpdate()
|
||||
self.functionalButtonDelegate?.stopAccelUpdate()
|
||||
self.functionalButtonDelegate?.stopGyroUpdate(interruption: false)
|
||||
self.functionalButtonDelegate?.stopAccelUpdate(interruption: false)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
else{
|
||||
self.mixInputDelegate?.mixRightStickAndGyroInput(x: 0, y: 0)
|
||||
}
|
||||
self.mixInputDelegate?.mixRightStickAndGyroInput(x: 0, y: 0)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
else{
|
||||
self.mixInputDelegate?.mixLeftStickAndGyroInput(x: 0, y: 0)
|
||||
}
|
||||
self.mixInputDelegate?.mixLeftStickAndGyroInput(x: 0, y: 0)
|
||||
}
|
||||
|
||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
@@ -1844,11 +1850,11 @@ import UIKit
|
||||
override func didMoveToSuperview() {
|
||||
super.didMoveToSuperview()
|
||||
if superview == nil {
|
||||
if self.functionalButtonString == "GYRO" {self.functionalButtonDelegate?.stopGyroUpdate()}
|
||||
if self.functionalButtonString == "ACCEL" {self.functionalButtonDelegate?.stopAccelUpdate()}
|
||||
if self.functionalButtonString == "GYRO" {self.functionalButtonDelegate?.stopGyroUpdate(interruption: true)}
|
||||
if self.functionalButtonString == "ACCEL" {self.functionalButtonDelegate?.stopAccelUpdate(interruption: true)}
|
||||
if self.functionalButtonString == "MOTION" {
|
||||
self.functionalButtonDelegate?.stopGyroUpdate()
|
||||
self.functionalButtonDelegate?.stopAccelUpdate()
|
||||
self.functionalButtonDelegate?.stopGyroUpdate(interruption: true)
|
||||
self.functionalButtonDelegate?.stopAccelUpdate(interruption: true)
|
||||
}
|
||||
buttonDownVisualEffectLayer.borderWidth = 0
|
||||
buttonDownVisualEffectLayer.borderColor = defaultBorderColor
|
||||
|
||||
+12
-11
@@ -76,7 +76,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
UIKeyModifierFlags comboKeyModifierFlags;
|
||||
|
||||
WidgetSizeTransition _widgetSizeTransition;
|
||||
OSCProfilesManager* profilesManager;
|
||||
OSCProfilesManager* oscProfileMan;
|
||||
}
|
||||
|
||||
- (void) setupStreamView:(ControllerSupport*)controllerSupport
|
||||
@@ -499,21 +499,21 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
if(relocatedWidgetDict.allKeys.count == 0) return;
|
||||
|
||||
profilesManager = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
||||
OSCProfile *newProfile = [profilesManager getSelectedProfile];
|
||||
oscProfileMan = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
||||
OSCProfile *newProfile = [oscProfileMan getSelectedProfile];
|
||||
|
||||
for (NSInteger i = 0; i < newProfile.buttonStatesEncoded.count; i++) {
|
||||
NSData* buttonStateEncoded = newProfile.buttonStatesEncoded[i];
|
||||
OnScreenButtonState *newButtonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
OnScreenButtonState *newButtonState = [oscProfileMan unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if([relocatedWidgetDict.allKeys containsObject: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];
|
||||
newProfile.buttonStatesEncoded[i] = newButtonStateEncoded;
|
||||
}
|
||||
}
|
||||
|
||||
[profilesManager replaceSelectedProfileWith:newProfile];
|
||||
[oscProfileMan replaceSelectedProfileWith:newProfile overwriteDefault:NO];
|
||||
}
|
||||
|
||||
- (void) reloadOnScreenWidgetViews{
|
||||
@@ -532,13 +532,14 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
OnScreenWidgetView.buttonVisualFeedbackEnabled = settings.buttonVisualFeedback;
|
||||
|
||||
if(!profilesManager) profilesManager = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
|
||||
if(!oscProfileMan) oscProfileMan = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
||||
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.
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
OnScreenButtonState* buttonState = [oscProfileMan unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if(buttonState.widgetType == CustomOnScreenWidget){
|
||||
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.mixInputDelegate = (id<OnScreenWidgetStickMixedInputDelegate>) [MotionHandler sharedInstance];
|
||||
widgetView.mixInputDelegate = (id<OnScreenWidgetStickMixedInputDelegate>)motionHandler;
|
||||
|
||||
widgetView.identifier = buttonState.identifier;
|
||||
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="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="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="gyroSensitivity" optional="YES" attributeType="Float" defaultValueString="1" 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" : {
|
||||
"extractionState" : "manual",
|
||||
"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" : {
|
||||
"extractionState" : "manual",
|
||||
"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" : {
|
||||
"extractionState" : "manual",
|
||||
"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%%" : {
|
||||
"extractionState" : "manual",
|
||||
"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" : {
|
||||
"extractionState" : "manual",
|
||||
"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+)" : {
|
||||
"extractionState" : "manual",
|
||||
"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" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
|
||||
@@ -22,11 +22,14 @@
|
||||
@property (class, nonatomic, assign) BOOL overridePersistedFoldState;
|
||||
|
||||
// 外部可访问属性
|
||||
@property (nonatomic, strong) UIStackView *rootStackView;
|
||||
@property (nonatomic, assign) CGFloat leadingTrailingPadding;
|
||||
@property (nonatomic, assign) CGFloat separatorLinePadding;
|
||||
@property (nonatomic, copy) NSString *sectionTitle;
|
||||
@property (nonatomic, copy) NSString *identifier;
|
||||
@property (nonatomic, assign) BOOL isExpanded;
|
||||
@property (nonatomic, assign) BOOL expandable;
|
||||
@property (nonatomic, copy) void (^lockedSectionHandler)(void);
|
||||
@property (nonatomic, strong) UIColor *backgroundColor;
|
||||
@property (nonatomic, assign) CGFloat rootStackViewSpacing;
|
||||
@property (nonatomic, assign) CGFloat headerViewHeight;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
@interface MenuSectionView ()
|
||||
|
||||
@property (nonatomic, strong) UIStackView *rootStackView;
|
||||
@property (nonatomic, strong) UILabel *titleLabel;
|
||||
@property (nonatomic, strong) UIButton *toggleButton;
|
||||
@property (nonatomic, strong) UIButton *toggleArea;
|
||||
@@ -57,6 +56,7 @@ static BOOL overridePersistedFoldState = YES;
|
||||
_separatorLinePadding = 40;
|
||||
_sectionTitle = @"Section";
|
||||
_isExpanded = YES;
|
||||
_expandable = true;
|
||||
_backgroundColor = [UIColor clearColor];
|
||||
_rootStackViewSpacing = [self isIPhone] ? 10 : 13.8;
|
||||
_subStackViews = [NSMutableArray array];
|
||||
@@ -235,7 +235,7 @@ static BOOL overridePersistedFoldState = YES;
|
||||
}
|
||||
|
||||
- (void)setExpanded:(BOOL)isExpanded {
|
||||
self.isExpanded = isExpanded;
|
||||
self.isExpanded = !_expandable ? false : isExpanded;
|
||||
[self updateViewForFoldState];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isExpanded forKey:self.identifier];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
@@ -323,8 +323,9 @@ static BOOL overridePersistedFoldState = YES;
|
||||
}
|
||||
|
||||
- (void)toggleFold {
|
||||
self.isExpanded = !self.isExpanded;
|
||||
self.isExpanded = !_expandable ? false : !self.isExpanded;
|
||||
[self updateViewForFoldState];
|
||||
if(!_expandable) self.lockedSectionHandler();
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isExpanded forKey:self.identifier];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
+7
-4
@@ -1270,10 +1270,12 @@
|
||||
self->_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
|
||||
|
||||
//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
|
||||
NSLog(@"profile profile");
|
||||
[self reloadLegacyOnScreenControls];
|
||||
self->_oscProfilesTableViewController.currentOSCButtonLayers = self.layoutOSC.OSCButtonLayers; //pass updated OSCLayout to OSCProfileTableView again
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
[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.
|
||||
@@ -1298,8 +1300,9 @@
|
||||
_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
|
||||
_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
|
||||
[self reloadLegacyOnScreenControls];
|
||||
[weakSelf reloadLegacyOnScreenControls];
|
||||
};
|
||||
|
||||
self.widgetPanelStack.hidden = YES;
|
||||
|
||||
@@ -159,8 +159,8 @@ const double NAV_BAR_HEIGHT = 50;
|
||||
[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
|
||||
[self->profilesManager saveProfileWithName: enteredProfileName andButtonLayers:self.currentOSCButtonLayers]; // the OSC layout here is passed from parent LayoutOSCViewController;
|
||||
[self->profilesManager setProfileToSelected: [self->profilesManager getIndexOfLastProfile]];
|
||||
[self->profilesManager duplicateSelectedProfileWithName:enteredProfileName]; // the OSC layout here is passed from parent LayoutOSCViewController;
|
||||
// [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
|
||||
|
||||
|
||||
@@ -147,6 +147,24 @@
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *rememberFoldStateStack;
|
||||
@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;
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#import "ThemeManager.h"
|
||||
#import "Connection.h"
|
||||
#import "Plot.h"
|
||||
#import "OSCProfilesManager.h"
|
||||
|
||||
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||
#import <VideoToolbox/VideoToolbox.h>
|
||||
@@ -24,6 +25,8 @@
|
||||
@implementation SettingsViewController {
|
||||
TemporarySettings* tempSettings;
|
||||
DataManager* dataMan;
|
||||
OSCProfilesManager* oscProfileMan;
|
||||
OSCProfile* oscProfile;
|
||||
|
||||
NSLayoutConstraint *parentStackLeadingConstraint;
|
||||
NSLayoutConstraint *parentStackWidthConstraint;
|
||||
@@ -43,6 +46,7 @@
|
||||
CGFloat _scrollSpeed;
|
||||
CGFloat _currentRefreshRate;
|
||||
MenuSectionView *touchAndControlSection;
|
||||
MenuSectionView *motionControlSection;
|
||||
MenuSectionView *videoSection;
|
||||
MenuSectionView *otherSection;
|
||||
MenuSectionView *experimentalSection;
|
||||
@@ -342,6 +346,47 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
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{
|
||||
[super viewWillAppear:NO];
|
||||
|
||||
@@ -350,7 +395,6 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
self.micHandler = [MicHandler new];
|
||||
[self.micHandler startTapping];
|
||||
*/
|
||||
|
||||
|
||||
[self updateParentStackHorizontalConstraints];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
@@ -362,6 +406,11 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
selector:@selector(updateTheme)
|
||||
name:ThemeDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(reloadMotionControlConfigs)
|
||||
name:@"OscLayoutCloseNotification"
|
||||
object:nil];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
if(self.mainFrameViewController.settingsExpandedInStreamView){
|
||||
@@ -383,6 +432,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
}
|
||||
|
||||
if(![self manuallyChangedFPS]) [self framerateChanged];
|
||||
|
||||
[self reloadMotionControlConfigs];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated{
|
||||
@@ -390,10 +441,10 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self updateResolutionTable];
|
||||
[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);
|
||||
[self.customResolutionSwitch setOn: isCustomResolution(tempSettings.resolutionSelected.intValue)];
|
||||
[self.customResolutionSwitch setOn: isCustomResolution(self->tempSettings.resolutionSelected.intValue)];
|
||||
[self.resolutionSelector setEnabled:!self.customResolutionSwitch.isOn];
|
||||
}
|
||||
|
||||
@@ -648,6 +699,22 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self addSetting:self.gyroSensitivityStack ofId:@"gyroSensitivityStack" withInfoTag:NO withDynamicLabel:YES to:touchAndControlSection];
|
||||
[touchAndControlSection addToParentStack:_parentStack];
|
||||
// [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];
|
||||
gesturesSection.delegate = self;
|
||||
@@ -1399,347 +1466,387 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
settingStackWillBeRelocatedToLowestPosition = false;
|
||||
hiddenStacks = [[NSMutableSet alloc] init];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"_UIConstraintBasedLayoutLogUnsatisfiable"];
|
||||
|
||||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||
[self.view addGestureRecognizer:longPress];
|
||||
|
||||
_settingStackDict = [[NSMutableDictionary alloc] init];
|
||||
|
||||
|
||||
for(UIView* view in self.view.subviews){
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
[self initParentStack];
|
||||
[UIView animateWithDuration:0 animations:^{
|
||||
|
||||
// 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->oscProfileMan = [OSCProfilesManager sharedManager:CGRectZero];
|
||||
|
||||
self->settingStackWillBeRelocatedToLowestPosition = false;
|
||||
self->hiddenStacks = [[NSMutableSet alloc] init];
|
||||
|
||||
// [self swi];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"_UIConstraintBasedLayoutLogUnsatisfiable"];
|
||||
|
||||
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];
|
||||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||
[self.view addGestureRecognizer:longPress];
|
||||
|
||||
settingsViewJustLoaded = true;
|
||||
self->_settingStackDict = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// 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;
|
||||
|
||||
for(UIView* view in self.view.subviews){
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
}
|
||||
if (!enable120Fps) {
|
||||
[self.framerateSelector removeSegmentAtIndex:2 animated:NO];
|
||||
}
|
||||
|
||||
// Disable codec selector segments for unsupported codecs
|
||||
#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
|
||||
// they support HEVC decoding (A9 or later).
|
||||
[self.resolutionSelector setEnabled:NO forSegmentAtIndex:2];
|
||||
}
|
||||
switch (tempSettings.preferredCodec) {
|
||||
case CODEC_PREF_AUTO:
|
||||
[self.codecSelector setSelectedSegmentIndex:self.codecSelector.numberOfSegments - 1];
|
||||
break;
|
||||
[self initParentStack];
|
||||
|
||||
// load rememberFoldState before section layout
|
||||
self->dataMan = [[DataManager alloc] init];
|
||||
self->tempSettings = [self->dataMan getSettings];
|
||||
[self.rememberFoldStateSwitch setOn:self->tempSettings.rememberFoldState];// Load old setting
|
||||
MenuSectionView.overridePersistedFoldState = !self->tempSettings.rememberFoldState;
|
||||
[self.rememberFoldStateSwitch addTarget:self action:@selector(rememberFoldStateSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
[self layoutSections];
|
||||
|
||||
|
||||
// [self swi];
|
||||
|
||||
self->slideToCloseSettingsViewRecognizer = [[CustomEdgeSlideGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)];
|
||||
self->slideToCloseSettingsViewRecognizer.edges = UIRectEdgeLeft;
|
||||
self->slideToCloseSettingsViewRecognizer.normalizedThresholdDistance = 0.0;
|
||||
self->slideToCloseSettingsViewRecognizer.EDGE_TOLERANCE = 10;
|
||||
self->slideToCloseSettingsViewRecognizer.immediateTriggering = true;
|
||||
self->slideToCloseSettingsViewRecognizer.delaysTouchesBegan = NO;
|
||||
self->slideToCloseSettingsViewRecognizer.delaysTouchesEnded = NO;
|
||||
[self.view addGestureRecognizer:self->slideToCloseSettingsViewRecognizer];
|
||||
|
||||
self->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;
|
||||
}
|
||||
|
||||
|
||||
self->currentSettingsMenuMode = self->tempSettings.settingsMenuMode.intValue;
|
||||
[self loadFavoriteSettingStackIdentifiers];
|
||||
if(self->tempSettings.settingsMenuMode.intValue == FavoriteSettings) [self switchToFavoriteSettings];
|
||||
|
||||
// Ensure we pick a bitrate that falls exactly onto a slider notch
|
||||
self->_bitrate = bitrateTable[[self getSliderValueForBitrate:[self->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([self->tempSettings.width integerValue], [self->tempSettings.height integerValue]); // custom initial value
|
||||
[self updateResolutionTable];
|
||||
|
||||
|
||||
NSInteger framerate;
|
||||
switch ([self->tempSettings.framerate integerValue]) {
|
||||
case 30:
|
||||
framerate = 0;
|
||||
break;
|
||||
default:
|
||||
case 60:
|
||||
framerate = 1;
|
||||
break;
|
||||
case 120:
|
||||
framerate = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
NSInteger resolution = self->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];
|
||||
}
|
||||
|
||||
[UIView animateWithDuration:0 animations:^{
|
||||
// Disable codec selector segments for unsupported codecs
|
||||
#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
|
||||
// they support HEVC decoding (A9 or later).
|
||||
[self.resolutionSelector setEnabled:NO forSegmentAtIndex:2];
|
||||
}
|
||||
|
||||
case CODEC_PREF_AV1:
|
||||
[self.codecSelector setSelectedSegmentIndex:2];
|
||||
break;
|
||||
|
||||
case CODEC_PREF_HEVC:
|
||||
[self.codecSelector setSelectedSegmentIndex:1];
|
||||
break;
|
||||
|
||||
case CODEC_PREF_H264:
|
||||
[self.codecSelector setSelectedSegmentIndex:0];
|
||||
break;
|
||||
}
|
||||
switch (self->tempSettings.preferredCodec) {
|
||||
case CODEC_PREF_AUTO:
|
||||
[self.codecSelector setSelectedSegmentIndex:self.codecSelector.numberOfSegments - 1];
|
||||
break;
|
||||
|
||||
case CODEC_PREF_AV1:
|
||||
[self.codecSelector setSelectedSegmentIndex:2];
|
||||
break;
|
||||
|
||||
case CODEC_PREF_HEVC:
|
||||
[self.codecSelector setSelectedSegmentIndex:1];
|
||||
break;
|
||||
|
||||
case CODEC_PREF_H264:
|
||||
[self.codecSelector setSelectedSegmentIndex:0];
|
||||
break;
|
||||
}
|
||||
}];
|
||||
|
||||
if (![self hdrSupported]) {
|
||||
[self.hdrSwitch setOn:NO];
|
||||
[self.hdrSwitch setEnabled:NO];
|
||||
}
|
||||
else {
|
||||
[self.hdrSwitch setOn:tempSettings.enableHdr];
|
||||
}
|
||||
if (![self hdrSupported]) {
|
||||
[self.hdrSwitch setOn:NO];
|
||||
[self.hdrSwitch setEnabled:NO];
|
||||
}
|
||||
else {
|
||||
[self.hdrSwitch setOn:self->tempSettings.enableHdr];
|
||||
}
|
||||
|
||||
[self.yuv444Switch setOn:tempSettings.enableYUV444];
|
||||
|
||||
[self.pipSwitch setOn:tempSettings.enablePIP];
|
||||
if(@available(iOS 15.0, *)) [self.pipSwitch setEnabled:true];
|
||||
else{
|
||||
[self.pipSwitch setOn:false];
|
||||
[self.pipSwitch setEnabled:false];
|
||||
}
|
||||
|
||||
[self.statsOverlaySelector setSelectedSegmentIndex:tempSettings.statsOverlayLevel.intValue];
|
||||
[self.yuv444Switch setOn:self->tempSettings.enableYUV444];
|
||||
|
||||
[self.pipSwitch setOn:self->tempSettings.enablePIP];
|
||||
if(@available(iOS 15.0, *)) [self.pipSwitch setEnabled:true];
|
||||
else{
|
||||
[self.pipSwitch setOn:false];
|
||||
[self.pipSwitch setEnabled:false];
|
||||
}
|
||||
|
||||
[self.statsOverlaySelector setSelectedSegmentIndex:self->tempSettings.statsOverlayLevel.intValue];
|
||||
|
||||
NSInteger renderingBackend = [tempSettings.renderingBackend integerValue];
|
||||
[self.renderingBackendSelector setSelectedSegmentIndex:renderingBackend];
|
||||
[self.renderingBackendSelector addTarget:self action:@selector(renderingBackendChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
NSInteger renderingBackend = [self->tempSettings.renderingBackend integerValue];
|
||||
[self.renderingBackendSelector setSelectedSegmentIndex:renderingBackend];
|
||||
[self.renderingBackendSelector addTarget:self action:@selector(renderingBackendChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
NSInteger framePacingMode = [tempSettings.framePacingMode integerValue];
|
||||
[self.framePacingModeSelector setSelectedSegmentIndex:framePacingMode];
|
||||
[self.framePacingModeSelector addTarget:self action:@selector(framePacingModeChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
[self framePacingModeChanged:self.framePacingModeSelector];
|
||||
NSInteger framePacingMode = [self->tempSettings.framePacingMode integerValue];
|
||||
[self.framePacingModeSelector setSelectedSegmentIndex:framePacingMode];
|
||||
[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
|
||||
[self renderingBackendChanged:self.renderingBackendSelector]; // Update PiP and frame pacing state based on current selection
|
||||
|
||||
[self.citrixX1MouseSwitch setOn:tempSettings.btMouseSupport];
|
||||
[self.optimizeGamesSwitch setOn: tempSettings.optimizeGames];
|
||||
[self.multiControllerSwitch setOn:tempSettings.multiController];
|
||||
[self.swapAbxySwitch setOn:tempSettings.swapABXYButtons];
|
||||
[self.buttonVisualFeedbackSwitch setOn:tempSettings.buttonVisualFeedback];
|
||||
[self.citrixX1MouseSwitch setOn:self->tempSettings.btMouseSupport];
|
||||
[self.optimizeGamesSwitch setOn: self->tempSettings.optimizeGames];
|
||||
[self.multiControllerSwitch setOn:self->tempSettings.multiController];
|
||||
[self.swapAbxySwitch setOn:self->tempSettings.swapABXYButtons];
|
||||
[self.buttonVisualFeedbackSwitch setOn:self->tempSettings.buttonVisualFeedback];
|
||||
|
||||
[self.gyroModeSelector setSelectedSegmentIndex:tempSettings.gyroMode.intValue];
|
||||
[self.gyroSensitivitySlider setValue: (uint16_t)(tempSettings.gyroSensitivity.floatValue * 100) animated:YES]; // Load old setting.
|
||||
[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]];
|
||||
[self.emulatedControllerTypeSelector addTarget:self action:@selector(emulatedControllerTypeChanged:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||
[self emulatedControllerTypeChanged:self.emulatedControllerTypeSelector];
|
||||
|
||||
[self.audioOnPcSwitch setOn:tempSettings.playAudioOnPC];
|
||||
|
||||
[self.localVolumeSlider setValue:tempSettings.localVolume.floatValue*100];
|
||||
[self.localVolumeSlider addTarget:self action:@selector(localVolumeSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||
[self localVolumeSliderMoved:self.localVolumeSlider];
|
||||
|
||||
[self.micVolumeSlider setValue:tempSettings.micVolume.floatValue*100];
|
||||
[self.micVolumeSlider addTarget:self action:@selector(micVolumeSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||
[self micVolumeSliderMoved:self.self.micVolumeSlider];
|
||||
|
||||
[self.redirectMicSwitch setOn:tempSettings.redirectMic];
|
||||
[self.redirectMicSwitch addTarget:self action:@selector(redirectMicSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||
[self redirectMicSwitchFlipped:self.redirectMicSwitch];
|
||||
|
||||
[self.useBuiltinMicSwitch setOn:tempSettings.useBuiltinMic];
|
||||
|
||||
_lastSelectedResolutionIndex = resolution;
|
||||
[self.resolutionSelector setSelectedSegmentIndex:resolution];
|
||||
[self.resolutionSelector addTarget:self action:@selector(newResolutionChosen) forControlEvents:UIControlEventValueChanged];
|
||||
[self.gyroModeSelector setSelectedSegmentIndex:self->tempSettings.gyroMode.intValue];
|
||||
[self.gyroSensitivitySlider setValue: (uint16_t)(self->tempSettings.gyroSensitivity.floatValue * 100) animated:YES]; // Load old setting.
|
||||
[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: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:_bitrate] animated:YES];
|
||||
[self.bitrateSlider addTarget:self action:@selector(bitrateSliderMoved) forControlEvents:UIControlEventValueChanged];
|
||||
[self updateBitrateText];
|
||||
[self updateResolutionDisplayLabel];
|
||||
[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:tempSettings.frameQueueSize.intValue];
|
||||
[self.frameQueueSizeSlider addTarget:self action:@selector(frameQueueSizeSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||
[self frameQueueSizeSliderMoved:self.frameQueueSizeSlider];
|
||||
[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: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)tempSettings.graphOpacity.intValue];
|
||||
[self.graphOpacityStepper addTarget:self action:@selector(graphOpacityStepperTapped:) forControlEvents:UIControlEventValueChanged];
|
||||
[self graphOpacityStepperTapped:self.graphOpacityStepper];
|
||||
[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 ([tempSettings.audioConfig integerValue]) {
|
||||
case 2:
|
||||
[self.audioConfigSelector setSelectedSegmentIndex:0];
|
||||
break;
|
||||
case 6:
|
||||
[self.audioConfigSelector setSelectedSegmentIndex:1];
|
||||
break;
|
||||
case 8:
|
||||
[self.audioConfigSelector setSelectedSegmentIndex:2];
|
||||
break;
|
||||
}
|
||||
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: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];
|
||||
// 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)tempSettings.backgroundSessionTimer.floatValue];
|
||||
[self.backgroundSessionTimerSlider addTarget:self action:@selector(backgroundSessionTimerSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||
[self backgroundSessionTimerSliderMoved:self.backgroundSessionTimerSlider];
|
||||
[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:tempSettings.liftStreamViewForKeyboard ? 1 : 0];// Load old setting
|
||||
// lift streamview setting
|
||||
[self.liftStreamViewForKeyboardSelector setSelectedSegmentIndex:self->tempSettings.liftStreamViewForKeyboard ? 1 : 0];// Load old setting
|
||||
|
||||
// showkeyboard toolbar setting
|
||||
[self.softKeyboardToolbarSwitch setOn:tempSettings.showKeyboardToolbar];// Load old setting
|
||||
// showkeyboard toolbar setting
|
||||
[self.softKeyboardToolbarSwitch setOn:self->tempSettings.showKeyboardToolbar];// Load old setting
|
||||
|
||||
// reverse mouse wheel direction setting
|
||||
[self.reverseMouseWheelDirectionSelector setSelectedSegmentIndex:tempSettings.reverseMouseWheelDirection ? 1 : 0];// 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)tempSettings.slideToSettingsScreenEdge.integerValue]];
|
||||
// Load old setting
|
||||
[self.slideToToolboxScreenEdgeSelector setEnabled:false];
|
||||
[self.slideToSettingsScreenEdgeSelector addTarget:self action:@selector(slideToSettingsScreenEdgeChanged) forControlEvents:UIControlEventValueChanged];
|
||||
[self slideToSettingsScreenEdgeChanged];
|
||||
// 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:tempSettings.slideToSettingsDistance.floatValue];
|
||||
[self.slideToMenuDistanceSlider addTarget:self action:@selector(slideToMenuDistanceSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||
[self slideToMenuDistanceSliderMoved:self.slideToMenuDistanceSlider];
|
||||
[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
|
||||
[self.pointerVelocityModeDividerSlider setValue: (uint8_t)(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 pointerVelocityModeDividerSliderMoved:self.pointerVelocityModeDividerSlider];
|
||||
// pointer veloc setting, will be enable/disabled by touchMode
|
||||
[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 pointerVelocityModeDividerSliderMoved:self.pointerVelocityModeDividerSlider];
|
||||
|
||||
// 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 addTarget:self action:@selector(touchPointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||
[self touchPointerVelocityFactorSliderMoved:self.touchPointerVelocityFactorSlider];
|
||||
// init pointer veloc setting, will be enable/disabled by touchMode
|
||||
[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 touchPointerVelocityFactorSliderMoved:self.touchPointerVelocityFactorSlider];
|
||||
|
||||
// async native touch event
|
||||
// [self.asyncNativeTouchPrioritySelector setSelectedSegmentIndex:currentSettings.asyncNativeTouchPriority.intValue]; // load old setting of asyncNativeTouchPriority
|
||||
// [self.asyncNativeTouchPrioritySelector addTarget:self action:@selector(asyncNativeTouchPriorityChanged) forControlEvents:UIControlEventValueChanged];
|
||||
// async native touch event
|
||||
// [self.asyncNativeTouchPrioritySelector setSelectedSegmentIndex:currentSettings.asyncNativeTouchPriority.intValue]; // load old setting of asyncNativeTouchPriority
|
||||
// [self.asyncNativeTouchPrioritySelector addTarget:self action:@selector(asyncNativeTouchPriorityChanged) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
// 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 addTarget:self action:@selector(mousePointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||
[self mousePointerVelocityFactorSliderMoved:self.mousePointerVelocityFactorSlider];
|
||||
// init relative touch mouse pointer veloc setting, will be enable/disabled by touchMode
|
||||
[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 mousePointerVelocityFactorSliderMoved:self.mousePointerVelocityFactorSlider];
|
||||
|
||||
|
||||
// these settings will be affected by onscreenControl & touchMode, must be loaded before them.
|
||||
// NSLog(@"osc tool fingers setting test: %d", currentSettings.oscLayoutToolFingers.intValue);
|
||||
self->oswLayoutFingers = (uint16_t)tempSettings.oscLayoutToolFingers.intValue; // load old setting of oscLayoutFingers
|
||||
uint8_t keyboardToggleFingers = tempSettings.keyboardToggleFingers.intValue;
|
||||
// these settings will be affected by onscreenControl & touchMode, must be loaded before them.
|
||||
// NSLog(@"osc tool fingers setting test: %d", currentSettings.oscLayoutToolFingers.intValue);
|
||||
self->oswLayoutFingers = (uint16_t)self->tempSettings.oscLayoutToolFingers.intValue; // load old setting of oscLayoutFingers
|
||||
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.
|
||||
NSInteger onscreenControlsLevel = [tempSettings.onscreenControls integerValue];
|
||||
[self.onScreenWidgetSelector setSelectedSegmentIndex:onscreenControlsLevel];
|
||||
[self.onScreenWidgetSelector addTarget:self action:@selector(onScreenWidgetChanged) forControlEvents:UIControlEventValueChanged];
|
||||
[self onScreenWidgetChanged];
|
||||
// this setting will be affected by touchMode, must be loaded before them.
|
||||
NSInteger onscreenControlsLevel = [self->tempSettings.onscreenControls integerValue];
|
||||
[self.onScreenWidgetSelector setSelectedSegmentIndex:onscreenControlsLevel];
|
||||
[self.onScreenWidgetSelector addTarget:self action:@selector(onScreenWidgetChanged) forControlEvents:UIControlEventValueChanged];
|
||||
[self onScreenWidgetChanged];
|
||||
|
||||
// touch move event interval for native-touch.
|
||||
[self.touchMoveEventIntervalSlider setValue: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 touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
|
||||
// touch move event interval for native-touch.
|
||||
[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 touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
|
||||
|
||||
|
||||
// this part will enable/disable oscSelector & the asyncNativeTouchPriority selector
|
||||
uint8_t touchModeSelectorIndex = tempSettings.touchMode.intValue == NativeTouchOnly ? NativeTouch : tempSettings.touchMode.intValue;
|
||||
[self.touchModeSelector setSelectedSegmentIndex:touchModeSelectorIndex]; //Load old touchMode setting
|
||||
[self.touchModeSelector addTarget:self action:@selector(touchModeChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
[self touchModeChanged:self.touchModeSelector];
|
||||
// this part will enable/disable oscSelector & the asyncNativeTouchPriority selector
|
||||
uint8_t touchModeSelectorIndex = self->tempSettings.touchMode.intValue == NativeTouchOnly ? NativeTouch : self->tempSettings.touchMode.intValue;
|
||||
[self.touchModeSelector setSelectedSegmentIndex:touchModeSelectorIndex]; //Load old touchMode setting
|
||||
[self.touchModeSelector addTarget:self action:@selector(touchModeChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
[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.enableOswForNativeTouchSwitch setOn:tempSettings.touchMode.intValue != NativeTouchOnly];
|
||||
[self.enableOswForNativeTouchSwitch addTarget:self action:@selector(enableOswForNativeTouchSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||
[self enableOswForNativeTouchSwitchFlipped:self.enableOswForNativeTouchSwitch];
|
||||
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:self->tempSettings.touchMode.intValue != NativeTouchOnly];
|
||||
[self.enableOswForNativeTouchSwitch addTarget:self action:@selector(enableOswForNativeTouchSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||
[self enableOswForNativeTouchSwitchFlipped:self.enableOswForNativeTouchSwitch];
|
||||
|
||||
[self.externalDisplayModeSelector setSelectedSegmentIndex:self->tempSettings.externalDisplayMode.integerValue];
|
||||
[self.localMousePointerModeSelector setSelectedSegmentIndex:self->tempSettings.localMousePointerMode.integerValue];
|
||||
|
||||
[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;
|
||||
}];
|
||||
|
||||
[self.externalDisplayModeSelector setSelectedSegmentIndex:tempSettings.externalDisplayMode.integerValue];
|
||||
[self.localMousePointerModeSelector setSelectedSegmentIndex:tempSettings.localMousePointerMode.integerValue];
|
||||
|
||||
[self.sendDummyEventSwitch setOn:tempSettings.sendDummyEvent];// Load old setting
|
||||
|
||||
settingsViewJustLoaded = false;
|
||||
}
|
||||
|
||||
- (void)slideToSettingsScreenEdgeChanged{
|
||||
@@ -1793,7 +1900,11 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
}
|
||||
|
||||
- (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{
|
||||
@@ -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{
|
||||
// init CustomOSC stuff
|
||||
/* 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 {
|
||||
[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 {
|
||||
@@ -1977,10 +2158,10 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
|
||||
|
||||
// veloc factor upto 700%
|
||||
- (uint16_t) map_velocFactorDisplay_fromSliderValue:(CGFloat)sliderValue{
|
||||
uint16_t velocFactorDisplay = 0;
|
||||
- (CGFloat) map_velocFactorDisplay_fromSliderValue:(CGFloat)sliderValue{
|
||||
CGFloat velocFactorDisplay = 0;
|
||||
if(sliderValue > 200) velocFactorDisplay = 200 + ((uint16_t)sliderValue % 200) * 5;
|
||||
else velocFactorDisplay = (uint16_t) sliderValue;
|
||||
else velocFactorDisplay = sliderValue;
|
||||
return velocFactorDisplay;
|
||||
}
|
||||
|
||||
@@ -1994,7 +2175,7 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
}
|
||||
|
||||
- (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 {
|
||||
@@ -2516,6 +2697,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
}
|
||||
|
||||
if(![MicHandler permissionGranted]) [self.redirectMicSwitch setOn:false];
|
||||
|
||||
[self saveMotionControlConfigs];
|
||||
}
|
||||
|
||||
- (void) saveSettings {
|
||||
|
||||
@@ -434,6 +434,10 @@
|
||||
}
|
||||
|
||||
_motionHandler = [MotionHandler sharedInstance];
|
||||
_motionHandler.gyroBiasX = _settings.gyroBiasX.doubleValue;
|
||||
_motionHandler.gyroBiasY = _settings.gyroBiasY.doubleValue;
|
||||
_motionHandler.gyroBiasZ = _settings.gyroBiasZ.doubleValue;
|
||||
|
||||
_streamView.onScreenControls.instanceReceiverDelegate = _motionHandler;
|
||||
[_streamView.onScreenControls sendInstance];
|
||||
|
||||
@@ -534,64 +538,22 @@
|
||||
|
||||
- (void)popFirstStreamingTip {
|
||||
// 初始化倒计时秒数
|
||||
__block NSInteger remainingSeconds = 16;
|
||||
|
||||
|
||||
NSString* settingsEdgeSide = _settings.slideToSettingsScreenEdge.intValue == UIRectEdgeLeft ? [LocalizationHelper localizedStringForKey:@"left"] : [LocalizationHelper localizedStringForKey:@"right"];
|
||||
NSString* cmdToolEdgeSide = _settings.slideToSettingsScreenEdge.intValue == UIRectEdgeLeft ? [LocalizationHelper localizedStringForKey:@"right"] : [LocalizationHelper localizedStringForKey:@"left"];
|
||||
uint8_t slideDist = (uint8_t)(_settings.slideToSettingsDistance.floatValue * 100);
|
||||
// 创建弹窗
|
||||
|
||||
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
|
||||
withCancel:NO
|
||||
buttonTitle:[LocalizationHelper localizedStringForKey:@"Got it!"]
|
||||
countdown:16
|
||||
completion:^{}];
|
||||
|
||||
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||
paragraphStyle.alignment = NSTextAlignmentLeft;
|
||||
|
||||
NSDictionary *attributes = @{
|
||||
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 {
|
||||
@@ -1570,8 +1532,8 @@
|
||||
[_motionHandler startAccelUpdate];
|
||||
}
|
||||
|
||||
- (void)stopGyroUpdate{
|
||||
[_motionHandler stopGyroUpdate];
|
||||
- (void)stopGyroUpdateWithInterruption:(BOOL)interruption{
|
||||
[_motionHandler stopGyroUpdateWithInterruption:interruption];
|
||||
}
|
||||
|
||||
- (void)stopAccelUpdate{
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <Limelight.h>
|
||||
#import "OnScreenControls.h"
|
||||
#import "OnScreenButtonState.h"
|
||||
#import "OSCProfile.h"
|
||||
#import "OSCProfilesManager.h"
|
||||
#import "opus.h"
|
||||
#import "opus_defines.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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Preferred Codec\"; ObjectID = \"6w5-Md-NpV\";",
|
||||
"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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Show Local Keyboard Toolbar \"; ObjectID = \"aup-5k-z6P\";",
|
||||
"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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Touch Pointer Velocity\"; ObjectID = \"egA-7n-Dpe\";",
|
||||
"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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Send Dummy Event\"; ObjectID = \"Gus-CK-wpc\";",
|
||||
"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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Emulated Controller Type\"; ObjectID = \"JTl-un-QJJ\";",
|
||||
"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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Rendering Mode (Experimental)\"; ObjectID = \"v8l-hf-kiR\";",
|
||||
"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" : {
|
||||
"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",
|
||||
|
||||
@@ -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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Enable On-Screen Widget & Peripherals\"; ObjectID = \"3av-lc-tcF\";",
|
||||
"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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Enable PiP\"; ObjectID = \"4Ul-P6-eUk\";",
|
||||
"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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Open Toolbox in Streaming\"; ObjectID = \"awK-Ei-T9i\";",
|
||||
"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" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Frame Rate\"; ObjectID = \"E7F-vx-pnM\";",
|
||||
"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]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; hfR-K8-Ti4.segmentTitles[0] = \"Stereo\"; ObjectID = \"hfR-K8-Ti4\";",
|
||||
"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" : {
|
||||
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"Z9M-yq-an2\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
|
||||
Reference in New Issue
Block a user