mac desktop experience fixes & tips

This commit is contained in:
True Zhuanjia
2026-04-28 17:09:33 +08:00
parent 9e858520c8
commit efc34ae1fc
13 changed files with 170 additions and 56 deletions
+45 -7
View File
@@ -8,6 +8,13 @@
import UIKit
@objc class AlertControllerUtil: NSObject {
private static func makeCountdownMessage(baseMessage: String?, remainingSeconds: Int) -> String {
let countdownText = "\(remainingSeconds)"
guard let baseMessage, !baseMessage.isEmpty else {
return countdownText
}
return "\(baseMessage)\n\n\(countdownText)"
}
/// 类方法,直接在任何 UIViewController 上显示倒计时弹窗
/// - Parameters:
@@ -34,16 +41,24 @@ import UIKit
completion: (() -> Void)? = nil
) {
var remainingSeconds = countdown
let originalMessage = message
var isAlertDismissed = false
alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController = UIAlertController(
title: title,
message: (countdown > 0 && !autoCompletion) ? makeCountdownMessage(baseMessage: originalMessage, remainingSeconds: remainingSeconds) : message,
preferredStyle: .alert
)
let confirmAction = UIAlertAction(title: "\(remainingSeconds)", style: .default) { _ in
let confirmAction = UIAlertAction(title: buttonTitle, style: .default) { _ in
isAlertDismissed = true
actionCancelled = false
completion?()
cancelButtonString = "Cancel"
}
let cancelAction = UIAlertAction(title: SwiftLocalizationHelper.localizedString(forKey: cancelButtonString), style: .cancel) { _ in
isAlertDismissed = true
actionCancelled = true
completion?()
cancelButtonString = "Cancel"
@@ -55,7 +70,6 @@ import UIKit
if buttonTitle != "" && !autoCompletion {alertController.addAction(confirmAction)}
if(countdown == 0) {
confirmAction.setValue(buttonTitle, forKey: "title")
confirmAction.isEnabled = buttonTitle != ""
}
@@ -65,22 +79,46 @@ import UIKit
// 倒计时
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
timer.schedule(deadline: .now(), repeating: 1.0, leeway: .milliseconds(50))
timer.schedule(deadline: .now() + (autoCompletion ? 0.5 : 1.0), repeating: autoCompletion ? 0.1: 1.0, leeway: .milliseconds(50))
timer.setEventHandler {
guard !isAlertDismissed else {
timer.cancel()
return
}
remainingSeconds -= 1
if remainingSeconds <= 0 {
isAlertDismissed = true
timer.cancel()
confirmAction.isEnabled = true
confirmAction.setValue(buttonTitle, forKey: "title")
if autoCompletion {
alertController.message = originalMessage
autoCompletion = false
completion?()
alertController.dismiss(animated: false)
return
}
if GenericUtils.isRunningOnMacAsiPadApp && buttonTitle != "" {
alertController.dismiss(animated: false) {
showAlert(
in: viewController,
title: title,
message: originalMessage,
withCancel: withCancel,
buttonTitle: buttonTitle,
countdown: 0,
action: nil,
completion: completion
)
}
return
}
confirmAction.isEnabled = true
alertController.message = originalMessage
} else {
confirmAction.setValue("\(remainingSeconds)", forKey: "title")
if !autoCompletion {alertController.message = makeCountdownMessage(baseMessage: originalMessage, remainingSeconds: remainingSeconds)}
}
}
timer.resume()
+2 -2
View File
@@ -1372,13 +1372,13 @@
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="nE1-Al-qRc" userLabel="localMousePointerMode Stack">
<rect key="frame" x="18" y="2848" width="380" height="56.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Local Mouse Pointer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tAd-bm-2IO">
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Local Mouse Cursor" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tAd-bm-2IO">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="4FU-iP-c6T" userLabel="Mouse Mode">
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="4FU-iP-c6T" userLabel="Mouse Mode">
<rect key="frame" x="0.0" y="25.5" width="380" height="32"/>
<segments>
<segment title="Captured"/>
+13 -13
View File
@@ -152,13 +152,13 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Jnu-63-bcf" customClass="ToolBarContainerView">
<rect key="frame" x="0.0" y="96" width="410" height="45"/>
<rect key="frame" x="0.0" y="96" width="1300" height="45"/>
<subviews>
<stackView opaque="NO" contentMode="scaleAspectFill" distribution="fillEqually" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="4he-pe-kYB">
<rect key="frame" x="0.0" y="-2.5" width="410" height="50"/>
<rect key="frame" x="0.0" y="-2.5" width="1300" height="50"/>
<subviews>
<button opaque="NO" tag="40" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="fill" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nHH-6h-4D1" userLabel="Exit">
<rect key="frame" x="0.0" y="9.5" width="50" height="31"/>
<rect key="frame" x="0.0" y="9.5" width="177" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="sBF-hd-nF8"/>
</constraints>
@@ -174,7 +174,7 @@
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Pdg-l4-BvF" userLabel="Undo">
<rect key="frame" x="60" y="0.0" width="50" height="50"/>
<rect key="frame" x="187" y="0.0" width="177.5" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="7KW-Xe-OEK"/>
<constraint firstAttribute="width" constant="50" id="gHD-A0-AVS"/>
@@ -191,7 +191,7 @@
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rXW-Ms-2cJ" userLabel="Load">
<rect key="frame" x="120" y="0.0" width="50" height="50"/>
<rect key="frame" x="374.5" y="0.0" width="177" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="91g-27-mVH"/>
<constraint firstAttribute="height" constant="50" id="kRj-b6-Bx1"/>
@@ -207,7 +207,7 @@
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LPA-ea-YHC" userLabel="Save">
<rect key="frame" x="180" y="0.0" width="50" height="50"/>
<rect key="frame" x="561.5" y="0.0" width="177" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="Lot-pn-o60"/>
<constraint firstAttribute="height" constant="50" id="uWO-So-qxr"/>
@@ -223,7 +223,7 @@
</connections>
</button>
<button opaque="NO" tag="20" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dhG-Fo-0GK" userLabel="Trash">
<rect key="frame" x="240" y="0.0" width="50" height="50"/>
<rect key="frame" x="748.5" y="0.0" width="177" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="0vV-wV-Efz"/>
<constraint firstAttribute="width" constant="50" id="j9W-Ja-bVn"/>
@@ -239,7 +239,7 @@
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Up4-28-sgF" userLabel="Add">
<rect key="frame" x="300" y="0.0" width="50" height="50"/>
<rect key="frame" x="935.5" y="0.0" width="177.5" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="rWU-US-9dl"/>
<constraint firstAttribute="width" constant="50" id="wWj-wB-MRd"/>
@@ -254,7 +254,7 @@
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4pE-po-Zn1" userLabel="Edit">
<rect key="frame" x="360" y="0.0" width="50" height="50"/>
<rect key="frame" x="1123" y="0.0" width="177" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="oFn-jm-orG"/>
<constraint firstAttribute="height" constant="50" id="sls-Vh-uxz"/>
@@ -273,7 +273,7 @@
</subviews>
</stackView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Zll-IP-PFt" userLabel="Chevron View">
<rect key="frame" x="179" y="45" width="52" height="25"/>
<rect key="frame" x="624" y="45" width="52" height="25"/>
<subviews>
<imageView clipsSubviews="YES" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ChevronCompactUp" translatesAutoresizingMaskIntoConstraints="NO" id="76O-3K-aa8">
<rect key="frame" x="0.0" y="0.0" width="52" height="25"/>
@@ -302,7 +302,7 @@
</constraints>
</view>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="7" translatesAutoresizingMaskIntoConstraints="NO" id="BVp-3O-vZ7" userLabel="widgetPanelStack" customClass="WidgetPanelStackView">
<rect key="frame" x="-70" y="132" width="550" height="1110"/>
<rect key="frame" x="375" y="132" width="550" height="1110"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Profile:" textAlignment="center" lineBreakMode="tailTruncation" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="f46-Pt-0Pv">
<rect key="frame" x="0.0" y="0.0" width="550" height="28"/>
@@ -1940,13 +1940,13 @@
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="IKt-3O-raJ" userLabel="localMousePointerMode Stack">
<rect key="frame" x="0.0" y="2553" width="380" height="56.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Local Mouse Pointer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mdA-jF-9Mf">
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Local Mouse Cursor" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mdA-jF-9Mf">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="zLl-8w-K8J" userLabel="Mouse Mode">
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="zLl-8w-K8J" userLabel="Mouse Mode">
<rect key="frame" x="0.0" y="25.5" width="380" height="32"/>
<segments>
<segment title="Captured"/>
+1
View File
@@ -24,6 +24,7 @@
- (void)toggleStatsOverlay;
- (void)toggleMouseCapture;
- (void)toggleMouseVisible;
- (void)expandSettingsView;
- (void)disconnectAndQuitApp;
@end
+6 -3
View File
@@ -1239,19 +1239,22 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
return;
}
switch (press.key.keyCode) {
case UIKeyboardHIDUsageKeyboardQ:
case UIKeyboardHIDUsageKeyboardD:
[_interactionDelegate streamExitRequested];
break;
case UIKeyboardHIDUsageKeyboardS:
case UIKeyboardHIDUsageKeyboardO:
[_interactionDelegate toggleStatsOverlay];
break;
case UIKeyboardHIDUsageKeyboardS:
[_interactionDelegate expandSettingsView];
break;
case UIKeyboardHIDUsageKeyboardM:
[_interactionDelegate toggleMouseCapture];
break;
case UIKeyboardHIDUsageKeyboardC:
[_interactionDelegate toggleMouseVisible];
break;
case UIKeyboardHIDUsageKeyboardD:
case UIKeyboardHIDUsageKeyboardQ:
[_interactionDelegate disconnectAndQuitApp];
break;
default:
@@ -73,7 +73,7 @@
<attribute name="keyboardToggleFingers" optional="YES" attributeType="Integer 16" defaultValueString="99" usesScalarValueType="NO" syncable="YES"/>
<attribute name="leftClickDelayMs" optional="YES" attributeType="Float" defaultValueString="15" usesScalarValueType="NO" syncable="YES"/>
<attribute name="liftStreamViewForKeyboard" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="localMousePointerMode" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="localMousePointerMode" optional="YES" attributeType="Integer 16" defaultValueString="2" usesScalarValueType="NO" syncable="YES"/>
<attribute name="localVolume" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="mapControllerToMouse" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="micVolume" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
+37 -8
View File
@@ -4734,25 +4734,25 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Select “Stage Manager” to enable full screen external display.\n\nKeyboard Shortcuts for VoidLink (Desktop Mode):\n\nCtrl + Alt + Shift + Q — Disconnect the current session\n\nCtrl + Alt + Shift + S — Toggle the statistics overlay style\n\nCtrl + Alt + Shift + M — Toggle mouse capture mode\n\nCtrl + Alt + Shift + C — Toggle mouse pointer visibility\n"
"value" : "\nKeyboard Shortcuts for VoidLink (Desktop Mode):\n\nCtrl + Alt + Shift + D — Disconnect the current session\n\nCtrl + Alt + Shift + Q — Disconnect the current session and quit current app\n\nCtrl + Alt + Shift + S — Expand the settings menu\n\nCtrl + Alt + Shift + O — Toggle the statistics overlay\n\nCtrl + Alt + Shift + C — Toggle mouse pointer visibility"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "VoidLink 桌面模式快捷键:\n\n- Ctrl + Alt + Shift + Q — 断开当前会话\n\n- Ctrl + Alt + Shift + S — 切换性能信息叠加样式\n\n- Ctrl + Alt + Shift + M — 切换鼠标指针捕获模式\n\n- Ctrl + Alt + Shift + C — 切换鼠标指针可见性"
"state" : "translated",
"value" : "\nVoidLink 桌面模式快捷键:\n\nCtrl + Alt + Shift + D — 断开当前会话\n\nCtrl + Alt + Shift + Q — 断开当前会话并退出当前app\n\nCtrl + Alt + Shift + S — 展开设置菜单\n\nCtrl + Alt + Shift + O — 切换性能信息叠加\n\nCtrl + Alt + Shift + C — 切换鼠标指针可见性"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "VoidLink 桌面模式快捷鍵:\n\nCtrl + Alt + Shift + Q — 中斷目前連線\n\nCtrl + Alt + Shift + S — 切換統計資訊覆蓋樣式\n\nCtrl + Alt + Shift + M — 切換滑鼠指標捕捉模式\n\nCtrl + Alt + Shift + C — 切換滑鼠指標可見性"
"state" : "translated",
"value" : "\nVoidLink 桌面模式快速鍵:\n\nCtrl + Alt + Shift + D — 中斷目前連線\n\nCtrl + Alt + Shift + Q — 中斷目前連線並退出目前app\n\nCtrl + Alt + Shift + S — 展開設定選單\n\nCtrl + Alt + Shift + O — 切換統計資訊覆蓋\n\nCtrl + Alt + Shift + C — 切換滑鼠指標可見性"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "VoidLink 桌面模式快捷鍵:\n\nCtrl + Alt + Shift + Q — 中斷目前連線\n\nCtrl + Alt + Shift + S — 切換統計資訊覆蓋樣式\n\nCtrl + Alt + Shift + M — 切換滑鼠指標捕捉模式\n\nCtrl + Alt + Shift + C — 切換滑鼠指標可見性"
"state" : "translated",
"value" : "\nVoidLink 桌面模式快速鍵:\n\nCtrl + Alt + Shift + D — 中斷目前連線\n\nCtrl + Alt + Shift + Q — 中斷目前連線並退出目前app\n\nCtrl + Alt + Shift + S — 展開設定選單\n\nCtrl + Alt + Shift + O — 切換統計資訊覆蓋\n\nCtrl + Alt + Shift + C — 切換滑鼠指標可見性"
}
}
}
@@ -6911,6 +6911,35 @@
}
}
},
"macFirstLaunchTip" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "\nKeyboard Shortcuts for VoidLink (Desktop Mode):\n\nCtrl + Alt + Shift + D — Disconnect the current session\n\nCtrl + Alt + Shift + Q — Disconnect the current session and quit current app\n\nCtrl + Alt + Shift + S — Expand the settings menu\n\nCtrl + Alt + Shift + O — Toggle the statistics overlay\n\nCtrl + Alt + Shift + C — Toggle mouse pointer visibility\n\nBy default, the mouse cannot control the camera in 3D games. To stream games, please disconnect the current session, then go to Settings → Peripheral Section → Local Mouse Cursor, and select “Capture”."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "\nVoidLink 桌面模式快捷键:\n\nCtrl + Alt + Shift + D — 断开当前会话\n\nCtrl + Alt + Shift + Q — 断开当前会话并退出当前app\n\nCtrl + Alt + Shift + S — 展开设置菜单\n\nCtrl + Alt + Shift + O — 切换性能信息叠加\n\nCtrl + Alt + Shift + C — 切换鼠标指针可见性\n\n默认状态下鼠标无法在3D游戏中转动视角。如需串流游戏,请断开当前会话,前往设置菜单 -> 外设分区 -> 本地鼠标指针,选择“被窗口捕获”。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "\nVoidLink 桌面模式快速鍵:\n\nCtrl + Alt + Shift + D — 中斷目前連線\n\nCtrl + Alt + Shift + Q — 中斷目前連線並退出目前app\n\nCtrl + Alt + Shift + S — 展開設定選單\n\nCtrl + Alt + Shift + O — 切換統計資訊覆蓋\n\nCtrl + Alt + Shift + C — 切換滑鼠指標可見性\n\n預設狀態下,滑鼠無法在 3D 遊戲中轉動視角。如需進行串流遊戲,請中斷目前會話,前往設定選單 → 外設分區 → 本地滑鼠指標,選擇「被視窗捕獲」。"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "\nVoidLink 桌面模式快速鍵:\n\nCtrl + Alt + Shift + D — 中斷目前連線\n\nCtrl + Alt + Shift + Q — 中斷目前連線並退出目前app\n\nCtrl + Alt + Shift + S — 展開設定選單\n\nCtrl + Alt + Shift + O — 切換統計資訊覆蓋\n\nCtrl + Alt + Shift + C — 切換滑鼠指標可見性\n\n預設狀態下,滑鼠無法在 3D 遊戲中轉動視角。如需進行串流遊戲,請中斷目前會話,前往設定選單 → 外設分區 → 本地滑鼠指標,選擇「被視窗捕獲」。"
}
}
}
},
"Manual_Add_Host_Fail" : {
"extractionState" : "manual",
"localizations" : {
@@ -12241,7 +12270,7 @@
},
"zh-Hant" : {
"stringUnit" : {
"state" : "needs_review",
"state" : "translated",
"value" : "- 觸控板:拖曳方式與 Windows 筆記型電腦觸控板完全相同。快速輕點兩下,從第二次輕點開始進入拖曳模式。\n\n- 多点觸控:macOS 不支援。\n\n- 單點滑鼠:在 macOS 上提供類似觸控螢幕的體驗。"
}
},
+31
View File
@@ -22,6 +22,16 @@ import UIKit
return true
}
@objc static func needUpdatePartialSettings() -> Bool {
// let key = "needUpdateDefaultSettings20260226-1"
let key = "needUpdatePartialSettings20260428"
guard !UserDefaults.standard.bool(forKey: key) else {
return false
}
UserDefaults.standard.set(true, forKey: key)
return true
}
@objc static func isEnableOswForNativeTouchSwitchFirstFlipping() -> Bool {
let key = "enableOswForNativeTouchSwitchFlipped"
guard !UserDefaults.standard.bool(forKey: key) else {
@@ -78,6 +88,20 @@ import UIKit
}
return false
}
@objc static func isFirstStreamingOnMac() -> Bool {
if !isRunningOnMacAsiPadApp {return false}
let key = "hasStreamedOnMac"
let defaults = UserDefaults.standard
let launchedBefore = defaults.bool(forKey: key)
if !launchedBefore {
defaults.set(true, forKey: key)
return true
}
return false
}
@objc static func gamepadOverlayFeatureTipTitle() -> String {
SwiftLocalizationHelper.localizedString(forKey: "Gamepad Overlay")
@@ -98,6 +122,13 @@ import UIKit
@objc static func isIPad() -> Bool {
return UIDevice.current.userInterfaceIdiom == .pad
}
@objc static var isRunningOnMacAsiPadApp: Bool {
if #available(iOS 14.0, *) {
return ProcessInfo.processInfo.isiOSAppOnMac
}
return false
}
@objc static let liquidGlassEnabled: Bool = {
if #available(iOS 26.0, tvOS 26.0, *) {
@@ -1740,6 +1740,15 @@ static NSMutableSet* hostList;
[dataMan saveData];
}
- (void)updatePartialSettings{
if(![GenericUtils needUpdatePartialSettings]) return;
DataManager* dataMan = [[DataManager alloc] init];
Settings* settings = [dataMan retrieveSettings];
settings.touchMoveEventInterval = @(0);
settings.localMousePointerMode = @(2);
[dataMan saveData];
}
- (void)viewDidLoad{
[super viewDidLoad];
@@ -1863,7 +1872,7 @@ static NSMutableSet* hostList;
[IAPManager.shared fetchProducts];
[self changeDefaultSettings];
[self updatePartialSettings];
/*
if (@available(iOS 15.0, *)) {
[IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) {
@@ -1174,8 +1174,8 @@ BOOL isCustomResolution(int resolutionSelected) {
[self addSetting:self.touchModeStack2 ofId:@"touchModeStack2" to:experimentalSection];
self.touchMoveEventIntervalStack.hasDynamicLabel = YES;
[self addSetting:self.touchMoveEventIntervalStack ofId:@"touchMoveEventIntervalStack" to:experimentalSection];
// self.touchMoveEventIntervalStack.hasDynamicLabel = YES;
// [self addSetting:self.touchMoveEventIntervalStack ofId:@"touchMoveEventIntervalStack" to:experimentalSection];
self.relativeTouchSlideThresholdStack.hasInfoTag = YES;
self.relativeTouchSlideThresholdStack.hasDynamicLabel = YES;
@@ -2389,7 +2389,7 @@ BOOL isCustomResolution(int resolutionSelected) {
[self.onScreenWidgetSelector addTarget:self action:@selector(onScreenWidgetChanged) forControlEvents:UIControlEventValueChanged];
[self onScreenWidgetChanged];
// touch move event interval for native-touch.
/*
[self.touchMoveEventIntervalSlider setValue:self->tempSettings.touchMoveEventInterval.intValue animated:NO]; // Load old setting.
[self.touchMoveEventIntervalSlider addTarget:self action:@selector(touchMoveEventIntervalSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
[self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
@@ -2398,6 +2398,7 @@ BOOL isCustomResolution(int resolutionSelected) {
[self.leftClickDelaySlider setValue:self->tempSettings.leftClickDelayMs.intValue animated:NO]; // Load old setting.
[self.leftClickDelaySlider addTarget:self action:@selector(leftClickDelaySliderMoved:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
[self leftClickDelaySliderMoved:self.leftClickDelaySlider];
*/
// this part will enable/disable oscSelector & the asyncNativeTouchPriority selector
uint8_t touchModeSelectorIndex = self->tempSettings.touchMode.intValue == NativeTouchOnly ? NativeTouch : self->tempSettings.touchMode.intValue;
@@ -3183,10 +3184,10 @@ BOOL isCustomResolution(int resolutionSelected) {
[self setHidden:!isNativeTouch forStack:self.pointerVelocityDividerStack];
[self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
// [self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
[self setHidden:!isNativeTouch forStack:self.pointerVelocityDividerStack];
[self setHidden:!isNativeTouch forStack:self.pointerVelocityFactorStack];
[self setHidden:!isNativeTouch forStack:self.touchMoveEventIntervalStack];
// [self setHidden:!isNativeTouch forStack:self.touchMoveEventIntervalStack];
/*
[self setHidden:(sender.selectedSegmentIndex!=RelativeTouch
@@ -3384,7 +3385,7 @@ BOOL isCustomResolution(int resolutionSelected) {
[[NSUserDefaults standardUserDefaults] synchronize];
// NSInteger fps = [self getChosenFrameRate];
[self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
// [self touchMoveEventIntervalSliderMoved:self.touchMoveEventIntervalSlider];
[self updateBitrate];
}
@@ -3993,7 +3994,7 @@ BOOL isCustomResolution(int resolutionSelected) {
CGFloat localVolume = self.localVolumeSlider.value/100;
CGFloat micVolume = self.micVolumeSlider.value/100;
uint16_t touchMoveEventInterval = (uint16_t)self.touchMoveEventIntervalSlider.value;
uint16_t touchMoveEventInterval = 0;
BOOL reverseMouseWheelDirection = [self.reverseMouseWheelDirectionSelector selectedSegmentIndex] == 1;
NSInteger asyncNativeTouchPriority = 1;
@@ -582,7 +582,9 @@
NSString* cmdToolEdgeSide = _settings.slideToSettingsScreenEdge.intValue == UIRectEdgeLeft ? [LocalizationHelper localizedStringForKey:@"right"] : [LocalizationHelper localizedStringForKey:@"left"];
uint8_t slideDist = (uint8_t)(_settings.slideToSettingsDistance.floatValue * 100);
// 创建弹窗
NSString* tipText = [LocalizationHelper localizedStringForKey:@"firstLaunchTip", settingsEdgeSide, slideDist, cmdToolEdgeSide, slideDist];
NSString* tipText = (GenericUtils.isRunningOnMacAsiPadApp
? [LocalizationHelper localizedStringForKey:@"macFirstLaunchTip"]
: [LocalizationHelper localizedStringForKey:@"firstLaunchTip", settingsEdgeSide, slideDist, cmdToolEdgeSide, slideDist]) ;
[AlertControllerUtil showAlertIn:self
title:[LocalizationHelper localizedStringForKey:@"First Launch Tips"]
@@ -659,7 +661,7 @@
//[_streamView setupStreamView:_controllerSupport interactionDelegate:self config:self.streamConfig];
[self reConfigStreamViewRealtime]; // call this method again to make sure all gestures are configured & added to the superview(self.view), including the gestures added from inside the streamview.
if([self isFirstStreaming]) [self popFirstStreamingTip];
if([self isFirstStreaming] || GenericUtils.isFirstStreamingOnMac) [self popFirstStreamingTip];
#if TARGET_OS_TV
if (!_menuTapGestureRecognizer || !_menuDoubleTapGestureRecognizer || !_playPauseTapGestureRecognizer) {
+5 -5
View File
@@ -6095,30 +6095,30 @@
}
},
"tAd-bm-2IO.text" : {
"comment" : "Class = \"UILabel\"; text = \"Local Mouse Pointer\"; ObjectID = \"tAd-bm-2IO\";",
"comment" : "Class = \"UILabel\"; text = \"Local Mouse Cursor\"; ObjectID = \"tAd-bm-2IO\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Local Mouse Pointer"
"value" : "Local Mouse Cursor"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"state" : "needs_review",
"value" : "本地鼠标指针"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"state" : "needs_review",
"value" : "本地滑鼠指標"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"state" : "needs_review",
"value" : "本地滑鼠指標"
}
}
+7 -7
View File
@@ -3422,30 +3422,30 @@
}
},
"mdA-jF-9Mf.text" : {
"comment" : "Class = \"UILabel\"; text = \"Local Mouse Pointer\"; ObjectID = \"mdA-jF-9Mf\";",
"comment" : "Class = \"UILabel\"; text = \"Local Mouse Cursor\"; ObjectID = \"mdA-jF-9Mf\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Local Mouse Pointer"
"value" : "Local Mouse Cursor"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"state" : "needs_review",
"value" : "本地鼠标指针"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"state" : "needs_review",
"value" : "本地滑鼠指標"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"state" : "needs_review",
"value" : "本地滑鼠指標"
}
}
@@ -3505,13 +3505,13 @@
},
"zh-Hant" : {
"stringUnit" : {
"state" : "needs_review",
"state" : "translated",
"value" : "異步多點觸控"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "needs_review",
"state" : "translated",
"value" : "異步多點觸控"
}
}