Skip to content

Events

Vextr routes input events through the widget tree. The focused widget gets each event first. If it doesn’t handle it, the event bubbles up to its parent, then its grandparent, all the way to the root.

User presses Ctrl+S
│
▼
TextArea (focused) ── returns false ──▶ not handled
│
▼
Container ── has addEventListener ──▶ handles it, returns true
│
stop

This means you can put app-wide shortcuts on the root container and they’ll fire whenever nothing else claims the event first.

root->addEventListener([&](const events::Event& e) -> bool {
if (auto* k = std::get_if<events::KeyEvent>(&e)) {
if (k->modifiers.ctrl && k->key == 's') {
save();
return true; // handled - stop bubbling
}
}
return false; // not handled - keep bubbling
});

Return true to consume the event. Return false to let it continue up the tree. You can register multiple listeners on the same widget - they run in registration order.

Fired on every key press.

if (auto* k = std::get_if<events::KeyEvent>(&e)) {
k->key; // key code - ASCII or Key:: constant
k->modifiers.ctrl; // Ctrl held
k->modifiers.shift; // Shift held
k->modifiers.alt; // Alt held
}

For printable characters, key is the ASCII code - 'a', '1', ' ', etc. For special keys, use the constants from vextr::utils::Key:

ConstantKey
Key::EnterEnter
Key::EscapeEscape
Key::BackspaceBackspace
Key::TabTab
Key::ShiftTabShift+Tab
Key::UpArrow Up
Key::DownArrow Down
Key::LeftArrow Left
Key::RightArrow Right
Key::PageUpPage Up
Key::PageDownPage Down
Key::HomeHome
Key::EndEnd
Key::DeleteDelete
Key::InsertInsert
Key::F1–Key::F12Function keys

Fired when the terminal is resized. Vextr re-layouts automatically, but you can listen to this if you need to react manually.

if (auto* r = std::get_if<events::ResizeEvent>(&e)) {
r->width; // new terminal width in columns
r->height; // new terminal height in rows
}

MousePressEvent, MouseReleaseEvent, and MouseMoveEvent are available in the event variant but not yet dispatched by the framework.

If you’re building a custom widget, override onEvent() directly instead of using addEventListener. Always call Widget::onEvent(event) first so any listeners attached to your widget still fire.

bool MyWidget::onEvent(const events::Event& event) {
if (Widget::onEvent(event)) return true; // listeners first
if (auto* k = std::get_if<events::KeyEvent>(&event)) {
if (k->key == Key::Enter) {
activate();
return true;
}
if (k->key == Key::Escape) {
cancel();
return true;
}
}
return false; // not handled
}

If you want to handle multiple event types without chaining get_if:

root->addEventListener([](const events::Event& e) -> bool {
return std::visit(events::overloaded{
[](const events::KeyEvent& k) -> bool {
// handle key
return false;
},
[](const events::ResizeEvent& r) -> bool {
// handle resize
return false;
},
[](const auto&) -> bool {
return false; // catch-all
}
}, e);
});