diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.swift b/VoidLink/Input/CustomOSC/OSCProfilesManager.swift index d749e97d..5070c8d1 100644 --- a/VoidLink/Input/CustomOSC/OSCProfilesManager.swift +++ b/VoidLink/Input/CustomOSC/OSCProfilesManager.swift @@ -14,7 +14,7 @@ import UIKit @objcMembers class OSCProfilesManager: NSObject { private static let profilesDefaultsKey = "OSCProfiles" - private static let widgetProfileUpdatedKey = "widgetProfileUpdated-20260605" + private static let widgetProfileUpdatedKey = "widgetProfileUpdated-20260606" private static var sharedInstance: OSCProfilesManager? private static var onScreenWidgetViews: NSMutableSet? @@ -184,7 +184,8 @@ class OSCProfilesManager: NSObject { for case let profile as OSCProfile in profilesDecoded { if localProfileNames.contains(profile.name) { - profile.name = "\(profile.name)-\(LocalizationHelper.localizedString(forKey:"Restored"))" + // profile.name = "\(profile.name)-\(LocalizationHelper.localizedString(forKey:"Restored"))" + profile.name = "\(profile.name) - Restored" } } @@ -201,8 +202,10 @@ class OSCProfilesManager: NSObject { localProfiles.remove(profile) } + var indexOffset = 0 for profile in localProfiles { - profilesDecoded.add(profile) + profilesDecoded.insert(profile, at: 1+indexOffset) + indexOffset += 1 } persistEncodedProfiles(encodedProfiles(from: profilesDecoded)) @@ -228,6 +231,7 @@ class OSCProfilesManager: NSObject { } } + /* func updateDefaultTemplates() { guard let filePath = Bundle.main.path(forResource: isIPhone() ? "widgetTemplatesIPhone" : "widgetTemplates", ofType: "bin") else { return @@ -272,6 +276,7 @@ class OSCProfilesManager: NSObject { return } } + */ func getAllProfiles() -> NSMutableArray { guard @@ -304,11 +309,7 @@ class OSCProfilesManager: NSObject { let needImportDefaultTemplates = defaults.object(forKey: Self.widgetProfileUpdatedKey) == nil if profiles.count == 0 || needImportDefaultTemplates { - if profiles.count == 0 { - importDefaultTemplates() - } else { - updateDefaultTemplates() - } + importDefaultTemplates() defaults.set(true, forKey: Self.widgetProfileUpdatedKey) defaults.synchronize() profiles = getAllProfiles() @@ -526,6 +527,14 @@ class OSCProfilesManager: NSObject { } return nil } + + func getIndex(byName name: String) -> UInt32? { + let profiles = getAllProfiles() + for case let profile as OSCProfile in profiles where profile.name == name { + return UInt32(profiles.index(of: profile)) + } + return nil + } func unarchiveButtonStateEncoded(_ data: Data) -> OnScreenButtonState { if let buttonState = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: Self.buttonStateCodingClasses, from: data) as? OnScreenButtonState { diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 3544206f..ba022246 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -3394,7 +3394,7 @@ import ObjectiveC.runtime // MARK: - Auto Dock private static let autoDockExposedEdgeLength: CGFloat = GenericUtils.isIPhone() ? 70 : 90 - private static let autoDockExposedThickness: CGFloat = 22 + private static let autoDockExposedThickness: CGFloat = 17 private static let autoDockVerticalInset: CGFloat = 12 @objc var autoDockIdleDuration: TimeInterval = 0 @objc var storedAutoDockIdleDuration: TimeInterval = 0 @@ -3901,6 +3901,21 @@ import ObjectiveC.runtime } } + @objc static func clearSubWidgets(for folder:OnScreenWidgetView, recursive:Bool = false){ + for widget in OnScreenWidgetView.mapping.values where widget.parentSequence == folder.sequence && (!widget.isFolder || widget.sequenceSet.isEmpty) { + folder.sequenceSet.remove(widget.sequence) + OnScreenWidgetView.mapping.removeValue(forKey: widget.sequence) + widget.removeFromSuperview() + } + guard recursive else {return} + for subFolder in OnScreenWidgetView.mapping.values where subFolder.parentSequence == folder.sequence && subFolder.isFolder { + OnScreenWidgetView.clearSubWidgets(for: subFolder, recursive: true) + folder.sequenceSet.remove(subFolder.sequence) + OnScreenWidgetView.mapping.removeValue(forKey: subFolder.sequence) + subFolder.removeFromSuperview() + } + } + private static func getRootFolder(of widget:OnScreenWidgetView) -> OnScreenWidgetView?{ var widgetRef:OnScreenWidgetView? = widget var parentFolder:OnScreenWidgetView? = OnScreenWidgetView.mapping[widgetRef?.parentSequence ?? -1] diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 47b21077..97a60722 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -703,7 +703,7 @@ }, "zh-Hans" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "方向键" } }, @@ -779,6 +779,35 @@ } } }, + "=buttonModeDemo" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo of 5 Button Combo and Slidable Buttons" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "理解技能组合键与可滑动按键" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "理解技能組合鍵與可滑動按鍵" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "理解技能組合鍵與可滑動按鍵" + } + } + } + }, "=closeTab" : { "extractionState" : "manual", "localizations" : { @@ -1707,6 +1736,35 @@ } } }, + "=kbmfps" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "= FPS Example (Keyboard & Mouse)" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "键鼠FPS" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "键鼠FPS" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "键鼠FPS" + } + } + } + }, "=kbmTip" : { "extractionState" : "manual", "localizations" : { @@ -1852,6 +1910,35 @@ } } }, + "=legacyWidgets" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legacy Widgets" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "旧版控件" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "舊版控制項" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "舊版控制項" + } + } + } + }, "=lock" : { "extractionState" : "manual", "localizations" : { @@ -2258,6 +2345,35 @@ } } }, + "=null" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Null" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "空白" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "空白" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "空白" + } + } + } + }, "=numpad" : { "extractionState" : "manual", "localizations" : { @@ -3418,6 +3534,93 @@ } } }, + "=vGamepad1" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gamepad Layout 1" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手柄布局1" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "手把配置1" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "手把配置1" + } + } + } + }, + "=vGamepad2" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gamepad Layout 2" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手柄布局2" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "手把配置2" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "手把配置2" + } + } + } + }, + "=vGamepad3" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zenless Zone Zero (Genshin Style)" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "绝区零(原神风格)" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "絕區零(原神風格)" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "絕區零(原神風格)" + } + } + } + }, "=virtualGamepad" : { "extractionState" : "manual", "localizations" : { @@ -5758,6 +5961,29 @@ } } }, + "Default" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "默认" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "預設" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "預設" + } + } + } + }, "Del" : { "extractionState" : "manual", "localizations" : { diff --git a/VoidLink/PresetConfigs/widgetTemplates.bin b/VoidLink/PresetConfigs/widgetTemplates.bin index b5d37b80..90d34067 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 45995a8a..663b4c83 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 cce31214..b117e72d 100644 --- a/VoidLink/Utility/GenericUtils.swift +++ b/VoidLink/Utility/GenericUtils.swift @@ -700,6 +700,18 @@ extension CGPoint { } } +extension String { + var localized: String { + LocalizationHelper.localizedString(forKey: self) + } + + var localizedProfileName: String { + let parts = self.components(separatedBy: " - Restored") + let isRestored = self.contains(" - Restored") + let localized = isRestored ? "\(parts.first?.localized ?? "") - \("Restored".localized)" : self.localized + return localized + } +} private var previousSelectedSegmentIndexKey: UInt8 = 0 private var lastKnownSelectedSegmentIndexKey: UInt8 = 0 diff --git a/VoidLink/Utility/IAPManager.swift b/VoidLink/Utility/IAPManager.swift index e855622e..a8e74846 100644 --- a/VoidLink/Utility/IAPManager.swift +++ b/VoidLink/Utility/IAPManager.swift @@ -251,13 +251,11 @@ import StoreKit settings?.onscreenControls = 1 dataMan.saveData() let profileMan = OSCProfilesManager.sharedManager(.zero) - profileMan.updateDefaultTemplates() let profiles = profileMan.getAllProfiles() - guard profiles.count > 1 else { break } - guard let targetProfile = profiles[1] as? OSCProfile else { break } - if targetProfile.name == "Pencil Pro" { - profileMan.setProfileToSelected(1) - } + var toolkitProfileIndex = profileMan.getIndex(byName: "Pencil Pro") + if toolkitProfileIndex == nil {profileMan.importDefaultTemplates()} + toolkitProfileIndex = profileMan.getIndex(byName: "Pencil Pro") + profileMan.setProfileToSelected(toolkitProfileIndex ?? 1) default: break } diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.swift b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.swift index 16e3baaf..1b564a39 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.swift +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.swift @@ -395,7 +395,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg if folder != nil { if buttonState.alias == "=widgets" || buttonState.alias == "=pickProfile" - || buttonState.alias == "widgetTool" { + || buttonState.alias == "=widgetTool" { continue } } @@ -587,15 +587,9 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg } @IBAction func clearFolderButtonTapped(_ sender: Any?) { - for widget in OnScreenWidgetView.mapping.values { - if widget.parentSequence == selectedWidget?.sequence, widget.sequenceSet.isEmpty { - selectedWidget?.sequenceSet.remove(widget.sequence) - OnScreenWidgetView.mapping.removeValue(forKey: widget.sequence) - widget.removeFromSuperview() - } + if let selectedWidget = selectedWidget { + OnScreenWidgetView.clearSubWidgets(for: selectedWidget, recursive: true) } - - selectedWidget?.sequenceSet = Set() } private func setBulkEditStackHidden(_ hidden:Bool) { @@ -839,7 +833,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg let key = decelerationRateSliderMode == .decelerationRateX ? "DecelerationRateX: %.3f " : "DecelerationRateY: %.3f " decelerationRateLabel.text = LocalizationHelper.localizedString(forKey: key, decelerationRateSlider.value) } - + private func refreshPanelForSelectedWidget(_ widgetView: OnScreenWidgetView) { // hideStickIndicators() if OnScreenWidgetView.gamepadArrivalReported {clearSickInput()} @@ -855,12 +849,12 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg autoFitLabel(currentProfileLabel) currentProfileLabel.textAlignment = .left - currentProfileLabel.text = LocalizationHelper.localizedString(forKey: " Profile: %@ Alias: %@ Cmd: %@ Parent: %@", profilesManager.getSelectedProfile().name, widgetView.widgetLabel, widgetView.cmdString, LocalizationHelper.localizedString(forKey:OnScreenWidgetView.mapping[widgetView.parentSequence]?.widgetLabel ?? "null")) + currentProfileLabel.text = LocalizationHelper.localizedString(forKey: " Profile: %@ Alias: %@ Cmd: %@ Parent: %@", profilesManager.getSelectedProfile().name.localizedProfileName, widgetView.widgetLabel, widgetView.cmdString, (OnScreenWidgetView.mapping[widgetView.parentSequence]?.widgetLabel ?? "null").localized) if selectedWidget?.hasWalkSprintKeys == true { let cmdCounts = widgetView.comboButtonStrings.count currentProfileLabel.text = LocalizationHelper.localizedString(forKey: " Cmd: %@ Sprint: %@ Walk: %@ Parent: %@", widgetView.cmdString, cmdCounts>0 ? widgetView.comboButtonStrings[0] : LocalizationHelper.localizedString(forKey: "null"), - cmdCounts>1 ? widgetView.comboButtonStrings[1] : LocalizationHelper.localizedString(forKey: "null"), LocalizationHelper.localizedString(forKey:OnScreenWidgetView.mapping[widgetView.parentSequence]?.widgetLabel ?? "null")) + cmdCounts>1 ? widgetView.comboButtonStrings[1] : LocalizationHelper.localizedString(forKey: "null"), (OnScreenWidgetView.mapping[widgetView.parentSequence]?.widgetLabel ?? "null").localized) } undoButton.alpha = widgetView.layoutChanges.count > 1 ? 1.0 : 0.3 @@ -1120,7 +1114,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg autoFitLabel(currentProfileLabel) currentProfileLabel.textAlignment = .left - currentProfileLabel.text = LocalizationHelper.localizedString(forKey: " Profile: %@ Widget: %@", profilesManager.getSelectedProfile().name, selectedControllerLayer?.name ?? "") + currentProfileLabel.text = LocalizationHelper.localizedString(forKey: " Profile: %@ Widget: %@", profilesManager.getSelectedProfile().name.localizedProfileName, selectedControllerLayer?.name ?? "") let sizeFactor = OnScreenControls.getControllerLayerSizeFactor(controllerLayer) widgetSizeSlider.value = Float(sizeFactor) @@ -1757,7 +1751,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg widgetPanelStack.clipsToBounds = true widgetPanelStack.backgroundColor = UIColor.black.withAlphaComponent(0.5) - currentProfileLabel.text = LocalizationHelper.localizedString(forKey: "Profile: %@", profilesManager.getSelectedProfile().name) + currentProfileLabel.text = LocalizationHelper.localizedString(forKey: "Profile: %@", profilesManager.getSelectedProfile().name.localizedProfileName) currentProfileLabel.layer.cornerRadius = isIPhone() ? 9 : 12 currentProfileLabel.clipsToBounds = true currentProfileLabel.textAlignment = .center diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/Table/OSCProfilesTableViewController.swift b/VoidLink/ViewControllers/CustomOSCViewControl/Table/OSCProfilesTableViewController.swift index 8d63c2f8..d3352c2b 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/Table/OSCProfilesTableViewController.swift +++ b/VoidLink/ViewControllers/CustomOSCViewControl/Table/OSCProfilesTableViewController.swift @@ -383,7 +383,7 @@ final class OSCProfilesTableViewController: UIViewController, UITableViewDelegat return UITableViewCell() } - nameLabel.text = profile.name + nameLabel.text = profile.name.localizedProfileName nameLabel.backgroundColor = UIColor.clear nameLabel.alpha = 1.0 nameLabel.textColor = UIColor.white.withAlphaComponent(0.9)