Key bindings
architect-input's keymap engine (chords, sequences, modes, contexts, held-letter tools, MIDI and OSC, which-key), the person's keymap over the app's, and the binding editor.
What it is and when to use it#
architect-input turns input into Action List ids. A KeymapConfig (JSON)
binds keys, sequences (g s), modes, mouse gestures, scroll, MIDI and OSC to
action ids, globally or in a named context (session.arrange) where the
innermost context wins. InputProcessor runs events through it and returns
InputCommands; it also keeps the which-key model (what the keys typed so
far can lead to), timeouts for a pending sequence, and held-letter tools
(Z held while dragging). It is a pure engine: no UI, wasm-clean.
architect-commands layers the person’s keymap over the app’s: shortcuts
declared on actions are bindings, the person rebinds in the action list
window or the binding editor, and their layer syncs between devices.
Design: Input and
Actions everywhere.
A minimal example#
The engine on its own, after features/input/tests/processor.rs:
use architect_input::{InputCommand, InputContext, InputProcessor, KeymapConfig, MacroKeys};
let mut keys = InputProcessor::from_config(KeymapConfig::from_json_str(r#"{
"keymap": { "normal": { "Ctrl+s": "FILE_SAVE" } },
"contexts": { "session.arrange": { "keys": { "Ctrl+d": "ARRANGE_DUPLICATE" } } }
}"#)?)?
.with_macro_keys(MacroKeys::Off); // not a vim app
let mut ctx = InputContext::new();
ctx.set_context("session.arrange");
for command in keys.process(event, &ctx) {
if let InputCommand::Action(id) = command {
let _ = list.run(&id);
}
}
In a Dioxus app the commands layer does the dispatch (from
features/commands/tests/keys.rs):
let _ = use_commands_provider();
use_action_list(use_hook(actions));
use_keymap(
|| KeymapConfig::from_json_str(APP_KEYMAP).unwrap(),
PersonKeymap::default, // or the person's kept layer
Some(Callback::new(|person: PersonKeymap| keep_and_sync(person.to_json()))),
);
// Desk app: rsx! { WindowKeys {} } sends window chords to Commands::handle_chord
Code tour#
features/input/src/config.rsKeymapConfig:from_json_str,merge.features/input/src/processor.rsInputProcessor:from_config,process,sequence_timeoutandexpire_sequence,notify_key_release,held_tool,which_key,with_macro_keys.features/input/src/context.rsInputContext:set_context(dotted names, innermost wins),set_tag.features/input/src/command.rsInputCommand(Action,Gesture,Hold,Pending) andfeatures/input/src/which_key.rsWhichKey.features/input/src/dioxus/hook.rsuse_input_processor,InputHandle: the engine as a hook, callable from a listener outside the Dioxus runtime.features/commands/src/keymap.rsKeymap(bind,unbind,conflicts,what_does,reset),PersonKeymap.features/commands/src/keys_ui.rsuse_keymap,Commands::set_context,Commands::set_person_keymap,Commands::edit_keymap; the componentsBindingEditor,KeymapTools,WhichKeyPanel.features/commands/src/ui.rsCommands::handle_chordandfeatures/commands/src/native.rsWindowKeys.
A keydown in the window reaches WindowKeys, which calls
Commands::handle_chord("⌘W"). That feeds an InputProcessor built from
Keymap::config() (the app’s layer plus the person’s) in the live
InputContext. It returns InputCommand::Action(id), and Commands::run
runs the id. When the person rebinds, edit_keymap changes their layer and
calls use_keymap’s callback with the new PersonKeymap to keep and sync;
another device’s change comes back through set_person_keymap.
Tests to read#
features/input/tests/processor.rs: sequences, modes, counts and operators, which-key, release tickets, contexts, held tools, gestures, MIDI and OSC.features/input/tests/hook.rs(featuredioxus): the handle driven from outside the runtime.features/commands/tests/keys.rs(featureui): a declared shortcut is the binding; rebinding; a change from another device; a sequence; a context; the heldZtool; a custom action.features/commands/tests/commands.rs: conflicts,what_doesper context, resetting the person’s layer.
Status#
Done in architect. Not yet: which-key’s pause, keymap import and export,
importing REAPER’s reaper-kb.ini. The sources of MIDI and OSC events are
the app’s. A host that sees key releases reports them (notify_key_release),
or a repeated key reads as auto-repeat.
- Task uses the engine (
task:crates/ui/src/shortcuts.rs, withuse_input_processor); still to adopt:architect-commands’ person keymap and binding editor. - Studio still runs its own input crate (
studio:features/input), where this engine came from; moving toarchitect-inputis Studio’s work.