new touchpad widget: absolute mousepad

This commit is contained in:
True Zhuanjia committed 2026-04-28 14:25:25 +08:00
1 parent cb235d9255
commit 20aba200b3
7 files changed
+133 -63

No files matched your search

+3 -3
View File
@@ -118,9 +118,9 @@ import UIKit
"OSCBACK" "OSCBACK"
] ]
@objc public static let touchPadCmds: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD", "LSWHEEL", "RSWHEEL", "LTPAD", "RTPAD", "DS4TOUCH", "MOUSEPAD", "ABSMOUSE", "MOUSEWHEEL", "WHEEL", "DISCRETEWHEEL", "DSWHEEL", "DPAD", "TRACKBALL", "WASDPAD", "ARROWPAD"] @objc public static let touchPadCmds: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD", "LSWHEEL", "RSWHEEL", "LTPAD", "RTPAD", "DS4TOUCH", "MOUSEPAD", "ABSMOUSEPAD", "ABSMOUSE", "MOUSEWHEEL", "WHEEL", "DISCRETEWHEEL", "DSWHEEL", "DPAD", "TRACKBALL", "WASDPAD", "ARROWPAD"]
@objc public static let mousePadWithButtonActions: [String] = ["MOUSEPAD", "ABSMOUSE"] @objc public static let mousePadWithButtonActions: [String] = ["MOUSEPAD", "ABSMOUSE", "ABSMOUSEPAD"]
@objc public static let mousePads: [String] = ["MOUSEPAD", "ABSMOUSE", "TRACKBALL"] @objc public static let mousePads: [String] = ["MOUSEPAD", "ABSMOUSE", "TRACKBALL", "ABSMOUSEPAD"]
@objc public static let directionPads: [String] = ["DPAD", "WASDPAD", "ARROWPAD"] @objc public static let directionPads: [String] = ["DPAD", "WASDPAD", "ARROWPAD"]
@objc public static let stickTouchPads: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD"] @objc public static let stickTouchPads: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD"]
@objc public static let nonVectorStickPads: [String] = ["LSPAD", "RSPAD"] @objc public static let nonVectorStickPads: [String] = ["LSPAD", "RSPAD"]
+76 -41
View File
@@ -129,7 +129,8 @@ import ObjectiveC.runtime
@objc public var initialCenter: CGPoint = .zero // location from persisted data @objc public var initialCenter: CGPoint = .zero // location from persisted data
@objc public var layoutChanges: [CGPoint] = [] @objc public var layoutChanges: [CGPoint] = []
@objc public var mouseButtonAction: MouseButtonAction = .hovering; @objc public var mouseButtonAction: MouseButtonAction = .hovering;
@objc public var mouseButtonActionDelay: TimeInterval = 0.005;
//autoTapTimer //autoTapTimer
@objc public var autoTapInterval: Int = 49; @objc public var autoTapInterval: Int = 49;
private var autoTapTimer: SafeTimer? private var autoTapTimer: SafeTimer?
@@ -279,7 +280,7 @@ import ObjectiveC.runtime
// for mousePad // for mousePad
private var touchBegan: Bool = false private var touchBegan: Bool = false
private var directionPadTouchBegan: Bool = false private var directionPadTouchBegan: Bool = false
private var firstTouchMoved: Bool = false @objc private(set) var firstTouchMoved: Bool = false
private var mousePointerMoved: Bool private var mousePointerMoved: Bool
private var twoTouchesDetected: Bool private var twoTouchesDetected: Bool
private var allSpawnedTouchesCount: Int = 0 private var allSpawnedTouchesCount: Int = 0
@@ -329,6 +330,7 @@ import ObjectiveC.runtime
@objc public var bulkMoveEnabled: Bool = false @objc public var bulkMoveEnabled: Bool = false
@objc public var sequenceSet: Set<Int16> = Set() @objc public var sequenceSet: Set<Int16> = Set()
@objc public var parentSequence: Int16 = -1 @objc public var parentSequence: Int16 = -1
@objc public var standardFoldingInterval: TimeInterval = 0.05
static weak var capturer: OnScreenWidgetView? static weak var capturer: OnScreenWidgetView?
@objc init(cmdString: String, buttonLabel: String, shape:String, profile:OSCProfile) { @objc init(cmdString: String, buttonLabel: String, shape:String, profile:OSCProfile) {
@@ -535,6 +537,10 @@ import ObjectiveC.runtime
self.hasTrackPoint = true self.hasTrackPoint = true
self.hasNonEditableLabel = (self.cmdString == "DISABLETOUCH" self.hasNonEditableLabel = (self.cmdString == "DISABLETOUCH"
|| self.cmdString == "GAMEPADOVERLAY") || self.cmdString == "GAMEPADOVERLAY")
self.mouseButtonActionDelay = self.cmdString.contains("ABSMOUSEPAD") ? 0.005 : 0
self.standardFoldingInterval = widgetType == .touchPad ? 0.05 : 0.15;
} }
// ====================================================================================================== // ======================================================================================================
@@ -2076,7 +2082,7 @@ import ObjectiveC.runtime
} }
if self.widgetType == WidgetTypeEnum.touchPad && CommandManager.mousePadWithButtonActions.contains(self.touchPadString) && allSpawnedTouchesCount == 1 && !twoTouchesDetected { if self.widgetType == WidgetTypeEnum.touchPad && CommandManager.mousePadWithButtonActions.contains(self.touchPadString) && allSpawnedTouchesCount == 1 && !twoTouchesDetected {
self.handleMousePadButtonActionDown() self.handleMousePadButtonActionDown(touch: touch)
} }
} }
@@ -2433,6 +2439,10 @@ import ObjectiveC.runtime
} }
self.updateTouchLocation(touch: touch) self.updateTouchLocation(touch: touch)
break break
case "ABSMOUSEPAD":
let touchLocation = touch.location(in: self.superview)
LiSendMousePositionEvent(Int16(touchLocation.x), Int16(touchLocation.y), Int16(self.superViewWidth), Int16(self.superViewHeight))
break
case "LSWHEEL", "RSWHEEL": case "LSWHEEL", "RSWHEEL":
self.handleStickWheelMove(touch: touch) self.handleStickWheelMove(touch: touch)
case "LSPAD": case "LSPAD":
@@ -2507,47 +2517,72 @@ import ObjectiveC.runtime
} }
} }
private func handleMousePadButtonActionUp(){ private func handleMousePadButtonActionUp(touch:UITouch?=nil){
switch self.mouseButtonAction{ if let touch = touch {
case .leftButtonDown: let touchLocation = touch.location(in: self.superview)
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_RELEASE), BUTTON_LEFT) switch touchPadString {
case .middleButtonDown: case "ABSMOUSEPAD":
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_RELEASE), BUTTON_MIDDLE) LiSendMousePositionEvent(Int16(touchLocation.x), Int16(touchLocation.y), Int16(self.superViewWidth), Int16(self.superViewHeight))
case .rightButtonDown: default:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_RELEASE), BUTTON_RIGHT) break
case .hovering: }
if !self.mousePointerMoved && !self.quickDoubleTapDetected {self.sendLongMouseLeftButtonClickEvent()} // deal with single tap(click) }
if self.quickDoubleTapDetected { //deal with quick double tap
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_RELEASE), BUTTON_LEFT) //must release the button anyway, because the button is likely being held down since the long click turned into a dragging event. DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + self.mouseButtonActionDelay) {
if !self.mousePointerMoved {self.sendShortMouseLeftButtonClickEvent()} switch self.mouseButtonAction{
self.quickDoubleTapDetected = false case .leftButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_RELEASE), BUTTON_LEFT)
case .middleButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_RELEASE), BUTTON_MIDDLE)
case .rightButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_RELEASE), BUTTON_RIGHT)
case .hovering:
if !self.mousePointerMoved && !self.quickDoubleTapDetected {self.sendLongMouseLeftButtonClickEvent()} // deal with single tap(click)
if self.quickDoubleTapDetected { //deal with quick double tap
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_RELEASE), BUTTON_LEFT) //must release the button anyway, because the button is likely being held down since the long click turned into a dragging event.
if !self.mousePointerMoved {self.sendShortMouseLeftButtonClickEvent()}
self.quickDoubleTapDetected = false
}
self.mousePointerMoved = false // reset this flag
case .noClick:
// quickDoubleTapDetected = false
break
default:
break
} }
self.mousePointerMoved = false // reset this flag
case .noClick:
// quickDoubleTapDetected = false
break
default:
break
} }
} }
private func handleMousePadButtonActionDown(){
switch mouseButtonAction{ private func handleMousePadButtonActionDown(touch:UITouch?=nil){
case .leftButtonDown: DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + self.mouseButtonActionDelay) {
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_LEFT) switch self.mouseButtonAction{
case .middleButtonDown: case .leftButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_MIDDLE) LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_LEFT)
case .rightButtonDown: case .middleButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_RIGHT) LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_MIDDLE)
case .noClick: case .rightButtonDown:
if quickDoubleTapDetected && !self.comboButtonStrings.isEmpty { LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_RIGHT)
self.showl3r3Indicator() case .noClick:
self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings) if self.quickDoubleTapDetected && !self.comboButtonStrings.isEmpty {
self.showl3r3Indicator()
self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings)
}
case .hovering:
break
default:
break
} }
case .hovering: }
break
default: if let touch = touch {
break let touchLocation = touch.location(in: self.superview)
switch touchPadString {
case "ABSMOUSEPAD":
LiSendMousePositionEvent(Int16(touchLocation.x), Int16(touchLocation.y), Int16(self.superViewWidth), Int16(self.superViewHeight))
default:
break
}
} }
} }
@@ -3460,7 +3495,7 @@ import ObjectiveC.runtime
|| abs(widget.backgroundAlpha) < 0.1){ || abs(widget.backgroundAlpha) < 0.1){
widget.highlightBorder(highlighted: true, color: UIColor.systemBlue.cgColor) widget.highlightBorder(highlighted: true, color: UIColor.systemBlue.cgColor)
} }
let duration = OnScreenWidgetView.enableFolderAnimation ? (folder.buttonMode == .slideAndHold ? 0.05 : 0.15) : 0 let duration = OnScreenWidgetView.enableFolderAnimation ? (folder.buttonMode == .slideAndHold ? 0.05 : widget.standardFoldingInterval) : 0
UIView.animate(withDuration: duration, animations: { UIView.animate(withDuration: duration, animations: {
widget.center = folder.center widget.center = folder.center
},completion: { finished in },completion: { finished in
@@ -3487,7 +3522,7 @@ import ObjectiveC.runtime
|| abs(widget.backgroundAlpha) < 0.1){ || abs(widget.backgroundAlpha) < 0.1){
widget.highlightBorder(highlighted: true, color: UIColor.systemBlue.cgColor) widget.highlightBorder(highlighted: true, color: UIColor.systemBlue.cgColor)
} }
UIView.animate(withDuration: OnScreenWidgetView.enableFolderAnimation ? (folder.buttonMode == .slideAndHold ? 0.05 : 0.15) : 0, animations: { UIView.animate(withDuration: OnScreenWidgetView.enableFolderAnimation ? (folder.buttonMode == .slideAndHold ? 0.05 : widget.standardFoldingInterval) : 0, animations: {
widget.center = widget.storedCenter widget.center = widget.storedCenter
},completion: { finished in },completion: { finished in
widget.capturedTouches.removeAllObjects() widget.capturedTouches.removeAllObjects()
+1 -1
View File
@@ -11,7 +11,7 @@ import UIKit
@objc class UITouchUtil: NSObject { @objc class UITouchUtil: NSObject {
@objc public static func touches(in view: UIView, from event: UIEvent?) -> Set<UITouch> { @objc public static func touches(in view: UIView, from event: UIEvent?) -> Set<UITouch> {
guard (event?.allTouches) != nil else { return [] } guard (event?.allTouches) != nil else { return [] }
return event?.allTouches?.filter({ $0.view == view && $0.type == .direct}) ?? [] return event?.allTouches?.filter({ $0.view == view && ($0.type == .direct || $0.type == .pencil)}) ?? []
} }
@objc public static func distance(between touch1: UITouch?, and touch2: UITouch?, in view: UIView?) -> CGFloat { @objc public static func distance(between touch1: UITouch?, and touch2: UITouch?, in view: UIView?) -> CGFloat {
+26 -3
View File
@@ -1526,6 +1526,29 @@
} }
} }
}, },
"Absolute MousePad" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "鼠标触控板(绝对坐标)"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "滑鼠觸控板(絕對座標)"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "滑鼠觸控板(絕對座標)"
}
}
}
},
"Absolute touch drag" : { "Absolute touch drag" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@@ -1600,19 +1623,19 @@
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "needs_review", "state" : "translated",
"value" : "添加主机" "value" : "添加主机"
} }
}, },
"zh-Hant" : { "zh-Hant" : {
"stringUnit" : { "stringUnit" : {
"state" : "needs_review", "state" : "translated",
"value" : "新增主機" "value" : "新增主機"
} }
}, },
"zh-HK" : { "zh-HK" : {
"stringUnit" : { "stringUnit" : {
"state" : "needs_review", "state" : "translated",
"value" : "新增主機" "value" : "新增主機"
} }
} }
@@ -2305,7 +2305,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
view.sendSubviewToBack(selectedWidgetView) view.sendSubviewToBack(selectedWidgetView)
} }
} }
if !isToolbarHidden, let selectedWidgetView, layerIsOverlappingWithTrashcanButton(selectedWidgetView.layer) { if !isToolbarHidden, let selectedWidgetView, layerIsOverlappingWithTrashcanButton(selectedWidgetView.layer), selectedWidgetView.firstTouchMoved {
OnScreenWidgetView.setFree(widget: selectedWidgetView) OnScreenWidgetView.setFree(widget: selectedWidgetView)
OnScreenWidgetView.removeWidgetFromMappings(key: selectedWidgetView.sequence) OnScreenWidgetView.removeWidgetFromMappings(key: selectedWidgetView.sequence)
selectedWidgetView.removeFromSuperview() selectedWidgetView.removeFromSuperview()
+25 -14
View File
@@ -638,7 +638,7 @@ struct VirtualKeyboardView: View {
} }
private func isPadLikeControl(_ key: Key) -> Bool { private func isPadLikeControl(_ key: Key) -> Bool {
let padCommands: Set<String> = ["WHEEL", "MOUSEPAD", "TRACKBALL"] let padCommands: Set<String> = ["WHEEL", "MOUSEPAD", "TRACKBALL", "ABSMOUSEPAD"]
if let cmd = key.cmdString(for: keyboardType) { if let cmd = key.cmdString(for: keyboardType) {
return padCommands.contains(cmd) return padCommands.contains(cmd)
} }
@@ -1080,18 +1080,28 @@ struct VirtualKeyboardView: View {
), ),
height: keyHeight * 2 + spacing height: keyHeight * 2 + spacing
) )
VStack(spacing: spacing) {
keyboardKeyView( keyboardKeyView(
Key( Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Trackball"), label: SwiftLocalizationHelper.localizedString(forKey: "Absolute MousePad"),
width: unit * 2 + spacing, width: unit * 2 + spacing,
winCmdString: "TRACKBALL", winCmdString: "ABSMOUSEPAD",
macCmdString: "TRACKBALL", macCmdString: "ABSMOUSEPAD",
winKeyCode: 0xFF, winKeyCode: 0xFF,
macKeyCode: 0xFF macKeyCode: 0xFF
), ),
height: keyHeight * 2 + spacing )
) keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Trackball"),
width: unit * 2 + spacing,
winCmdString: "TRACKBALL",
macCmdString: "TRACKBALL",
winKeyCode: 0xFF,
macKeyCode: 0xFF
),
)
}
} }
} }
} }
@@ -1274,12 +1284,13 @@ struct VirtualKeyboardView: View {
Key(label: SwiftLocalizationHelper.localizedString(forKey: "Wheel\nDown"), width: unit, winCmdString: "WHEELUP", macCmdString: "WHEELUP", winKeyCode: 0xFF, macKeyCode: 0xFF), Key(label: SwiftLocalizationHelper.localizedString(forKey: "Wheel\nDown"), width: unit, winCmdString: "WHEELUP", macCmdString: "WHEELUP", winKeyCode: 0xFF, macKeyCode: 0xFF),
Key(label: SwiftLocalizationHelper.localizedString(forKey: "WheelPad"), width: unit * 2 + spacing, winCmdString: "WHEEL", macCmdString: "WHEEL", winKeyCode: 0xFF, macKeyCode: 0xFF), Key(label: SwiftLocalizationHelper.localizedString(forKey: "WheelPad"), width: unit * 2 + spacing, winCmdString: "WHEEL", macCmdString: "WHEEL", winKeyCode: 0xFF, macKeyCode: 0xFF),
Key(label: SwiftLocalizationHelper.localizedString(forKey: "MousePad"), width: unit * 2 + spacing, winCmdString: "MOUSEPAD", macCmdString: "MOUSEPAD", winKeyCode: 0xFF, macKeyCode: 0xFF), Key(label: SwiftLocalizationHelper.localizedString(forKey: "MousePad"), width: unit * 2 + spacing, winCmdString: "MOUSEPAD", macCmdString: "MOUSEPAD", winKeyCode: 0xFF, macKeyCode: 0xFF),
Key(label: SwiftLocalizationHelper.localizedString(forKey: "Absolute MousePad"), width: unit * 2 + spacing, winCmdString: "ABSMOUSEPAD", macCmdString: "ABSMOUSEPAD", winKeyCode: 0xFF, macKeyCode: 0xFF),
Key(label: SwiftLocalizationHelper.localizedString(forKey: "Trackball"), width: unit * 2 + spacing, winCmdString: "TRACKBALL", macCmdString: "TRACKBALL", winKeyCode: 0xFF, macKeyCode: 0xFF) Key(label: SwiftLocalizationHelper.localizedString(forKey: "Trackball"), width: unit * 2 + spacing, winCmdString: "TRACKBALL", macCmdString: "TRACKBALL", winKeyCode: 0xFF, macKeyCode: 0xFF)
] ]
if mode == .shortcutPicker { if mode == .shortcutPicker {
keys.removeAll { key in keys.removeAll { key in
let command = key.cmdString(for: keyboardType) ?? "" let command = key.cmdString(for: keyboardType) ?? ""
return command == "WHEEL" || command == "MOUSEPAD" || command == "TRACKBALL" return command == "WHEEL" || command == "MOUSEPAD" || command == "TRACKBALL" || command == "ABSMOUSEPAD"
} }
} }
return keys return keys
+1
View File
@@ -1257,6 +1257,7 @@ struct WidgetPickerView: View {
"ARROWPAD", "ARROWPAD",
"WHEEL", "WHEEL",
"MOUSEPAD", "MOUSEPAD",
"ABSMOUSEPAD",
"TRACKBALL" "TRACKBALL"
] ]
} }