new touchpad widget: absolute mousepad

This commit is contained in:
True Zhuanjia
2026-04-28 14:25:25 +08:00
parent cb235d9255
commit 20aba200b3
7 changed files with 133 additions and 63 deletions
+3 -3
View File
@@ -118,9 +118,9 @@ import UIKit
"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 mousePadWithButtonActions: [String] = ["MOUSEPAD", "ABSMOUSE"]
@objc public static let mousePads: [String] = ["MOUSEPAD", "ABSMOUSE", "TRACKBALL"]
@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", "ABSMOUSEPAD"]
@objc public static let mousePads: [String] = ["MOUSEPAD", "ABSMOUSE", "TRACKBALL", "ABSMOUSEPAD"]
@objc public static let directionPads: [String] = ["DPAD", "WASDPAD", "ARROWPAD"]
@objc public static let stickTouchPads: [String] = ["LSVPAD", "RSVPAD", "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 layoutChanges: [CGPoint] = []
@objc public var mouseButtonAction: MouseButtonAction = .hovering;
@objc public var mouseButtonActionDelay: TimeInterval = 0.005;
//autoTapTimer
@objc public var autoTapInterval: Int = 49;
private var autoTapTimer: SafeTimer?
@@ -279,7 +280,7 @@ import ObjectiveC.runtime
// for mousePad
private var touchBegan: 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 twoTouchesDetected: Bool
private var allSpawnedTouchesCount: Int = 0
@@ -329,6 +330,7 @@ import ObjectiveC.runtime
@objc public var bulkMoveEnabled: Bool = false
@objc public var sequenceSet: Set<Int16> = Set()
@objc public var parentSequence: Int16 = -1
@objc public var standardFoldingInterval: TimeInterval = 0.05
static weak var capturer: OnScreenWidgetView?
@objc init(cmdString: String, buttonLabel: String, shape:String, profile:OSCProfile) {
@@ -535,6 +537,10 @@ import ObjectiveC.runtime
self.hasTrackPoint = true
self.hasNonEditableLabel = (self.cmdString == "DISABLETOUCH"
|| 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 {
self.handleMousePadButtonActionDown()
self.handleMousePadButtonActionDown(touch: touch)
}
}
@@ -2433,6 +2439,10 @@ import ObjectiveC.runtime
}
self.updateTouchLocation(touch: touch)
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":
self.handleStickWheelMove(touch: touch)
case "LSPAD":
@@ -2507,47 +2517,72 @@ import ObjectiveC.runtime
}
}
private func handleMousePadButtonActionUp(){
switch self.mouseButtonAction{
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
private func handleMousePadButtonActionUp(touch:UITouch?=nil){
if let touch = touch {
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
}
}
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + self.mouseButtonActionDelay) {
switch self.mouseButtonAction{
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{
case .leftButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_LEFT)
case .middleButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_MIDDLE)
case .rightButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_RIGHT)
case .noClick:
if quickDoubleTapDetected && !self.comboButtonStrings.isEmpty {
self.showl3r3Indicator()
self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings)
private func handleMousePadButtonActionDown(touch:UITouch?=nil){
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + self.mouseButtonActionDelay) {
switch self.mouseButtonAction{
case .leftButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_LEFT)
case .middleButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_MIDDLE)
case .rightButtonDown:
LiSendMouseButtonEvent(CChar(BUTTON_ACTION_PRESS), BUTTON_RIGHT)
case .noClick:
if self.quickDoubleTapDetected && !self.comboButtonStrings.isEmpty {
self.showl3r3Indicator()
self.sendComboButtonsDownEvent(comboStrings: self.comboButtonStrings)
}
case .hovering:
break
default:
break
}
case .hovering:
break
default:
break
}
if let touch = touch {
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){
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: {
widget.center = folder.center
},completion: { finished in
@@ -3487,7 +3522,7 @@ import ObjectiveC.runtime
|| abs(widget.backgroundAlpha) < 0.1){
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
},completion: { finished in
widget.capturedTouches.removeAllObjects()
+1 -1
View File
@@ -11,7 +11,7 @@ import UIKit
@objc class UITouchUtil: NSObject {
@objc public static func touches(in view: UIView, from event: UIEvent?) -> Set<UITouch> {
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 {
+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" : {
"extractionState" : "manual",
"localizations" : {
@@ -1600,19 +1623,19 @@
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "needs_review",
"state" : "translated",
"value" : "添加主机"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "needs_review",
"state" : "translated",
"value" : "新增主機"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "needs_review",
"state" : "translated",
"value" : "新增主機"
}
}
@@ -2305,7 +2305,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
view.sendSubviewToBack(selectedWidgetView)
}
}
if !isToolbarHidden, let selectedWidgetView, layerIsOverlappingWithTrashcanButton(selectedWidgetView.layer) {
if !isToolbarHidden, let selectedWidgetView, layerIsOverlappingWithTrashcanButton(selectedWidgetView.layer), selectedWidgetView.firstTouchMoved {
OnScreenWidgetView.setFree(widget: selectedWidgetView)
OnScreenWidgetView.removeWidgetFromMappings(key: selectedWidgetView.sequence)
selectedWidgetView.removeFromSuperview()
+25 -14
View File
@@ -638,7 +638,7 @@ struct VirtualKeyboardView: View {
}
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) {
return padCommands.contains(cmd)
}
@@ -1080,18 +1080,28 @@ struct VirtualKeyboardView: View {
),
height: keyHeight * 2 + spacing
)
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Trackball"),
width: unit * 2 + spacing,
winCmdString: "TRACKBALL",
macCmdString: "TRACKBALL",
winKeyCode: 0xFF,
macKeyCode: 0xFF
),
height: keyHeight * 2 + spacing
)
VStack(spacing: spacing) {
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Absolute MousePad"),
width: unit * 2 + spacing,
winCmdString: "ABSMOUSEPAD",
macCmdString: "ABSMOUSEPAD",
winKeyCode: 0xFF,
macKeyCode: 0xFF
),
)
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: "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: "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)
]
if mode == .shortcutPicker {
keys.removeAll { key in
let command = key.cmdString(for: keyboardType) ?? ""
return command == "WHEEL" || command == "MOUSEPAD" || command == "TRACKBALL"
return command == "WHEEL" || command == "MOUSEPAD" || command == "TRACKBALL" || command == "ABSMOUSEPAD"
}
}
return keys
+1
View File
@@ -1257,6 +1257,7 @@ struct WidgetPickerView: View {
"ARROWPAD",
"WHEEL",
"MOUSEPAD",
"ABSMOUSEPAD",
"TRACKBALL"
]
}