From e7132e3371ec0f9f91032968f5197e5446d3a82f Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Tue, 28 Oct 2025 16:41:39 +0800 Subject: [PATCH] - feat: highlight size tweak - fixed: legacy onscreen controls unavailable in touch disabled mode --- VoidLink/Input/CustomOSC/OSCProfilesManager.m | 1 + .../Input/CustomOSC/OnScreenButtonState.h | 1 + .../Input/CustomOSC/OnScreenButtonState.m | 2 + VoidLink/Input/OnScreenWidgetView.swift | 291 ++++++++++-------- VoidLink/Input/StreamView.m | 6 +- VoidLink/Localization/Localizable.xcstrings | 23 ++ .../LayoutOnScreenControlsViewController.m | 117 +++++-- 7 files changed, 292 insertions(+), 149 deletions(-) diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.m b/VoidLink/Input/CustomOSC/OSCProfilesManager.m index f5395a45..01d8396a 100644 --- a/VoidLink/Input/CustomOSC/OSCProfilesManager.m +++ b/VoidLink/Input/CustomOSC/OSCProfilesManager.m @@ -396,6 +396,7 @@ static CGRect layoutViewBounds; buttonState.labelAlpha = widgetView.labelAlpha; buttonState.borderAlpha = widgetView.borderAlpha; buttonState.borderWidth = widgetView.borderWidth; + buttonState.highlightSizeFactor = widgetView.highlightSizeFactor; buttonState.autoTapInterval = widgetView.autoTapInterval; buttonState.vibrationStyle = widgetView.vibrationStyle; buttonState.mouseButtonAction = widgetView.mouseButtonAction; diff --git a/VoidLink/Input/CustomOSC/OnScreenButtonState.h b/VoidLink/Input/CustomOSC/OnScreenButtonState.h index c7f525a3..7f3c9368 100644 --- a/VoidLink/Input/CustomOSC/OnScreenButtonState.h +++ b/VoidLink/Input/CustomOSC/OnScreenButtonState.h @@ -33,6 +33,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) CGFloat widthFactor; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat heightFactor; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat borderWidth; // for OnScreenWidgetView +@property (nonatomic, assign) CGFloat highlightSizeFactor; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat sensitivityFactorX; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat sensitivityFactorY; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat yawFactor; // for OnScreenWidgetView diff --git a/VoidLink/Input/CustomOSC/OnScreenButtonState.m b/VoidLink/Input/CustomOSC/OnScreenButtonState.m index 102c433f..54eff1a6 100644 --- a/VoidLink/Input/CustomOSC/OnScreenButtonState.m +++ b/VoidLink/Input/CustomOSC/OnScreenButtonState.m @@ -55,6 +55,7 @@ [encoder encodeFloat:self.labelAlpha forKey:@"labelAlpha"]; [encoder encodeFloat:self.borderAlpha forKey:@"borderAlpha"]; [encoder encodeFloat:self.borderWidth forKey:@"borderWidth"]; + [encoder encodeFloat:self.highlightSizeFactor forKey:@"highlightSizeFactor"]; [encoder encodeObject:self.widgetShape forKey:@"widgetShape"]; [encoder encodeFloat:self.minStickOffset forKey:@"minStickOffset"]; } @@ -85,6 +86,7 @@ self.labelAlpha = [decoder containsValueForKey:@"labelAlpha"] ? [decoder decodeFloatForKey:@"labelAlpha"] : 0.82; self.borderAlpha = [decoder containsValueForKey:@"borderAlpha"] ? [decoder decodeFloatForKey:@"borderAlpha"] : 0.19; self.borderWidth = [decoder decodeFloatForKey:@"borderWidth"]; + self.highlightSizeFactor = [decoder containsValueForKey:@"highlightSizeFactor"] ? [decoder decodeFloatForKey:@"highlightSizeFactor"] : 1.0; self.widgetShape = [decoder decodeObjectForKey:@"widgetShape"]; self.minStickOffset = [decoder decodeFloatForKey:@"minStickOffset"]; } diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index c85bc1f6..6193a02e 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -54,7 +54,7 @@ import UIKit private var buttonString: String = "" private var functionalButtonString: String = "" public var motionControlButtonString: String = "" - private var touchPadString: String = "" + @objc public var touchPadString: String = "" // super combo key string set @objc public var comboButtonStrings: [String] = [] private var comboKeyTimeIntervalMs: UInt32 = 0 @@ -116,6 +116,8 @@ import UIKit @objc public var hasTrackBall: Bool = false @objc public var isFuncationalButton: Bool = false @objc public var hasHapticFeedback: Bool = false + @objc public var isDirectionPad: Bool = false + @objc public var hasL3R3Indicator: Bool = false // for all stick pads @objc public var minStickOffset: CGFloat = 0 @@ -132,10 +134,9 @@ import UIKit private let crossMarkColor: CGColor = UIColor(white: 1, alpha: 0.70).cgColor private let stickBallColor: CGColor = UIColor(white: 1, alpha: 0.75).cgColor private var stickInputScale: CGFloat = 35 - private var l3r3Indicator = CAShapeLayer() + @objc public var l3r3Indicator = CAShapeLayer() private let stickBallMaxOffset = 18.0 @objc public var crossMarkLayer = CAShapeLayer() - @objc public var stickBallLayer = CAShapeLayer() // this is for all stick pads and mouse Pad @objc public var sensitivityFactorX: CGFloat = 1.0 @@ -153,13 +154,13 @@ import UIKit @objc public var stickIndicatorOffset: CGFloat = 120 // for all LRUD pads - private var upIndicator = CAShapeLayer() - private var downIndicator = CAShapeLayer() - private var leftIndicator = CAShapeLayer() - private var rightIndicator = CAShapeLayer() + @objc public var upIndicator = CAShapeLayer() + @objc public var downIndicator = CAShapeLayer() + @objc public var leftIndicator = CAShapeLayer() + @objc public var rightIndicator = CAShapeLayer() // for DPAD LRUD pad - private var lrudIndicatorBall = CAShapeLayer() + @objc public var lrudIndicatorBall = CAShapeLayer() private let triggeringAngle = 67.5 private enum Direction: Int { case right = 1 @@ -212,8 +213,10 @@ import UIKit // whole button press down visual effect @objc public let buttonDownVisualEffectLayer = CAShapeLayer() - private var buttonDownVisualEffectWidth: CGFloat - + @objc public var buttonDownVisualEffectStandardWidth: CGFloat + @objc public var highlightSizeFactor: CGFloat = 1.0 + @objc static public var isTweakingHighlightSize: Bool = false + @objc init(cmdString: String, buttonLabel: String, shape:String) { @@ -288,7 +291,7 @@ import UIKit self.quickDoubleTapDetected = false self.touchTapTimeInterval = 100 self.touchTapTimeStamp = 100 - self.buttonDownVisualEffectWidth = 0 + self.buttonDownVisualEffectStandardWidth = 0 self.mousePointerMoved = false self.touchLockedForMoveEvent = UITouch() self.twoTouchesDetected = false @@ -351,6 +354,8 @@ import UIKit self.hasTrackBall = self.touchPadString == "TRACKBALL" self.isFuncationalButton = self.functionalButtonString != "" self.hasHapticFeedback = !self.comboButtonStrings.isEmpty || CommandManager.directionPads.contains(self.touchPadString) + self.isDirectionPad = self.widgetType == WidgetTypeEnum.touchPad && CommandManager.directionPads.contains(self.touchPadString) + self.hasL3R3Indicator = !self.isDirectionPad && self.widgetType == WidgetTypeEnum.touchPad } // ====================================================================================================== @@ -626,39 +631,46 @@ import UIKit center = storedCenter //anchor the center while resizing self - setupButtonDownVisualEffectLayer(); + hideAllHighlightLayersOfAllWidgets(selfIncluded: false) + if widgetType == WidgetTypeEnum.button {setupButtonDownVisualEffectLayer()} if CommandManager.directionPads.contains(touchPadString) {setupLrudDirectionIndicatorlayers()} - if CommandManager.stickTouchPads.contains(touchPadString) {self.l3r3Indicator = createl3r3Indicator()} - if CommandManager.verticalTouchPads.contains(touchPadString) {self.l3r3Indicator = createl3r3Indicator()} - if self.touchPadString == "MOUSEPAD" {self.l3r3Indicator = createl3r3Indicator()} + if CommandManager.stickTouchPads.contains(touchPadString) {setupL3R3Indicator()} + if CommandManager.verticalTouchPads.contains(touchPadString) {setupL3R3Indicator()} + if self.touchPadString == "MOUSEPAD" {setupL3R3Indicator()} if self.hasStickIndicator { if self.crossMarkLayer.superlayer == nil {self.crossMarkLayer = createCrossMark()} - if self.stickBallLayer.superlayer == nil {self.stickBallLayer = createStickBall()} + if self.lrudIndicatorBall.superlayer == nil {self.lrudIndicatorBall = createStickBall()} } } - private func createl3r3Indicator() -> CAShapeLayer{ + @objc public func setupL3R3Indicator() { + CATransaction.begin() + CATransaction.setDisableActions(true) + let indicatorFrame = CAShapeLayer(); - let indicatorBorder = CAShapeLayer(); + // l3r3Indicator = CAShapeLayer(); - indicatorFrame.frame = CGRectMake(0, 0, 75, 75) - indicatorFrame.cornerRadius = 9 - indicatorBorder.borderWidth = 7 - indicatorBorder.frame = indicatorFrame.bounds.insetBy(dx: -indicatorBorder.borderWidth, dy: -indicatorBorder.borderWidth) // Adjust the inset as needed - indicatorBorder.borderColor = UIColor.clear.cgColor + indicatorFrame.frame = CGRectMake(0, 0, 75*highlightSizeFactor, 75*highlightSizeFactor) + indicatorFrame.cornerRadius = 9 * highlightSizeFactor + l3r3Indicator.borderWidth = 7 * min(highlightSizeFactor,1.0) + l3r3Indicator.frame = indicatorFrame.bounds.insetBy(dx: -l3r3Indicator.borderWidth, dy: -l3r3Indicator.borderWidth) // Adjust the inset as needed + l3r3Indicator.borderColor = UIColor.clear.cgColor - indicatorBorder.cornerRadius = indicatorFrame.cornerRadius + indicatorBorder.borderWidth - indicatorBorder.backgroundColor = UIColor.clear.cgColor - indicatorBorder.fillColor = UIColor.clear.cgColor - let path = UIBezierPath(roundedRect: indicatorBorder.bounds, cornerRadius: indicatorBorder.cornerRadius) + l3r3Indicator.cornerRadius = indicatorFrame.cornerRadius + l3r3Indicator.borderWidth + l3r3Indicator.backgroundColor = UIColor.clear.cgColor + l3r3Indicator.fillColor = UIColor.clear.cgColor + let path = UIBezierPath(roundedRect: l3r3Indicator.bounds, cornerRadius: l3r3Indicator.cornerRadius) - indicatorBorder.path = path.cgPath - indicatorBorder.borderColor = voidlinkPurple - indicatorBorder.isHidden = true + l3r3Indicator.path = path.cgPath + l3r3Indicator.borderColor = voidlinkPurple + self.l3r3Indicator.position = CGPointMake(self.bounds.width/2, self.bounds.height/2) + if !OnScreenWidgetView.isTweakingHighlightSize {l3r3Indicator.isHidden = true} - self.layer.addSublayer(indicatorBorder) + if l3r3Indicator.superlayer == nil { + self.layer.addSublayer(l3r3Indicator) + } - return indicatorBorder + CATransaction.commit() } private func showl3r3Indicator(){ @@ -685,18 +697,18 @@ import UIKit private func handleStickBallReachingBorder(){ CATransaction.begin() CATransaction.setDisableActions(true) - stickBallLayer.lineWidth = 0.6 + lrudIndicatorBall.lineWidth = 0.6 // stickBallLayer.shadowOffset = CGSize(width: 0.0, height: 0.0) - stickBallLayer.shadowOffset = CGSize(width: 0.0, height: 0.0) + lrudIndicatorBall.shadowOffset = CGSize(width: 0.0, height: 0.0) // stickBallLayer.shadowColor = stickBallLayer.strokeColor CATransaction.commit() } private func handleStickBallLeavingBorder(){ - stickBallLayer.lineWidth = 0 - stickBallLayer.shadowOffset = CGSize(width: 0.5, height: 0.5) - stickBallLayer.shadowOpacity = 0.8 - stickBallLayer.shadowColor = UIColor.black.cgColor + lrudIndicatorBall.lineWidth = 0 + lrudIndicatorBall.shadowOffset = CGSize(width: 0.5, height: 0.5) + lrudIndicatorBall.shadowOpacity = 0.8 + lrudIndicatorBall.shadowColor = UIColor.black.cgColor } // create stick indicator: the crossMark & stickBall: @@ -791,8 +803,8 @@ import UIKit // Create a CAShapeLayer // self.stickBallLayer.path = path.cgPath // Assign the circular path to the shape layer // self.stickBallLayer.position = CGPointMake(CGRectGetMidX(self.crossMarkLayer.frame), CGRectGetMidY(self.crossMarkLayer.frame)) - self.stickBallLayer.position = point - self.stickBallLayer.isHidden = false + self.lrudIndicatorBall.position = point + self.lrudIndicatorBall.isHidden = false CATransaction.commit() } @@ -801,17 +813,17 @@ import UIKit @objc public func updateStickIndicator(){ CATransaction.begin() CATransaction.setDisableActions(true) - self.stickBallLayer.removeAllAnimations() + self.lrudIndicatorBall.removeAllAnimations() if !OnScreenWidgetView.editMode { let realOffsetX = touchInputToStickBallCoord(input: offSetX*sensitivityFactorX) let realOffsetY = touchInputToStickBallCoord(input: offSetY*sensitivityFactorY) - self.stickBallLayer.position = CGPointMake(touchBeganLocation.x + realOffsetX, touchBeganLocation.y + realOffsetY - self.stickIndicatorOffset) + self.lrudIndicatorBall.position = CGPointMake(touchBeganLocation.x + realOffsetX, touchBeganLocation.y + realOffsetY - self.stickIndicatorOffset) /* if fabs(realOffsetX) == stickBallMaxOffset || fabs(realOffsetY) == stickBallMaxOffset {handleStickBallReachingBorder()} else{handleStickBallLeavingBorder()}*/ } else{ - self.stickBallLayer.position = CGPointMake(CGRectGetMidX(self.crossMarkLayer.frame), CGRectGetMidY(self.crossMarkLayer.frame)-stickIndicatorOffset) + self.lrudIndicatorBall.position = CGPointMake(CGRectGetMidX(self.crossMarkLayer.frame), CGRectGetMidY(self.crossMarkLayer.frame)-stickIndicatorOffset) } CATransaction.commit() } @@ -825,7 +837,7 @@ import UIKit CATransaction.begin() CATransaction.setAnimationDuration(0.15) - self.stickBallLayer.position = CGPointMake(touchBeganLocation.x, touchBeganLocation.y - self.stickIndicatorOffset) + self.lrudIndicatorBall.position = CGPointMake(touchBeganLocation.x, touchBeganLocation.y - self.stickIndicatorOffset) CATransaction.setCompletionBlock { // 动画结束后执行的代码 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { @@ -833,7 +845,7 @@ import UIKit CATransaction.begin() CATransaction.setDisableActions(true) self.crossMarkLayer.isHidden = true - self.stickBallLayer.isHidden = true + self.lrudIndicatorBall.isHidden = true CATransaction.commit() } } @@ -858,45 +870,47 @@ import UIKit CATransaction.commit() } - private func createLrudBall() -> CAShapeLayer { + private func setupLrudBall() { // Create a circular path using UIBezierPath - let path = UIBezierPath(arcCenter: CGPoint.zero, radius: 10, startAngle: 0, endAngle: 2 * .pi, clockwise: true) + let path = UIBezierPath(arcCenter: CGPoint.zero, radius: 10*min(highlightSizeFactor,1.0), startAngle: 0, endAngle: 2 * .pi, clockwise: true) // Create a CAShapeLayer - let ballLayer = CAShapeLayer() - ballLayer.path = path.cgPath // Assign the circular path to the shape layer - self.layer.addSublayer(ballLayer) + lrudIndicatorBall.path = path.cgPath // Assign the circular path to the shape layer + if lrudIndicatorBall.superlayer == nil { + self.layer.addSublayer(lrudIndicatorBall) + } + + lrudIndicatorBall.position = CGPoint(x: self.bounds.width/2, y: self.bounds.height/2,) // Set the stroke color and width (border of the circle) - ballLayer.strokeColor = stickBallColor - ballLayer.lineWidth = 0 - ballLayer.shadowOffset = CGSize(width: 0.5, height: 0.5) - ballLayer.shadowRadius = 0; - ballLayer.shadowOpacity = 0.8 - ballLayer.name = "lrudBall" - ballLayer.isHidden = true + lrudIndicatorBall.strokeColor = stickBallColor + lrudIndicatorBall.lineWidth = 0 + lrudIndicatorBall.shadowOffset = CGSize(width: 0.5, height: 0.5) + lrudIndicatorBall.shadowRadius = 0; + lrudIndicatorBall.shadowOpacity = 0.8 + lrudIndicatorBall.name = "lrudBall" + if !OnScreenWidgetView.isTweakingHighlightSize {lrudIndicatorBall.isHidden = true} // Set the fill color (inside of the circle) - ballLayer.fillColor = stickBallColor // Light fill with some transparency - return ballLayer + lrudIndicatorBall.fillColor = stickBallColor // Light fill with some transparency } - private func createLrudDirectionLayer() -> CAShapeLayer { + private func setupLrudDirectionLayer(directionLayer:CAShapeLayer) { let indicatorFrame = CAShapeLayer(); - let indicatorBorder = CAShapeLayer(); - - indicatorFrame.frame = CGRectMake(0, 0, 75, 75) - indicatorFrame.cornerRadius = 9 - indicatorBorder.borderWidth = 6 - indicatorBorder.frame = indicatorFrame.bounds.insetBy(dx: -indicatorBorder.borderWidth, dy: -indicatorBorder.borderWidth) // Adjust the inset as needed - indicatorBorder.cornerRadius = indicatorFrame.cornerRadius + indicatorBorder.borderWidth - indicatorBorder.backgroundColor = UIColor.clear.cgColor - indicatorBorder.fillColor = UIColor.clear.cgColor - let path = UIBezierPath(roundedRect: indicatorBorder.bounds, cornerRadius: indicatorBorder.cornerRadius) - indicatorBorder.path = path.cgPath - indicatorBorder.borderColor = voidlinkPurple - - return indicatorBorder + indicatorFrame.frame = CGRectMake(0, 0, 75*highlightSizeFactor, 75*highlightSizeFactor) + indicatorFrame.cornerRadius = 9 * highlightSizeFactor + directionLayer.borderWidth = 6 * min(1.0, highlightSizeFactor) + directionLayer.frame = indicatorFrame.bounds.insetBy(dx: -directionLayer.borderWidth, dy: -directionLayer.borderWidth) // Adjust the inset as needed + directionLayer.cornerRadius = indicatorFrame.cornerRadius + directionLayer.borderWidth + directionLayer.backgroundColor = UIColor.clear.cgColor + directionLayer.fillColor = UIColor.clear.cgColor + let path = UIBezierPath(roundedRect: directionLayer.bounds, cornerRadius: directionLayer.cornerRadius) + directionLayer.path = path.cgPath + directionLayer.borderColor = voidlinkPurple + directionLayer.position = CGPoint(x: self.bounds.width/2, y: self.bounds.height/2) + if directionLayer.superlayer == nil { + self.layer.insertSublayer(directionLayer, below: lrudIndicatorBall) + } } private func showLrudDirectionIndicator(with indicatorLayer:CAShapeLayer){ @@ -1131,7 +1145,7 @@ import UIKit if OnScreenWidgetView.buttonVisualFeedbackEnabled { CATransaction.begin() CATransaction.setDisableActions(true) - buttonDownVisualEffectLayer.isHidden = false + if widgetType == WidgetTypeEnum.button {buttonDownVisualEffectLayer.isHidden = false} CATransaction.commit() } } @@ -1141,7 +1155,7 @@ import UIKit if OnScreenWidgetView.buttonVisualFeedbackEnabled { CATransaction.begin() CATransaction.setDisableActions(true) - buttonDownVisualEffectLayer.isHidden = true + if !OnScreenWidgetView.isTweakingHighlightSize {buttonDownVisualEffectLayer.isHidden = true} CATransaction.commit() } } @@ -1160,45 +1174,60 @@ import UIKit } } - private func setupLrudDirectionIndicatorlayers() { - upIndicator = createLrudDirectionLayer() + @objc public func setupLrudDirectionIndicatorlayers() { + CATransaction.begin() + CATransaction.setDisableActions(true) + + self.setupLrudBall() + + setupLrudDirectionLayer(directionLayer: upIndicator) upIndicator.anchorPoint = CGPoint(x: 0.5, y: 1) - downIndicator = createLrudDirectionLayer() + setupLrudDirectionLayer(directionLayer: downIndicator) downIndicator.anchorPoint = CGPoint(x: 0.5, y: 0) - leftIndicator = createLrudDirectionLayer() + setupLrudDirectionLayer(directionLayer: leftIndicator) leftIndicator.anchorPoint = CGPoint(x: 1, y: 0.5) - rightIndicator = createLrudDirectionLayer() + setupLrudDirectionLayer(directionLayer: rightIndicator) rightIndicator.anchorPoint = CGPoint(x: 0, y: 0.5) - self.layer.insertSublayer(leftIndicator, below: self.layer); - self.layer.insertSublayer(rightIndicator, below: self.layer); - self.layer.insertSublayer(upIndicator, below: self.layer); - self.layer.insertSublayer(downIndicator, below: self.layer); - leftIndicator.isHidden = true - rightIndicator.isHidden = true - upIndicator.isHidden = true - downIndicator.isHidden = true - /* - leftIndicator.borderColor = UIColor.clear.cgColor - rightIndicator.borderColor = UIColor.clear.cgColor - upIndicator.borderColor = UIColor.clear.cgColor - downIndicator.borderColor = UIColor.clear.cgColor */ - self.lrudIndicatorBall = self.createLrudBall() - } - - private func setupButtonDownVisualEffectLayer() { - self.buttonDownVisualEffectWidth = 8 - if self.shape == "round" { - if deNormalizedWidthFactor < 1.3 {self.buttonDownVisualEffectWidth = 15.3} // wider visual effect for osc buttons - else {self.buttonDownVisualEffectWidth = 9} + + if !OnScreenWidgetView.isTweakingHighlightSize { + leftIndicator.isHidden = true + rightIndicator.isHidden = true + upIndicator.isHidden = true + downIndicator.isHidden = true } - if self.motionControlButtonString != "" {self.buttonDownVisualEffectWidth = 3} + CATransaction.commit() + } + + @objc public func hideAllHighlightLayersOfAllWidgets(selfIncluded:Bool) { + self.forEachWidget(){ widget in + if !selfIncluded && widget == self {return} + widget.buttonDownVisualEffectLayer.isHidden = true; + widget.l3r3Indicator.isHidden = true + widget.lrudIndicatorBall.isHidden = true + widget.upIndicator.isHidden = true + widget.downIndicator.isHidden = true + widget.leftIndicator.isHidden = true + widget.rightIndicator.isHidden = true + } + } + + @objc public func setupButtonDownVisualEffectLayer() { + CATransaction.begin() + CATransaction.setDisableActions(true) + self.buttonDownVisualEffectStandardWidth = 8 + if self.shape == "round" { + if deNormalizedWidthFactor < 1.3 {self.buttonDownVisualEffectStandardWidth = 15.3} // wider visual effect for osc buttons + else {self.buttonDownVisualEffectStandardWidth = 9} + } + + if self.motionControlButtonString != "" {self.buttonDownVisualEffectStandardWidth = 3} // Set the frame to be larger than the view to expand outward - buttonDownVisualEffectLayer.borderWidth = self.buttonDownVisualEffectWidth // set this 0 to hide the visual effect first + buttonDownVisualEffectLayer.borderWidth = self.buttonDownVisualEffectStandardWidth * self.highlightSizeFactor // set this 0 to hide the visual effect first buttonDownVisualEffectLayer.borderColor = voidlinkPurple - buttonDownVisualEffectLayer.frame = self.bounds.insetBy(dx: -self.buttonDownVisualEffectWidth, dy: -self.buttonDownVisualEffectWidth) // Adjust the inset as needed - buttonDownVisualEffectLayer.cornerRadius = self.layer.cornerRadius + self.buttonDownVisualEffectWidth + buttonDownVisualEffectLayer.frame = self.bounds.insetBy(dx: -buttonDownVisualEffectLayer.borderWidth, dy: -buttonDownVisualEffectLayer.borderWidth) // Adjust the inset as needed + buttonDownVisualEffectLayer.cornerRadius = self.layer.cornerRadius + buttonDownVisualEffectLayer.borderWidth buttonDownVisualEffectLayer.backgroundColor = UIColor.clear.cgColor; buttonDownVisualEffectLayer.fillColor = UIColor.clear.cgColor; @@ -1206,10 +1235,14 @@ import UIKit let path = UIBezierPath( roundedRect: buttonDownVisualEffectLayer.bounds, cornerRadius: buttonDownVisualEffectLayer.cornerRadius) buttonDownVisualEffectLayer.path = path.cgPath - self.layer.insertSublayer(buttonDownVisualEffectLayer, below: self.layer) + if buttonDownVisualEffectLayer.superlayer == nil { + self.layer.insertSublayer(buttonDownVisualEffectLayer, below: self.layer) + } buttonDownVisualEffectLayer.position = CGPointMake(self.bounds.midX, self.bounds.midY) - buttonDownVisualEffectLayer.isHidden = true + if !OnScreenWidgetView.isTweakingHighlightSize {buttonDownVisualEffectLayer.isHidden = true} + + CATransaction.commit() } //========================================================================================================== @@ -1620,7 +1653,7 @@ import UIKit if let touch = touches.first { self.moveByTouch(touch: touch) } - self.stickBallLayer.removeFromSuperlayer() + self.lrudIndicatorBall.removeFromSuperlayer() self.crossMarkLayer.removeFromSuperlayer() } } @@ -1902,24 +1935,26 @@ import UIKit } } - if CommandManager.stickTouchPads.contains(touchPadString){ - self.l3r3Indicator.isHidden = true - } - - if CommandManager.directionPads.contains(touchPadString){ - self.upIndicator.isHidden = true - self.downIndicator.isHidden = true - self.leftIndicator.isHidden = true - self.rightIndicator.isHidden = true - self.lrudIndicatorBall.isHidden = true - } - - if CommandManager.verticalTouchPads.contains(touchPadString){ - self.l3r3Indicator.isHidden = true - } - - if self.touchPadString == "MOUSEPAD" && self.mouseButtonAction == .noClick { - self.l3r3Indicator.isHidden = true + if !OnScreenWidgetView.isTweakingHighlightSize { + if CommandManager.stickTouchPads.contains(touchPadString){ + self.l3r3Indicator.isHidden = true + } + + if CommandManager.directionPads.contains(touchPadString){ + self.upIndicator.isHidden = true + self.downIndicator.isHidden = true + self.leftIndicator.isHidden = true + self.rightIndicator.isHidden = true + self.lrudIndicatorBall.isHidden = true + } + + if CommandManager.verticalTouchPads.contains(touchPadString){ + self.l3r3Indicator.isHidden = true + } + + if self.touchPadString == "MOUSEPAD" && self.mouseButtonAction == .noClick { + self.l3r3Indicator.isHidden = true + } } if !OnScreenWidgetView.editMode && !self.cmdString.contains("+") && !self.comboButtonStrings.isEmpty { // if the command(keystring contains "+", it's a legacy multi-key command @@ -2054,7 +2089,7 @@ import UIKit if self.motionControlButtonString == "MOTION" {} buttonDownVisualEffectLayer.removeFromSuperlayer() crossMarkLayer.removeFromSuperlayer() - stickBallLayer.removeFromSuperlayer() + lrudIndicatorBall.removeFromSuperlayer() l3r3Indicator.removeFromSuperlayer() upIndicator.removeFromSuperlayer() downIndicator.removeFromSuperlayer() diff --git a/VoidLink/Input/StreamView.m b/VoidLink/Input/StreamView.m index e9fd9ab3..20c38df2 100644 --- a/VoidLink/Input/StreamView.m +++ b/VoidLink/Input/StreamView.m @@ -429,12 +429,12 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; // we'll enable on screen buttons, and disable on screen controllers for absolute touch - (bool) isOscEnabled{ - return (settings.touchMode.intValue == RelativeTouch || settings.touchMode.intValue == NativeTouch) && settings.onscreenControls.intValue != OnScreenControlsLevelOff; + return (settings.touchMode.intValue == RelativeTouch || settings.touchMode.intValue == NativeTouch || settings.touchMode.intValue == AbsoluteTouch || settings.touchMode.intValue == TouchDisabled) && settings.onscreenControls.intValue != OnScreenControlsLevelOff; } // we'll enable on screen buttons, and disable on screen controllers for absolute touch - (bool) isOnScreenWidgetEnabled{ - return (settings.touchMode.intValue == RelativeTouch || settings.touchMode.intValue == NativeTouch || settings.touchMode.intValue == AbsoluteTouch || settings.touchMode.intValue == TouchDisabled) && settings.onscreenControls.intValue == OnScreenControlsLevelCustom; + return [self isOscEnabled] && settings.onscreenControls.intValue == OnScreenControlsLevelCustom; } - (void) reloadOnScreenControlsRealtimeWith:(ControllerSupport*)controllerSupport @@ -462,6 +462,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; - (void) clearOnScreenWidgets{ + OnScreenWidgetView.isTweakingHighlightSize = false; for (UIView *subview in self->streamFrameTopLayerView.subviews) { // 检查子视图是否是特定类型的实例 if ([subview isKindOfClass:[OnScreenWidgetView class]]) { @@ -556,6 +557,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; widgetView.widthFactor = buttonState.widthFactor; widgetView.heightFactor = buttonState.heightFactor; widgetView.borderWidth = buttonState.borderWidth; + widgetView.highlightSizeFactor = buttonState.highlightSizeFactor; widgetView.autoTapInterval = buttonState.autoTapInterval; [widgetView setVibrationWithStyle:buttonState.vibrationStyle]; widgetView.mouseButtonAction = buttonState.mouseButtonAction; diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 8500bae8..a7835276 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -2563,6 +2563,29 @@ } } }, + "Highlight size: %.2f" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "高亮尺寸: %.2f" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "高亮尺寸: %.2f" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "高亮尺寸: %.2f" + } + } + } + }, "Host information updated" : { "extractionState" : "manual", "localizations" : { diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index f4bfe5d2..64768dd4 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -28,6 +28,12 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { AlphaSliderModeCount }; +typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { + widgetBorderWidth, + hightlightSize, + BorderWidthSliderModeCount +}; + @end @@ -36,6 +42,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { OSCProfilesManager* profilesManager; OnScreenWidgetView* selectedWidgetView; AlphaSliderMode alphaSliderMode; + BorderWidthSliderMode borderWidthSliderMode; CALayer* selectedControllerLayer; CGRect controllerLoadedBounds; bool widgetViewSelected; @@ -73,10 +80,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { - (void) viewWillDisappear:(BOOL)animated{ OnScreenWidgetView.editMode = false; - for (OnScreenWidgetView* widgetView in self.onScreenWidgetViews){ - [widgetView.stickBallLayer removeFromSuperlayer]; - [widgetView.crossMarkLayer removeFromSuperlayer]; - } + OnScreenWidgetView.isTweakingHighlightSize = false; [super viewWillDisappear:animated]; [[NSNotificationCenter defaultCenter] postNotificationName:@"OscLayoutCloseNotification" object:self]; } @@ -114,6 +118,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { - (void)reloadOnScreenWidgetViews{ NSLog(@"reloadOnScreenWidgets %f", CACurrentMediaTime()); + OnScreenWidgetView.isTweakingHighlightSize = false; OnScreenWidgetView.editMode = true; [self hideStickIndicators]; @@ -142,6 +147,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { widgetView.widthFactor = buttonState.widthFactor; widgetView.heightFactor = buttonState.heightFactor; widgetView.borderWidth = buttonState.borderWidth; + widgetView.highlightSizeFactor = buttonState.highlightSizeFactor; widgetView.autoTapInterval = buttonState.autoTapInterval; [widgetView setVibrationWithStyle:buttonState.vibrationStyle]; widgetView.mouseButtonAction = buttonState.mouseButtonAction; @@ -373,6 +379,57 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { } } +- (void)switchBorderWidthSlider:(UISwipeGestureRecognizer *)sender { + if(!widgetViewSelected) return; + borderWidthSliderMode = (borderWidthSliderMode + 1) % BorderWidthSliderModeCount; + OnScreenWidgetView.isTweakingHighlightSize = borderWidthSliderMode == hightlightSize; + [self loadWidgetWidths]; +} + +- (void)loadWidgetWidths{ + if(self->selectedWidgetView != nil && self->widgetViewSelected){ + switch(borderWidthSliderMode){ + case widgetBorderWidth: + [self.widgetBorderWidthSlider setMinimumValue:0]; + [self.widgetBorderWidthSlider setMaximumValue:8]; + [self.widgetBorderWidthSlider setValue:selectedWidgetView.borderWidth animated:NO]; + + selectedWidgetView.buttonDownVisualEffectLayer.hidden = true; + selectedWidgetView.l3r3Indicator.hidden = true; + + (selectedWidgetView.lrudIndicatorBall.hidden + = selectedWidgetView.upIndicator.hidden + = selectedWidgetView.downIndicator.hidden + = selectedWidgetView.leftIndicator.hidden + = selectedWidgetView.rightIndicator.hidden + = true); + + [self.widgetBorderWidthLabel setText:[LocalizationHelper localizedStringForKey:@"Border width: %.2f", _widgetBorderWidthSlider.value]]; + break; + + case hightlightSize: + [self.widgetBorderWidthSlider setMinimumValue:0]; + [self.widgetBorderWidthSlider setMaximumValue:2]; + [self.widgetBorderWidthSlider setValue:selectedWidgetView.highlightSizeFactor animated:NO]; + selectedWidgetView.buttonDownVisualEffectLayer.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton; + selectedWidgetView.l3r3Indicator.hidden = !selectedWidgetView.hasL3R3Indicator; + + (selectedWidgetView.lrudIndicatorBall.hidden + = selectedWidgetView.upIndicator.hidden + = selectedWidgetView.downIndicator.hidden + = selectedWidgetView.leftIndicator.hidden + = selectedWidgetView.rightIndicator.hidden + = !selectedWidgetView.isDirectionPad); + + [self.widgetBorderWidthLabel setText:[LocalizationHelper localizedStringForKey:@"Highlight size: %.2f", _widgetBorderWidthSlider.value]]; + break; + + default: + break; + } + } +} + /* animates the toolbar up and off the screen or back down onto the screen */ - (void) moveToolbar:(UISwipeGestureRecognizer *)sender { BOOL isPad = [[UIDevice currentDevice].model hasPrefix:@"iPad"]; @@ -683,6 +740,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { newWidget.widthFactor = widget.widthFactor; newWidget.heightFactor = widget.heightFactor; newWidget.borderWidth = widget.borderWidth; + newWidget.highlightSizeFactor = widget.highlightSizeFactor; newWidget.autoTapInterval = widget.autoTapInterval; newWidget.sensitivityFactorX = widget.sensitivityFactorX; newWidget.sensitivityFactorY = widget.sensitivityFactorY; @@ -695,6 +753,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { newWidget.mouseButtonAction = widget.mouseButtonAction; newWidget.buttonMode = widget.buttonMode; [self.view insertSubview:newWidget belowSubview:self.widgetPanelStack]; + [newWidget accessWidgetAttributes]; if(createNew) [newWidget setLocationWithPosition:CGPointMake(90, 130)]; else [newWidget setLocationWithPosition:widget.center]; @@ -779,7 +838,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { if([view isKindOfClass:[OnScreenWidgetView class]]){ OnScreenWidgetView* widget = (OnScreenWidgetView* )view; [widget.crossMarkLayer setHidden:true]; - [widget.stickBallLayer setHidden:true]; + [widget.lrudIndicatorBall setHidden:true]; } } } @@ -802,10 +861,14 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { self->widgetViewSelected = true; self->controllerLayerSelected = false; - if(widgetView != selectedWidgetView) [selectedWidgetView setAutoTapIntervalByTextWithStr:_autoTapField.text]; + if(widgetView != selectedWidgetView){ + [selectedWidgetView setAutoTapIntervalByTextWithStr:_autoTapField.text]; + } self->selectedWidgetView = widgetView; - + + [selectedWidgetView hideAllHighlightLayersOfAllWidgetsWithSelfIncluded:YES]; + [self autoFitLabel:self.currentProfileLabel]; self.currentProfileLabel.textAlignment = NSTextAlignmentLeft; [self.currentProfileLabel setText: @@ -878,13 +941,13 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { [self autoFitLabel:self.widgetHeightLabel]; [self.widgetHeightLabel setText:[LocalizationHelper localizedStringForKey:@"Height: %.2f", self->selectedWidgetView.deNormalizedHeightFactor]]; - [self loadWidgetAlphas]; [self autoFitLabel:self.widgetAlphaLabel]; - [self.widgetBorderWidthSlider setValue:self->selectedWidgetView.borderWidth]; + // [self.widgetBorderWidthSlider setValue:self->selectedWidgetView.borderWidth]; + [self loadWidgetWidths]; [self autoFitLabel:self.widgetBorderWidthLabel]; - [self widgetBorderWidthSliderMoved:self.widgetBorderWidthSlider]; + // [self widgetBorderWidthSliderMoved:self.widgetBorderWidthSlider]; self.autoTapStack.hidden = !selectedWidgetView.hasAutoTap; // [self.autoTapSlider setValue:self->selectedWidgetView.autoTapInterval]; @@ -1013,16 +1076,25 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { if(self->selectedControllerLayer != nil && self->controllerLayerSelected){ [self.layoutOSC adjustControllerLayerOpacityWith:self->selectedControllerLayer and:sender.value]; } - - return; } - (void)widgetBorderWidthSliderMoved:(UISlider* )sender{ - [self.widgetBorderWidthLabel setText:[LocalizationHelper localizedStringForKey:@"Border width: %.2f", sender.value]]; if(self->selectedWidgetView != nil && self->widgetViewSelected){ - [self->selectedWidgetView adjustBorderWithWidth:sender.value]; + switch(borderWidthSliderMode){ + case widgetBorderWidth: + [self->selectedWidgetView adjustBorderWithWidth:sender.value]; + break; + case hightlightSize: + self->selectedWidgetView.highlightSizeFactor = sender.value; + if(selectedWidgetView.widgetType == WidgetTypeEnumButton) [selectedWidgetView setupButtonDownVisualEffectLayer]; + if(selectedWidgetView.hasL3R3Indicator) [selectedWidgetView setupL3R3Indicator]; + if(selectedWidgetView.isDirectionPad) [selectedWidgetView setupLrudDirectionIndicatorlayers]; + break; + default: + break; + } } - return; + [self loadWidgetWidths]; } /* @@ -1040,7 +1112,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { } } -- (void)slideModeChanged:(UISegmentedControl* )sender{ +- (void)buttonModeChanged:(UISegmentedControl* )sender{ if(self->selectedWidgetView != nil && self->widgetViewSelected){ selectedWidgetView.buttonMode = _buttonModeSelector.selectedSegmentIndex; } @@ -1201,6 +1273,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { self.widgetHeightLabel.text = [LocalizationHelper localizedStringForKey:@"Height"]; self.widgetHeightStack.hidden = YES; + alphaSliderMode = widgetAlpha; [self.widgetAlphaSlider addTarget:self action:@selector(widgetAlphaSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; self.widgetAlphaLabel.text = [LocalizationHelper localizedStringForKey:@"Alpha"]; self.widgetAlphaLabel.userInteractionEnabled = true; @@ -1209,10 +1282,17 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { action:@selector(switchAlphaSlider:)]; [self.widgetAlphaLabel addGestureRecognizer:alphaSwitchTapGesture]; - + borderWidthSliderMode = widgetBorderWidth; [self.widgetBorderWidthSlider addTarget:self action:@selector(widgetBorderWidthSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; self.widgetBorderWidthLabel.text = [LocalizationHelper localizedStringForKey:@"Border width"]; + self.widgetBorderWidthLabel.userInteractionEnabled = true; + UITapGestureRecognizer *borderWidthSwitchTapGesture = + [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(switchBorderWidthSlider:)]; + [self.widgetBorderWidthLabel addGestureRecognizer:borderWidthSwitchTapGesture]; + self.borderWidthAlphaStack.hidden = YES; + // [self.autoTapSlider addTarget:self action:@selector(autoTapSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; self.autoTapLabel.text = [LocalizationHelper localizedStringForKey:@"Autotap timer "]; @@ -1254,7 +1334,7 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { [self.mouseButtonDownSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal]; self.mouseDownButtonStack.hidden = YES; - [self.buttonModeSelector addTarget:self action:@selector(slideModeChanged:) forControlEvents:(UIControlEventValueChanged)]; + [self.buttonModeSelector addTarget:self action:@selector(buttonModeChanged:) forControlEvents:(UIControlEventValueChanged)]; [self.buttonModeSelector setTitleTextAttributes:whiteFontAttributes forState:UIControlStateNormal]; self.buttonModeStack.hidden = YES; @@ -1524,7 +1604,6 @@ typedef NS_ENUM(NSUInteger, AlphaSliderMode) { if(!isToolbarHidden && self->selectedWidgetView != nil && [self layerIsOverlappingWithTrashcanButton:selectedWidgetView.layer]){ [self->selectedWidgetView removeFromSuperview]; [self.onScreenWidgetViews removeObject:self->selectedWidgetView]; - [selectedWidgetView.buttonDownVisualEffectLayer removeFromSuperlayer]; }