aboutsummaryrefslogtreecommitdiffstats
path: root/content_scripts
diff options
context:
space:
mode:
authorStephen Blott2016-10-15 12:01:15 +0100
committerStephen Blott2016-10-15 12:13:25 +0100
commit32dc2bf3bf5c28ff44fff89d2436a76bb7bba925 (patch)
treecde2a7ec9c6ad338e3ef35c3f961b0363b873a7d /content_scripts
parent68205d32188cd94c4c533d8bacc8a2b384821bae (diff)
downloadvimium-32dc2bf3bf5c28ff44fff89d2436a76bb7bba925.tar.bz2
enterNormalMode; new command - implementation
Here's the problem... Many sites define their own keyboard shortcuts, for example Google Play Music defines `gh` for "go home". On such sites, it's natural to set up pass keys for `g` and `h`. But that makes any Vimium key bindings which begin with `g` inaccessible. Here, we add a new command `enterNormalMode` which installs a new normal-mode instance (without any pass keys). This executes a single normal-mode command then exits. Example: map \ enterNormalMode map | enterNormalMode count=999999 Assuming `g` and `o` are pass keys: - `gh` or `o` - use the page's binding - `\gg` - scroll to top - `2\ggo` - scroll to the top and open the Vomnibar - `\g<Escape>o` - open the Vomnibar - `\<Escape>o` - use the page's bindings - `\\\\\\<Escape>o` - use the page's bindings (new normal-mode instances displace previous ones) This required some changes to the scroller. Previously, we only ever had one normal-mode instance, and could arrange statically that the scroller's key listeners were above normal-mode's key listeners in the handler stack. Here, we fix this by adding and removing the scroller's listeners dynamically, so they're always at the top of the handler stack.
Diffstat (limited to 'content_scripts')
-rw-r--r--content_scripts/mode.coffee3
-rw-r--r--content_scripts/mode_find.coffee3
-rw-r--r--content_scripts/mode_insert.coffee3
-rw-r--r--content_scripts/mode_key_handler.coffee18
-rw-r--r--content_scripts/scroller.coffee18
-rw-r--r--content_scripts/vimium_frontend.coffee16
6 files changed, 43 insertions, 18 deletions
diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee
index 6508627e..c1dea59a 100644
--- a/content_scripts/mode.coffee
+++ b/content_scripts/mode.coffee
@@ -81,9 +81,8 @@ class Mode
_name: "mode-#{@id}/exitOnEscape"
"keydown": (event) =>
return @continueBubbling unless KeyboardUtils.isEscape event
- DomUtils.suppressKeyupAfterEscape handlerStack
@exit event, event.srcElement
- @suppressEvent
+ DomUtils.suppressKeyupAfterEscape handlerStack
# If @options.exitOnBlur is truthy, then it should be an element. The mode will exit when that element
# loses the focus.
diff --git a/content_scripts/mode_find.coffee b/content_scripts/mode_find.coffee
index 8621edf8..0178527b 100644
--- a/content_scripts/mode_find.coffee
+++ b/content_scripts/mode_find.coffee
@@ -47,9 +47,8 @@ class PostFindMode extends SuppressPrintable
_name: "mode-#{@id}/handle-escape"
keydown: (event) =>
if KeyboardUtils.isEscape event
- DomUtils.suppressKeyupAfterEscape handlerStack
@exit()
- @suppressEvent
+ DomUtils.suppressKeyupAfterEscape handlerStack
else
handlerStack.remove()
@continueBubbling
diff --git a/content_scripts/mode_insert.coffee b/content_scripts/mode_insert.coffee
index 4cb0a39e..d5d98297 100644
--- a/content_scripts/mode_insert.coffee
+++ b/content_scripts/mode_insert.coffee
@@ -17,7 +17,6 @@ class InsertMode extends Mode
return @suppressEvent
return @passEventToPage unless event.type == 'keydown' and KeyboardUtils.isEscape event
- DomUtils.suppressKeyupAfterEscape handlerStack
target = event.srcElement
if target and DomUtils.isFocusable target
# Remove the focus, so the user can't just get back into insert mode by typing in the same input box.
@@ -26,7 +25,7 @@ class InsertMode extends Mode
# An editable element in a shadow DOM is focused; blur it.
@insertModeLock.blur()
@exit event, event.srcElement
- @suppressEvent
+ DomUtils.suppressKeyupAfterEscape handlerStack
defaults =
name: "insert"
diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee
index 480a79af..9b5a1fef 100644
--- a/content_scripts/mode_key_handler.coffee
+++ b/content_scripts/mode_key_handler.coffee
@@ -36,6 +36,18 @@ class KeyHandlerMode extends Mode
@mapKeyRegistry = {}
Utils.monitorChromeStorage "mapKeyRegistry", (value) => @mapKeyRegistry = value
+ if options.exitOnEscape
+ # If we're part way through a command's key sequence, then a first Escape should reset the key state,
+ # and only a second Escape should actually exit this mode.
+ @push
+ _name: "key-handler-escape-listener"
+ keydown: (event) =>
+ if KeyboardUtils.isEscape(event) and not @isInResetState()
+ @reset()
+ DomUtils.suppressKeyupAfterEscape handlerStack
+ else
+ @continueBubbling
+
onKeydown: (event) ->
keyChar = KeyboardUtils.getKeyCharString event
keyChar = @mapKeyRegistry[keyChar] ? keyChar
@@ -92,7 +104,10 @@ class KeyHandlerMode extends Mode
# Keystrokes are *never* considered pass keys if the user has begun entering a command. So, for example, if
# 't' is a passKey, then the "t"-s of 'gt' and '99t' are neverthless handled as regular keys.
isPassKey: (keyChar) ->
- @countPrefix == 0 and @keyState.length == 1 and keyChar in (@passKeys ? "")
+ @isInResetState() and keyChar in (@passKeys ? "")
+
+ isInResetState: ->
+ @countPrefix == 0 and @keyState.length == 1
handleKeyChar: (keyChar) ->
bgLog "handle key #{keyChar} (#{@name})"
@@ -106,6 +121,7 @@ class KeyHandlerMode extends Mode
bgLog " invoke #{command.command} count=#{count} "
@reset()
@commandHandler {command, count}
+ @exit() if @options.count? and --@options.count <= 0
@suppressEvent
root = exports ? window
diff --git a/content_scripts/scroller.coffee b/content_scripts/scroller.coffee
index 3a1b3772..a533c161 100644
--- a/content_scripts/scroller.coffee
+++ b/content_scripts/scroller.coffee
@@ -128,13 +128,15 @@ checkVisibility = (element) ->
CoreScroller =
init: ->
@time = 0
- @lastEvent = null
- @keyIsDown = false
+ @lastEvent = @keyIsDown = @cancelEventListenerId = null
+ # This installs listeners for events which should cancel smooth scrolling.
+ installCanceEventListener: ->
+ @removeCancelEventListener()
# NOTE(smblott) With extreme keyboard configurations, Chrome sometimes does not get a keyup event for
# every keydown, in which case tapping "j" scrolls indefinitely. This appears to be a Chrome/OS/XOrg bug
# of some kind. See #1549.
- handlerStack.push
+ @cancelEventListenerId = handlerStack.push
_name: 'scroller/track-key-status'
keydown: (event) =>
handlerStack.alwaysContinueBubbling =>
@@ -149,6 +151,10 @@ CoreScroller =
handlerStack.alwaysContinueBubbling =>
@time += 1 if event.target == window
+ removeCancelEventListener: ->
+ handlerStack.remove @cancelEventListenerId if @cancelEventListenerId?
+ @cancelEventListenerId = @lastEvent = @keyIsDown = null
+
# Return true if CoreScroller would not initiate a new scroll right now.
wouldNotInitiateScroll: -> @lastEvent?.repeat and Settings.get "smoothScroll"
@@ -175,7 +181,7 @@ CoreScroller =
return if @lastEvent?.repeat
activationTime = ++@time
- myKeyIsStillDown = => @time == activationTime and @keyIsDown
+ myKeyIsStillDown = => @time == activationTime and @keyIsDown ? true
# Store amount's sign and make amount positive; the arithmetic is clearer when amount is positive.
sign = getSign amount
@@ -215,13 +221,15 @@ CoreScroller =
requestAnimationFrame animate
else
# We're done.
+ @removeCancelEventListener()
checkVisibility element
# If we've been asked not to be continuous, then we advance time, so the myKeyIsStillDown test always
# fails.
++@time unless continuous
- # Launch animator.
+ # Start scrolling.
+ @installCanceEventListener()
requestAnimationFrame animate
# Scroller contains the two main scroll functions which are used by clients.
diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee
index fcca98ab..64e13852 100644
--- a/content_scripts/vimium_frontend.coffee
+++ b/content_scripts/vimium_frontend.coffee
@@ -111,11 +111,13 @@ handlerStack.push
class NormalMode extends KeyHandlerMode
constructor: (options = {}) ->
- super extend options,
+ defaults =
name: "normal"
- indicator: false # There is no mode indicator in normal mode.
+ indicator: false # There is normally no mode indicator in normal mode.
commandHandler: @commandHandler.bind this
+ super extend defaults, options
+
chrome.storage.local.get "normalModeKeyStateMapping", (items) =>
@setKeyMapping items.normalModeKeyStateMapping
@@ -123,10 +125,6 @@ class NormalMode extends KeyHandlerMode
if area == "local" and changes.normalModeKeyStateMapping?.newValue
@setKeyMapping changes.normalModeKeyStateMapping.newValue
- # Initialize components which normal mode depends upon.
- Scroller.init()
- FindModeHistory.init()
-
commandHandler: ({command: registryEntry, count}) ->
count *= registryEntry.options.count ? 1
count = 1 if registryEntry.noRepeat
@@ -150,6 +148,9 @@ installModes = ->
# Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and
# activates/deactivates itself accordingly.
normalMode = new NormalMode
+ # Initialize components upon which normal mode depends.
+ Scroller.init()
+ FindModeHistory.init()
new InsertMode permanent: true
new GrabBackFocus if isEnabledForUrl
normalMode # Return the normalMode object (for the tests).
@@ -386,6 +387,9 @@ extend window,
passNextKey: (count) ->
new PassNextKeyMode count
+ enterNormalMode: (count) ->
+ new NormalMode exitOnEscape: true, indicator: "Normal mode", count: count, singleton: "enterNormalMode"
+
focusInput: do ->
# Track the most recently focused input element.
recentlyFocusedElement = null