Keyboard Shortcuts

ZCS 4.5 contains several significant enhancements to keyboard navigation: First, shortcuts are now defined in properties files. Second, you can create numeric aliases for folders, tags, and saved searches, which you can then use in shortcuts. Third, there is now a page within Preferences that describes all available shortcuts.

[This post is a higher-level followup to Ross’s earlier post; read that if you’d like a detailed and technical explanation of the underpinnings of keyboard navigation and focus management.]

Properties Files

Before this release, keyboard shortcuts were defined in hashes in Javascript code. For example, the map of shortcuts available when composing a message looked like this:

this._map["ZmComposeController"] = {
"Esc":					ZmKeyMap.CANCEL,
"Alt+A":				ZmKeyMap.ATTACHMENT,
"Alt+S":				ZmKeyMap.SEND,
"Alt+D":				ZmKeyMap.SAVE,
"Alt+H":				ZmKeyMap.HTML_FORMAT,
"Alt+L":				ZmKeyMap.SPELLCHECK,
"Alt+W":				ZmKeyMap.NEW_WINDOW,
"Alt+T":				ZmKeyMap.ADDRESS_PICKER
};

There are several problems with doing it that way. First of all, shortcuts change often, and burying them in code makes them cumbersome and risky to change. Whoever changes them must have at least a rudimentary understanding of Javascript, otherwise bugs can be introduced. It is easy to break the code such that the file will not parse. Also, it’s not obvious what everything means. Though it’s reasonably clear in this case, it’s not so obvious elsewhere:

