- on screen button “folder” prototype
- overlapped buttons triggered at touch began - memory management fixes - reduce toolbox view & widget layout tool animation - fast widget profile loading - toolbox & widget profile selector quick dismiss
This commit is contained in:
@@ -726,11 +726,11 @@
|
||||
9827E7A7251436EA00F25707 /* HapticContext.h */,
|
||||
840751B12BF49B5A0096B657 /* CustomTapGestureRecognizer.h */,
|
||||
840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */,
|
||||
9819CC12254F107A008A7C8E /* RelativeTouchHandler.h */,
|
||||
843CC7F42C1E778000BBC475 /* NativeTouchHandler.h */,
|
||||
844CD0E82C7F847E004861A7 /* PureNativeTouchHandler.h */,
|
||||
844CD0EA2C7F8500004861A7 /* PureNativeTouchHandler.m */,
|
||||
843CC7F42C1E778000BBC475 /* NativeTouchHandler.h */,
|
||||
843CC7F52C1E7A1700BBC475 /* NativeTouchHandler.m */,
|
||||
9819CC12254F107A008A7C8E /* RelativeTouchHandler.h */,
|
||||
9819CC13254F107A008A7C8E /* RelativeTouchHandler.m */,
|
||||
9819CC20254F180F008A7C8E /* AbsoluteTouchHandler.h */,
|
||||
9819CC21254F180F008A7C8E /* AbsoluteTouchHandler.m */,
|
||||
|
||||
@@ -1950,6 +1950,46 @@
|
||||
<constraint firstAttribute="height" constant="31" id="RRk-QS-MEj"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="prr-Do-Nna">
|
||||
<rect key="frame" x="3145" y="270" width="300.5" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Double Tap Shorcut" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="v5f-j3-pEc" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="150.5" 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="cw7-Jc-7bZ">
|
||||
<rect key="frame" x="170.5" y="0.0" width="132" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="zQj-YX-sHL"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="BW8-EB-nuy"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="jLJ-Eo-Zi6">
|
||||
<rect key="frame" x="3145" y="307" width="358.5" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Pencil Pauses Native Touch" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hZw-pC-Qxm" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="208.5" 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="Pjf-Vi-3wE">
|
||||
<rect key="frame" x="228.5" y="0.0" width="132" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="jfj-nX-rgt"/>
|
||||
</constraints>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="31" id="pV0-m0-1pz"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
@@ -1987,6 +2027,8 @@
|
||||
<outlet property="customResolutionSwitch" destination="E5w-kK-dOo" id="hrt-v2-8dY"/>
|
||||
<outlet property="delayLeftClickStack" destination="Xp7-7O-Ndt" id="CwB-Wy-beh"/>
|
||||
<outlet property="delayLeftClickSwitch" destination="Haf-rg-zLB" id="FaA-3p-JDw"/>
|
||||
<outlet property="doubleTapShortcutStack" destination="prr-Do-Nna" id="sK3-va-U5W"/>
|
||||
<outlet property="doubleTapShortcutSwitch" destination="cw7-Jc-7bZ" id="bRy-9x-0Tl"/>
|
||||
<outlet property="duckOtherAppStack" destination="gBG-8F-6F8" id="T9P-ys-pHB"/>
|
||||
<outlet property="duckOtherAppSwitch" destination="YEv-1B-97I" id="o3Q-yt-74a"/>
|
||||
<outlet property="edgeSlidingSensitivitySlider" destination="41s-BD-8rZ" id="ytk-Y5-Alq"/>
|
||||
@@ -2049,6 +2091,8 @@
|
||||
<outlet property="optimizeGamesSwitch" destination="Sui-Ig-YxN" id="Lxu-si-ugy"/>
|
||||
<outlet property="passthroughGesturesStack" destination="wUP-2q-5vh" id="a4w-16-lLs"/>
|
||||
<outlet property="passthroughGesturesSwitch" destination="eO1-kV-e9j" id="5r8-o4-t9J"/>
|
||||
<outlet property="pencilPausesNativeTouchStack" destination="jLJ-Eo-Zi6" id="a4V-Lp-xDG"/>
|
||||
<outlet property="pencilPausesNativeTouchSwitch" destination="Pjf-Vi-3wE" id="L84-RY-6iV"/>
|
||||
<outlet property="pencilTickIntervalSlider" destination="IOG-l4-gtQ" id="3Kb-uT-ILa"/>
|
||||
<outlet property="pencilTickIntervalStack" destination="O2s-9s-ABi" id="0kk-q4-ppy"/>
|
||||
<outlet property="pencilTickSelector" destination="bVv-CW-kN8" id="Ymr-VI-Ilj"/>
|
||||
@@ -2423,7 +2467,7 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="z5Q-bW-WQ8" userLabel="widgetPanelStack" customClass="WidgetPanelStackView">
|
||||
<rect key="frame" x="325" y="156" width="650" height="854"/>
|
||||
<rect key="frame" x="325" y="156" width="650" height="890"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Profile:" textAlignment="center" lineBreakMode="tailTruncation" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iel-xl-xd9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="650" height="37"/>
|
||||
@@ -2825,6 +2869,55 @@
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FEI-wQ-Q3w">
|
||||
<rect key="frame" x="0.0" y="859" width="650" height="31"/>
|
||||
<subviews>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Collected widgets " textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.20000000000000001" translatesAutoresizingMaskIntoConstraints="NO" id="WLg-7a-5wu" userLabel="uiLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="130" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="MfE-jp-WQW"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" alpha="0.76999998092651367" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="sch-0e-nfa">
|
||||
<rect key="frame" x="130" y="0.0" width="180" height="32"/>
|
||||
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="180" id="TbL-OE-jvg"/>
|
||||
</constraints>
|
||||
<segments>
|
||||
<segment title="display"/>
|
||||
<segment title="hide"/>
|
||||
</segments>
|
||||
<color key="tintColor" systemColor="systemGray3Color"/>
|
||||
<color key="selectedSegmentTintColor" red="0.23087820410000001" green="0.4576539993" blue="0.51765710119999997" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
</segmentedControl>
|
||||
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Reveal mode " textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.20000000298023224" translatesAutoresizingMaskIntoConstraints="NO" id="MyP-Aq-htW" userLabel="uiLabel">
|
||||
<rect key="frame" x="310" y="0.0" width="130" height="31"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="130" id="6dc-EO-c30"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" alpha="0.76999998092651367" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="Qr8-Df-7Zu" userLabel="revealModeSelector">
|
||||
<rect key="frame" x="440" y="0.0" width="210" height="32"/>
|
||||
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="180" id="jaU-fY-1Xa"/>
|
||||
</constraints>
|
||||
<segments>
|
||||
<segment title="coexist"/>
|
||||
<segment title="exclusive"/>
|
||||
</segments>
|
||||
<color key="tintColor" systemColor="systemGray3Color"/>
|
||||
<color key="selectedSegmentTintColor" red="0.23087820410000001" green="0.4576539993" blue="0.51765710119999997" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
</segmentedControl>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
<constraints>
|
||||
@@ -2852,6 +2945,8 @@
|
||||
<outlet property="buttonModeStack" destination="LGW-oJ-5Zf" id="Jn6-Mu-XYp"/>
|
||||
<outlet property="chevronImageView" destination="iVZ-Fd-dY7" id="403-Nj-wRx"/>
|
||||
<outlet property="chevronView" destination="hKp-ds-86I" id="Og9-aT-zNH"/>
|
||||
<outlet property="collectedWidgetsSelector" destination="sch-0e-nfa" id="ApS-gt-9Wq"/>
|
||||
<outlet property="collectedWidgetsStack" destination="FEI-wQ-Q3w" id="o17-RT-wJJ"/>
|
||||
<outlet property="componentSizeLabel" destination="XMg-F0-m2y" id="pQ6-MN-dcV"/>
|
||||
<outlet property="componentSizeSlider" destination="ewF-K6-Rkd" id="yVC-sP-VSe"/>
|
||||
<outlet property="componentSizeStack" destination="WHh-JZ-wlT" id="6p9-Rd-Caq"/>
|
||||
@@ -2870,6 +2965,7 @@
|
||||
<outlet property="pitchFactorLabel" destination="fq3-ie-8by" id="dpT-7M-rIg"/>
|
||||
<outlet property="pitchFactorSlider" destination="4NQ-cL-QH0" id="VxC-yb-L1u"/>
|
||||
<outlet property="pitchFactorStack" destination="JNt-eT-HCs" id="iHx-Nk-Oko"/>
|
||||
<outlet property="revealModeSelector" destination="Qr8-Df-7Zu" id="cnM-SJ-iGS"/>
|
||||
<outlet property="rollFactorLabel" destination="x9i-JN-FSn" id="Z6N-It-yAL"/>
|
||||
<outlet property="rollFactorSlider" destination="ELy-jP-cY6" id="6aX-c5-0po"/>
|
||||
<outlet property="rollFactorStack" destination="zqk-Xa-Dku" id="PNE-l7-JwX"/>
|
||||
@@ -3010,7 +3106,7 @@
|
||||
<image name="tray.full.fill" catalog="system" width="128" height="88"/>
|
||||
<image name="video.and.waveform" catalog="system" width="128" height="94"/>
|
||||
<systemColor name="linkColor">
|
||||
<color red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="secondarySystemGroupedBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -3019,13 +3115,13 @@
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBrownColor">
|
||||
<color red="0.63529411759999999" green="0.51764705879999995" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.67450980392156867" green="0.49803921568627452" blue="0.36862745098039218" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemGray3Color">
|
||||
<color red="0.78039215689999997" green="0.78039215689999997" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.7803921568627451" green="0.7803921568627451" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemTealColor">
|
||||
<color red="0.18823529410000001" green="0.69019607839999997" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.0" green="0.76470588235294112" blue="0.81568627450980391" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
self.address = host.address;
|
||||
|
||||
NSSet *classes = [NSSet setWithObjects: [NSMutableSet class], nil];
|
||||
NSSet *classes = [NSSet setWithObjects: [NSMutableSet class], [NSString class], nil];
|
||||
NSError *error = nil;
|
||||
self.activeAddressPool = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:host.activeAddressPool error:&error];
|
||||
if(self.activeAddressPool){
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
static int mouseButtonForCursorMove = BUTTON_LEFT;
|
||||
|
||||
@implementation AbsoluteTouchHandler {
|
||||
StreamView* streamView;
|
||||
__weak StreamView* streamView;
|
||||
|
||||
bool multiTouchesDetected;
|
||||
bool passthroughGestures;
|
||||
@@ -108,7 +108,6 @@ static int mouseButtonForCursorMove = BUTTON_LEFT;
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
rightButtonClicked = false;
|
||||
|
||||
|
||||
if([UITouchUtil touchesIn:streamView from:event].count>=2){
|
||||
multiTouchesDetected = true;
|
||||
[longPressTimer invalidate];
|
||||
|
||||
@@ -135,9 +135,22 @@ import UIKit
|
||||
"WIDGETTOOL",
|
||||
"WIDGETPROFILES",
|
||||
"PROFILES",
|
||||
"PICKPROFILE",
|
||||
"PICKPRFL",
|
||||
"SOFTKEYBOARD",
|
||||
"ABSTCHDRAG",
|
||||
"PENCILHOVER"
|
||||
"FOLDER",
|
||||
"PRESSURECURVE",
|
||||
"PENCILHOVER",
|
||||
"BRUSH",
|
||||
"ERASER",
|
||||
"NOSINGLETOUCH"
|
||||
]
|
||||
@objc public static let pencilProButtonCmds: [String] = [
|
||||
"PENCILHOVER",
|
||||
"BRUSH",
|
||||
"ERASER",
|
||||
"NOSINGLETOUCH"
|
||||
]
|
||||
@objc public static let motionControlButtonCmds: [String] = ["GYRO","GYROPAUSE","ACCEL","MOTION"]
|
||||
|
||||
@@ -146,6 +159,8 @@ import UIKit
|
||||
@objc public static let keyboardButtonMappings: [String: Int16] = [
|
||||
// Windows Key Codes
|
||||
"NULL": 0xFF,
|
||||
"BRUSH": 0xFF,
|
||||
"ERASER": 0xFF,
|
||||
"CTRL": 0x11, // VK_CONTROL
|
||||
"SHIFT": 0x10, // VK_SHIFT
|
||||
"ALT": 0x12, // VK_MENU
|
||||
@@ -431,7 +446,8 @@ import UIKit
|
||||
}
|
||||
|
||||
// extractKeyStrings from keyboardCMDString
|
||||
@objc public func extractKeyStringsFromComboCommand(from input: String) -> [String]? {
|
||||
@objc public func extractKeyStrings(from keyboardCmd: String) -> [String]? {
|
||||
let keyboardCmd = keyboardCmd.uppercased()
|
||||
let keys = CommandManager.keyboardButtonMappings.keys.joined(separator: "|")
|
||||
let pattern = "^(?:(\(keys))(?:\\+(\(keys))*)*)$"
|
||||
|
||||
@@ -439,14 +455,14 @@ import UIKit
|
||||
print("Failed to create regex")
|
||||
return nil
|
||||
}
|
||||
let range = NSRange(location: 0, length: input.utf16.count)
|
||||
guard let match = regex.firstMatch(in: input, options: [], range: range) else {
|
||||
print("No match found for input: \(input)")
|
||||
let range = NSRange(location: 0, length: keyboardCmd.utf16.count)
|
||||
guard let match = regex.firstMatch(in: keyboardCmd, options: [], range: range) else {
|
||||
print("No match found for input: \(keyboardCmd)")
|
||||
return nil
|
||||
}
|
||||
// print("Regex matched for input: \(input)")
|
||||
|
||||
let matchedString = (input as NSString).substring(with: match.range(at: 0))
|
||||
let matchedString = (keyboardCmd as NSString).substring(with: match.range(at: 0))
|
||||
let keyStrings = matchedString.split(separator: "+").map { String($0) }
|
||||
|
||||
guard !keyStrings.isEmpty else {
|
||||
@@ -479,7 +495,8 @@ import UIKit
|
||||
}
|
||||
|
||||
//super combo key button strings
|
||||
@objc public func extractSinglCmdStringsFromComboKeys(from input: String) -> [String]? {
|
||||
@objc public func extractCmdStrings(from input: String) -> [String]? {
|
||||
let input = input.uppercased()
|
||||
let combinedStrings = [CommandManager.keyboardButtonMappings.keys.map { $0 as String },
|
||||
CommandManager.oscButtonMappings.keys.map { $0 as String },
|
||||
CommandManager.mouseButtonMappings.keys.map { $0 as String },
|
||||
@@ -535,7 +552,7 @@ import UIKit
|
||||
@objc public func addCommand(_ command: RemoteCommand) -> Bool {
|
||||
command.cmdString = command.cmdString.uppercased() // convert all letters to upper case
|
||||
if(command.alias.trimmingCharacters(in: .whitespacesAndNewlines).count == 0) {command.alias = command.cmdString} // copy cmd string as alias when alias is empty
|
||||
let keyStrings = extractKeyStringsFromComboCommand(from: command.cmdString)
|
||||
let keyStrings = extractKeyStrings(from: command.cmdString)
|
||||
if (keyStrings == nil) {return false} // in case of non-keyboard command strings, return false
|
||||
commands.append(command)
|
||||
saveCommands()
|
||||
@@ -581,8 +598,9 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func sendKeyComboCommand(keyboardCmdStrings: [String], delay: TimeInterval = 0.2, index: Int = 0) { // we need a large delay for WAN streaming
|
||||
@objc public func sendKeyComboCommand(keyboardCmdStrings: [String]?, delay: TimeInterval = 0.2, index: Int = 0) { // we need a large delay for WAN streaming
|
||||
// 如果已处理完所有按键,则开始释放按键
|
||||
guard let keyboardCmdStrings = keyboardCmdStrings else { return }
|
||||
guard index < keyboardCmdStrings.count else {
|
||||
// 释放按键
|
||||
for keyStr in keyboardCmdStrings.reversed() { // 从后往前释放按键
|
||||
|
||||
@@ -42,8 +42,8 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
|
||||
|
||||
NSLock *_controllerStreamLock;
|
||||
NSMutableDictionary *_voidControllers;
|
||||
id<ControllerSupportDelegate> _delegate;
|
||||
StreamConfiguration* _streamConfig;
|
||||
__weak id<ControllerSupportDelegate> _delegate;
|
||||
|
||||
float accumulatedDeltaX;
|
||||
float accumulatedDeltaY;
|
||||
@@ -75,8 +75,8 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
|
||||
VoidController *_oscController;
|
||||
NSMutableSet* _activeGCControllers;
|
||||
TemporarySettings* tempSettings;
|
||||
OSCProfilesManager* oscProfileMan;
|
||||
OSCProfile* oscProfile;
|
||||
OSCProfilesManager* oscProfileMan;
|
||||
|
||||
#define EMULATING_SELECT 0x1
|
||||
#define EMULATING_SPECIAL 0x2
|
||||
@@ -98,7 +98,7 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
|
||||
bool _controllerGyroSwitchHoldPressed;
|
||||
ControllerGyroSwitchMode _gyroSwitchMode;
|
||||
|
||||
MotionHandler* motionHandler;
|
||||
__weak MotionHandler* motionHandler;
|
||||
}
|
||||
|
||||
// UPDATE_BUTTON_FLAG(controller, flag, pressed)
|
||||
@@ -1723,6 +1723,7 @@ double rc_expo(double x, double expo) {
|
||||
_oscController.playerIndex = 0;
|
||||
|
||||
oscProfile = [oscProfileMan getSelectedProfile];
|
||||
motionHandler = [MotionHandler sharedWithProfile:oscProfile];
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
tempSettings = [dataMan getSettings];
|
||||
|
||||
@@ -1965,7 +1966,6 @@ double rc_expo(double x, double expo) {
|
||||
_controllerMouseEnabledFlag = false;
|
||||
|
||||
_gyroEnabledFlag = false;
|
||||
motionHandler = [MotionHandler sharedInstance];
|
||||
oscProfileMan = [OSCProfilesManager sharedManager:CGRectZero];
|
||||
|
||||
[self updateCommonConfig:streamConfig];
|
||||
|
||||
@@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property UIView* _view;
|
||||
@property NSMutableArray *layoutChanges;
|
||||
@property CALayer *layerBeingDragged;
|
||||
@property UIViewController *layoutToolVC;
|
||||
@property (nonatomic, weak) UIViewController *layoutToolVC;
|
||||
|
||||
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport
|
||||
streamConfig:(StreamConfiguration*)streamConfig oscLevel:(int)oscLevel;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
layoutChanges = [[NSMutableArray alloc] init]; // will contain OSC button layout changes the user has made for this profile
|
||||
|
||||
[self drawButtons];
|
||||
// [self drawButtons];
|
||||
[self drawGuidelines]; // add the blue guidelines that appear when button is being tapped and dragged
|
||||
|
||||
return self;
|
||||
@@ -55,35 +55,45 @@
|
||||
/**
|
||||
* This method overrides the superclass's drawButtons method. The purpose of this method is to create a dPad parent layer, and add the four dPad buttons to it so that the user can drag the entire dPad around, directional buttons and all, as one unit as is the expected behavior. Note that we do not want the four dPad buttons to be child layers of a CALayer parent layer on the game stream view since the touch logic implemented for the four dPad buttons on the game stream view is written assuming the dPad buttons are not children of another parent CALayer
|
||||
*/
|
||||
- (void) drawButtons {
|
||||
[super setDPadCenter]; // Set custom position for D-Pad here
|
||||
[super setAnalogStickPositions]; // Set custom position for analog sticks here
|
||||
- (void) drawButtons:(OSCProfile* )profile {
|
||||
// [super regenLayerPool];
|
||||
[super setDPadCenter:profile]; // Set custom position for D-Pad here
|
||||
[super setAnalogStickPositions:profile]; // Set custom position for analog sticks here
|
||||
[super drawBumpers];
|
||||
[super drawButtons];
|
||||
[super drawButtons:profile];
|
||||
|
||||
/*
|
||||
UIImage* downButtonImage = [UIImage imageNamed:@"DownButton"];
|
||||
UIImage* rightButtonImage = [UIImage imageNamed:@"RightButton"];
|
||||
UIImage* upButtonImage = [UIImage imageNamed:@"UpButton"];
|
||||
UIImage* leftButtonImage = [UIImage imageNamed:@"LeftButton"];
|
||||
*/
|
||||
|
||||
UIImage* downButtonImage = [UIImage imageNamed:@"DownButton"];
|
||||
UIImage* rightButtonImage = [UIImage imageNamed:@"RightButton"];
|
||||
UIImage* upButtonImage = [UIImage imageNamed:@"UpButton"];
|
||||
UIImage* leftButtonImage = [UIImage imageNamed:@"LeftButton"];
|
||||
|
||||
// create dPad background layer
|
||||
self._dPadBackground = [CALayer layer];
|
||||
self._dPadBackground.name = @"dPad";
|
||||
self._dPadBackground.frame = CGRectMake(self.D_PAD_CENTER_X,
|
||||
self.D_PAD_CENTER_Y,
|
||||
self._leftButton.frame.size.width * 2 + BUTTON_DIST,
|
||||
self._leftButton.frame.size.width * 2 + BUTTON_DIST);
|
||||
self._dPadBackground.position = CGPointMake(self.D_PAD_CENTER_X, self.D_PAD_CENTER_Y); // since dPadBackground's dimensions have change after settings its width and height you need to reset its position again here
|
||||
[self.OSCButtonLayers addObject:self._dPadBackground];
|
||||
[_view.layer addSublayer:self._dPadBackground];
|
||||
|
||||
// add dPad buttons to parent layer
|
||||
[self._dPadBackground addSublayer:self._downButton];
|
||||
[self._dPadBackground addSublayer:self._rightButton];
|
||||
[self._dPadBackground addSublayer:self._upButton];
|
||||
[self._dPadBackground addSublayer:self._leftButton];
|
||||
|
||||
if([self.OSCButtonLayerPool containsObject:self._upButton]){
|
||||
[self.OSCButtonLayerPool addObject:self._dPadBackground];
|
||||
self._dPadBackground.name = @"dPad";
|
||||
self._dPadBackground.frame = CGRectMake(self.D_PAD_CENTER_X,
|
||||
self.D_PAD_CENTER_Y,
|
||||
self._leftButton.frame.size.width * 2 + BUTTON_DIST,
|
||||
self._leftButton.frame.size.width * 2 + BUTTON_DIST);
|
||||
self._dPadBackground.position = CGPointMake(self.D_PAD_CENTER_X, self.D_PAD_CENTER_Y); // since dPadBackground's dimensions have change after settings its width and height you need to reset its position again here
|
||||
[self.OSCButtonLayerPool addObject:self._dPadBackground];
|
||||
[_view.layer addSublayer:self._dPadBackground];
|
||||
|
||||
// add dPad buttons to parent layer
|
||||
[self._dPadBackground addSublayer:self._downButton];
|
||||
[self._dPadBackground addSublayer:self._rightButton];
|
||||
[self._dPadBackground addSublayer:self._upButton];
|
||||
[self._dPadBackground addSublayer:self._leftButton];
|
||||
}
|
||||
|
||||
NSLog(@"layer name ttttttttttt============================");
|
||||
for(CALayer* buttonLayer in self.OSCButtonLayerPool){
|
||||
NSLog(@"layer name %@", buttonLayer.name);
|
||||
}
|
||||
|
||||
[self resizeControllerLayerWith:self._dPadBackground and:_dPadSizeFactor];
|
||||
|
||||
/*
|
||||
@@ -128,7 +138,7 @@
|
||||
*/
|
||||
|
||||
horizontalGuideline.backgroundColor = [UIColor blueColor]; // change horizontal guideline back to blue if it doesn't line up with one of the on screen buttons
|
||||
for (CALayer *button in self.OSCButtonLayers) { // horizontal guideline position check
|
||||
for (CALayer *button in self.OSCButtonLayerPool) { // horizontal guideline position check
|
||||
if ((layerBeingDragged != button) && !button.isHidden) {
|
||||
if ((horizontalGuideline.center.y < button.position.y + 1) &&
|
||||
(horizontalGuideline.center.y > button.position.y - 1)) {
|
||||
@@ -147,7 +157,7 @@
|
||||
}
|
||||
|
||||
verticalGuideline.backgroundColor = [UIColor blueColor]; // change vertical guideline back to blue if it doesn't line up with one of the on screen buttons
|
||||
for (CALayer *button in self.OSCButtonLayers) { // vertical guideline position check
|
||||
for (CALayer *button in self.OSCButtonLayerPool) { // vertical guideline position check
|
||||
if ((layerBeingDragged != button) && !button.isHidden) {
|
||||
if ((verticalGuideline.center.x < button.position.x + 1) &&
|
||||
(verticalGuideline.center.x > button.position.x - 1)) {
|
||||
@@ -177,7 +187,7 @@
|
||||
-Change vertical guideline color to yellow if its x-position is almost equal to that of one of the buttons on screen.
|
||||
*/
|
||||
horizontalGuideline.backgroundColor = [UIColor blueColor]; // change horizontal guideline back to blue if it doesn't line up with one of the on screen buttons
|
||||
for (CALayer *button in self.OSCButtonLayers) { // horizontal guideline position check
|
||||
for (CALayer *button in self.OSCButtonLayerPool) { // horizontal guideline position check
|
||||
if ((layerBeingDragged != button) && !button.isHidden) {
|
||||
if ((horizontalGuideline.center.y < button.position.y + 1) &&
|
||||
(horizontalGuideline.center.y > button.position.y - 1)) {
|
||||
@@ -196,7 +206,7 @@
|
||||
}
|
||||
}
|
||||
verticalGuideline.backgroundColor = [UIColor blueColor]; // change vertical guideline back to blue if it doesn't line up with one of the on screen buttons
|
||||
for (CALayer *button in self.OSCButtonLayers) { // vertical guideline position check
|
||||
for (CALayer *button in self.OSCButtonLayerPool) { // vertical guideline position check
|
||||
if ((layerBeingDragged != button) && !button.isHidden) {
|
||||
if ((verticalGuideline.center.x < button.position.x + 1) &&
|
||||
(verticalGuideline.center.x > button.position.x - 1)) {
|
||||
@@ -233,7 +243,7 @@
|
||||
|
||||
/* returns reference to button layer object given the button's name*/
|
||||
- (CALayer*)controllerLayerFromName: (NSString*)name {
|
||||
for (CALayer *buttonLayer in self.OSCButtonLayers) {
|
||||
for (CALayer *buttonLayer in self.OSCButtonLayerPool) {
|
||||
|
||||
if ([buttonLayer.name isEqualToString:name]) {
|
||||
return buttonLayer;
|
||||
|
||||
@@ -50,6 +50,11 @@ typedef NS_ENUM(NSInteger, MapGyroTo) {
|
||||
@property (nonatomic, assign) int controllerGyroSwitchToggle;
|
||||
@property NSArray<NSNumber *> *pressureCurvePoints;
|
||||
@property (nonatomic, assign) bool pressureCurveEnabled;
|
||||
@property (nonatomic, assign) bool doubleTapShorcutEnabled;
|
||||
@property NSString *brushShortcut;
|
||||
@property NSString *eraserShortcut;
|
||||
@property (nonatomic, assign) bool pencilPausesNativeTouch;
|
||||
|
||||
|
||||
- (id) initWithName:(NSString*)name buttonStates:(NSMutableArray*)buttonStates isSelected:(BOOL)isSelected;
|
||||
|
||||
|
||||
@@ -51,11 +51,20 @@
|
||||
[encoder encodeInt:self.controllerGyroSwitchToggle forKey:@"controllerGyroSwitchToggle"];
|
||||
[encoder encodeObject:self.pressureCurvePoints forKey:@"pressureCurvePoints"];
|
||||
[encoder encodeBool:self.pressureCurveEnabled forKey:@"pressureCurveEnabled"];
|
||||
[encoder encodeBool:self.doubleTapShorcutEnabled forKey:@"doubleTapShorcutEnabled"];
|
||||
[encoder encodeObject:self.brushShortcut forKey:@"brushShortcut"];
|
||||
[encoder encodeObject:self.eraserShortcut forKey:@"eraserShortcut"];
|
||||
[encoder encodeBool:self.pencilPausesNativeTouch forKey:@"pencilPausesNativeTouch"];
|
||||
}
|
||||
|
||||
- (id) initWithCoder:(NSCoder*)decoder {
|
||||
if (self = [super init]) {
|
||||
self.name = [decoder decodeObjectForKey:@"name"];
|
||||
self.name =
|
||||
[decoder decodeObjectOfClasses:
|
||||
[NSSet setWithObjects:
|
||||
[NSString class],
|
||||
nil]
|
||||
forKey:@"name"];
|
||||
self.buttonStatesEncoded =
|
||||
[decoder decodeObjectOfClasses:
|
||||
[NSSet setWithObjects:
|
||||
@@ -92,6 +101,27 @@
|
||||
forKey:@"pressureCurvePoints"]
|
||||
: @[@0.0, @0.0, @1.0, @1.0];
|
||||
self.pressureCurveEnabled = [decoder containsValueForKey:@"pressureCurveEnabled"] ? [decoder decodeBoolForKey:@"pressureCurveEnabled"] : false;
|
||||
|
||||
self.doubleTapShorcutEnabled = [decoder containsValueForKey:@"doubleTapShorcutEnabled"] ? [decoder decodeBoolForKey:@"doubleTapShorcutEnabled"] : false;
|
||||
self.brushShortcut =
|
||||
[decoder containsValueForKey:@"brushShortcut"]
|
||||
? [decoder decodeObjectOfClasses:
|
||||
[NSSet setWithObjects:
|
||||
[NSString class],
|
||||
nil]
|
||||
forKey:@"brushShortcut"]
|
||||
: @"";
|
||||
|
||||
self.eraserShortcut =
|
||||
[decoder containsValueForKey:@"eraserShortcut"]
|
||||
? [decoder decodeObjectOfClasses:
|
||||
[NSSet setWithObjects:
|
||||
[NSString class],
|
||||
nil]
|
||||
forKey:@"eraserShortcut"]
|
||||
: @"";
|
||||
|
||||
self.pencilPausesNativeTouch = [decoder containsValueForKey:@"pencilPausesNativeTouch"] ? [decoder decodeBoolForKey:@"pencilPausesNativeTouch"] : false;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -121,6 +151,10 @@
|
||||
copy.controllerGyroSwitchToggle = self.controllerGyroSwitchToggle;
|
||||
copy.pressureCurvePoints = [[NSMutableArray alloc] initWithArray:self.pressureCurvePoints copyItems:YES];
|
||||
copy.pressureCurveEnabled = self.pressureCurveEnabled;
|
||||
copy.doubleTapShorcutEnabled = self.doubleTapShorcutEnabled;
|
||||
copy.brushShortcut = [self.brushShortcut mutableCopy]; // NSString → NSMutableString
|
||||
copy.eraserShortcut = [self.eraserShortcut mutableCopy]; // NSString → NSMutableString
|
||||
copy.pencilPausesNativeTouch = self.pencilPausesNativeTouch;
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
@@ -207,7 +207,6 @@ static CGRect layoutViewBounds;
|
||||
#pragma mark - Getters
|
||||
|
||||
- (NSMutableArray *) getAllProfiles {
|
||||
|
||||
NSData *profilesArrayEncoded = [[NSUserDefaults standardUserDefaults] objectForKey: @"OSCProfiles"]; // Get the encoded array of encoded OSC profiles from persistent storage
|
||||
NSSet *classes = [NSSet setWithObjects:[NSString class], [NSMutableData class], [NSMutableArray class], [OSCProfile class], [OnScreenButtonState class], nil];
|
||||
|
||||
@@ -226,6 +225,7 @@ static CGRect layoutViewBounds;
|
||||
}
|
||||
|
||||
- (OSCProfile *) getSelectedProfile {
|
||||
NSLog(@"getAllProfiles test %f", CACurrentMediaTime());
|
||||
NSMutableArray *profiles = [self getAllProfiles];
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
@@ -344,25 +344,27 @@ static CGRect layoutViewBounds;
|
||||
- (NSMutableArray* ) convertOnScreenControllerAndWidgetsToButtonStates:(NSMutableArray *) oscButtonLayers {
|
||||
/* iterate through each OSC button the user sees on screen, create an 'OnScreenButtonState' object from each button, encode each object, and then add each object to an array */
|
||||
/*
|
||||
NSSet *validPositionButtonNames = [NSSet setWithObjects:
|
||||
@"l2Button",
|
||||
@"l1Button",
|
||||
@"dPad",
|
||||
@"selectButton",
|
||||
@"leftStickBackground",
|
||||
@"rightStickBackground",
|
||||
@"r2Button",
|
||||
@"r1Button",
|
||||
@"aButton",
|
||||
@"bButton",
|
||||
@"xButton",
|
||||
@"yButton",
|
||||
@"startButton",
|
||||
nil]; */
|
||||
NSSet *validPositionButtonNames = [NSSet setWithObjects:
|
||||
@"l2Button",
|
||||
@"l1Button",
|
||||
@"dPad",
|
||||
@"selectButton",
|
||||
@"leftStickBackground",
|
||||
@"rightStickBackground",
|
||||
@"r2Button",
|
||||
@"r1Button",
|
||||
@"aButton",
|
||||
@"bButton",
|
||||
@"xButton",
|
||||
@"yButton",
|
||||
@"startButton",
|
||||
nil]; */
|
||||
NSMutableArray *buttonStatesEncoded = [[NSMutableArray alloc] init];
|
||||
|
||||
// save on-screen game controller buttons & sticks as buttonstate:
|
||||
for (CALayer *oscButtonLayer in oscButtonLayers) {
|
||||
if(oscButtonLayer.isHidden) continue;
|
||||
|
||||
CGPoint normalizedPosition = [self normalizeWidgetPosition:oscButtonLayer.position];
|
||||
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:oscButtonLayer.name buttonType:LegacyOnScreenControls andPosition:normalizedPosition];
|
||||
// add hidden attr here
|
||||
@@ -390,7 +392,7 @@ static CGRect layoutViewBounds;
|
||||
CGPoint normalizedPosition = [self normalizeWidgetPosition:widgetView.center];
|
||||
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:widgetView.cmdString buttonType:CustomOnScreenWidget andPosition:normalizedPosition];
|
||||
buttonState.alias = widgetView.widgetLabel;
|
||||
buttonState.identifier = widgetView.identifier;
|
||||
buttonState.sequence = widgetView.sequence;
|
||||
buttonState.widthFactor = [self normalizeSizeWidthFactorWith:widgetView];
|
||||
buttonState.heightFactor = [self normalizeSizeHeightFactorWith:widgetView];
|
||||
buttonState.componentSizeFactor = [self normalizeComponentSizeFactorWith:widgetView];
|
||||
|
||||
@@ -24,7 +24,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property NSString *name;
|
||||
@property NSString *alias;
|
||||
@property NSString *identifier;
|
||||
@property (nonatomic, assign) BOOL folded;
|
||||
@property (nonatomic, assign) uint8_t revealMode;
|
||||
@property (nonatomic, assign) int16_t sequence;
|
||||
@property NSMutableSet *sequenceSet;
|
||||
@property CGPoint position;
|
||||
@property (nonatomic, assign) BOOL isHidden;
|
||||
@property (nonatomic, assign) uint8_t buttonMode;
|
||||
@@ -86,6 +89,11 @@ typedef NS_ENUM(NSInteger, ButtonMode) {
|
||||
movable
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, RevealMode) {
|
||||
coexist,
|
||||
exclusive
|
||||
};
|
||||
|
||||
- (id) initWithButtonName:(NSString*)name buttonType:(uint8_t)buttonType andPosition:(CGPoint)position;
|
||||
|
||||
+ (BOOL) supportsSecureCoding;
|
||||
|
||||
@@ -32,7 +32,10 @@
|
||||
|
||||
- (void) encodeWithCoder:(NSCoder*)encoder {
|
||||
[encoder encodeObject:self.name forKey:@"name"];
|
||||
[encoder encodeObject:([self.identifier isEqualToString:@""]||!self.identifier) ? [UUIDHelper newUUID] : self.identifier forKey:@"identifier"];
|
||||
[encoder encodeBool:self.folded forKey:@"folded"];
|
||||
[encoder encodeInt32:self.revealMode forKey:@"revealMode"];
|
||||
[encoder encodeInt32:self.sequence forKey:@"sequence"];
|
||||
[encoder encodeObject:self.sequenceSet forKey:@"sequenceSet"];
|
||||
[encoder encodeObject:self.alias forKey:@"alias"];
|
||||
[encoder encodeInt:self.widgetType forKey:@"buttonType"]; // keep original key
|
||||
[encoder encodeInt:self.sizeReference forKey:@"sizeReference"];
|
||||
@@ -69,7 +72,10 @@
|
||||
- (id) initWithCoder:(NSCoder*)decoder {
|
||||
if (self = [super init]) {
|
||||
self.name = [decoder decodeObjectForKey:@"name"];
|
||||
self.identifier = [decoder containsValueForKey:@"identifier"] ? [decoder decodeObjectForKey:@"identifier"] : [UUIDHelper newUUID];
|
||||
self.folded = [decoder containsValueForKey:@"folded"] ? [decoder decodeBoolForKey:@"folded"] : false;
|
||||
self.revealMode = [decoder containsValueForKey:@"revealMode"] ? [decoder decodeInt32ForKey:@"revealMode"] : exclusive;
|
||||
self.sequence = [decoder containsValueForKey:@"sequence"] ? [decoder decodeInt32ForKey:@"sequence"] : -1;
|
||||
self.sequenceSet = [decoder containsValueForKey:@"sequenceSet"] ? [decoder decodeObjectForKey:@"sequenceSet"] : [NSSet set];
|
||||
self.alias = [decoder decodeObjectForKey:@"alias"];
|
||||
self.widgetType = [decoder decodeIntForKey:@"buttonType"];
|
||||
self.sizeReference = [decoder containsValueForKey:@"sizeReference"] ? [decoder decodeIntForKey:@"sizeReference"] : longSide;
|
||||
|
||||
@@ -15,7 +15,7 @@ import CoreMotion
|
||||
func gyroMixInputStarted() -> Bool
|
||||
}
|
||||
|
||||
@objc class MotionHandler: NSObject, OscInstanceReceiverDelegate, OnScreenWidgetStickMixedInputDelegate{
|
||||
@objc class MotionHandler: NSObject, OnScreenWidgetStickMixedInputDelegate{
|
||||
public func mixOnScreenRightStickAndGyroInput(x: CGFloat, y: CGFloat) {
|
||||
rightStickTouchInputX = x
|
||||
rightStickTouchInputY = y
|
||||
@@ -28,48 +28,46 @@ import CoreMotion
|
||||
}
|
||||
|
||||
@objc public func mixPhysicalRightStickAndGyroInput(x: CGFloat, y: CGFloat) {
|
||||
rightStickPhysicalInputX = oscProfile.synthesizePhysicalStick ? x : 0
|
||||
rightStickPhysicalInputY = oscProfile.synthesizePhysicalStick ? y : 0
|
||||
rightStickPhysicalInputX = synthesizePhysicalStick ? x : 0
|
||||
rightStickPhysicalInputY = synthesizePhysicalStick ? y : 0
|
||||
}
|
||||
|
||||
@objc public func mixPhysicalLeftStickAndGyroInput(x: CGFloat, y: CGFloat) {
|
||||
// rollIntegral = 0
|
||||
leftStickPhysicalInputX = oscProfile.synthesizePhysicalStick ? x : 0
|
||||
leftStickPhysicalInputY = oscProfile.synthesizePhysicalStick ? y : 0
|
||||
leftStickPhysicalInputX = synthesizePhysicalStick ? x : 0
|
||||
leftStickPhysicalInputY = synthesizePhysicalStick ? y : 0
|
||||
}
|
||||
|
||||
public func gyroMixInputStarted() -> Bool {
|
||||
return gyroIsWorking
|
||||
}
|
||||
|
||||
func getOnScreenControlsInstance(_ sender: Any!) {
|
||||
if let controls = sender as? OnScreenControls {
|
||||
self.onScreenControls = controls
|
||||
print("ClassA received OnScreenControls instance: \(controls)")
|
||||
} else {
|
||||
print("ClassA received an unknown sender")
|
||||
}
|
||||
}
|
||||
private static let sharedInstance = MotionHandler()
|
||||
|
||||
static let shared = MotionHandler()
|
||||
|
||||
@objc class func sharedInstance() -> MotionHandler {
|
||||
let sharedInstance = MotionHandler.shared
|
||||
sharedInstance.oscProfile = sharedInstance.oscProfileMan.getSelectedProfile()
|
||||
sharedInstance.sensitvityYaw = sharedInstance.oscProfile.gyroSensitivityYaw
|
||||
sharedInstance.sensitvityPitch = sharedInstance.oscProfile.gyroSensitivityPitch
|
||||
sharedInstance.sensitvityRoll = sharedInstance.oscProfile.gyroSensitivityRoll
|
||||
sharedInstance.gyroToStickMinOffset = sharedInstance.oscProfile.gyroToStickMinOffset
|
||||
@objc class func shared(profile: OSCProfile?) -> MotionHandler {
|
||||
let sharedInstance = MotionHandler.sharedInstance
|
||||
guard let profile = profile else { return sharedInstance }
|
||||
sharedInstance.mapGyroTo = profile.mapGyroTo
|
||||
sharedInstance.synthesizePhysicalStick = profile.synthesizePhysicalStick
|
||||
sharedInstance.rollToLeftStick = profile.rollToLeftStick
|
||||
sharedInstance.yawPitchToRightStick = profile.yawPitchToRightStick
|
||||
sharedInstance.sensitvityYaw = profile.gyroSensitivityYaw
|
||||
sharedInstance.sensitvityPitch = profile.gyroSensitivityPitch
|
||||
sharedInstance.sensitvityRoll = profile.gyroSensitivityRoll
|
||||
sharedInstance.gyroToStickMinOffset = profile.gyroToStickMinOffset
|
||||
sharedInstance.onScreenControls = OnScreenControls.shared()
|
||||
return sharedInstance
|
||||
}
|
||||
|
||||
private var oscProfileMan: OSCProfilesManager
|
||||
private var oscProfile: OSCProfile
|
||||
|
||||
@objc public var gyroControlStarted: Bool = false
|
||||
private var gyroIsWorking: Bool = false
|
||||
private var accelControlStarted: Bool = false
|
||||
private let motionManager = CMMotionManager()
|
||||
|
||||
private var synthesizePhysicalStick:Bool = false
|
||||
private var mapGyroTo:MapGyroTo = .mapGyroToControllerStick
|
||||
private var rollToLeftStick:Bool = false
|
||||
private var yawPitchToRightStick:Bool = false
|
||||
public var sensitvityYaw:CGFloat = 1.0
|
||||
public var sensitvityPitch:CGFloat = 1.0
|
||||
public var sensitvityRoll:CGFloat = 1.0
|
||||
@@ -82,7 +80,7 @@ import CoreMotion
|
||||
public var gyroStarter:Any?
|
||||
private var windowScene: Any?
|
||||
|
||||
@objc public var onScreenControls: OnScreenControls = OnScreenControls.init()
|
||||
@objc public var onScreenControls: OnScreenControls?
|
||||
public let stickMaxOffset: CGFloat = 0x7FFE
|
||||
private var stickInputScale: CGFloat = 35
|
||||
|
||||
@@ -124,8 +122,7 @@ import CoreMotion
|
||||
}
|
||||
|
||||
@objc public override init() {
|
||||
self.oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
self.oscProfile = oscProfileMan.getSelectedProfile()
|
||||
let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
super.init()
|
||||
if #available(iOS 13.0, *) {
|
||||
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
|
||||
@@ -295,14 +292,16 @@ import CoreMotion
|
||||
|
||||
gyroIsWorking = true
|
||||
|
||||
if oscProfile.mapGyroTo == MapGyroTo.mapGyroToMouse {
|
||||
if mapGyroTo == MapGyroTo.mapGyroToMouse {
|
||||
yaw = yawSource*sensitvityYaw*widgetYawFactor*30
|
||||
pitch = pitchSource*sensitvityPitch*widgetPitchFactor*30
|
||||
LiSendMouseMoveEvent(Int16(yaw),Int16(pitch))
|
||||
}
|
||||
|
||||
if oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick {
|
||||
if oscProfile.yawPitchToRightStick {
|
||||
|
||||
//guard let onScreenControls = onScreenControls else { return }
|
||||
|
||||
if mapGyroTo == MapGyroTo.mapGyroToControllerStick {
|
||||
if yawPitchToRightStick {
|
||||
gyroToStickOffset.dx = gyroInputToStickInput(input:yawSource*sensitvityYaw*widgetYawFactor*10)
|
||||
yaw = rightStickTouchInputX + rightStickPhysicalInputX + gyroToStickOffset.dx
|
||||
yaw = self.clampStickInput(input: yaw)
|
||||
@@ -313,9 +312,9 @@ import CoreMotion
|
||||
|
||||
let offsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: yaw, dy: pitch), minOffset: gyroToStickMinOffset)
|
||||
|
||||
self.onScreenControls.sendRightStickTouchPadEvent(offsetVector.dx, offsetVector.dy)
|
||||
onScreenControls?.sendRightStickTouchPadEvent(offsetVector.dx, offsetVector.dy)
|
||||
}
|
||||
if oscProfile.rollToLeftStick {
|
||||
if rollToLeftStick {
|
||||
roll = gyroInputToStickInput(input:rollSource*sensitvityRoll*widgetRollFactor*0.2)
|
||||
rollIntegral = rollIntegral + roll
|
||||
|
||||
@@ -324,24 +323,26 @@ import CoreMotion
|
||||
|
||||
let offsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: mixedLeftStickOffsetX, dy: mixedLeftStickOffsetY), minOffset: gyroToStickMinOffset)
|
||||
|
||||
self.onScreenControls.sendLeftStickTouchPadEvent(offsetVector.dx, offsetVector.dy)
|
||||
onScreenControls?.sendLeftStickTouchPadEvent(offsetVector.dx, offsetVector.dy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func clearGyroInput(interruptNonGyroInput:Bool, resetLeftStick:Bool=false){
|
||||
if oscProfile.yawPitchToRightStick{
|
||||
self.onScreenControls.sendRightStickTouchPadEvent(rightStickPhysicalInputX+rightStickTouchInputX-yawBias, rightStickPhysicalInputY+rightStickTouchInputY-pitchBias)
|
||||
// guard let onScreenControls = onScreenControls else { return }
|
||||
|
||||
if yawPitchToRightStick{
|
||||
onScreenControls?.sendRightStickTouchPadEvent(rightStickPhysicalInputX+rightStickTouchInputX-yawBias, rightStickPhysicalInputY+rightStickTouchInputY-pitchBias)
|
||||
}
|
||||
if oscProfile.rollToLeftStick{
|
||||
if rollToLeftStick{
|
||||
if resetLeftStick {
|
||||
rollIntegral = 0
|
||||
self.onScreenControls.sendLeftStickTouchPadEvent(leftStickPhysicalInputX+leftStickTouchInputX-rollBias,leftStickPhysicalInputY+leftStickTouchInputY)
|
||||
onScreenControls?.sendLeftStickTouchPadEvent(leftStickPhysicalInputX+leftStickTouchInputX-rollBias,leftStickPhysicalInputY+leftStickTouchInputY)
|
||||
}
|
||||
}
|
||||
if(interruptNonGyroInput){
|
||||
self.onScreenControls.clearLeftStickTouchPadFlag()
|
||||
self.onScreenControls.clearRightStickTouchPadFlag()
|
||||
onScreenControls?.clearLeftStickTouchPadFlag()
|
||||
onScreenControls?.clearRightStickTouchPadFlag()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, assign) NSMutableSet<UITouch *>* touchesCapturedByOnScreenButtons;
|
||||
|
||||
- (id)initWithView:(StreamView* )view andSettings:(TemporarySettings*)settings;
|
||||
|
||||
- (void)setAllowSingleTouchEnabled:(BOOL)enabled;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
#include <Limelight.h>
|
||||
|
||||
@implementation NativeTouchHandler {
|
||||
StreamView* streamView;
|
||||
TemporarySettings* currentSettings;
|
||||
__weak StreamView* streamView;
|
||||
bool activateCoordSelector;
|
||||
bool singleTouchDisabled;
|
||||
CGFloat pointerVelocityDividerLocationByPoints;
|
||||
|
||||
bool asyncNativeTouch;
|
||||
@@ -187,10 +188,18 @@
|
||||
}
|
||||
|
||||
|
||||
- (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{
|
||||
- (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType withEvent:(UIEvent *)event{
|
||||
//if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash
|
||||
if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return;
|
||||
|
||||
if(singleTouchDisabled
|
||||
&& [UITouchUtil touchesIn:streamView from:event].count != 2
|
||||
&& touchType != LI_TOUCH_EVENT_UP) return;
|
||||
|
||||
if(PencilHandler.pencilPausesNativeTouch
|
||||
&& PencilHandler.isDrawing
|
||||
&& touchType != LI_TOUCH_EVENT_UP) return;
|
||||
|
||||
CGPoint targetCoords;
|
||||
//NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved);
|
||||
// NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]);
|
||||
@@ -216,7 +225,7 @@
|
||||
if([OnScreenControls.touchesCapturedByOnScreenControls containsObject:touch]) continue;
|
||||
[self handleTouchDown:touch]; //generate & populate pointerId
|
||||
if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN withEvent:event];
|
||||
}
|
||||
});
|
||||
else{
|
||||
@@ -225,7 +234,7 @@
|
||||
if([OnScreenControls.touchesCapturedByOnScreenControls containsObject:touch]) continue;
|
||||
[self handleTouchDown:touch]; //generate & populate pointerId
|
||||
if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN withEvent:event];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,7 +244,7 @@
|
||||
for (UITouch* touch in touches){
|
||||
if([OnScreenControls.touchesCapturedByOnScreenControls containsObject:touch]) continue;
|
||||
if(self->activateCoordSelector) [self updatePointerObjInDict:touch];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE withEvent:event];
|
||||
[[self getPointerObjFromDict:touch] doesNeedResetCoords];
|
||||
}
|
||||
});
|
||||
@@ -248,7 +257,7 @@
|
||||
[OnScreenControls.touchesCapturedByOnScreenControls removeObject:touch];
|
||||
continue;
|
||||
}
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId
|
||||
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP withEvent:event]; //send touch event before remove pointerId
|
||||
[self removePointerId:touch]; //then remove pointerId
|
||||
if(self->activateCoordSelector) [self removePointerObjFromDict:touch];
|
||||
[self->blacklistedTouches removeObject:@((uintptr_t)touch)];
|
||||
@@ -289,4 +298,8 @@
|
||||
return pointer.useRelativeCoords ? pointer.latestRelativePoint : pointer.latestPoint;
|
||||
}
|
||||
|
||||
- (void)setAllowSingleTouchEnabled:(BOOL)enabled{
|
||||
singleTouchDisabled = !enabled;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
+ (void)initContextWithView:(StreamView*)view andSettings:(TemporarySettings*)settings;
|
||||
+ (void)cleanUpContext;
|
||||
|
||||
- (bool)doesNeedResetCoords;
|
||||
- (void)updatePointerCoords:(UITouch *)touch;
|
||||
|
||||
@@ -63,4 +63,8 @@ static StreamView* streamView;
|
||||
NSLog(@"stream wdith %f, stream height %f", streamViewWidth, streamViewHeight);
|
||||
}
|
||||
|
||||
+ (void)cleanUpContext {
|
||||
streamView = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,12 +17,7 @@
|
||||
|
||||
@class ControllerSupport;
|
||||
@class StreamConfiguration;
|
||||
|
||||
// sending self as an instance to OnScreenWidgetView or other related classes
|
||||
@protocol OscInstanceReceiverDelegate <NSObject>
|
||||
- (void)getOnScreenControlsInstance:(id)sender; // Renamed delegate method
|
||||
@end
|
||||
|
||||
@class LayoutOnScreenControls;
|
||||
|
||||
static const float D_PAD_DIST = 10;
|
||||
static const float BUTTON_DIST = 20;
|
||||
@@ -52,11 +47,6 @@ typedef NS_ENUM(NSInteger, OnScreenControlsLevel) {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@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
|
||||
@property (nonatomic, assign) bool isLayingOut;
|
||||
//@property (nonatomic) NSMutableSet<UITouch* >* touchesCapturedByOnScreenButtons;
|
||||
@@ -105,7 +95,7 @@ typedef NS_ENUM(NSInteger, OnScreenControlsLevel) {
|
||||
|
||||
@property OnScreenControlsLevel _level;
|
||||
|
||||
@property NSMutableArray *OSCButtonLayers;
|
||||
@property NSMutableSet *OSCButtonLayerPool;
|
||||
@property NSMutableDictionary* layerVibrationStyleDic;
|
||||
|
||||
|
||||
@@ -128,18 +118,19 @@ typedef NS_ENUM(NSInteger, OnScreenControlsLevel) {
|
||||
- (BOOL) handleTouchUpEvent:(NSSet*)touches;
|
||||
- (BOOL) handleTouchMovedEvent:(NSSet*)touches;
|
||||
- (void) setLevel:(OnScreenControlsLevel)level;
|
||||
- (void) show;
|
||||
- (void) showLegacyWidgetsWith:(OSCProfile* )profile;
|
||||
- (void) regenLayerPool;
|
||||
- (void) setupComplexControls;
|
||||
- (void) drawButtons;
|
||||
- (void) drawButtons:(OSCProfile* _Nullable)profile;
|
||||
- (void) drawBumpers;
|
||||
- (void) updateControls;
|
||||
- (void) updateLegacyWidgetsWith:(OSCProfile* _Nullable )profile;
|
||||
- (OnScreenControlsLevel) getLevel;
|
||||
- (void) setDPadCenter;
|
||||
- (void) setAnalogStickPositions;
|
||||
- (void) positionAndResizeSingleControllerLayers;
|
||||
- (void) resizeControllerLayerWith:(CALayer*)layer and:(CGFloat)sizeFactor;
|
||||
- (void) adjustControllerLayerOpacityWith:(CALayer*)layer and:(CGFloat)alpha;
|
||||
+ (CGFloat) getControllerLayerSizeFactor:(CALayer*)layer;
|
||||
- (CGFloat) getControllerLayerOpacity:(CALayer*)layer;
|
||||
- (void) setDPadCenter:(OSCProfile *_Nonnull)profile;;
|
||||
- (void) setAnalogStickPositions:(OSCProfile *_Nonnull)profile;
|
||||
- (void) positionAndResizeSingleControllerLayers:(OSCProfile *_Nonnull)profile;
|
||||
- (void) resizeControllerLayerWith:(CALayer*_Nonnull)layer and:(CGFloat)sizeFactor;
|
||||
- (void) adjustControllerLayerOpacityWith:(CALayer*_Nonnull)layer and:(CGFloat)alpha;
|
||||
+ (CGFloat) getControllerLayerSizeFactor:(CALayer*_Nonnull)layer;
|
||||
- (CGFloat) getControllerLayerOpacity:(CALayer*_Nonnull)layer;
|
||||
|
||||
@end
|
||||
|
||||
+298
-215
@@ -10,6 +10,7 @@
|
||||
//
|
||||
|
||||
#import "OnScreenControls.h"
|
||||
#import "LayoutOnScreenControls.h"
|
||||
#import "CustomTapGestureRecognizer.h"
|
||||
#import "VoidController.h"
|
||||
#include "Limelight.h"
|
||||
@@ -105,7 +106,7 @@ static NSSet *validPositionButtonNames;
|
||||
@synthesize _l2Button;
|
||||
@synthesize _l3Button;
|
||||
@synthesize _level;
|
||||
@synthesize OSCButtonLayers;
|
||||
@synthesize OSCButtonLayerPool;
|
||||
@synthesize _dPadBackground;
|
||||
|
||||
static const float EDGE_WIDTH = .05;
|
||||
@@ -203,16 +204,6 @@ static float L3_Y;
|
||||
[_controllerSupport updateFinished:_controller];
|
||||
}
|
||||
|
||||
// sending self as an instance to OnScreenWidgetView
|
||||
- (void)sendInstance{
|
||||
NSLog(@"OnScreenControls is sending its instance...");
|
||||
if ([self.instanceReceiverDelegate respondsToSelector:@selector(getOnScreenControlsInstance:)]) {
|
||||
[self.instanceReceiverDelegate getOnScreenControlsInstance:self];
|
||||
} else {
|
||||
NSLog(@"Delegate not set or does not respond to getOnScreenControlsInstance:");
|
||||
}
|
||||
}
|
||||
|
||||
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport streamConfig:(StreamConfiguration*)streamConfig {
|
||||
self = [self init];
|
||||
self.isLayingOut = false; // set false by default (play mode instead of layout mode)
|
||||
@@ -220,7 +211,7 @@ static float L3_Y;
|
||||
|
||||
profilesManager = [OSCProfilesManager sharedManager:view.bounds];
|
||||
|
||||
self.OSCButtonLayers = [[NSMutableArray alloc] init];
|
||||
self.OSCButtonLayerPool = [[NSMutableSet alloc] init];
|
||||
|
||||
if (controllerSupport) {
|
||||
_controllerSupport = controllerSupport;
|
||||
@@ -272,28 +263,10 @@ static float L3_Y;
|
||||
_rightStickBackground = [CALayer layer];
|
||||
_leftStick = [CALayer layer];
|
||||
_rightStick = [CALayer layer];
|
||||
|
||||
[self.OSCButtonLayers addObject:_aButton];
|
||||
[self.OSCButtonLayers addObject:_bButton];
|
||||
[self.OSCButtonLayers addObject:_xButton];
|
||||
[self.OSCButtonLayers addObject:_yButton];
|
||||
[self.OSCButtonLayers addObject:_startButton];
|
||||
[self.OSCButtonLayers addObject:_selectButton];
|
||||
[self.OSCButtonLayers addObject:_r1Button];
|
||||
[self.OSCButtonLayers addObject:_r2Button];
|
||||
[self.OSCButtonLayers addObject:_r3Button];
|
||||
[self.OSCButtonLayers addObject:_l1Button];
|
||||
[self.OSCButtonLayers addObject:_l2Button];
|
||||
[self.OSCButtonLayers addObject:_l3Button];
|
||||
[self.OSCButtonLayers addObject:_upButton];
|
||||
[self.OSCButtonLayers addObject:_downButton];
|
||||
[self.OSCButtonLayers addObject:_leftButton];
|
||||
[self.OSCButtonLayers addObject:_rightButton];
|
||||
[self.OSCButtonLayers addObject:_leftStickBackground];
|
||||
[self.OSCButtonLayers addObject:_rightStickBackground];
|
||||
[self.OSCButtonLayers addObject:_leftStick];
|
||||
[self.OSCButtonLayers addObject:_rightStick];
|
||||
_dPadBackground = [CALayer layer];
|
||||
|
||||
[self regenLayerPool];
|
||||
|
||||
/* Name button layers to allow us to more easily associate them with 'OnScreenButtonState' objects by comparing their name properties */
|
||||
_leftStickBackground.name = @"leftStickBackground";
|
||||
_rightStickBackground.name = @"rightStickBackground";
|
||||
@@ -315,6 +288,7 @@ static float L3_Y;
|
||||
_rightButton.name = @"rightButton";
|
||||
_downButton.name = @"downButton";
|
||||
_leftButton.name = @"leftButton";
|
||||
_dPadBackground.name = @"dPad";
|
||||
|
||||
|
||||
_standardRoundButtonBounds = standardRoundButtonBounds = CGRectMake(0, 0, [UIImage imageNamed:@"AButton"].size.width, [UIImage imageNamed:@"AButton"].size.height);
|
||||
@@ -326,14 +300,43 @@ static float L3_Y;
|
||||
|
||||
_activeCustomOscButtonPositionDict = [[NSMutableDictionary alloc] init];
|
||||
touchesCapturedByOnScreenControls = [[NSMutableSet alloc] init];
|
||||
|
||||
|
||||
if(![self isKindOfClass:[LayoutOnScreenControls class]]) OnScreenControls.shared = self;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) show {
|
||||
- (void)regenLayerPool{
|
||||
for(CALayer* layer in self.OSCButtonLayerPool){
|
||||
[layer removeFromSuperlayer];
|
||||
}
|
||||
[self.OSCButtonLayerPool removeAllObjects];
|
||||
[self.OSCButtonLayerPool addObject:_aButton];
|
||||
[self.OSCButtonLayerPool addObject:_bButton];
|
||||
[self.OSCButtonLayerPool addObject:_xButton];
|
||||
[self.OSCButtonLayerPool addObject:_yButton];
|
||||
[self.OSCButtonLayerPool addObject:_startButton];
|
||||
[self.OSCButtonLayerPool addObject:_selectButton];
|
||||
[self.OSCButtonLayerPool addObject:_r1Button];
|
||||
[self.OSCButtonLayerPool addObject:_r2Button];
|
||||
// [self.OSCButtonLayerPool addObject:_r3Button];
|
||||
[self.OSCButtonLayerPool addObject:_l1Button];
|
||||
[self.OSCButtonLayerPool addObject:_l2Button];
|
||||
// [self.OSCButtonLayerPool addObject:_l3Button];
|
||||
[self.OSCButtonLayerPool addObject:_upButton];
|
||||
[self.OSCButtonLayerPool addObject:_downButton];
|
||||
[self.OSCButtonLayerPool addObject:_leftButton];
|
||||
[self.OSCButtonLayerPool addObject:_rightButton];
|
||||
[self.OSCButtonLayerPool addObject:_leftStickBackground];
|
||||
[self.OSCButtonLayerPool addObject:_rightStickBackground];
|
||||
[self.OSCButtonLayerPool addObject:_leftStick];
|
||||
[self.OSCButtonLayerPool addObject:_rightStick];
|
||||
}
|
||||
|
||||
- (void) showLegacyWidgetsWith:(OSCProfile* )profile {
|
||||
_visible = YES;
|
||||
|
||||
[self updateControls];
|
||||
NSLog(@"show updateControls %f", CACurrentMediaTime());
|
||||
[self updateLegacyWidgetsWith:profile];
|
||||
}
|
||||
|
||||
- (void) setLevel:(OnScreenControlsLevel)level {
|
||||
@@ -342,7 +345,7 @@ static float L3_Y;
|
||||
// Only update controls if we're showing, otherwise
|
||||
// show will do it for us.
|
||||
if (_visible) {
|
||||
[self updateControls];
|
||||
[self updateLegacyWidgetsWith:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,13 +362,20 @@ static float L3_Y;
|
||||
return position;
|
||||
}
|
||||
|
||||
- (void) updateControls {
|
||||
if(self._level == OnScreenControlsLevelCustom){
|
||||
- (void) updateLegacyWidgetsWith:(OSCProfile* _Nullable)profile {
|
||||
if(self._level == OnScreenControlsLevelCustom && profile){
|
||||
// mark all OSC buttons that has valid coords of positions
|
||||
validPositionButtonNames = [NSSet setWithObjects:
|
||||
[self regenLayerPool];
|
||||
[_activeCustomOscButtonPositionDict removeAllObjects]; //reset the Dict.
|
||||
|
||||
NSSet* persistedButtonNames = [NSSet setWithObjects:
|
||||
@"l2Button",
|
||||
@"l1Button",
|
||||
@"dPad",
|
||||
@"upButton",
|
||||
@"downButton",
|
||||
@"leftButton",
|
||||
@"rightButton",
|
||||
@"selectButton",
|
||||
@"leftStickBackground",
|
||||
@"rightStickBackground",
|
||||
@@ -378,15 +388,29 @@ static float L3_Y;
|
||||
@"startButton",
|
||||
nil];
|
||||
|
||||
NSSet* singleLayerButtons = [NSSet setWithObjects:
|
||||
@"l2Button",
|
||||
@"l1Button",
|
||||
@"selectButton",
|
||||
@"r2Button",
|
||||
@"r1Button",
|
||||
@"aButton",
|
||||
@"bButton",
|
||||
@"xButton",
|
||||
@"yButton",
|
||||
@"startButton",
|
||||
nil];
|
||||
|
||||
// _activeCustomOscButtonPositionDict will be updated every time when the osc profile is reloaded
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
[_activeCustomOscButtonPositionDict removeAllObjects]; //reset the Dict.
|
||||
// if(!profile) profile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
|
||||
// NSLog(@"_activeCustomOscButtonPositionDict update: STARTOVER");
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
for (NSData *buttonStateEncoded in profile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if(!buttonState) continue;
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
[OnScreenControls.layerVibrationStyleDic setObject:@(buttonState.vibrationStyle) forKey:buttonState.name];
|
||||
if(!buttonState.isHidden && [validPositionButtonNames containsObject:buttonState.name] && (buttonState.widgetType == LegacyOnScreenControls || [profilesManager getIndexOfSelectedProfile] == 0 ) ){
|
||||
if(!buttonState.isHidden && [persistedButtonNames containsObject:buttonState.name] && buttonState.widgetType == LegacyOnScreenControls){
|
||||
[_activeCustomOscButtonPositionDict setObject:[NSValue valueWithCGPoint:buttonState.position] forKey:buttonState.name]; // we got a buttonname -> position dict here
|
||||
// NSLog(@"_activeCustomOscButtonPositionDict update, button name:%@, position: %f, %f", buttonState.name, buttonState.position.x, buttonState.position.y);
|
||||
}
|
||||
@@ -410,6 +434,33 @@ static float L3_Y;
|
||||
}
|
||||
}
|
||||
//NSLog(@"_activeCustomOscButtonPositionDict update, active button number: %lu", (unsigned long)[_activeCustomOscButtonPositionDict count]);
|
||||
NSSet* tempLayerPool = [self.OSCButtonLayerPool copy];
|
||||
|
||||
for (CALayer* button in tempLayerPool){
|
||||
if([singleLayerButtons containsObject:button.name]
|
||||
&& ![_activeCustomOscButtonPositionDict.allKeys containsObject:button.name]) [OSCButtonLayerPool removeObject:button];
|
||||
if(![_activeCustomOscButtonPositionDict.allKeys containsObject:@"upButton"]){
|
||||
[OSCButtonLayerPool removeObject:_dPadBackground];
|
||||
[OSCButtonLayerPool removeObject:_upButton];
|
||||
[OSCButtonLayerPool removeObject:_downButton];
|
||||
[OSCButtonLayerPool removeObject:_leftButton];
|
||||
[OSCButtonLayerPool removeObject:_rightButton];
|
||||
}
|
||||
if(![_activeCustomOscButtonPositionDict.allKeys containsObject:@"leftStickBackground"]){
|
||||
[OSCButtonLayerPool removeObject:_leftStick];
|
||||
[OSCButtonLayerPool removeObject:_leftStickBackground];
|
||||
}
|
||||
if(![_activeCustomOscButtonPositionDict.allKeys containsObject:@"rightStickBackground"]){
|
||||
[OSCButtonLayerPool removeObject:_rightStick];
|
||||
[OSCButtonLayerPool removeObject:_rightStickBackground];
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"OSCButtonLayerPool count: %ld", OSCButtonLayerPool.count);
|
||||
for(CALayer* layer in OSCButtonLayerPool){
|
||||
NSLog(@"OSCButtonLayerPool name: %@", layer.name);
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
// deal with simple & full mode configurations
|
||||
@@ -472,13 +523,13 @@ static float L3_Y;
|
||||
[self hideBumpers];
|
||||
[self hideSticks];
|
||||
[self drawStartSelect];
|
||||
[self drawButtons];
|
||||
[self drawButtons:profile];
|
||||
[self setOpacityForStandardControllerLayers];
|
||||
break;
|
||||
case OnScreenControlsLevelFull:
|
||||
|
||||
[self setupComplexControls];
|
||||
[self drawButtons];
|
||||
[self drawButtons:profile];
|
||||
[self drawStartSelect];
|
||||
[self drawBumpers];
|
||||
[self drawTriggers];
|
||||
@@ -489,15 +540,15 @@ static float L3_Y;
|
||||
case OnScreenControlsLevelCustom:
|
||||
|
||||
[self setupComplexControls]; // Default postion for D-Pad set here
|
||||
[self setDPadCenter]; // Custom position for D-Pad set here
|
||||
[self setAnalogStickPositions]; // Custom position for analog sticks set here
|
||||
[self drawButtons];
|
||||
[self setDPadCenter:profile]; // Custom position for D-Pad set here
|
||||
[self setAnalogStickPositions:profile]; // Custom position for analog sticks set here
|
||||
[self drawButtons:profile];
|
||||
[self drawStartSelect];
|
||||
[self drawBumpers];
|
||||
[self drawTriggers];
|
||||
[self drawSticks];
|
||||
[self positionAndResizeSingleControllerLayers];
|
||||
[self setOpacityForCutsomControllerLayers];
|
||||
[self positionAndResizeSingleControllerLayers:profile];
|
||||
[self setOpacityForCutsomControllerLayers:profile];
|
||||
|
||||
break;
|
||||
default:
|
||||
@@ -506,9 +557,11 @@ static float L3_Y;
|
||||
}
|
||||
|
||||
// populate the controllerLayer.name -> Opacity dictionary... have to do this in order to make touchdown visual effect consistent
|
||||
for (CALayer* controllerLayer in self.OSCButtonLayers){
|
||||
for (CALayer* controllerLayer in self.OSCButtonLayerPool){
|
||||
[_originalControllerLayerOpacityDict setObject:@(controllerLayer.opacity) forKey:controllerLayer.name];
|
||||
}
|
||||
|
||||
[self removeHiddenlayers];
|
||||
}
|
||||
|
||||
// For GCExtendedGamepad controls we move start, select, L3, and R3 to the button
|
||||
@@ -635,7 +688,7 @@ static float L3_Y;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) drawButtons {
|
||||
- (void) drawButtons:(OSCProfile* )profile {
|
||||
UIImage* aButtonImage = [UIImage imageNamed:@"AButton"];
|
||||
UIImage* aButtonHdImage = [UIImage imageNamed:@"AButtonHD"];
|
||||
UIImage* bButtonImage = [UIImage imageNamed:@"BButton"];
|
||||
@@ -644,7 +697,7 @@ static float L3_Y;
|
||||
UIImage* xButtonHdImage = [UIImage imageNamed:@"XButtonHD"];
|
||||
UIImage* yButtonImage = [UIImage imageNamed:@"YButton"];
|
||||
UIImage* yButtonHdImage = [UIImage imageNamed:@"YButtonHD"];
|
||||
|
||||
|
||||
|
||||
|
||||
CGRect aButtonFrame = CGRectMake(BUTTON_CENTER_X - aButtonImage.size.width / 2, BUTTON_CENTER_Y + BUTTON_DIST, aButtonImage.size.width, aButtonImage.size.height);
|
||||
@@ -653,33 +706,41 @@ static float L3_Y;
|
||||
CGRect yButtonFrame = CGRectMake(BUTTON_CENTER_X - yButtonImage.size.width / 2, BUTTON_CENTER_Y - BUTTON_DIST - yButtonImage.size.height, yButtonImage.size.width, yButtonImage.size.height);
|
||||
|
||||
// create A button
|
||||
_aButton.contents = (id) aButtonHdImage.CGImage;
|
||||
// Set the filtering mode for smooth downscaling
|
||||
_aButton.minificationFilter = kCAFilterLinear; // Smooth scaling down
|
||||
_aButton.magnificationFilter = kCAFilterLinear; // Smooth scaling up (if needed)
|
||||
_aButton.minificationFilterBias = 0.0f; // Trilinear-like effect
|
||||
|
||||
// Enable anti-aliasing for smoother edges
|
||||
_aButton.allowsEdgeAntialiasing = YES;
|
||||
_aButton.edgeAntialiasingMask = kCALayerLeftEdge | kCALayerRightEdge | kCALayerBottomEdge | kCALayerTopEdge;
|
||||
|
||||
_aButton.frame = _swapABXY ? bButtonFrame : aButtonFrame;
|
||||
[_view.layer addSublayer:_aButton]; // rendering OSC Button here
|
||||
if([OSCButtonLayerPool containsObject:_aButton]){
|
||||
_aButton.contents = (id) aButtonHdImage.CGImage;
|
||||
// Set the filtering mode for smooth downscaling
|
||||
_aButton.minificationFilter = kCAFilterLinear; // Smooth scaling down
|
||||
_aButton.magnificationFilter = kCAFilterLinear; // Smooth scaling up (if needed)
|
||||
_aButton.minificationFilterBias = 0.0f; // Trilinear-like effect
|
||||
|
||||
// Enable anti-aliasing for smoother edges
|
||||
_aButton.allowsEdgeAntialiasing = YES;
|
||||
_aButton.edgeAntialiasingMask = kCALayerLeftEdge | kCALayerRightEdge | kCALayerBottomEdge | kCALayerTopEdge;
|
||||
|
||||
_aButton.frame = _swapABXY ? bButtonFrame : aButtonFrame;
|
||||
[_view.layer addSublayer:_aButton]; // rendering OSC Button here
|
||||
}
|
||||
|
||||
// create B button
|
||||
_bButton.frame = _swapABXY ? aButtonFrame : bButtonFrame;
|
||||
_bButton.contents = (id) bButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_bButton];
|
||||
if([OSCButtonLayerPool containsObject:_bButton]){
|
||||
_bButton.frame = _swapABXY ? aButtonFrame : bButtonFrame;
|
||||
_bButton.contents = (id) bButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_bButton];
|
||||
}
|
||||
|
||||
// create X Button
|
||||
_xButton.frame = _swapABXY ? yButtonFrame : xButtonFrame;
|
||||
_xButton.contents = (id) xButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_xButton];
|
||||
if([OSCButtonLayerPool containsObject:_xButton]){
|
||||
_xButton.frame = _swapABXY ? yButtonFrame : xButtonFrame;
|
||||
_xButton.contents = (id) xButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_xButton];
|
||||
}
|
||||
|
||||
// create Y Button
|
||||
_yButton.frame = _swapABXY ? xButtonFrame : yButtonFrame;
|
||||
_yButton.contents = (id) yButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_yButton];
|
||||
if([OSCButtonLayerPool containsObject:_yButton]){
|
||||
_yButton.frame = _swapABXY ? xButtonFrame : yButtonFrame;
|
||||
_yButton.contents = (id) yButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_yButton];
|
||||
}
|
||||
|
||||
|
||||
if(self._level == OnScreenControlsLevelFull ||
|
||||
@@ -687,59 +748,58 @@ static float L3_Y;
|
||||
_dPadSizeFactor = 1.0;
|
||||
}
|
||||
|
||||
// Calculate the distances of each button from the shared center based on their transformed positions
|
||||
CGFloat newDPadDistFactor = 0.2/_dPadSizeFactor;
|
||||
CGFloat newLongSideLength = standardLeftRightButtonBounds.size.width * _dPadSizeFactor;
|
||||
CGFloat newShortSideLength = standardLeftRightButtonBounds.size.height * _dPadSizeFactor;
|
||||
CGPoint sharedCenter = CGPointMake(D_PAD_CENTER_X, D_PAD_CENTER_Y); // this will anchor the center point of the dPad
|
||||
|
||||
|
||||
// dPad buttons are resized HERE
|
||||
// up button
|
||||
// UIImage* upButtonImage = [UIImage imageNamed:@"UpButton"];
|
||||
UIImage* upButtonHdImage = [UIImage imageNamed:@"UpButtonHD"];
|
||||
_upButton.contents = (id) upButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_upButton];
|
||||
_upButton.anchorPoint = CGPointMake(0.5, 1 + newDPadDistFactor);
|
||||
_upButton.bounds = CGRectMake(0, 0, newShortSideLength, newLongSideLength);
|
||||
_upButton.position = CGPointMake(sharedCenter.x, sharedCenter.y);
|
||||
|
||||
// down button
|
||||
// UIImage* downButtonImage = [UIImage imageNamed:@"DownButton"];
|
||||
UIImage* downButtonHdImage = [UIImage imageNamed:@"DownButtonHD"];
|
||||
_downButton.contents = (id) downButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_downButton];
|
||||
// Resize and reposition the button
|
||||
_downButton.anchorPoint = CGPointMake(0.5, 0 - newDPadDistFactor);
|
||||
_downButton.bounds = CGRectMake(0, 0, newShortSideLength, newLongSideLength);
|
||||
_downButton.position = CGPointMake(sharedCenter.x, sharedCenter.y);
|
||||
|
||||
// left button
|
||||
// UIImage* leftButtonImage = [UIImage imageNamed:@"LeftButton"];
|
||||
UIImage* leftButtonHdImage = [UIImage imageNamed:@"LeftButtonHD"];
|
||||
_leftButton.contents = (id) leftButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_leftButton];
|
||||
_leftButton.anchorPoint = CGPointMake(1 + newDPadDistFactor, 0.5);
|
||||
_leftButton.bounds = CGRectMake(0, 0, newLongSideLength, newShortSideLength);
|
||||
_leftButton.position = CGPointMake(sharedCenter.x, sharedCenter.y);
|
||||
|
||||
// right button
|
||||
// UIImage* rightButtonImage = [UIImage imageNamed:@"RightButton"];
|
||||
UIImage* rightButtonHdImage = [UIImage imageNamed:@"RightButtonHD"];
|
||||
_rightButton.contents = (id) rightButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_rightButton];
|
||||
_rightButton.anchorPoint = CGPointMake(0 - newDPadDistFactor, 0.5);
|
||||
_rightButton.bounds = CGRectMake(0, 0, newLongSideLength, newShortSideLength);
|
||||
_rightButton.position = CGPointMake(sharedCenter.x, sharedCenter.y);
|
||||
if([_activeCustomOscButtonPositionDict.allKeys containsObject:@"dPad"]){
|
||||
// Calculate the distances of each button from the shared center based on their transformed positions
|
||||
CGFloat newDPadDistFactor = 0.2/_dPadSizeFactor;
|
||||
CGFloat newLongSideLength = standardLeftRightButtonBounds.size.width * _dPadSizeFactor;
|
||||
CGFloat newShortSideLength = standardLeftRightButtonBounds.size.height * _dPadSizeFactor;
|
||||
CGPoint sharedCenter = CGPointMake(D_PAD_CENTER_X, D_PAD_CENTER_Y); // this will anchor the center point of the dPad
|
||||
|
||||
UIImage* upButtonHdImage = [UIImage imageNamed:@"UpButtonHD"];
|
||||
_upButton.contents = (id) upButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_upButton];
|
||||
_upButton.anchorPoint = CGPointMake(0.5, 1 + newDPadDistFactor);
|
||||
_upButton.bounds = CGRectMake(0, 0, newShortSideLength, newLongSideLength);
|
||||
_upButton.position = CGPointMake(sharedCenter.x, sharedCenter.y);
|
||||
|
||||
// down button
|
||||
// UIImage* downButtonImage = [UIImage imageNamed:@"DownButton"];
|
||||
UIImage* downButtonHdImage = [UIImage imageNamed:@"DownButtonHD"];
|
||||
_downButton.contents = (id) downButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_downButton];
|
||||
// Resize and reposition the button
|
||||
_downButton.anchorPoint = CGPointMake(0.5, 0 - newDPadDistFactor);
|
||||
_downButton.bounds = CGRectMake(0, 0, newShortSideLength, newLongSideLength);
|
||||
_downButton.position = CGPointMake(sharedCenter.x, sharedCenter.y);
|
||||
|
||||
// left button
|
||||
// UIImage* leftButtonImage = [UIImage imageNamed:@"LeftButton"];
|
||||
UIImage* leftButtonHdImage = [UIImage imageNamed:@"LeftButtonHD"];
|
||||
_leftButton.contents = (id) leftButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_leftButton];
|
||||
_leftButton.anchorPoint = CGPointMake(1 + newDPadDistFactor, 0.5);
|
||||
_leftButton.bounds = CGRectMake(0, 0, newLongSideLength, newShortSideLength);
|
||||
_leftButton.position = CGPointMake(sharedCenter.x, sharedCenter.y);
|
||||
|
||||
// right button
|
||||
// UIImage* rightButtonImage = [UIImage imageNamed:@"RightButton"];
|
||||
UIImage* rightButtonHdImage = [UIImage imageNamed:@"RightButtonHD"];
|
||||
_rightButton.contents = (id) rightButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_rightButton];
|
||||
_rightButton.anchorPoint = CGPointMake(0 - newDPadDistFactor, 0.5);
|
||||
_rightButton.bounds = CGRectMake(0, 0, newLongSideLength, newShortSideLength);
|
||||
_rightButton.position = CGPointMake(sharedCenter.x, sharedCenter.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets D-Pad position for class const var
|
||||
*/
|
||||
- (void) setDPadCenter {
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
- (void) setDPadCenter:(OSCProfile *)profile {
|
||||
for (NSData *buttonStateEncoded in profile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if ([buttonState.name isEqualToString:@"dPad"]) {
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
@@ -752,10 +812,8 @@ static float L3_Y;
|
||||
/**
|
||||
* Sets analog stick positions for class const var
|
||||
*/
|
||||
- (void) setAnalogStickPositions {
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; // returns the currently selected OSCProfile
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
- (void) setAnalogStickPositions:(OSCProfile *)profile {
|
||||
for (NSData *buttonStateEncoded in profile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
if ([buttonState.name isEqualToString:@"leftStickBackground"]) {
|
||||
@@ -775,16 +833,10 @@ static float L3_Y;
|
||||
|
||||
# define LR2_Y_UP_OFFSET 8
|
||||
# define LR1_Y_DOWN_OFFSET 3.5
|
||||
- (void) positionAndResizeSingleControllerLayers {
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile];
|
||||
bool defaultProfileSelected = [profilesManager getIndexOfSelectedProfile] == 0;
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
|
||||
- (void) positionAndResizeSingleControllerLayers:(OSCProfile *)profile {
|
||||
for (NSData *buttonStateEncoded in profile.buttonStatesEncoded) {
|
||||
OnScreenButtonState *buttonStateDecoded = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
|
||||
|
||||
for (CALayer *buttonLayer in self.OSCButtonLayers) { // iterate through each button layer on screen and position and hide/unhide each according to the instructions of its associated 'buttonState'
|
||||
for (CALayer *buttonLayer in self.OSCButtonLayerPool) { // iterate through each button layer on screen and position and hide/unhide each according to the instructions of its associated 'buttonState'
|
||||
if ([buttonLayer.name isEqualToString:buttonStateDecoded.name]) {
|
||||
if ([buttonLayer.name isEqualToString:@"upButton"] == NO &&
|
||||
[buttonLayer.name isEqualToString:@"rightButton"] == NO &&
|
||||
@@ -795,10 +847,15 @@ static float L3_Y;
|
||||
buttonStateDecoded.position = [self denormalizeWidgetPosition:buttonStateDecoded.position];
|
||||
buttonLayer.position = buttonStateDecoded.position;
|
||||
}
|
||||
buttonLayer.hidden = buttonStateDecoded.isHidden;
|
||||
|
||||
buttonLayer.hidden = buttonStateDecoded.isHidden;
|
||||
if(buttonLayer.hidden){
|
||||
[buttonLayer removeFromSuperlayer];
|
||||
continue;
|
||||
}
|
||||
|
||||
// adjust default layout for largerStickLR1
|
||||
if(defaultProfileSelected && _largerStickLR1){
|
||||
if(_largerStickLR1){
|
||||
if([buttonLayer.name isEqualToString:@"l2Button"] || [buttonLayer.name isEqualToString:@"r2Button"]) buttonLayer.position = CGPointMake(buttonLayer.position.x, buttonLayer.position.y - LR2_Y_UP_OFFSET);
|
||||
if([buttonLayer.name isEqualToString:@"l1Button"] || [buttonLayer.name isEqualToString:@"r1Button"]) buttonLayer.position = CGPointMake(buttonLayer.position.x, buttonLayer.position.y + LR1_Y_DOWN_OFFSET);
|
||||
}
|
||||
@@ -823,7 +880,7 @@ static float L3_Y;
|
||||
}
|
||||
|
||||
- (void) setOpacityForStandardControllerLayers {
|
||||
for(CALayer* oscLayer in self.OSCButtonLayers){
|
||||
for(CALayer* oscLayer in self.OSCButtonLayerPool){
|
||||
if(oscLayer == self._leftStick ||
|
||||
oscLayer == self._rightStick){
|
||||
oscLayer.opacity = DEFAULT_STICK_OPACITY;
|
||||
@@ -836,15 +893,10 @@ static float L3_Y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void) setOpacityForCutsomControllerLayers {
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile];
|
||||
// bool defaultProfileSelected = [profilesManager getIndexOfSelectedProfile] == 0;
|
||||
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
- (void) setOpacityForCutsomControllerLayers:(OSCProfile *)profile {
|
||||
for (NSData *buttonStateEncoded in profile.buttonStatesEncoded) {
|
||||
OnScreenButtonState* buttonStateDecoded = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
|
||||
for (CALayer *buttonLayer in self.OSCButtonLayers) { // iterate through each button layer on screen
|
||||
for (CALayer *buttonLayer in self.OSCButtonLayerPool) { // iterate through each button layer on screen
|
||||
// Here we deal with resizing single layer controllers only
|
||||
if ([buttonLayer.name isEqualToString:buttonStateDecoded.name]) {
|
||||
if ([buttonLayer.name isEqualToString:@"l1Button"] ||
|
||||
@@ -875,22 +927,30 @@ static float L3_Y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)removeHiddenlayers{
|
||||
for (CALayer *buttonLayer in self.OSCButtonLayerPool) { // iterate through each button layer on screen
|
||||
if(buttonLayer.isHidden || buttonLayer.opacity == 0) [buttonLayer removeFromSuperlayer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) drawStartSelect {
|
||||
// create Start button
|
||||
UIImage* startButtonImage = [UIImage imageNamed:@"StartButton"];
|
||||
UIImage* startButtonHdImage = [UIImage imageNamed:@"StartButtonHD"];
|
||||
_startButton.frame = CGRectMake(START_X - startButtonImage.size.width / 2, START_Y - startButtonImage.size.height / 2, startButtonImage.size.width, startButtonImage.size.height);
|
||||
_startButton.contents = (id) startButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_startButton];
|
||||
if([OSCButtonLayerPool containsObject:_startButton]){
|
||||
UIImage* startButtonImage = [UIImage imageNamed:@"StartButton"];
|
||||
UIImage* startButtonHdImage = [UIImage imageNamed:@"StartButtonHD"];
|
||||
_startButton.frame = CGRectMake(START_X - startButtonImage.size.width / 2, START_Y - startButtonImage.size.height / 2, startButtonImage.size.width, startButtonImage.size.height);
|
||||
_startButton.contents = (id) startButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_startButton];
|
||||
}
|
||||
|
||||
// create Select button
|
||||
UIImage* selectButtonImage = [UIImage imageNamed:@"SelectButton"];
|
||||
UIImage* selectHdButtonImage = [UIImage imageNamed:@"SelectButtonHD"];
|
||||
_selectButton.frame = CGRectMake(SELECT_X - selectButtonImage.size.width / 2, SELECT_Y - selectButtonImage.size.height / 2, selectButtonImage.size.width, selectButtonImage.size.height);
|
||||
_selectButton.contents = (id) selectHdButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_selectButton];
|
||||
if([OSCButtonLayerPool containsObject:_selectButton]){
|
||||
UIImage* selectButtonImage = [UIImage imageNamed:@"SelectButton"];
|
||||
UIImage* selectHdButtonImage = [UIImage imageNamed:@"SelectButtonHD"];
|
||||
_selectButton.frame = CGRectMake(SELECT_X - selectButtonImage.size.width / 2, SELECT_Y - selectButtonImage.size.height / 2, selectButtonImage.size.width, selectButtonImage.size.height);
|
||||
_selectButton.contents = (id) selectHdButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_selectButton];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) drawBumpers {
|
||||
@@ -898,26 +958,31 @@ static float L3_Y;
|
||||
if(_largerStickLR1) L1_Y = R1_Y = L1_Y + LR1_Y_DOWN_OFFSET;
|
||||
|
||||
// create L1 button
|
||||
UIImage* l1ButtonImage = [UIImage imageNamed:@"L1"];
|
||||
UIImage* l1ButtonHdImage = [UIImage imageNamed:@"L1HD"];
|
||||
_l1Button.frame = CGRectMake(L1_X - l1ButtonImage.size.width / 2, L1_Y - l1ButtonImage.size.height / 2, l1ButtonImage.size.width, l1ButtonImage.size.height);
|
||||
_l1Button.contents = (id) l1ButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_l1Button];
|
||||
|
||||
if([OSCButtonLayerPool containsObject:_l1Button]){
|
||||
UIImage* l1ButtonImage = [UIImage imageNamed:@"L1"];
|
||||
UIImage* l1ButtonHdImage = [UIImage imageNamed:@"L1HD"];
|
||||
_l1Button.frame = CGRectMake(L1_X - l1ButtonImage.size.width / 2, L1_Y - l1ButtonImage.size.height / 2, l1ButtonImage.size.width, l1ButtonImage.size.height);
|
||||
_l1Button.contents = (id) l1ButtonHdImage.CGImage;
|
||||
if(_largerStickLR1){
|
||||
UIImage* l2ButtonImage = [UIImage imageNamed:@"L2"];
|
||||
_l1Button.bounds = CGRectMake(0, 0, l2ButtonImage.size.width, l2ButtonImage.size.height);
|
||||
}
|
||||
[_view.layer addSublayer:_l1Button];
|
||||
}
|
||||
|
||||
// create R1 button
|
||||
UIImage* r1ButtonImage = [UIImage imageNamed:@"R1"];
|
||||
UIImage* r1ButtonHdImage = [UIImage imageNamed:@"R1HD"];
|
||||
_r1Button.frame = CGRectMake(R1_X - r1ButtonImage.size.width / 2, R1_Y - r1ButtonImage.size.height / 2, r1ButtonImage.size.width, r1ButtonImage.size.height);
|
||||
_r1Button.contents = (id) r1ButtonHdImage.CGImage;
|
||||
|
||||
// make l1 r1 the same size as l2 r2
|
||||
if(_largerStickLR1){
|
||||
UIImage* l2ButtonImage = [UIImage imageNamed:@"L2"];
|
||||
UIImage* l2ButtonHdImage = [UIImage imageNamed:@"L2HD"];
|
||||
_l1Button.bounds = _r1Button.bounds = CGRectMake(0, 0, l2ButtonImage.size.width, l2ButtonImage.size.height);
|
||||
if([OSCButtonLayerPool containsObject:_r1Button]){
|
||||
UIImage* r1ButtonImage = [UIImage imageNamed:@"R1"];
|
||||
UIImage* r1ButtonHdImage = [UIImage imageNamed:@"R1HD"];
|
||||
_r1Button.frame = CGRectMake(R1_X - r1ButtonImage.size.width / 2, R1_Y - r1ButtonImage.size.height / 2, r1ButtonImage.size.width, r1ButtonImage.size.height);
|
||||
_r1Button.contents = (id) r1ButtonHdImage.CGImage;
|
||||
// make l1 r1 the same size as l2 r2
|
||||
if(_largerStickLR1){
|
||||
UIImage* l2ButtonImage = [UIImage imageNamed:@"L2"];
|
||||
_r1Button.bounds = CGRectMake(0, 0, l2ButtonImage.size.width, l2ButtonImage.size.height);
|
||||
}
|
||||
[_view.layer addSublayer:_r1Button];
|
||||
}
|
||||
|
||||
[_view.layer addSublayer:_r1Button];
|
||||
}
|
||||
|
||||
- (void) drawTriggers {
|
||||
@@ -925,73 +990,84 @@ static float L3_Y;
|
||||
if(_largerStickLR1) L2_Y = R2_Y = L2_Y - LR2_Y_UP_OFFSET;
|
||||
|
||||
// create L2 button
|
||||
UIImage* l2ButtonImage = [UIImage imageNamed:@"L2"];
|
||||
UIImage* l2ButtonHdImage = [UIImage imageNamed:@"L2HD"];
|
||||
_l2Button.frame = CGRectMake(L2_X - l2ButtonImage.size.width / 2, L2_Y - l2ButtonImage.size.height / 2, l2ButtonImage.size.width, l2ButtonImage.size.height);
|
||||
_l2Button.contents = (id) l2ButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_l2Button];
|
||||
if([OSCButtonLayerPool containsObject:_l2Button]){
|
||||
UIImage* l2ButtonImage = [UIImage imageNamed:@"L2"];
|
||||
UIImage* l2ButtonHdImage = [UIImage imageNamed:@"L2HD"];
|
||||
_l2Button.frame = CGRectMake(L2_X - l2ButtonImage.size.width / 2, L2_Y - l2ButtonImage.size.height / 2, l2ButtonImage.size.width, l2ButtonImage.size.height);
|
||||
_l2Button.contents = (id) l2ButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_l2Button];
|
||||
}
|
||||
|
||||
// create R2 button
|
||||
UIImage* r2ButtonImage = [UIImage imageNamed:@"R2"];
|
||||
UIImage* r2ButtonHdImage = [UIImage imageNamed:@"R2HD"];
|
||||
_r2Button.frame = CGRectMake(R2_X - r2ButtonImage.size.width / 2, R2_Y - r2ButtonImage.size.height / 2, r2ButtonImage.size.width, r2ButtonImage.size.height);
|
||||
_r2Button.contents = (id) r2ButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_r2Button];
|
||||
if([OSCButtonLayerPool containsObject:_r2Button]){
|
||||
UIImage* r2ButtonImage = [UIImage imageNamed:@"R2"];
|
||||
UIImage* r2ButtonHdImage = [UIImage imageNamed:@"R2HD"];
|
||||
_r2Button.frame = CGRectMake(R2_X - r2ButtonImage.size.width / 2, R2_Y - r2ButtonImage.size.height / 2, r2ButtonImage.size.width, r2ButtonImage.size.height);
|
||||
_r2Button.contents = (id) r2ButtonHdImage.CGImage;
|
||||
[_view.layer addSublayer:_r2Button];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) drawSticks {
|
||||
// create left analog stick
|
||||
UIImage* leftStickBgImage = [UIImage imageNamed:@"StickOuter"];
|
||||
UIImage* stickBgHdImage = [UIImage imageNamed:@"StickOuterHD"];
|
||||
bool hasLeftStick = [_activeCustomOscButtonPositionDict.allKeys containsObject:@"leftStickBackground"];
|
||||
bool hasRightStick = [_activeCustomOscButtonPositionDict.allKeys containsObject:@"rightStickBackground"];
|
||||
|
||||
_leftStickBackground.frame = CGRectMake(LS_CENTER_X - leftStickBgImage.size.width / 2, LS_CENTER_Y - leftStickBgImage.size.height / 2, leftStickBgImage.size.width, leftStickBgImage.size.height);
|
||||
_leftStickBackground.contents = (id) stickBgHdImage.CGImage;
|
||||
[_view.layer addSublayer:_leftStickBackground];
|
||||
if(!hasLeftStick && !hasRightStick) return;
|
||||
|
||||
UIImage* leftStickImage = [UIImage imageNamed:@"StickInner"];
|
||||
UIImage* stickBgImage = [UIImage imageNamed:@"StickOuter"];
|
||||
UIImage* stickBgHdImage = [UIImage imageNamed:@"StickOuterHD"];
|
||||
UIImage* stickImage = [UIImage imageNamed:@"StickInner"];
|
||||
UIImage* stickHdImage = [UIImage imageNamed:@"StickInnerHD"];
|
||||
_leftStick.frame = CGRectMake(LS_CENTER_X - leftStickImage.size.width / 2, LS_CENTER_Y - leftStickImage.size.height / 2, leftStickImage.size.width, leftStickImage.size.height);
|
||||
_leftStick.contents = (id) stickHdImage.CGImage;
|
||||
_leftStick.opacity = _leftStickOpacity; // make stick half transparent when it's idle
|
||||
[_view.layer addSublayer:_leftStick];
|
||||
|
||||
if(hasLeftStick){
|
||||
_leftStickBackground.frame = CGRectMake(LS_CENTER_X - stickBgImage.size.width / 2, LS_CENTER_Y - stickBgImage.size.height / 2, stickBgImage.size.width, stickBgImage.size.height);
|
||||
_leftStickBackground.contents = (id) stickBgHdImage.CGImage;
|
||||
[_view.layer addSublayer:_leftStickBackground];
|
||||
|
||||
_leftStick.frame = CGRectMake(LS_CENTER_X - stickImage.size.width / 2, LS_CENTER_Y - stickImage.size.height / 2, stickImage.size.width, stickImage.size.height);
|
||||
_leftStick.contents = (id) stickHdImage.CGImage;
|
||||
_leftStick.opacity = _leftStickOpacity; // make stick half transparent when it's idle
|
||||
[_view.layer addSublayer:_leftStick];
|
||||
}
|
||||
|
||||
// create right analog stick
|
||||
UIImage* rightStickBgImage = [UIImage imageNamed:@"StickOuter"];
|
||||
_rightStickBackground.frame = CGRectMake(RS_CENTER_X - rightStickBgImage.size.width / 2, RS_CENTER_Y - rightStickBgImage.size.height / 2, rightStickBgImage.size.width, rightStickBgImage.size.height);
|
||||
_rightStickBackground.contents = (id) stickBgHdImage.CGImage;
|
||||
[_view.layer addSublayer:_rightStickBackground];
|
||||
|
||||
|
||||
|
||||
UIImage* rightStickImage = [UIImage imageNamed:@"StickInner"];
|
||||
_rightStick.frame = CGRectMake(RS_CENTER_X - rightStickImage.size.width / 2, RS_CENTER_Y - rightStickImage.size.height / 2, rightStickImage.size.width, rightStickImage.size.height);
|
||||
|
||||
_rightStick.contents = (id) stickHdImage.CGImage;
|
||||
_rightStick.opacity = _rightStickOpacity; // make stick half transparent when it's idle
|
||||
[_view.layer addSublayer:_rightStick];
|
||||
if(hasRightStick){
|
||||
_rightStickBackground.frame = CGRectMake(RS_CENTER_X - stickBgImage.size.width / 2, RS_CENTER_Y - stickBgImage.size.height / 2, stickBgImage.size.width, stickBgImage.size.height);
|
||||
_rightStickBackground.contents = (id) stickBgHdImage.CGImage;
|
||||
[_view.layer addSublayer:_rightStickBackground];
|
||||
|
||||
|
||||
|
||||
_rightStick.frame = CGRectMake(RS_CENTER_X - stickImage.size.width / 2, RS_CENTER_Y - stickImage.size.height / 2, stickImage.size.width, stickImage.size.height);
|
||||
|
||||
_rightStick.contents = (id) stickHdImage.CGImage;
|
||||
_rightStick.opacity = _rightStickOpacity; // make stick half transparent when it's idle
|
||||
[_view.layer addSublayer:_rightStick];
|
||||
}
|
||||
|
||||
// make stick larger
|
||||
if(_largerStickLR1){
|
||||
// sticks are resized here by the sizefactor persisted in the osc profile.
|
||||
RIGHT_STICK_INNER_SIZE = rightStickImage.size.width *1.33 * _rightStickSizeFactor;
|
||||
RIGHT_STICK_OUTER_SIZE = rightStickBgImage.size.width *1.10 * _rightStickSizeFactor;
|
||||
RIGHT_STICK_INNER_SIZE = stickImage.size.width *1.33 * _rightStickSizeFactor;
|
||||
RIGHT_STICK_OUTER_SIZE = stickBgImage.size.width *1.10 * _rightStickSizeFactor;
|
||||
_rightStick.bounds = CGRectMake(0, 0, RIGHT_STICK_INNER_SIZE, RIGHT_STICK_INNER_SIZE);
|
||||
_rightStickBackground.bounds = CGRectMake(0, 0, RIGHT_STICK_OUTER_SIZE, RIGHT_STICK_OUTER_SIZE);
|
||||
|
||||
LEFT_STICK_INNER_SIZE = rightStickImage.size.width *1.33 * _leftStickSizeFactor;
|
||||
LEFT_STICK_OUTER_SIZE = rightStickBgImage.size.width *1.10 * _leftStickSizeFactor;
|
||||
LEFT_STICK_INNER_SIZE = stickImage.size.width *1.33 * _leftStickSizeFactor;
|
||||
LEFT_STICK_OUTER_SIZE = stickBgImage.size.width *1.10 * _leftStickSizeFactor;
|
||||
_leftStick.bounds = CGRectMake(0, 0, LEFT_STICK_INNER_SIZE, LEFT_STICK_INNER_SIZE);
|
||||
_leftStickBackground.bounds = CGRectMake(0, 0, LEFT_STICK_OUTER_SIZE, LEFT_STICK_OUTER_SIZE);
|
||||
}
|
||||
else{
|
||||
// sticks are resized here by the sizefactor persisted in the osc profile.
|
||||
RIGHT_STICK_INNER_SIZE = rightStickImage.size.width * _rightStickSizeFactor;
|
||||
RIGHT_STICK_OUTER_SIZE = rightStickBgImage.size.width * _rightStickSizeFactor;
|
||||
RIGHT_STICK_INNER_SIZE = stickImage.size.width * _rightStickSizeFactor;
|
||||
RIGHT_STICK_OUTER_SIZE = stickBgImage.size.width * _rightStickSizeFactor;
|
||||
_rightStick.bounds = CGRectMake(0, 0, RIGHT_STICK_INNER_SIZE, RIGHT_STICK_INNER_SIZE);
|
||||
_rightStickBackground.bounds = CGRectMake(0, 0, RIGHT_STICK_OUTER_SIZE, RIGHT_STICK_OUTER_SIZE);
|
||||
|
||||
LEFT_STICK_INNER_SIZE = rightStickImage.size.width * _leftStickSizeFactor;
|
||||
LEFT_STICK_OUTER_SIZE = rightStickBgImage.size.width * _leftStickSizeFactor;
|
||||
LEFT_STICK_INNER_SIZE = stickImage.size.width * _leftStickSizeFactor;
|
||||
LEFT_STICK_OUTER_SIZE = stickBgImage.size.width * _leftStickSizeFactor;
|
||||
_leftStick.bounds = CGRectMake(0, 0, LEFT_STICK_INNER_SIZE, LEFT_STICK_INNER_SIZE);
|
||||
_leftStickBackground.bounds = CGRectMake(0, 0, LEFT_STICK_OUTER_SIZE, LEFT_STICK_OUTER_SIZE);
|
||||
}
|
||||
@@ -1992,5 +2068,12 @@ static float L3_Y;
|
||||
return dict;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
for(CALayer* layer in self.OSCButtonLayerPool){
|
||||
[layer removeFromSuperlayer];
|
||||
}
|
||||
[self.OSCButtonLayerPool removeAllObjects];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,12 +9,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc class PencilHandler: UIResponder {
|
||||
@objc static var sharedInstance: PencilHandler?
|
||||
@objc class PencilHandler: UIResponder, UIPencilInteractionDelegate {
|
||||
@objc static var shared: PencilHandler?
|
||||
|
||||
/// 用来处理所有 touch 事件的宿主视图
|
||||
weak var streamView: UIView?
|
||||
// private var tickTimer: SafeTimer
|
||||
private var pencilInteraction: Any?
|
||||
private var streamAspectRatio: Float
|
||||
private var tickInterval: TimeInterval
|
||||
private var manualTick: Bool
|
||||
@@ -22,6 +23,12 @@ import UIKit
|
||||
private var pressureCurveEnabled: Bool = false
|
||||
private var manualHoverFlag: Bool = false
|
||||
private var autoHoverFlag: Bool = false
|
||||
private(set) var pencilProEnabled: Bool = false
|
||||
@objc static private(set) var isDrawing: Bool = false
|
||||
@objc static private(set) var pencilPausesNativeTouch: Bool = false
|
||||
|
||||
// static private let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
static private var selectedProfile:OSCProfile?
|
||||
|
||||
private var pressureLUT = PressureCurveLUT(curve: PressureCurve())
|
||||
|
||||
@@ -33,22 +40,36 @@ import UIKit
|
||||
pencilTickEnabled = settings.pencilTickMode.intValue != PencilTickMode.PencilTickDisabled.rawValue
|
||||
super.init()
|
||||
setupPressureLUT()
|
||||
PencilHandler.sharedInstance = self
|
||||
PencilHandler.shared = self
|
||||
}
|
||||
|
||||
public func setupPressureLUT(){
|
||||
let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
let oscProfile = oscProfileMan.getSelectedProfile()
|
||||
pressureCurveEnabled = oscProfile.pressureCurveEnabled
|
||||
PencilHandler.selectedProfile = oscProfileMan.getSelectedProfile()
|
||||
guard let selectedProfile = PencilHandler.selectedProfile else {return}
|
||||
|
||||
if selectedProfile.eraserShortcut != "" {
|
||||
doubleTapShorcuts.append(selectedProfile.eraserShortcut)
|
||||
}
|
||||
if selectedProfile.brushShortcut != "" {
|
||||
doubleTapShorcuts.append(selectedProfile.brushShortcut)
|
||||
}
|
||||
|
||||
pressureCurveEnabled = selectedProfile.pressureCurveEnabled
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
IAPManager.checkPurchaseInfo(.PencilProPack) { info in
|
||||
self.pressureCurveEnabled = self.pressureCurveEnabled && info.valid
|
||||
self.pencilTickEnabled = self.pencilTickEnabled && info.valid
|
||||
self.pencilProEnabled = info.valid
|
||||
PencilHandler.pencilPausesNativeTouch = selectedProfile.pencilPausesNativeTouch && info.valid
|
||||
if info.valid {
|
||||
self.setupPencilInteraction(view: self.streamView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let persistedCurvePoints = PressureCurve.importCurvePoints(oscProfile.pressureCurvePoints)
|
||||
let persistedCurvePoints = PressureCurve.importCurvePoints(selectedProfile.pressureCurvePoints)
|
||||
let curve = PressureCurve()
|
||||
curve.polylinePoints = persistedCurvePoints
|
||||
curve.buildCurveSegments()
|
||||
@@ -61,6 +82,7 @@ import UIKit
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
guard let event = event else { return }
|
||||
PencilHandler.isDrawing = true
|
||||
for touch in touches {
|
||||
let coalesced = event.coalescedTouches(for: touch) ?? []
|
||||
_ = self.sendStylusEvent(touchBatch: pencilTickEnabled ? coalesced : [touch])
|
||||
@@ -189,13 +211,16 @@ import UIKit
|
||||
|
||||
DispatchQueue.global().asyncAfter(deadline: dispatchMoment + tickMoment + delay) {
|
||||
LiSendPenEvent(eventType, UInt8(LI_TOOL_TYPE_PEN), 0, Float(normalizedLocation.x), Float(normalizedLocation.y), targetForce, 0, 0, self.getRotation(fromAzimuthAngle: Float(azimuth)), self.getTilt(fromAltitudeAngle: Float(altitude)))
|
||||
if eventType == UInt8(LI_TOUCH_EVENT_UP) {
|
||||
PencilHandler.isDrawing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dispatchMoment + tickMoment + delay
|
||||
}
|
||||
|
||||
@objc public func switchPencilHoever(){
|
||||
@objc public func switchPencilHover(){
|
||||
manualHoverFlag = !manualHoverFlag
|
||||
}
|
||||
|
||||
@@ -206,7 +231,146 @@ import UIKit
|
||||
@objc public func disablePencilHover(){
|
||||
manualHoverFlag = false
|
||||
}
|
||||
|
||||
@available(iOS 12.1, *)
|
||||
private func setupPencilInteraction(view:UIView?) {
|
||||
let interaction = UIPencilInteraction()
|
||||
guard let view else { return }
|
||||
interaction.delegate = self
|
||||
view.addInteraction(interaction)
|
||||
pencilInteraction = interaction
|
||||
}
|
||||
|
||||
@available(iOS 12.1, *)
|
||||
func pencilInteractionDidTap(_ interaction: UIPencilInteraction) {
|
||||
if !pencilProEnabled {return}
|
||||
if doubleTapShorcuts.isEmpty {return}
|
||||
let keyStrings = CommandManager.shared.extractKeyStrings(from: doubleTapShorcuts[shorcutIndex])
|
||||
CommandManager.shared.sendKeyComboCommand(keyboardCmdStrings: keyStrings, delay: 0.1)
|
||||
shorcutIndex = (shorcutIndex + 1) % doubleTapShorcuts.count
|
||||
}
|
||||
|
||||
@available(iOS 17.5, *)
|
||||
func pencilInteraction(
|
||||
_ interaction: UIPencilInteraction,
|
||||
didReceiveSqueeze squeeze: UIPencilInteraction.Squeeze
|
||||
) {
|
||||
switch squeeze.phase {
|
||||
case .began:
|
||||
break
|
||||
case .ended, .cancelled:
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@objc static private(set) var eraserShortcut:String = ""
|
||||
@objc static private(set) var brushShortcut:String = ""
|
||||
private var doubleTapShorcuts: Array<String> = []
|
||||
private var shorcutIndex: Int = 0
|
||||
|
||||
@objc func replaceBrush(with shortcut:String){
|
||||
if let i = doubleTapShorcuts.indices.last {
|
||||
doubleTapShorcuts[i] = shortcut
|
||||
}
|
||||
}
|
||||
|
||||
@objc func replaceEraser(with shortcut:String){
|
||||
if let i = doubleTapShorcuts.indices.first {
|
||||
doubleTapShorcuts[i] = shortcut
|
||||
}
|
||||
}
|
||||
|
||||
@objc static public func enterDoubleTapShortcuts(in viewController: UIViewController){
|
||||
let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
selectedProfile = oscProfileMan.getSelectedProfile()
|
||||
guard let selectedProfile = selectedProfile else {return}
|
||||
|
||||
let alert = UIAlertController(title: SwiftLocalizationHelper.localizedString(forKey: "Eraser Shortcut"),
|
||||
message: SwiftLocalizationHelper.localizedString(forKey: "Enter eraser keyboard shortcut:"),
|
||||
preferredStyle: .alert)
|
||||
|
||||
alert.addTextField { textField in
|
||||
textField.placeholder = SwiftLocalizationHelper.localizedString(forKey:"Example: e, ctrl+e, alt+e ...")
|
||||
textField.keyboardType = .asciiCapable
|
||||
textField.autocorrectionType = .no
|
||||
textField.spellCheckingType = .no
|
||||
textField.text = selectedProfile.eraserShortcut
|
||||
}
|
||||
|
||||
let okAction = UIAlertAction(title: SwiftLocalizationHelper.localizedString(forKey: "OK"), style: .default) { _ in
|
||||
let keyboardShortcut = alert.textFields?[0].text ?? ""
|
||||
let keyStrings = CommandManager.shared.extractKeyStrings(from: keyboardShortcut)
|
||||
if keyStrings?.count ?? 0 > 0 || keyboardShortcut == "" {
|
||||
eraserShortcut = keyboardShortcut
|
||||
}
|
||||
enterBrushShortcut(in: viewController)
|
||||
}
|
||||
|
||||
let learnMoreAction = UIAlertAction(title: SwiftLocalizationHelper.localizedString(forKey: "Learn More"), style: .default) { _ in
|
||||
if let url = URL(string: SwiftLocalizationHelper.localizedString(forKey: "pencilKeyboardCmdURL")) {
|
||||
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
||||
}
|
||||
}
|
||||
|
||||
alert.addAction(learnMoreAction)
|
||||
alert.addAction(okAction)
|
||||
|
||||
viewController.present(alert, animated: true, completion: {
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@objc static public func enterBrushShortcut(in viewController: UIViewController){
|
||||
|
||||
let alert = UIAlertController(title: SwiftLocalizationHelper.localizedString(forKey: "Brush Shortcut"),
|
||||
message: SwiftLocalizationHelper.localizedString(forKey: "Enter brush keyboard shortcut:"),
|
||||
preferredStyle: .alert)
|
||||
|
||||
alert.addTextField { textField in
|
||||
textField.placeholder = SwiftLocalizationHelper.localizedString(forKey:"Example: b, ctrl+b, alt+b ...")
|
||||
textField.keyboardType = .asciiCapable
|
||||
textField.autocorrectionType = .no
|
||||
textField.spellCheckingType = .no
|
||||
textField.text = selectedProfile?.brushShortcut
|
||||
}
|
||||
|
||||
let okAction = UIAlertAction(title: SwiftLocalizationHelper.localizedString(forKey: "OK"), style: .default) { _ in
|
||||
let keyboardShortcut = alert.textFields?[0].text ?? ""
|
||||
let keyStrings = CommandManager.shared.extractKeyStrings(from: keyboardShortcut)
|
||||
if keyStrings?.count ?? 0 > 0 || keyboardShortcut == "" {
|
||||
brushShortcut = keyboardShortcut
|
||||
}
|
||||
|
||||
if selectedProfile?.brushShortcut != brushShortcut
|
||||
|| selectedProfile?.eraserShortcut != eraserShortcut {
|
||||
guard let selectedProfile = selectedProfile else {return}
|
||||
let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
selectedProfile.brushShortcut = brushShortcut
|
||||
selectedProfile.eraserShortcut = eraserShortcut
|
||||
oscProfileMan.replaceSelectedProfile(with: selectedProfile, overwriteDefault: true)
|
||||
}
|
||||
}
|
||||
|
||||
let learnMoreAction = UIAlertAction(title: SwiftLocalizationHelper.localizedString(forKey: "Learn More"), style: .default) { _ in
|
||||
if let url = URL(string: SwiftLocalizationHelper.localizedString(forKey: "pencilKeyboardCmdURL")) {
|
||||
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
||||
}
|
||||
}
|
||||
|
||||
alert.addAction(learnMoreAction)
|
||||
alert.addAction(okAction)
|
||||
|
||||
viewController.present(alert, animated: true, completion: {
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private func attachHoverLeave(normalizedLocation:CGPoint){
|
||||
DispatchQueue.global().asyncAfter(deadline: .now()+0.0086) {
|
||||
LiSendPenEvent(UInt8(LI_TOUCH_EVENT_HOVER), UInt8(LI_TOOL_TYPE_PEN), 0, Float(normalizedLocation.x), Float(normalizedLocation.y), 0, 0, 0, 0, 0)
|
||||
|
||||
@@ -675,7 +675,7 @@ class PressureCurveViewController: UIViewController {
|
||||
buttonTitle: SwiftLocalizationHelper.localizedString(forKey: "OK"),
|
||||
countdown: 0,
|
||||
completion: {
|
||||
PencilHandler.sharedInstance?.setupPressureLUT()
|
||||
PencilHandler.shared?.setupPressureLUT()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#include <Limelight.h>
|
||||
|
||||
@implementation PureNativeTouchHandler {
|
||||
StreamView* streamView;
|
||||
TemporarySettings* currentSettings;
|
||||
__weak StreamView* streamView;
|
||||
bool activateCoordSelector;
|
||||
CGFloat pointerVelocityDividerLocationByPoints;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2;
|
||||
UIGestureRecognizer* remoteLongPressRecognizer;
|
||||
#endif
|
||||
|
||||
StreamView* streamView;
|
||||
__weak StreamView* streamView;
|
||||
}
|
||||
|
||||
- (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings {
|
||||
|
||||
@@ -34,13 +34,14 @@
|
||||
@interface StreamView : UIView <X1KitMouseDelegate, UITextFieldDelegate, UIPointerInteractionDelegate>
|
||||
#endif
|
||||
|
||||
@property (assign, nonatomic) UIView* streamFrameTopLayerView;
|
||||
@property (weak, nonatomic) UIView* streamFrameTopLayerView;
|
||||
@property (weak, nonatomic) id<UserInteractionDelegate> interactionDelegate;
|
||||
@property (assign, nonatomic) CGFloat streamAspectRatio;
|
||||
@property (assign, nonatomic) CGRect originalFrame;
|
||||
@property (assign, nonatomic) bool widgetToolOpened;
|
||||
@property (strong, nonatomic) OnScreenControls* onScreenControls;
|
||||
@property (weak, nonatomic) PencilHandler* pencilHandler;
|
||||
|
||||
@property (weak, nonatomic) UIViewController* streamFrameVC;
|
||||
|
||||
- (void) setupStreamView:(ControllerSupport*)controllerSupport
|
||||
interactionDelegate:(id<UserInteractionDelegate>)interactionDelegate
|
||||
@@ -48,7 +49,9 @@
|
||||
streamFrameTopLayerView:(UIView* )topLayerView
|
||||
;
|
||||
|
||||
- (void) showOnScreenControls;
|
||||
- (void)cleanUp;
|
||||
|
||||
- (void) reloadLegacyWidgets:(OSCProfile* )profile;
|
||||
- (void) setOnScreenControls;
|
||||
- (void) disableOnScreenControls;
|
||||
- (void) reloadOnScreenControlsRealtimeWith:(ControllerSupport*)controllerSupport
|
||||
@@ -56,7 +59,7 @@
|
||||
- (void) reloadOnScreenControlsWith:(ControllerSupport*)controllerSupport
|
||||
andConfig:(StreamConfiguration*)streamConfig;
|
||||
- (void) clearOnScreenWidgets;
|
||||
- (void) reloadOnScreenWidgetViews;
|
||||
- (void) reloadOnScreenWidgetViews:(bool)reload;
|
||||
- (void) saveRelocatedWidgetViews;
|
||||
- (bool) isOnScreenWidgetEnabled;
|
||||
|
||||
@@ -75,6 +78,7 @@
|
||||
|
||||
- (void)enablePencilHover;
|
||||
- (void)disablePencilHover;
|
||||
- (void)setAllowSingleTouchEnabled:(BOOL)enabled;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (void) updateCursorLocation:(CGPoint)location isMouse:(BOOL)isMouse;
|
||||
|
||||
+204
-153
@@ -26,6 +26,7 @@
|
||||
#import "KeyboardInputField.h"
|
||||
#import "CustomTapGestureRecognizer.h"
|
||||
#import "LocalizationHelper.h"
|
||||
#import "StreamFrameViewController.h"
|
||||
|
||||
|
||||
static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
@@ -34,8 +35,6 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
Stream Video has been moved out of this class to _renderView in StreamFrameViewController.
|
||||
*/
|
||||
@implementation StreamView {
|
||||
UIView* streamFrameTopLayerView;
|
||||
UIViewController* streamFrameVC;
|
||||
|
||||
TemporarySettings* settings;
|
||||
|
||||
@@ -59,7 +58,6 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
UIResponder* touchHandler;
|
||||
|
||||
id<UserInteractionDelegate> interactionDelegate;
|
||||
NSTimer* interactionTimer;
|
||||
BOOL hasUserInteracted;
|
||||
|
||||
@@ -85,9 +83,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
streamFrameTopLayerView:(UIView* )topLayerView{
|
||||
self->comboKeyModifierFlags = (UIKeyModifierControl|UIKeyModifierAlternate|UIKeyModifierShift);
|
||||
|
||||
self->streamFrameTopLayerView = topLayerView;
|
||||
self.streamFrameTopLayerView = topLayerView; // this will be used as a read-only pointer for other class
|
||||
self->interactionDelegate = interactionDelegate;
|
||||
self->_streamFrameTopLayerView = topLayerView;
|
||||
self->_interactionDelegate = interactionDelegate;
|
||||
self->streamAspectRatio = (float)streamConfig.width / (float)streamConfig.height;
|
||||
self.streamAspectRatio = self->streamAspectRatio;
|
||||
_widgetSizeTransition = keepWidgetSize;
|
||||
@@ -128,8 +125,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
self->touchHandler = [[RelativeTouchHandler alloc] initWithView:self];
|
||||
#else
|
||||
|
||||
PencilHandler.sharedInstance = [[PencilHandler alloc] initWithStreamView:self settings:settings];
|
||||
_pencilHandler = PencilHandler.sharedInstance;
|
||||
PencilHandler.shared = [[PencilHandler alloc] initWithStreamView:self settings:settings];
|
||||
_pencilHandler = PencilHandler.shared;
|
||||
|
||||
// iOS uses touch Mode depending on user preference
|
||||
|
||||
@@ -160,8 +157,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
}
|
||||
|
||||
// we'll render on-screen controls on the toplayer too:
|
||||
_onScreenControls = [[OnScreenControls alloc] initWithView:self->streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig]; // don't delete, this is mandatory
|
||||
OnScreenControls.shared = _onScreenControls;
|
||||
_onScreenControls = [[OnScreenControls alloc] initWithView:self->_streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig]; // don't delete, this is mandatory
|
||||
// OnScreenControls.shared = _onScreenControls;
|
||||
/*
|
||||
// here we pass the tap recognizer to the onscreencontrols obj
|
||||
if (settings.touchMode.intValue == RelativeTouch){
|
||||
@@ -400,7 +397,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
// Notify the delegate if this was a new user interaction
|
||||
if (!timerAlreadyRunning) {
|
||||
[interactionDelegate userInteractionBegan];
|
||||
[_interactionDelegate userInteractionBegan];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +405,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
if (!hasUserInteracted) {
|
||||
// User has finished touching the screen
|
||||
interactionTimer = nil;
|
||||
[interactionDelegate userInteractionEnded];
|
||||
[_interactionDelegate userInteractionEnded];
|
||||
}
|
||||
else {
|
||||
// User is still touching the screen. Restart the timer.
|
||||
@@ -416,9 +413,9 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) showOnScreenControls {
|
||||
- (void) reloadLegacyWidgets:(OSCProfile* )profile {
|
||||
#if !TARGET_OS_TV
|
||||
if(!_widgetToolOpened) [_onScreenControls show];
|
||||
if(!_widgetToolOpened) [_onScreenControls showLegacyWidgetsWith:profile];
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -446,16 +443,17 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
- (void) reloadOnScreenControlsRealtimeWith:(ControllerSupport*)controllerSupport
|
||||
andConfig:(StreamConfiguration*)streamConfig {
|
||||
[self reloadOnScreenControlsWith:controllerSupport andConfig:streamConfig];
|
||||
if([self isOscEnabled]) [self showOnScreenControls];
|
||||
else [self disableOnScreenControls];
|
||||
/*
|
||||
StreamFrameViewController* vc = (StreamFrameViewController* )_streamFrameVC;
|
||||
if([self isOscEnabled]) [self reloadLegacyWidgets:nil];
|
||||
else [self disableOnScreenControls];*/
|
||||
}
|
||||
|
||||
|
||||
- (void) reloadOnScreenControlsWith:(ControllerSupport*)controllerSupport
|
||||
andConfig:(StreamConfiguration*)streamConfig {
|
||||
|
||||
// we'll render on-screen controllers on the toplayer too.
|
||||
_onScreenControls = [[OnScreenControls alloc] initWithView:self->streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig];
|
||||
_onScreenControls = [[OnScreenControls alloc] initWithView:self->_streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig];
|
||||
/*
|
||||
// pass mouseRightClickTapRecognizer to onScreenControls obj here:
|
||||
if([self isOscEnabled]){
|
||||
@@ -469,7 +467,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
- (void) clearOnScreenWidgets{
|
||||
OnScreenWidgetView.isTweakingHighlight = false;
|
||||
for (UIView *subview in self->streamFrameTopLayerView.subviews) {
|
||||
for (UIView *subview in self->_streamFrameTopLayerView.subviews) {
|
||||
// 检查子视图是否是特定类型的实例
|
||||
if ([subview isKindOfClass:[OnScreenWidgetView class]]) {
|
||||
// 如果是,就添加到将要被移除的数组中
|
||||
@@ -480,8 +478,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
- (CGPoint)denormalizeWidgetPosition:(CGPoint)position {
|
||||
if(position.x < 1.0 && position.y < 1.0){
|
||||
position.x = position.x * streamFrameTopLayerView.bounds.size.width;
|
||||
position.y = position.y * streamFrameTopLayerView.bounds.size.height;
|
||||
position.x = position.x * _streamFrameTopLayerView.bounds.size.width;
|
||||
position.y = position.y * _streamFrameTopLayerView.bounds.size.height;
|
||||
}
|
||||
else{
|
||||
NSLog(@"invalid coords");
|
||||
@@ -498,23 +496,25 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
- (void)saveRelocatedWidgetViews{
|
||||
NSMutableDictionary* relocatedWidgetDict = [NSMutableDictionary dictionary];
|
||||
|
||||
for(UIView* subview in self->streamFrameTopLayerView.subviews){
|
||||
for(UIView* subview in self->_streamFrameTopLayerView.subviews){
|
||||
if([subview isKindOfClass:[OnScreenWidgetView class]]){
|
||||
OnScreenWidgetView* widget = (OnScreenWidgetView* )subview;
|
||||
if(widget.relocatedDuringStreaming) [relocatedWidgetDict setObject:widget forKey:widget.identifier];
|
||||
if(widget.relocatedDuringStreaming) [relocatedWidgetDict setObject:widget forKey:@(widget.sequence)];
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"relocatedWidgetDict.allKeys.count %d, %f", relocatedWidgetDict.allKeys.count, CACurrentMediaTime());
|
||||
|
||||
if(relocatedWidgetDict.allKeys.count == 0) return;
|
||||
|
||||
oscProfileMan = [OSCProfilesManager sharedManager:self->streamFrameTopLayerView.bounds];
|
||||
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 = [oscProfileMan unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if([relocatedWidgetDict.allKeys containsObject:newButtonState.identifier]){
|
||||
OnScreenWidgetView* widget = [relocatedWidgetDict objectForKey:newButtonState.identifier];
|
||||
if([relocatedWidgetDict.allKeys containsObject:@(newButtonState.sequence)]){
|
||||
OnScreenWidgetView* widget = [relocatedWidgetDict objectForKey:@(newButtonState.sequence)];
|
||||
newButtonState.position = [oscProfileMan normalizeWidgetPosition:widget.center];
|
||||
NSData *newButtonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:newButtonState requiringSecureCoding:YES error:nil];
|
||||
newProfile.buttonStatesEncoded[i] = newButtonStateEncoded;
|
||||
@@ -524,105 +524,125 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
[oscProfileMan replaceSelectedProfileWith:newProfile overwriteDefault:NO];
|
||||
}
|
||||
|
||||
- (void) reloadOnScreenWidgetViews{
|
||||
|
||||
// NSLog(@"reload on screen keyboard buttons here");
|
||||
- (void) reloadOnScreenWidgetViews:(bool)reload{
|
||||
NSLog(@"reloadOnScreenWidgets in streamview %f", CACurrentMediaTime());
|
||||
|
||||
// remove all keyboard widget views first
|
||||
[self clearOnScreenWidgets];
|
||||
|
||||
// get streamFrameVC
|
||||
if(!streamFrameVC) streamFrameVC = [self parentViewController];
|
||||
|
||||
// bool customOscEnabled = [self isOscEnabled] && settings.onscreenControls.intValue == OnScreenControlsLevelCustom;
|
||||
|
||||
if(![self isOnScreenWidgetEnabled]) return;
|
||||
|
||||
OnScreenWidgetView.buttonVisualFeedbackEnabled = settings.buttonVisualFeedback;
|
||||
|
||||
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 = [oscProfileMan unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if(buttonState.widgetType == CustomOnScreenWidget){
|
||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:buttonState.name buttonLabel:buttonState.alias shape:buttonState.widgetShape]; //reconstruct widgetView
|
||||
//--------------------------------------------------
|
||||
_onScreenControls.instanceReceiverDelegate = widgetView; // connecting onScreenControls to OnScreenWidgetView, sending the active instance for touchPad stick control
|
||||
[_onScreenControls sendInstance];
|
||||
//--------------------------------------------------
|
||||
|
||||
widgetView.functionalButtonDelegate = (id<OnScreenFunctionalButtonDelegate>)streamFrameVC;
|
||||
widgetView.motionHandler = 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
|
||||
widgetView.widthFactor = buttonState.widthFactor;
|
||||
widgetView.heightFactor = buttonState.heightFactor;
|
||||
widgetView.componentSizeFactor = buttonState.componentSizeFactor;
|
||||
widgetView.borderWidth = buttonState.borderWidth;
|
||||
widgetView.highlightSizeFactor = buttonState.highlightSizeFactor;
|
||||
widgetView.autoTapInterval = buttonState.autoTapInterval;
|
||||
[widgetView setVibrationWithStyle:buttonState.vibrationStyle];
|
||||
widgetView.mouseButtonAction = buttonState.mouseButtonAction;
|
||||
widgetView.sensitivityFactorX = buttonState.sensitivityFactorX;
|
||||
widgetView.sensitivityFactorY = buttonState.sensitivityFactorY;
|
||||
widgetView.slideThreshold = buttonState.slideThreshold;
|
||||
widgetView.yawFactor = buttonState.yawFactor;
|
||||
widgetView.pitchFactor = buttonState.pitchFactor;
|
||||
widgetView.rollFactor = buttonState.rollFactor;
|
||||
widgetView.decelerationRateX = buttonState.decelerationRateX;
|
||||
widgetView.decelerationRateY = buttonState.decelerationRateY;
|
||||
widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset;
|
||||
widgetView.minStickOffset = buttonState.minStickOffset;
|
||||
widgetView.dWheelWalkModeThreshold = buttonState.walkModeThreshold;
|
||||
widgetView.buttonMode = buttonState.buttonMode;
|
||||
// Add the widgetView to the view controller's view
|
||||
[self->streamFrameTopLayerView addSubview:widgetView]; // add keyboard button to the stream frame view. must add it to the target view before setting location.
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
[widgetView setLocationWithPosition:buttonState.position];
|
||||
widgetView.sizeReference = [self getCurrentWidgetSizeReference];
|
||||
// widgetView.sizeReference = buttonState.sizeReference;
|
||||
//portrait markmarkmark
|
||||
[widgetView resizeWidgetView]; // resize must be called after relocation
|
||||
[widgetView adjustTransparencyWithAlpha:buttonState.backgroundAlpha tweakBorderAlpha:NO];
|
||||
[widgetView adjustBorderWithWidth:buttonState.borderWidth];
|
||||
[widgetView tweakLabelAlphaWithAlpha:buttonState.labelAlpha];
|
||||
[widgetView tweakBorderAlphaWithAlpha:buttonState.borderAlpha];
|
||||
[widgetView tweakHighlightAlphaWithAlpha:buttonState.highlightAlpha];
|
||||
[widgetView setupAutoTapTimer];
|
||||
[widgetView setupInertialScroller];
|
||||
}
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if(!self->oscProfileMan) self->oscProfileMan = [OSCProfilesManager sharedManager:self->_streamFrameTopLayerView.bounds];
|
||||
OSCProfile *oscProfile = [self->oscProfileMan getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
MotionHandler* motionHandler = [MotionHandler sharedWithProfile:oscProfile];
|
||||
|
||||
uint64_t buttonIndex = 9999999;
|
||||
UIView* deepestButton;
|
||||
for (UIView *subview in self->streamFrameTopLayerView.subviews) {
|
||||
if ([subview isKindOfClass:[OnScreenWidgetView class]]) {
|
||||
OnScreenWidgetView* widget = (OnScreenWidgetView* ) subview;
|
||||
if(widget.widgetType == WidgetTypeEnumButton){
|
||||
uint64_t index = [self->streamFrameTopLayerView.subviews indexOfObject:subview];
|
||||
if (index<buttonIndex){
|
||||
buttonIndex = index;
|
||||
deepestButton = subview;
|
||||
// get streamFrameVC
|
||||
if(!self->_streamFrameVC) self->_streamFrameVC = [self parentViewController];
|
||||
|
||||
// bool customOscEnabled = [self isOscEnabled] && settings.onscreenControls.intValue == OnScreenControlsLevelCustom;
|
||||
|
||||
if(![self isOnScreenWidgetEnabled]) return;
|
||||
|
||||
OnScreenWidgetView.buttonVisualFeedbackEnabled = self->settings.buttonVisualFeedback;
|
||||
|
||||
bool hasLegacyWidget = false;
|
||||
if(reload && !OnScreenWidgetView.editMode){
|
||||
// remove all keyboard widget views first
|
||||
[self clearOnScreenWidgets];
|
||||
|
||||
bool sequenceGenerated = false;
|
||||
bool hasMovableWidget = false;
|
||||
for (NSInteger i = 0; i < oscProfile.buttonStatesEncoded.count; i++) {
|
||||
NSData* buttonStateEncoded = oscProfile.buttonStatesEncoded[i];
|
||||
OnScreenButtonState* buttonState = [self->oscProfileMan unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if(buttonState.widgetType == CustomOnScreenWidget){
|
||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:buttonState.name buttonLabel:buttonState.alias shape:buttonState.widgetShape profile:oscProfile]; //reconstruct widgetView
|
||||
|
||||
widgetView.functionalButtonDelegate = (id<OnScreenFunctionalButtonDelegate>)self->_streamFrameVC;
|
||||
widgetView.motionHandler = motionHandler;
|
||||
|
||||
widgetView.sequence = buttonState.sequence == -1 ? i : buttonState.sequence;
|
||||
if(!sequenceGenerated) sequenceGenerated = buttonState.sequence == -1;
|
||||
if(!hasMovableWidget) hasMovableWidget = buttonState.buttonMode == movable;
|
||||
if(sequenceGenerated){
|
||||
NSLog(@"widgetView.sequence %d %f", widgetView.sequence, CACurrentMediaTime());
|
||||
buttonState.sequence = widgetView.sequence;
|
||||
oscProfile.buttonStatesEncoded[i] = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
|
||||
}
|
||||
|
||||
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||
widgetView.widthFactor = buttonState.widthFactor;
|
||||
widgetView.heightFactor = buttonState.heightFactor;
|
||||
widgetView.componentSizeFactor = buttonState.componentSizeFactor;
|
||||
widgetView.borderWidth = buttonState.borderWidth;
|
||||
widgetView.highlightSizeFactor = buttonState.highlightSizeFactor;
|
||||
widgetView.autoTapInterval = buttonState.autoTapInterval;
|
||||
[widgetView setVibrationWithStyle:buttonState.vibrationStyle];
|
||||
widgetView.mouseButtonAction = buttonState.mouseButtonAction;
|
||||
widgetView.sensitivityFactorX = buttonState.sensitivityFactorX;
|
||||
widgetView.sensitivityFactorY = buttonState.sensitivityFactorY;
|
||||
widgetView.slideThreshold = buttonState.slideThreshold;
|
||||
widgetView.yawFactor = buttonState.yawFactor;
|
||||
widgetView.pitchFactor = buttonState.pitchFactor;
|
||||
widgetView.rollFactor = buttonState.rollFactor;
|
||||
widgetView.decelerationRateX = buttonState.decelerationRateX;
|
||||
widgetView.decelerationRateY = buttonState.decelerationRateY;
|
||||
widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset;
|
||||
widgetView.minStickOffset = buttonState.minStickOffset;
|
||||
widgetView.dWheelWalkModeThreshold = buttonState.walkModeThreshold;
|
||||
widgetView.buttonMode = buttonState.buttonMode;
|
||||
// Add the widgetView to the view controller's view
|
||||
[self->_streamFrameTopLayerView addSubview:widgetView]; // add keyboard button to the stream frame view. must add it to the target view before setting location.
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
[widgetView setLocationWithPosition:buttonState.position];
|
||||
widgetView.sizeReference = [self getCurrentWidgetSizeReference];
|
||||
// widgetView.sizeReference = buttonState.sizeReference;
|
||||
//portrait markmarkmark
|
||||
[widgetView resizeWidgetView]; // resize must be called after relocation
|
||||
[widgetView adjustTransparencyWithAlpha:buttonState.backgroundAlpha tweakBorderAlpha:NO];
|
||||
[widgetView adjustBorderWithWidth:buttonState.borderWidth];
|
||||
[widgetView tweakLabelAlphaWithAlpha:buttonState.labelAlpha];
|
||||
[widgetView tweakBorderAlphaWithAlpha:buttonState.borderAlpha];
|
||||
[widgetView tweakHighlightAlphaWithAlpha:buttonState.highlightAlpha];
|
||||
[widgetView setupAutoTapTimer];
|
||||
[widgetView setupInertialScrollerWithFps:self->settings.framerate.intValue];
|
||||
}
|
||||
else if(buttonState.widgetType == LegacyOnScreenControls) hasLegacyWidget = true;
|
||||
}
|
||||
|
||||
if(sequenceGenerated && hasMovableWidget){
|
||||
[self->oscProfileMan replaceSelectedProfileWith:oscProfile overwriteDefault:YES];
|
||||
}
|
||||
|
||||
uint64_t buttonIndex = 9999999;
|
||||
UIView* deepestButton;
|
||||
for (UIView *subview in self->_streamFrameTopLayerView.subviews) {
|
||||
if ([subview isKindOfClass:[OnScreenWidgetView class]]) {
|
||||
OnScreenWidgetView* widget = (OnScreenWidgetView* ) subview;
|
||||
if(widget.widgetType == WidgetTypeEnumButton){
|
||||
uint64_t index = [self->_streamFrameTopLayerView.subviews indexOfObject:subview];
|
||||
if (index<buttonIndex){
|
||||
buttonIndex = index;
|
||||
deepestButton = subview;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(deepestButton){
|
||||
for (UIView *subview in self->_streamFrameTopLayerView.subviews) {
|
||||
if ([subview isKindOfClass:[OnScreenWidgetView class]]) {
|
||||
OnScreenWidgetView* widget = (OnScreenWidgetView* ) subview;
|
||||
if(widget.widgetType == WidgetTypeEnumTouchPad){
|
||||
[self->_streamFrameTopLayerView insertSubview:subview belowSubview:deepestButton];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!deepestButton) return;
|
||||
|
||||
for (UIView *subview in self->streamFrameTopLayerView.subviews) {
|
||||
if ([subview isKindOfClass:[OnScreenWidgetView class]]) {
|
||||
OnScreenWidgetView* widget = (OnScreenWidgetView* ) subview;
|
||||
if(widget.widgetType == WidgetTypeEnumTouchPad){
|
||||
[self->streamFrameTopLayerView insertSubview:subview belowSubview:deepestButton];
|
||||
}
|
||||
}
|
||||
NSLog(@"hasLegacyWidget %d %f", hasLegacyWidget, CACurrentMediaTime());
|
||||
// legacy widgets
|
||||
if(!OnScreenWidgetView.editMode && (hasLegacyWidget || !reload)){
|
||||
if([self isOscEnabled]) [self reloadLegacyWidgets:oscProfile];
|
||||
else [self disableOnScreenControls];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (OnScreenControlsLevel) getCurrentOscState {
|
||||
@@ -787,14 +807,18 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
[touchHandler touchesBegan:touches withEvent:event];
|
||||
return; //This is a native touch oriented fork, in pure native touch mode, this call back method deals with native touch only.
|
||||
}
|
||||
if (@available(iOS 13.4, *)) { // now only pencil events are restricted
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
[_pencilHandler touchesBegan:touches withEvent:event];
|
||||
return;
|
||||
}
|
||||
NSMutableSet* pencilTouches = [NSMutableSet set];
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
[pencilTouches addObject:touch];
|
||||
}
|
||||
}
|
||||
NSMutableSet* nonPencilTouches;
|
||||
if(pencilTouches.count>0){
|
||||
[_pencilHandler touchesBegan:pencilTouches withEvent:event];
|
||||
nonPencilTouches = [touches mutableCopy];
|
||||
[nonPencilTouches minusSet:pencilTouches];
|
||||
}
|
||||
|
||||
#endif
|
||||
if ([self handleMouseButtonEvent:BUTTON_ACTION_PRESS
|
||||
@@ -809,11 +833,12 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
// Notify of user interaction and start expiration timer
|
||||
[self startInteractionTimer];
|
||||
|
||||
NSSet* targetTouches = nonPencilTouches ? nonPencilTouches : touches;
|
||||
if(settings.touchMode.intValue == NativeTouch || settings.touchMode.intValue == RelativeTouch){
|
||||
[self->_onScreenControls handleTouchDownEvent:touches];
|
||||
[self->touchHandler touchesBegan:touches withEvent:event];
|
||||
[self->_onScreenControls handleTouchDownEvent:targetTouches];
|
||||
[self->touchHandler touchesBegan:targetTouches withEvent:event];
|
||||
}
|
||||
else if(![_onScreenControls handleTouchDownEvent:touches]) [touchHandler touchesBegan:touches withEvent:event];
|
||||
else if(![_onScreenControls handleTouchDownEvent:targetTouches]) [touchHandler touchesBegan:targetTouches withEvent:event];
|
||||
}
|
||||
|
||||
- (UIBarButtonItem *)createButtonWithImageNamed:(NSString *)imageName backgroundColor:(UIColor *)backgroundColor target:(id)target action:(SEL)action keyCode:(NSInteger)keyCode isToggleable:(BOOL)isToggleable {
|
||||
@@ -944,10 +969,10 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
return; //This is a native touch oriented fork, in pure native touch mode, this call back method deals with native touch only.
|
||||
}
|
||||
|
||||
NSMutableSet* pencilTouches = [NSMutableSet set];
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
[_pencilHandler touchesMoved:touches withEvent:event];
|
||||
return;
|
||||
[pencilTouches addObject:touch];
|
||||
}
|
||||
if (@available(iOS 13.4, *)) {
|
||||
UITouch *touch = [touches anyObject];
|
||||
@@ -970,14 +995,21 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NSMutableSet* nonPencilTouches;
|
||||
if(pencilTouches.count>0){
|
||||
[_pencilHandler touchesMoved:pencilTouches withEvent:event];
|
||||
nonPencilTouches = [touches mutableCopy];
|
||||
[nonPencilTouches minusSet:pencilTouches];
|
||||
}
|
||||
|
||||
hasUserInteracted = YES;
|
||||
|
||||
NSSet* targetTouches = nonPencilTouches ? nonPencilTouches : touches;
|
||||
if(self->settings.touchMode.intValue == NativeTouch || self->settings.touchMode.intValue == RelativeTouch){
|
||||
[self->touchHandler touchesMoved:touches withEvent:event];
|
||||
[self->_onScreenControls handleTouchMovedEvent:touches];
|
||||
[self->touchHandler touchesMoved:targetTouches withEvent:event];
|
||||
[self->_onScreenControls handleTouchMovedEvent:targetTouches];
|
||||
}
|
||||
else if(![self->_onScreenControls handleTouchMovedEvent:touches]) [self->touchHandler touchesMoved:touches withEvent:event];
|
||||
else if(![self->_onScreenControls handleTouchMovedEvent:targetTouches]) [self->touchHandler touchesMoved:targetTouches withEvent:event];
|
||||
}
|
||||
|
||||
- (void) handleKeyCombos:(UIPress*) press{
|
||||
@@ -986,19 +1018,19 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
}
|
||||
switch (press.key.keyCode) {
|
||||
case UIKeyboardHIDUsageKeyboardQ:
|
||||
[interactionDelegate streamExitRequested];
|
||||
[_interactionDelegate streamExitRequested];
|
||||
break;
|
||||
case UIKeyboardHIDUsageKeyboardS:
|
||||
[interactionDelegate toggleStatsOverlay];
|
||||
[_interactionDelegate toggleStatsOverlay];
|
||||
break;
|
||||
case UIKeyboardHIDUsageKeyboardM:
|
||||
[interactionDelegate toggleMouseCapture];
|
||||
[_interactionDelegate toggleMouseCapture];
|
||||
break;
|
||||
case UIKeyboardHIDUsageKeyboardC:
|
||||
[interactionDelegate toggleMouseVisible];
|
||||
[_interactionDelegate toggleMouseVisible];
|
||||
break;
|
||||
case UIKeyboardHIDUsageKeyboardD:
|
||||
[interactionDelegate disconnectAndQuitApp];
|
||||
[_interactionDelegate disconnectAndQuitApp];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -1052,18 +1084,22 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
return; //This is a native touch oriented fork, in pure native touch mode, this call back method deals with native touch only.
|
||||
}
|
||||
|
||||
if (@available(iOS 13.4, *)){ //now only pencil events are restricted
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
[_pencilHandler touchesEnded:touches withEvent:event];
|
||||
return;
|
||||
}
|
||||
NSMutableSet* pencilTouches = [NSMutableSet set];
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
[pencilTouches addObject:touch];
|
||||
}
|
||||
}
|
||||
NSMutableSet* nonPencilTouches;
|
||||
if(pencilTouches.count>0){
|
||||
[_pencilHandler touchesEnded:pencilTouches withEvent:event];
|
||||
nonPencilTouches = [touches mutableCopy];
|
||||
[nonPencilTouches minusSet:pencilTouches];
|
||||
}
|
||||
|
||||
#endif
|
||||
if ([self handleMouseButtonEvent:BUTTON_ACTION_RELEASE
|
||||
forTouches:touches
|
||||
forTouches:nonPencilTouches ? nonPencilTouches : touches
|
||||
withEvent:event]) {
|
||||
// If it's a mouse event, we're done
|
||||
return;
|
||||
@@ -1073,22 +1109,21 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
hasUserInteracted = YES;
|
||||
|
||||
NSSet* targetTouches = nonPencilTouches ? nonPencilTouches : touches;
|
||||
if(settings.touchMode.intValue == NativeTouch || settings.touchMode.intValue == RelativeTouch){
|
||||
[self->touchHandler touchesEnded:touches withEvent:event]; // when touches ended, must call the native touchhandler before onScreenControls, since the NSSet of touches captured by on screen button shall be updated later
|
||||
[self->_onScreenControls handleTouchUpEvent:touches];
|
||||
[self->touchHandler touchesEnded:targetTouches withEvent:event]; // when touches ended, must call the native touchhandler before onScreenControls, since the NSSet of touches captured by on screen button shall be updated later
|
||||
[self->_onScreenControls handleTouchUpEvent:targetTouches];
|
||||
}
|
||||
else if(![_onScreenControls handleTouchUpEvent:touches]) [touchHandler touchesEnded:touches withEvent:event];
|
||||
else if(![_onScreenControls handleTouchUpEvent:targetTouches]) [touchHandler touchesEnded:targetTouches withEvent:event];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
[touchHandler touchesCancelled:touches withEvent:event];
|
||||
#if !TARGET_OS_TV
|
||||
if (settings.touchMode.intValue == NativeTouchOnly) return; //This is a native touch oriented fork, in pure native touch mode, this call back method deals with native touch only.
|
||||
if (@available(iOS 13.4, *)){ //now only pencil events are restricted
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
[self touchesEnded:touches withEvent:event];
|
||||
}
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
[self touchesEnded:touches withEvent:event];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1434,7 +1469,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
}
|
||||
|
||||
- (void)switchPencilHover{
|
||||
[_pencilHandler switchPencilHoever];
|
||||
[_pencilHandler switchPencilHover];
|
||||
}
|
||||
|
||||
- (void)enablePencilHover{
|
||||
@@ -1445,6 +1480,22 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
[_pencilHandler disablePencilHover];
|
||||
}
|
||||
|
||||
- (void)setAllowSingleTouchEnabled:(BOOL)enabled{
|
||||
if([touchHandler isKindOfClass:[NativeTouchHandler class]]){
|
||||
NativeTouchHandler* handler = (NativeTouchHandler* )touchHandler;
|
||||
[handler setAllowSingleTouchEnabled:enabled];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cleanUp{
|
||||
[keyInputField resignFirstResponder];
|
||||
keyInputField.delegate = nil;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
NSLog(@"dealloc streamView %f", CACurrentMediaTime());
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (BOOL)isMultipleTouchEnabled {
|
||||
return YES;
|
||||
|
||||
@@ -11,7 +11,7 @@ import UIKit
|
||||
@objc class UITouchUtil: NSObject {
|
||||
@objc public static func touches(in view: UIView, from event: UIEvent?) -> Set<UITouch> {
|
||||
guard (event?.allTouches) != nil else { return [] }
|
||||
return event?.allTouches?.filter({ $0.view == view }) ?? []
|
||||
return event?.allTouches?.filter({ $0.view == view && $0.type == .direct}) ?? []
|
||||
}
|
||||
|
||||
@objc public static func distance(between touch1: UITouch?, and touch2: UITouch?, in view: UIView?) -> CGFloat {
|
||||
|
||||
@@ -1192,6 +1192,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Brush Shortcut" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "笔刷快捷键"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆刷快速鍵"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆刷快速鍵"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Calibrating..." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -2033,6 +2056,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"doubleTapShortcutStackTip" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Brush shortcut: %@\nEraser shortcut: %@"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "笔刷快捷键: %@\n橡皮擦快捷键: %@"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆刷快速鍵: %@\n橡皮擦快速鍵: %@"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆刷快速鍵: %@\n橡皮擦快速鍵: %@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Down" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -2281,6 +2333,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Enter brush keyboard shortcut:" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "输入笔刷快捷键:"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "输入筆刷快速鍵:"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "输入筆刷快速鍵:"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Enter Custom Resolution" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -2304,6 +2379,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Enter eraser keyboard shortcut:" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "输入橡皮擦快捷键:"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "輸入橡皮擦快速鍵:"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "輸入橡皮擦快速鍵:"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Enter IP address to add host manually" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -2402,6 +2500,75 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Eraser Shortcut" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "橡皮擦快捷键"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "橡皮擦快速鍵"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "橡皮擦快速鍵"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Example: b, ctrl+b, alt+b ..." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "例如: b, ctrl+b, alt+b …"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "例如: b, ctrl+b, alt+b …"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "例如: b, ctrl+b, alt+b …"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Example: e, ctrl+e, alt+e ..." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "例如: e, ctrl+e, alt+e …"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "例如: e, ctrl+e, alt+e …"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "例如: e, ctrl+e, alt+e …"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Exit" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -4822,6 +4989,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"pencilKeyboardCmdURL" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ma82z70rdt6u99b4#32535f99"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/nm6l9yixu0idsybg#32535f99"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/nm6l9yixu0idsybg#32535f99"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/nm6l9yixu0idsybg#32535f99"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"PencilProPackDescription" : {
|
||||
"extractionState" : "manual"
|
||||
},
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
- (void) stopStream
|
||||
{
|
||||
[_connection terminate];
|
||||
_callbacks = nil;
|
||||
}
|
||||
|
||||
- (BOOL) launchApp:(HttpManager*)hMan receiveSessionUrl:(NSString**)sessionUrl {
|
||||
|
||||
@@ -36,7 +36,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
||||
@implementation VideoDecoderRenderer {
|
||||
dispatch_queue_t _sq, _vtq;
|
||||
StreamView* _view;
|
||||
id<ConnectionCallbacks> _callbacks;
|
||||
__weak id<ConnectionCallbacks> _callbacks;
|
||||
float _streamAspectRatio;
|
||||
|
||||
AVSampleBufferDisplayLayer* _displayLayer;
|
||||
|
||||
@@ -11,19 +11,26 @@ import Foundation
|
||||
import SVGKit
|
||||
|
||||
@objc public class GraphicUtils: NSObject {
|
||||
@objc public class func makeCenteredSVGLayer(
|
||||
@objc public class func makeSVGLayer(
|
||||
from file: String,
|
||||
in container: CALayer,
|
||||
at normalizedPosition: CGPoint = .zero,
|
||||
targetSize: CGSize
|
||||
) -> CALayer {
|
||||
|
||||
guard let svg = SVGKImage(named: file) else {
|
||||
fatalError("Failed to load SVG \(file)")
|
||||
guard let url = Bundle.main.url(forResource: file, withExtension: "svg"),
|
||||
let data = try? Data(contentsOf: url) else {
|
||||
return CALayer()
|
||||
}
|
||||
|
||||
return _makeCenteredSVGLayer(
|
||||
guard let svg = SVGKImage(data: data) else {
|
||||
fatalError("Failed to load SVG \(file)")
|
||||
}
|
||||
|
||||
return _makeSVGLayer(
|
||||
from: svg,
|
||||
in: container,
|
||||
at: normalizedPosition,
|
||||
targetSize: targetSize
|
||||
)
|
||||
}
|
||||
@@ -33,16 +40,17 @@ import SVGKit
|
||||
in container: CALayer,
|
||||
targetSize: CGSize
|
||||
) -> CALayer {
|
||||
return _makeCenteredSVGLayer(
|
||||
return _makeSVGLayer(
|
||||
from: svg,
|
||||
in: container,
|
||||
targetSize: targetSize
|
||||
)
|
||||
}
|
||||
|
||||
@objc public class func _makeCenteredSVGLayer(
|
||||
@objc public class func _makeSVGLayer(
|
||||
from svg: SVGKImage,
|
||||
in container: CALayer,
|
||||
at normalizedPosition: CGPoint = .zero,
|
||||
targetSize: CGSize,
|
||||
getWrapperLayer: Bool = true
|
||||
) -> CALayer {
|
||||
@@ -64,14 +72,15 @@ import SVGKit
|
||||
y: -unionRect.origin.y
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
let realPosition = (normalizedPosition == .zero
|
||||
? CGPoint(x: container.bounds.midX,y: container.bounds.midY)
|
||||
: CGPoint(x: container.bounds.width*normalizedPosition.x,y: container.bounds.height*normalizedPosition.y))
|
||||
|
||||
let wrapper = CALayer()
|
||||
wrapper.bounds = CGRect(origin: .zero, size: unionRect.size)
|
||||
wrapper.anchorPoint = CGPoint(x: 0.5, y: 0.5)
|
||||
wrapper.position = CGPoint(
|
||||
x: container.bounds.midX,
|
||||
y: container.bounds.midY
|
||||
)
|
||||
wrapper.position = realPosition
|
||||
|
||||
let scale = min(
|
||||
targetSize.width / unionRect.width,
|
||||
@@ -93,7 +102,7 @@ import SVGKit
|
||||
wrappedLayer.bounds = CGRect(x: 0, y: 0, width: container.bounds.size.width, height: container.bounds.size.height)
|
||||
wrappedLayer.position = CGPoint(x: container.bounds.midX, y: container.bounds.midY)
|
||||
wrappedLayer.insertSublayer(wrapper, at: 0)
|
||||
|
||||
|
||||
return wrappedLayer
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@interface LayoutOnScreenControlsViewController : UIViewController <OnScreenWidgetGuidelineUpdateDelegate,UITextFieldDelegate>
|
||||
- (void)profileRefresh;
|
||||
- (void)reloadOnScreenWidgetViews;
|
||||
- (void)presentProfilesTableView;
|
||||
- (void)presentProfilesTableViewWithPickProfile:(bool)pickProfile;
|
||||
|
||||
@property LayoutOnScreenControls *layoutOSC; // object that contains a view which contains the on screen controller buttons that allows the user to drag and positions each button on the screen using touch
|
||||
@property (nonatomic) NSMutableSet* onScreenWidgetViews;
|
||||
@@ -129,6 +129,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (strong, nonatomic) IBOutlet UILabel *walkModeThresholdLabel;
|
||||
@property (strong, nonatomic) IBOutlet UISlider *walkModeThresholdSlider;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIStackView *collectedWidgetsStack;
|
||||
@property (weak, nonatomic) IBOutlet UISegmentedControl *collectedWidgetsSelector;
|
||||
@property (weak, nonatomic) IBOutlet UISegmentedControl *revealModeSelector;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIStackView *widgetPanelStack;
|
||||
|
||||
|
||||
+144
-85
@@ -103,8 +103,8 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
return newPosition;
|
||||
}
|
||||
|
||||
- (void)reloadLegacyOnScreenControls{
|
||||
[self.layoutOSC updateControls]; // creates and saves a 'Default' OSC profile or loads the o//ne the user selected on the previous screen
|
||||
- (void)reloadLegacyOnScreenControls:(OSCProfile* )profile{
|
||||
[self.layoutOSC updateLegacyWidgetsWith:profile]; // creates and saves a 'Default' OSC profile or loads the o//ne the user selected on the previous screen
|
||||
[self addInnerAnalogSticksToOuterAnalogLayers];
|
||||
[self.layoutOSC.layoutChanges removeAllObjects]; // since a new OSC profile is being loaded, this will remove all previous layout changes made from the array
|
||||
[self OSCLayoutChanged]; // fades the 'Undo Button' out
|
||||
@@ -123,74 +123,94 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
else return longSide;
|
||||
}
|
||||
|
||||
- (void)dummytest{
|
||||
|
||||
}
|
||||
|
||||
- (void)reloadOnScreenWidgetViews{
|
||||
NSLog(@"reloadOnScreenWidgets %f", CACurrentMediaTime());
|
||||
OnScreenWidgetView.isTweakingHighlight = false;
|
||||
OnScreenWidgetView.editMode = true;
|
||||
[self hideStickIndicators];
|
||||
|
||||
- (void)clearOnScreenWidgets{
|
||||
for (UIView *subview in self.view.subviews) {
|
||||
if ([subview isKindOfClass:[OnScreenWidgetView class]]) {
|
||||
[subview removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
[self.onScreenWidgetViews removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)reloadOnScreenWidgetViews{
|
||||
OnScreenWidgetView.isTweakingHighlight = false;
|
||||
OnScreenWidgetView.editMode = true;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self hideStickIndicators];
|
||||
[self clearOnScreenWidgets];
|
||||
|
||||
int16_t sequence = -1;
|
||||
|
||||
// _activeCustomOscButtonPositionDict will be updated every time when the osc profile is reloaded
|
||||
OSCProfile *oscProfile = [self->profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
NSLog(@"reloadOnScreenWidgets %lu", oscProfile.buttonStatesEncoded.count);
|
||||
bool hasLegacyWidget = false;
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
// OnScreenButtonState* buttonState = [NSKeyedUnarchiver unarchivedObjectOfClass:[OnScreenButtonState class] fromData:buttonStateEncoded error:nil];
|
||||
OnScreenButtonState* buttonState = [self->profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
NSLog(@"reloadOnScreenWidgets name %@", buttonState.name);
|
||||
if(buttonState.widgetType == CustomOnScreenWidget){
|
||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:buttonState.name buttonLabel:buttonState.alias shape:buttonState.widgetShape profile:oscProfile]; //reconstruct widgetView
|
||||
widgetView.sequence = buttonState.sequence == -1 ? ++sequence : buttonState.sequence;
|
||||
widgetView.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
|
||||
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||
widgetView.widthFactor = buttonState.widthFactor;
|
||||
widgetView.heightFactor = buttonState.heightFactor;
|
||||
widgetView.componentSizeFactor = buttonState.componentSizeFactor;
|
||||
widgetView.borderWidth = buttonState.borderWidth;
|
||||
widgetView.highlightSizeFactor = buttonState.highlightSizeFactor;
|
||||
widgetView.autoTapInterval = buttonState.autoTapInterval;
|
||||
[widgetView setVibrationWithStyle:buttonState.vibrationStyle];
|
||||
widgetView.mouseButtonAction = buttonState.mouseButtonAction;
|
||||
widgetView.sensitivityFactorX = buttonState.sensitivityFactorX;
|
||||
widgetView.sensitivityFactorY = buttonState.sensitivityFactorY;
|
||||
widgetView.slideThreshold = buttonState.slideThreshold;
|
||||
widgetView.yawFactor = buttonState.yawFactor;
|
||||
widgetView.pitchFactor = buttonState.pitchFactor;
|
||||
widgetView.rollFactor = buttonState.rollFactor;
|
||||
widgetView.decelerationRateX = buttonState.decelerationRateX;
|
||||
widgetView.decelerationRateY = buttonState.decelerationRateY;
|
||||
widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset;
|
||||
widgetView.dWheelWalkModeThreshold = buttonState.walkModeThreshold;
|
||||
widgetView.minStickOffset = buttonState.minStickOffset;
|
||||
widgetView.buttonMode = buttonState.buttonMode;
|
||||
// Add the widgetView to the view controller's view
|
||||
[self.view insertSubview:widgetView belowSubview:self.widgetPanelStack];
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
[widgetView setLocationWithPosition:buttonState.position];
|
||||
widgetView.sizeReference = [self getCurrentWidgetSizeReference];
|
||||
[widgetView resizeWidgetView]; // resize must be called after relocation
|
||||
[widgetView adjustTransparencyWithAlpha:buttonState.backgroundAlpha tweakBorderAlpha:NO];
|
||||
[widgetView tweakLabelAlphaWithAlpha:buttonState.labelAlpha];
|
||||
[widgetView tweakBorderAlphaWithAlpha:buttonState.borderAlpha];
|
||||
[widgetView tweakHighlightAlphaWithAlpha:buttonState.highlightAlpha];
|
||||
[widgetView adjustBorderWithWidth:buttonState.borderWidth];
|
||||
[self.onScreenWidgetViews addObject:widgetView];
|
||||
}
|
||||
else if(buttonState.widgetType == LegacyOnScreenControls) hasLegacyWidget = true;
|
||||
}
|
||||
NSLog(@"reloadOnScreenWidgets hasLegacyWidget %d %f", hasLegacyWidget, CACurrentMediaTime());
|
||||
if(hasLegacyWidget) [self reloadLegacyOnScreenControls:oscProfile];
|
||||
else{
|
||||
for(CALayer* layer in self.layoutOSC.OSCButtonLayerPool){
|
||||
[layer removeFromSuperlayer];
|
||||
}
|
||||
[self.layoutOSC.OSCButtonLayerPool removeAllObjects];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
NSLog(@"reload os Key here");
|
||||
|
||||
// _activeCustomOscButtonPositionDict will be updated every time when the osc profile is reloaded
|
||||
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
|
||||
for (NSData *buttonStateEncoded in oscProfile.buttonStatesEncoded) {
|
||||
// OnScreenButtonState* buttonState = [NSKeyedUnarchiver unarchivedObjectOfClass:[OnScreenButtonState class] fromData:buttonStateEncoded error:nil];
|
||||
OnScreenButtonState* buttonState = [profilesManager unarchiveButtonStateEncoded:buttonStateEncoded];
|
||||
if(buttonState.widgetType == CustomOnScreenWidget){
|
||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:buttonState.name buttonLabel:buttonState.alias shape:buttonState.widgetShape]; //reconstruct widgetView
|
||||
widgetView.identifier = buttonState.identifier;
|
||||
NSLog(@"cmd: %@, id: %@", buttonState.name, widgetView.identifier);
|
||||
widgetView.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
|
||||
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||
widgetView.widthFactor = buttonState.widthFactor;
|
||||
widgetView.heightFactor = buttonState.heightFactor;
|
||||
widgetView.componentSizeFactor = buttonState.componentSizeFactor;
|
||||
widgetView.borderWidth = buttonState.borderWidth;
|
||||
widgetView.highlightSizeFactor = buttonState.highlightSizeFactor;
|
||||
widgetView.autoTapInterval = buttonState.autoTapInterval;
|
||||
[widgetView setVibrationWithStyle:buttonState.vibrationStyle];
|
||||
widgetView.mouseButtonAction = buttonState.mouseButtonAction;
|
||||
widgetView.sensitivityFactorX = buttonState.sensitivityFactorX;
|
||||
widgetView.sensitivityFactorY = buttonState.sensitivityFactorY;
|
||||
widgetView.slideThreshold = buttonState.slideThreshold;
|
||||
widgetView.yawFactor = buttonState.yawFactor;
|
||||
widgetView.pitchFactor = buttonState.pitchFactor;
|
||||
widgetView.rollFactor = buttonState.rollFactor;
|
||||
widgetView.decelerationRateX = buttonState.decelerationRateX;
|
||||
widgetView.decelerationRateY = buttonState.decelerationRateY;
|
||||
widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset;
|
||||
widgetView.dWheelWalkModeThreshold = buttonState.walkModeThreshold;
|
||||
widgetView.minStickOffset = buttonState.minStickOffset;
|
||||
widgetView.buttonMode = buttonState.buttonMode;
|
||||
// Add the widgetView to the view controller's view
|
||||
[self.view insertSubview:widgetView belowSubview:self.widgetPanelStack];
|
||||
buttonState.position = [self denormalizeWidgetPosition:buttonState.position];
|
||||
[widgetView setLocationWithPosition:buttonState.position];
|
||||
widgetView.sizeReference = [self getCurrentWidgetSizeReference];
|
||||
[widgetView resizeWidgetView]; // resize must be called after relocation
|
||||
[widgetView adjustTransparencyWithAlpha:buttonState.backgroundAlpha tweakBorderAlpha:NO];
|
||||
[widgetView tweakLabelAlphaWithAlpha:buttonState.labelAlpha];
|
||||
[widgetView tweakBorderAlphaWithAlpha:buttonState.borderAlpha];
|
||||
[widgetView tweakHighlightAlphaWithAlpha:buttonState.highlightAlpha];
|
||||
[widgetView adjustBorderWithWidth:buttonState.borderWidth];
|
||||
[self.onScreenWidgetViews addObject:widgetView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
profilesManager = [OSCProfilesManager sharedManager:self.view.bounds];
|
||||
NSLog(@"self.view.bounds %f, %f", self.view.bounds.size.width, self.view.bounds.size.height);
|
||||
self.onScreenWidgetViews = [[NSMutableSet alloc] init]; // will be revised to read persisted data , somewhere else
|
||||
[OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager
|
||||
|
||||
@@ -220,7 +240,6 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
self.layoutOSC._level = OnScreenControlsLevelCustom;
|
||||
self.layoutOSC.layoutToolVC = self;
|
||||
//[self.layoutOSC show]; // draw on screen controls
|
||||
[self.layoutOSC show]; // draw on screen controls
|
||||
|
||||
[self addInnerAnalogSticksToOuterAnalogLayers]; // allows inner and analog sticks to be dragged together around the screen together as one unit which is the expected behavior
|
||||
|
||||
@@ -269,6 +288,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
widgetPanelStoredCenter = self.widgetPanelStack.center;
|
||||
[super viewDidAppear:animated];
|
||||
[self profileRefresh];
|
||||
[self setupWidgetPanel];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated{
|
||||
@@ -285,7 +305,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(reloadOnScreenWidgetViews)
|
||||
selector:@selector(dummytest)
|
||||
name:@"OscLayoutProfileSelctedInTableView" // This is a special notification for reloading the on screen keyboard buttons. which can't be executed by _oscProfilesTableViewController.needToUpdateOscLayoutTVC code block, and has to be triggered by a notification
|
||||
object:nil];
|
||||
|
||||
@@ -324,8 +344,9 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
_oscProfilesTableViewController.layoutViewBounds = self.view.bounds;
|
||||
[OSCProfilesManager setLayoutViewBounds:self.view.bounds];
|
||||
[OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager
|
||||
|
||||
[self reloadOnScreenWidgetViews];
|
||||
[self reloadLegacyOnScreenControls];
|
||||
// [self reloadLegacyOnScreenControls];
|
||||
}
|
||||
|
||||
- (void)handleEnterBackground{
|
||||
@@ -740,8 +761,8 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
NSString *widgetShape = [widgetInitParams[@"shape"] lowercaseString];
|
||||
|
||||
widgetInitParams[@"cmdString"] = cmdString;
|
||||
bool noValidKeyboardString = [CommandManager.shared extractKeyStringsFromComboCommandFrom:cmdString] == nil; // this is a invalid string.
|
||||
bool noValidSuperComboButtonString = [CommandManager.shared extractSinglCmdStringsFromComboKeysFrom:cmdString] == nil; // this is a invalid string.
|
||||
bool noValidKeyboardString = [CommandManager.shared extractKeyStringsFrom:cmdString] == nil; // this is a invalid string.
|
||||
bool noValidSuperComboButtonString = [CommandManager.shared extractCmdStringsFrom:cmdString] == nil; // this is a invalid string.
|
||||
bool noValidMouseButtonString = ![CommandManager.mouseButtonMappings.allKeys containsObject:cmdString];
|
||||
bool noValidTouchPadString = ![CommandManager.touchPadCmds containsObject:cmdString];
|
||||
bool noValidOscButtonString = ![CommandManager.oscButtonMappings.allKeys containsObject:cmdString];
|
||||
@@ -765,8 +786,9 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
|
||||
- (void) updateWidget:(OnScreenWidgetView* )widget byParams:(NSMutableDictionary* )widgetInitParams createNew:(bool)createNew{
|
||||
if(![self isWidgetParamsValid:widgetInitParams]) return;
|
||||
OnScreenWidgetView* newWidget = [[OnScreenWidgetView alloc] initWithCmdString:widgetInitParams[@"cmdString"] buttonLabel:widgetInitParams[@"buttonLabel"] shape:widgetInitParams[@"shape"]]; //reconstruct widgetView
|
||||
newWidget.identifier = createNew ? [UUIDHelper newUUID] : widget.identifier;
|
||||
OSCProfile* profile = [[OSCProfilesManager sharedManager:CGRectZero] getSelectedProfile];
|
||||
OnScreenWidgetView* newWidget = [[OnScreenWidgetView alloc] initWithCmdString:widgetInitParams[@"cmdString"] buttonLabel:widgetInitParams[@"buttonLabel"] shape:widgetInitParams[@"shape"] profile:profile]; //reconstruct widgetView
|
||||
newWidget.sequence = widget.sequence;
|
||||
newWidget.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
|
||||
newWidget.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||
newWidget.widthFactor = widget.widthFactor;
|
||||
@@ -798,7 +820,10 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
[self.view insertSubview:newWidget belowSubview:self.widgetPanelStack];
|
||||
[newWidget accessWidgetAttributes];
|
||||
|
||||
if(createNew) [newWidget setLocationWithPosition:CGPointMake(90, 130)];
|
||||
if(createNew){
|
||||
[newWidget setLocationWithPosition:CGPointMake(90, 130)];
|
||||
newWidget.sequence = [newWidget getAvailableSequence];
|
||||
}
|
||||
else [newWidget setLocationWithPosition:widget.center];
|
||||
newWidget.sizeReference = widget.sizeReference;
|
||||
[newWidget resizeWidgetView]; // resize must be called after relocation
|
||||
@@ -819,14 +844,17 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
- (void) createWidgetFromParams: (NSMutableDictionary*) widgetInitParams{
|
||||
if(![self isWidgetParamsValid:widgetInitParams]) return;
|
||||
//saving & present the keyboard button:
|
||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:widgetInitParams[@"cmdString"] buttonLabel:widgetInitParams[@"buttonLabel"] shape:widgetInitParams[@"shape"]];
|
||||
widgetView.identifier = [UUIDHelper newUUID];
|
||||
OSCProfile* profile = [[OSCProfilesManager sharedManager:CGRectZero] getSelectedProfile];
|
||||
OnScreenWidgetView* widgetView = [[OnScreenWidgetView alloc] initWithCmdString:widgetInitParams[@"cmdString"] buttonLabel:widgetInitParams[@"buttonLabel"] shape:widgetInitParams[@"shape"] profile:profile];
|
||||
[self.view insertSubview:widgetView belowSubview:self.widgetPanelStack];
|
||||
widgetView.hidden = true;
|
||||
widgetView.sequence = [widgetView getAvailableSequence];
|
||||
widgetView.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
|
||||
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
|
||||
[self.onScreenWidgetViews addObject:widgetView];
|
||||
// Add the widgetView to the view controller's view
|
||||
[self.view insertSubview:widgetView belowSubview:self.widgetPanelStack];
|
||||
[widgetView setLocationWithPosition:CGPointMake(90, 130)];
|
||||
widgetView.hidden = false;
|
||||
[widgetView resizeWidgetView];
|
||||
[widgetView setVibrationWithStyle:UIImpactFeedbackStyleLight];
|
||||
}
|
||||
@@ -834,7 +862,6 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
|
||||
/* show pop up notification that lets users choose to save the current OSC layout configuration as a profile they can load when they want. User can also choose to cancel out of this pop up */
|
||||
- (IBAction) saveTapped:(id)sender {
|
||||
|
||||
/*
|
||||
OSCProfile* targetProfile = [profilesManager getAllProfiles][0];
|
||||
OSCProfile* currentProfile = [profilesManager getSelectedProfile];
|
||||
@@ -846,7 +873,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
[self clearSickInput];
|
||||
[OSCProfilesManager setLayoutViewBounds:self.view.bounds];
|
||||
|
||||
if([self->profilesManager updateSelectedProfile:self.layoutOSC.OSCButtonLayers]){
|
||||
if([self->profilesManager updateSelectedProfile:self.layoutOSC.OSCButtonLayerPool]){
|
||||
UIAlertController * savedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Current profile updated successfully"] preferredStyle:UIAlertControllerStyleAlert];
|
||||
[savedAlertController addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
}]];
|
||||
@@ -904,7 +931,6 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
*/
|
||||
|
||||
- (void)widgetViewTapped: (NSNotification *)notification{
|
||||
|
||||
[self hideStickIndicators];
|
||||
|
||||
[self clearSickInput];
|
||||
@@ -1064,7 +1090,15 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
self.buttonModeStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton;
|
||||
[self.buttonModeSelector setEnabled:!selectedWidgetView.isFuncationalButton forSegmentAtIndex:0];
|
||||
[self.buttonModeSelector setEnabled:!selectedWidgetView.isFuncationalButton forSegmentAtIndex:1];
|
||||
[self.buttonModeSelector setEnabled:!selectedWidgetView.isFuncationalButton
|
||||
|| [selectedWidgetView.functionalButtonString containsString:@"ABSTCHDRAG"]
|
||||
forSegmentAtIndex:3];
|
||||
[self.buttonModeSelector setSelectedSegmentIndex:selectedWidgetView.buttonMode];
|
||||
|
||||
self.collectedWidgetsStack.hidden = !selectedWidgetView.isFolder;
|
||||
[self.collectedWidgetsSelector setSelectedSegmentIndex:selectedWidgetView.folded ? 1 :0];
|
||||
[self.revealModeSelector setSelectedSegmentIndex:selectedWidgetView.revealMode];
|
||||
[OnScreenWidgetView setCollectionWithHidden:selectedWidgetView.folded for:selectedWidgetView];
|
||||
|
||||
[self autoFitStack:self.widgetPanelStack];
|
||||
|
||||
@@ -1228,6 +1262,19 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)collectionHiddenChanged:(UISegmentedControl* )sender{
|
||||
if(self->selectedWidgetView != nil && self->widgetViewSelected){
|
||||
[OnScreenWidgetView setCollectionWithHidden:sender.selectedSegmentIndex==1 for:selectedWidgetView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)revealModeChanged:(UISegmentedControl* )sender{
|
||||
if(self->selectedWidgetView != nil && self->widgetViewSelected){
|
||||
selectedWidgetView.revealMode = sender.selectedSegmentIndex;
|
||||
[OnScreenWidgetView setCollectionWithHidden:selectedWidgetView.folded for:selectedWidgetView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)vibrationStyleChanged:(UISegmentedControl* )sender{
|
||||
bool vibraiontOn;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
@@ -1538,7 +1585,12 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
[self.buttonModeSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal];
|
||||
self.buttonModeStack.hidden = YES;
|
||||
|
||||
|
||||
[self.collectedWidgetsSelector addTarget:self action:@selector(collectionHiddenChanged:) forControlEvents:(UIControlEventValueChanged)];
|
||||
[self.collectedWidgetsSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal];
|
||||
[self.revealModeSelector addTarget:self action:@selector(revealModeChanged:) forControlEvents:(UIControlEventValueChanged)];
|
||||
[self.revealModeSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal];
|
||||
self.collectedWidgetsStack.hidden = YES;
|
||||
|
||||
if([self isIPhone]){
|
||||
[self.vibrationStyleSelector addTarget:self action:@selector(vibrationStyleChanged:) forControlEvents:(UIControlEventValueChanged)];
|
||||
self.vibrationStyleStack.hidden = YES;
|
||||
@@ -1637,8 +1689,9 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
}
|
||||
|
||||
- (void)handleProfileTablViewDismiss{
|
||||
[self profileRefresh];
|
||||
if(_quickSwitchEnabled) [self dismissViewControllerAnimated:NO completion:nil];
|
||||
else [self setupWidgetPanel];
|
||||
// setup: current profile lable, button width slider, button height slider & button alpha slider
|
||||
}
|
||||
|
||||
/* Basically the same method as loadTapped, without parameter*/
|
||||
@@ -1652,10 +1705,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
else {
|
||||
storyboard = [UIStoryboard storyboardWithName:@"iPad" bundle:nil];
|
||||
}
|
||||
|
||||
// setup: current profile lable, button width slider, button height slider & button alpha slider
|
||||
[self setupWidgetPanel];
|
||||
|
||||
|
||||
//initialiaze _oscProfilesTableViewController
|
||||
self->_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
|
||||
|
||||
@@ -1664,20 +1714,22 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
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
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
[weakSelf reloadLegacyOnScreenControls];
|
||||
strongSelf->_oscProfilesTableViewController.currentOSCButtonLayers = weakSelf.layoutOSC.OSCButtonLayers; //pass updated OSCLayout to OSCProfileTableView again
|
||||
[weakSelf reloadOnScreenWidgetViews];
|
||||
strongSelf->_oscProfilesTableViewController.currentOSCButtonLayers = weakSelf.layoutOSC.OSCButtonLayerPool; //pass updated OSCLayout to OSCProfileTableView again
|
||||
};
|
||||
|
||||
[self.oscProfilesTableViewController profileViewRefresh]; // execute this will make sure OSCLayout is updated from persisted profile, not any cache.
|
||||
[self reloadOnScreenWidgetViews];
|
||||
NSLog(@"profileRefresh %f", CACurrentMediaTime());
|
||||
// [self reloadOnScreenWidgetViews];
|
||||
|
||||
// [self presentViewController:vc animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void) presentProfilesTableView{
|
||||
[self saveTapped:nil];
|
||||
- (void) presentProfilesTableViewWithPickProfile:(bool)pickProfile{
|
||||
[self hideStickIndicators];
|
||||
selectedWidgetView = nil;
|
||||
// if(pickProfile) [self clearOnScreenWidgets];
|
||||
|
||||
UIStoryboard *storyboard;
|
||||
BOOL isIPhone = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone);
|
||||
if (isIPhone) {
|
||||
@@ -1692,21 +1744,24 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
|
||||
__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
|
||||
[weakSelf reloadLegacyOnScreenControls];
|
||||
if(!pickProfile || true) [weakSelf reloadOnScreenWidgetViews];
|
||||
};
|
||||
|
||||
self.widgetPanelStack.hidden = YES;
|
||||
|
||||
_oscProfilesTableViewController.currentOSCButtonLayers = self.layoutOSC.OSCButtonLayers;
|
||||
_oscProfilesTableViewController.currentOSCButtonLayers = self.layoutOSC.OSCButtonLayerPool;
|
||||
|
||||
// _oscProfilesTableViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
|
||||
_oscProfilesTableViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
|
||||
[self presentViewController:_oscProfilesTableViewController animated:YES completion:nil];
|
||||
// _oscProfilesTableViewController.modalPresentationStyle = UIModalPresentationPageSheet;
|
||||
_oscProfilesTableViewController.pickProfileEnabled = pickProfile;
|
||||
[self presentViewController:_oscProfilesTableViewController animated:NO completion:nil];
|
||||
}
|
||||
|
||||
/* Presents the view controller that lists all OSC profiles the user can choose from */
|
||||
- (IBAction) loadTapped:(id)sender {
|
||||
[self presentProfilesTableView];
|
||||
[self saveTapped:nil];
|
||||
[self presentProfilesTableViewWithPickProfile:false];
|
||||
}
|
||||
|
||||
|
||||
@@ -1849,5 +1904,9 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
|
||||
widgetPanelMovedByTouch = false;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
NSLog(@"dealloc layoutToolVC %f ...", CACurrentMediaTime());
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,9 +29,10 @@ typedef NS_ENUM(NSUInteger, FileOperation) {
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UITableView *tableView;
|
||||
@property (nonatomic, copy) void (^needToUpdateOscLayoutTVC)(void);
|
||||
@property (nonatomic, assign) NSMutableArray *currentOSCButtonLayers;
|
||||
@property (nonatomic, assign) NSMutableSet *currentOSCButtonLayers;
|
||||
@property (nonatomic, assign) CGRect layoutViewBounds;
|
||||
@property (weak, nonatomic) IBOutlet UINavigationBar *profileTableViewNavigationBar;
|
||||
@property (nonatomic, assign) bool pickProfileEnabled;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
const double NAV_BAR_HEIGHT = 50;
|
||||
|
||||
@interface OSCProfilesTableViewController ()
|
||||
@interface OSCProfilesTableViewController () <UIGestureRecognizerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@@ -42,9 +42,15 @@ const double NAV_BAR_HEIGHT = 50;
|
||||
return [self getCurrentOrientation]; // 90 Degree rotation not allowed in streaming or app view
|
||||
}
|
||||
|
||||
/*
|
||||
- (void) viewDidDisappear:(BOOL)animated{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"OscLayoutTableViewCloseNotification" object:self]; // notify other view that oscLayoutManager is closing
|
||||
[super viewDidDisappear:animated];
|
||||
}*/
|
||||
|
||||
- (void) viewWillDisappear:(BOOL)animated{
|
||||
[super viewWillDisappear:animated];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"OscLayoutTableViewCloseNotification" object:self]; // notify other view that oscLayoutManager is closing
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +94,17 @@ const double NAV_BAR_HEIGHT = 50;
|
||||
self.tableView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; // set background color & transparency
|
||||
// self.tableView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2]; // set background color & transparency
|
||||
|
||||
UITapGestureRecognizer *tap =
|
||||
[[UITapGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(dismissSelf)];
|
||||
tap.cancelsTouchesInView = NO;
|
||||
tap.delegate = (id<UIGestureRecognizerDelegate>)self;
|
||||
|
||||
[self.view addGestureRecognizer:tap];
|
||||
}
|
||||
|
||||
- (void)dismissSelf {
|
||||
[self dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
|
||||
- (void) viewDidAppear:(BOOL)animated {
|
||||
@@ -180,6 +197,7 @@ const double NAV_BAR_HEIGHT = 50;
|
||||
//[self dismissViewControllerAnimated:YES completion:nil];
|
||||
//[selfparentLayoutOSCViewController]
|
||||
[self.tableView reloadData]; // table view will be refreshed by calling reloadData
|
||||
// if(_pickProfileEnabled) return;
|
||||
if (self.needToUpdateOscLayoutTVC) { // tells the presenting view controller to lay out the on screen buttons according to the selected profile's instructions
|
||||
self.needToUpdateOscLayoutTVC();
|
||||
}
|
||||
@@ -215,7 +233,7 @@ const double NAV_BAR_HEIGHT = 50;
|
||||
}
|
||||
|
||||
- (IBAction) exitTapped:(id)sender {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
[self dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - UIDocumentPickerDelegate
|
||||
@@ -438,5 +456,13 @@ didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
||||
[self profileViewRefresh]; // update OSC layout when table view option is changed
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
|
||||
shouldReceiveTouch:(UITouch *)touch {
|
||||
if (_pickProfileEnabled && [touch.view isDescendantOfView:self.tableView]) {
|
||||
return true;
|
||||
}
|
||||
if(touch.view == self.view) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
UIView* menuSeparator;
|
||||
UIView* snapshot;
|
||||
SettingsViewController* settingsViewController;
|
||||
StreamFrameViewController* streamFrameViewController;
|
||||
__weak StreamFrameViewController* streamFrameViewController;
|
||||
id navBarAppearanceStandard;
|
||||
bool _viewJustAppeared;
|
||||
TemporaryApp * launchedApp;
|
||||
@@ -1257,6 +1257,7 @@ static NSMutableSet* hostList;
|
||||
streamFrameViewController.mainFrameViewcontroller = self;
|
||||
streamFrameViewController.streamConfig = _streamConfig;
|
||||
}
|
||||
NSLog(@"streamVC seque... %lu %f",(uintptr_t)streamFrameViewController , CACurrentMediaTime());
|
||||
}
|
||||
|
||||
- (void) showLoadingFrame:(void (^)(void))completion {
|
||||
|
||||
@@ -206,7 +206,7 @@ typedef NS_ENUM(NSInteger, SWRevealToggleAnimationType)
|
||||
- (void)setRightViewController:(UIViewController *)rightViewController animated:(BOOL)animated;
|
||||
|
||||
// Front view controller, can be nil on initialization but must be supplied by the time the view is loaded
|
||||
@property (nonatomic) UIViewController *frontViewController;
|
||||
@property (nonatomic, weak) UIViewController *frontViewController;
|
||||
- (void)setFrontViewController:(UIViewController *)frontViewController animated:(BOOL)animated;
|
||||
|
||||
// Sets the frontViewController using a default set of chained animations consisting on moving the
|
||||
|
||||
@@ -645,7 +645,7 @@ static CGFloat scaledValue( CGFloat v1, CGFloat min2, CGFloat max2, CGFloat min1
|
||||
UIView* _separatorLine;
|
||||
NSMutableArray *_animationQueue;
|
||||
BOOL _userInteractionStore;
|
||||
UIViewController *_primaryViewController;
|
||||
__weak UIViewController *_primaryViewController;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -256,7 +256,11 @@
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *pencilTickIntervalStack;
|
||||
@property (strong, nonatomic) IBOutlet UISlider *pencilTickIntervalSlider;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIStackView *doubleTapShortcutStack;
|
||||
@property (weak, nonatomic) IBOutlet UISwitch *doubleTapShortcutSwitch;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIStackView *pencilPausesNativeTouchStack;
|
||||
@property (weak, nonatomic) IBOutlet UISwitch *pencilPausesNativeTouchSwitch;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
|
||||
@@ -393,6 +393,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self.synthPhysicalInputSwitch setOn:oscProfile.synthesizePhysicalStick];
|
||||
|
||||
[self.pressureCurveSwitch setOn:oscProfile.pressureCurveEnabled];
|
||||
[self.doubleTapShortcutSwitch setOn:oscProfile.doubleTapShorcutEnabled];
|
||||
[self.pencilPausesNativeTouchSwitch setOn:oscProfile.pencilPausesNativeTouch];
|
||||
}
|
||||
|
||||
- (void)saveGameProfileConfigs{
|
||||
@@ -414,6 +416,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
&& (int16_t)(oscProfile.physicalLeftStickMinOffset) == (int16_t)self.leftStickMinOffsetSlider.value
|
||||
&& (int16_t)(oscProfile.physicalRightStickMinOffset) == (int16_t)self.rightStickMinOffsetSlider.value
|
||||
&& oscProfile.pressureCurveEnabled == self.pressureCurveSwitch.isOn
|
||||
&& oscProfile.doubleTapShorcutEnabled == self.doubleTapShortcutSwitch.isOn
|
||||
&& oscProfile.pencilPausesNativeTouch == self.pencilPausesNativeTouchSwitch.isOn
|
||||
);
|
||||
|
||||
if(!configNotChanged){
|
||||
@@ -431,6 +435,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
oscProfile.physicalLeftStickMinOffset = (int16_t)self.leftStickMinOffsetSlider.value;
|
||||
oscProfile.physicalRightStickMinOffset = (int16_t)self.rightStickMinOffsetSlider.value;
|
||||
oscProfile.pressureCurveEnabled = self.pressureCurveSwitch.isOn;
|
||||
oscProfile.doubleTapShorcutEnabled = self.doubleTapShortcutSwitch.isOn;
|
||||
oscProfile.pencilPausesNativeTouch = self.pencilPausesNativeTouchSwitch.isOn;
|
||||
[oscProfileMan replaceSelectedProfileWith:oscProfile overwriteDefault:YES];
|
||||
}
|
||||
}
|
||||
@@ -866,6 +872,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self addSetting:self.pencilTickStack ofId:@"pencilTickStack" withInfoTag:YES withDynamicLabel:NO to:pencilSection];
|
||||
[self addSetting:self.pencilTickIntervalStack ofId:@"pencilTickIntervalStack" withInfoTag:NO withDynamicLabel:YES to:pencilSection];
|
||||
[self addSetting:self.pressureCurveStack ofId:@"pressureCurveStack" withInfoTag:NO withDynamicLabel:NO to:pencilSection];
|
||||
[self addSetting:self.doubleTapShortcutStack ofId:@"doubleTapShortcutStack" withInfoTag:YES withDynamicLabel:NO to:pencilSection];
|
||||
[self addSetting:self.pencilPausesNativeTouchStack ofId:@"pencilPausesNativeTouchStack" withInfoTag:NO withDynamicLabel:NO to:pencilSection];
|
||||
[pencilSection addToParentStack:_parentStack];
|
||||
}
|
||||
|
||||
@@ -1472,6 +1480,15 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
tipText = [LocalizationHelper localizedStringForKey:@"audioConfigStackTip"];
|
||||
showOnlineDocAction = false;
|
||||
}
|
||||
|
||||
if([sender.superview.accessibilityIdentifier isEqualToString: @"doubleTapShortcutStack"]){
|
||||
oscProfile = [oscProfileMan getSelectedProfile];
|
||||
tipText = [LocalizationHelper localizedStringForKey:@"doubleTapShortcutStackTip"
|
||||
, [oscProfile.brushShortcut isEqualToString:@""] ? [LocalizationHelper localizedStringForKey:@"Null"] : oscProfile.brushShortcut
|
||||
, [oscProfile.eraserShortcut isEqualToString:@""] ? [LocalizationHelper localizedStringForKey:@"Null"] : oscProfile.eraserShortcut
|
||||
];
|
||||
showOnlineDocAction = false;
|
||||
}
|
||||
|
||||
UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"Tips"] message:tipText preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
@@ -2369,7 +2386,7 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self mapGyroToChanged:self.mapGyroToSelector];
|
||||
}
|
||||
else{
|
||||
MotionHandler* motionHandler = [MotionHandler sharedInstance];
|
||||
MotionHandler* motionHandler = [MotionHandler sharedWithProfile:nil];
|
||||
[motionHandler calibrateGyroBiasWithDuration:5 completion:^{
|
||||
Settings* currentSettings = [self->dataMan retrieveSettings];
|
||||
currentSettings.gyroBiasX = [NSNumber numberWithDouble:motionHandler.gyroBiasX];
|
||||
@@ -3355,6 +3372,8 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.pencilTickSelector.selectedSegmentIndex = PencilTickDisabled;
|
||||
[self pencilTickModeChanged:self.pencilTickSelector];
|
||||
[self.doubleTapShortcutSwitch setOn:false];
|
||||
[self.pencilPausesNativeTouchSwitch setOn:false];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3371,6 +3390,29 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)doubleTapShortcutSwitchFlipped:(UISwitch* )sender{
|
||||
if(sender.isOn && !settingsViewJustLoaded){
|
||||
if (@available(iOS 15.0, *)) {
|
||||
[IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) {
|
||||
if(info.valid) [PencilHandler enterDoubleTapShortcutsIn:self];
|
||||
else {
|
||||
[IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pencilPausesNativeTouchSwitchFlipped:(UISwitch* )sender{
|
||||
if(sender.isOn && !settingsViewJustLoaded){
|
||||
if (@available(iOS 15.0, *)) {
|
||||
[IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) {
|
||||
if(!info.valid) [IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadPencilSettings:(TemporarySettings*) tempSettings{
|
||||
if([self isIPad]){
|
||||
self.pencilTickSelector.selectedSegmentIndex = tempSettings.pencilTickMode.intValue;
|
||||
@@ -3381,8 +3423,9 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self.pencilTickIntervalSlider addTarget:self action:@selector(pencilTickIntervalSliderMoved:) forControlEvents:UIControlEventValueChanged];
|
||||
[self pencilTickIntervalSliderMoved:self.pencilTickIntervalSlider];
|
||||
|
||||
[self.pressureCurveSwitch setOn:true];
|
||||
[self.pressureCurveSwitch addTarget:self action:@selector(pressureCurveSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||
[self.doubleTapShortcutSwitch addTarget:self action:@selector(doubleTapShortcutSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||
[self.pencilPausesNativeTouchSwitch addTarget:self action:@selector(pencilPausesNativeTouchSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,9 @@
|
||||
@property (nonatomic) StreamConfiguration* streamConfig;
|
||||
@property (nonatomic, strong) AVPictureInPictureController *pipController API_AVAILABLE(ios(9.0));
|
||||
@property (nonatomic, strong) AVPictureInPictureControllerContentSource *pipContentSource API_AVAILABLE(ios(15.0)); // Needed for iOS 15+ layer-based PiP
|
||||
@property (nonatomic, assign) MainFrameViewController *mainFrameViewcontroller;
|
||||
@property (nonatomic, weak) MainFrameViewController *mainFrameViewcontroller;
|
||||
@property (nonatomic, assign) bool micStreamInitialized;
|
||||
@property (nonatomic, assign) bool viewJustLoaded;
|
||||
|
||||
@property (nonatomic, strong) MetalViewController *metalViewController;
|
||||
@property (nonatomic, strong) ImGuiRenderer *imguiView;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#import "VoidLink-Swift.h"
|
||||
#import "OSCProfilesManager.h"
|
||||
#import "ThemeManager.h"
|
||||
#import "NativeTouchPointer.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -61,7 +62,6 @@
|
||||
StreamView *_streamView;
|
||||
UIScrollView *_scrollView;
|
||||
BOOL _userIsInteracting;
|
||||
bool viewJustLoaded;
|
||||
bool viewIsBeingResized;
|
||||
bool previousOnScreenWidgetEnabled;
|
||||
CGSize _keyboardSize;
|
||||
@@ -378,7 +378,7 @@
|
||||
|
||||
Connection.muteInBackground = _settings.muteInBackground;
|
||||
|
||||
if(!viewJustLoaded) [_controllerSupport updateControllerSupport:self.streamConfig delegate:self];
|
||||
if(!_viewJustLoaded) [_controllerSupport updateControllerSupport:self.streamConfig delegate:self];
|
||||
// reload controllerSupport obj, this is mandatory for OSC reload,especially when the stream view is launched without OSC
|
||||
[_streamView setupStreamView:_controllerSupport interactionDelegate:self config:self.streamConfig streamFrameTopLayerView:self.view]; //reinitiate setupStreamView process.
|
||||
// we got self.view passed to streamView class as the topLayerView, will be useful in many cases
|
||||
@@ -387,7 +387,8 @@
|
||||
|
||||
bool onScreenWidgetSwitched = previousOnScreenWidgetEnabled != [_streamView isOnScreenWidgetEnabled];
|
||||
bool needReload = onScreenWidgetSwitched && !previousOnScreenWidgetEnabled;
|
||||
if(viewJustLoaded||reloadOnscreenWidgets||needReload) [_streamView reloadOnScreenWidgetViews]; //reload keyboard buttons here. the keyboard widget view will be added to the streamframe view instead streamview, the highest layer, which saves a lot of reengineering
|
||||
[_streamView reloadOnScreenWidgetViews:_viewJustLoaded||reloadOnscreenWidgets||needReload]; //reload keyboard buttons here. the keyboard widget view will be added to the streamframe view instead streamview, the highest layer, which saves a lot of reengineering
|
||||
|
||||
if(onScreenWidgetSwitched && previousOnScreenWidgetEnabled) [_streamView clearOnScreenWidgets];
|
||||
previousOnScreenWidgetEnabled = [_streamView isOnScreenWidgetEnabled];
|
||||
|
||||
@@ -444,13 +445,10 @@
|
||||
[self startDisplayLink];
|
||||
}
|
||||
|
||||
_motionHandler = [MotionHandler sharedInstance];
|
||||
_motionHandler = [MotionHandler sharedWithProfile: nil];
|
||||
_motionHandler.gyroBiasX = _settings.gyroBiasX.doubleValue;
|
||||
_motionHandler.gyroBiasY = _settings.gyroBiasY.doubleValue;
|
||||
_motionHandler.gyroBiasZ = _settings.gyroBiasZ.doubleValue;
|
||||
|
||||
_streamView.onScreenControls.instanceReceiverDelegate = _motionHandler;
|
||||
[_streamView.onScreenControls sendInstance];
|
||||
|
||||
TouchPadGestureHandler.enablePinch = _settings.enablePinch;
|
||||
TouchPadGestureHandler.ctrlDownForPinch = _settings.ctrlDownForPinch;
|
||||
@@ -474,7 +472,7 @@
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
viewJustLoaded = false;
|
||||
_viewJustLoaded = false;
|
||||
_deviceWindow = self.view.window;
|
||||
previousOnScreenWidgetEnabled = [_streamView isOnScreenWidgetEnabled];
|
||||
if (@available(iOS 13.0, *)) {
|
||||
@@ -499,6 +497,11 @@
|
||||
}
|
||||
|
||||
self->_streamView.originalFrame = self->_streamView.frame;
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSLog(@"pausing...");
|
||||
nil;
|
||||
});
|
||||
|
||||
// check to see if external screen is connected/disconnected
|
||||
|
||||
@@ -583,7 +586,7 @@
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
viewJustLoaded = true;
|
||||
_viewJustLoaded = true;
|
||||
viewIsBeingResized = false;
|
||||
[super viewDidLoad];
|
||||
|
||||
@@ -768,7 +771,7 @@
|
||||
[self presentViewController:_layoutOnScreenControlsVC animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)switchWidgetProfile{
|
||||
- (void)openWidgetProfileTableWithPickProfile:(BOOL)pickProfile{
|
||||
[_streamView saveRelocatedWidgetViews];
|
||||
_streamView.widgetToolOpened = true;
|
||||
[self->_streamView disableOnScreenControls];
|
||||
@@ -777,7 +780,7 @@
|
||||
_layoutOnScreenControlsVC.toolbarStackView.hidden = true;
|
||||
_layoutOnScreenControlsVC.toolbarRootView.hidden = true;
|
||||
[self presentViewController:_layoutOnScreenControlsVC animated:NO completion:^{
|
||||
[self->_layoutOnScreenControlsVC presentProfilesTableView];
|
||||
[self->_layoutOnScreenControlsVC presentProfilesTableViewWithPickProfile:pickProfile];
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -799,8 +802,8 @@
|
||||
[self->_streamView disableOnScreenControls]; // add this to get realtime back menu working.
|
||||
[self->_streamView reloadOnScreenControlsWith:(ControllerSupport*)_controllerSupport
|
||||
andConfig:(StreamConfiguration*)_streamConfig];
|
||||
[self->_streamView showOnScreenControls];
|
||||
[self->_streamView reloadOnScreenWidgetViews]; //update keyboard buttons here
|
||||
// [self->_streamView reloadLegacyWidgets];
|
||||
[self->_streamView reloadOnScreenWidgetViews:true]; //update keyboard buttons here
|
||||
}
|
||||
|
||||
- (void)setUserInteractionEnabledForStreamView:(bool)enabled{
|
||||
@@ -817,7 +820,8 @@
|
||||
- (void)willMoveToParentViewController:(UIViewController *)parent {
|
||||
// Only cleanup when we're being destroyed
|
||||
if (parent == nil) {
|
||||
//NSLog(@"gyro cleanup, count: %ld", _controller.count);
|
||||
_streamView = nil;
|
||||
[_streamView cleanUp];
|
||||
[_controllerSupport cleanup];
|
||||
|
||||
[UIApplication sharedApplication].idleTimerDisabled = NO;
|
||||
@@ -832,7 +836,11 @@
|
||||
self.metalViewController = nil;
|
||||
NSLog(@"Metal renderer stopped and cleaned up.");
|
||||
}
|
||||
[NativeTouchPointer cleanUpContext];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
for(UIView* view in self.view.subviews){
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,7 +944,7 @@
|
||||
- (void) returnToMainFrame {
|
||||
[_streamView clearOnScreenWidgets];
|
||||
if(micHandler) [micHandler clean];
|
||||
PencilHandler.sharedInstance = nil;
|
||||
PencilHandler.shared = nil;
|
||||
|
||||
// Reset display mode back to default
|
||||
[self updatePreferredDisplayMode:NO];
|
||||
@@ -1188,7 +1196,7 @@
|
||||
[self.view sendSubviewToBack:self->_streamView];
|
||||
}
|
||||
|
||||
[self->_streamView showOnScreenControls];
|
||||
// [self->_streamView showOnScreenControls];
|
||||
|
||||
[self->_controllerSupport connectionEstablished];
|
||||
|
||||
@@ -1609,6 +1617,28 @@
|
||||
[_streamView disablePencilHover];
|
||||
}
|
||||
|
||||
- (void)setAllowSingleTouchEnabled:(BOOL)enabled{
|
||||
[_streamView setAllowSingleTouchEnabled:enabled];
|
||||
}
|
||||
|
||||
- (void)replaceBrushWithShortcut:(NSString *)shortcut{
|
||||
if(PencilHandler.shared){
|
||||
[PencilHandler.shared replaceBrushWith:shortcut];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)replaceEraserWithShortcut:(NSString *)shortcut{
|
||||
if(PencilHandler.shared){
|
||||
[PencilHandler.shared replaceEraserWith:shortcut];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)presentPressureCurveVC{
|
||||
PressureCurveViewController* pressureCurveVC = [[PressureCurveViewController alloc] init];
|
||||
pressureCurveVC.modalPresentationStyle = UIModalPresentationOverFullScreen;
|
||||
[self presentViewController:pressureCurveVC animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
// Require a confirmation when streaming to activate a system gesture
|
||||
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
|
||||
@@ -1645,7 +1675,8 @@
|
||||
|
||||
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
||||
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
||||
|
||||
[self->_streamView saveRelocatedWidgetViews];
|
||||
|
||||
if (_isRestoringFromPiP) {
|
||||
Log(LOG_I, @"View size changed during PiP restore, skipping redundant reconfiguration.");
|
||||
return;
|
||||
@@ -1668,6 +1699,7 @@
|
||||
|
||||
- (void)dealloc {
|
||||
[self stopDisplayLink];
|
||||
NSLog(@"dealloc StreamFrameViewController %f", CACurrentMediaTime());
|
||||
}
|
||||
|
||||
- (void)setupDisplayLink {
|
||||
@@ -1698,5 +1730,4 @@
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,7 +10,7 @@ import UIKit
|
||||
|
||||
@objc protocol ToolboxSpecialEntryDelegate: NSObjectProtocol {
|
||||
@objc optional func openWidgetLayoutTool()
|
||||
@objc optional func switchWidgetProfile()
|
||||
@objc optional func openWidgetProfileTable(pickProfile: Bool)
|
||||
@objc optional func bringUpSoftKeyboard()
|
||||
@objc optional func enterPip()
|
||||
@objc optional func toggleStatsOverlay()
|
||||
@@ -18,7 +18,7 @@ import UIKit
|
||||
}
|
||||
|
||||
|
||||
@objc public class ToolboxViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
||||
@objc public class ToolboxViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UIGestureRecognizerDelegate {
|
||||
|
||||
@objc weak var specialEntryDelegate: ToolboxSpecialEntryDelegate?
|
||||
public let tableView = UITableView()
|
||||
@@ -30,6 +30,8 @@ import UIKit
|
||||
private let viewBackgroundColor = UIColor(white: 0.2, alpha: 0.8);
|
||||
private let highlightColor = UIColor(white: 0.55, alpha: 0.8);
|
||||
private let titleLabel = UILabel()
|
||||
private var contentView = UIView()
|
||||
|
||||
@objc public var specialEntries : NSMutableArray = ["widgetSwitchTool", "widgetLayoutTool", "bringUpSoftKeyboard", "enterPip", "toggleStatsOverlay", "disconnectAndQuitApp"]
|
||||
private let specialEntryAliasDic : [String:String] = [
|
||||
"widgetSwitchTool":SwiftLocalizationHelper.localizedString(forKey: "[ Switch on-screen widget profile ]"),
|
||||
@@ -48,6 +50,7 @@ import UIKit
|
||||
}
|
||||
|
||||
override public func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad()
|
||||
|
||||
setupViews()
|
||||
@@ -58,6 +61,11 @@ import UIKit
|
||||
|
||||
// Register the cell class
|
||||
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
|
||||
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissSelf))
|
||||
tap.delegate = self
|
||||
view.addGestureRecognizer(tap)
|
||||
|
||||
//pass self to the CommandManager
|
||||
CommandManager.shared.viewController = self
|
||||
}
|
||||
@@ -75,21 +83,21 @@ import UIKit
|
||||
//setupConstraints()
|
||||
}
|
||||
|
||||
|
||||
private func setupViews() {
|
||||
|
||||
contentView = UIView(frame: self.view.frame)
|
||||
|
||||
// Set corner radius
|
||||
view.layer.cornerRadius = 20 // Adjust the corner radius
|
||||
view.layer.masksToBounds = true
|
||||
contentView.layer.cornerRadius = 20 // Adjust the corner radius
|
||||
contentView.layer.masksToBounds = true
|
||||
|
||||
// Set up the title label
|
||||
titleLabel.text = SwiftLocalizationHelper.localizedString(forKey: "Toolbox")
|
||||
titleLabel.font = UIFont.boldSystemFont(ofSize: 20) // Adjust font size as needed
|
||||
titleLabel.textColor = UIColor.white // Adjust color as needed
|
||||
titleLabel.textAlignment = .center
|
||||
view.addSubview(titleLabel)
|
||||
contentView.addSubview(titleLabel)
|
||||
|
||||
view.backgroundColor = viewBackgroundColor
|
||||
contentView.backgroundColor = viewBackgroundColor
|
||||
tableView.backgroundColor = .clear
|
||||
tableView.rowHeight = isIPhone() ? 47 : 60
|
||||
tableView.separatorColor = .white.withAlphaComponent(0.33)
|
||||
@@ -107,12 +115,14 @@ import UIKit
|
||||
pinButton.titleLabel?.font = UIFont.systemFont(ofSize: 20)
|
||||
|
||||
// Add subviews
|
||||
view.addSubview(tableView)
|
||||
view.addSubview(addButton)
|
||||
view.addSubview(deleteButton)
|
||||
view.addSubview(editButton)
|
||||
view.addSubview(exitButton)
|
||||
view.addSubview(pinButton)
|
||||
contentView.addSubview(tableView)
|
||||
contentView.addSubview(addButton)
|
||||
contentView.addSubview(deleteButton)
|
||||
contentView.addSubview(editButton)
|
||||
contentView.addSubview(exitButton)
|
||||
contentView.addSubview(pinButton)
|
||||
|
||||
self.view.addSubview(contentView)
|
||||
|
||||
// Setup button targets
|
||||
addButton.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside)
|
||||
@@ -134,14 +144,10 @@ import UIKit
|
||||
}
|
||||
|
||||
@objc public func setupConstraints() {
|
||||
|
||||
guard view.superview != nil else {
|
||||
return
|
||||
}
|
||||
|
||||
NSLayoutConstraint.deactivate(view.constraints)
|
||||
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
@@ -154,47 +160,47 @@ import UIKit
|
||||
NSLayoutConstraint.activate([
|
||||
|
||||
// Set the width and height of the view
|
||||
view.widthAnchor.constraint(equalTo: view.superview!.widthAnchor, multiplier: isIPhone() ? 0.6 : 0.52),
|
||||
view.heightAnchor.constraint(equalTo: view.superview!.heightAnchor, multiplier: 0.93),
|
||||
contentView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: isIPhone() ? 0.6 : 0.52),
|
||||
contentView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.93),
|
||||
// Set the width and height of the view
|
||||
//view.leadingAnchor.constraint(equalTo: view.superview!.leadingAnchor, constant: 60),
|
||||
//view.trailingAnchor.constraint(equalTo: view.superview!.trailingAnchor, constant: -60),
|
||||
|
||||
// Center the view horizontally and vertically
|
||||
view.centerXAnchor.constraint(equalTo: view.superview!.centerXAnchor),
|
||||
view.centerYAnchor.constraint(equalTo: view.superview!.centerYAnchor),
|
||||
contentView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
||||
contentView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
|
||||
|
||||
// ViewTitle constrains
|
||||
titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 13.5),
|
||||
titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
|
||||
titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
|
||||
titleLabel.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor, constant: 13.5),
|
||||
titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
|
||||
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
|
||||
|
||||
|
||||
// TableView constraints
|
||||
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
|
||||
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
|
||||
tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50),
|
||||
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -60),
|
||||
tableView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
|
||||
tableView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
|
||||
tableView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 50),
|
||||
tableView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -60),
|
||||
|
||||
// ExitButton constraints
|
||||
exitButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 25),
|
||||
exitButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
|
||||
exitButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 25),
|
||||
exitButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
|
||||
|
||||
// EditButton constraints
|
||||
editButton.leadingAnchor.constraint(equalTo: exitButton.trailingAnchor, constant: 25),
|
||||
editButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
|
||||
editButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
|
||||
|
||||
// AddButton constraints
|
||||
addButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -25),
|
||||
addButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
|
||||
addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -25),
|
||||
addButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
|
||||
|
||||
// DeleteButton constraints
|
||||
deleteButton.trailingAnchor.constraint(equalTo: addButton.leadingAnchor, constant: -25),
|
||||
deleteButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
|
||||
deleteButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
|
||||
|
||||
// DeleteButton constraints
|
||||
pinButton.trailingAnchor.constraint(equalTo: deleteButton.leadingAnchor, constant: -25),
|
||||
pinButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
|
||||
pinButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -357,8 +363,12 @@ import UIKit
|
||||
else {
|
||||
let command = CommandManager.shared.getAllCommands()[indexPath.row-specialEntries.count]
|
||||
sendKeyboardCommand(command)
|
||||
if !viewPinned {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
||||
self.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
} // dimiss the view in sending mode & the view is not pinned
|
||||
}
|
||||
if !viewPinned { dismiss(animated: false, completion: nil) } // dimiss the view in sending mode & the view is not pinned
|
||||
}
|
||||
else {
|
||||
let specialEntrySelected = indexPath.row < specialEntries.count
|
||||
@@ -372,30 +382,42 @@ import UIKit
|
||||
}
|
||||
|
||||
private func handleSpecialEntries(index:Int){
|
||||
usleep(1000*100)
|
||||
dismiss(animated: true, completion: nil)
|
||||
switch specialEntries[index] as? String {
|
||||
case "widgetLayoutTool":
|
||||
specialEntryDelegate?.openWidgetLayoutTool!()
|
||||
case "widgetSwitchTool":
|
||||
specialEntryDelegate?.switchWidgetProfile?()
|
||||
case "bringUpSoftKeyboard":
|
||||
specialEntryDelegate?.bringUpSoftKeyboard?()
|
||||
case "enterPip":
|
||||
specialEntryDelegate?.enterPip?()
|
||||
case "toggleStatsOverlay":
|
||||
specialEntryDelegate?.toggleStatsOverlay?()
|
||||
case "disconnectAndQuitApp":
|
||||
specialEntryDelegate?.disconnectAndQuitApp?()
|
||||
default: break
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
||||
self.dismiss(animated: false, completion: nil)
|
||||
switch self.specialEntries[index] as? String {
|
||||
case "widgetLayoutTool":
|
||||
self.specialEntryDelegate?.openWidgetLayoutTool!()
|
||||
case "widgetSwitchTool":
|
||||
self.specialEntryDelegate?.openWidgetProfileTable?(pickProfile: false)
|
||||
case "bringUpSoftKeyboard":
|
||||
self.specialEntryDelegate?.bringUpSoftKeyboard?()
|
||||
case "enterPip":
|
||||
self.specialEntryDelegate?.enterPip?()
|
||||
case "toggleStatsOverlay":
|
||||
self.specialEntryDelegate?.toggleStatsOverlay?()
|
||||
case "disconnectAndQuitApp":
|
||||
self.specialEntryDelegate?.disconnectAndQuitApp?()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func sendKeyboardCommand(_ cmd: RemoteCommand) {
|
||||
print("Sending key-value")
|
||||
let keyboardCmdStrings = CommandManager.shared.extractKeyStringsFromComboCommand(from: cmd.cmdString)
|
||||
let keyboardCmdStrings = CommandManager.shared.extractKeyStrings(from: cmd.cmdString)
|
||||
CommandManager.shared.sendKeyComboCommand(keyboardCmdStrings: keyboardCmdStrings!)
|
||||
}
|
||||
|
||||
public func gestureRecognizer(
|
||||
_ gestureRecognizer: UIGestureRecognizer,
|
||||
shouldReceive touch: UITouch
|
||||
) -> Bool {
|
||||
return touch.view == view
|
||||
}
|
||||
|
||||
@objc private func dismissSelf(){
|
||||
dismiss(animated: false, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3031,6 +3031,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"hZw-pC-Qxm.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Pencil Pauses Native Touch\"; ObjectID = \"hZw-pC-Qxm\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Pencil Pauses Native Touch"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "使用笔时暂停多点触控"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "使用筆時暫停多點觸控"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "使用筆時暫停多點觸控"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iel-xl-xd9.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Profile:\"; ObjectID = \"iel-xl-xd9\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
@@ -4009,6 +4039,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"MyP-Aq-htW.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \" Reveal mode \"; ObjectID = \"MyP-Aq-htW\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : " Reveal mode "
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"MzN-ZR-qyM.segmentTitles[0]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; MzN-ZR-qyM.segmentTitles[0] = \"Auto\"; ObjectID = \"MzN-ZR-qyM\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
@@ -4705,6 +4747,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Qr8-Df-7Zu.segmentTitles[0]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; Qr8-Df-7Zu.segmentTitles[0] = \"coexist\"; ObjectID = \"Qr8-Df-7Zu\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "coexist"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Qr8-Df-7Zu.segmentTitles[1]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; Qr8-Df-7Zu.segmentTitles[1] = \"exclusive\"; ObjectID = \"Qr8-Df-7Zu\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "exclusive"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Qs9-iS-YoW.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Divider Position\"; ObjectID = \"Qs9-iS-YoW\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
@@ -4981,6 +5047,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"sch-0e-nfa.segmentTitles[0]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; sch-0e-nfa.segmentTitles[0] = \"display\"; ObjectID = \"sch-0e-nfa\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "display"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sch-0e-nfa.segmentTitles[1]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; sch-0e-nfa.segmentTitles[1] = \"hide\"; ObjectID = \"sch-0e-nfa\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "hide"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"shy-hG-9wY.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Remember Menu Fold/Expand\"; ObjectID = \"shy-hG-9wY\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
@@ -5185,6 +5275,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v5f-j3-pEc.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Double Tap Shorcut\"; ObjectID = \"v5f-j3-pEc\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Double Tap Shorcut"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -5389,6 +5509,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"WLg-7a-5wu.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Collected widgets \"; ObjectID = \"WLg-7a-5wu\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Collected widgets "
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x9i-JN-FSn.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Roll factor\"; ObjectID = \"x9i-JN-FSn\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
|
||||
Reference in New Issue
Block a user