mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-08 01:04:39 +08:00
df38008c56
Summary: This diff includes a few small things: - Menu: Don't select the first item until the user taps down arrow, and allow the user to use the arrow keys to move up and down through Menu items. - Menu: Make scroll code from MultiselectList re-usable, use in Menu. Now if you use the keys to move to an item that is offscreen it will follow. - Popover: Tapping the button that opened popover should close it - Make sure buttons in toolbars are at least standard height - Re-enable Markdown processing via `grunt docs` - A bit of initial inline documentation for crosjdoc. Need to evaluate whether this is worth doing everywhere. - New `search-playground` package for experimenting with search and search weights. - Swap itemClassProvider for more generic itemPropProvider - Add crojsdoc config file - Export React, because third party packages can't require things from our app - [FEATURE] Bring back static file support in third party packages via `nylas://translate/IMG_20150417_124142.jpg` - Fix invariant error with search bar - [FEATURE] "Show Original" under Message actions - Fix DatabaseView so that many archives at once don't cause problems Test Plan: Run specs Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1426
175 lines
7.4 KiB
Markdown
175 lines
7.4 KiB
Markdown
# Keymaps In-Depth
|
|
|
|
## Structure of a Keymap File
|
|
|
|
Keymap files are encoded as JSON or CSON files containing nested hashes. They
|
|
work much like style sheets, but instead of applying style properties to elements
|
|
matching the selector, they specify the meaning of keystrokes on elements
|
|
matching the selector. Here is an example of some bindings that apply when
|
|
keystrokes pass through `atom-text-editor` elements:
|
|
|
|
```coffee
|
|
'atom-text-editor':
|
|
'cmd-delete': 'editor:delete-to-beginning-of-line'
|
|
'alt-backspace': 'editor:delete-to-beginning-of-word'
|
|
'ctrl-A': 'editor:select-to-first-character-of-line'
|
|
'ctrl-shift-e': 'editor:select-to-end-of-line'
|
|
'cmd-left': 'editor:move-to-first-character-of-line'
|
|
|
|
'atom-text-editor:not([mini])'
|
|
'cmd-alt-[': 'editor:fold-current-row'
|
|
'cmd-alt-]': 'editor:unfold-current-row'
|
|
```
|
|
|
|
Beneath the first selector are several bindings, mapping specific *keystroke
|
|
patterns* to *commands*. When an element with the `atom-text-editor` class is focused and
|
|
`cmd-delete` is pressed, an custom DOM event called
|
|
`editor:delete-to-beginning-of-line` is emitted on the `atom-text-editor` element.
|
|
|
|
The second selector group also targets editors, but only if they don't have the
|
|
`mini` attribute. In this example, the commands for code folding don't really
|
|
make sense on mini-editors, so the selector restricts them to regular editors.
|
|
|
|
### Keystroke Patterns
|
|
|
|
Keystroke patterns express one or more keystrokes combined with optional
|
|
modifier keys. For example: `ctrl-w v`, or `cmd-shift-up`. A keystroke is
|
|
composed of the following symbols, separated by a `-`. A multi-keystroke pattern
|
|
can be expressed as keystroke patterns separated by spaces.
|
|
|
|
|
|
| Type | Examples
|
|
| --------------------|----------------------------
|
|
| Character literals | `a` `4` `$`
|
|
| Modifier keys | `cmd` `ctrl` `alt` `shift`
|
|
| Special keys | `enter` `escape` `backspace` `delete` `tab` `home` `end` `pageup` `pagedown` `left` `right` `up` `down`
|
|
|
|
### Commands
|
|
|
|
Commands are custom DOM events that are triggered when a keystroke matches a
|
|
binding. This allows user interface code to listen for named commands without
|
|
specifying the specific keybinding that triggers it. For example, the following
|
|
code creates a command to insert the current date in an editor:
|
|
|
|
```coffee
|
|
atom.commands.add 'atom-text-editor',
|
|
'user:insert-date': (event) ->
|
|
editor = @getModel()
|
|
editor.insertText(new Date().toLocaleString())
|
|
```
|
|
|
|
`atom.commands` refers to the global {CommandRegistry} instance where all commands
|
|
are set and consequently picked up by the command palette.
|
|
|
|
When you are looking to bind new keys, it is often useful to use the command
|
|
palette (`ctrl-shift-p`) to discover what commands are being listened for in a
|
|
given focus context. Commands are "humanized" following a simple algorithm, so a
|
|
command like `editor:fold-current-row` would appear as "Editor: Fold Current
|
|
Row".
|
|
|
|
### "Composed" Commands
|
|
|
|
A common question is, "How do I make a single keybinding execute two or more
|
|
commands?" There isn't any direct support for this in Atom, but it can be
|
|
achieved by creating a custom command that performs the multiple actions
|
|
you desire and then creating a keybinding for that command. For example, let's
|
|
say I want to create a "composed" command that performs a Select Line followed
|
|
by Cut. You could add the following to your `init.coffee`:
|
|
|
|
```coffee
|
|
atom.commands.add 'atom-text-editor', 'custom:cut-line', ->
|
|
editor = atom.workspace.getActiveTextEditor()
|
|
editor.selectLinesContainingCursors()
|
|
editor.cutSelectedText()
|
|
```
|
|
|
|
Then let's say we want to map this custom command to `alt-ctrl-z`, you could
|
|
add the following to your keymap:
|
|
|
|
```coffee
|
|
'atom-text-editor':
|
|
'alt-ctrl-z': 'custom:cut-line'
|
|
```
|
|
|
|
### Specificity and Cascade Order
|
|
|
|
As is the case with CSS applying styles, when multiple bindings match for a
|
|
single element, the conflict is resolved by choosing the most *specific*
|
|
selector. If two matching selectors have the same specificity, the binding
|
|
for the selector appearing later in the cascade takes precedence.
|
|
|
|
Currently, there's no way to specify selector ordering within a single keymap,
|
|
because JSON objects do not preserve order. We eventually plan to introduce a
|
|
custom CSS-like file format for keymaps that allows for ordering within a single
|
|
file. For now, we've opted to handle cases where selector ordering is critical
|
|
by breaking the keymap into two separate files, such as `snippets-1.cson` and
|
|
`snippets-2.cson`.
|
|
|
|
## Removing Bindings
|
|
|
|
When the keymap system encounters a binding with the `unset!` directive as its
|
|
command, it will treat the current element as if it had no key bindings matching
|
|
the current keystroke sequence and continue searching from its parent. If you
|
|
want to remove a binding from a keymap you don't control, such as keymaps in
|
|
Atom core or in packages, use the `unset!` directive.
|
|
|
|
For example, the following code removes the keybinding for `a` in the Tree View,
|
|
which is normally used to trigger the `tree-view:add-file` command:
|
|
|
|
```coffee
|
|
'.tree-view':
|
|
'a': 'unset!'
|
|
```
|
|
|
|
![](https://cloud.githubusercontent.com/assets/38924/3174771/e7f6ce64-ebf4-11e3-922d-f280bffb3fc5.png)
|
|
|
|
## Forcing Chromium's Native Keystroke Handling
|
|
|
|
EDIT: This has been overridden in the Edgehill project. By default, Chromium's
|
|
native keystroke handling for Copy, Paste, Select-All, etc. is enabled. To except
|
|
yourself from these behaviors, apply the `.override-key-bindings` class to an element.
|
|
|
|
If you want to force the native browser behavior for a given keystroke, use the
|
|
`native!` directive as the command of a binding. This can be useful to enable
|
|
the correct behavior in native input elements, for example. If you apply the
|
|
`.native-key-bindings` class to an element, all the keystrokes typically handled
|
|
by the browser will be assigned the `native!` directive.
|
|
|
|
## Overloading Key Bindings
|
|
|
|
Occasionally, it makes sense to layer multiple actions on top of the same key
|
|
binding. An example of this is the snippets package. Snippets are inserted by
|
|
typing a snippet prefix such as `for` and then pressing `tab`. Every time `tab`
|
|
is pressed, we want to execute code attempting to expand a snippet if one exists
|
|
for the text preceding the cursor. If a snippet *doesn't* exist, we want `tab`
|
|
to actually insert whitespace.
|
|
|
|
To achieve this, the snippets package makes use of the `.abortKeyBinding()`
|
|
method on the event object representing the `snippets:expand` command.
|
|
|
|
```coffee-script
|
|
# pseudo-code
|
|
editor.command 'snippets:expand', (e) =>
|
|
if @cursorFollowsValidPrefix()
|
|
@expandSnippet()
|
|
else
|
|
e.abortKeyBinding()
|
|
```
|
|
|
|
When the event handler observes that the cursor does not follow a valid prefix,
|
|
it calls `e.abortKeyBinding()`, telling the keymap system to continue searching
|
|
for another matching binding.
|
|
|
|
## Step-by-Step: How Keydown Events are Mapped to Commands
|
|
|
|
* A keydown event occurs on a *focused* element.
|
|
* Starting at the focused element, the keymap walks upward towards the root of
|
|
the document, searching for the most specific CSS selector that matches the
|
|
current DOM element and also contains a keystroke pattern matching the keydown
|
|
event.
|
|
* When a matching keystroke pattern is found, the search is terminated and the
|
|
pattern's corresponding command is triggered on the current element.
|
|
* If `.abortKeyBinding()` is called on the triggered event object, the search
|
|
is resumed, triggering a binding on the next-most-specific CSS selector for
|
|
the same element or continuing upward to parent elements.
|
|
* If no bindings are found, the event is handled by Chromium normally.
|