Kontra TUI uses a unified event system to handle keyboard, mouse, and scroll interactions through a single, clean interface.
You handle all events inside the kontra::run(...)
loop. This gives you complete control over your application's interactivity.
Kontra's main event loop provides an InputEvent
object every time the user interacts with the terminal.
kontra::run(screen, [&](const InputEvent& event) {
// Handle the event here
switch (event.type) {
// ... cases for different event types ...
}
});
The InputEvent
struct abstracts away platform differences:
// core/event.hpp
struct InputEvent {
EventType type; // The kind of event that occurred
char key; // The character, ONLY valid for EventType::KEY_PRESS
int mouse_x; // X coordinate (column) for mouse events
int mouse_y; // Y coordinate (row) for mouse events
};
Instead of checking for raw ASCII codes, you should handle these abstract event types. This makes your code readable and cross-platform.
Here are the main event types supported by InputEvent
. Your application logic should check event.type
to decide how to react.
EventType::KEY_PRESS
Triggered by any printable character (e.g., a
, b
, 1
, 2
, !
, ?
). The character itself is stored in event.key
.
EventType::KEY_ENTER
Triggered by the Enter
key.
EventType::KEY_BACKSPACE
Triggered by the Backspace
key.
EventType::KEY_ESCAPE
Triggered by the Esc
key.
EventType::KEY_UP
/ KEY_DOWN
Triggered by the Up
and Down
arrow keys, respectively.
EventType::KEY_LEFT
/ KEY_RIGHT
Triggered by the Left
and Right
arrow keys, respectively.
EventType::MOUSE_PRESS
Triggered by a left mouse click. The coordinates are stored in event.mouse_x
and event.mouse_y
.
EventType::MOUSE_SCROLL_UP
Triggered when the mouse wheel is scrolled up.
EventType::MOUSE_SCROLL_DOWN
Triggered when the mouse wheel is scrolled down.
Handle navigation with arrow keys and actions with Enter/Escape.
Detect if the user clicked on a button or component.
๐ Tip: All Kontra components inherit a
.contains(x, y)
method to check if a coordinate lies within their last rendered position.
Scroll vertically with the mouse wheel. This works best with scrollable components like List
.
You can manage complex states like editing vs. navigating by checking an AppMode
enum first.
EventType
like KEY_ENTER
instead of raw numbers like 13
.AppMode
, selected_index
), then call an update function.RadioGroup
or a smart Input
form) handle their own internal events when possible.contains(x, y)
to detect mouse clicks on any component.InputEvent
inside kontra::run()
for all interaction.EventType::KEY_ENTER
, KEY_UP
, etc.EventType::KEY_PRESS
and event.key
.event.mouse_x/y
and .contains(...)
.MOUSE_SCROLL_UP
/ DOWN
.Now go build something awesome and interactive โจ
kontra::run(screen, [&](const InputEvent& event) {
switch (event.type) {
case EventType::KEY_UP:
selected_index--;
update_ui();
break;
case EventType::KEY_DOWN:
selected_index++;
update_ui();
break;
case EventType::KEY_ENTER:
handle_selection();
break;
case EventType::KEY_ESCAPE:
cancel_input();
break;
case EventType::KEY_PRESS:
// Handle printable characters for an input box
if (event.key == 'i') {
current_mode = AppMode::Editing;
update_ui();
}
break;
}
});
if (event.type == EventType::MOUSE_PRESS) {
// Iterate through all clickable components
for (const auto& btn : buttons) {
// Use the .contains() method to check for a hit
if (btn->contains(event.mouse_x, event.mouse_y)) {
btn->click();
// Optional: update focus to the clicked button
active_button = btn;
break; // Stop checking once a button is found
}
}
}
kontra::run(screen, [&](const InputEvent& event) {
switch (event.type) {
case EventType::MOUSE_SCROLL_UP:
main_list->scroll_up();
break;
case EventType::MOUSE_SCROLL_DOWN:
main_list->scroll_down();
break;
}
});
kontra::run(screen, [&](const InputEvent& event) {
if (current_mode == AppMode::Editing) {
// Handle events differently in editing mode
switch (event.type) {
case EventType::KEY_ENTER:
add_task();
break;
case EventType::KEY_ESCAPE:
current_mode = AppMode::Navigating;
update_ui();
break;
default:
// Pass all other events to the input box
input_box->handle_event(event);
break;
}
} else { // Navigation Mode
switch (event.type) {
case EventType::KEY_UP: navigate_up(); break;
case EventType::KEY_DOWN: navigate_down(); break;
case EventType::KEY_PRESS:
if (event.key == 'i') {
current_mode = AppMode::Editing;
update_ui();
}
break;
case EventType::MOUSE_PRESS:
// ... handle mouse clicks for navigation ...
break;
}
}
});