From 5750b99af3ff957e7164b8c102648c5122dc204b Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 7 Nov 2025 17:34:19 +0800 Subject: [PATCH] - fixed: memory leak and UI stuck caused by MenuSenctionView - feat: memorize content offset for settings menu - optimize gesture passthrough for relative touch & absolute touch - feat: passthroughGesturesSwitch, ctrlDownForPinchSwitch, leftClickDelaySlider, touchModeSelector2 in experimental section --- VoidLink/Base.lproj/iPad.storyboard | 82 ++++++++++-- VoidLink/Base.lproj/iPhone.storyboard | 82 ++++++++++-- VoidLink/Database/DataManager.h | 4 + VoidLink/Database/DataManager.m | 8 ++ VoidLink/Database/TemporarySettings.h | 4 + VoidLink/Database/TemporarySettings.m | 4 + VoidLink/Input/AbsoluteTouchHandler.m | 45 +++++-- VoidLink/Input/RelativeTouchHandler.m | 29 +++-- VoidLink/Input/TouchPadGestureHandler.swift | 4 +- .../VoidLink v1.0.xcdatamodel/contents | 6 +- VoidLink/Localization/Localizable.xcstrings | 29 +++++ VoidLink/MenuSectionView.m | 6 +- .../ViewControllers/MainFrameViewController.m | 2 +- .../ViewControllers/SettingsViewController.h | 10 +- .../ViewControllers/SettingsViewController.m | 122 ++++++++++++++---- .../StreamFrameViewController.m | 10 +- VoidLink/mul.lproj/iPad.xcstrings | 92 ++++++++++++- VoidLink/mul.lproj/iPhone.xcstrings | 94 +++++++++++++- 18 files changed, 556 insertions(+), 77 deletions(-) diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index 997aa212..df6828e1 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -1403,16 +1403,16 @@ - + - + @@ -1578,7 +1578,7 @@ - + + + + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1661,6 +1721,8 @@ + + @@ -1694,6 +1756,8 @@ + + @@ -1717,6 +1781,8 @@ + + @@ -1769,7 +1835,7 @@ - + @@ -1803,7 +1869,7 @@ - + diff --git a/VoidLink/Base.lproj/iPhone.storyboard b/VoidLink/Base.lproj/iPhone.storyboard index 4eafeb8e..283e198c 100644 --- a/VoidLink/Base.lproj/iPhone.storyboard +++ b/VoidLink/Base.lproj/iPhone.storyboard @@ -2284,16 +2284,16 @@ - + - + @@ -2470,6 +2470,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2494,6 +2554,8 @@ + + @@ -2528,6 +2590,8 @@ + + @@ -2550,6 +2614,8 @@ + + @@ -2601,7 +2667,7 @@ - + @@ -2640,19 +2706,19 @@ - + - + - + - + diff --git a/VoidLink/Database/DataManager.h b/VoidLink/Database/DataManager.h index ea182d49..8e9357dc 100644 --- a/VoidLink/Database/DataManager.h +++ b/VoidLink/Database/DataManager.h @@ -133,6 +133,10 @@ typedef NS_ENUM(NSInteger, WidgetSizeTransition) { enablePinch:(BOOL)enablePinch scrollSensitivity:(CGFloat)scrollSensitivity pinchSensitivity:(CGFloat)pinchSensitivity + ctrlDownForPinch:(BOOL)ctrlDownForPinch + leftClickDelayMs:(CGFloat)leftClickDelayMs + settingsMenuOffset:(CGFloat)settingsMenuOffset + passthroughGestures:(BOOL)passthroughGestures backgroundSessionTimer:(NSInteger)backgroundSessionTimer; - (NSArray*) getHosts; diff --git a/VoidLink/Database/DataManager.m b/VoidLink/Database/DataManager.m index a0e2d436..d66a713f 100644 --- a/VoidLink/Database/DataManager.m +++ b/VoidLink/Database/DataManager.m @@ -116,6 +116,10 @@ enablePinch:(BOOL)enablePinch scrollSensitivity:(CGFloat)scrollSensitivity pinchSensitivity:(CGFloat)pinchSensitivity + ctrlDownForPinch:(BOOL)ctrlDownForPinch + leftClickDelayMs:(CGFloat)leftClickDelayMs + settingsMenuOffset:(CGFloat)settingsMenuOffset + passthroughGestures:(BOOL)passthroughGestures backgroundSessionTimer:(NSInteger)backgroundSessionTimer{ [_managedObjectContext performBlockAndWait:^{ @@ -182,6 +186,10 @@ settingsToSave.enablePinch = enablePinch; settingsToSave.scrollSensitivity = [NSNumber numberWithFloat:scrollSensitivity]; settingsToSave.pinchSensitivity = [NSNumber numberWithFloat:pinchSensitivity]; + settingsToSave.ctrlDownForPinch = ctrlDownForPinch; + settingsToSave.leftClickDelayMs = [NSNumber numberWithFloat:leftClickDelayMs]; + settingsToSave.settingsMenuOffset = [NSNumber numberWithFloat:settingsMenuOffset]; + settingsToSave.passthroughGestures = passthroughGestures; settingsToSave.rememberFoldState = rememberFoldState; [self saveData]; }]; diff --git a/VoidLink/Database/TemporarySettings.h b/VoidLink/Database/TemporarySettings.h index 44ee605d..5cb31203 100644 --- a/VoidLink/Database/TemporarySettings.h +++ b/VoidLink/Database/TemporarySettings.h @@ -87,10 +87,14 @@ @property (nonatomic) BOOL duckOtherApps; @property (nonatomic) BOOL muteInBackground; @property (nonatomic) BOOL enablePinch; +@property (nonatomic) BOOL ctrlDownForPinch; +@property (nonatomic) BOOL passthroughGestures; @property (nonatomic, retain) NSNumber * framePacingMode; @property (nonatomic, retain) NSNumber * scrollSensitivity; @property (nonatomic, retain) NSNumber * pinchSensitivity; @property (nonatomic, retain) NSNumber * relativeTouchSlideThreshold; +@property (nonatomic, retain) NSNumber * leftClickDelayMs; +@property (nonatomic, retain) NSNumber * settingsMenuOffset; - (id) initFromSettings:(Settings*)settings; diff --git a/VoidLink/Database/TemporarySettings.m b/VoidLink/Database/TemporarySettings.m index 151792a1..b177e540 100644 --- a/VoidLink/Database/TemporarySettings.m +++ b/VoidLink/Database/TemporarySettings.m @@ -147,6 +147,10 @@ self.enablePinch = settings.enablePinch; self.scrollSensitivity = settings.scrollSensitivity; self.pinchSensitivity = settings.pinchSensitivity; + self.leftClickDelayMs = settings.leftClickDelayMs; + self.ctrlDownForPinch = settings.ctrlDownForPinch; + self.settingsMenuOffset = settings.settingsMenuOffset; + self.passthroughGestures = settings.passthroughGestures; #endif return self; diff --git a/VoidLink/Input/AbsoluteTouchHandler.m b/VoidLink/Input/AbsoluteTouchHandler.m index 475c5dc6..3d6896d9 100644 --- a/VoidLink/Input/AbsoluteTouchHandler.m +++ b/VoidLink/Input/AbsoluteTouchHandler.m @@ -29,6 +29,9 @@ @implementation AbsoluteTouchHandler { StreamView* streamView; + bool multiTouchesDetected; + bool passthroughGestures; + NSTimer* longPressTimer; UITouch* lastTouchDown; CGPoint lastTouchDownLocation; @@ -50,6 +53,8 @@ CGFloat _edgeTolerance; bool _delayMouseLeftClick; + NSTimeInterval leftClickDelay; + bool dragButtonDown; UInt8 currentTouchesCount; @@ -60,10 +65,13 @@ self = [self init]; self->streamView = view; - // _delayMouseLeftClick = settings.delayLeftClick; - _delayMouseLeftClick = true; // deprecate legacy absolute touch + multiTouchesDetected = false; + passthroughGestures = settings.passthroughGestures; + + _delayMouseLeftClick = settings.delayLeftClick; + // _delayMouseLeftClick = true; dragButtonDown = false; - + leftClickTimeThreshold = 0.1; // upper screen check @@ -74,6 +82,8 @@ _mouseButtonForCursorMove = BUTTON_LEFT; + leftClickDelay = ((CGFloat)settings.leftClickDelayMs.intValue)/1000; + return self; } @@ -97,6 +107,10 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { rightButtonClicked = false; + + + if([UITouchUtil touchesIn:streamView from:event].count>=2) multiTouchesDetected = true; + CGPoint initialPoint = [[touches anyObject] locationInView:streamView]; if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < _edgeTolerance || initialPoint.x > screenWidthWithThreshold)) { @@ -126,7 +140,6 @@ // Press the left button down if(!_delayMouseLeftClick){ - // _delayMouseLeftClick will always be true. LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT); //deprecated } @@ -169,10 +182,10 @@ if(currentTouchesCount == 2){ if(_mouseButtonForCursorMove!=BUTTON_LEFT) LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, _mouseButtonForCursorMove); - [TouchPadGestureHandler handleGestureIn:streamView with:event]; + if(passthroughGestures) [TouchPadGestureHandler handleGestureIn:streamView with:event]; } - if(currentTouchesCount>=2) return; + if(multiTouchesDetected) return; if(![currentTouches containsObject:capturedTouch]) return; @@ -197,8 +210,17 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + if(TouchPadGestureHandler.ctrlDown) LiSendKeyboardEvent(CommandManager.keyboardButtonMappings[@"CTRL"].shortValue,KEY_ACTION_UP,0); + if(touchPointSpawnedAtUpperScreenEdge) return; // we're done here. this touch event will not be sent to the remote PC. + if(multiTouchesDetected) { + if([UITouchUtil touchesIn:streamView from:event].count == touches.count) multiTouchesDetected = false; + return; + }; + + if([UITouchUtil touchesIn:streamView from:event].count == touches.count) multiTouchesDetected = false; + // Only fire this logic if all touches have ended if ([touches containsObject:capturedTouch]) { // Cancel the long press timer @@ -220,17 +242,14 @@ LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT); } } - else{ // deprecated - /* + else{ // Left button up on finger up LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); // Raise right button too in case we triggered a long press gesture - LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT); */ + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT); } - - if(TouchPadGestureHandler.ctrlDown) LiSendKeyboardEvent(CommandManager.keyboardButtonMappings[@"CTRL"].shortValue,KEY_ACTION_UP,0); - + lastTouchUp = [touches anyObject]; lastTouchUpLocation = [lastTouchUp locationInView:streamView]; @@ -244,7 +263,7 @@ } - (void)sendShortMouseLeftButtonClickEvent{ - dispatch_time_t delayShort = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)); + dispatch_time_t delayShort = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(leftClickDelay * NSEC_PER_SEC)); dispatch_time_t delayLong = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.03 * NSEC_PER_SEC)); dispatch_after(delayShort, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT); diff --git a/VoidLink/Input/RelativeTouchHandler.m b/VoidLink/Input/RelativeTouchHandler.m index 97fd4e13..68ee3cf7 100644 --- a/VoidLink/Input/RelativeTouchHandler.m +++ b/VoidLink/Input/RelativeTouchHandler.m @@ -1,6 +1,6 @@ // // RelativeTouchHandler.m -// Moonlight +// VoidLink // // Completely refactored by True砖家 on 2024.9.13 // Copyright © 2024 True砖家 on Bilibili. All rights reserved. @@ -33,6 +33,8 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; UITouch* touchLockedForMouseMove; UITouch* quickTapTouch; + bool multiTouchesDetected; + CADisplayLink *displayLink; #if TARGET_OS_TV @@ -129,7 +131,12 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; } 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([UITouchUtil touchesIn:streamView from:event].count>=2){ + multiTouchesDetected = true; + return; + } + UITouch* candidateTouch = nil; // the onscreen controllers are implmented by CALayer, which can not intercept UITouch event, touch will penetrate to the streamView level and captured in the touches callback of this touchHandler class. @@ -166,11 +173,10 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { NSSet* currentTouches = [UITouchUtil touchesIn:streamView from:event]; - uint8_t currentTouchesCount = currentTouches.count; if(![self isOnScreenControllerBeingPressed:currentTouches]) [TouchPadGestureHandler handleGestureIn:streamView with:event]; - if(currentTouchesCount>=2) return; + if(multiTouchesDetected) return; if([touches containsObject:touchLockedForMouseMove]){ CGPoint currentLocation = [touchLockedForMouseMove locationInView:streamView]; @@ -184,6 +190,15 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + if(TouchPadGestureHandler.ctrlDown) LiSendKeyboardEvent(CommandManager.keyboardButtonMappings[@"CTRL"].shortValue,KEY_ACTION_UP,0); + + if(multiTouchesDetected){ + if([UITouchUtil touchesIn:streamView from:event].count == touches.count) multiTouchesDetected = false; + return; + } + + if([UITouchUtil touchesIn:streamView from:event].count == touches.count) multiTouchesDetected = false; + if([touches containsObject:touchLockedForMouseMove]){ // dealing with a single first tap, whether the button will be released, is going to be decided in sendLongMouseLeftButtonClickEvent if(!mousePointerMoved && !self->quickTapDetected) [self sendLongMouseLeftButtonClickEvent]; @@ -202,14 +217,12 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; for(UITouch* touch in touches){ [OnScreenControls.touchesCapturedByOnScreenControls removeObject:touch]; } - - if(TouchPadGestureHandler.ctrlDown) LiSendKeyboardEvent(CommandManager.keyboardButtonMappings[@"CTRL"].shortValue,KEY_ACTION_UP,0); - + if([UITouchUtil touchesIn:streamView from:event].count == [touches count]){ touchLockedForMouseMove = nil; mousePointerMoved = false; // need to reset this anyway } - + touchPointSpawnedAtUpperScreenEdge = false; } diff --git a/VoidLink/Input/TouchPadGestureHandler.swift b/VoidLink/Input/TouchPadGestureHandler.swift index eafa5d38..f0061ce8 100644 --- a/VoidLink/Input/TouchPadGestureHandler.swift +++ b/VoidLink/Input/TouchPadGestureHandler.swift @@ -12,6 +12,7 @@ import UIKit @objc public static var ctrlDown:Bool = false @objc public static var enablePinch:Bool = true + @objc public static var ctrlDownForPinch:Bool = true @objc public static var enableHorizontalScroll:Bool = true @objc public static var scrollSensitivity:CGFloat = 1.0 @objc public static var pinchSensitivity:CGFloat = 1.0 @@ -36,7 +37,8 @@ import UIKit LiSendHighResScrollEvent(Int16(pinchDelta + midPointDeltaY*5*scrollSensitivity)) if enableHorizontalScroll {LiSendHighResHScrollEvent(Int16(-midPointDeltaX*5*scrollSensitivity))} - if !enablePinch {return}; + if !enablePinch || !ctrlDownForPinch {return}; + if abs(currentDistance - previousDistance) > abs(midPointDeltaY) { LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["CTRL"]!, CChar(KEY_ACTION_DOWN), 0) ctrlDown = true diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index 74d5d497..c2253de9 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -29,6 +29,7 @@ + @@ -52,6 +53,7 @@ + @@ -62,6 +64,7 @@ + @@ -75,6 +78,7 @@ + diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index e1f0a37e..4e375a2b 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -1422,6 +1422,35 @@ } } }, + "ctrlDownForPinchStackTip" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "When disabled, pinch gesture equals vertical scrolling. Works for some games that use vertical scrolling for zooming." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "关闭此选项时, 捏合缩手势相当于垂直滚动. 对一些使用垂直滚动缩放画面的游戏有效." + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉此選項時, 捏合縮手勢相當於垂直滾動. 對一些使用垂直滾動縮放畫面的遊戲有效." + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉此選項時, 捏合縮手勢相當於垂直滾動. 對一些使用垂直滾動縮放畫面的遊戲有效." + } + } + } + }, "Current profile updated successfully" : { "extractionState" : "manual", "localizations" : { diff --git a/VoidLink/MenuSectionView.m b/VoidLink/MenuSectionView.m index 1e73f3e9..40e4c1f5 100644 --- a/VoidLink/MenuSectionView.m +++ b/VoidLink/MenuSectionView.m @@ -58,7 +58,7 @@ static BOOL overridePersistedFoldState = YES; _isExpanded = YES; _expandable = true; _backgroundColor = [UIColor clearColor]; - _rootStackViewSpacing = [self isIPhone] ? 10 : 13.8; + _rootStackViewSpacing = [self isIPhone] ? 10 : 12; _subStackViews = [NSMutableArray array]; _headerViewHeight = 37; _headerViewVerticalSpacing = 25; @@ -342,7 +342,9 @@ static BOOL overridePersistedFoldState = YES; - (void)updateViewForFoldState { - [self setupConstraints]; + // [self setupConstraints]; + // don't run this repeatedly + NSInteger visibleCount = 0; for (UIView *subview in _rootStackView.arrangedSubviews) { if (!subview.isHidden) { diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index cbd1d7e6..8e9c17a0 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1183,7 +1183,7 @@ static NSMutableSet* hostList; // [settingsViewController widget:settingsViewController.bitrateSlider setEnabled:!self.settingsExpandedInStreamView]; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.optimizeGamesStack]; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.audioOnPcStack]; - [settingsViewController.touchModeSelector setEnabled:!_settingsExpandedInStreamView || !_sessionLaunchedWithAbsoluteTouch]; + [settingsViewController.touchModeSelector1 setEnabled:!_settingsExpandedInStreamView || !_sessionLaunchedWithAbsoluteTouch]; [settingsViewController.codecSelector setEnabled:!_settingsExpandedInStreamView]; [settingsViewController.yuv444Switch setEnabled:!_settingsExpandedInStreamView]; [settingsViewController.hdrSwitch setEnabled:!_settingsExpandedInStreamView && [settingsViewController hdrSupported]]; diff --git a/VoidLink/ViewControllers/SettingsViewController.h b/VoidLink/ViewControllers/SettingsViewController.h index 35b77cf4..3a3805fd 100644 --- a/VoidLink/ViewControllers/SettingsViewController.h +++ b/VoidLink/ViewControllers/SettingsViewController.h @@ -68,7 +68,7 @@ @property (strong, nonatomic) IBOutlet UISegmentedControl *resolutionSelector; @property (strong, nonatomic) IBOutlet UISwitch *customResolutionSwitch; @property (strong, nonatomic) IBOutlet UILabel *touchModeLabel; -@property (strong, nonatomic) IBOutlet UISegmentedControl *touchModeSelector; +@property (strong, nonatomic) IBOutlet UISegmentedControl *touchModeSelector1; @property (strong, nonatomic) IBOutlet UISwitch *enableOswForNativeTouchSwitch; @property (strong, nonatomic) IBOutlet UILabel *onscreenControllerLabel; @property (strong, nonatomic) IBOutlet UISegmentedControl *onScreenWidgetSelector; @@ -205,6 +205,14 @@ @property (strong, nonatomic) IBOutlet UIStackView *touchModeStack2; @property (strong, nonatomic) IBOutlet UISegmentedControl *touchModeSelector2; +@property (strong, nonatomic) IBOutlet UIStackView *ctrlDownForPinchStack; +@property (strong, nonatomic) IBOutlet UISwitch *ctrlDownForPinchSwitch; + +@property (strong, nonatomic) IBOutlet UIStackView *leftClickDelayStack; +@property (strong, nonatomic) IBOutlet UISlider *leftClickDelaySlider; + +@property (strong, nonatomic) IBOutlet UIStackView *passthroughGesturesStack; +@property (strong, nonatomic) IBOutlet UISwitch *passthroughGesturesSwitch; @property (strong, nonatomic) IBOutlet UIStackView *testStack; diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index aa63206c..7f995d40 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -460,7 +460,9 @@ BOOL isCustomResolution(int resolutionSelected) { //CGSize currentResolution = CGSizeMake(currentSettings.width.intValue, currentSettings.height.intValue); [self.customResolutionSwitch setOn: isCustomResolution(self->tempSettings.resolutionSelected.intValue)]; [self.resolutionSelector setEnabled:!self.customResolutionSwitch.isOn]; - [self touchModeChanged:self.touchModeSelector]; // a special fix for iOS 14 to set hidden for the "enableOswStack" + [self touchModeChanged:self.touchModeSelector1]; // a special fix for iOS 14 to set hidden for the "enableOswStack" + + _scrollView.contentOffset = CGPointMake(_scrollView.contentOffset.x, tempSettings.settingsMenuOffset.floatValue); settingsViewJustExpanded = false; } @@ -712,14 +714,16 @@ BOOL isCustomResolution(int resolutionSelected) { [touchAndControlSection setSectionWithIcon:[UIImage imageNamed:@"arcade.stick.console"] andSize:20.5]; } [self addSetting:self.touchModeStack ofId:@"touchModeStack" withInfoTag:YES withDynamicLabel:NO to:touchAndControlSection]; + [self addSetting:self.mousePointerVelocityStack ofId:@"mousePointerVelocityStack" withInfoTag:NO withDynamicLabel:YES to:touchAndControlSection]; [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.delayLeftClickStack ofId:@"delayLeftClickStack" withInfoTag:YES withDynamicLabel:NO to:touchAndControlSection]; + [self addSetting:self.passthroughGesturesStack ofId:@"passthroughGesturesStack" withInfoTag:NO withDynamicLabel:NO to:touchAndControlSection]; [self addSetting:self.pinchGestureStack ofId:@"pinchGestureStack" withInfoTag:NO withDynamicLabel:NO to:touchAndControlSection]; - [self addSetting:self.mousePointerVelocityStack ofId:@"mousePointerVelocityStack" withInfoTag:NO withDynamicLabel:YES to:touchAndControlSection]; + [self addSetting:self.ctrlDownForPinchStack ofId:@"ctrlDownForPinchStack" withInfoTag:YES withDynamicLabel:NO to:touchAndControlSection]; [self addSetting:self.scrollSensitivityStack ofId:@"scrollSensitivityStack" withInfoTag:NO withDynamicLabel:YES to:touchAndControlSection]; [self addSetting:self.pinchSensitivityStack ofId:@"pinchSensitivityStack" withInfoTag:NO withDynamicLabel:YES to:touchAndControlSection]; [self addSetting:self.mousePointerVelocityStack ofId:@"mousePointerVelocityStack" withInfoTag:NO withDynamicLabel:YES to:touchAndControlSection]; - // [self addSetting:self.delayLeftClickStack ofId:@"delayLeftClickStack" withInfoTag:YES withDynamicLabel:NO 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]; @@ -832,10 +836,11 @@ BOOL isCustomResolution(int resolutionSelected) { } [self addSetting:self.touchModeStack2 ofId:@"touchModeStack2" withInfoTag:NO withDynamicLabel:NO to:experimentalSection]; - [self.touchModeSelector2 setEnabled:false]; + // [self.touchModeSelector2 setEnabled:false]; [self addSetting:self.touchMoveEventIntervalStack ofId:@"touchMoveEventIntervalStack" withInfoTag:NO withDynamicLabel:YES to:experimentalSection]; [self addSetting:self.relativeTouchSlideThresholdStack ofId:@"relativeTouchSlideThresholdStack" withInfoTag:YES withDynamicLabel:YES to:experimentalSection]; [self addSetting:self.singleTapSensitivityStack ofId:@"singleTapSensitivityStack" withInfoTag:NO withDynamicLabel:YES to:experimentalSection]; + [self addSetting:self.leftClickDelayStack ofId:@"leftClickDelayStack" withInfoTag:NO withDynamicLabel:YES to:experimentalSection]; [self addSetting:self.renderingBackendStack ofId:@"renderingBackendStack" withInfoTag:YES withDynamicLabel:NO to:experimentalSection]; [self addSetting:self.performanceGraphStack ofId:@"performanceGraphStack" withInfoTag:YES withDynamicLabel:NO to:experimentalSection]; [self addDynamicLabelForStack:self.graphOpacityStack]; @@ -970,12 +975,14 @@ BOOL isCustomResolution(int resolutionSelected) { }]; } -- (void)highlightedBackgroundForView:(UIView* )view animateWithDuration:(CGFloat)duration{ +- (void)highlightedBackgroundForView:(UIView* )view animateWithDuration:(CGFloat)duration completion: (void (^)(void))completion{ [UIView animateWithDuration:duration animations:^{ view.layer.cornerRadius = 6; view.layer.masksToBounds = YES; view.clipsToBounds = YES; view.backgroundColor = [ThemeManager appPrimaryColorWithAlpha]; + } completion:^(BOOL finished){ + completion(); }]; } @@ -994,7 +1001,7 @@ BOOL isCustomResolution(int resolutionSelected) { settingStackWillBeRelocatedToLowestPosition = true; } else{ - [self highlightedBackgroundForView:currentStack animateWithDuration:0]; + [self highlightedBackgroundForView:currentStack animateWithDuration:0 completion:nil]; snapshot.backgroundColor = [UIColor clearColor]; } } @@ -1052,7 +1059,7 @@ BOOL isCustomResolution(int resolutionSelected) { if(currentSettingsMenuMode == AllSettings &&gesture.state == UIGestureRecognizerStateBegan) { [self findCapturedStackByTouchLocation:locationInParentStack]; if(capturedStack == nil) return; - [self highlightedBackgroundForView:capturedStack animateWithDuration:0]; + [self highlightedBackgroundForView:capturedStack animateWithDuration:0 completion:nil]; UIAlertController* actionSheet = [self prepareAddToFavoriteActionSheet]; actionSheet.popoverPresentationController.sourceView = capturedStack; [self presentViewController:actionSheet animated:YES completion:nil]; @@ -1300,6 +1307,11 @@ BOOL isCustomResolution(int resolutionSelected) { showOnlineDocAction = true; onlineDocLink = [LocalizationHelper localizedStringForKey:@"relativeTouchSlideThresholdStackLink"]; } + if([sender.superview.accessibilityIdentifier isEqualToString: @"ctrlDownForPinchStack"]){ + tipText = [LocalizationHelper localizedStringForKey:@"ctrlDownForPinchStackTip"]; + showOnlineDocAction = false; + } + UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"Tips"] message:tipText preferredStyle:UIAlertControllerStyleAlert]; @@ -1731,7 +1743,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self.duckOtherAppSwitch setOn:self->tempSettings.duckOtherApps]; - [self.muteInBackgroundSwitch setOn:tempSettings.muteInBackground]; + [self.muteInBackgroundSwitch setOn:self->tempSettings.muteInBackground]; [self.muteInBackgroundSwitch addTarget:self action:@selector(muteInBackgroundSwitchFlipped:) forControlEvents:UIControlEventValueChanged]; [self.micVolumeSlider setValue:self->tempSettings.micVolume.floatValue*100]; @@ -1856,8 +1868,13 @@ BOOL isCustomResolution(int resolutionSelected) { [self.mousePointerVelocityFactorSlider addTarget:self action:@selector(mousePointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. [self mousePointerVelocityFactorSliderMoved:self.mousePointerVelocityFactorSlider]; + [self.passthroughGesturesSwitch setOn:self->tempSettings.passthroughGestures]; + [self.passthroughGesturesSwitch addTarget:self action:@selector(passthroughGesturesSwitchFlipped:) forControlEvents:(UIControlEventValueChanged)]; + [self.pinchGestureSwitch setOn:self->tempSettings.enablePinch]; [self.pinchGestureSwitch addTarget:self action:@selector(pinchGestureSwitchFlipped:) forControlEvents:(UIControlEventValueChanged)]; + + [self.ctrlDownForPinchSwitch setOn:self->tempSettings.ctrlDownForPinch]; [self.scrollSensitivitySlider setValue:self->tempSettings.scrollSensitivity.floatValue animated:NO]; [self.scrollSensitivitySlider addTarget:self action:@selector(scrollSensitivitySliderMoved:) forControlEvents:(UIControlEventValueChanged)]; @@ -1895,12 +1912,19 @@ BOOL isCustomResolution(int resolutionSelected) { [self.touchMoveEventIntervalSlider addTarget:self action:@selector(touchMoveEventIntervalSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. [self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider]; + // touch move event interval for native-touch. + [self.leftClickDelaySlider setValue:self->tempSettings.leftClickDelayMs.intValue animated:NO]; // Load old setting. + [self.leftClickDelaySlider addTarget:self action:@selector(leftClickDelaySliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. + [self leftClickDelaySliderMoved:self.leftClickDelaySlider]; // this part will enable/disable oscSelector & the asyncNativeTouchPriority selector uint8_t touchModeSelectorIndex = self->tempSettings.touchMode.intValue == NativeTouchOnly ? NativeTouch : self->tempSettings.touchMode.intValue; - [self.touchModeSelector setSelectedSegmentIndex:touchModeSelectorIndex]; //Load old touchMode setting - [self.touchModeSelector addTarget:self action:@selector(touchModeChanged:) forControlEvents:UIControlEventValueChanged]; - [self touchModeChanged:self.touchModeSelector]; + [self.touchModeSelector1 setSelectedSegmentIndex:touchModeSelectorIndex]; //Load old touchMode setting + [self.touchModeSelector1 addTarget:self action:@selector(touchMode1Changed:) forControlEvents:UIControlEventValueChanged]; + [self touchModeChanged:self.touchModeSelector1]; + + [self.touchModeSelector2 addTarget:self action:@selector(touchMode2Changed:) forControlEvents:UIControlEventValueChanged]; + // self.enableOswSwitchStack.hidden = !(self->tempSettings.touchMode.intValue == NativeTouch || self->tempSettings.touchMode.intValue == NativeTouchOnly); // do not use setHidden to stack wrapped by a settingStack @@ -1927,7 +1951,7 @@ BOOL isCustomResolution(int resolutionSelected) { completion:^{ if(!CountdownAlertController.actionCancelled){ [weakSelf.onScreenWidgetSelector setSelectedSegmentIndex:OnScreenControlsLevelCustom]; - if(weakSelf.touchModeSelector.selectedSegmentIndex == NativeTouch){ + if(weakSelf.touchModeSelector1.selectedSegmentIndex == NativeTouch){ [weakSelf.enableOswForNativeTouchSwitch setOn:true]; [weakSelf enableOswForNativeTouchSwitchFlipped:weakSelf.enableOswForNativeTouchSwitch]; } @@ -2013,7 +2037,7 @@ BOOL isCustomResolution(int resolutionSelected) { } - (bool)isNotNativeTouchOnly{ - return (self.enableOswForNativeTouchSwitch.isOn && self.touchModeSelector.selectedSegmentIndex == NativeTouch) || self.touchModeSelector.selectedSegmentIndex != NativeTouch; + return (self.enableOswForNativeTouchSwitch.isOn && self.touchModeSelector1.selectedSegmentIndex == NativeTouch) || self.touchModeSelector1.selectedSegmentIndex != NativeTouch; } - (void)handleOswGestureChange{ @@ -2357,14 +2381,32 @@ BOOL isCustomResolution(int resolutionSelected) { } */ +- (void)touchMode2Changed:(UISegmentedControl* )sender { + // [UIView animateWithDuration:0 animations:^{ + [self.touchModeSelector1 setSelectedSegmentIndex: sender.selectedSegmentIndex]; + // } completion:^(BOOL finished) { + // 动画完成时执行的代码 + [self touchModeChanged:sender]; + // }]; +} + +- (void)touchMode1Changed:(UISegmentedControl* )sender { + // [UIView animateWithDuration:0 animations:^{ + self.touchModeSelector2.selectedSegmentIndex = sender.selectedSegmentIndex; + // } completion:^(BOOL finished) { + // 动画完成时执行的代码 + [self touchModeChanged:sender]; + // }]; +} + - (void)touchModeChanged:(UISegmentedControl* )sender { // Disable On-Screen Controls & Widgets in non-relative touch mode // bool customOscEnabled = [self isOswEnabled] && [self.onScreenWidgetSelector selectedSegmentIndex] == OnScreenControlsLevelCustom; - self.touchModeSelector2.selectedSegmentIndex = sender.selectedSegmentIndex; bool isNativeTouch = sender.selectedSegmentIndex == NativeTouch; - //bool asyncNativeTouchEnabled = [self.asyncNativeTouchPrioritySelector selectedSegmentIndex] != AsyncNativeTouchOff; - if(self.enableOswSwitchStack.hidden != !isNativeTouch && isNativeTouch) [self highlightEmergingStack:self.enableOswSwitchStack]; + bool isEgmerging = self.enableOswSwitchStack.hidden != !isNativeTouch && isNativeTouch; self.enableOswSwitchStack.hidden = !isNativeTouch; + if(isEgmerging) [self highlightEmergingStack:self.enableOswSwitchStack]; + [self setHidden:!isNativeTouch forStack:self.pointerVelocityDividerStack]; @@ -2373,14 +2415,28 @@ BOOL isCustomResolution(int resolutionSelected) { [self setHidden:!isNativeTouch forStack:self.pointerVelocityFactorStack]; [self setHidden:!isNativeTouch forStack:self.touchMoveEventIntervalStack]; + /* [self setHidden:(sender.selectedSegmentIndex!=RelativeTouch && sender.selectedSegmentIndex!=AbsoluteTouch) forStack:self.pinchGestureStack]; [self setHidden:(sender.selectedSegmentIndex!=RelativeTouch - && sender.selectedSegmentIndex!=AbsoluteTouch) forStack:self.scrollSensitivityStack]; + && sender.selectedSegmentIndex!=AbsoluteTouch) forStack:self.scrollSensitivityStack];*/ + + [self setHidden:sender.selectedSegmentIndex!=AbsoluteTouch forStack:self.passthroughGesturesStack]; + UISwitch* dummySwitch = [[UISwitch alloc] init]; + [dummySwitch setOn:(sender.selectedSegmentIndex==RelativeTouch + || (sender.selectedSegmentIndex==AbsoluteTouch && _passthroughGesturesSwitch.isOn))]; + [self passthroughGesturesSwitchFlipped:dummySwitch]; + + /* [self setHidden:((sender.selectedSegmentIndex!=RelativeTouch && sender.selectedSegmentIndex!=AbsoluteTouch) || !_pinchGestureSwitch.isOn) forStack:self.pinchSensitivityStack]; + [self setHidden:((sender.selectedSegmentIndex!=RelativeTouch + && sender.selectedSegmentIndex!=AbsoluteTouch) + || !_pinchGestureSwitch.isOn) forStack:self.ctrlDownForPinchStack];*/ + [self setHidden:sender.selectedSegmentIndex!=AbsoluteTouch forStack:self.leftClickDelayStack]; + [self setHidden:sender.selectedSegmentIndex!=RelativeTouch forStack:self.mousePointerVelocityStack]; [self setHidden:sender.selectedSegmentIndex!=RelativeTouch forStack:self.singleTapSensitivityStack]; [self setHidden:sender.selectedSegmentIndex!=RelativeTouch forStack:self.relativeTouchSlideThresholdStack]; @@ -2431,16 +2487,22 @@ BOOL isCustomResolution(int resolutionSelected) { } } +- (void)passthroughGesturesSwitchFlipped:(UISwitch* )sender{ + [self setHidden:!sender.isOn forStack:_pinchGestureStack]; + [self setHidden:!sender.isOn forStack:_scrollSensitivityStack]; + if(!sender.isOn) [self pinchGestureSwitchFlipped:sender]; + else [self pinchGestureSwitchFlipped:_pinchGestureSwitch]; +} + - (void)pinchGestureSwitchFlipped:(UISwitch* )sender{ [self setHidden:!sender.isOn forStack:_pinchSensitivityStack]; + [self setHidden:!sender.isOn forStack:_ctrlDownForPinchStack]; } - (void)highlightEmergingStack:(UIStackView* )stack{ - [self highlightedBackgroundForView:stack animateWithDuration:0.2]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ + [self highlightedBackgroundForView:stack animateWithDuration:0.2 completion:^{ [self clearBackgroundColorForView:stack animateWithDuration:0.2]; - }); + }]; } - (void)muteInBackgroundSwitchFlipped:(UISwitch* )sender{ @@ -2652,6 +2714,9 @@ BOOL isCustomResolution(int resolutionSelected) { [NSString stringWithFormat:@" %d μs ", (uint16_t)self.touchMoveEventIntervalSlider.value] : @""; } +- (void) leftClickDelaySliderMoved:(UISlider* )sender{ + [self findDynamicLabelFromStack:self.leftClickDelayStack].text = [NSString stringWithFormat:@" %d ms ", (uint16_t)sender.value]; +} - (void) slideToMenuDistanceSliderMoved:(UISlider* )sender{ UILabel* displayLabel = [self findDynamicLabelFromStack:_slideToSettingsDistanceStack]; @@ -2870,6 +2935,8 @@ BOOL isCustomResolution(int resolutionSelected) { Settings* currentSettings = [dataMan retrieveSettings]; + CGFloat settingsMenuOffset = _rememberFoldStateSwitch.isOn ? _scrollView.contentOffset.y : 0; + NSInteger height = self.mainFrameViewController.settingsExpandedInStreamView ? currentSettings.height.intValue : [self getChosenStreamHeight]; NSInteger width = self.mainFrameViewController.settingsExpandedInStreamView ? currentSettings.width.intValue : [self getChosenStreamWidth]; @@ -2912,7 +2979,7 @@ BOOL isCustomResolution(int resolutionSelected) { BOOL enableYUV444 = self.yuv444Switch.isOn; BOOL enablePIP = self.pipSwitch.isOn; BOOL btMouseSupport = self.citrixX1MouseSwitch.isOn; - NSInteger touchMode = [self isNotNativeTouchOnly] ? self.touchModeSelector.selectedSegmentIndex : NativeTouchOnly; + NSInteger touchMode = [self isNotNativeTouchOnly] ? self.touchModeSelector1.selectedSegmentIndex : NativeTouchOnly; NSInteger statsOverlayLevel = [self.statsOverlaySelector selectedSegmentIndex]; BOOL statsOverlayEnabled = statsOverlayLevel != 0; BOOL enableHdr = self.hdrSwitch.isOn; @@ -2932,14 +2999,17 @@ BOOL isCustomResolution(int resolutionSelected) { NSInteger hapticEngine = self.hapticEngineSelector.selectedSegmentIndex; CGFloat edgeSlidingSensitivity = self.edgeSlidingSensitivitySlider.value; NSInteger audioEngine = self.audioEngineSelector.selectedSegmentIndex; - // BOOL delayLeftClick = self.delayLeftClickSwitch.isOn; - BOOL delayLeftClick = true; + BOOL delayLeftClick = self.delayLeftClickSwitch.isOn; + // BOOL delayLeftClick = true; BOOL duckOtherApps = self.duckOtherAppSwitch.isOn; BOOL muteInBackground = self.muteInBackgroundSwitch.isOn; CGFloat relativeTouchSlideThreshold = self.relativeTouchSlideThresholdSlider.value; BOOL enablePinch = self.pinchGestureSwitch.isOn; CGFloat scrollSensitivity = self.scrollSensitivitySlider.value; CGFloat pinchSensitivity = self.pinchSensitivitySlider.value; + BOOL ctrlDownForPinch = self.ctrlDownForPinchSwitch.isOn; + CGFloat leftClickDelayMs = self.leftClickDelaySlider.value; + BOOL passthroughGestures = self.passthroughGesturesSwitch.isOn; NSInteger backgroundSessionTimer = self.backgroundSessionTimerSlider.value == self.backgroundSessionTimerSlider.maximumValue ? (uint32_t) INT16_MAX : (uint32_t)self.backgroundSessionTimerSlider.value; [dataMan saveSettingsWithBitrate:_bitrate @@ -3002,6 +3072,10 @@ BOOL isCustomResolution(int resolutionSelected) { enablePinch:enablePinch scrollSensitivity:scrollSensitivity pinchSensitivity:pinchSensitivity + ctrlDownForPinch:ctrlDownForPinch + leftClickDelayMs:leftClickDelayMs + settingsMenuOffset:settingsMenuOffset + passthroughGestures:passthroughGestures backgroundSessionTimer:backgroundSessionTimer]; } diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 8284f792..6373dba3 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -310,11 +310,6 @@ } -- (void)handleAbsTouchPanGesture:(UIPanGestureRecognizer* )gesture{ - NSLog(@"handleAbsTouchPanGesture %f", CACurrentMediaTime()); - LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); -} - - (void)configZoomGestureAndAddStreamView{ if (_settings.touchMode.intValue == AbsoluteTouch) { if(!_scrollView) _scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame]; @@ -322,17 +317,17 @@ [_scrollView.panGestureRecognizer setMinimumNumberOfTouches:2]; [_scrollView.panGestureRecognizer setMaximumNumberOfTouches:2]; // reduce competing with keyboardToggleRecognizer in StreamView. #endif - [_scrollView.panGestureRecognizer addTarget:self action:@selector(handleAbsTouchPanGesture:)]; [_scrollView setShowsHorizontalScrollIndicator:NO]; [_scrollView setShowsVerticalScrollIndicator:NO]; [_scrollView setDelegate:self]; - [_scrollView setMaximumZoomScale:1.0f]; + [_scrollView setMaximumZoomScale:_settings.passthroughGestures ? 1.0 : 10.0f]; if(!_mainFrameViewcontroller.settingsExpandedInStreamView){ // Add StreamView inside a UIScrollView for absolute mode [_scrollView addSubview:_streamView]; // Insert at index 0 to ensure it doesn't cover OSC controls (CALayers) [self.view insertSubview:_scrollView atIndex:0]; } + _scrollView.panGestureRecognizer.enabled = !_settings.passthroughGestures; } else{ // Add streamView directly to self.view in other touch modes @@ -453,6 +448,7 @@ [_streamView.onScreenControls sendInstance]; TouchPadGestureHandler.enablePinch = _settings.enablePinch; + TouchPadGestureHandler.ctrlDownForPinch = _settings.ctrlDownForPinch; TouchPadGestureHandler.scrollSensitivity = _settings.scrollSensitivity.floatValue; TouchPadGestureHandler.pinchSensitivity = _settings.pinchSensitivity.floatValue; diff --git a/VoidLink/mul.lproj/iPad.xcstrings b/VoidLink/mul.lproj/iPad.xcstrings index 3aeea0e1..5d53aaec 100644 --- a/VoidLink/mul.lproj/iPad.xcstrings +++ b/VoidLink/mul.lproj/iPad.xcstrings @@ -583,7 +583,7 @@ }, "zh-HK" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "延遲滑鼠左單擊" } } @@ -811,6 +811,36 @@ } } }, + "aDg-5A-6M9.text" : { + "comment" : "Class = \"UILabel\"; text = \"Left Click Delay\"; ObjectID = \"aDg-5A-6M9\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Left Click Delay" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "左单击延迟" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "左單擊延遲" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "左單擊延遲" + } + } + } + }, "AeU-kl-XFa.text" : { "comment" : "Class = \"UILabel\"; text = \"Local Volume\"; ObjectID = \"AeU-kl-XFa\";", "extractionState" : "extracted_with_value", @@ -1447,6 +1477,36 @@ } } }, + "BsJ-eo-2Ac.text" : { + "comment" : "Class = \"UILabel\"; text = \"Ctrl Down for Pinch\"; ObjectID = \"BsJ-eo-2Ac\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Ctrl Down for Pinch" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "捏合时按下Ctrl" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "捏合時按下Ctrl" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "捏合時按下Ctrl" + } + } + } + }, "c1r-Hf-b7t.text" : { "comment" : "Class = \"UILabel\"; text = \"Down button\"; ObjectID = \"c1r-Hf-b7t\";", "extractionState" : "extracted_with_value", @@ -1885,6 +1945,36 @@ } } }, + "dfj-BB-Vby.text" : { + "comment" : "Class = \"UILabel\"; text = \"Passthrough Gestures\"; ObjectID = \"dfj-BB-Vby\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Passthrough Gestures" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "透传手势" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "透傳手勢" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "透傳手勢" + } + } + } + }, "dOF-FW-cBU.text" : { "comment" : "Class = \"UILabel\"; text = \" \"; ObjectID = \"dOF-FW-cBU\";", "extractionState" : "extracted_with_value", diff --git a/VoidLink/mul.lproj/iPhone.xcstrings b/VoidLink/mul.lproj/iPhone.xcstrings index df7d26b3..fe161a51 100644 --- a/VoidLink/mul.lproj/iPhone.xcstrings +++ b/VoidLink/mul.lproj/iPhone.xcstrings @@ -373,6 +373,36 @@ } } }, + "5S1-w1-Wnq.text" : { + "comment" : "Class = \"UILabel\"; text = \"Left Click Delay\"; ObjectID = \"5S1-w1-Wnq\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Left Click Delay" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "左单击延迟" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "左單擊延遲" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "左單擊延遲" + } + } + } + }, "5uF-E5-hpX.text" : { "comment" : "Class = \"UILabel\"; text = \"On-Screen Widgets \"; ObjectID = \"5uF-E5-hpX\";", "extractionState" : "extracted_with_value", @@ -1009,6 +1039,36 @@ } } }, + "A5m-3n-5qc.text" : { + "comment" : "Class = \"UILabel\"; text = \"Passthrough Gestures\"; ObjectID = \"A5m-3n-5qc\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Passthrough Gestures" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "透传手势" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "透傳手勢" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "透傳手勢" + } + } + } + }, "A9r-dE-rT4.segmentTitles[0]" : { "comment" : "Class = \"UISegmentedControl\"; A9r-dE-rT4.segmentTitles[0] = \"Touchpad\"; ObjectID = \"A9r-dE-rT4\";", "extractionState" : "extracted_with_value", @@ -2377,6 +2437,36 @@ } } }, + "ICY-9b-uFD.text" : { + "comment" : "Class = \"UILabel\"; text = \"Ctrl Down for Pinch\"; ObjectID = \"ICY-9b-uFD\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Ctrl Down for Pinch" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "捏合时按下Ctrl" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "捏合時按下Ctrl" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "捏合時按下Ctrl" + } + } + } + }, "Idx-O1-5dn.text" : { "comment" : "Class = \"UILabel\"; text = \"Height\"; ObjectID = \"Idx-O1-5dn\";", "extractionState" : "extracted_with_value", @@ -3787,13 +3877,13 @@ }, "zh-Hans" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "延迟鼠标左单击" } }, "zh-Hant" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "延遲滑鼠左單擊" } },