How To Add Keybindings
Adding configurable keybindings to new aspects of the Jitsi’s UI isn’t terribly difficult but can be tricky if you don’t know where to look. The KeybindingsService handles the management and persistence of bindings for use either by InputMap
s or KeyListener
s (if you’re new to Swing’s use of keybindings then see How to Use Key Bindings). This page exemplifies usage for:
net.java.sip.communicator.impl.gui.main.chat.toolBars.EditTextToolBar
which uses shortcuts for the fonts. All new binding sets need the following components:
- Unique string identifier for the component of the UI to which the bindings belong. For instance the identifier for the
EditTextToolBar
is chatToolbar. - Strings associated with the actions to be performed. For instance the
EditTextToolBar
uses bold, italic, and underline. The string identifiers used by your InputMap and ActionMap later will be <component>-<action> (ex. chatToolbar-italic). - i18n translations that are a reader friendly description of the action for the chooser. As of this writing we only have English and the
EditTextToolBar
simply uses: “Bold”, “Italic”, and “Underline”. - Default keybindings. This uses the string values described in getKeyStroke(String) method of KeyStroke.
- The actions to be fired when key’s pressed. This is often easiest via AbstractAction.
To add in a new keybinding set:
- Add the default bindings to resources/config/defaults.properties. This consists of mapping
net.java.sip.communicator.impl.gui.keybindings.<component>.<action>.<field>
to the appropriate values where the fields are:
- index: ordering in which the bindings are presented
- shortcut:
KeyStroke
string for default binding - shortcutAlt: Optional alternative default
KeyStroke
binding.
For instance the EditTextToolBar
contributes the following:
net.java.sip.communicator.impl.gui.keybindings.chatToolbar.bold.index=0 net.java.sip.communicator.impl.gui.keybindings.chatToolbar.bold.shortcut=ctrl pressed B net.java.sip.communicator.impl.gui.keybindings.chatToolbar.bold.shortcutAlt=ctrl pressed F net.java.sip.communicator.impl.gui.keybindings.chatToolbar.italic.index=1 net.java.sip.communicator.impl.gui.keybindings.chatToolbar.italic.shortcut=ctrl pressed I net.java.sip.communicator.impl.gui.keybindings.chatToolbar.italic.shortcutAlt=ctrl pressed K net.java.sip.communicator.impl.gui.keybindings.chatToolbar.underline.index=2 net.java.sip.communicator.impl.gui.keybindings.chatToolbar.underline.shortcut=ctrl pressed U
- Make your new bindings available for internal use. To do this you’ll need to edit
net.java.sip.communicator.service.keybindings.KeybindingSet
adding the component to the Category enum. For instance the following was added for the EditTextToolBar
:
CHAT_TOOLBAR(“chatToolbar”)
- Add the mapping between the internal identifier and the i18n strings for the UI in:
resources/languages/resources.properties
under the “key binding chooser” heading. For instance the following entries were added for the EditTextToolBar
:
bold=Bold italic=Italic underline=Underline
Changes made in your class:
If your class extends net.java.sip.communicator.impl.gui.main.MainFrame then using the bindings is trivial. Simply use the setKeybindingInput(KeybindingSet.Category) and addKeybindingAction(String, Action) methods to associate the bindings with the actions. For instance if the EditTextToolBar
extended MainFrame
then it would use something like:
this.setKeybindingInput(KeybindingSet.Category.CHAT_TOOLBAR); this.addKeybindingAction("chatToolbar-bold", new BoldAction()); this.addKeybindingAction("chatToolbar-italic", new ItalicAction()); this.addKeybindingAction("chatToolbar-underline", new UnderlineAction());
If not then there’s still some work to do.
- Get an instance of the
KeybindingService
via the getKeybindingService() method of theUIService
. - Snag an instance of your component’s KeybindingSet which stores the current bindings the user’s selected. You can get it via the getBindings(KeybindingSet.Category) method of the KeybindingService.
- Make your class implement the Observer interface and add it to the KeybindingSet. This will notify your class when the bindings have been changed.
- Create a method for resetting the keybindings. This should simply empty your class’ InputMap and re-add the bindings provided by your KeybindingSet. Call this method both when you initialize and for the update(Observable, Object) method you implement as an Observer.
- Add an
ActionMap
associating the <component>-<action> values to the actions they should perform.
For instance:
public class YourClass extends JPanel implements Observer { private KeybindingSet bindings; YourClass(UIService uiService) { KeybindingsService service = uiService.getKeybindingsService(); this.bindings = service.getBindings(KeybindingSet.Category.CHAT_TOOLBAR); this.bindings.addObserver(this); resetBindings(); // initializes InputMap ActionMap actionMap = getActionMap(); actionMap.put("chatToolbar-bold", new BoldAction()); actionMap.put("chatToolbar-italic", new ItalicAction()); actionMap.put("chatToolbar-underline", new UnderlineAction()); } // replaces input map entries with new bindings private void resetBindings() { InputMap inputMap = getInputMap(); inputMap.clear(); HashMap <KeyStroke, String> currentBindings = this.bindings.getBindings(); for (KeyStroke shortcut : currentBindings.keySet()) { inputMap.put(shortcut, currentBindings.get(shortcut)); } } // Listens for changes in binding sets so they can be reflected in the InputMap public void update(Observable obs, Object arg) { if (obs instanceof KeybindingSet) resetBindings(); } }
That’s it. Your new bindings should now be included in a new tab under Settings > Keybindings. Changes applied here will be reflected in your class and those changes will be preserved within the user’s preferences.