-
Notifications
You must be signed in to change notification settings - Fork 687
Expand file tree
/
Copy pathhotkey.lua
More file actions
625 lines (586 loc) · 23.8 KB
/
hotkey.lua
File metadata and controls
625 lines (586 loc) · 23.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
--- === hs.hotkey ===
---
--- Create and manage global keyboard shortcuts
local hotkey = require "hs.libhotkey"
local keycodes = require "hs.keycodes"
local alert = require'hs.alert'
local log = require'hs.logger'.new('hotkey','info')
--- hs.hotkey.setLogLevel(loglevel)
--- Method
--- Sets the log level of the hotkey logger instance
---
--- Parameters:
--- * loglevel - can be 'nothing', 'error', 'warning', 'info', 'debug', or 'verbose'; or a corresponding number between 0 and 5
---
--- Returns:
--- * None
hotkey.setLogLevel=log.setLogLevel
--- hs.hotkey.getLogLevel() -> number
--- Method
--- Gets the log level of the hotkey logger instance
---
--- Parameters:
--- * None
---
--- Returns:
--- * The log level of the hotkey logger as a number between 0 and 5
hotkey.getLogLevel=log.getLogLevel
local tonumber,pairs,ipairs,type,tremove,tinsert,tconcat,tsort = tonumber,pairs,ipairs,type,table.remove,table.insert,table.concat,table.sort
local supper,slower,sfind=string.upper,string.lower,string.find
--local function error(err,lvl) return hs.showError(err,(lvl or 1)+1,true) end -- this should go away, #477
local function getKeycode(s)
local n
if type(s)=='number' then n=s
elseif type(s)~='string' then error('key must be a string or a number',3)
elseif (s:sub(1, 1) == '#') then n=tonumber(s:sub(2))
else n=keycodes.map[slower(s)] end
if not n then error('Invalid key: '..s..' - this may mean that the key requested does not exist in your keymap (particularly if you switch keyboard layouts frequently)',3) end
return n
end
-- all enabled hotkeys go here; every key is a keyboard combination (string), associated value is a stack (list) of lightweight
-- hotkey objects, created in new(); the currently enabled hotkey sits at the top of the stack
local hotkeys = {}
--- hs.hotkey.alertDuration
--- Variable
--- Duration of the alert shown when a hotkey created with a `message` parameter is triggered, in seconds. Default is 1.
---
--- Usage:
--- hs.hotkey.alertDuration = 2.5 -- alert stays on screen a bit longer
--- hs.hotkey.alertDuration = 0 -- hotkey alerts are disabled
hotkey.alertDuration = 1
--- hs.hotkey:enable() -> hs.hotkey object | nil
--- Method
--- Enables a hotkey object
---
--- Parameters:
--- * None
---
--- Returns:
--- * The `hs.hotkey` object for method chaining or nil if the hotkey could not be enabled for some reason.
---
--- Notes:
--- * When you enable a hotkey that uses the same keyboard combination as another previously-enabled hotkey, the old
--- one will stop working as it's being "shadowed" by the new one. As soon as the new hotkey is disabled or deleted
--- the old one will trigger again.
local function enable(self,force,isModal)
-- force is there just in case it'll be needed down the line; not exposed as public API for now
-- isModal is only used to differentiate loglevel (modals can cause a lot of shadowing/unshadowing)
-- this ensures "nested shadowing" behaviour; i.e. can't re-enable a hotkey that is currently shadowed (unless force),
-- must wait for the current top dog to get disabled
if not force and self.enabled then log.v('Hotkey already enabled') return self end
local idx = self.idx
if not idx or not hotkeys[idx] then log.e('The hotkey was deleted, cannot enable it') return end
for i=#hotkeys[idx],1,-1 do
local hk=hotkeys[idx][i]
if hk==self then tremove(hotkeys[idx],i) -- this hotkey will go to the top of the stack
elseif hk.enabled then log.i('Disabled previous hotkey '..hk.msg) end --shadow previous hotkeys
hk._hk:disable() --objc
end
self.enabled = true
local returnVal = self._hk:enable() --objc
if returnVal ~= nil then
log[isModal and 'df' or 'f']('Enabled hotkey %s%s',self.msg,isModal and ' (in modal)' or '')
tinsert(hotkeys[idx],self) -- bring to the top of the stack
-- return returnVal
return self
else
self.enabled = false
return nil
end
end
--- hs.hotkey:disable() -> hs.hotkey object
--- Method
--- Disables a hotkey object
---
--- Parameters:
--- * None
---
--- Returns:
--- * The `hs.hotkey` object for method chaining
local function disable(self,isModal)
if not self.enabled then return self end
local idx = self.idx
if not idx or not hotkeys[idx] then log.w('The hotkey was deleted, cannot disable it') return end
self.enabled = nil
self._hk:disable() --objc
log[isModal and 'df' or 'f']('Disabled hotkey %s%s',self.msg,isModal and ' (in modal)' or '')
for i=#hotkeys[idx],1,-1 do --scan the stack top-to-bottom
if hotkeys[idx][i].enabled then
log.i('Re-enabled previous hotkey '..hotkeys[idx][i].msg)
hotkeys[idx][i]._hk:enable() --unshadow previous top dog and exit
break
end
end
return self
end
--- hs.hotkey:delete()
--- Method
--- Disables and deletes a hotkey object
---
--- Parameters:
--- * None
---
--- Returns:
--- * None
local function delete(self)
local idx=self.idx
if not idx or not hotkeys[idx] then log.w('The hotkey has already been deleted') return end --?
disable(self)
for i,hk in ipairs(hotkeys[idx]) do if hk==self then tremove(hotkeys[idx],i) break end end
log.i('Deleted hotkey '..self.msg)
for k in pairs(self) do self[k]=nil end --gc
end
local function getMods(mods)
local r={}
if not mods then return r end
if type(mods)=='table' then mods=tconcat(mods,'-') end
if type(mods)~='string' then error('mods must be a string or a table of strings',3) end
-- super simple substring search for mod names in a string
mods=slower(mods)
local function find(ps)
for _,s in ipairs(ps) do
if sfind(mods,s,1,true) then r[#r+1]=ps[#ps] return end
end
end
find{'cmd','command','⌘'} find{'ctrl','control','⌃'}
find{'alt','option','⌥'} find{'shift','⇧'}
return r --pass a list of unicode symbols to objc
end
local CONCAVE_DIAMOND='✧' -- used for HYPER
local function getIndex(modsID,keycode) -- key for hotkeys table
local mods = getMods(modsID)
mods = #mods>=4 and CONCAVE_DIAMOND or tconcat(mods)
local key=keycodes.map[keycode]
key=key and supper(key) or '[#'..keycode..']'
return mods..key
end
local function getFunc(f)
if f == nil then return nil end
if type(f) == 'function' then return f end
if type(f) == 'table' then
local m = getmetatable(f)
if m and m.__call and type(m.__call) == 'function' then
return function() m.__call(f) end
end
end
return nil
end
--- hs.hotkey.new(mods, key, [message,] pressedfn, releasedfn, repeatfn) -> hs.hotkey object
--- Constructor
--- Creates a new hotkey
---
--- Parameters:
--- * mods - A table or a string containing (as elements, or as substrings with any separator) the keyboard modifiers required,
--- which should be zero or more of the following:
--- * "cmd", "command" or "⌘"
--- * "ctrl", "control" or "⌃"
--- * "alt", "option" or "⌥"
--- * "shift" or "⇧"
--- * key - A string containing the name of a keyboard key (as found in [hs.keycodes.map](hs.keycodes.html#map) ), or a raw keycode number
--- * message - (optional) A string containing a message to be displayed via `hs.alert()` when the hotkey has been
--- triggered; if omitted, no alert will be shown
--- * pressedfn - A function that will be called when the hotkey has been pressed, or nil
--- * releasedfn - A function that will be called when the hotkey has been released, or nil
--- * repeatfn - A function that will be called when a pressed hotkey is repeating, or nil
---
--- Returns:
--- * A new `hs.hotkey` object or nil if the hotkey could not be enabled
---
--- Notes:
--- * You can create multiple `hs.hotkey` objects for the same keyboard combination, but only one can be active
--- at any given time - see `hs.hotkey:enable()`
--- * If `message` is the empty string `""`, the alert will just show the triggered keyboard combination
--- * If you don't want any alert, you must *actually* omit the `message` parameter; a `nil` in 3rd position
--- will be interpreted as a missing `pressedfn`
--- * You must pass at least one of `pressedfn`, `releasedfn` or `repeatfn`; to delete a hotkey, use `hs.hotkey:delete()`
function hotkey.new(mods, key, message, pressedfn, releasedfn, repeatfn)
local keycode = getKeycode(key)
mods = getMods(mods)
-- message can be omitted
if message==nil or getFunc(message) then
repeatfn=releasedfn releasedfn=pressedfn pressedfn=message message=nil -- shift down arguments
end
pressedfn = getFunc(pressedfn)
releasedfn = getFunc(releasedfn)
repeatfn = getFunc(repeatfn)
if not pressedfn and not releasedfn and not repeatfn then
error('At least one of pressedfn, releasedfn or repeatfn must be a function',2) end
if type(message)~='string' then message=nil end
local idx = getIndex(mods,keycode)
local msg=(message and #message>0) and idx..': '..message or idx
if message then
local actualfn=pressedfn or releasedfn or repeatfn -- which function will be wrapped to provide an alert (the first valid one)
local fnalert=function()alert(msg,hotkey.alertDuration or 0)actualfn()end -- wrapper
if pressedfn then pressedfn=fnalert -- substitute 'actualfn' with wrapper
elseif releasedfn then releasedfn=fnalert
elseif repeatfn then repeatfn=fnalert end
end
-- the lightweight hotkey object; _hk=objc userdata; then msg, idx, and the methods
local hk = {_hk=hotkey._new(mods, keycode, pressedfn, releasedfn, repeatfn),enable=enable,disable=disable,delete=delete,msg=msg,idx=idx}
log.v('Created hotkey for '..idx)
local h = hotkeys[idx] or {} -- create stack if this is the first hotkey for a given key combo
h[#h+1] = hk -- go on top of the stack
hotkeys[idx] = h
return hk
end
--- hs.hotkey.systemAssigned(mods, key) -> table | false
--- Function
--- Examine whether a potential hotkey is in use by the macOS system such as the Screen Capture, Universal Access, and Keyboard Navigation keys.
---
--- Parameters:
--- * mods - A table or a string containing (as elements, or as substrings with any separator) the keyboard modifiers required,
--- which should be zero or more of the following:
--- * "cmd", "command" or "⌘"
--- * "ctrl", "control" or "⌃"
--- * "alt", "option" or "⌥"
--- * "shift" or "⇧"
--- * key - A string containing the name of a keyboard key (as found in [hs.keycodes.map](hs.keycodes.html#map) ), or a raw keycode number
---
--- Returns:
--- * if the hotkey combination is in use by a system function, returns a table containing the following keys:
--- * keycode - the numeric keycode for the hotkey
--- * mods - a numeric representation of the modifier flags for the hotkey
--- * enabled - a boolean indicating whether or not the key is currently enabled
--- * if the hotkey combination is not in use by the operating system, returns the boolean value `false`
---
--- Notes:
--- * this is provided for informational purposes and does not provide a reliable test as to whether or not Hammerspoon can use the combination to create a custom hotkey -- some combinations which return a table can be over-ridden by Hammerspoon while others cannot. See also [hs.hotkey.assignable](#assignable).
local originalSystemAssigned = hotkey.systemAssigned
function hotkey.systemAssigned(mods, key)
local keycode = getKeycode(key)
mods = getMods(mods)
return originalSystemAssigned(mods, keycode)
end
--- hs.hotkey.assignable(mods, key) -> boolean
--- Function
--- Determines whether the hotkey combination can be assigned a callback through Hammerspoon.
---
--- Parameters:
--- * mods - A table or a string containing (as elements, or as substrings with any separator) the keyboard modifiers required,
--- which should be zero or more of the following:
--- * "cmd", "command" or "⌘"
--- * "ctrl", "control" or "⌃"
--- * "alt", "option" or "⌥"
--- * "shift" or "⇧"
--- * key - A string containing the name of a keyboard key (as found in [hs.keycodes.map](hs.keycodes.html#map) ), or a raw keycode number
---
--- Returns:
--- * a boolean value, true if the hotkey combination can be given an assignment by Hammerspoon or false if it cannot.
---
--- Notes:
--- * The most common reason a hotkey combination cannot be given an assignment by Hammerspoon is because it is in use by the Mac operating system -- see the Shortcuts tab of Keyboard in the System Preferences application or [hs.hotkey.systemAssigned](#systemAssigned).
function hotkey.assignable(mods, key)
local k = hotkey.new(mods, key, function() end)
local prevLevel = hs.luaSkinLog.level
-- suppress luaSkinLog error if binding fails
hs.luaSkinLog.level = 0
local status = k._hk:enable()
if status then k._hk:disable() end
k.enabled = false
hs.luaSkinLog.level = prevLevel
return status and true or false
end
--- hs.hotkey.disableAll(mods, key)
--- Function
--- Disables all previously set callbacks for a given keyboard combination
---
--- Parameters:
--- * mods - A table or a string containing (as elements, or as substrings with any separator) the keyboard modifiers required,
--- which should be zero or more of the following:
--- * "cmd", "command" or "⌘"
--- * "ctrl", "control" or "⌃"
--- * "alt", "option" or "⌥"
--- * "shift" or "⇧"
--- * key - A string containing the name of a keyboard key (as found in [hs.keycodes.map](hs.keycodes.html#map) ), or a raw keycode number
---
--- Returns:
--- * None
function hotkey.disableAll(mods,key)
local idx=getIndex(mods,getKeycode(key))
for _,hk in ipairs(hotkeys[idx] or {}) do hk:disable() end
end
--- hs.hotkey.deleteAll(mods, key)
--- Function
--- Deletes all previously set callbacks for a given keyboard combination
---
--- Parameters:
--- * mods - A table or a string containing (as elements, or as substrings with any separator) the keyboard modifiers required,
--- which should be zero or more of the following:
--- * "cmd", "command" or "⌘"
--- * "ctrl", "control" or "⌃"
--- * "alt", "option" or "⌥"
--- * "shift" or "⇧"
--- * key - A string containing the name of a keyboard key (as found in [hs.keycodes.map](hs.keycodes.html#map) ), or a raw keycode number
---
--- Returns:
--- * None
function hotkey.deleteAll(mods,key)
local idx=getIndex(mods,getKeycode(key))
local t=hotkeys[idx] or {}
for i=#t,1,-1 do t[i]:delete() end
hotkeys[idx]=nil
end
--- hs.hotkey.getHotkeys() -> table
--- Function
--- Returns a list of all currently active hotkeys
---
--- Parameters:
--- * None
---
--- Returns:
--- * A table containing the hotkeys that are active, i.e. enabled and not "shadowed", in the current context
--- (usually, the global hotkey context, but it could be a modal hotkey context). Every element in the list
--- is a table with two fields:
--- * idx - a string describing the keyboard combination for the hotkey
--- * msg - the hotkey message, if provided when the hotkey was created (prefixed with the keyboard combination)
--- hs.hotkey.showHotkeys(mods, key) -> hs.hotkey object
--- Function
--- Creates (and enables) a hotkey that shows all currently active hotkeys (i.e. enabled and not "shadowed" in the current context) while pressed
---
--- Parameters:
--- * mods - A table or a string containing (as elements, or as substrings with any separator) the keyboard modifiers required,
--- which should be zero or more of the following:
--- * "cmd", "command" or "⌘"
--- * "ctrl", "control" or "⌃"
--- * "alt", "option" or "⌥"
--- * "shift" or "⇧"
--- * key - A string containing the name of a keyboard key (as found in [hs.keycodes.map](hs.keycodes.html#map) ), or a raw keycode number
---
--- Returns:
--- * The new `hs.hotkey` object
local helpHotkey
function hotkey.getHotkeys()
local t={}
for _,hks in pairs(hotkeys) do
for i=#hks,1,-1 do
if hks[i].enabled and hks[i]~=helpHotkey then
t[#t+1] = hks[i]
break
end
end
end
tsort(t,function(a,b)if#a.idx==#b.idx then return a.idx<b.idx else return #a.idx<#b.idx end end)
if helpHotkey then tinsert(t,1,helpHotkey) end
return t
end
local function showHelp()
local t=hotkey.getHotkeys()
-- hs.alert(helpHotkey.msg,3600)
local s=''
for i=2,#t do s=s..t[i].msg..'\n' end
alert(s:sub(1,-2),3600)
end
function hotkey.showHotkeys(mods,key)
if helpHotkey then delete(helpHotkey) end
helpHotkey = hotkey.bind(mods,key,'Show active hotkeys',showHelp,alert.closeAll)
return helpHotkey
end
--- hs.hotkey.bind(mods, key, [message,] pressedfn, releasedfn, repeatfn) -> hs.hotkey object
--- Constructor
--- Creates a new hotkey and enables it immediately
---
--- Parameters:
--- * mods - A table or a string containing (as elements, or as substrings with any separator) the keyboard modifiers required,
--- which should be zero or more of the following:
--- * "cmd", "command" or "⌘"
--- * "ctrl", "control" or "⌃"
--- * "alt", "option" or "⌥"
--- * "shift" or "⇧"
--- * key - A string containing the name of a keyboard key (as found in [hs.keycodes.map](hs.keycodes.html#map) ), or a raw keycode number
--- * message - (optional) A string containing a message to be displayed via `hs.alert()` when the hotkey has been
--- triggered; if omitted, no alert will be shown
--- * pressedfn - A function that will be called when the hotkey has been pressed, or nil
--- * releasedfn - A function that will be called when the hotkey has been released, or nil
--- * repeatfn - A function that will be called when a pressed hotkey is repeating, or nil
---
--- Returns:
--- * A new `hs.hotkey` object or nil if the hotkey could not be enabled
---
--- Notes:
--- * This function is just a wrapper that performs `hs.hotkey.new(...):enable()`
--- * You can create multiple `hs.hotkey` objects for the same keyboard combination, but only one can be active
--- at any given time - see `hs.hotkey:enable()`
--- * If `message` is the empty string `""`, the alert will just show the triggered keyboard combination
--- * If you don't want any alert, you must *actually* omit the `message` parameter; a `nil` in 3rd position
--- will be interpreted as a missing `pressedfn`
--- * You must pass at least one of `pressedfn`, `releasedfn` or `repeatfn`; to delete a hotkey, use `hs.hotkey:delete()`
function hotkey.bind(...)
return hotkey.new(...):enable()
end
--- hs.hotkey.bindSpec(keyspec, ...) -> hs.hotkey object
--- Constructor
--- Creates a hotkey and enables it immediately
---
--- Parameters:
--- * keyspec - A table containing two items:
--- * first, a table containing keyboard modifiers, as specified in `hs.hotkey.bind()`
--- * second, a string containing the name of a keyboard key, as specified in `hs.hotkey.bind()`
--- * ... - All remaining arguments are as specified in `hs.hotkey.bind()`
---
--- Returns:
--- * A new `hs.hotkey` object for method chaining
---
--- Notes:
--- * This function is just a wrapper that performs `hs.hotkey.bind(keyspec[1], keyspec[2], ...)`
function hotkey.bindSpec(keyspec, ...)
return hotkey.bind(keyspec[1], keyspec[2], ...)
end
--- === hs.hotkey.modal ===
---
--- Create/manage modal keyboard shortcut environments
---
--- Usage:
--- ```lua
--- k = hs.hotkey.modal.new('cmd-shift', 'd')
--- function k:entered() hs.alert'Entered mode' end
--- function k:exited() hs.alert'Exited mode' end
--- k:bind('', 'escape', function() k:exit() end)
--- k:bind('', 'J', 'Pressed J',function() print'let the record show that J was pressed' end)```
hotkey.modal = {}
hotkey.modal.__index = hotkey.modal
--- hs.hotkey.modal:entered()
--- Method
--- Optional callback for when a modal is entered
---
--- Parameters:
--- * None
---
--- Returns:
--- * None
---
--- Notes:
--- * This is a preexisting function that you should override if you need to use it; the default implementation does nothing.
function hotkey.modal:entered() -- luacheck: ignore
end
--- hs.hotkey.modal:exited()
--- Method
--- Optional callback for when a modal is exited
---
--- Parameters:
--- * None
---
--- Returns:
--- * None
---
--- Notes:
--- * This is a preexisting function that you should override if you need to use it; the default implementation does nothing.
function hotkey.modal:exited() -- luacheck: ignore
end
--- hs.hotkey.modal:bind(mods, key, message, pressedfn, releasedfn, repeatfn) -> hs.hotkey.modal object
--- Method
--- Creates a hotkey that is enabled/disabled as the modal is entered/exited
---
--- Parameters:
--- * mods - A table or a string containing (as elements, or as substrings with any separator) the keyboard modifiers required,
--- which should be zero or more of the following:
--- * "cmd", "command" or "⌘"
--- * "ctrl", "control" or "⌃"
--- * "alt", "option" or "⌥"
--- * "shift" or "⇧"
--- * key - A string containing the name of a keyboard key (as found in [hs.keycodes.map](hs.keycodes.html#map) ), or a raw keycode number
--- * message - A string containing a message to be displayed via `hs.alert()` when the hotkey has been triggered, or nil for no alert
--- * pressedfn - A function that will be called when the hotkey has been pressed, or nil
--- * releasedfn - A function that will be called when the hotkey has been released, or nil
--- * repeatfn - A function that will be called when a pressed hotkey is repeating, or nil
---
--- Returns:
--- * The `hs.hotkey.modal` object for method chaining
function hotkey.modal:bind(...)
tinsert(self.keys, hotkey.new(...))
return self
end
--- hs.hotkey.modal:enter() -> hs.hotkey.modal object
--- Method
--- Enters a modal state
---
--- Parameters:
--- * None
---
--- Returns:
--- * The `hs.hotkey.modal` object for method chaining
---
--- Notes:
--- * This method will enable all of the hotkeys defined in the modal state via `hs.hotkey.modal:bind()`,
--- and disable the hotkey that entered the modal state (if one was defined)
--- * If the modal state was created with a keyboard combination, this method will be called automatically
function hotkey.modal:enter()
log.d('Entering modal')
if (self.k) then
disable(self.k)
end
for _,hk in ipairs(self.keys) do enable(hk,nil,true) end
self:entered()
self._isActive = true
return self
end
--- hs.hotkey.modal:exit() -> hs.hotkey.modal object
--- Method
--- Exits a modal state
---
--- Parameters:
--- * None
---
--- Returns:
--- * The `hs.hotkey.modal` object for method chaining
---
--- Notes:
--- * This method will disable all of the hotkeys defined in the modal state, and enable the hotkey for entering the modal state (if one was defined)
function hotkey.modal:exit()
for _,hk in ipairs(self.keys) do disable(hk,true) end
if (self.k) then
enable(self.k)
end
self._isActive = false
self:exited()
log.d('Exited modal')
return self
end
--- hs.hotkey.modal.new(mods, key, message) -> hs.hotkey.modal object
--- Constructor
--- Creates a new modal state, optionally with a global keyboard combination to trigger it
---
--- Parameters:
--- * mods - A table or a string containing (as elements, or as substrings with any separator) the keyboard modifiers required,
--- which should be zero or more of the following:
--- * "cmd", "command" or "⌘"
--- * "ctrl", "control" or "⌃"
--- * "alt", "option" or "⌥"
--- * "shift" or "⇧"
--- * key - A string containing the name of a keyboard key (as found in [hs.keycodes.map](hs.keycodes.html#map) ), or a raw keycode number
--- * message - A string containing a message to be displayed via `hs.alert()` when the hotkey has been triggered, or nil for no alert
---
--- Returns:
--- * A new `hs.hotkey.modal` object
---
--- Notes:
--- * If `key` is nil, no global hotkey will be registered (all other parameters will be ignored)
function hotkey.modal.new(mods, key, message)
local m = setmetatable({keys = {}}, hotkey.modal)
m._isActive = false
if (key) then
m.k = hotkey.bind(mods, key, message, function() m:enter() end)
end
log.d('Created modal hotkey')
return m
end
--- hs.hotkey.modal:delete()
--- Method
--- Deletes a modal hotkey object without calling :exited()
---
--- Parameters:
--- * None
---
--- Returns:
--- * None
function hotkey.modal:delete()
for _,hk in ipairs(self.keys) do
delete(hk)
end
if (self.k) then
self.k:delete()
end
end
function hotkey.modal:isActive()
return self._isActive
end
return hotkey