From 1e610697918a9fa69f26b53ab66f9312c0921559 Mon Sep 17 00:00:00 2001 From: Cidy20 Date: Tue, 31 Mar 2026 14:27:56 +0800 Subject: [PATCH] feat(mouse): rename ScrollWheel to ScrollWheelZoom and add mutually exclusive ScrollWheelMapping support --- PlayTools/Controls/ActionDispatcher.swift | 12 +++++++++ PlayTools/Controls/Frontend/ControlMode.swift | 3 ++- .../CameraControlMouseEventAdapter.swift | 25 ++++++++++++++--- .../Instances/EditorMouseEventAdapter.swift | 15 ++++++++++- .../TouchscreenMouseEventAdapter.swift | 27 ++++++++++++++++--- PlayTools/Keymap/KeyCodeNames.swift | 2 ++ PlayTools/PlaySettings.swift | 6 +++-- 7 files changed, 80 insertions(+), 10 deletions(-) diff --git a/PlayTools/Controls/ActionDispatcher.swift b/PlayTools/Controls/ActionDispatcher.swift index 000cf1a1..e7bc32a5 100644 --- a/PlayTools/Controls/ActionDispatcher.swift +++ b/PlayTools/Controls/ActionDispatcher.swift @@ -202,6 +202,18 @@ public class ActionDispatcher { return mapped } + static public func dispatchClick(key: String) -> Bool { + guard buttonHandlers[key] != nil else { + return false + } + _ = dispatch(key: key, pressed: true) + // 延迟 30ms 自动释放,模拟一次物理点击 + PlayInput.touchQueue.asyncAfter(deadline: .now() + 0.03, qos: .userInteractive, execute: { + _ = dispatch(key: key, pressed: false) + }) + return true + } + static public func dispatch(key: String, valueX: CGFloat, valueY: CGFloat) -> Bool { for priority in 0.. Bool { - _ = ActionDispatcher.dispatch(key: KeyCodeNames.scrollWheelScale, valueX: deltaX, valueY: deltaY) - // I dont know why but this is the logic before the refactor. - // Might be a mistake but keeping it for now + // Priority 1: Keymapping. If enabled and triggered, consume the event. + if PlaySettings.shared.enableScrollWheelMapping { + let threshold: CGFloat = 0.5 + var handled = false + if deltaY > threshold { + handled = ActionDispatcher.dispatchClick(key: "ScrU") + } else if deltaY < -threshold { + handled = ActionDispatcher.dispatchClick(key: "ScrD") + } + + // If mapping was triggered, return true to consume the event + if handled { + return true + } + } + + // Priority 2: Zoom/Scale logic. + if PlaySettings.shared.enableScrollWheelZoom { + _ = ActionDispatcher.dispatch(key: KeyCodeNames.scrollWheelScale, valueX: deltaX, valueY: deltaY) + return true + } + return true } diff --git a/PlayTools/Controls/Frontend/EventAdapter/Mouse/Instances/EditorMouseEventAdapter.swift b/PlayTools/Controls/Frontend/EventAdapter/Mouse/Instances/EditorMouseEventAdapter.swift index 77295cf9..db8cb30b 100644 --- a/PlayTools/Controls/Frontend/EventAdapter/Mouse/Instances/EditorMouseEventAdapter.swift +++ b/PlayTools/Controls/Frontend/EventAdapter/Mouse/Instances/EditorMouseEventAdapter.swift @@ -36,8 +36,21 @@ public class EditorMouseEventAdapter: MouseEventAdapter { return true } + private static var lastScrollTime: TimeInterval = 0 + public func handleScrollWheel(deltaX: CGFloat, deltaY: CGFloat) -> Bool { - false + let currentTime = ProcessInfo.processInfo.systemUptime + // 阈值判断:deltaY 绝对值需大于 0.2 以防极小干扰,且 0.3s 内不重复触发 + if abs(deltaY) > 0.2 && (currentTime - EditorMouseEventAdapter.lastScrollTime) > 0.3 { + EditorMouseEventAdapter.lastScrollTime = currentTime + let keyCode = deltaY > 0 ? -100 : -101 + DispatchQueue.main.async(qos: .userInteractive, execute: { + EditorController.shared.setKey(keyCode) + Toucher.writeLog(logMessage: "mouse wheel editor set: \(keyCode)") + }) + return true + } + return false } public func handleMove(deltaX: CGFloat, deltaY: CGFloat) -> Bool { diff --git a/PlayTools/Controls/Frontend/EventAdapter/Mouse/Instances/TouchscreenMouseEventAdapter.swift b/PlayTools/Controls/Frontend/EventAdapter/Mouse/Instances/TouchscreenMouseEventAdapter.swift index 65941fb7..25804db2 100644 --- a/PlayTools/Controls/Frontend/EventAdapter/Mouse/Instances/TouchscreenMouseEventAdapter.swift +++ b/PlayTools/Controls/Frontend/EventAdapter/Mouse/Instances/TouchscreenMouseEventAdapter.swift @@ -50,9 +50,30 @@ public class TouchscreenMouseEventAdapter: MouseEventAdapter { } public func handleScrollWheel(deltaX: CGFloat, deltaY: CGFloat) -> Bool { - _ = ActionDispatcher.dispatch(key: KeyCodeNames.scrollWheelDrag, valueX: deltaX, valueY: deltaY) - // I dont know why but this is the logic before the refactor. - // Might be a mistake but keeping it for now + // Priority 1: Keymapping. If enabled and triggered, consume the event. + if PlaySettings.shared.enableScrollWheelMapping { + let threshold: CGFloat = 0.5 + var handled = false + if deltaY > threshold { + handled = ActionDispatcher.dispatchClick(key: "ScrU") + } else if deltaY < -threshold { + handled = ActionDispatcher.dispatchClick(key: "ScrD") + } + + // If mapping was triggered, return true to consume the event + // and prevent it from reaching any other logic (like zoom). + if handled { + return true + } + } + + // Priority 2: Zoom logic. + if PlaySettings.shared.enableScrollWheelZoom { + _ = ActionDispatcher.dispatch(key: KeyCodeNames.scrollWheelDrag, valueX: deltaX, valueY: deltaY) + // Generally return true after processing to keep behavior consistent + return true + } + return false } diff --git a/PlayTools/Keymap/KeyCodeNames.swift b/PlayTools/Keymap/KeyCodeNames.swift index e11cfc11..693b7891 100644 --- a/PlayTools/Keymap/KeyCodeNames.swift +++ b/PlayTools/Keymap/KeyCodeNames.swift @@ -30,6 +30,8 @@ class KeyCodeNames { -1: "LMB", -2: "RMB", -3: "MMB", + -100: "ScrU", + -101: "ScrD", 41: "Esc", 44: "Spc", 225: "Lshft", diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index c20d6d32..f641fb9f 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -82,7 +82,8 @@ let settings = PlaySettings.shared @objc lazy var noKMOnInput = settingsData.noKMOnInput - @objc lazy var enableScrollWheel = settingsData.enableScrollWheel + @objc lazy var enableScrollWheelZoom = settingsData.enableScrollWheelZoom + @objc lazy var enableScrollWheelMapping = settingsData.enableScrollWheelMapping @objc lazy var hideTitleBar = settingsData.hideTitleBar @@ -118,7 +119,8 @@ struct AppSettingsData: Codable { var windowFixMethod = 0 var rootWorkDir = true var noKMOnInput = false - var enableScrollWheel = true + var enableScrollWheelZoom = true // Original zoom logic + var enableScrollWheelMapping = false // New keymapping logic var hideTitleBar = false var floatingWindow = false var checkMicPermissionSync = false