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.
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 👇
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:
Kontra gives you full control over what's active and clickable by handling abstract InputEvent
types.
Tab
:Enter
:You can even respond to mouse clicks:
Button
to make parts of your terminal interface interactive.KEY_ENTER
, KEY_PRESS
) to manage focus and clicks.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;
}
});