// for these to work, controller must implement getTabView()
this._map[ZmKeyMap.TABVIEW_KEYMAP] = {

Maintenance of shortcuts in this manner is clearly designed to be done by programmers. But shortcuts are more along the lines of strings, which are kept in properties files – they are an atomic component of the UI that should be easy to change. And – here’s the second big problem with the old approach – they should be easy to localize. That would be a pain to do via more Javascript code, but properties files (with their associated resource bundles) provide a built-in mechanism for localization. Also, because different types of computers (eg Macs vs Windows machines) have different keyboard behavior, we need an easy way to override shortcuts by platform.

Now that the shortcuts are in properties files, they can be maintained by the same folks who maintain the strings files. Plus, we can add documentation into the properties files which will appear in the web client. Of course, we use more natural names for the shortcut maps and actions, and translate those when we convert them from properties to Javascript.

As before, shortcuts are divided into two categories:

1. Shortcuts that apply directly to UI components (“widgets”) such as buttons and menus. The shortcuts that are in play are for the widget that currently has focus. In general, these do not use letter keys. They are in:

Ajax/WebRoot/js/config/keys/AjxKeys.properties

2. Application shortcuts that are independent of focus, which depend on what the user is currently doing. For example, there are different sets of shortcuts for composing a message, for viewing your inbox, and for viewing your calendar. In general, these use the letter keys. They are in:

ZimbraWebClient/WebRoot/js/config/keys/ZmKeys.properties

A good number of the shortcuts of the latter type are mnemonic. For example, typing “i” takes you to your Inbox.
Of course, it won’t be called “Inbox” in every language, so translators will want to be able to override that
association. We have a Brazilian customer who has translated “Inbox” to “Caixa de Entrada”. If they want to
make “c” the shortcut for that rather than “i”, then all they need to do is create the file:

ZimbraWebClient/WebRoot/js/config/keys/ZmKeys_pt_BR.properties

which contains overrides for Brazilian Portuguese. Remember, these are overrides, so only shortcuts that are being changed need to be included. To change the Inbox shortcut, add the line:

mail.GoToInbox = C

The mechanism for overriding shortcuts by platform is internal to the properties files, and is done by appending “.mac”, “.win”, or “.linux” to the property name. For example, Macs do not have a separate Delete key, so we use Backspace instead:

global.Delete.mac = Backspace

At the end of this post is an abbreviated version of the application shortcuts properties file, which includes a detailed description of the shortcut property format. Most of the actual shortcuts (there are over 100) have been removed. A description of the documentation mechanism remains, along with a few examples.

Our public wiki includes a reasonably up-to-date list of shortcuts.

Custom Shortcuts

As you can probably tell, I really like keyboard shortcuts. The more I can manage my mailbox without having to go back and forth between the mouse and the keyboard, the happier I am. When Ross first implemented keyboard navigation, one of the first features I really wanted was the ability to interact with folders, tags, and saved searches via shortcuts. The way I manage some of my work-related mail provides a good use case:

  1. Click on saved search “ZWC commits”, which shows recent changes to the web client.
  2. Read the summary of each change, moving to next message with arrow keys.
  3. If I need to integrate a change to another branch, drag and drop “Integrate” tag onto message.
  4. After all messages have been processed, use Ctrl-A to select all.
  5. Drag and drop them to the folder “commits/ZimbraWebClient”.

There are several other types of commits I filter, and it is something I do at least once a day. I want it to be fast, and going back and forth to the mouse (steps 1, 3, and 5) slows me down. If I could assign aliases to my organizers (folders, tags, saved searches) which could be used in shortcuts, then I could do everything from the keyboard, like so:

  1. Type “s1” to run saved search #1 (“ZWC commits”).
  2. Read the summary of each change, moving to next note with arrow keys.
  3. If I need to integrate a change to another branch, type “t1” to tag with tag #1 (“Integrate”).
  4. After all messages have been processed, use Ctrl-A to select all.
  5. Type “.1” to move them to folder #1 (“commits/ZimbraWebClient”).

And that’s what we’ve done. There is a new tab in Options titled “Shortcuts” which has four tabs inside it. The first is a list of all currently available shortcuts, along with descriptions culled from the properties files. The other three are for assigning numeric shortcuts to folders, tags, and saved searches. The following custom shortcuts are available:

v[n]	Show the specified mail folder
.[n]	Move messages to the specified mail folder
y[n]	Show items with specified tag
t[n]	Tag selected items with specified tag
s[n]	Show results of specified search

There is no limit to the number of aliases you may create; you may use two or more digits in your alias, and it will still work. Of course, a large number will be harder to remember, and you may need to refer to the Shortcuts tab to see what they are.

Property File Details

The documentation and some selected examples from ZmKeys.properties:

# Keyboard Shortcuts for the ZCS Web Client
#
#
# There are three parts to each property below. The first two are the name
# of a map and an action, and they are combined with a period to form the
# property name. The property’s value is a key sequence. For example:
#
# compose.Send = Alt+S
#
# Maps
# —-
#
# The map names in this properties file refer to views within the ZCS web
# client. The map name is the first part of the property name and
# comes before the period. The following are valid map names:
#
# global applies to all views
# compose a form for creating a new message
# mail a list of mail messages or conversations
# conversation a list of mail messages, with the content of the
# selected message displayed in the lower pane
# message the content of a single mail message; only used
# by message list view with reading pane off
# contacts a set of contacts in either list or cards view
# editContact a form for creating or editing a contact
# calendar any of several calendar views (week, month, etc)
# editAppointment a form for creating or editing an appointment
# options a set of tabs for changing settings
# mixed a view of different types of items (eg Trash)
# notebook the wiki application
#
# Actions
# ——-
#
# An action is an event triggered by a shortcut. It is what the shortcut
# does. Most of the time, the action invoked by a web client shortcut emulates
# something that could have been done using the mouse, for example clicking
# on a particular button. To see what actions are available and what each action does,
# check the documentation section below.
#
# The action “INHERIT” has special meaning. When it is used, the name of a
# map is given rather than a key sequence. The current map will copy all the
# shortcuts of the given map, and then may add or override those shortcuts.
# In general, you probably do not want to change the “INHERIT” properties.
#
# To define a shortcut that applies only on a particular platform (Windows,
# Macintosh, or Linux), add a platform identifier to the action. The platform
# identifier can be one of:
#
# win mac linux
#
# For example:
#
# compose.Send.mac = Ctrl+Enter
#
# Key Sequences (shortcuts)
# ————-
#
# A key sequence is a set of one or more keys that triggers an action. Each
# key in the sequence may have a modifier (such as the Control or Shift key).
# Most key sequences consist of just one key. The keys in multiple-key
# sequences are separated with a comma. The next key in a sequence must be
# struck within a short time for the sequence to continue.
#
# The following are valid modifiers:
#
# Ctrl Meta Alt Shift
#
# To add a modifier to a key, specify the modifier, then a plus sign, then the
# key. For example: Ctrl+C. If you want to add more than one modifier, use
# another plus sign. For example: Ctrl+Alt+Del.
#
# If you want to have more than one shortcut for the same action, use a
# semicolon (and optional space) to separate the shortcuts. For example, to
# specify three shortcuts for list action Foo:
#
# list.Foo = A; B; C
#
# Key sequences have no notion of upper case or lower case. They map to what
# you see on your keyboard (for example, a “T”), rather than the character it
# produces when you strike it (a “t”). To specify a keystroke that requires the
# Shift key, you must use the Shift modifier. For example, to specify the “@”
# key, you’d use: Shift+2.
#
# Each letter, number, and non-shifted printable character represents itself:
#
# A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9
# ` – = [ ] ‘ . /
#
# Non-printable keys are specified with special names. In addition, comma and
# semicolon have special names because they are used as delimiters. The
# following special names are available:
#
# Home End Esc Del Backspace Enter ArrowUp ArrowDown ArrowLeft ArrowRight
# Space Comma Backslash Semicolon
#
# The sequence “NNN” has a special meaning in shortcuts. It stands for “any
# number”. For example, with the mapping
#
# mail.MoveToFolder = M,NNN
#
# the key sequence “M,3” will move selected messages to the folder which has
# 3 as its alias. Only actions which contain “NNN” support that sort of mapping.

global.NewMessage = N,M; C
global.NewMessageWindow = Shift+C
global.GoToTag = Y,NNN
global.Tag = T,NNN
global.SavedSearch = S,NNN

mail.INHERIT = global
mail.Reply = R
mail.ReplyAll = A
mail.GoToFolder = V,NNN
mail.MoveToFolder = .,NNN; Shift+.,NNN

compose.Cancel = Esc
compose.Send = Alt+S
compose.Send.mac = Ctrl+Enter; Ctrl+S

###########################################################################
#
# Documentation
#
# Maps and actions can be documented by appending “.description” to the map
# name or the action and using that as a property name. The descriptive
# text is the property’s value. The descriptions below show up as content in
# the Shortcuts tab on the Options page.
#
# The “summary” text is used when printing the Quick Reference (not yet
# implemented).
#
# The “sort” field sets the sorting order among either map names or among
# the actions within a map, from low to high. There are gaps so that
# properties may be inserted and given a sort order without having to
# change the order of other properties.
#
# The “example” field contains text that is used in the examples for
# creating custom shortcuts for folders, tags, and saved searches.
###########################################################################

global.sort = 10
global.description = Shortcuts for All Applications
global.summary = “All Applications”
global.NewMessage.sort = 80
global.NewMessage.description = New message (“compose”)
global.NewMessageWindow.sort = 90
global.NewMessageWindow.description = New message in a new window
global.GoToTag.sort = 240
global.GoToTag.description = Show items with specified tag (set the tag in the “Tag Shortcuts” tab)
global.GoToTag.summary = Show items with specified tag.
global.GoToTag.example = will show all items with tag {0}.
global.Tag.sort = 250
global.Tag.description = Tag selected items with specified tag (set the tag in the “Tag Shortcuts” tab)
global.Tag.summary = Tag selected items with specified tag
global.Tag.example = will apply tag {0} to selected items.
global.SavedSearch.sort = 260
global.SavedSearch.description = Show results of specified search (set the search in the “Search Shortcuts” tab)
global.SavedSearch.summary = Show results of specified search
global.SavedSearch.example = will show results of the {0} search.

mail.sort = 20
mail.description = Shortcuts in Mail
mail.summary = Mail
mail.GoToFolder.sort = 50
mail.GoToFolder.description = Show the specified mail folder (set the folder in the “Mail Folder Shortcuts” tab)
mail.GoToFolder.summary = Show the specified mail folder
mail.GoToFolder.example = will show the {0} folder.
mail.MoveToFolder.sort = 140
mail.MoveToFolder.description = Move messages to the specified mail folder (set the folder in the “Mail Folder

Shortcuts” tab)
mail.MoveToFolder.summary = Move messages to the specified mail folder
mail.MoveToFolder.example = will move selected messages to the {0} folder.
mail.Reply.sort = 150
mail.Reply.description = Reply to sender
mail.ReplyAll.sort = 160
mail.ReplyAll.description = Reply to all recipients

compose.sort = 30
compose.description = Shortcuts in Mail Compose form
compose.summary = Mail Compose
compose.Send.sort = 10
compose.Send.description = Send the message

Comments are closed.

Copyright © 2022 Zimbra, Inc. All rights reserved.

All information contained in this blog is intended for informational purposes only. Synacor, Inc. is not responsible or liable in any manner for the use or misuse of any technical content provided herein. No specific or implied warranty is provided in association with the information or application of the information provided herein, including, but not limited to, use, misuse or distribution of such information by any user. The user assumes any and all risk pertaining to the use or distribution in any form of any subject matter contained in this blog.

Legal Information | Privacy Policy | Do Not Sell My Personal Information | CCPA Disclosures