Configure Navigation Keys
ListViewWidget and TreeViewWidget accept a NavigableKeymap. The default is
vim-like, but apps can provide another map without subclassing widgets.
from lazy_cuh import NavigableKeymapfrom lazy_cuh.widgets import ListViewWidget
keymap = NavigableKeymap( down=("n",), up=("p",), first_prefix=("f",), first=("f",), last=("end",), select=("o",),)
yield ListViewWidget(model, keymap=keymap, id="items")This maps:
nto move downpto move upf fto jump to the first itemendto jump to the last itemoto select the current item
Listen To Actions
Section titled “Listen To Actions”Use ItemActionMessage when the app needs semantic item actions.
from lazy_cuh.widgets import ItemActionMessage, NavigableAction
def on_item_action_message(self, message: ItemActionMessage) -> None: if message.action == NavigableAction.SELECT: self.open_item(message.item_id)ItemSelectedMessage is still emitted for selection compatibility, but
ItemActionMessage is the better fit when an app wants one event shape for
select, expand, collapse, and toggle.
Display Prefixes And Aliases
Section titled “Display Prefixes And Aliases”Action bindings may use symbolic prefixes for configurable leader-style keys.
Resolution and display are separate: KeyPrefixMap expands a symbolic prefix
for input handling, while KeyDisplayMap controls how the same binding appears
in keybars and help text.
from lazy_cuh import ( ActionBinding, ActionId, ActionMap, HintVisibility, KeyDisplayMap, KeyPrefixMap, PrefixedKeySequence, keybar_spec_from_actions,)
actions = ActionMap( ( ActionBinding( (PrefixedKeySequence.of("pane", "h"),), ActionId.SELECT, label="Pane left", visibility=HintVisibility.COMPACT, ), ))
prefixes = KeyPrefixMap.from_mapping({"pane": ("z",)})key_display = KeyDisplayMap(prefixes=prefixes, separator="+")spec = keybar_spec_from_actions(actions, key_display=key_display)That keybar renders Pane left: z+h. Without a configured display map, the
same binding renders symbolically as Pane left: <pane>h.
When multiple keys mean the same thing, keep them on one binding with
ActionBinding.from_keys(...). That produces one help row such as
Left: h/left instead of duplicate rows for the same action.
Count Prefixes Are Scoped
Section titled “Count Prefixes Are Scoped”Count prefixes are opt-in. The framework does not reserve number keys globally.
Use VIM_NAVIGATION_KEYS only for the scopes that should treat leading digits
as counts, and use SIMPLE_KEYS where bare numbers should remain normal
bindings.
from lazy_cuh import ( ActionBinding, ActionId, BindingContext, BindingRegistry, ScopedBinding, SIMPLE_KEYS, VIM_NAVIGATION_KEYS,)
registry = BindingRegistry( bindings=( ScopedBinding( ActionBinding.from_key("1", ActionId.SELECT), context=BindingContext(panel="tabs"), profile=SIMPLE_KEYS, ), ScopedBinding( ActionBinding.from_key("j", ActionId.SELECT), context=BindingContext(widget="tree"), profile=VIM_NAVIGATION_KEYS, ), ))diagnostics = registry.validate()With VIM_NAVIGATION_KEYS, a bare 1 binding reports a warning because it may
conflict with count input such as 10j. Prefix sequences such as z1 are still
valid because the digit is no longer a bare leading count.
Validate Binding Registries
Section titled “Validate Binding Registries”Validation is explicit. The framework does not automatically validate every
registry during app startup. Run BindingRegistry.validate() in tests, CI, or a
development-only startup check, then decide whether warnings should fail your
app.
from lazy_cuh import format_binding_diagnostics
diagnostics = registry.validate()for line in format_binding_diagnostics(diagnostics): print(line)Diagnostics include duplicate bindings, global/local override conflicts, ambiguous sequence prefixes, unknown symbolic prefixes, and bare digit bindings inside count-enabled scopes.
Use collision_policy=CollisionPolicy.WARN when duplicate/override/prefix
diagnostics should be warnings instead of errors. Use allow_override=True on a
local ScopedBinding when a local key intentionally takes over a global key.
Use CollisionPolicy.ALLOW_OVERRIDE only when that is the desired default for
the whole registry.
Keep Domain Meaning Outside Widgets
Section titled “Keep Domain Meaning Outside Widgets”The widget keymap decides how to navigate and which UI action happened. It does not decide what selecting an item means in your app. App controllers should translate item actions into commands when side effects are needed.