KontraKontra
    DocumentationAbout
Docs
  • Getting Started
  • Installation
  • Hello World
  • Ansi
  • Todo App
  • Event Handling
  • Style Builder
  • Components
  • Border
  • Button
  • Checkbox
  • Flex
  • Input
  • List
  • Radio Buttons
  • Tabs
  • Text
Todo AppStyle Builder
KontraKontra

๐ŸŽฎ Event Handling in Kontra TUI

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.


๐Ÿง  How It Works

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
};

๐ŸŽ›๏ธ Abstract Event Types

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.


Keyboard Input Example

Handle navigation with arrow keys and actions with Enter/Escape.

Mouse Click Example

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 Example

Scroll vertically with the mouse wheel. This works best with scrollable components like List.


๐Ÿคน Combining Input Modes

You can manage complex states like editing vs. navigating by checking an AppMode enum first.


๐Ÿ“š Best Practices

  • โœ… Use Abstract Events: Always check for EventType like KEY_ENTER instead of raw numbers like 13.
  • โœ… Keep Logic State-Driven: Change state variables (AppMode, selected_index), then call an update function.
  • โœ… Delegate: Let container components (like RadioGroup or a smart Input form) handle their own internal events when possible.
  • โœ… Use contains(x, y) to detect mouse clicks on any component.

TL;DR

  • Use InputEvent inside kontra::run() for all interaction.
  • Handle specific actions with EventType::KEY_ENTER, KEY_UP, etc.
  • Handle printable characters with EventType::KEY_PRESS and event.key.
  • Handle clicks with event.mouse_x/y and .contains(...).
  • Support scroll with MOUSE_SCROLL_UP / DOWN.
  • Split input logic based on app mode or active components.

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;
          }
      }
  });