]> git.gir.st - VimFx.git/blob - extension/packages/events.coffee
Refactor Esc key handling
[VimFx.git] / extension / packages / events.coffee
1 utils = require 'utils'
2 keyUtils = require 'key-utils'
3 { Vim } = require 'vim'
4 { getPref } = require 'prefs'
5 { setWindowBlacklisted } = require 'button'
6 { unload } = require 'unload'
7 { commands } = require 'commands'
8 { modes } = require 'modes'
9
10 { interfaces: Ci } = Components
11
12 # Not suppressing Esc allows for stopping the loading of the page as well as closing many custom
13 # dialogs (and perhaps other things -- Esc is a very commonly used key). There are two reasons we
14 # might suppress it in other modes. If some custom dialog of a website is open, we should be able to
15 # cancel hint markers on it without closing it. Secondly, otherwise cancelling hint markers on
16 # google causes its search bar to be focused.
17 NEVER_SUPPRESS_IN_NORMAL_MODE = ['Esc']
18
19 # TODO: Should 'Esc' be configurable?
20 newFunc = (window) -> new Vim({window, commands, modes, esc: 'Esc'})
21 vimBucket = new utils.Bucket(utils.getWindowId, newFunc)
22
23 keyStrFromEvent = (event) ->
24 { ctrlKey: ctrl, metaKey: meta, altKey: alt, shiftKey: shift } = event
25
26 if !meta and !alt
27 return unless keyChar = keyUtils.keyCharFromCode(event.keyCode, shift)
28 keyStr = keyUtils.applyModifiers(keyChar, ctrl, alt, meta)
29 return keyStr
30
31 return null
32
33 # Passthrough mode is activated when VimFx should temporarily stop processing keyboard input, for
34 # example when a menu is shown.
35 passthrough = false
36 checkPassthrough = (event) ->
37 if event.target.nodeName in ['menupopup', 'panel']
38 passthrough = switch event.type
39 when 'popupshown' then true
40 when 'popuphidden' then false
41
42 suppress = false
43 keyStr = undefined
44 keyListener = (event) ->
45 try
46 return if passthrough or getPref('disabled')
47 return unless window = utils.getCurrentTabWindow(event)
48 return unless vim = vimBucket.get(window)
49 return if vim.blacklisted
50
51 if event.type == 'keydown'
52 suppress = false
53
54 return unless keyStr = keyStrFromEvent(event)
55
56 isEditable = utils.isElementEditable(event.originalTarget)
57 unless isEditable and keyStr != vim.esc
58
59 # This check must be done before `vim.onInput()` below, since that call might change the
60 # mode. We are interested in the mode at the beginning of the events, not whatever it might
61 # be afterwards.
62 suppressException = (vim.mode == Vim.MODE_NORMAL and keyStr in NEVER_SUPPRESS_IN_NORMAL_MODE)
63
64 suppress = vim.onInput(keyStr, event)
65 if suppressException
66 suppress = false
67
68 if suppress
69 event.preventDefault()
70 event.stopPropagation()
71
72 catch error
73 console.log("#{ error } (in #{ event.type })\n#{ error.stack.replace(/@.+-> /g, '@') }")
74
75 removeVimFromTab = (tab, gBrowser) ->
76 return unless browser = gBrowser.getBrowserForTab(tab)
77 vimBucket.forget(browser.contentWindow)
78
79 # The following listeners are installed on every top level Chrome window
80 windowsListeners =
81 keydown: keyListener
82 keypress: keyListener
83 keyup: keyListener
84 popupshown: checkPassthrough
85 popuphidden: checkPassthrough
86
87 # When the top level window closes we should release all Vims that were
88 # associated with tabs in this window
89 DOMWindowClose: (event) ->
90 return unless { gBrowser } = event.originalTarget
91 for tab in gBrowser.tabs
92 removeVimFromTab(tab, gBrowser)
93
94 TabClose: (event) ->
95 return unless gBrowser = utils.getEventTabBrowser(event)
96 tab = event.originalTarget
97 removeVimFromTab(tab, gBrowser)
98
99 # Update the toolbar button icon to reflect the blacklisted state
100 TabSelect: (event) ->
101 return unless window = event.originalTarget?.linkedBrowser?.contentDocument?.defaultView
102 return unless vim = vimBucket.get(window)
103 return unless rootWindow = utils.getRootWindow(window)
104 setWindowBlacklisted(rootWindow, vim.blacklisted)
105
106
107 # This listener works on individual tabs within Chrome Window
108 tabsListener =
109 # Listenfor location changes, disable the extension on blacklisted urls, and make sure that we
110 # start out in normal mode
111 onLocationChange: (browser, webProgress, request, location) ->
112 return unless vim = vimBucket.get(browser.contentWindow)
113
114 vim.enterNormalMode()
115
116 blacklisted = utils.isBlacklisted(location.spec, getPref('black_list'))
117 vim.blacklisted = blacklisted
118 return unless rootWindow = utils.getRootWindow(vim.window)
119 setWindowBlacklisted(rootWindow, blacklisted)
120
121 addEventListeners = (window) ->
122 for name, listener of windowsListeners
123 window.addEventListener(name, listener, true)
124
125 window.gBrowser.addTabsProgressListener(tabsListener)
126
127 unload ->
128 for name, listener of windowsListeners
129 window.removeEventListener(name, listener, true)
130
131 window.gBrowser.removeTabsProgressListener(tabsListener)
132
133 exports.addEventListeners = addEventListeners
Imprint / Impressum