- genshin touch control enhancement preview for settings menu
- controller navigation & first connection tips - enhancedTouchPreview bug fix
This commit is contained in:
1 parent
b0c16922a2
commit
e0aff4b532
10 files changed
+656
-8
No files matched your search
@@ -47,6 +47,7 @@
|
|||||||
2AC1E3832F507D80004F76C0 /* GenericUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC1E3822F507D80004F76C0 /* GenericUtils.swift */; };
|
2AC1E3832F507D80004F76C0 /* GenericUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC1E3822F507D80004F76C0 /* GenericUtils.swift */; };
|
||||||
2AC1EAFB2F92260F004EDA80 /* UIStackExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */; };
|
2AC1EAFB2F92260F004EDA80 /* UIStackExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */; };
|
||||||
2AC1EAFC2F922889004EDA80 /* UIStackExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */; };
|
2AC1EAFC2F922889004EDA80 /* UIStackExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */; };
|
||||||
|
2B7A10022FFB000100000001 /* SettingsTouchVelocityPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B7A10012FFB000100000001 /* SettingsTouchVelocityPreview.swift */; };
|
||||||
3A4A3A112F0C000100000001 /* ProfileSelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4A3A102F0C000100000001 /* ProfileSelectorViewController.swift */; };
|
3A4A3A112F0C000100000001 /* ProfileSelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4A3A102F0C000100000001 /* ProfileSelectorViewController.swift */; };
|
||||||
3A4A3A132F0C000100000003 /* LayoutOnScreenControlsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4A3A122F0C000100000002 /* LayoutOnScreenControlsViewController.swift */; };
|
3A4A3A132F0C000100000003 /* LayoutOnScreenControlsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4A3A122F0C000100000002 /* LayoutOnScreenControlsViewController.swift */; };
|
||||||
840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */; };
|
840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */; };
|
||||||
@@ -218,6 +219,7 @@
|
|||||||
2A7114002F5A6C65001D852D /* GamepadNavigationIllustrationHud.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamepadNavigationIllustrationHud.swift; sourceTree = "<group>"; };
|
2A7114002F5A6C65001D852D /* GamepadNavigationIllustrationHud.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamepadNavigationIllustrationHud.swift; sourceTree = "<group>"; };
|
||||||
2AC1E3822F507D80004F76C0 /* GenericUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericUtils.swift; sourceTree = "<group>"; };
|
2AC1E3822F507D80004F76C0 /* GenericUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericUtils.swift; sourceTree = "<group>"; };
|
||||||
2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackExtension.swift; sourceTree = "<group>"; };
|
2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackExtension.swift; sourceTree = "<group>"; };
|
||||||
|
2B7A10012FFB000100000001 /* SettingsTouchVelocityPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTouchVelocityPreview.swift; sourceTree = "<group>"; };
|
||||||
3A4A3A102F0C000100000001 /* ProfileSelectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSelectorViewController.swift; sourceTree = "<group>"; };
|
3A4A3A102F0C000100000001 /* ProfileSelectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSelectorViewController.swift; sourceTree = "<group>"; };
|
||||||
3A4A3A122F0C000100000002 /* LayoutOnScreenControlsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutOnScreenControlsViewController.swift; sourceTree = "<group>"; };
|
3A4A3A122F0C000100000002 /* LayoutOnScreenControlsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutOnScreenControlsViewController.swift; sourceTree = "<group>"; };
|
||||||
693B3A9A218638CD00982F7B /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
693B3A9A218638CD00982F7B /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
||||||
@@ -864,6 +866,7 @@
|
|||||||
FB89462719F646E200339C8A /* StreamFrameViewController.m */,
|
FB89462719F646E200339C8A /* StreamFrameViewController.m */,
|
||||||
FBD3494E19FF2174002D2A60 /* SettingsViewController.h */,
|
FBD3494E19FF2174002D2A60 /* SettingsViewController.h */,
|
||||||
FBD3494F19FF2174002D2A60 /* SettingsViewController.m */,
|
FBD3494F19FF2174002D2A60 /* SettingsViewController.m */,
|
||||||
|
2B7A10012FFB000100000001 /* SettingsTouchVelocityPreview.swift */,
|
||||||
2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */,
|
2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */,
|
||||||
FB4A23B61A9D3637004D2EF2 /* LoadingFrameViewController.h */,
|
FB4A23B61A9D3637004D2EF2 /* LoadingFrameViewController.h */,
|
||||||
FB4A23B71A9D3637004D2EF2 /* LoadingFrameViewController.m */,
|
FB4A23B71A9D3637004D2EF2 /* LoadingFrameViewController.m */,
|
||||||
@@ -1188,6 +1191,7 @@
|
|||||||
97E20DEA2E216532000D5DCA /* SceneDelegate.m in Sources */,
|
97E20DEA2E216532000D5DCA /* SceneDelegate.m in Sources */,
|
||||||
1817B61E2E6FD04D000D938D /* SafeTimer.swift in Sources */,
|
1817B61E2E6FD04D000D938D /* SafeTimer.swift in Sources */,
|
||||||
FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */,
|
FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */,
|
||||||
|
2B7A10022FFB000100000001 /* SettingsTouchVelocityPreview.swift in Sources */,
|
||||||
843CC7F02C1E757D00BBC475 /* NativeTouchPointer.m in Sources */,
|
843CC7F02C1E757D00BBC475 /* NativeTouchPointer.m in Sources */,
|
||||||
2A7113FD2F5A6C65001D852D /* ThemeManager.swift in Sources */,
|
2A7113FD2F5A6C65001D852D /* ThemeManager.swift in Sources */,
|
||||||
FB89462C19F646E200339C8A /* HttpManager.m in Sources */,
|
FB89462C19F646E200339C8A /* HttpManager.m in Sources */,
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ final class ControllerNavigator: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func updateUINavigationDelegateState() {
|
static func updateUINavigationDelegateState() {
|
||||||
|
ControllerUtil.disableSysGestures(ControllerUtil.primaryGCController)
|
||||||
if uiNavigationDelegate is StreamFrameViewController {
|
if uiNavigationDelegate is StreamFrameViewController {
|
||||||
if radialMenuState == .mouseModeEnabled {
|
if radialMenuState == .mouseModeEnabled {
|
||||||
startControllerMouse()
|
startControllerMouse()
|
||||||
@@ -2273,7 +2274,8 @@ extension ControllerCollectionNavigationDelegate {
|
|||||||
|
|
||||||
extension MainFrameViewController {
|
extension MainFrameViewController {
|
||||||
func forceLaunchApp(_ app: TemporaryApp?){
|
func forceLaunchApp(_ app: TemporaryApp?){
|
||||||
if let currentRunningApp = self.findRunningApp(app?.host) {
|
guard let app = app else { return }
|
||||||
|
if let currentRunningApp = self.findRunningApp(app.host) {
|
||||||
if currentRunningApp !== app {
|
if currentRunningApp !== app {
|
||||||
self.quitRunningAppAndStart(app)
|
self.quitRunningAppAndStart(app)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -466,6 +466,18 @@ import Foundation
|
|||||||
|
|
||||||
guard GCController.controllers().count == 1 else { return }
|
guard GCController.controllers().count == 1 else { return }
|
||||||
|
|
||||||
|
if let mainFrameVC = delegate as? MainFrameViewController {
|
||||||
|
let vc = mainFrameVC.isStreaming() ? StreamFrameViewController.sharedInstance() : mainFrameVC
|
||||||
|
GenericUtils.handleFirstGamepadConnection(in: vc) {
|
||||||
|
setGCControllerToPrimary(controller)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setGCControllerToPrimary(controller)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func setGCControllerToPrimary(_ controller: GCController) {
|
||||||
primaryGCController = controller
|
primaryGCController = controller
|
||||||
_ = buildMapping(for: controller, swapABXY: false)
|
_ = buildMapping(for: controller, swapABXY: false)
|
||||||
disableSysGestures(controller)
|
disableSysGestures(controller)
|
||||||
|
|||||||
@@ -536,6 +536,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"%.0f%% velocity" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%.0f%% velocity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%.0f%% 移速缩放"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%.0f%% 移速縮放"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "%.0f%% 移速縮放"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"%@ is currently running" : {
|
"%@ is currently running" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -6269,6 +6298,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"controllerNavigationTip" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "VoidLink now supports global controller navigation! Go to [Settings Menu] → [Controller] section → turn [Controller Navigation] off and on again to customize radial menu buttons and controller mouse mode."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "VoidLink已支持全局手柄导航!前往 [设置菜单] → [手柄] 分区 → 关闭再打开 [手柄导航],即可自定义环形菜单按键与手柄鼠标模式。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "VoidLink 已支援全域手把導航!前往 [設定選單] → [手把] 分區 → 關閉再開啟 [手把導航],即可自訂環形選單按鍵與手把滑鼠模式。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "VoidLink 已支援全域手把導航!前往 [設定選單] → [手把] 分區 → 關閉再開啟 [手把導航],即可自訂環形選單按鍵與手把滑鼠模式。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Create" : {
|
"Create" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -6678,6 +6736,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"descendants" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "descendants"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "衍生版"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "衍生版"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "衍生版"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Detailed Tutorial" : {
|
"Detailed Tutorial" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -7750,6 +7837,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Enhanced Touch Control for Genshin Game Streaming" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Enhanced Touch Control for Genshin Game Streaming"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "原神串流增强触控"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "原神串流增強觸控"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "原神串流增強觸控"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Enter a new command and alias" : {
|
"Enter a new command and alias" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -12922,6 +13038,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Originally designed & coded by True Zhuanjia with pride" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Originally designed & coded by True Zhuanjia with pride"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "由 True砖家@Bilibili 自豪地原创设计并编码"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "由 True砖家@Bilibili 自豪地原創設計並編碼"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "由 True砖家@Bilibili 自豪地原創設計並編碼"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Others" : {
|
"Others" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -13083,6 +13228,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"passthrough" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "passthrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "坐标透传"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "座標透傳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "座標透傳"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Pen Pressure Curve" : {
|
"Pen Pressure Curve" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -17252,6 +17426,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Tap to learn more" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "点击了解更多"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "點擊了解更多"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "點擊了解更多"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Tap to toggle" : {
|
"Tap to toggle" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -19046,4 +19243,4 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"version" : "1.1"
|
"version" : "1.1"
|
||||||
}
|
}
|
||||||
@@ -442,6 +442,34 @@ import UIKit
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc public static func isFirstConnectingGamepad() -> Bool {
|
||||||
|
let key = "isFirstConnectingGamepad20260730"
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
let launchedBefore = defaults.bool(forKey: key)
|
||||||
|
if !launchedBefore {
|
||||||
|
defaults.set(true, forKey: key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@objc public static func handleFirstGamepadConnection(in vc: UIViewController?, handler: @escaping () -> Void) {
|
||||||
|
if isFirstConnectingGamepad() {
|
||||||
|
AlertControllerUtil.showAlert(
|
||||||
|
in: vc,
|
||||||
|
title: "Tips".localized,
|
||||||
|
message: "controllerNavigationTip".localized,
|
||||||
|
withCancel: false,
|
||||||
|
buttonTitle: "Got it!".localized,
|
||||||
|
countdown: 6,
|
||||||
|
completion: {
|
||||||
|
handler()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@objc public static func isFirstStreamingOnMac() -> Bool {
|
@objc public static func isFirstStreamingOnMac() -> Bool {
|
||||||
if !PublicUtils.isRunningOnMacAsiPadApp {return false}
|
if !PublicUtils.isRunningOnMacAsiPadApp {return false}
|
||||||
let key = "hasStreamedOnMac"
|
let key = "hasStreamedOnMac"
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import UIKit
|
|||||||
|
|
||||||
extension UIViewController {
|
extension UIViewController {
|
||||||
var hasNoPresentedVC: Bool {
|
var hasNoPresentedVC: Bool {
|
||||||
let hasPresentedVC = self.presentedViewController == nil && self.view.window != nil
|
let hasNoPresentedVC = self.presentedViewController == nil && self.view.window != nil
|
||||||
// let presentedVC = self.presentedViewController
|
// let presentedVC = self.presentedViewController
|
||||||
return hasPresentedVC
|
return hasNoPresentedVC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
@@ -99,6 +99,9 @@ private final class ProfileCollectionViewCell: UICollectionViewCell, ControllerN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override var canBecomeFocused: Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc(FileOperation)
|
@objc(FileOperation)
|
||||||
|
|||||||
@@ -0,0 +1,370 @@
|
|||||||
|
//
|
||||||
|
// SettingsTouchVelocityPreview.swift
|
||||||
|
// VoidLink
|
||||||
|
//
|
||||||
|
// Created by True砖家 on 2026/7/30.
|
||||||
|
// Copyright © 2026 True砖家 on Bilibili. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ObjectiveC
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
private var touchVelocityPreviewViewKey: UInt8 = 0
|
||||||
|
private var touchVelocityPreviewDismissWorkItemKey: UInt8 = 0
|
||||||
|
private let touchVelocityPreviewScreenAspectRatio: CGFloat = 1.3333
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
private final class TouchVelocityPreviewView: UIView {
|
||||||
|
static let horizontalScreenInset: CGFloat = 16
|
||||||
|
static let topPadding: CGFloat = 7
|
||||||
|
static let titleHeight: CGFloat = 22
|
||||||
|
static let titleScreenGap: CGFloat = 2
|
||||||
|
static let footerGap: CGFloat = 6
|
||||||
|
static let footerHeight: CGFloat = 40
|
||||||
|
static let bottomPadding: CGFloat = 2
|
||||||
|
static let heightExtra = topPadding
|
||||||
|
+ titleHeight
|
||||||
|
+ titleScreenGap
|
||||||
|
+ footerGap
|
||||||
|
+ footerHeight
|
||||||
|
+ bottomPadding
|
||||||
|
- horizontalScreenInset * 2 / touchVelocityPreviewScreenAspectRatio
|
||||||
|
static let preferredWidth: CGFloat = PublicUtils.isIPhone ? 280 : 360
|
||||||
|
static let preferredHeight = preferredWidth / touchVelocityPreviewScreenAspectRatio + heightExtra
|
||||||
|
|
||||||
|
var dividerPercent: CGFloat = 50 {
|
||||||
|
didSet { setNeedsDisplay() }
|
||||||
|
}
|
||||||
|
|
||||||
|
var velocityPercent: CGFloat = 100 {
|
||||||
|
didSet { setNeedsDisplay() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func commonInit() {
|
||||||
|
isOpaque = false
|
||||||
|
isUserInteractionEnabled = true
|
||||||
|
layer.cornerRadius = 14
|
||||||
|
layer.cornerCurve = .continuous
|
||||||
|
layer.masksToBounds = true
|
||||||
|
layer.borderWidth = 1
|
||||||
|
|
||||||
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(openUrl))
|
||||||
|
addGestureRecognizer(tapGesture)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func openUrl() {
|
||||||
|
PublicUtils.openUrl("https://www.bilibili.com/video/BV1A1421r7qA")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func draw(_ rect: CGRect) {
|
||||||
|
let isDark = ThemeManager.userInterfaceStyle() == .dark
|
||||||
|
let backgroundColor = isDark
|
||||||
|
? UIColor(white: 0.07, alpha: 0.9)
|
||||||
|
: UIColor.white.withAlphaComponent(0.94)
|
||||||
|
let borderColor = ThemeManager.appPrimaryColor.withAlphaComponent(isDark ? 0.34 : 0.24)
|
||||||
|
let lineColor = ThemeManager.textColor.withAlphaComponent(isDark ? 0.32 : 0.22)
|
||||||
|
let passthroughColor = UIColor.systemTeal.withAlphaComponent(isDark ? 0.22 : 0.16)
|
||||||
|
let velocityColor = UIColor.systemIndigo.withAlphaComponent(isDark ? 0.20 : 0.13)
|
||||||
|
let controlColor = ThemeManager.textColor.withAlphaComponent(isDark ? 0.82 : 0.66)
|
||||||
|
let accentColor = ThemeManager.appPrimaryColor
|
||||||
|
let secondaryAccentColor = UIColor(red: 0.18, green: 0.82, blue: 0.95, alpha: 1)
|
||||||
|
layer.borderColor = borderColor.cgColor
|
||||||
|
|
||||||
|
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||||
|
context.setFillColor(backgroundColor.cgColor)
|
||||||
|
context.fill(bounds)
|
||||||
|
|
||||||
|
let titleRect = CGRect(
|
||||||
|
x: 12,
|
||||||
|
y: Self.topPadding,
|
||||||
|
width: bounds.width - 24,
|
||||||
|
height: Self.titleHeight
|
||||||
|
)
|
||||||
|
drawPreviewTitle(in: titleRect, textColor: ThemeManager.textColor)
|
||||||
|
|
||||||
|
let screenTop = titleRect.maxY + Self.titleScreenGap
|
||||||
|
let availableScreenHeight = max(bounds.height - screenTop - Self.footerGap - Self.footerHeight - Self.bottomPadding, 1)
|
||||||
|
let availableScreenWidth = bounds.width - Self.horizontalScreenInset * 2
|
||||||
|
let screenWidth = min(availableScreenWidth, availableScreenHeight * touchVelocityPreviewScreenAspectRatio)
|
||||||
|
let screenHeight = screenWidth / touchVelocityPreviewScreenAspectRatio
|
||||||
|
let screenRect = CGRect(
|
||||||
|
x: bounds.midX - screenWidth / 2,
|
||||||
|
y: screenTop,
|
||||||
|
width: screenWidth,
|
||||||
|
height: screenHeight
|
||||||
|
)
|
||||||
|
let screenPath = UIBezierPath(roundedRect: screenRect, cornerRadius: 10)
|
||||||
|
context.saveGState()
|
||||||
|
screenPath.addClip()
|
||||||
|
|
||||||
|
let dividerX = screenRect.minX + min(max(dividerPercent, 0), 100) / 100 * screenRect.width
|
||||||
|
context.setFillColor(passthroughColor.cgColor)
|
||||||
|
context.fill(CGRect(x: screenRect.minX, y: screenRect.minY, width: dividerX - screenRect.minX, height: screenRect.height))
|
||||||
|
context.setFillColor(velocityColor.cgColor)
|
||||||
|
context.fill(CGRect(x: dividerX, y: screenRect.minY, width: screenRect.maxX - dividerX, height: screenRect.height))
|
||||||
|
|
||||||
|
drawDirectionWheel(in: screenRect, color: controlColor, accentColor: secondaryAccentColor)
|
||||||
|
drawCharacterSelector(in: screenRect, color: controlColor, accentColor: accentColor)
|
||||||
|
drawActionButtons(in: screenRect, color: controlColor, accentColor: accentColor)
|
||||||
|
|
||||||
|
context.restoreGState()
|
||||||
|
|
||||||
|
screenPath.lineWidth = 1
|
||||||
|
lineColor.setStroke()
|
||||||
|
screenPath.stroke()
|
||||||
|
|
||||||
|
context.saveGState()
|
||||||
|
context.setLineDash(phase: 0, lengths: [6, 5])
|
||||||
|
context.setStrokeColor(accentColor.withAlphaComponent(0.9).cgColor)
|
||||||
|
context.setLineWidth(2)
|
||||||
|
context.move(to: CGPoint(x: dividerX, y: screenRect.minY + 2))
|
||||||
|
context.addLine(to: CGPoint(x: dividerX, y: screenRect.maxY - 2))
|
||||||
|
context.strokePath()
|
||||||
|
context.restoreGState()
|
||||||
|
|
||||||
|
drawRegionLabels(screenRect: screenRect, dividerX: dividerX, textColor: ThemeManager.textColor)
|
||||||
|
drawPreviewFooter(in: CGRect(x: 12, y: screenRect.maxY + Self.footerGap, width: bounds.width - 24, height: Self.footerHeight), textColor: ThemeManager.textColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func drawPreviewTitle(in rect: CGRect, textColor: UIColor) {
|
||||||
|
let paragraph = NSMutableParagraphStyle()
|
||||||
|
paragraph.alignment = .center
|
||||||
|
paragraph.lineBreakMode = .byTruncatingTail
|
||||||
|
let attributes: [NSAttributedString.Key: Any] = [
|
||||||
|
.font: UIFont.systemFont(ofSize: 14.5, weight: .semibold),
|
||||||
|
.foregroundColor: textColor.withAlphaComponent(0.88),
|
||||||
|
.paragraphStyle: paragraph
|
||||||
|
]
|
||||||
|
("Enhanced Touch Control for Genshin Game Streaming".localized as NSString).draw(in: rect, withAttributes: attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func drawPreviewFooter(in rect: CGRect, textColor: UIColor) {
|
||||||
|
let paragraph = NSMutableParagraphStyle()
|
||||||
|
paragraph.alignment = .center
|
||||||
|
paragraph.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
|
let firstLineAttributes: [NSAttributedString.Key: Any] = [
|
||||||
|
.font: UIFont.systemFont(ofSize: 13, weight: .semibold),
|
||||||
|
.foregroundColor: textColor.withAlphaComponent(0.82),
|
||||||
|
.paragraphStyle: paragraph
|
||||||
|
]
|
||||||
|
("Originally designed & coded by True Zhuanjia with pride".localized as NSString).draw(
|
||||||
|
in: CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: 20),
|
||||||
|
withAttributes: firstLineAttributes
|
||||||
|
)
|
||||||
|
|
||||||
|
let secondLine = "Tap to learn more".localized
|
||||||
|
let secondLineAttributes: [NSAttributedString.Key: Any] = [
|
||||||
|
.font: UIFont.systemFont(ofSize: 13, weight: .semibold),
|
||||||
|
.foregroundColor: textColor.withAlphaComponent(0.82),
|
||||||
|
.paragraphStyle: paragraph
|
||||||
|
]
|
||||||
|
(secondLine as NSString).draw(
|
||||||
|
in: CGRect(x: rect.minX, y: rect.minY + 20, width: rect.width, height: 20),
|
||||||
|
withAttributes: secondLineAttributes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func drawRegionLabels(screenRect: CGRect, dividerX: CGFloat, textColor: UIColor) {
|
||||||
|
let paragraph = NSMutableParagraphStyle()
|
||||||
|
paragraph.alignment = .center
|
||||||
|
paragraph.lineBreakMode = .byTruncatingTail
|
||||||
|
let attributes: [NSAttributedString.Key: Any] = [
|
||||||
|
.font: UIFont.systemFont(ofSize: 13, weight: .semibold),
|
||||||
|
.foregroundColor: textColor.withAlphaComponent(0.82),
|
||||||
|
.paragraphStyle: paragraph
|
||||||
|
]
|
||||||
|
|
||||||
|
let leftRect = CGRect(
|
||||||
|
x: screenRect.minX + 8,
|
||||||
|
y: screenRect.minY + 10,
|
||||||
|
width: max(dividerX - screenRect.minX - 16, 0),
|
||||||
|
height: 18
|
||||||
|
)
|
||||||
|
let rightRect = CGRect(
|
||||||
|
x: dividerX + 8,
|
||||||
|
y: screenRect.minY + 10,
|
||||||
|
width: max(screenRect.maxX - dividerX - 16, 0),
|
||||||
|
height: 18
|
||||||
|
)
|
||||||
|
|
||||||
|
if leftRect.width >= 72 {
|
||||||
|
("passthrough".localized as NSString).draw(in: leftRect, withAttributes: attributes)
|
||||||
|
}
|
||||||
|
if rightRect.width >= 80 {
|
||||||
|
let velocityText = LocalizationHelper.localizedString(forKey: "%.0f%% velocity", velocityPercent)
|
||||||
|
(velocityText as NSString).draw(in: rightRect, withAttributes: attributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func drawDirectionWheel(in rect: CGRect, color: UIColor, accentColor: UIColor) {
|
||||||
|
let base = min(rect.width, rect.height)
|
||||||
|
let center = CGPoint(x: rect.minX + rect.width * 0.18, y: rect.maxY - base * 0.20)
|
||||||
|
let radius = base * 0.13
|
||||||
|
color.withAlphaComponent(0.18).setStroke()
|
||||||
|
let outer = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
|
||||||
|
outer.lineWidth = 2
|
||||||
|
outer.stroke()
|
||||||
|
|
||||||
|
accentColor.withAlphaComponent(0.9).setStroke()
|
||||||
|
let arc = UIBezierPath(arcCenter: center, radius: radius * 0.72, startAngle: -.pi * 0.8, endAngle: -.pi * 0.18, clockwise: true)
|
||||||
|
arc.lineWidth = 5
|
||||||
|
arc.lineCapStyle = .round
|
||||||
|
arc.stroke()
|
||||||
|
|
||||||
|
color.withAlphaComponent(0.52).setStroke()
|
||||||
|
let hub = UIBezierPath(ovalIn: CGRect(x: center.x - radius * 0.17, y: center.y - radius * 0.17, width: radius * 0.34, height: radius * 0.34))
|
||||||
|
hub.lineWidth = 2
|
||||||
|
hub.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func drawCharacterSelector(in rect: CGRect, color: UIColor, accentColor: UIColor) {
|
||||||
|
let rowWidth = rect.width * 0.14
|
||||||
|
let rowHeight = rect.height * 0.07
|
||||||
|
let startX = rect.maxX - rowWidth - rect.width * 0.05
|
||||||
|
let startY = rect.minY + rect.height * 0.22
|
||||||
|
|
||||||
|
for index in 0..<3 {
|
||||||
|
let y = startY + CGFloat(index) * rowHeight * 1.42
|
||||||
|
let lineRect = CGRect(x: startX, y: y + rowHeight * 0.36, width: rowWidth * 0.6, height: rowHeight * 0.18)
|
||||||
|
let line = UIBezierPath(roundedRect: lineRect, cornerRadius: lineRect.height / 2)
|
||||||
|
color.withAlphaComponent(0.34).setFill()
|
||||||
|
line.fill()
|
||||||
|
|
||||||
|
let portraitRect = CGRect(x: startX + rowWidth * 0.68, y: y, width: rowHeight, height: rowHeight)
|
||||||
|
color.withAlphaComponent(0.38).setStroke()
|
||||||
|
let portrait = UIBezierPath(ovalIn: portraitRect)
|
||||||
|
portrait.lineWidth = 2
|
||||||
|
portrait.stroke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func drawActionButtons(in rect: CGRect, color: UIColor, accentColor: UIColor) {
|
||||||
|
let base = rect.width
|
||||||
|
let center = CGPoint(x: rect.maxX - base * 0.19, y: rect.maxY - base * 0.16)
|
||||||
|
let buttons: [(CGPoint, CGFloat)] = [
|
||||||
|
(center, base * 0.055),
|
||||||
|
(CGPoint(x: center.x + base * 0.11, y: center.y - base * 0.077), base * 0.05),
|
||||||
|
(CGPoint(x: center.x + base * 0.11, y: center.y + base * 0.068), base * 0.05),
|
||||||
|
(CGPoint(x: center.x - base * 0.11, y: center.y + base * 0.068), base * 0.05)
|
||||||
|
]
|
||||||
|
|
||||||
|
for (index, button) in buttons.enumerated() {
|
||||||
|
let rect = CGRect(x: button.0.x - button.1, y: button.0.y - button.1, width: button.1 * 2, height: button.1 * 2)
|
||||||
|
let path = UIBezierPath(ovalIn: rect)
|
||||||
|
color.withAlphaComponent(index == 0 ? 0.44 : 0.32).setStroke()
|
||||||
|
path.lineWidth = index == 0 ? 3 : 2
|
||||||
|
path.stroke()
|
||||||
|
|
||||||
|
let iconRect = rect.insetBy(dx: button.1 * 0.42, dy: button.1 * 0.42)
|
||||||
|
let icon = UIBezierPath(ovalIn: iconRect)
|
||||||
|
(index == 0 ? accentColor : color).withAlphaComponent(index == 0 ? 0.78 : 0.55).setFill()
|
||||||
|
icon.fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
extension SettingsViewController {
|
||||||
|
private var touchVelocityPreviewView: TouchVelocityPreviewView? {
|
||||||
|
get {
|
||||||
|
objc_getAssociatedObject(self, &touchVelocityPreviewViewKey) as? TouchVelocityPreviewView
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
objc_setAssociatedObject(self, &touchVelocityPreviewViewKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var touchVelocityPreviewDismissWorkItem: DispatchWorkItem? {
|
||||||
|
get {
|
||||||
|
objc_getAssociatedObject(self, &touchVelocityPreviewDismissWorkItemKey) as? DispatchWorkItem
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
objc_setAssociatedObject(self, &touchVelocityPreviewDismissWorkItemKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(showTouchVelocityPreviewWithDividerPercent:velocityPercent:)
|
||||||
|
func showTouchVelocityPreview(dividerPercent: CGFloat, velocityPercent: CGFloat) {
|
||||||
|
PublicUtils.runOnMain { [weak self] in
|
||||||
|
self?.showTouchVelocityPreviewOnMain(dividerPercent: dividerPercent, velocityPercent: velocityPercent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showTouchVelocityPreviewOnMain(dividerPercent: CGFloat, velocityPercent: CGFloat) {
|
||||||
|
let previewView = touchVelocityPreviewView ?? TouchVelocityPreviewView()
|
||||||
|
touchVelocityPreviewView = previewView
|
||||||
|
previewView.dividerPercent = dividerPercent
|
||||||
|
previewView.velocityPercent = velocityPercent
|
||||||
|
previewView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
|
if previewView.superview == nil {
|
||||||
|
previewView.alpha = 0
|
||||||
|
guard let containerView = view.window ?? view else { return }
|
||||||
|
containerView.addSubview(previewView)
|
||||||
|
let containerSafeArea = containerView.safeAreaLayoutGuide
|
||||||
|
var contraints = [
|
||||||
|
previewView.centerYAnchor.constraint(equalTo: containerSafeArea.centerYAnchor),
|
||||||
|
previewView.widthAnchor.constraint(equalToConstant: TouchVelocityPreviewView.preferredWidth),
|
||||||
|
previewView.heightAnchor.constraint(equalToConstant: TouchVelocityPreviewView.preferredHeight)
|
||||||
|
]
|
||||||
|
|
||||||
|
if PublicUtils.isIPhone {
|
||||||
|
contraints.append(previewView.trailingAnchor.constraint(equalTo: containerSafeArea.trailingAnchor, constant: previewView.bounds.width-50))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
contraints.append(previewView.centerXAnchor.constraint(equalTo: containerSafeArea.centerXAnchor, constant: 80))
|
||||||
|
}
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate(contraints)
|
||||||
|
}
|
||||||
|
|
||||||
|
previewView.superview?.bringSubviewToFront(previewView)
|
||||||
|
UIView.animate(withDuration: 0.12) {
|
||||||
|
previewView.alpha = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(cancelTouchVelocityPreviewDismiss)
|
||||||
|
func cancelTouchVelocityPreviewDismiss() {
|
||||||
|
PublicUtils.runOnMain { [weak self] in
|
||||||
|
self?.touchVelocityPreviewDismissWorkItem?.cancel()
|
||||||
|
self?.touchVelocityPreviewDismissWorkItem = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(scheduleTouchVelocityPreviewDismiss)
|
||||||
|
func scheduleTouchVelocityPreviewDismiss() {
|
||||||
|
PublicUtils.runOnMain { [weak self] in
|
||||||
|
self?.scheduleTouchVelocityPreviewDismissOnMain()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func scheduleTouchVelocityPreviewDismissOnMain() {
|
||||||
|
guard let previewView = touchVelocityPreviewView else { return }
|
||||||
|
touchVelocityPreviewDismissWorkItem?.cancel()
|
||||||
|
let workItem = DispatchWorkItem { [weak self, weak previewView] in
|
||||||
|
UIView.animate(withDuration: 0.18, animations: {
|
||||||
|
previewView?.alpha = 0
|
||||||
|
}, completion: { _ in
|
||||||
|
previewView?.removeFromSuperview()
|
||||||
|
if self?.touchVelocityPreviewView === previewView {
|
||||||
|
self?.touchVelocityPreviewView = nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
touchVelocityPreviewDismissWorkItem = workItem
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: workItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2411,9 +2411,17 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
|
|
||||||
// pointer veloc setting, will be enable/disabled by touchMode
|
// pointer veloc setting, will be enable/disabled by touchMode
|
||||||
[self.pointerVelocityModeDividerSlider addTarget:self action:@selector(pointerVelocityModeDividerSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
[self.pointerVelocityModeDividerSlider addTarget:self action:@selector(pointerVelocityModeDividerSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||||
|
if (@available(iOS 13.0, *)) {
|
||||||
|
[self.pointerVelocityModeDividerSlider addTarget:self action:@selector(cancelTouchVelocityPreviewDismiss) forControlEvents:UIControlEventTouchDown];
|
||||||
|
[self.pointerVelocityModeDividerSlider addTarget:self action:@selector(scheduleTouchVelocityPreviewDismiss) forControlEvents:(UIControlEventTouchUpInside | UIControlEventTouchUpOutside | UIControlEventTouchCancel)];
|
||||||
|
}
|
||||||
|
|
||||||
// init pointer veloc setting, will be enable/disabled by touchMode
|
// init pointer veloc setting, will be enable/disabled by touchMode
|
||||||
[self.touchPointerVelocityFactorSlider addTarget:self action:@selector(touchPointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
[self.touchPointerVelocityFactorSlider addTarget:self action:@selector(touchPointerVelocityFactorSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||||
|
if (@available(iOS 13.0, *)) {
|
||||||
|
[self.touchPointerVelocityFactorSlider addTarget:self action:@selector(cancelTouchVelocityPreviewDismiss) forControlEvents:UIControlEventTouchDown];
|
||||||
|
[self.touchPointerVelocityFactorSlider addTarget:self action:@selector(scheduleTouchVelocityPreviewDismiss) forControlEvents:(UIControlEventTouchUpInside | UIControlEventTouchUpOutside | UIControlEventTouchCancel)];
|
||||||
|
}
|
||||||
|
|
||||||
// async native touch event
|
// async native touch event
|
||||||
// self.asyncNativeTouchPrioritySelector.selectedSegmentIndex = currentSettings.asyncNativeTouchPriority.intValue; // load old setting of asyncNativeTouchPriority
|
// self.asyncNativeTouchPrioritySelector.selectedSegmentIndex = currentSettings.asyncNativeTouchPriority.intValue; // load old setting of asyncNativeTouchPriority
|
||||||
@@ -3264,10 +3272,20 @@ BOOL isCustomResolution(int resolutionSelected) {
|
|||||||
|
|
||||||
- (void) pointerVelocityModeDividerSliderMoved:(UISlider* )sender {
|
- (void) pointerVelocityModeDividerSliderMoved:(UISlider* )sender {
|
||||||
[self findDynamicLabelFromStack:(UIStackView*)sender.superview].text = [NSString stringWithFormat:@" | %d%% | %d%% | ", (uint8_t)sender.value, 100-(uint8_t)sender.value];
|
[self findDynamicLabelFromStack:(UIStackView*)sender.superview].text = [NSString stringWithFormat:@" | %d%% | %d%% | ", (uint8_t)sender.value, 100-(uint8_t)sender.value];
|
||||||
|
bool hasNoPresentedVC = self.presentedViewController == nil && self.view.window != nil;
|
||||||
|
if (@available(iOS 13.0, *)) if(!settingsViewJustExpanded && hasNoPresentedVC) {
|
||||||
|
[self showTouchVelocityPreviewWithDividerPercent:sender.value
|
||||||
|
velocityPercent:[self map_velocFactorDisplay_fromSliderValue:self.touchPointerVelocityFactorSlider.value]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) touchPointerVelocityFactorSliderMoved:(UISlider* )sender {
|
- (void) touchPointerVelocityFactorSliderMoved:(UISlider* )sender {
|
||||||
[self findDynamicLabelFromStack:(UIStackView*)sender.superview].text = [NSString stringWithFormat:@" %d%% ", (uint16_t)[self map_velocFactorDisplay_fromSliderValue:sender.value]]; // Update label display
|
[self findDynamicLabelFromStack:(UIStackView*)sender.superview].text = [NSString stringWithFormat:@" %d%% ", (uint16_t)[self map_velocFactorDisplay_fromSliderValue:sender.value]]; // Update label display
|
||||||
|
bool hasNoPresentedVC = self.presentedViewController == nil && self.view.window != nil;
|
||||||
|
if (@available(iOS 13.0, *)) if(!settingsViewJustExpanded && hasNoPresentedVC) {
|
||||||
|
[self showTouchVelocityPreviewWithDividerPercent:self.pointerVelocityModeDividerSlider.value
|
||||||
|
velocityPercent:[self map_velocFactorDisplay_fromSliderValue:sender.value]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) gyroSensitivitySliderMoved:(UISlider* )sender {
|
- (void) gyroSensitivitySliderMoved:(UISlider* )sender {
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ import UIKit
|
|||||||
private var controllerNavigationExecuteDismissWindowOpen = false
|
private var controllerNavigationExecuteDismissWindowOpen = false
|
||||||
private var controllerNavigationExecuteDismissToken = 0
|
private var controllerNavigationExecuteDismissToken = 0
|
||||||
private var controllerNavigationExecuteSuppressNextRelease = false
|
private var controllerNavigationExecuteSuppressNextRelease = false
|
||||||
|
private var pendingSpecialEntryToken = 0
|
||||||
private var isEditingMode = false {
|
private var isEditingMode = false {
|
||||||
didSet {
|
didSet {
|
||||||
if !isEditingMode {
|
if !isEditingMode {
|
||||||
@@ -643,10 +644,18 @@ import UIKit
|
|||||||
|
|
||||||
private func activateEntry(at indexPath: IndexPath, dismissAfterAction: Bool = true) {
|
private func activateEntry(at indexPath: IndexPath, dismissAfterAction: Bool = true) {
|
||||||
guard entries.indices.contains(indexPath.item) else { return }
|
guard entries.indices.contains(indexPath.item) else { return }
|
||||||
|
let entryIdentifier = entries[indexPath.item].orderIdentifier
|
||||||
|
activateEntry(withIdentifier: entryIdentifier, fallbackIndexPath: indexPath, dismissAfterAction: dismissAfterAction)
|
||||||
|
}
|
||||||
|
|
||||||
switch entries[indexPath.item] {
|
private func activateEntry(withIdentifier identifier: String, fallbackIndexPath: IndexPath? = nil, dismissAfterAction: Bool = true) {
|
||||||
|
guard let item = entries.firstIndex(where: { $0.orderIdentifier == identifier }) else { return }
|
||||||
|
let indexPath = IndexPath(item: item, section: 0)
|
||||||
|
pendingSpecialEntryToken += 1
|
||||||
|
|
||||||
|
switch entries[item] {
|
||||||
case .special(let id, _):
|
case .special(let id, _):
|
||||||
collectionView.deselectItem(at: indexPath, animated: false)
|
collectionView.deselectItem(at: fallbackIndexPath ?? indexPath, animated: false)
|
||||||
if !isEditingMode {
|
if !isEditingMode {
|
||||||
flashCell(at: indexPath)
|
flashCell(at: indexPath)
|
||||||
handleSpecialEntry(id: id)
|
handleSpecialEntry(id: id)
|
||||||
@@ -670,6 +679,7 @@ import UIKit
|
|||||||
sendKeyboardCommand(command)
|
sendKeyboardCommand(command)
|
||||||
if dismissAfterAction && !viewPinned {
|
if dismissAfterAction && !viewPinned {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
||||||
|
guard self.presentingViewController != nil else { return }
|
||||||
self.dismiss(animated: false)
|
self.dismiss(animated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -848,7 +858,11 @@ import UIKit
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func handleSpecialEntry(id: String) {
|
private func handleSpecialEntry(id: String) {
|
||||||
|
pendingSpecialEntryToken += 1
|
||||||
|
let token = pendingSpecialEntryToken
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
||||||
|
guard self.pendingSpecialEntryToken == token else { return }
|
||||||
self.dismiss(animated: false)
|
self.dismiss(animated: false)
|
||||||
|
|
||||||
switch id {
|
switch id {
|
||||||
@@ -1074,7 +1088,7 @@ extension ToolboxViewController: ControllerCollectionNavigationDelegate {
|
|||||||
controllerNavigationDeleteDoublePressToken += 1
|
controllerNavigationDeleteDoublePressToken += 1
|
||||||
let token = controllerNavigationDeleteDoublePressToken
|
let token = controllerNavigationDeleteDoublePressToken
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
|
||||||
guard let self,
|
guard let self,
|
||||||
self.controllerNavigationDeleteDoublePressToken == token else {
|
self.controllerNavigationDeleteDoublePressToken == token else {
|
||||||
return
|
return
|
||||||
@@ -1383,7 +1397,7 @@ private final class ToolboxCardCell: UICollectionViewCell, ControllerNavigationH
|
|||||||
}
|
}
|
||||||
|
|
||||||
override var canBecomeFocused: Bool {
|
override var canBecomeFocused: Bool {
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var controllerNavigationHighlightTargetView: UIView {
|
var controllerNavigationHighlightTargetView: UIView {
|
||||||
|
|||||||
Reference in new issue
Block a user