diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard
index b5ff27ed..6ab239d2 100644
--- a/VoidLink/Base.lproj/iPad.storyboard
+++ b/VoidLink/Base.lproj/iPad.storyboard
@@ -1396,7 +1396,7 @@
-
+
-
+
@@ -1588,10 +1588,44 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1631,6 +1665,8 @@
+
+
diff --git a/VoidLink/Base.lproj/iPhone.storyboard b/VoidLink/Base.lproj/iPhone.storyboard
index 9fb1c0e7..3af8470f 100644
--- a/VoidLink/Base.lproj/iPhone.storyboard
+++ b/VoidLink/Base.lproj/iPhone.storyboard
@@ -13,7 +13,7 @@
-
+
@@ -314,7 +314,7 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -597,6 +634,8 @@
+
+
@@ -1684,16 +1723,16 @@
-
+
-
+
-
+
-
+
diff --git a/VoidLink/Input/CommandManager.swift b/VoidLink/Input/CommandManager.swift
index d07cc750..2266a801 100644
--- a/VoidLink/Input/CommandManager.swift
+++ b/VoidLink/Input/CommandManager.swift
@@ -90,6 +90,7 @@ import UIKit
"OSCDOWN" : DOWN_FLAG,
"OSCLEFT" : LEFT_FLAG,
"OSCRIGHT" : RIGHT_FLAG,
+ "DS4TOUCH" : TOUCHPAD_FLAG,
"OSCL2" : 0,
"L2" : 0,
"LT" : 0,
@@ -109,7 +110,7 @@ import UIKit
"OSCBACK"
]
- @objc public static let touchPadCmds: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD", "MOUSEPAD", "DPAD", "TRACKBALL", "WASDPAD", "ARROWPAD"]
+ @objc public static let touchPadCmds: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD", "DS4TOUCH", "MOUSEPAD", "DPAD", "TRACKBALL", "WASDPAD", "ARROWPAD"]
@objc public static let stickTouchPads: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD"]
@objc public static let nonVectorStickPads: [String] = ["LSPAD", "RSPAD"]
@objc public static let specialOverlayButtonCmds: [String] = ["SETTINGS"]
diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.m b/VoidLink/Input/CustomOSC/OSCProfilesManager.m
index 9a18105f..6f232993 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;
@@ -388,6 +387,7 @@ static CGRect layoutViewBounds;
buttonState.stickIndicatorOffset = widgetView.stickIndicatorOffset;
buttonState.widgetShape = widgetView.shape;
buttonState.minStickOffset = widgetView.minStickOffset;
+ buttonState.slideMode = widgetView.slideMode;
NSData *buttonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
[buttonStatesEncoded addObject: buttonStateEncoded];
@@ -396,6 +396,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/CustomOSC/OnScreenButtonState.h b/VoidLink/Input/CustomOSC/OnScreenButtonState.h
index 427c4dfe..12a37e6a 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) uint8_t slideMode;
@property (nonatomic, assign) uint8_t buttonType;
@property (nonatomic, assign) CGFloat widthFactor; // for OnScreenWidgetView
@property (nonatomic, assign) CGFloat heightFactor; // for OnScreenWidgetView
@@ -56,6 +57,13 @@ typedef NS_ENUM(NSInteger, MouseButtonAction) {
rightButtonDown,
};
+typedef NS_ENUM(NSInteger, ButtonSlideMode) {
+ toggle,
+ slideAndHold,
+ disabled
+};
+
+
- (id) initWithButtonName:(NSString*)name buttonType:(uint8_t)buttonType andPosition:(CGPoint)position;
diff --git a/VoidLink/Input/CustomOSC/OnScreenButtonState.m b/VoidLink/Input/CustomOSC/OnScreenButtonState.m
index a91041f8..95df86d2 100644
--- a/VoidLink/Input/CustomOSC/OnScreenButtonState.m
+++ b/VoidLink/Input/CustomOSC/OnScreenButtonState.m
@@ -35,6 +35,7 @@
[encoder encodeInt:self.buttonType forKey:@"buttonType"];
[encoder encodeInt:self.vibrationStyle forKey:@"vibrationStyle"];
[encoder encodeInt:self.mouseButtonAction forKey:@"mouseButtonAction"];
+ [encoder encodeInt:self.slideMode forKey:@"slideMode"];
[encoder encodeCGPoint:self.position forKey:@"position"];
[encoder encodeBool:self.isHidden forKey:@"isHidden"];
[encoder encodeFloat:self.widthFactor forKey:@"widthFactor"];
@@ -57,6 +58,7 @@
self.buttonType = [decoder decodeIntForKey:@"buttonType"];
self.vibrationStyle = [decoder decodeIntForKey:@"vibrationStyle"];
self.mouseButtonAction = [decoder decodeIntForKey:@"mouseButtonAction"];
+ self.slideMode = [decoder containsValueForKey:@"slideMode"] ? [decoder decodeIntForKey:@"slideMode"] : 0;
self.position = [decoder decodeCGPointForKey:@"position"];
self.isHidden = [decoder decodeBoolForKey:@"isHidden"];
self.widthFactor = [decoder decodeFloatForKey:@"widthFactor"];
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 48bf2aac..d802b1f3 100644
--- a/VoidLink/Input/OnScreenWidgetView.swift
+++ b/VoidLink/Input/OnScreenWidgetView.swift
@@ -45,6 +45,11 @@ import UIKit
@objc public var pressed: Bool
@objc public var widthFactor: CGFloat = 1.0
@objc public var heightFactor: CGFloat = 1.0
+ @objc public var slideMode: Int = 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
@@ -131,8 +136,16 @@ 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()
+
+ //controller touch pad
+ private var pointerIdPool: Set
+ private var pointerIdDict: Dictionary
+ private var activePointerIds: Set
// whole button press down visual effect
@objc public let buttonDownVisualEffectLayer = CAShapeLayer()
@@ -180,6 +193,8 @@ import UIKit
self.comboButtonStrings = ["OSCL3"]
case "RSPAD", "RSVPAD":
self.comboButtonStrings = ["OSCR3"]
+ case "DS4TOUCH":
+ self.comboButtonStrings = ["DS4TOUCH"]
default: break
}
}
@@ -223,6 +238,13 @@ import UIKit
self.stickIndicatorOffset = 95
self.sensitivityFactorX = 1.0
self.sensitivityFactorY = 1.0
+ self.capturedTouches = NSMutableSet()
+ self.pointerIdDict = [:]
+ self.pointerIdPool = []
+ for i in 0...10 { // iPadOS supports up to 11 finger touches
+ self.pointerIdPool.insert(UInt32(i))
+ }
+ self.activePointerIds = []
super.init(frame: .zero)
upIndicator = createLrudDirectionLayer()
@@ -319,7 +341,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 +378,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 +425,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() {
@@ -464,7 +515,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)
@@ -658,7 +709,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
}
@@ -668,7 +719,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(){
@@ -718,6 +777,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)
@@ -736,6 +796,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)
@@ -754,6 +815,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)
@@ -772,6 +834,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)
@@ -853,14 +916,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()
@@ -869,7 +932,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
@@ -882,7 +946,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}
}
@@ -1008,11 +1072,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()
@@ -1073,6 +1137,11 @@ import UIKit
self.sendComboButtonsUpEvent(comboStrings: self.comboButtonStrings)
}
}
+ case "DS4TOUCH":
+ if quickDoubleTapDetected {
+ self.l3r3Indicator = self.createAndShowl3r3Indicator()
+ self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings)
+ }
default:
break
}
@@ -1093,25 +1162,15 @@ import UIKit
}
}
- /*
- if !self.buttonString.isEmpty {
- // if there's no "+" in the keystring, treat it as a regular button:
- if CommandManager.oscButtonMappings.keys.contains(self.cmdString) {
- self.sendOscButtonDownEvent(keyString: self.cmdString)
- }
- if CommandManager.keyboardButtonMappings.keys.contains(self.cmdString) {
- LiSendLiSendKeyboardEvent(CommandManager.keyboardButtonMappings[self.cmdString]!,Int8(KEY_ACTION_DOWN), 0)
- }
- if CommandManager.mouseButtonMappings.keys.contains(self.cmdString) {
- LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), Int32(CommandManager.mouseButtonMappings[self.cmdString]!))
- }
+ if self.widgetType == WidgetTypeEnum.touchPad && self.touchPadString == "DS4TOUCH" {
+ self.handleControllerTouchesDown(touches: touches)
}
- */
-
+
// 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 "+"
@@ -1119,13 +1178,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
}
}
@@ -1151,16 +1210,100 @@ import UIKit
}
}
+ private func handleControllerTouchesDown(touches: Set) {
+ for touch in touches{
+ let availablePointerIds = pointerIdPool.subtracting(activePointerIds)
+ if let pointerId = availablePointerIds.first {
+ pointerIdDict[ObjectIdentifier(touch)] = pointerId
+ let coordX = touch.location(in: self).x/self.bounds.width
+ let coordY = touch.location(in: self).y/self.bounds.height
+ LiSendControllerTouchEvent(0, UInt8(LI_TOUCH_EVENT_DOWN), pointerId, Float(coordX), Float(coordY), 1)
+ activePointerIds.insert(pointerId)
+ }
+ }
+ }
+
+ private func handleControllerTouchesMove(touches: Set) {
+ for touch in touches{
+ if let pointerId = pointerIdDict[ObjectIdentifier(touch)] {
+ let coordX = touch.location(in: self).x/self.bounds.width
+ let coordY = touch.location(in: self).y/self.bounds.height
+ LiSendControllerTouchEvent(0, UInt8(LI_TOUCH_EVENT_MOVE), pointerId, Float(coordX), Float(coordY), 1)
+ }
+ }
+ }
+
+ private func handleControllerTouchesUp(touches: Set) {
+ for touch in touches{
+ if let pointerId = pointerIdDict[ObjectIdentifier(touch)] {
+ let coordX = touch.location(in: self).x/self.bounds.width
+ let coordY = touch.location(in: self).y/self.bounds.height
+ LiSendControllerTouchEvent(0, UInt8(LI_TOUCH_EVENT_UP), pointerId, Float(coordX), Float(coordY), 1)
+ activePointerIds.remove(pointerId)
+ pointerIdDict.removeValue(forKey: ObjectIdentifier(touch))
+ }
+ }
+ }
+
+
+ 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.slideMode == ButtonSlideMode.disabled.rawValue {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.slideMode == ButtonSlideMode.disabled.rawValue {continue}
+ widget.capturedTouches.add(touch)
+ widget.handleButtonDown()
+ // print("UIButton: \(widget.buttonLabel) in, \(widget.touchPadString), \(CACurrentMediaTime())")
+ }
+ else{
+ if !widget.capturedTouches.contains(touch) || widget.slideMode == ButtonSlideMode.disabled.rawValue {continue}
+ // print("UIButton: \(widget.buttonLabel) out test, \(widget.touchPadString), \(CACurrentMediaTime())")
+ if(widget.slideMode == ButtonSlideMode.toggle.rawValue){
+ widget.capturedTouches.remove(touch)
+ widget.handlebuttonUp()
+ }
+ if(widget.slideMode == ButtonSlideMode.slideAndHold.rawValue){
+ // do nothing here
+ }
+ }
+ }
+ }
+ }
+ }
+
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.slideMode != ButtonSlideMode.disabled.rawValue {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()
}
}
}
@@ -1195,7 +1338,6 @@ import UIKit
private func handleTouchPadMoveEvent (_ touches: Set, with event: UIEvent?){
if touches.count == 1{ // don't use event.alltouches.count here, it will counts all touches
-
switch self.touchPadString{
case "MOUSEPAD":
DispatchQueue.global(qos: .userInteractive).async {
@@ -1240,6 +1382,9 @@ import UIKit
break
}
}
+ if self.widgetType == WidgetTypeEnum.touchPad && self.touchPadString == "DS4TOUCH" {
+ self.handleControllerTouchesMove(touches: touches)
+ }
}
override func touchesEnded(_ touches: Set, with event: UIEvent?) {
@@ -1320,6 +1465,8 @@ import UIKit
self.onScreenControls.releaseControllerButton(RIGHT_FLAG)
self.onScreenControls.releaseControllerButton(UP_FLAG)
self.onScreenControls.releaseControllerButton(DOWN_FLAG)
+ case "DS4TOUCH":
+ self.handleControllerTouchesUp(touches: touches)
default:
break
}
@@ -1332,7 +1479,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){
@@ -1384,7 +1532,7 @@ import UIKit
}
}
- self.buttonUpVisualEffect()
+ self.handlebuttonUp()
}
}
diff --git a/VoidLink/Input/RelativeTouchHandler.m b/VoidLink/Input/RelativeTouchHandler.m
index 46a2d932..0aba3acc 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;
}
@@ -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 04c130bc..45beb39f 100644
--- a/VoidLink/Input/StreamView.m
+++ b/VoidLink/Input/StreamView.m
@@ -234,12 +234,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.
@@ -259,7 +259,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){
@@ -325,7 +325,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];
@@ -499,6 +499,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
widgetView.trackballDecelerationRate = buttonState.decelerationRate;
widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset;
widgetView.minStickOffset = buttonState.minStickOffset;
+ widgetView.slideMode = buttonState.slideMode;
// 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 25a389ad..c5761881 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.slideMode = buttonState.slideMode;
// 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.slideMode = widget.slideMode;
[self.view insertSubview:newWidget belowSubview:self.widgetPanelStack];
if(createNew) [newWidget setLocationWithPosition:CGPointMake(90, 130)];
@@ -714,6 +716,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,11 +747,14 @@
[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];
+ self.slidableStack.hidden = selectedWidgetView.widgetType != WidgetTypeEnumButton;
+ [self.slidableSelector setSelectedSegmentIndex:selectedWidgetView.slideMode];
+
bool showSensitivityFactorStack = selectedWidgetView.hasSensitivityTweak;
bool showStickIndicatorOffsetStack = selectedWidgetView.hasStickIndicator;
@@ -772,10 +784,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 +811,7 @@
}
}
+
- (void)legacyOscLayerTapped: (NSNotification *)notification{
[self enableCommonWidgetTools];
CALayer* controllerLayer = (CALayer* )notification.object;
@@ -891,6 +906,12 @@
}
}
+- (void)slideModeChanged:(UISegmentedControl* )sender{
+ if(self->selectedWidgetView != nil && self->widgetViewSelected){
+ selectedWidgetView.slideMode = _slidableSelector.selectedSegmentIndex;
+ }
+}
+
- (void)vibrationStyleChanged:(UISegmentedControl* )sender{
bool vibraiontOn;
if (@available(iOS 13.0, *)) {
@@ -1036,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(slideModeChanged:) 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 f4f7b064..e671f8e0 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/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m
index 7ff53399..66a1878f 100644
--- a/VoidLink/ViewControllers/SettingsViewController.m
+++ b/VoidLink/ViewControllers/SettingsViewController.m
@@ -1598,9 +1598,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];
diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m
index 77994851..78019051 100644
--- a/VoidLink/ViewControllers/StreamFrameViewController.m
+++ b/VoidLink/ViewControllers/StreamFrameViewController.m
@@ -1442,7 +1442,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
diff --git a/VoidLink/mul.lproj/iPad.xcstrings b/VoidLink/mul.lproj/iPad.xcstrings
index 8d567abf..d7e87e2c 100644
--- a/VoidLink/mul.lproj/iPad.xcstrings
+++ b/VoidLink/mul.lproj/iPad.xcstrings
@@ -1099,6 +1099,96 @@
}
}
},
+ "cKS-fy-Zpg.segmentTitles[0]" : {
+ "comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[0] = \"toggle\"; ObjectID = \"cKS-fy-Zpg\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "toggle"
+ }
+ },
+ "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] = \"slide & hold\"; ObjectID = \"cKS-fy-Zpg\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "slide & hold"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "滑动时按下"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "滑動時按下"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "滑動時按下"
+ }
+ }
+ }
+ },
+ "cKS-fy-Zpg.segmentTitles[2]" : {
+ "comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[2] = \"disabled\"; ObjectID = \"cKS-fy-Zpg\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "disabled"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "关闭"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "關閉"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "關閉\n"
+ }
+ }
+ }
+ },
"CpY-Sb-BDc.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"CpY-Sb-BDc\";",
"extractionState" : "extracted_with_value",
@@ -1201,6 +1291,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",
@@ -2797,6 +2899,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..06d173ba 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,96 @@
}
}
},
+ "ojp-aS-m8w.segmentTitles[0]" : {
+ "comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[0] = \"toggle\"; ObjectID = \"ojp-aS-m8w\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "toggle"
+ }
+ },
+ "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] = \"slide & hold\"; ObjectID = \"ojp-aS-m8w\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "slide & hold"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "滑动时按下"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "滑動時按下"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "滑動時按下"
+ }
+ }
+ }
+ },
+ "ojp-aS-m8w.segmentTitles[2]" : {
+ "comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[2] = \"disabled\"; ObjectID = \"ojp-aS-m8w\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "disabled"
+ }
+ },
+ "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",