diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index 2d94ccce..ac316243 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -1359,6 +1359,26 @@ + + + + + + + + + + + + + + + @@ -1400,7 +1420,6 @@ - @@ -1450,6 +1469,8 @@ + + @@ -2024,8 +2045,8 @@ - - + + @@ -2195,6 +2216,7 @@ + @@ -2207,7 +2229,7 @@ - + @@ -2216,13 +2238,13 @@ - + - + - + diff --git a/VoidLink/Base.lproj/iPhone.storyboard b/VoidLink/Base.lproj/iPhone.storyboard index af94c953..aa5aa306 100644 --- a/VoidLink/Base.lproj/iPhone.storyboard +++ b/VoidLink/Base.lproj/iPhone.storyboard @@ -621,8 +621,8 @@ - - + + @@ -1703,6 +1703,26 @@ + + + + + + + + + + + + + + + @@ -2019,7 +2039,7 @@ - + @@ -2039,7 +2059,7 @@ - + @@ -2061,7 +2081,7 @@ - + @@ -2181,6 +2201,8 @@ + + @@ -2223,6 +2245,7 @@ + @@ -2234,19 +2257,19 @@ - + - + - + - + diff --git a/VoidLink/Database/DataManager.h b/VoidLink/Database/DataManager.h index 792b9eef..d0ee1291 100644 --- a/VoidLink/Database/DataManager.h +++ b/VoidLink/Database/DataManager.h @@ -110,6 +110,7 @@ typedef NS_ENUM(NSInteger, WidgetSizeTransition) { framePacingMode:(NSInteger)framePacingMode sendDummyEvent:(BOOL)sendDummyEvent rememberFoldState:(BOOL)rememberFoldState + singleTapSensitivy:(CGFloat)singleTapSensitivy backgroundSessionTimer:(NSInteger)backgroundSessionTimer; - (NSArray*) getHosts; diff --git a/VoidLink/Database/DataManager.m b/VoidLink/Database/DataManager.m index efd68206..461a1ab7 100644 --- a/VoidLink/Database/DataManager.m +++ b/VoidLink/Database/DataManager.m @@ -105,6 +105,7 @@ framePacingMode:(NSInteger)framePacingMode sendDummyEvent:(BOOL)sendDummyEvent rememberFoldState:(BOOL)rememberFoldState + singleTapSensitivy:(CGFloat)singleTapSensitivy backgroundSessionTimer:(NSInteger)backgroundSessionTimer{ [_managedObjectContext performBlockAndWait:^{ @@ -160,6 +161,7 @@ settingsToSave.renderingBackend = [NSNumber numberWithInteger:renderingBackend]; settingsToSave.framePacingMode = [NSNumber numberWithInteger:framePacingMode]; settingsToSave.sendDummyEvent = sendDummyEvent; + settingsToSave.singleTapSensitivity = [NSNumber numberWithDouble:singleTapSensitivy]; settingsToSave.rememberFoldState = rememberFoldState; [self saveData]; }]; diff --git a/VoidLink/Database/TemporarySettings.h b/VoidLink/Database/TemporarySettings.h index 15f36b8a..7fedcfc7 100644 --- a/VoidLink/Database/TemporarySettings.h +++ b/VoidLink/Database/TemporarySettings.h @@ -78,6 +78,7 @@ @property (nonatomic, retain) NSNumber * renderingBackend; @property (nonatomic) BOOL sendDummyEvent; @property (nonatomic) BOOL rememberFoldState; +@property (nonatomic, retain) NSNumber * singleTapSensitivity; @property (nonatomic, retain) NSNumber * framePacingMode; - (id) initFromSettings:(Settings*)settings; diff --git a/VoidLink/Database/TemporarySettings.m b/VoidLink/Database/TemporarySettings.m index ffe11d4a..a0218eb7 100644 --- a/VoidLink/Database/TemporarySettings.m +++ b/VoidLink/Database/TemporarySettings.m @@ -133,6 +133,7 @@ self.gyroBiasX = settings.gyroBiasX; self.gyroBiasY = settings.gyroBiasY; self.gyroBiasZ = settings.gyroBiasZ; + self.singleTapSensitivity = settings.singleTapSensitivity; self.backgroundSessionTimer = settings.backroundSessionTimer; #endif self.uniqueId = settings.uniqueId; diff --git a/VoidLink/Input/CommandManager.swift b/VoidLink/Input/CommandManager.swift index 14384f87..76bdcf6b 100644 --- a/VoidLink/Input/CommandManager.swift +++ b/VoidLink/Input/CommandManager.swift @@ -115,11 +115,14 @@ import UIKit "OSCBACK" ] - @objc public static let touchPadCmds: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD", "DS4TOUCH", "MOUSEPAD", "MOUSEWHEEL", "WHEEL", "DPAD", "TRACKBALL", "WASDPAD", "ARROWPAD"] + @objc public static let touchPadCmds: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD","LTPAD", "RTPAD", "DS4TOUCH", "MOUSEPAD", "MOUSEWHEEL", "WHEEL", "DPAD", "TRACKBALL", "WASDPAD", "ARROWPAD"] @objc public static let directionPads: [String] = ["DPAD", "WASDPAD", "ARROWPAD"] @objc public static let stickTouchPads: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD"] @objc public static let nonVectorStickPads: [String] = ["LSPAD", "RSPAD"] - @objc public static let functionalButtonCmds: [String] = ["SETTINGS","GYRO","ACCEL","MOTION"] + @objc public static let verticalTouchPads: [String] = ["LTPAD", "RTPAD", "MOUSEWHEEL", "WHEEL"] + @objc public static let bidirectionalVerticalTouchPads: [String] = ["LTPAD", "RTPAD", "MOUSEWHEEL", "WHEEL"] + @objc public static let functionalButtonCmds: [String] = ["SETTINGS"] + @objc public static let motionControlButtonCmds: [String] = ["GYRO","ACCEL","MOTION"] // @objc public static let specialGameWidgets: [String] = ["YSRSV", "YSLT", "YSRT", "YSRB", "YSB", "YSRT2", "YSRB2", "YSB2", "YSEM", "YSML", "YSMR", "YSWASD"] @@ -464,6 +467,7 @@ import UIKit CommandManager.oscButtonMappings.keys.map { $0 as String }, CommandManager.mouseButtonMappings.keys.map { $0 as String }, CommandManager.functionalButtonCmds.map { $0 as String }, + CommandManager.motionControlButtonCmds.map { $0 as String }, CommandManager.touchPadCmds.map { $0 as String } ] .lazy diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 4a1d067b..e84e6bc2 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -63,6 +63,7 @@ import UIKit @objc public var identifier: String = "" private var buttonString: String = "" private var functionalButtonString: String = "" + private var motionControlButtonString: String = "" private var touchPadString: String = "" // super combo key string set @objc public var comboButtonStrings: [String] = [] @@ -104,8 +105,18 @@ import UIKit // for all touchPad or buttons hybrid with touchPads @objc public var hasStickIndicator: Bool = false - @objc public var hasSensitivityTweak: Bool = false - + @objc public var hasSensitivityX: Bool = false + @objc public var sensitivityXMin: CGFloat = 0 + @objc public var sensitivityXMAX: CGFloat = 8 + @objc public var hasSensitivityY: Bool = false + @objc public var sensitivityYMin: CGFloat = 0 + @objc public var sensitivityYMAX: CGFloat = 8 + @objc public var hasAutoTap: Bool = false + @objc public var isMousePad: Bool = false + @objc public var hasTrackBall: Bool = false + @objc public var isFuncationalButton: Bool = false + @objc public var hasHapticFeedback: Bool = false + // for all stick pads @objc public var minStickOffset: CGFloat = 0 public let stickMaxOffset: CGFloat = 0x7FFE @@ -200,7 +211,7 @@ import UIKit @objc init(cmdString: String, buttonLabel: String, shape:String) { - + self.cmdString = cmdString self.touchPadString = "" @@ -223,19 +234,13 @@ import UIKit else {self.widgetType = WidgetTypeEnum.button} let touchPadString = Set(comboStrings).intersection(Set(CommandManager.touchPadCmds)).first ?? "" + self.comboButtonStrings = comboStrings.filter{$0 != touchPadString} self.touchPadString = touchPadString self.buttonString = self.comboButtonStrings.first ?? "" self.functionalButtonString = Set(self.comboButtonStrings).intersection(Set(CommandManager.functionalButtonCmds)).first ?? "" - - // let stickAndMouseTouchpads = ["LSPAD", "RSPAD", "LSVPAD", "RSVPAD", "MOUSEPAD"] - let nonVectorStickPads = ["LSPAD", "RSPAD"] - // if CommandManager.touchPadCmds.contains(self.touchPadString) {self.hasSensitivityTweak = true} - self.hasSensitivityTweak = CommandManager.touchPadCmds.contains(self.touchPadString) - - // if nonVectorStickPads.contains(self.touchPadString) && widgetType == WidgetTypeEnum.touchPad {self.hasStickIndicator = true} - self.hasStickIndicator = nonVectorStickPads.contains(self.touchPadString) && widgetType == WidgetTypeEnum.touchPad - + self.motionControlButtonString = Set(self.comboButtonStrings).intersection(Set(CommandManager.motionControlButtonCmds)).first ?? "" + switch self.cmdString { case "LSPAD", "LSVPAD": self.comboButtonStrings = ["OSCL3"] @@ -251,8 +256,7 @@ import UIKit else { self.widgetType = WidgetTypeEnum.button // legacy combo button connected by "+" } - - + print("widgetType: \(self.widgetType)") print("touchPadString: \(self.touchPadString)") for comboButtonString in comboButtonStrings { @@ -296,6 +300,9 @@ import UIKit self.oscProfile = oscProfileMan.getSelectedProfile() super.init(frame: .zero) + // helps widget panel to hide/show stacks + self.accessWidgetAttributes() + upIndicator = createLrudDirectionLayer() upIndicator.anchorPoint = CGPoint(x: 0.5, y: 1) downIndicator = createLrudDirectionLayer() @@ -310,12 +317,11 @@ import UIKit self.mouseButtonAction = MouseButtonAction.noClick self.buttonMode = ButtonMode.regular.rawValue } + if !self.motionControlButtonString.isEmpty { + self.buttonMode = ButtonMode.tapToToggle.rawValue + } if !self.functionalButtonString.isEmpty { self.buttonMode = ButtonMode.movable.rawValue - if (self.functionalButtonString == "GYRO" || - self.functionalButtonString == "ACCEL" || - self.functionalButtonString == "MOTION") - {self.buttonMode = ButtonMode.tapToToggle.rawValue} } } @@ -327,6 +333,20 @@ import UIKit fatalError("init(coder:) has not been implemented") } + @objc public func accessWidgetAttributes(){ + self.hasStickIndicator = CommandManager.nonVectorStickPads.contains(self.touchPadString) && widgetType == WidgetTypeEnum.touchPad + self.hasSensitivityX = CommandManager.touchPadCmds.contains(self.touchPadString) && !CommandManager.verticalTouchPads.contains(self.touchPadString) + self.hasSensitivityY = CommandManager.touchPadCmds.contains(self.touchPadString) + if CommandManager.bidirectionalVerticalTouchPads.contains(self.touchPadString){ + self.sensitivityYMin = -4 + self.sensitivityYMAX = 4 + } + self.hasAutoTap = self.widgetType == WidgetTypeEnum.button && self.functionalButtonString != "" + self.isMousePad = self.touchPadString == "MOUSEPAD" && widgetType == WidgetTypeEnum.touchPad + self.hasTrackBall = self.touchPadString == "TRACKBALL" + self.isFuncationalButton = self.functionalButtonString != "" + self.hasHapticFeedback = !self.comboButtonStrings.isEmpty + } // ====================================================================================================== @objc public func setupAutoTapTimer() { @@ -1095,8 +1115,8 @@ import UIKit private func handleButtonDown() { if !OnScreenWidgetView.editMode {self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings)} - if !OnScreenWidgetView.editMode, !self.functionalButtonString.isEmpty {self.handleFunctionalButtonDown()} - + if !OnScreenWidgetView.editMode, !self.motionControlButtonString.isEmpty {self.handleMotionControlButtonDown()} + // sadflkasdfl; if OnScreenWidgetView.buttonVisualFeedbackEnabled { @@ -1118,6 +1138,10 @@ import UIKit private func handleButtonUp() { if !OnScreenWidgetView.editMode {self.sendComboButtonsUpEvent(comboStrings: self.comboButtonStrings)} + if !OnScreenWidgetView.editMode && !self.motionControlButtonString.isEmpty{ + self.handleMotionControlButtonUp() + } + if !OnScreenWidgetView.editMode && !self.functionalButtonString.isEmpty{ self.handleFunctionalButtonUp() } @@ -1171,7 +1195,7 @@ import UIKit //========================================================================================================== - //=========================================send on screen controller stick events + //=========================================send on screen controller stick/trigger events private func touchInputToStickInput(input: CGFloat) -> CGFloat{ let target = stickMaxOffset * input / stickInputScale return fmax(fmin(target, stickMaxOffset),-stickMaxOffset) @@ -1216,6 +1240,15 @@ import UIKit } self.mixInputDelegate?.mixLeftStickAndGyroInput(x: targetX, y: targetY) } + + private func sendLeftTriggerTouchPadEvent(inputY: CGFloat){ + self.onScreenControls.updateLeftTrigger(UInt8(max(min(inputY,255),0))) + } + + private func sendRightTriggerTouchPadEvent(inputY: CGFloat){ + self.onScreenControls.updateRightTrigger(UInt8(max(min(inputY,255),0))) + } + //========================================================================================================== private func sendOscButtonDownEvent(oscString: String){ @@ -1627,6 +1660,17 @@ import UIKit self.updateTouchLocation(touch: touches.first!) self.sendRightStickTouchPadEvent(inputX: self.deltaX*1.5167*self.sensitivityFactorX, inputY: self.deltaY*1.5167*self.sensitivityFactorY) } + case "LTPAD": + DispatchQueue.global(qos: .userInteractive).async { + self.updateTouchLocation(touch: touches.first!) + self.sendLeftTriggerTouchPadEvent(inputY: (-self.offSetY*4.5*self.sensitivityFactorY)) + } + case "RTPAD": + DispatchQueue.global(qos: .userInteractive).async { + self.updateTouchLocation(touch: touches.first!) + self.sendRightTriggerTouchPadEvent(inputY: (-self.offSetY*4.5*self.sensitivityFactorY)) + } + break case "DPAD", "WASDPAD", "ARROWPAD": self.updateTouchLocation(touch: touches.first!) handleLrudTouchMove() @@ -1642,8 +1686,8 @@ import UIKit } } - private func handleFunctionalButtonDown(){ - switch self.functionalButtonString { + private func handleMotionControlButtonDown(){ + switch self.motionControlButtonString { case "GYRO": self.functionalButtonDelegate?.startGyroUpdate() case "ACCEL": @@ -1656,12 +1700,9 @@ import UIKit } } - private func handleFunctionalButtonUp(){ + private func handleMotionControlButtonUp(){ let longPressed = CACurrentMediaTime() - self.touchTapTimeStamp > 0.3 - switch self.functionalButtonString { - case "SETTINGS": - if longPressed, buttonMode == ButtonMode.movable.rawValue {break} - NotificationCenter.default.post(name: Notification.Name("SettingsOverlayButtonPressedNotification"), object:nil) // inform layout tool controller to fetch button size factors. self will be passed as the object of the notification + switch self.motionControlButtonString { case "GYRO": self.functionalButtonDelegate?.stopGyroUpdate(interruption: false) case "ACCEL": @@ -1674,6 +1715,18 @@ import UIKit } } + private func handleFunctionalButtonUp(){ + let longPressed = CACurrentMediaTime() - self.touchTapTimeStamp > 0.3 + switch self.functionalButtonString { + case "SETTINGS": + if longPressed, buttonMode == ButtonMode.movable.rawValue {break} + NotificationCenter.default.post(name: Notification.Name("SettingsOverlayButtonPressedNotification"), object:nil) // inform layout tool controller to fetch button size factors. self will be passed as the object of the notification + default: + break + } + } + + private func clearRightStickTouchPadFlag(){ let mixRightStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick && oscProfile.yawPitchToRightStick) @@ -1764,6 +1817,10 @@ import UIKit self.clearLeftStickTouchPadFlag() case "RSVPAD": self.clearRightStickTouchPadFlag() + case "LTPAD": + self.onScreenControls.updateLeftTrigger(0x00) + case "RTPAD": + self.onScreenControls.updateRightTrigger(0x00) case "WASDPAD": LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["W"]!,Int8(KEY_ACTION_UP), 0) LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["A"]!,Int8(KEY_ACTION_UP), 0) diff --git a/VoidLink/Input/RelativeTouchHandler.m b/VoidLink/Input/RelativeTouchHandler.m index 275b29e4..4a6f2585 100644 --- a/VoidLink/Input/RelativeTouchHandler.m +++ b/VoidLink/Input/RelativeTouchHandler.m @@ -33,6 +33,8 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; UITouch* touchLockedForMouseMove; + CADisplayLink *displayLink; + #if TARGET_OS_TV UIGestureRecognizer* remotePressRecognizer; UIGestureRecognizer* remoteLongPressRecognizer; @@ -65,6 +67,9 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; screenWidthWithThreshold = CGRectGetWidth([[UIScreen mainScreen] bounds]) - EDGE_TOLERANCE; self->touchPointSpawnedAtUpperScreenEdge = false; + // self->displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkCallback:)]; + // [self->displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; + #if TARGET_OS_TV remotePressRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(remoteButtonPressed:)]; remotePressRecognizer.allowedPressTypes = @[@(UIPressTypeSelect)]; @@ -132,8 +137,13 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; return hypotf(originalPoint.x - currentPoint.x, originalPoint.y - currentPoint.y) <= 300; } +- (BOOL)isAdjacentPoints:(CGPoint)currentPoint from:(CGPoint)originalPoint { + bool isAdjacent = hypotf(originalPoint.x - currentPoint.x, originalPoint.y - currentPoint.y) <= hypot(currentSettings.singleTapSensitivity.doubleValue, currentSettings.singleTapSensitivity.doubleValue); + return isAdjacent; +} - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + firstTouchMoved = false; //check if touch point is spawned on the left or right upper half screen edges, this is the highest priority CGPoint initialPoint = [[touches anyObject] locationInView:streamView]; @@ -142,7 +152,6 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; return; } - firstTouchMoved = false; touchPointSpawnedAtUpperScreenEdge = false; // reset this flag immediately if we get a touch event passing the check above, this fixes irresponsive touch after closing the command tool menu. if([[event allTouches] count] == 2 && ![self isOnScreenWidgetViewBeingPressed] && ![self isOnScreenControllerBeingPressed:[event allTouches]]){ @@ -206,7 +215,9 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; // NSLog(@"%f touchesMoved callback, locked touch: %llu", CACurrentMediaTime(), (uintptr_t)touchLockedForMouseMove); if([touches containsObject:touchLockedForMouseMove]){ - mousePointerMoved = true; + if(!mousePointerMoved && ![self isAdjacentPoints:initialMousePointerLocation from:[touchLockedForMouseMove locationInView:streamView]]){ + mousePointerMoved = true; + } [self sendMouseMoveEvent:touchLockedForMouseMove]; } } @@ -242,10 +253,18 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; touchPointSpawnedAtUpperScreenEdge = false; } +- (void)displayLinkCallback:(CADisplayLink *)link { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ + if(self->touchLockedForMouseMove && !self->mousePointerMoved){ + CGPoint testpoint = [self->touchLockedForMouseMove locationInView:self->streamView]; + NSLog(@"displayLinkCallback %f: %f, %f", CACurrentMediaTime(), testpoint.x, testpoint.y); + //[self sendMouseMoveEvent:touchLockedForMouseMove]; + } + }); +} - (void)sendMouseMoveEvent:(UITouch* )touch{ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ - if(self->touchPointSpawnedAtUpperScreenEdge) return; // we're done here. this touch event will not be sent to the remote PC. CGPoint currentLocation = [touch locationInView:self->streamView]; @@ -261,8 +280,9 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; int deltaY = (currentLocation.y - self->latestMousePointerLocation.y) * 1.35 * self->currentSettings.mousePointerVelocityFactor.floatValue; if (deltaX != 0 || deltaY != 0) { - LiSendMouseMoveEvent(deltaX, deltaY); self->latestMousePointerLocation = currentLocation; + if(self->touchPointSpawnedAtUpperScreenEdge) return; // we're done here. this touch event will not be sent to the remote PC. + LiSendMouseMoveEvent(deltaX, deltaY); } } }); diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index a156178f..1d9dce47 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -66,6 +66,7 @@ + diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 7fed9c97..39e1fb1b 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -162,25 +162,48 @@ } } }, - " Profile: %@ Widget: %@" : { + " Profile: %@ Alias: %@ Command: %@" : { "extractionState" : "manual", "localizations" : { "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : " 当前布局: %@ 当前组件: %@" + "value" : " 当前布局: %@ 别名: %@ 命令: %@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : " 版面配置:%@ 小工具:%@" + "value" : " 版面配置: %@ 别名: %@ 命令: %@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : " 版面配置:%@ 小工具:%@" + "value" : " 版面配置: %@ 别名: %@ 命令: %@" + } + } + } + }, + " Profile: %@ Widget: %@" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : " 当前布局: %@ 控件: %@" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : " 版面配置: %@ 小工具: %@" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : " 版面配置: %@ 小工具: %@" } } } diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index 1609a629..eb0f67e8 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -612,7 +612,8 @@ bool noValidTouchPadString = ![CommandManager.touchPadCmds containsObject:cmdString]; bool noValidOscButtonString = ![CommandManager.oscButtonMappings.allKeys containsObject:cmdString]; bool noValidFunctionalButtonString = ![CommandManager.functionalButtonCmds containsObject:cmdString]; - bool paramInvalid = noValidKeyboardString && noValidMouseButtonString && noValidTouchPadString && noValidOscButtonString && noValidFunctionalButtonString && noValidSuperComboButtonString; + bool noValidMotionControlButtonString = ![CommandManager.motionControlButtonCmds containsObject:cmdString]; + bool paramInvalid = noValidKeyboardString && noValidMouseButtonString && noValidTouchPadString && noValidOscButtonString && noValidFunctionalButtonString && noValidSuperComboButtonString && noValidMotionControlButtonString; if([buttonLabel isEqualToString:@""]) widgetInitParams[@"buttonLabel"] = [[cmdString lowercaseString] capitalizedString]; @@ -761,51 +762,37 @@ [self autoFitLabel:self.currentProfileLabel]; self.currentProfileLabel.textAlignment = NSTextAlignmentLeft; [self.currentProfileLabel setText: - [LocalizationHelper localizedStringForKey:@" Profile: %@ Widget: %@", + [LocalizationHelper localizedStringForKey:@" Profile: %@ Alias: %@ Command: %@", [profilesManager getSelectedProfile].name, - selectedWidgetView.widgetLabel]]; + selectedWidgetView.widgetLabel, selectedWidgetView.cmdString]]; self.undoButton.alpha = selectedWidgetView.layoutChanges.count>1 ? 1.0 : 0.3; [self.layoutOSC updateGuidelinesForOnScreenWidget:self->selectedWidgetView]; // shows guideline immediately when widget is tapped - - - // hide irrelevant stacks - self.autoTapStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton; - self.buttonModeStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton; - - bool showSensitivityFactorStack = selectedWidgetView.hasSensitivityTweak; - bool showStickIndicatorOffsetStack = selectedWidgetView.hasStickIndicator; - self.sensitivityXStack.hidden = !showSensitivityFactorStack || [selectedWidgetView.cmdString containsString:@"MOUSEWHEEL"] || [selectedWidgetView.cmdString containsString:@"WHEEL"]; - self.sensitivityYStack.hidden = !showSensitivityFactorStack; - if(showSensitivityFactorStack){ - if([selectedWidgetView.cmdString containsString:@"MOUSEWHEEL"] || [selectedWidgetView.cmdString containsString:@"WHEEL"]){ - [self.sensitivityYSlider setMinimumValue:-4]; - [self.sensitivityYSlider setMaximumValue:4]; - } - else{ - [self.sensitivityYSlider setMinimumValue:0]; - [self.sensitivityYSlider setMaximumValue:8]; - } - } + // stack & values + [selectedWidgetView accessWidgetAttributes]; - self.stickIndicatorOffsetStack.hidden = !showStickIndicatorOffsetStack; - self.mouseDownButtonStack.hidden = !([selectedWidgetView.cmdString containsString:@"MOUSEPAD"] && selectedWidgetView.widgetType == WidgetTypeEnumTouchPad); - self.decelerationRateStack.hidden = !([selectedWidgetView.cmdString containsString:@"TRACKBALL"] && selectedWidgetView.widgetType == WidgetTypeEnumTouchPad); - - // setup values - - - if(showSensitivityFactorStack){ + self.sensitivityXStack.hidden = !selectedWidgetView.hasSensitivityX; + if(selectedWidgetView.hasSensitivityX){ + [self.sensitivityXSlider setMinimumValue:selectedWidgetView.sensitivityXMin]; + [self.sensitivityXSlider setMaximumValue:selectedWidgetView.sensitivityXMAX]; [self.sensitivityXSlider setValue:self->selectedWidgetView.sensitivityFactorX]; [self autoFitLabel:self.sensitivityXLabel]; + } + + self.sensitivityYStack.hidden = !selectedWidgetView.hasSensitivityY; + if(selectedWidgetView.hasSensitivityY){ + [self.sensitivityYSlider setMinimumValue:selectedWidgetView.sensitivityYMin]; + [self.sensitivityYSlider setMaximumValue:selectedWidgetView.sensitivityYMAX]; [self.sensitivityXLabel setText:[LocalizationHelper localizedStringForKey:@"SensitivityX: %.2f", self->selectedWidgetView.sensitivityFactorX]]; [self autoFitLabel:self.sensitivityYLabel]; [self.sensitivityYSlider setValue:self->selectedWidgetView.sensitivityFactorY]; [self.sensitivityYLabel setText:[LocalizationHelper localizedStringForKey:@"SensitivityY: %.2f", self->selectedWidgetView.sensitivityFactorY]]; } - if(showStickIndicatorOffsetStack){ + + self.stickIndicatorOffsetStack.hidden = !selectedWidgetView.hasStickIndicator; + if(selectedWidgetView.hasStickIndicator){ // illustrating the indicator offset, [self hideStickIndicators]; selectedWidgetView.touchBeganLocation = CGPointMake(CGRectGetWidth(selectedWidgetView.frame)/2, CGRectGetHeight(selectedWidgetView.frame)/4); @@ -833,30 +820,28 @@ [self autoFitLabel:self.widgetBorderWidthLabel]; [self widgetBorderWidthSliderMoved:self.widgetBorderWidthSlider]; + self.autoTapStack.hidden = !selectedWidgetView.hasAutoTap; [self.autoTapSlider setValue:self->selectedWidgetView.autoTapInterval]; [self autoFitLabel:self.autoTapLabel]; [self autoTapSliderMoved:self.autoTapSlider]; + self.decelerationRateStack.hidden = !selectedWidgetView.hasTrackBall; [self.decelerationRateSlider setValue:selectedWidgetView.trackballDecelerationRate]; [self autoFitLabel:self.decelerationRateLabel]; [self decelerationRateSliderMoved:self.decelerationRateSlider]; + self.mouseDownButtonStack.hidden = !selectedWidgetView.isMousePad; self.mouseButtonDownSelector.selectedSegmentIndex = selectedWidgetView.mouseButtonAction; - NSSet *specialCmds = [NSSet setWithArray:CommandManager.functionalButtonCmds]; - NSSet *comboButtonStrings = [NSSet setWithArray:widgetView.comboButtonStrings]; - bool isFuncationalButton = [specialCmds intersectsSet:comboButtonStrings]; - [self.buttonModeSelector setEnabled:!isFuncationalButton forSegmentAtIndex:0]; - [self.buttonModeSelector setEnabled:!isFuncationalButton forSegmentAtIndex:1]; - if(isFuncationalButton) [self.autoTapStack setHidden:YES]; + self.buttonModeStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton; + [self.buttonModeSelector setEnabled:!selectedWidgetView.isFuncationalButton forSegmentAtIndex:0]; + [self.buttonModeSelector setEnabled:!selectedWidgetView.isFuncationalButton forSegmentAtIndex:1]; [self.buttonModeSelector setSelectedSegmentIndex:selectedWidgetView.buttonMode]; [self autoFitStack:self.widgetPanelStack]; if([self isIPhone]){ - self.vibrationStyleStack.hidden = - [widgetView.cmdString containsString:@"MOUSEPAD"] || - [widgetView.cmdString containsString:@"TRACKBALL"]; + self.vibrationStyleStack.hidden = !selectedWidgetView.hasHapticFeedback; [self autoFitStack:self.widgetPanelStack]; self.vibrationStyleSelector.selectedSegmentIndex = self->selectedWidgetView.vibrationStyle; } @@ -884,7 +869,7 @@ [self autoFitLabel:self.currentProfileLabel]; self.currentProfileLabel.textAlignment = NSTextAlignmentLeft; [self.currentProfileLabel setText: - [LocalizationHelper localizedStringForKey:@" Profile: %@ Widget: %@", + [LocalizationHelper localizedStringForKey:@" Profile: %@ Widget: %@", [profilesManager getSelectedProfile].name, selectedControllerLayer.name]]; diff --git a/VoidLink/ViewControllers/SettingsViewController.h b/VoidLink/ViewControllers/SettingsViewController.h index 07abf8e2..595e1014 100644 --- a/VoidLink/ViewControllers/SettingsViewController.h +++ b/VoidLink/ViewControllers/SettingsViewController.h @@ -166,6 +166,9 @@ @property (strong, nonatomic) IBOutlet UIStackView *gyroToStickMinOffsetStack; @property (strong, nonatomic) IBOutlet UISlider *gyroToStickMinOffsetSlider; +@property (strong, nonatomic) IBOutlet UIStackView *singleTapSensitivityStack; +@property (strong, nonatomic) IBOutlet UISlider *singleTapSensitivitySlider; + @property (strong, nonatomic) IBOutlet UIStackView *testStack; @property (nonatomic, strong) MicHandler *micHandler; diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 1240548e..ce939afb 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -696,6 +696,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self addSetting:self.pointerVelocityDividerStack ofId:@"pointerVelocityDividerStack" withInfoTag:YES withDynamicLabel:YES to:touchAndControlSection]; [self addSetting:self.pointerVelocityFactorStack ofId:@"pointerVelocityFactorStack" withInfoTag:YES withDynamicLabel:YES to:touchAndControlSection]; [self addSetting:self.mousePointerVelocityStack ofId:@"mousePointerVelocityStack" withInfoTag:NO withDynamicLabel:YES to:touchAndControlSection]; + [self addSetting:self.singleTapSensitivityStack ofId:@"singleTapSensitivityStack" withInfoTag:NO withDynamicLabel:YES to:touchAndControlSection]; [self addSetting:self.onScreenWidgetStack ofId:@"onScreenWidgetStack" withInfoTag:YES withDynamicLabel:YES to:touchAndControlSection]; [self addSetting:self.buttonVisualFeedbackStack ofId:@"buttonVisualFeedbackStack" withInfoTag:NO withDynamicLabel:NO to:touchAndControlSection]; [self addSetting:self.swapAbxyStack ofId:@"swapAbaxyStack" withInfoTag:NO withDynamicLabel:NO to:touchAndControlSection]; @@ -1649,7 +1650,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self.buttonVisualFeedbackSwitch setOn:self->tempSettings.buttonVisualFeedback]; [self.gyroModeSelector setSelectedSegmentIndex:self->tempSettings.gyroMode.intValue]; - [self.gyroSensitivitySlider setValue: (uint16_t)(self->tempSettings.gyroSensitivity.floatValue * 100) animated:YES]; // Load old setting. + [self.gyroSensitivitySlider setValue: (uint16_t)(self->tempSettings.gyroSensitivity.floatValue * 100) animated:NO]; // Load old setting. [self.gyroSensitivitySlider addTarget:self action:@selector(gyroSensitivitySliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. [self gyroSensitivitySliderMoved:self.gyroSensitivitySlider]; @@ -1690,7 +1691,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self.bitrateSlider setMinimumValue:0]; [self.bitrateSlider setMaximumValue:(sizeof(bitrateTable) / sizeof(*bitrateTable)) - 1]; - [self.bitrateSlider setValue:[self getSliderValueForBitrate:self->_bitrate] animated:YES]; + [self.bitrateSlider setValue:[self getSliderValueForBitrate:self->_bitrate] animated:NO]; [self.bitrateSlider addTarget:self action:@selector(bitrateSliderMoved) forControlEvents:UIControlEventValueChanged]; [self updateBitrateText]; [self updateResolutionDisplayLabel]; @@ -1763,12 +1764,12 @@ BOOL isCustomResolution(int resolutionSelected) { //TouchMode & OSC Related Settings: // pointer veloc setting, will be enable/disabled by touchMode - [self.pointerVelocityModeDividerSlider setValue: (uint8_t)(self->tempSettings.pointerVelocityModeDivider.floatValue * 100) animated:YES]; // Load old setting. + [self.pointerVelocityModeDividerSlider setValue: (uint8_t)(self->tempSettings.pointerVelocityModeDivider.floatValue * 100) animated:NO]; // Load old setting. [self.pointerVelocityModeDividerSlider addTarget:self action:@selector(pointerVelocityModeDividerSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. [self pointerVelocityModeDividerSliderMoved:self.pointerVelocityModeDividerSlider]; // init pointer veloc setting, will be enable/disabled by touchMode - [self.touchPointerVelocityFactorSlider setValue: [self map_SliderValue_fromVelocFactor: self->tempSettings.touchPointerVelocityFactor.floatValue] animated:YES]; // Load old setting. + [self.touchPointerVelocityFactorSlider setValue: [self map_SliderValue_fromVelocFactor: self->tempSettings.touchPointerVelocityFactor.floatValue] animated:NO]; // Load old setting. [self.touchPointerVelocityFactorSlider addTarget:self action:@selector(touchPointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. [self touchPointerVelocityFactorSliderMoved:self.touchPointerVelocityFactorSlider]; @@ -1777,10 +1778,13 @@ BOOL isCustomResolution(int resolutionSelected) { // [self.asyncNativeTouchPrioritySelector addTarget:self action:@selector(asyncNativeTouchPriorityChanged) forControlEvents:UIControlEventValueChanged]; // init relative touch mouse pointer veloc setting, will be enable/disabled by touchMode - [self.mousePointerVelocityFactorSlider setValue:[self map_SliderValue_fromVelocFactor: self->tempSettings.mousePointerVelocityFactor.floatValue] animated:YES]; // Load old setting. + [self.mousePointerVelocityFactorSlider setValue:[self map_SliderValue_fromVelocFactor: self->tempSettings.mousePointerVelocityFactor.floatValue] animated:NO]; // Load old setting. [self.mousePointerVelocityFactorSlider addTarget:self action:@selector(mousePointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. [self mousePointerVelocityFactorSliderMoved:self.mousePointerVelocityFactorSlider]; - + + [self.singleTapSensitivitySlider setValue:self->tempSettings.singleTapSensitivity.doubleValue animated:NO]; + [self.singleTapSensitivitySlider addTarget:self action:@selector(singleTapSensitivitySliderMoved:) forControlEvents:(UIControlEventValueChanged)]; + [self singleTapSensitivitySliderMoved:self.singleTapSensitivitySlider]; // these settings will be affected by onscreenControl & touchMode, must be loaded before them. // NSLog(@"osc tool fingers setting test: %d", currentSettings.oscLayoutToolFingers.intValue); @@ -1798,7 +1802,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self onScreenWidgetChanged]; // touch move event interval for native-touch. - [self.touchMoveEventIntervalSlider setValue:self->tempSettings.touchMoveEventInterval.intValue animated:YES]; // Load old setting. + [self.touchMoveEventIntervalSlider setValue:self->tempSettings.touchMoveEventInterval.intValue animated:NO]; // Load old setting. [self.touchMoveEventIntervalSlider addTarget:self action:@selector(touchMoveEventIntervalSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. [self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider]; @@ -1895,7 +1899,7 @@ BOOL isCustomResolution(int resolutionSelected) { if (!self->_mainFrameViewController.settingsExpandedInStreamView) { [self invokeOscLayout]; // Don't open osc layout tool immediately during streaming } - + [self findDynamicLabelFromStack:self.onScreenWidgetStack].text = [self isCustomOswEnabled] ? [LocalizationHelper localizedStringForKey:@"%d finger tap", self->oswLayoutFingers] : @""; [self handleOswGestureChange];}]; @@ -2195,6 +2199,11 @@ BOOL isCustomResolution(int resolutionSelected) { [self findDynamicLabelFromStack:_mousePointerVelocityStack].text = [NSString stringWithFormat:@" %d%% ",(uint16_t)[self map_velocFactorDisplay_fromSliderValue:sender.value]]; } +- (void) singleTapSensitivitySliderMoved:(UISlider* )sender { + [self findDynamicLabelFromStack:_singleTapSensitivityStack].text = [NSString stringWithFormat:@" %.1f ",sender.value]; +} + + - (uint32_t) getScreenEdgeFromSelector { switch (self.slideToSettingsScreenEdgeSelector.selectedSegmentIndex) { case 0: return UIRectEdgeLeft; @@ -2254,6 +2263,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self setHidden:!isNativeTouch forStack:self.pointerVelocityFactorStack]; [self setHidden:!(sender.selectedSegmentIndex == RelativeTouch) forStack:self.mousePointerVelocityStack]; + [self setHidden:!(sender.selectedSegmentIndex == RelativeTouch) forStack:self.singleTapSensitivityStack]; [self setHidden:![self isNotNativeTouchOnly] forStack:self.onScreenWidgetStack]; [self setHidden:![self isNotNativeTouchOnly] forStack:self.buttonVisualFeedbackStack]; [self handleOswGestureChange]; @@ -2401,7 +2411,7 @@ BOOL isCustomResolution(int resolutionSelected) { defaultBitrate = round(resolutionFactor * frameRateFactor) * 1000; _bitrate = MIN(defaultBitrate, 100000); - [self.bitrateSlider setValue:[self getSliderValueForBitrate:_bitrate] animated:YES]; + [self.bitrateSlider setValue:[self getSliderValueForBitrate:_bitrate] animated:NO]; [self updateBitrateText]; } @@ -2781,6 +2791,7 @@ BOOL isCustomResolution(int resolutionSelected) { NSInteger localMousePointerMode = [self.localMousePointerModeSelector selectedSegmentIndex]; BOOL sendDummyEvent = self.sendDummyEventSwitch.isOn; BOOL rememberFoldState = self.rememberFoldStateSwitch.isOn; + CGFloat singleTapSensitivity = self.singleTapSensitivitySlider.value; NSInteger backgroundSessionTimer = self.backgroundSessionTimerSlider.value == self.backgroundSessionTimerSlider.maximumValue ? (uint32_t) INT16_MAX : (uint32_t)self.backgroundSessionTimerSlider.value; [dataMan saveSettingsWithBitrate:_bitrate @@ -2832,6 +2843,7 @@ BOOL isCustomResolution(int resolutionSelected) { framePacingMode:framePacingMode sendDummyEvent:sendDummyEvent rememberFoldState:rememberFoldState + singleTapSensitivy:singleTapSensitivity backgroundSessionTimer:backgroundSessionTimer]; } diff --git a/VoidLink/mul.lproj/iPad.xcstrings b/VoidLink/mul.lproj/iPad.xcstrings index 2022de41..5f7bc9e1 100644 --- a/VoidLink/mul.lproj/iPad.xcstrings +++ b/VoidLink/mul.lproj/iPad.xcstrings @@ -1394,13 +1394,13 @@ } }, "cKS-fy-Zpg.segmentTitles[0]" : { - "comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[0] = \"slide to toggle\"; ObjectID = \"cKS-fy-Zpg\";", + "comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[0] = \"slide-toggle\"; ObjectID = \"cKS-fy-Zpg\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", - "value" : "slide to toggle" + "value" : "slide-toggle" } }, "zh-Hans" : { @@ -1424,30 +1424,30 @@ } }, "cKS-fy-Zpg.segmentTitles[1]" : { - "comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[1] = \"slide & hold\"; ObjectID = \"cKS-fy-Zpg\";", + "comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[1] = \"slide-hold\"; ObjectID = \"cKS-fy-Zpg\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", - "value" : "slide & hold" + "value" : "slide-hold" } }, "zh-Hans" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "滑动时按下" } }, "zh-Hant" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "滑動時按下" } }, "zh-HK" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "滑動時按下" } } @@ -3865,6 +3865,36 @@ } } }, + "SuN-S2-8hj.text" : { + "comment" : "Class = \"UILabel\"; text = \"Single Tap Sensitivity\"; ObjectID = \"SuN-S2-8hj\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Single Tap Sensitivity" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "单击灵敏度" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "單擊靈敏度" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "單擊靈敏度" + } + } + } + }, "t7a-18-5wz.text" : { "comment" : "Class = \"UILabel\"; text = \"Performance Graphs \"; ObjectID = \"t7a-18-5wz\";", "extractionState" : "extracted_with_value", diff --git a/VoidLink/mul.lproj/iPhone.xcstrings b/VoidLink/mul.lproj/iPhone.xcstrings index 71229c12..c0f5c5ed 100644 --- a/VoidLink/mul.lproj/iPhone.xcstrings +++ b/VoidLink/mul.lproj/iPhone.xcstrings @@ -2672,60 +2672,60 @@ } }, "ojp-aS-m8w.segmentTitles[0]" : { - "comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[0] = \"slide to toggle\"; ObjectID = \"ojp-aS-m8w\";", + "comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[0] = \"slide-toggle\"; ObjectID = \"ojp-aS-m8w\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", - "value" : "slide to toggle" + "value" : "slide-toggle" } }, "zh-Hans" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "滑出时松开" } }, "zh-Hant" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "滑出時鬆開" } }, "zh-HK" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "滑出時鬆開" } } } }, "ojp-aS-m8w.segmentTitles[1]" : { - "comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[1] = \"slide & hold\"; ObjectID = \"ojp-aS-m8w\";", + "comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[1] = \"slide-hold\"; ObjectID = \"ojp-aS-m8w\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", - "value" : "slide & hold" + "value" : "slide-hold" } }, "zh-Hans" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "滑动时按下" } }, "zh-Hant" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "滑動時按下" } }, "zh-HK" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "滑動時按下" } } @@ -4171,6 +4171,36 @@ } } }, + "WzD-wo-Wum.text" : { + "comment" : "Class = \"UILabel\"; text = \"Single Tap Sensitivity\"; ObjectID = \"WzD-wo-Wum\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Single Tap Sensitivity" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "单击灵敏度" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "單擊靈敏度" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "單擊靈敏度" + } + } + } + }, "XtQ-YN-4jb.text" : { "comment" : "Class = \"UILabel\"; text = \"Touch Move Event Interval\"; ObjectID = \"XtQ-YN-4jb\";", "extractionState" : "extracted_with_value",