Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Actions & Keybindings

Kael separates what happens (an action) from how it’s triggered (a keybinding or click). Actions are dispatched up the focused element tree, so a keystroke is routed to the nearest handler in the currently focused context — the same model that powers editor-grade keyboard UX.

Defining actions

The actions! macro generates zero-field action types in a namespace:

#![allow(unused)]
fn main() {
use kael::actions;

actions!(editor, [Save, Undo, Redo, Tab, TabPrev]);
}

Each entry becomes a type (Save, Undo, …) implementing the Action trait, with a stable name like editor::Save used for keymaps and dispatch.

Binding keys

Register bindings once at startup with cx.bind_keys. KeyBinding::new takes the keystroke string, the action, and an optional key context that scopes the binding:

#![allow(unused)]
fn main() {
use kael::{Application, App, KeyBinding};

Application::new().run(|cx: &mut App| {
    cx.bind_keys([
        KeyBinding::new("cmd-s", Save, None),
        KeyBinding::new("cmd-z", Undo, None),
        KeyBinding::new("cmd-shift-z", Redo, None),
        KeyBinding::new("tab", Tab, Some("Editor")),       // only in the "Editor" context
        KeyBinding::new("shift-tab", TabPrev, Some("Editor")),
    ]);
    // ... open windows ...
});
}

Keystroke syntax uses cmd / ctrl / alt / shift modifiers joined with -, and a space separates multi-key sequences (e.g. "cmd-k cmd-s"). Use cmd on macOS and ctrl on Windows/Linux.

Handling actions

In render, mark the element that owns a focus context with track_focus, then register handlers with on_action(cx.listener(...)). Handlers take &mut self, a reference to the action, the window, and the context:

#![allow(unused)]
fn main() {
use kael::{div, prelude::*, Context, FocusHandle, Render, Window};

struct Editor { focus_handle: FocusHandle }

impl Editor {
    fn on_save(&mut self, _: &Save, _window: &mut Window, cx: &mut Context<Self>) {
        // ... persist ...
        cx.notify();
    }
}

impl Render for Editor {
    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .track_focus(&self.focus_handle)
            .on_action(cx.listener(Self::on_save))
            .on_action(cx.listener(Self::on_undo))
            .child("editor surface")
    }
}
}

Focus & tab order

Create focus handles from the context and arrange tab order with tab_index / tab_stop. Move focus from the window:

#![allow(unused)]
fn main() {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
    let items = vec![
        cx.focus_handle().tab_index(1).tab_stop(true),
        cx.focus_handle().tab_index(2).tab_stop(true),
        cx.focus_handle().tab_index(3).tab_stop(true),
    ];
    let focus_handle = cx.focus_handle();
    window.focus(&focus_handle);
    Self { focus_handle, items }
}
}

Window focus methods: window.focus(&handle), window.focus_next() (Tab), and window.focus_prev() (Shift-Tab). Query state with handle.is_focused(window) and style focused elements with .focus(|s| s.border_color(...)).

See examples/tab_stop.rs for a complete focus-navigation demo, and crates/kael/docs/key_dispatch.md for the dispatch internals.