n 次按键的解决方案
聚会很晚,但几乎没有关于这方面的信息,所以我想我真的应该把它放在这里,因为这是唯一的搜索结果之一。
我的解决方案比其他一些解决方案更优雅(在我看来);肯定有一些可以改进的地方,但我对 Lua 或 Hammerspoon 还不够熟悉,无法修复它们。
它应该可以根据需要为尽可能多的连续按键分配快捷方式。
阅读代码注释以了解其工作原理。我试图尽可能详细,以使其对那些不太了解编码和不太熟悉 Lua 或 Hammerspoon 的人(比如我)更适合初学者。
require("hs.timer") -- Load timer module, used for timing
keyDownCount = 0 -- Keypress counter, used later in the program to store the number of times the key has been pressed
keyMultipressGapTime = 0.3 -- Max time between consecutive keypresses, used to determine when the user has stopped pressing the key
keyMaxPressCount = 3 -- Max number of key presses
testKeyCode = 18 -- Key code to bind shortcut to (in this case the 1 key)
-- READ CheckKeyDownCount FUNCTION CODE (BELOW) FIRST
-- Function to press a key with code
-- This isn't completely intuitive so I'm including it
-- Im sure there's a better way of doing this but this is what I figured out
function PressKey(keyCode)
keyDown = hs.eventtap.event.newKeyEvent(keyCode, true) -- Create new keydown event using the keycode passed in the keycode argument
keyDown:setProperty(hs.eventtap.event.properties.eventSourceUserData, 1) -- Sets user data byte of keydown event to 1, used later to prevent keydown event handler from self triggering
keyDown:post() -- Fire keydown event
hs.eventtap.event.newKeyEvent(keyCode, false):post() -- Create and fire keyup event using the keycode passed in the keycode argument
end
-- READ EVENT HANDLER CODE (BELOW) FIRST
-- Function to check the number of times the key was pressed and act accordingly
-- Pretty self explanatory
function CheckKeyDownCount()
CheckKeyDownCountTimer:stop() -- Stops keydown timer so it doesn't repeat
-- There may be a better way of doing this but I can't find a way to creating and restarting a non repeating timer without creating a whole new timer object every time
if keyDownCount == 1 then -- Perform action based on number of keypresses
hs.alert("Pressed once")
PressKey(testKeyCode)
elseif keyDownCount == 2 then
hs.alert("Pressed twice")
elseif keyDownCount == 3 then
hs.alert("Pressed thrice")
end
keyDownCount = 0 -- Reset keypress counter
end
CheckKeyDownCountTimer = hs.timer.new(keyMultipressGapTime, CheckKeyDownCount) -- Creates timer for determining when the user has stopped pressing the key
-- Time interval is set to the max time between consecutive keypresses
-- Runs the CheckKeyDownCount function at end of time interval
-- IMPORTANT: Time interval automatically resets when timer is stopped and started
-- Creates keydown event handler
-- FOR BEGINNERS: An event handler is a routine that runs when triggered by an event (kind of like an interrupt if you know what that is), normally they call a function, like below
-- FOR BEGINNERS CONTINUED: The timer above is also an event handler of sorts, with the event being the end of the time interval, event handlers are very useful because they allow asynchronous code execution
-- FOR BEGINNERS CONTINUED: In this case asynchronous code execution means that the program will continue executing until an event needs to be handled, the program will then stop where it is, handel the event, and then jump back to where it left off
multipressBtnShortcuts = hs.eventtap.new({hs.eventtap.event.types.keyDown}, function(event)
-- FOR BEGINNERS: "function(event)" creates anonymous function containing the below code and passes it the keydown event as an object called "event" (Just makes the code neater, you could use a separate function if you want)
-- FOR BEGINNERS CONTINUED: An anonymous function is just a function without an identifier (name), instead they're objects and often behave kinda like variables (look this up, it's kinda hard to explain and not relevant here)
-- RANDOM NOTE: Also turns out all functions in lua are anonymous which is pretty interesting, the interpreter just converts the structure "function foo(x) return 2*x end" into "foo = function (x) return 2*x end"
if event:getKeyCode() == testKeyCode and event:getProperty(hs.eventtap.event.properties.eventSourceUserData) == 0 then -- Check if keycode is the shortcut keycode and check if the user data byte is set to 0 (default)
-- The user data byte check is to prevent the event handler from triggering itself (SEE PressKey FUNCTION ABOVE)
-- I'm sure there's a better way to do this but I cant find it
event:setType(hs.eventtap.event.types.nullEvent) -- Null the keypress event
-- Overrides the keypress, remove if you don't want the original keypresses to be overridden
-- I'm sure there's a better way to do this but I cant find it
keyDownCount = keyDownCount + 1 -- Add one to keypress counter
if CheckKeyDownCountTimer:running() then -- If the max key press gap timer is running stop it (NOTE: Stopping and starting it also resets it)
CheckKeyDownCountTimer:stop()
end
if keyDownCount < keyMaxPressCount then -- If keypress counter is less then the max number of keypresses restart the max key press gap timer (NOTE: Stopping and starting it also resets it)
CheckKeyDownCountTimer:start()
else -- Alternativly, if the keypress counter is greater than or equal to the max number of keypresses run the CheckKeyDownCount function
CheckKeyDownCount()
end
end
return false -- Ends the anonymous function by returning false, not sure if this is really necessary but it's what other people seem to do
end)
multipressBtnShortcuts:start() -- Starts the keydown event handler