diff --git a/VoidLink/Input/CustomOSC/OSCProfile.m b/VoidLink/Input/CustomOSC/OSCProfile.m index 47bc0ade..d7a06bd6 100644 --- a/VoidLink/Input/CustomOSC/OSCProfile.m +++ b/VoidLink/Input/CustomOSC/OSCProfile.m @@ -58,6 +58,7 @@ [encoder encodeFloat:self.touchPointerVelocityFactor forKey:@"touchPointerVelocityFactor"]; [encoder encodeCGPoint:self.normalizedStreamViewOffset forKey:@"normalizedStreamViewOffset"]; [encoder encodeFloat:self.streamViewScale forKey:@"streamViewScale"]; + [encoder encodeBool:self.gamepadOverlayEnabled forKey:@"gamepadOverlayEnabled"]; [encoder encodeObject:self.pressureCurvePoints forKey:@"pressureCurvePoints"]; // [encoder encodeObject:self.initialTouchPressureCurvePoints forKey:@"initialTouchPressureCurvePoints"]; @@ -213,7 +214,6 @@ copy.isSelected = self.isSelected; copy.useBuiltinGyro = self.useBuiltinGyro; copy.swapYawAndRoll = self.swapYawAndRoll; - copy.isSelected = self.isSelected; copy.mapGyroTo = self.mapGyroTo; copy.yawPitchToRightStick = self.yawPitchToRightStick; copy.rollToLeftStick = self.rollToLeftStick; diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.m b/VoidLink/Input/CustomOSC/OSCProfilesManager.m index d2fadfd6..b695b18e 100644 --- a/VoidLink/Input/CustomOSC/OSCProfilesManager.m +++ b/VoidLink/Input/CustomOSC/OSCProfilesManager.m @@ -286,7 +286,8 @@ static CGRect layoutViewBounds; // NSString* persistedKey = @"widgetProfileUpdated-20251015"; // NSString* persistedKey = @"widgetProfileUpdated-20260322"; - NSString* persistedKey = @"widgetProfileUpdated-20260413-2"; + // NSString* persistedKey = @"widgetProfileUpdated-20260413-2"; + NSString* persistedKey = @"widgetProfileUpdated-20260430-1"; BOOL needImportDefaultTemplates = [defaults objectForKey:persistedKey] == nil; if(profiles.count == 0 || needImportDefaultTemplates){ @@ -462,8 +463,8 @@ static CGRect layoutViewBounds; buttonState.widthFactor = [self normalizeSizeWidthFactorWith:widgetView]; buttonState.heightFactor = [self normalizeSizeHeightFactorWith:widgetView]; buttonState.componentSizeFactor = [self normalizeComponentSizeFactorWith:widgetView]; - buttonState.backgroundAlpha = widgetView.backgroundAlpha; - buttonState.labelAlpha = widgetView.labelAlpha; + buttonState.backgroundAlpha = widgetView.originalBackgroundAlpha; + buttonState.labelAlpha = widgetView.originalLabelAlpha; buttonState.borderAlpha = widgetView.borderAlpha; buttonState.highlightAlpha = widgetView.highlightAlpha; buttonState.borderWidth = widgetView.borderWidth; diff --git a/VoidLink/Input/InputAccessoryBar.swift b/VoidLink/Input/InputAccessoryBar.swift index b3b3ee18..394f6808 100644 --- a/VoidLink/Input/InputAccessoryBar.swift +++ b/VoidLink/Input/InputAccessoryBar.swift @@ -307,7 +307,7 @@ private final class InputAccessoryCapsuleButton: UIButton { if isTapToToggleMode { if isLogicallyPressed { - if !InputAccessoryBar.isEditingItems {CommandManager.shared.sendAutoReleaseComboCommand(cmdStrings: comboButtons,pressOnly: true)} + if !InputAccessoryBar.isEditingItems {CommandManager.shared.sendAutoReleaseComboCommand(cmdStrings: comboButtons, delay: 0.1, pressOnly: true)} } else { CommandManager.shared.sendAutoReleaseComboCommand(cmdStrings: comboButtons, releaseOnly: true) @@ -316,7 +316,7 @@ private final class InputAccessoryCapsuleButton: UIButton { } else { isLogicallyPressed = false - if !InputAccessoryBar.isEditingItems {CommandManager.shared.sendAutoReleaseComboCommand(cmdStrings: comboButtons)} + if !InputAccessoryBar.isEditingItems {CommandManager.shared.sendAutoReleaseComboCommand(cmdStrings: comboButtons, delay: 0.1)} } guard isPressVisualActive || isPressInAnimationRunning else { return } @@ -1208,6 +1208,8 @@ final class InputAccessoryBar: UIView, UIScrollViewDelegate, WidgetPickerViewCon InputAccessoryBarButtonRecord(buttonLabel: "Tab", cmdString: "TAB+Null"), InputAccessoryBarButtonRecord(buttonLabel: "←", cmdString: "LEFTARR"), InputAccessoryBarButtonRecord(buttonLabel: "→", cmdString: "RIGHTARR"), + InputAccessoryBarButtonRecord(buttonLabel: "PgUp", cmdString: "PgUp"), + InputAccessoryBarButtonRecord(buttonLabel: "PgDn", cmdString: "PgDown"), InputAccessoryBarButtonRecord(buttonLabel: "Del", cmdString: "DEL"), ] } diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 2c2e3cf2..e711c7d7 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -138,7 +138,7 @@ import ObjectiveC.runtime @objc public var storedCenter: CGPoint = .zero // location from persisted data @objc public var initialCenter: CGPoint = .zero // location from persisted data @objc public var layoutChanges: [CGPoint] = [] - @objc public var mouseButtonAction: MouseButtonAction = .hovering; + @objc public var mouseButtonAction: MouseButtonAction = .noClick; @objc public var mouseButtonActionDelay: TimeInterval = 0.005; //autoTapTimer @@ -237,6 +237,7 @@ import ObjectiveC.runtime @objc public var pitchFactor: CGFloat = 1.0 @objc public var rollFactor: CGFloat = 1.0 private var gyroControlPreviousStatus: NSMutableDictionary = NSMutableDictionary() + private var isFirstTappingOnscreenGyroButton: Bool = true private var superViewWidth: CGFloat = 0 private var superViewHeight: CGFloat = 0 @@ -2663,6 +2664,10 @@ import ObjectiveC.runtime } private func handleMotionControlButtonDown(){ + if isFirstTappingOnscreenGyroButton, GenericUtils.isFirstTappingOnscreenGyroButton() { + self.popGyroButtonTip() + } + isFirstTappingOnscreenGyroButton = false switch self.motionControlButtonString { case "GYRO": self.motionHandler?.startGyroByOnScreenButton(self, yawFactor: yawFactor, pitchFactor: pitchFactor, rollFactor: rollFactor) @@ -3232,7 +3237,17 @@ import ObjectiveC.runtime countdown: 5, ) } - + + private func popGyroButtonTip() { + AlertControllerUtil.showAlert( + in: self.parentViewController, + title: SwiftLocalizationHelper.localizedString(forKey: "Gyro Button"), + message: "\n\(SwiftLocalizationHelper.localizedString(forKey: "gyroButtonTip"))", + withCancel: false, + buttonTitle: SwiftLocalizationHelper.localizedString(forKey: "Got it!"), + countdown: 5, + ) + } // MARK: - Auto Dock private static let autoDockExposedEdgeLength: CGFloat = GenericUtils.isIPhone() ? 90 : 90 diff --git a/VoidLink/Input/StreamView.m b/VoidLink/Input/StreamView.m index 8232ae74..ab8f098c 100644 --- a/VoidLink/Input/StreamView.m +++ b/VoidLink/Input/StreamView.m @@ -669,6 +669,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; newProfile.unfoldedExclusiveFolderSequence = OnScreenWidgetView.unfoldedExclusiveFolderSequence; newProfile.postExclusiveUnfoldedSequences = OnScreenWidgetView.postExclusiveUnfoldedSequences; newProfile.gamepadOverlayEnabled = streamFrameVC.virtualGamepadOverlay != nil; + newProfile.normalizedStreamViewOffset = CGPointMake(streamFrameVC.streamViewMagnifierContentOffset.x/self.bounds.size.width, streamFrameVC.streamViewMagnifierContentOffset.y/self.bounds.size.height); newProfile.streamViewScale = streamFrameVC.streamViewMagnifierZoomScale; diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 599ed147..51c8d070 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -1046,19 +1046,19 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "键鼠布局" + "value" : "键鼠FPS" } }, "zh-Hant" : { "stringUnit" : { - "state" : "needs_review", - "value" : "鍵鼠佈局" + "state" : "translated", + "value" : "鍵鼠FPS" } }, "zh-HK" : { "stringUnit" : { - "state" : "needs_review", - "value" : "鍵鼠佈局" + "state" : "translated", + "value" : "鍵鼠FPS" } } } @@ -3700,29 +3700,6 @@ } } }, - "Current profile updated successfully" : { - "extractionState" : "manual", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "当前布局已修改" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "目前版面配置已成功更新" - } - }, - "zh-HK" : { - "stringUnit" : { - "state" : "translated", - "value" : "目前版面配置已成功更新" - } - } - } - }, "Custom Resolution Selected" : { "extractionState" : "manual", "localizations" : { @@ -6235,6 +6212,29 @@ } } }, + "Gyro Button" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "陀螺仪按键" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "陀螺儀按鍵" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "陀螺儀按鍵" + } + } + } + }, "Gyro button on controller" : { "extractionState" : "manual", "localizations" : { @@ -6350,6 +6350,35 @@ } } }, + "gyroButtonTip" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Before you start, please navigate to 'Settings Menu → Motion Control Section → Map Gyro to’ to confirm whether the gyroscope output is set to mouse or controller stick." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "开始之前,请先前往 “设置菜单 → 体感控制分区 → 陀螺仪映射为” 选项确认陀螺仪输出为鼠标还是手柄摇杆。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "開始之前,請先前往「設定選單 → 體感控制分區 → 陀螺儀映射為」選項,確認陀螺儀輸出為滑鼠還是手把搖桿。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "開始之前,請先前往「設定選單 → 體感控制分區 → 陀螺儀映射為」選項,確認陀螺儀輸出為滑鼠還是手把搖桿。" + } + } + } + }, "gyroModeStackTip" : { "extractionState" : "manual", "localizations" : { @@ -7283,25 +7312,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "- Tap the drawer icon to duplicate the default profile, since it cannot be modified or overwritten.\n\n- Tap to load configs for each widget.\n\n- To ensure smooth gameplay without interference:\n - Disable All gestures in: Settings → Multitasking & Gestures\n - Disable Settings → Accessibility → Touch → AssistiveTouch\n - Disable Settings → Accessibility → Zoom\n\n- We recommend using new widgets instead of legacy on-screen controls." + "value" : "- Tap the drawer icon to duplicate the default profile, since it could be overwritten after app update.\n\n- Tap to load configs for each widget.\n\n- To ensure smooth gameplay without interference:\n - Disable All gestures in: Settings → Multitasking & Gestures\n - Disable Settings → Accessibility → Touch → AssistiveTouch\n - Disable Settings → Accessibility → Zoom\n\n- We recommend using new widgets instead of legacy on-screen controls." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "- 请点击抽屉图标复制一份默认布局,因为默认布局无法修改或覆盖。\n\n- 点击以加载每个控件的配置。\n\n- 为避免游戏过程被干扰,请务必:\n - 设置 → 多任务与手势 → 关闭所有手势\n - 关闭: 设置 → 辅助功能 → 触控 → 辅助触控\n - 关闭: 设置 → 辅助功能 → 缩放\n\n- 建议使用新版屏显控件,而非旧版屏显手柄。" + "value" : "- 请点击抽屉图标复制一份默认布局。因为预置的模板布局可能因app升级被覆盖。\n\n- 点击以加载每个控件的配置。\n\n- 为避免游戏过程被干扰,请务必:\n - 设置 → 多任务与手势 → 关闭所有手势\n - 关闭: 设置 → 辅助功能 → 触控 → 辅助触控\n - 关闭: 设置 → 辅助功能 → 缩放\n\n- 建议使用新版屏显控件,而非旧版屏显手柄。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "- 請點擊抽屜圖示複製一份預設版面,因為預設版面無法修改或覆蓋。\n\n- 點擊以載入每個元件的設定。\n\n- 為避免遊戲過程被干擾,請務必:\n - 設定 → 多工處理與手勢 → 關閉所有手勢\n - 關閉: 設定 → 輔助使用 → 觸控 → 輔助觸控\n - 關閉: 設定 → 輔助使用 → 縮放\n\n- 建議使用新版螢幕控制元件,而非舊版螢幕顯示手把。" + "value" : "- 請點擊抽屜圖示複製一份預設佈局。因為預設的模板佈局可能會因 App 升級而被覆蓋。\n\n- 點擊以載入每個元件的設定。\n\n- 為避免遊戲過程被干擾,請務必:\n - 設定 → 多工處理與手勢 → 關閉所有手勢\n - 關閉: 設定 → 輔助使用 → 觸控 → 輔助觸控\n - 關閉: 設定 → 輔助使用 → 縮放\n\n- 建議使用新版螢幕控制元件,而非舊版螢幕顯示手把。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "- 請點擊抽屜圖示複製一份預設版面,因為預設版面無法修改或覆蓋。\n\n- 點擊以載入每個元件的設定。\n\n- 為避免遊戲過程被干擾,請務必:\n - 設定 → 多工處理與手勢 → 關閉所有手勢\n - 關閉: 設定 → 輔助使用 → 觸控 → 輔助觸控\n - 關閉: 設定 → 輔助使用 → 縮放\n\n- 建議使用新版螢幕控制元件,而非舊版螢幕顯示手把。" + "value" : "- 請點擊抽屜圖示複製一份預設佈局。因為預設的模板佈局可能會因 App 升級而被覆蓋。\n\n- 點擊以載入每個元件的設定。\n\n- 為避免遊戲過程被干擾,請務必:\n - 設定 → 多工處理與手勢 → 關閉所有手勢\n - 關閉: 設定 → 輔助使用 → 觸控 → 輔助觸控\n - 關閉: 設定 → 輔助使用 → 縮放\n\n- 建議使用新版螢幕控制元件,而非舊版螢幕顯示手把。" } } } @@ -10082,6 +10111,35 @@ } } }, + "profileSaveTip" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Current profile updated successfully.\nThe app update may overwrite the preset game profiles. If you just saved a preset profile, please duplicate it and modify the copy instead." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "当前布局已修改。\nApp升级可能覆盖预置的游戏配置,如果您刚刚保存的是预置游戏配置,先将该配置复制,后续请修改其副本。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "目前配置已成功更新\nApp升級可能會覆蓋預設的遊戲配置,如果您剛剛儲存的是預設遊戲配置,先將該配置複製,後續請修改其副本。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "目前配置已成功更新\nApp升級可能會覆蓋預設的遊戲配置,如果您剛剛儲存的是預設遊戲配置,先將該配置複製,後續請修改其副本。" + } + } + } + }, "Purchase Now" : { "extractionState" : "manual", "localizations" : { diff --git a/VoidLink/PresetConfigs/widgetTemplates.bin b/VoidLink/PresetConfigs/widgetTemplates.bin index 40ec47a1..26bc3437 100644 Binary files a/VoidLink/PresetConfigs/widgetTemplates.bin and b/VoidLink/PresetConfigs/widgetTemplates.bin differ diff --git a/VoidLink/PresetConfigs/widgetTemplatesIPhone.bin b/VoidLink/PresetConfigs/widgetTemplatesIPhone.bin index 4b8869d3..3b3c2c7e 100644 Binary files a/VoidLink/PresetConfigs/widgetTemplatesIPhone.bin and b/VoidLink/PresetConfigs/widgetTemplatesIPhone.bin differ diff --git a/VoidLink/Utility/GenericUtils.swift b/VoidLink/Utility/GenericUtils.swift index 6f5d8c45..3653c546 100644 --- a/VoidLink/Utility/GenericUtils.swift +++ b/VoidLink/Utility/GenericUtils.swift @@ -45,7 +45,6 @@ import UIKit let key = "hasLaunchedPressureCurveTool" let defaults = UserDefaults.standard let launchedBefore = defaults.bool(forKey: key) - if !launchedBefore { defaults.set(true, forKey: key) return true @@ -57,7 +56,6 @@ import UIKit let key = "hasTouchedGamepadOverlayFeature20260405-2" let defaults = UserDefaults.standard let launchedBefore = defaults.bool(forKey: key) - if !launchedBefore { defaults.set(true, forKey: key) return true @@ -69,7 +67,6 @@ import UIKit let key = "hasTappedGameProfileSelectorFromMainFrame-6" let defaults = UserDefaults.standard let launchedBefore = defaults.bool(forKey: key) - if !launchedBefore { defaults.set(true, forKey: key) return true @@ -81,7 +78,6 @@ import UIKit let key = "hasTappedFolderInLayoutTool" let defaults = UserDefaults.standard let launchedBefore = defaults.bool(forKey: key) - if !launchedBefore { defaults.set(true, forKey: key) return true @@ -90,10 +86,20 @@ import UIKit } @objc static func isFirstTappingMagnifier() -> Bool { - let key = "hasFirstTappingMagnifier2" + let key = "hasTappedMagnifier2" let defaults = UserDefaults.standard let launchedBefore = defaults.bool(forKey: key) + if !launchedBefore { + defaults.set(true, forKey: key) + return true + } + return false + } + @objc static func isFirstTappingOnscreenGyroButton() -> Bool { + let key = "hasTappedOnscreenGyroButton" + let defaults = UserDefaults.standard + let launchedBefore = defaults.bool(forKey: key) if !launchedBefore { defaults.set(true, forKey: key) return true @@ -105,7 +111,6 @@ import UIKit let key = "isFirstTappingInputAccessoryBar" let defaults = UserDefaults.standard let launchedBefore = defaults.bool(forKey: key) - if !launchedBefore { defaults.set(true, forKey: key) return true @@ -118,7 +123,6 @@ import UIKit let key = "hasStreamedOnMac" let defaults = UserDefaults.standard let launchedBefore = defaults.bool(forKey: key) - if !launchedBefore { defaults.set(true, forKey: key) return true diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.swift b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.swift index 06c53aba..df72ad02 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.swift +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.swift @@ -1502,7 +1502,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg let success = profilesManager.updateSelectedProfile(layoutOSC.oscButtonLayerPool) guard sender != nil else { return } let message = success - ? LocalizationHelper.localizedString(forKey: "Current profile updated successfully") + ? LocalizationHelper.localizedString(forKey: "profileSaveTip") : LocalizationHelper.localizedString(forKey: "Profile Default can not be overwritten") let alert = UIAlertController(title: "", message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Ok"), style: .default) { _ in diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index caf2cb93..34b7418b 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -2006,7 +2006,9 @@ - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; - + + [self resetMagnifierStreamViewWithAnimated:false]; + // handle view size change for on-screen widgets CGSize oldSize = self.view.bounds.size; CGFloat scaleX = size.width / oldSize.width;