Skip to content

Keyboard Shortcuts

Status: ✅ Implemented — @Action(shortcut = ...), @Trigger(type = OnEnter), @Tab(shortcut = ...)

Let expert users complete frequent tasks without touching the mouse.

Users who spend hours in a backoffice application are slowed down every time they must reach for the mouse to trigger a common action. A keyboard-hostile interface is the Desktop Denial anti-pattern: it penalises the users who know the system best.

Action shortcuts — @Action(shortcut = ...)

Section titled “Action shortcuts — @Action(shortcut = ...)”

Attach a keyboard shortcut to any action. The shortcut is active whenever the view that owns the action is focused.

@Action(shortcut = "ctrl+s")
public void save() {
orderService.save(this);
}
@Action(shortcut = "ctrl+enter")
public void submit() {
workflowService.submit(this);
}
@Action(shortcut = "escape")
public void cancel() { }

Shortcut strings follow standard modifier notation: ctrl, shift, alt, meta (⌘ on Mac), combined with a key name (s, enter, escape, f2, etc.).

Enter key on forms — @Trigger(type = OnEnter)

Section titled “Enter key on forms — @Trigger(type = OnEnter)”

Use @Trigger(type = TriggerType.OnEnter) to fire an action when the user presses Enter anywhere in a form, without attaching the shortcut to a specific action button.

@Trigger(type = TriggerType.OnEnter, calledActionId = "search")
public class ProductSearch {
private String name;
public List<Product> search() {
return productRepo.search(name);
}
}

Attach a shortcut to a tab so users can jump straight to it without the mouse. Use the same modifier notation as action shortcuts.

@UI("/order")
@Tabs
public class OrderForm {
@Tab(value = "Customer", shortcut = "alt+1")
String customer;
@Tab(value = "Items", shortcut = "alt+2")
String items;
@Tab(value = "Billing", shortcut = "alt+3")
String billing;
}

Pressing the shortcut selects the matching tab in place — no server round-trip, no navigation. alt+<number> reads naturally as “go to tab N” and avoids clashing with text input.

By default the first declared tab is selected when the strip first renders. Mark a different tab with open = true to have it be the one shown initially instead:

@Tab(value = "Customer", shortcut = "alt+1")
String customer;
@Tab(value = "Items", shortcut = "alt+2", open = true) // ← selected on first render
String items;
@Tab(value = "Billing", shortcut = "alt+3")
String billing;

open is independent of shortcut: the shortcut selects the tab on demand, open only sets the initial selection. If several tabs in the same strip declare open = true, the first one wins. Useful when the most relevant tab isn’t the first — e.g. a detail drawer whose most-used section should be front and center as soon as it opens.

On a long page with a sticky sections index, Ctrl+Alt+1..9 jump to the first nine sections (same as clicking the index entry). This is on by default whenever the index is shown — no extra configuration. The shortcut number is shown as a faint badge on each index entry.

Grid row selection — @OnRowSelected(shortcut = ...)

Section titled “Grid row selection — @OnRowSelected(shortcut = ...)”

Give a grid’s row-selection action a shortcut base so users can select a row by position: the base combo plus a digit selects that row.

@Stereotype(FieldStereotype.grid)
@OnRowSelected(value = "onGuestSelected", shortcut = "ctrl+shift")
List<GuestData> guests;

Ctrl+Shift+1 selects the first row … up to the ninth, exactly as if the row had been clicked (it fires the same @OnRowSelected method, so any master/detail wiring reacts identically).

Shortcuts are matched by physical key (e.code) as well as logical key, so Ctrl+Alt+<letter> and <modifier>+<digit> work regardless of the keyboard layout — even where e.g. a Spanish layout’s AltGr remaps Ctrl+Alt+E to — and the numeric keypad works too. Buttons that carry a shortcut show it in their tooltip.

Consistent conventions across the application reinforce the Consistency principle.

ActionSuggested shortcut
Save / SubmitCtrl+S
Confirm / SendCtrl+Enter
Cancel / CloseEscape
New recordCtrl+N
DeleteCtrl+Delete
Search / FilterEnter (via @Trigger)
  • Keyboard-first — frequent tasks require no mouse gesture
  • Consistency — the same shortcut does the same thing everywhere in the app
  • Workflow over screens — shortcuts let users stay in flow without stopping to navigate