From 22ce2edfa38c280d541998c3990a7b385f1e98ab Mon Sep 17 00:00:00 2001 From: Acaki Date: Thu, 7 Aug 2025 16:18:06 +0800 Subject: [PATCH 1/9] fix: prefersHomeIndicatorAutoHidden conflicting with preferredScreenEdgesDeferringSystemGestures --- VoidLink/ViewControllers/StreamFrameViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 11b71d44..57373ac3 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -1343,7 +1343,7 @@ } - (BOOL)prefersHomeIndicatorAutoHidden { - if ( [_streamView getCurrentOscState] == OnScreenControlsLevelOff && + if ( [_controllerSupport getConnectedGamepadCount] > 0 && [_streamView getCurrentOscState] == OnScreenControlsLevelOff && _userIsInteracting == NO) { // Autohide the home bar when a gamepad is connected // and the on-screen controls are disabled. We can't From 679c0a2b4cc62b07929eedc8d78c6562ced242c4 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 8 Aug 2025 12:57:59 +0800 Subject: [PATCH 2/9] normalize on-screen widget size --- VoidLink/Input/CustomOSC/OSCProfilesManager.m | 16 ++++-- VoidLink/Input/OnScreenWidgetView.swift | 49 ++++++++++++++++--- .../LayoutOnScreenControlsViewController.m | 18 +++++-- 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.m b/VoidLink/Input/CustomOSC/OSCProfilesManager.m index 9a18105f..7aea9c96 100644 --- a/VoidLink/Input/CustomOSC/OSCProfilesManager.m +++ b/VoidLink/Input/CustomOSC/OSCProfilesManager.m @@ -363,8 +363,6 @@ static CGRect layoutViewBounds; else buttonState.vibrationStyle = UIImpactFeedbackStyleLight; // NSLog(@"oscLayerName: %@, opacity: %f, ", oscButtonLayer.name, buttonState.backgroundAlpha); - - // buttonState.oscLayerSizeFactor = oscButtonLayer.bounds; NSData *buttonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil]; @@ -376,8 +374,9 @@ static CGRect layoutViewBounds; CGPoint normalizedPosition = [self normalizeWidgetPosition:widgetView.center]; OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:widgetView.cmdString buttonType:CustomOnScreenWidget andPosition:normalizedPosition]; buttonState.alias = widgetView.buttonLabel; - buttonState.widthFactor = widgetView.widthFactor; - buttonState.heightFactor = widgetView.heightFactor; + buttonState.widthFactor = [self normalizeSizeWidthFactor:widgetView]; + NSLog(@"logging widthFactor %f", buttonState.widthFactor); + buttonState.heightFactor = [self normalizeSizeHeightFactor:widgetView]; buttonState.backgroundAlpha = widgetView.backgroundAlpha; buttonState.borderWidth = widgetView.borderWidth; buttonState.vibrationStyle = widgetView.vibrationStyle; @@ -396,6 +395,15 @@ static CGRect layoutViewBounds; return buttonStatesEncoded; } +- (CGFloat)normalizeSizeWidthFactor:(OnScreenWidgetView* )widget{ + CGFloat screenWidthInPoints = CGRectGetWidth([[UIScreen mainScreen] bounds]); + return widget.bounds.size.width/screenWidthInPoints * 10000; +} + +- (CGFloat)normalizeSizeHeightFactor:(OnScreenWidgetView* )widget{ + CGFloat screenWidthInPoints = CGRectGetWidth([[UIScreen mainScreen] bounds]); + return widget.bounds.size.height/screenWidthInPoints * 10000; +} #pragma mark - Queries diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 48bf2aac..83820b4b 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -45,6 +45,10 @@ import UIKit @objc public var pressed: Bool @objc public var widthFactor: CGFloat = 1.0 @objc public var heightFactor: CGFloat = 1.0 + + @objc public var deNormalizedWidthFactor: CGFloat = 1.0 + @objc public var deNormalizedHeightFactor: CGFloat = 1.0 + @objc public var borderWidth: CGFloat = 0.0 @objc public var backgroundAlpha: CGFloat = 0.5 @objc public var vibrationStyle: Int = 6 @@ -319,7 +323,6 @@ import UIKit if self.widthFactor == 0 {self.widthFactor = 1.0} if self.heightFactor == 0 {self.heightFactor = 1.0} - /* NSLayoutConstraint.activate([ self.centerXAnchor.constraint(equalTo: self.superview!.leadingAnchor, constant: storedLocation.x), @@ -357,21 +360,46 @@ import UIKit } } + func nearestEven(_ value: CGFloat) -> CGFloat { + let rounded = round(value) + if Int(rounded) % 2 == 0 { + return rounded + } else { + let lowerEven = rounded - 1 + let upperEven = rounded + 1 + return abs(value - lowerEven) <= abs(value - upperEven) ? lowerEven : upperEven + } + } + + private func denormalizeSize(sizeFactor:CGFloat) -> CGFloat { + let screenWidthInPoints = UIScreen.main.bounds.width + // return CGFloat(Int(sizeFactor/10000*screenWidthInPoints/2)*2); + return nearestEven(sizeFactor/10000*screenWidthInPoints); + } + private func changeAndActivateContraints(){ + let isNormalizedSizeFactor = self.widthFactor > 6; + if self.shape == "round"{ // we'll make custom osc buttons round & smaller NSLayoutConstraint.activate([ - self.widthAnchor.constraint(equalToConstant: CGFloat(Int(60 * self.widthFactor / 2) * 2)), - self.heightAnchor.constraint(equalToConstant: CGFloat(Int(60 * self.widthFactor / 2) * 2)),]) + self.widthAnchor.constraint(equalToConstant: isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.widthFactor) : CGFloat(Int(60 * self.widthFactor / 2) * 2)), + self.heightAnchor.constraint(equalToConstant: isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.widthFactor) : CGFloat(Int(60 * self.widthFactor / 2) * 2)),]) + self.deNormalizedWidthFactor = isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.widthFactor)/60 : self.widthFactor; + self.deNormalizedHeightFactor = isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.widthFactor)/60 : self.widthFactor; } if self.shape == "square" { NSLayoutConstraint.activate([ - self.widthAnchor.constraint(equalToConstant: CGFloat(Int(70 * self.widthFactor / 2) * 2)), - self.heightAnchor.constraint(equalToConstant: CGFloat(Int(65 * self.heightFactor / 2) * 2)),]) + self.widthAnchor.constraint(equalToConstant: isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.widthFactor) : CGFloat(Int(70 * self.widthFactor / 2) * 2)), + self.heightAnchor.constraint(equalToConstant: isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.heightFactor) : CGFloat(Int(65 * self.heightFactor / 2) * 2)),]) + self.deNormalizedWidthFactor = isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.widthFactor)/70 : self.widthFactor; + self.deNormalizedHeightFactor = isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.heightFactor)/65 : self.heightFactor; } if self.shape == "largeSquare" { // override all shape strings NSLayoutConstraint.activate([ - self.widthAnchor.constraint(equalToConstant: CGFloat(Int(170 * self.widthFactor / 2) * 2)), - self.heightAnchor.constraint(equalToConstant: CGFloat(Int(150 * self.heightFactor / 2) * 2)),]) + self.widthAnchor.constraint(equalToConstant:isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.widthFactor) : CGFloat(Int(170 * self.widthFactor / 2) * 2)), + self.heightAnchor.constraint(equalToConstant:isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.heightFactor) : CGFloat(Int(150 * self.heightFactor / 2) * 2)),]) + self.deNormalizedWidthFactor = isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.widthFactor)/170 : self.widthFactor; + self.deNormalizedHeightFactor = isNormalizedSizeFactor ? denormalizeSize(sizeFactor:self.heightFactor)/150 : self.heightFactor; } NSLayoutConstraint.activate([ @@ -379,6 +407,11 @@ import UIKit label.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10), label.centerXAnchor.constraint(equalTo: centerXAnchor), label.centerYAnchor.constraint(equalTo: centerYAnchor),]) + + if self.shape != "round"{ + let shortSideLen = min(self.layer.bounds.size.width, self.layer.bounds.size.height) + self.layer.cornerRadius = shortSideLen/2 < 16 ? shortSideLen/3.2 : 16 + } } private func setupView() { @@ -882,7 +915,7 @@ import UIKit private func setupButtonDownVisualEffectLayer() { self.buttonDownVisualEffectWidth = 8 if self.shape == "round" { - if widthFactor < 1.3 {self.buttonDownVisualEffectWidth = 15.3} // wider visual effect for osc buttons + if deNormalizedWidthFactor < 1.3 {self.buttonDownVisualEffectWidth = 15.3} // wider visual effect for osc buttons else {self.buttonDownVisualEffectWidth = 9} } diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index 25a389ad..daf986fd 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -714,6 +714,13 @@ [self->selectedWidgetView.crossMarkLayer removeFromSuperlayer]; } +/* +- (CGFloat)denormalizeSizeFactor:(CGFloat)sizeFactor{ + bool isNormalizedSizeFactor = sizeFactor > 6; + return isNormalizedSizeFactor ? sizeFactor/10000*[UIScreen mainScreen].bounds.size.width +} + */ + - (void)widgetViewTapped: (NSNotification *)notification{ //self.undoButton.alpha = selectedWidgetView.layoutChanges.count>1 && !CGPointEqualToPoint(selectedWidgetView.layoutChanges.lastObject.CGPointValue, selectedWidgetView.initialCenter)? 1.0 : 0.3; @@ -738,8 +745,8 @@ [self.layoutOSC updateGuidelinesForOnScreenWidget:self->selectedWidgetView]; // shows guideline immediately when widget is tapped // setup slider values - [self.widgetSizeSlider setValue: self->selectedWidgetView.widthFactor]; - [self.widgetHeightSlider setValue: self->selectedWidgetView.heightFactor]; + [self.widgetSizeSlider setValue: self->selectedWidgetView.deNormalizedWidthFactor]; + [self.widgetHeightSlider setValue: self->selectedWidgetView.deNormalizedHeightFactor]; [self.widgetAlphaSlider setValue: self->selectedWidgetView.backgroundAlpha]; [self.widgetBorderWidthSlider setValue:self->selectedWidgetView.borderWidth]; @@ -772,10 +779,12 @@ [self->selectedWidgetView updateStickIndicator]; } [self autoFitLabel:self.widgetSizeLabel]; - [self.widgetSizeLabel setText:[LocalizationHelper localizedStringForKey:@"Size: %.2f", self->selectedWidgetView.widthFactor]]; + + + [self.widgetSizeLabel setText:[LocalizationHelper localizedStringForKey:@"Size: %.2f", self->selectedWidgetView.deNormalizedWidthFactor]]; [self autoFitLabel:self.widgetHeightLabel]; - [self.widgetHeightLabel setText:[LocalizationHelper localizedStringForKey:@"Height: %.2f", self->selectedWidgetView.heightFactor]]; + [self.widgetHeightLabel setText:[LocalizationHelper localizedStringForKey:@"Height: %.2f", self->selectedWidgetView.deNormalizedHeightFactor]]; [self autoFitLabel:self.widgetAlphaLabel]; [self.widgetAlphaLabel setText:[LocalizationHelper localizedStringForKey:@"Alpha: %.2f", self->selectedWidgetView.backgroundAlpha]]; @@ -797,6 +806,7 @@ } } + - (void)legacyOscLayerTapped: (NSNotification *)notification{ [self enableCommonWidgetTools]; CALayer* controllerLayer = (CALayer* )notification.object; From ab503875cb8f49c6989af4685c6d9e28aec738fc Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 9 Aug 2025 22:02:03 +0800 Subject: [PATCH 3/9] - feat: implementation of slidable button --- VoidLink/Base.lproj/iPad.storyboard | 41 ++++++- VoidLink/Base.lproj/iPhone.storyboard | 50 +++++++-- VoidLink/Input/CustomOSC/OSCProfilesManager.m | 1 + .../Input/CustomOSC/OnScreenButtonState.h | 1 + .../Input/CustomOSC/OnScreenButtonState.m | 2 + VoidLink/Input/OnScreenWidgetView.swift | 88 +++++++++++---- VoidLink/Input/StreamView.m | 5 +- .../LayoutOnScreenControlsViewController.h | 3 + .../LayoutOnScreenControlsViewController.m | 18 +++- .../ViewControllers/MainFrameViewController.m | 2 +- VoidLink/mul.lproj/iPad.xcstrings | 102 ++++++++++++++++++ VoidLink/mul.lproj/iPhone.xcstrings | 102 ++++++++++++++++++ 12 files changed, 384 insertions(+), 31 deletions(-) diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index bb8d1122..76466d2e 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -1309,7 +1309,7 @@ - + - + @@ -1501,10 +1501,43 @@ - + + + + + + + + + + + + + + + + + + @@ -1544,6 +1577,8 @@ + + diff --git a/VoidLink/Base.lproj/iPhone.storyboard b/VoidLink/Base.lproj/iPhone.storyboard index 9fb1c0e7..57d5b069 100644 --- a/VoidLink/Base.lproj/iPhone.storyboard +++ b/VoidLink/Base.lproj/iPhone.storyboard @@ -13,7 +13,7 @@ - + @@ -314,7 +314,7 @@ - + + + + + + + + + + + + + + + + + + + + + @@ -597,6 +633,8 @@ + + @@ -1684,16 +1722,16 @@ - + - + - + - + diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.m b/VoidLink/Input/CustomOSC/OSCProfilesManager.m index 7aea9c96..969920a6 100644 --- a/VoidLink/Input/CustomOSC/OSCProfilesManager.m +++ b/VoidLink/Input/CustomOSC/OSCProfilesManager.m @@ -387,6 +387,7 @@ static CGRect layoutViewBounds; buttonState.stickIndicatorOffset = widgetView.stickIndicatorOffset; buttonState.widgetShape = widgetView.shape; buttonState.minStickOffset = widgetView.minStickOffset; + buttonState.isSlidable = widgetView.isSlidable; NSData *buttonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil]; [buttonStatesEncoded addObject: buttonStateEncoded]; diff --git a/VoidLink/Input/CustomOSC/OnScreenButtonState.h b/VoidLink/Input/CustomOSC/OnScreenButtonState.h index 427c4dfe..a8f41b90 100644 --- a/VoidLink/Input/CustomOSC/OnScreenButtonState.h +++ b/VoidLink/Input/CustomOSC/OnScreenButtonState.h @@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @property NSString *alias; @property CGPoint position; @property (nonatomic, assign) BOOL isHidden; +@property (nonatomic, assign) BOOL isSlidable; @property (nonatomic, assign) uint8_t buttonType; @property (nonatomic, assign) CGFloat widthFactor; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat heightFactor; // for OnScreenWidgetView diff --git a/VoidLink/Input/CustomOSC/OnScreenButtonState.m b/VoidLink/Input/CustomOSC/OnScreenButtonState.m index a91041f8..9cec0dc9 100644 --- a/VoidLink/Input/CustomOSC/OnScreenButtonState.m +++ b/VoidLink/Input/CustomOSC/OnScreenButtonState.m @@ -37,6 +37,7 @@ [encoder encodeInt:self.mouseButtonAction forKey:@"mouseButtonAction"]; [encoder encodeCGPoint:self.position forKey:@"position"]; [encoder encodeBool:self.isHidden forKey:@"isHidden"]; + [encoder encodeBool:self.isSlidable forKey:@"isSlidable"]; [encoder encodeFloat:self.widthFactor forKey:@"widthFactor"]; [encoder encodeFloat:self.heightFactor forKey:@"heightFactor"]; [encoder encodeFloat:self.sensitivityFactorX forKey:@"sensitivityFactorX"]; @@ -59,6 +60,7 @@ self.mouseButtonAction = [decoder decodeIntForKey:@"mouseButtonAction"]; self.position = [decoder decodeCGPointForKey:@"position"]; self.isHidden = [decoder decodeBoolForKey:@"isHidden"]; + self.isSlidable = [decoder decodeBoolForKey:@"isSlidable"]; self.widthFactor = [decoder decodeFloatForKey:@"widthFactor"]; self.heightFactor = [decoder decodeFloatForKey:@"heightFactor"]; self.sensitivityFactorX = [decoder decodeFloatForKey:@"sensitivityFactorX"]; diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 83820b4b..fd1694ad 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -45,7 +45,8 @@ import UIKit @objc public var pressed: Bool @objc public var widthFactor: CGFloat = 1.0 @objc public var heightFactor: CGFloat = 1.0 - + @objc public var isSlidable: Bool = true + @objc public var deNormalizedWidthFactor: CGFloat = 1.0 @objc public var deNormalizedHeightFactor: CGFloat = 1.0 @@ -135,7 +136,11 @@ import UIKit // border & visual effect private var minimumBorderAlpha: CGFloat = 0.19 private var defaultBorderColor: CGColor = UIColor(white: 0.2, alpha: 0.3).cgColor - private let moonlightPurple: CGColor = UIColor(red: 0.5, green: 0.5, blue: 1.0, alpha: 0.86).cgColor + private let voidlinkPurple: CGColor = UIColor(red: 0.5, green: 0.5, blue: 1.0, alpha: 0.86).cgColor + + //slide buttons + private var capturedTouches: NSMutableSet + private let noTouch: UITouch = UITouch() // whole button press down visual effect @@ -227,6 +232,7 @@ import UIKit self.stickIndicatorOffset = 95 self.sensitivityFactorX = 1.0 self.sensitivityFactorY = 1.0 + self.capturedTouches = NSMutableSet() super.init(frame: .zero) upIndicator = createLrudDirectionLayer() @@ -497,7 +503,7 @@ import UIKit indicatorBorder.fillColor = UIColor.clear.cgColor let path = UIBezierPath(roundedRect: indicatorBorder.bounds, cornerRadius: indicatorBorder.cornerRadius) indicatorBorder.path = path.cgPath - indicatorBorder.borderColor = moonlightPurple + indicatorBorder.borderColor = voidlinkPurple self.layer.superlayer?.addSublayer(indicatorBorder) indicatorBorder.position = CGPointMake(CGRectGetMinX(self.frame)+touchBeganLocation.x, CGRectGetMinY(self.frame)+touchBeganLocation.y) @@ -691,7 +697,7 @@ import UIKit indicatorBorder.fillColor = UIColor.clear.cgColor let path = UIBezierPath(roundedRect: indicatorBorder.bounds, cornerRadius: indicatorBorder.cornerRadius) indicatorBorder.path = path.cgPath - indicatorBorder.borderColor = moonlightPurple + indicatorBorder.borderColor = voidlinkPurple return indicatorBorder } @@ -886,14 +892,14 @@ import UIKit //==== wholeButtonPress visual effect============================================= - private func buttonDownEffect() { - // setupBorderLayer() + private func handleButtonDown() { + if !OnScreenWidgetView.editMode {self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings)} CATransaction.begin() CATransaction.setDisableActions(true) // self.layer.borderWidth = 0 buttonDownVisualEffectLayer.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)) // update position every time we press down the button buttonDownVisualEffectLayer.borderWidth = self.buttonDownVisualEffectWidth // this will show the visual effect - buttonDownVisualEffectLayer.borderColor = moonlightPurple + buttonDownVisualEffectLayer.borderColor = voidlinkPurple if vibrationOn { vibrationGenerator.prepare() vibrationGenerator.impactOccurred() @@ -902,7 +908,8 @@ import UIKit CATransaction.commit() } - private func buttonUpVisualEffect() { + private func handlebuttonUp() { + if !OnScreenWidgetView.editMode {self.sendComboButtonsUpEvent(comboStrings: self.comboButtonStrings)} CATransaction.begin() CATransaction.setDisableActions(true) // self.layer.borderWidth = 1 @@ -1041,11 +1048,11 @@ import UIKit //============================================================================== // Touch event handling override func touchesBegan(_ touches: Set, with event: UIEvent?) { - self.touchBegan = true self.firstTouchMoved = false super.touchesBegan(touches, with: event) - self.isMultipleTouchEnabled = self.touchPadString == "MOUSEPAD" // only enable multi-touch in mousePad mode + // self.isMultipleTouchEnabled = self.touchPadString == "MOUSEPAD" // only enable multi-touch in mousePad mode + self.isMultipleTouchEnabled = true if !OnScreenWidgetView.editMode && self.touchPadString == "TRACKBALL" { stopTrackballMomentum() @@ -1143,8 +1150,9 @@ import UIKit // this will also deal with button events if self.widgetType == WidgetTypeEnum.button && !self.comboButtonStrings.isEmpty { - self.buttonDownEffect() - self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings) + self.handleButtonDown() + self.capturedTouches.union(touches) + //self.handleButtonSliding(touches: touches) } // legacy keyboard button combo connected by "+" @@ -1152,13 +1160,13 @@ import UIKit let keyboardCmdStrings = CommandManager.shared.extractKeyStringsFromComboCommand(from: self.cmdString)! CommandManager.shared.sendKeyComboCommand(keyboardCmdStrings: keyboardCmdStrings) // send multi-key command DispatchQueue.main.asyncAfter(deadline: .now() + 0.03) { // reset shadow color immediately 50ms later - self.buttonUpVisualEffect() + self.handlebuttonUp() } } } // here is in edit mode: else{ - self.buttonDownEffect() + self.handleButtonDown() NotificationCenter.default.post(name: Notification.Name("OnScreenWidgetViewSelected"),object: self) // inform layout tool controller to fetch button size factors. self will be passed as the object of the notification } } @@ -1184,16 +1192,59 @@ import UIKit } } + private func handleButtonSlidingUp(touches: Set) { + for touch in touches { + for subview in self.superview?.subviews ?? [] { + if let widget = subview as? OnScreenWidgetView{ + if !widget.capturedTouches.contains(touch) || !widget.isSlidable {continue} + widget.handlebuttonUp() + } + } + } + } + + private func handleButtonSliding(touches: Set) { + for touch in touches { + let locationInSuperView = touch.location(in: self.superview) + for subview in self.superview?.subviews ?? [] { + if let widget = subview as? OnScreenWidgetView{ + if widget.widgetType != WidgetTypeEnum.button {continue} + let pointInSubview = widget.convert(locationInSuperView, from: self.superview) + if widget.bounds.contains(pointInSubview){ + if widget.capturedTouches.contains(touch) || !widget.isSlidable {continue} + widget.capturedTouches.add(touch) + widget.handleButtonDown() + print("UIButton: \(widget.buttonLabel) in, \(widget.touchPadString), \(CACurrentMediaTime())") + } + else{ + if !widget.capturedTouches.contains(touch) || !widget.isSlidable {continue} + print("UIButton: \(widget.buttonLabel) out test, \(widget.touchPadString), \(CACurrentMediaTime())") + widget.capturedTouches.remove(touch) + widget.handlebuttonUp() + } + } + } + } + } + override func touchesMoved(_ touches: Set, with event: UIEvent?) { super.touchesMoved(touches, with: event) if !OnScreenWidgetView.editMode { - handleTouchPadMoveEvent(touches, with: event) + + if !self.touchPadString.isEmpty{ + handleTouchPadMoveEvent(touches, with: event) + } + + if !self.buttonString.isEmpty{ + if self.isSlidable {self.handleButtonSliding(touches: touches)} + } + if CommandManager.specialOverlayButtonCmds.contains(self.cmdString){ if let touch = touches.first { NSLog("touchTapTimeStamp %f", self.touchTapTimeStamp) if CACurrentMediaTime() - self.touchTapTimeStamp > 0.3 { // temporarily relocate special buttons self.moveByTouch(touch: touch) - self.buttonUpVisualEffect() + self.handlebuttonUp() } } } @@ -1365,7 +1416,8 @@ import UIKit self.lrudIndicatorBall.removeFromSuperlayer() if !OnScreenWidgetView.editMode && !self.cmdString.contains("+") && !self.comboButtonStrings.isEmpty { // if the command(keystring contains "+", it's a legacy multi-key command - self.sendComboButtonsUpEvent(comboStrings: self.comboButtonStrings) + self.handleButtonSlidingUp(touches: touches) + self.capturedTouches.minus(touches) } if !OnScreenWidgetView.editMode && CommandManager.specialOverlayButtonCmds.contains(self.cmdString){ @@ -1417,7 +1469,7 @@ import UIKit } } - self.buttonUpVisualEffect() + self.handlebuttonUp() } } diff --git a/VoidLink/Input/StreamView.m b/VoidLink/Input/StreamView.m index 92ae7144..dfe1541b 100644 --- a/VoidLink/Input/StreamView.m +++ b/VoidLink/Input/StreamView.m @@ -232,12 +232,12 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; } - (void)keyboardWillShow:(NSNotification *)notification{ - NSLog(@"keyboard will show markmark %f", CACurrentMediaTime()); + // NSLog(@"keyboard will show markmark %f", CACurrentMediaTime()); if(settings.liftStreamViewForKeyboard && !isInputingText){ NSDictionary *userInfo = notification.userInfo; // Get the keyboard size from the notification CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; - NSLog(@"keyboard will show markmark, lowest height %f", keyboardToggleRecognizer.lowestTouchPointHeight); + // NSLog(@"keyboard will show markmark, lowest height %f", keyboardToggleRecognizer.lowestTouchPointHeight); if(keyboardFrame.size.height < CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.25) return; // return in case of abnormal keyboard height HeightViewLiftedTo = keyboardFrame.size.height - keyboardToggleRecognizer.lowestTouchPointHeight + CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.1; // lift the StreamView to the height of lowest touch point of multi-finger tap gesture, while reserving the view of 1/10 screen height for remote typing. if(HeightViewLiftedTo < 0) HeightViewLiftedTo = 0; // set HeightViewLiftedTo to 0 if it is high enough and not going to be covered by keyboard. @@ -454,6 +454,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; widgetView.trackballDecelerationRate = buttonState.decelerationRate; widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset; widgetView.minStickOffset = buttonState.minStickOffset; + widgetView.isSlidable = buttonState.isSlidable; // 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]; diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h index c2272de5..bbf0d565 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h @@ -93,6 +93,9 @@ NS_ASSUME_NONNULL_BEGIN @property (strong, nonatomic) IBOutlet UIStackView *mouseDownButtonStack; @property (strong, nonatomic) IBOutlet UISegmentedControl *mouseButtonDownSelector; +@property (strong, nonatomic) IBOutlet UIStackView *slidableStack; +@property (strong, nonatomic) IBOutlet UISegmentedControl *slidableSelector; + @property (weak, nonatomic) IBOutlet UIStackView *widgetPanelStack; diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index daf986fd..6aeacd25 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -123,6 +123,7 @@ widgetView.trackballDecelerationRate = buttonState.decelerationRate; widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset; widgetView.minStickOffset = buttonState.minStickOffset; + widgetView.isSlidable = buttonState.isSlidable; // Add the widgetView to the view controller's view [self.view insertSubview:widgetView belowSubview:self.widgetPanelStack]; buttonState.position = [self denormalizeWidgetPosition:buttonState.position]; @@ -634,6 +635,7 @@ newWidget.minStickOffset = [widgetInitParams[@"minStickOffsetString"] floatValue]; [newWidget setVibrationWithStyle:widget.vibrationStyle]; newWidget.mouseButtonAction = widget.mouseButtonAction; + newWidget.isSlidable = widget.isSlidable; [self.view insertSubview:newWidget belowSubview:self.widgetPanelStack]; if(createNew) [newWidget setLocationWithPosition:CGPointMake(90, 130)]; @@ -750,6 +752,9 @@ [self.widgetAlphaSlider setValue: self->selectedWidgetView.backgroundAlpha]; [self.widgetBorderWidthSlider setValue:self->selectedWidgetView.borderWidth]; + self.slidableStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton; + [self.slidableSelector setSelectedSegmentIndex:selectedWidgetView.isSlidable ? 0 : 1]; + bool showSensitivityFactorStack = selectedWidgetView.hasSensitivityTweak; bool showStickIndicatorOffsetStack = selectedWidgetView.hasStickIndicator; @@ -901,6 +906,12 @@ } } +- (void)slidableChanged:(UISegmentedControl* )sender{ + if(self->selectedWidgetView != nil && self->widgetViewSelected){ + selectedWidgetView.isSlidable = _slidableSelector.selectedSegmentIndex == 0; + } +} + - (void)vibrationStyleChanged:(UISegmentedControl* )sender{ bool vibraiontOn; if (@available(iOS 13.0, *)) { @@ -1046,13 +1057,18 @@ self.stickIndicatorOffsetLabel.text = [LocalizationHelper localizedStringForKey:@"Indicator Offset"]; self.stickIndicatorOffsetStack.hidden = YES; - [self.mouseButtonDownSelector addTarget:self action:@selector(mouseDownButtonChanged:) forControlEvents:(UIControlEventValueChanged)]; NSDictionary *whiteFontAttributes = @{ NSForegroundColorAttributeName: [UIColor whiteColor] }; + + [self.mouseButtonDownSelector addTarget:self action:@selector(mouseDownButtonChanged:) forControlEvents:(UIControlEventValueChanged)]; [self.mouseButtonDownSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal]; self.mouseDownButtonStack.hidden = YES; + [self.slidableSelector addTarget:self action:@selector(slidableChanged:) forControlEvents:(UIControlEventValueChanged)]; + [self.slidableSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal]; + self.slidableStack.hidden = YES; + if([self isIPhone]){ [self.vibrationStyleSelector addTarget:self action:@selector(vibrationStyleChanged:) forControlEvents:(UIControlEventValueChanged)]; diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index e779f008..e22244bd 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1918,7 +1918,7 @@ static NSMutableSet* hostList; } - (void)updateHosts { - Log(LOG_I, @"Updating hosts %f", CACurrentMediaTime()); + // Log(LOG_I, @"Updating hosts %f", CACurrentMediaTime()); @synchronized (hostList) { // Sort the host list in alphabetical order NSArray* sortedHostList = [[hostList allObjects] sortedArrayUsingSelector:@selector(compareName:)]; diff --git a/VoidLink/mul.lproj/iPad.xcstrings b/VoidLink/mul.lproj/iPad.xcstrings index 44f9e595..e34f2f13 100644 --- a/VoidLink/mul.lproj/iPad.xcstrings +++ b/VoidLink/mul.lproj/iPad.xcstrings @@ -1051,6 +1051,66 @@ } } }, + "cKS-fy-Zpg.segmentTitles[0]" : { + "comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[0] = \"yes\"; ObjectID = \"cKS-fy-Zpg\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "yes" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "开启" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "開啟" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "開啟" + } + } + } + }, + "cKS-fy-Zpg.segmentTitles[1]" : { + "comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[1] = \"no\"; ObjectID = \"cKS-fy-Zpg\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "no" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "关闭" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉" + } + } + } + }, "CpY-Sb-BDc.title" : { "comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"CpY-Sb-BDc\";", "extractionState" : "extracted_with_value", @@ -1141,6 +1201,18 @@ } } }, + "dOF-FW-cBU.text" : { + "comment" : "Class = \"UILabel\"; text = \" \"; ObjectID = \"dOF-FW-cBU\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : " " + } + } + } + }, "dpd-Bw-aHE.segmentTitles[0]" : { "comment" : "Class = \"UISegmentedControl\"; dpd-Bw-aHE.segmentTitles[0] = \"No\"; ObjectID = \"dpd-Bw-aHE\";", "extractionState" : "stale", @@ -2737,6 +2809,36 @@ } } }, + "qNQ-fB-jhY.text" : { + "comment" : "Class = \"UILabel\"; text = \"Slide Activated\"; ObjectID = \"qNQ-fB-jhY\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Slide Activated" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "滑动触发" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "滑動觸發" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "滑動觸發" + } + } + } + }, "Qs9-iS-YoW.text" : { "comment" : "Class = \"UILabel\"; text = \"Divider Position\"; ObjectID = \"Qs9-iS-YoW\";", "extractionState" : "extracted_with_value", diff --git a/VoidLink/mul.lproj/iPhone.xcstrings b/VoidLink/mul.lproj/iPhone.xcstrings index 32b2c72c..ab75408c 100644 --- a/VoidLink/mul.lproj/iPhone.xcstrings +++ b/VoidLink/mul.lproj/iPhone.xcstrings @@ -1783,6 +1783,18 @@ } } }, + "KZ3-vJ-E8J.text" : { + "comment" : "Class = \"UILabel\"; text = \" \"; ObjectID = \"KZ3-vJ-E8J\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : " " + } + } + } + }, "lwy-Nu-hQj.text" : { "comment" : "Class = \"UILabel\"; text = \"External Display Mode\"; ObjectID = \"lwy-Nu-hQj\";", "extractionState" : "extracted_with_value", @@ -1975,6 +1987,36 @@ } } }, + "o9I-Gt-YRd.text" : { + "comment" : "Class = \"UILabel\"; text = \"Slide Activated\"; ObjectID = \"o9I-Gt-YRd\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Slide Activated" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "滑动触发" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "滑動觸發" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "滑動觸發" + } + } + } + }, "oBs-zz-ZQv.segmentTitles[0]" : { "comment" : "Class = \"UISegmentedControl\"; oBs-zz-ZQv.segmentTitles[0] = \"Slide From Left Edge\"; ObjectID = \"oBs-zz-ZQv\";", "extractionState" : "extracted_with_value", @@ -2035,6 +2077,66 @@ } } }, + "ojp-aS-m8w.segmentTitles[0]" : { + "comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[0] = \"yes\"; ObjectID = \"ojp-aS-m8w\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "yes" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "开启" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "開啟" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "開啟" + } + } + } + }, + "ojp-aS-m8w.segmentTitles[1]" : { + "comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[1] = \"no\"; ObjectID = \"ojp-aS-m8w\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "no" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "关闭" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉" + } + } + } + }, "ozU-7C-qKE.text" : { "comment" : "Class = \"UILabel\"; text = \"Button Down\"; ObjectID = \"ozU-7C-qKE\";", "extractionState" : "extracted_with_value", From c5fc72cc2c197e14faf2ec684fa301513dc510ea Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 9 Aug 2025 22:14:16 +0800 Subject: [PATCH 4/9] make double tap dragging easier to trigger on iPhone --- VoidLink/Input/OnScreenWidgetView.swift | 4 ++-- VoidLink/Input/RelativeTouchHandler.m | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index fd1694ad..47652eae 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -1214,11 +1214,11 @@ import UIKit if widget.capturedTouches.contains(touch) || !widget.isSlidable {continue} widget.capturedTouches.add(touch) widget.handleButtonDown() - print("UIButton: \(widget.buttonLabel) in, \(widget.touchPadString), \(CACurrentMediaTime())") + // print("UIButton: \(widget.buttonLabel) in, \(widget.touchPadString), \(CACurrentMediaTime())") } else{ if !widget.capturedTouches.contains(touch) || !widget.isSlidable {continue} - print("UIButton: \(widget.buttonLabel) out test, \(widget.touchPadString), \(CACurrentMediaTime())") + // print("UIButton: \(widget.buttonLabel) out test, \(widget.touchPadString), \(CACurrentMediaTime())") widget.capturedTouches.remove(touch) widget.handlebuttonUp() } diff --git a/VoidLink/Input/RelativeTouchHandler.m b/VoidLink/Input/RelativeTouchHandler.m index 46a2d932..d6983319 100644 --- a/VoidLink/Input/RelativeTouchHandler.m +++ b/VoidLink/Input/RelativeTouchHandler.m @@ -127,7 +127,7 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; }*/ - (BOOL)isAdjacentTouches:(CGPoint)currentPoint from:(CGPoint)originalPoint { - return hypotf(originalPoint.x - currentPoint.x, originalPoint.y - currentPoint.y) <= 100; + return hypotf(originalPoint.x - currentPoint.x, originalPoint.y - currentPoint.y) <= 300; } From 7b5d9cde2e31c6f6fd923653f872d0ca45d8c139 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 9 Aug 2025 23:50:28 +0800 Subject: [PATCH 5/9] - feat: vibration available for controller & keyboard direction pad sliding --- VoidLink/Input/CustomTapGestureRecognizer.m | 4 ++-- VoidLink/Input/OnScreenWidgetView.swift | 14 +++++++++++++- VoidLink/Input/RelativeTouchHandler.m | 4 ++-- VoidLink/Input/StreamView.m | 4 ++-- VoidLink/ViewControllers/SettingsViewController.m | 4 +--- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/VoidLink/Input/CustomTapGestureRecognizer.m b/VoidLink/Input/CustomTapGestureRecognizer.m index 79e9fe13..2814ddc4 100644 --- a/VoidLink/Input/CustomTapGestureRecognizer.m +++ b/VoidLink/Input/CustomTapGestureRecognizer.m @@ -53,7 +53,7 @@ static CGFloat screenWidthInPoints; } */ _lowestTouchPointHeight = screenHeightInPoints - lowestTouchPointYCoord; - NSLog(@"gen _lowestTouchPointHeight %f markmark", _lowestTouchPointHeight); + // NSLog(@"gen _lowestTouchPointHeight %f markmark", _lowestTouchPointHeight); if(_immediateTriggering){ lowestTouchPointYCoord = 0.0; //reset for next recoginition self.state = UIGestureRecognizerStateRecognized; @@ -81,7 +81,7 @@ static CGFloat screenWidthInPoints; if((CACurrentMediaTime() - _gestureCapturedTime) < _tapDownTimeThreshold){ lowestTouchPointYCoord = 0.0; //reset for next recognition self.state = UIGestureRecognizerStateRecognized; - NSLog(@"gen _lowestTouchPointHeight %f markmark touchesEnd", _lowestTouchPointHeight); + // NSLog(@"gen _lowestTouchPointHeight %f markmark touchesEnd", _lowestTouchPointHeight); } } if (allTouchesCount == [touches count]) _isOnScreenControllerBeingPressed = false; // need to reset this flag anyway, when all fingers are lefting diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 47652eae..5e5633f8 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -707,7 +707,15 @@ import UIKit self.layer.superlayer?.insertSublayer(indicatorLayer, below: self.layer) // show the indicator based on the touchBeganLocation - indicatorLayer.position = CGPointMake(CGRectGetMinX(self.frame)+touchBeganLocation.x, CGRectGetMinY(self.frame)+touchBeganLocation.y) + let newPosition = CGPointMake(CGRectGetMinX(self.frame)+touchBeganLocation.x, CGRectGetMinY(self.frame)+touchBeganLocation.y) + + if indicatorLayer.position != newPosition && vibrationOn { + vibrationGenerator.prepare() + vibrationGenerator.impactOccurred() + // print("vibrationInstance: \(vibrationGenerator)") + } + + indicatorLayer.position = newPosition } private func handleLrudTouchMove(){ @@ -757,6 +765,7 @@ import UIKit } else{ self.upIndicator.removeFromSuperlayer() + self.upIndicator.position = CGPointMake(0, 0) switch touchPadString { case "WASDPAD": LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["W"]!,Int8(KEY_ACTION_UP), 0) case "ARROWPAD": LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["UP_ARROW"]!,Int8(KEY_ACTION_UP), 0) @@ -775,6 +784,7 @@ import UIKit } else{ self.downIndicator.removeFromSuperlayer() + self.downIndicator.position = CGPointMake(0, 0) switch touchPadString { case "WASDPAD": LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["S"]!,Int8(KEY_ACTION_UP), 0) case "ARROWPAD": LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["DOWN_ARROW"]!,Int8(KEY_ACTION_UP), 0) @@ -793,6 +803,7 @@ import UIKit } else{ self.leftIndicator.removeFromSuperlayer() + self.leftIndicator.position = CGPointMake(0, 0) switch touchPadString { case "WASDPAD": LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["A"]!,Int8(KEY_ACTION_UP), 0) case "ARROWPAD": LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["LEFT_ARROW"]!,Int8(KEY_ACTION_UP), 0) @@ -811,6 +822,7 @@ import UIKit } else{ self.rightIndicator.removeFromSuperlayer() + self.rightIndicator.position = CGPointMake(0, 0) switch touchPadString { case "WASDPAD": LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["D"]!,Int8(KEY_ACTION_UP), 0) case "ARROWPAD": LiSendKeyboardEvent(CommandManager.keyboardButtonMappings["RIGHT_ARROW"]!,Int8(KEY_ACTION_UP), 0) diff --git a/VoidLink/Input/RelativeTouchHandler.m b/VoidLink/Input/RelativeTouchHandler.m index d6983319..0aba3acc 100644 --- a/VoidLink/Input/RelativeTouchHandler.m +++ b/VoidLink/Input/RelativeTouchHandler.m @@ -177,7 +177,7 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; // we must use [event allTouches] to check if touchLockedForMouseMove is captured, because the UITouch object could be captured by upper layer of UIView(in cases like tap gestures), not passed to the touches callbacks in this class, but still available in [event allTouches] if(candidateTouch != nil && ![[event allTouches] containsObject:touchLockedForMouseMove]){ touchLockedForMouseMove = candidateTouch; - NSLog(@"Candidate touch for mouse movement locked"); + // NSLog(@"Candidate touch for mouse movement locked"); mousePointerTimestamp = CACurrentMediaTime(); initialMousePointerLocation = latestMousePointerLocation = [touchLockedForMouseMove locationInView:streamView]; } @@ -278,7 +278,7 @@ static const float QUICK_TAP_TIME_INTERVAL = 0.2; if(!self->quickTapDetected){ LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); } - else NSLog(@"Left mouse button release cancelled, keep pressing down, turning into dragging..."); + // else NSLog(@"Left mouse button release cancelled, keep pressing down, turning into dragging..."); // do not release the button if we're still dragging, this will prevent the dragging from being interrupted. }); diff --git a/VoidLink/Input/StreamView.m b/VoidLink/Input/StreamView.m index dfe1541b..c1efbc92 100644 --- a/VoidLink/Input/StreamView.m +++ b/VoidLink/Input/StreamView.m @@ -253,7 +253,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; // this method also deals with recovering streamview when local keyboard is turned off - (void)keyboardWillHide{ - NSLog(@"keyboard will hide markmark %f", CACurrentMediaTime()); + // NSLog(@"keyboard will hide markmark %f", CACurrentMediaTime()); keyboardToggleRecognizer.numberOfTouchesRequired = settings.keyboardToggleFingers.intValue; // reset this number if(isInputingText){ @@ -280,7 +280,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; - (void)toggleKeyboard{ - NSLog(@"toggleKeyboard markmark, %d", isInputingText); + // NSLog(@"toggleKeyboard markmark, %d", isInputingText); if (isInputingText) { Log(LOG_D, @"Closing the keyboard"); [keyInputField resignFirstResponder]; diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index fc4bb18b..0ab109e3 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -1566,9 +1566,7 @@ BOOL isCustomResolution(int resolutionSelected) { } [self findDynamicLabelFromStack:self.onScreenWidgetStack].text = [self isCustomOswEnabled] ? [LocalizationHelper localizedStringForKey:@"%d finger tap", self->oswLayoutFingers] : @""; - //markmark - [self handleOswGestureChange]; - }]; + [self handleOswGestureChange];}]; [alertController addAction:cancelAction]; [alertController addAction:okAction]; From 88c0e358aa59a8f7a5f29a941f0a508b2843660f Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 10 Aug 2025 00:45:40 +0800 Subject: [PATCH 6/9] - make all buttons slidable by default --- VoidLink/Input/CustomOSC/OnScreenButtonState.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VoidLink/Input/CustomOSC/OnScreenButtonState.m b/VoidLink/Input/CustomOSC/OnScreenButtonState.m index 9cec0dc9..0073c2af 100644 --- a/VoidLink/Input/CustomOSC/OnScreenButtonState.m +++ b/VoidLink/Input/CustomOSC/OnScreenButtonState.m @@ -60,7 +60,7 @@ self.mouseButtonAction = [decoder decodeIntForKey:@"mouseButtonAction"]; self.position = [decoder decodeCGPointForKey:@"position"]; self.isHidden = [decoder decodeBoolForKey:@"isHidden"]; - self.isSlidable = [decoder decodeBoolForKey:@"isSlidable"]; + self.isSlidable = [decoder containsValueForKey:@"isSlidable"] ? [decoder decodeBoolForKey:@"isSlidable"] : YES; self.widthFactor = [decoder decodeFloatForKey:@"widthFactor"]; self.heightFactor = [decoder decodeFloatForKey:@"heightFactor"]; self.sensitivityFactorX = [decoder decodeFloatForKey:@"sensitivityFactorX"]; From 3536bf06ba8ca77d9c74293f785d343ca30ad3e0 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 10 Aug 2025 13:22:30 +0800 Subject: [PATCH 7/9] - feat: slide & hold mode for slidable buttons --- VoidLink/Base.lproj/iPad.storyboard | 11 +++-- VoidLink/Input/CustomOSC/OSCProfilesManager.m | 2 +- .../Input/CustomOSC/OnScreenButtonState.h | 9 +++- .../Input/CustomOSC/OnScreenButtonState.m | 4 +- VoidLink/Input/OnScreenWidgetView.swift | 19 +++++--- VoidLink/Input/StreamView.m | 2 +- .../LayoutOnScreenControlsViewController.m | 12 ++--- VoidLink/mul.lproj/iPad.xcstrings | 46 +++++++++++++++---- 8 files changed, 74 insertions(+), 31 deletions(-) diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index 76466d2e..ddf01639 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -1518,19 +1518,20 @@ - + - - + + + - + - - + + +