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
BorderCheckbox
KontraKontra

Button

The Button component lets you make your terminal UI interactive — click, tab, or mouse-press your way through clean TUI controls. Use it to perform actions, show feedback, or simply to give your app some life.


Creating a Button

You can create a button with just a label and an onClick callback:

Want to make it pop? Use ButtonStyleBuilder to give it color and personality 👇


Styling Buttons

Style your buttons with ease by defining separate styles for their inactive and active (focused) states.

  • set_inactive_style(...) - sets the full style for the normal state.
  • set_active_style(...) - sets the full style for the focused/hover state.

Each method takes a complete style object created with TextStyleBuilder (from text.hpp).

Now apply the style like this:


Handling Focus + Input

Kontra gives you full control over what's active and clickable by handling abstract InputEvent types.

Switch focus with Tab:

Trigger click with Enter:


Mouse Support (Yup, we got that)

You can even respond to mouse clicks:


Complete Layout Example


TL;DR

  • Use Button to make parts of your terminal interface interactive.
  • Each button has a label, an onClick callback, and customizable styles.
  • Handle keyboard input with abstract events (KEY_ENTER, KEY_PRESS) to manage focus and clicks.
  • Combine Button with Text, List, and Flex to build responsive interactive layouts.

  auto button = std::make_shared<Button>("Click Me!", []() {
    // This code runs when the button is clicked.
    std::cout << "Button was clicked!" << std::endl;
  });


  // 1. Create a style for the button when it's NOT focused (inactive)
  auto inactive_style = TextStyleBuilder()
      .set_color(ansi::FG_WHITE)
      .set_background_color(ansi::BG_BLUE)
      .set_bold(true)
      .build();

  // 2. Create a style for when it IS focused (active)
  auto active_style = TextStyleBuilder()
      .set_color(ansi::FG_BLUE)
      .set_background_color(ansi::BG_BRIGHT_WHITE)
      .set_bold(true)
      .build();

  // 3. Combine them using the ButtonStyleBuilder
  auto button_style = ButtonStyleBuilder()
      .set_inactive_style(inactive_style)
      .set_active_style(active_style)
      .build();


  auto styled_button = std::make_shared<Button>("Styled", []() {
    // ... click logic ...
  }, button_style);


  if (event.type == EventType::KEY_PRESS && event.key == '\t') {
    active_button->set_active(false); // Deactivate the old one
    if (active_button == button1) {
        active_button = button2;
    } else {
        active_button = button1;
    }
    active_button->set_active(true); // Activate the new one
  }


  if (event.type == EventType::KEY_ENTER) {
    active_button->click();
  }


  if (event.type == EventType::MOUSE_PRESS) {
    for (const auto& btn : buttons) {
        if (btn->contains(event.mouse_x, event.mouse_y)) {
            btn->click();
            // Also update the focus to the clicked button
            active_button->set_active(false);
            btn->set_active(true);
            active_button = btn;
            break; // Stop after handling one click
        }
    }
  }


  // State to hold the message
  std::string message = "Click or Tab to test!";

  // A dynamic Text component to show feedback
  auto message_display = std::make_shared<Text>([&](){ return message; });

  // Define a shared style for the buttons
  auto button_style = ButtonStyleBuilder()
      .set_inactive_style(TextStyleBuilder().set_bold(true).build())
      .set_active_style(TextStyleBuilder().set_bold(true).set_background_color(ansi::BG_BRIGHT_BLACK).build())
      .build();

  // Define buttons and their click actions
  auto button1 = std::make_shared<Button>("Set Message 1", [&]() {
    message = "Button 1 Clicked!";
  }, button_style);
  
  auto button2 = std::make_shared<Button>("Set Message 2", [&]() {
    message = "Button 2 Clicked!";
  }, button_style);

  // Manage focus state
  button1->set_active(true);
  std::shared_ptr<Button> active_button = button1;
  std::vector<std::shared_ptr<Button>> buttons = {button1, button2};

  auto layout = std::make_shared<List>(message_display, button1, button2);
  layout->set_gap(1);

  auto screen = std::make_shared<Screen>(std::make_shared<Border>(layout));

  kontra::run(screen, [&](const InputEvent& event) {
      switch (event.type) {
          case EventType::KEY_PRESS:
              if (event.key == '\t') {
                  // Swap focus
                  active_button->set_active(false);
                  active_button = (active_button == button1) ? button2 : button1;
                  active_button->set_active(true);
              }
              break;
          case EventType::KEY_ENTER:
              active_button->click();
              break;
          case EventType::MOUSE_PRESS:
              for (auto& btn : buttons) {
                  if (btn->contains(event.mouse_x, event.mouse_y)) {
                      btn->click();
                      active_button->set_active(false);
                      btn->set_active(true);
                      active_button = btn;
                      break;
                  }
              }
              break;
      }
  });