FTXUI
A modern C++ library for building TUIs in a functional and declarative style. Component-based design similar to React, header-only with no dependencies.
GitHub Overview
ArthurSonzogni/FTXUI
:computer: C++ Functional Terminal User Interface. :heart:
Topics
Star History
FTXUI
FTXUI is a modern C++ library for building Terminal User Interfaces (TUI) in a functional and declarative style. Adopting a React-like component-based design, it features header-only implementation with no external dependencies. It enables efficient development of beautiful and interactive terminal applications.
Features
Functional & Reactive Programming
- Declarative UI: React-like declarative approach
- Component-Based: Reusable UI components
- State Management: Automatic UI updates and event handling
- Functional Paradigm: Pure functions and immutable data
Architecture
- screen: Final rendering to terminal
- dom: Element tree defining UI structure
- component: Interactive elements and event handling
- animation: Smooth animation capabilities
Rich Components
- Input: Input, Checkbox, Radiobox, Slider
- Selection: Menu, Dropdown, Toggle
- Layout: Container, Scroller, Collapsible
- Display: Gauge, Spinner, Table
Modern Features
- Header-Only: Easy integration, no dependencies
- C++17 Support: Leverages modern C++ features
- Unicode Support: Emoji and internationalization support
- Mouse Support: Click, drag, and wheel events
Basic Usage
Installation
# Using CMake
git clone https://github.com/ArthurSonzogni/FTXUI
mkdir build && cd build
cmake ..
make -j
# Using vcpkg
vcpkg install ftxui
# Compilation example
g++ -std=c++17 main.cpp -lftxui-screen -lftxui-dom -lftxui-component
Hello World
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/screen.hpp>
#include <iostream>
using namespace ftxui;
int main() {
// Create DOM elements
auto document = hbox({
text("Hello"),
text(" "),
text("World!") | color(Color::Cyan) | bold,
}) | border;
// Render to screen
auto screen = Screen::Create(
Dimension::Fixed(20),
Dimension::Fixed(5)
);
Render(screen, document);
screen.Print();
return 0;
}
Interactive Application
#include <ftxui/component/captured_mouse.hpp>
#include <ftxui/component/component.hpp>
#include <ftxui/component/component_base.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
using namespace ftxui;
int main() {
// State variables
std::string input_value;
int slider_value = 50;
bool checkbox_checked = false;
int menu_selected = 0;
// Create components
auto input = Input(&input_value, "Enter name...");
auto slider = Slider("Volume: ", &slider_value, 0, 100, 1);
auto checkbox = Checkbox("Enable notifications", &checkbox_checked);
std::vector<std::string> menu_items = {
"New",
"Open",
"Save",
"Exit"
};
auto menu = Menu(&menu_items, &menu_selected);
// Layout
auto container = Container::Vertical({
input,
slider,
checkbox,
menu,
});
// Renderer
auto renderer = Renderer(container, [&] {
return vbox({
text("Settings") | bold | center,
separator(),
hbox({text("Name: "), input->Render()}),
slider->Render(),
checkbox->Render(),
separator(),
text("Menu:"),
menu->Render() | frame | size(HEIGHT, LESS_THAN, 10),
separator(),
text("Input: " + input_value),
text("Slider: " + std::to_string(slider_value)),
}) | border;
});
auto screen = ScreenInteractive::TerminalOutput();
screen.Loop(renderer);
return 0;
}
Custom Components
#include <ftxui/component/component.hpp>
#include <ftxui/component/component_base.hpp>
#include <ftxui/dom/elements.hpp>
using namespace ftxui;
// Custom button component
class CustomButton : public ComponentBase {
private:
std::string label_;
std::function<void()> on_click_;
bool hovered_ = false;
public:
CustomButton(const std::string& label, std::function<void()> on_click)
: label_(label), on_click_(on_click) {}
Element Render() override {
auto style = hovered_ ? inverted : nothing;
return text("[ " + label_ + " ]") | style | reflect(box_);
}
bool OnEvent(Event event) override {
if (event.is_mouse()) {
auto mouse = event.mouse();
hovered_ = box_.Contain(mouse.x, mouse.y);
if (mouse.button == Mouse::Left &&
mouse.motion == Mouse::Released &&
hovered_) {
on_click_();
return true;
}
}
if (event == Event::Return && Focused()) {
on_click_();
return true;
}
return false;
}
bool Focusable() const override { return true; }
private:
Box box_;
};
// Usage example
int main() {
int counter = 0;
auto button = Make<CustomButton>("Click me!", [&] {
counter++;
});
auto renderer = Renderer(button, [&] {
return vbox({
text("Counter: " + std::to_string(counter)) | center,
separator(),
button->Render() | center,
}) | border | size(WIDTH, EQUAL, 30);
});
auto screen = ScreenInteractive::TerminalOutput();
screen.Loop(renderer);
return 0;
}
Animation
#include <ftxui/component/animation.hpp>
#include <ftxui/component/component.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
#include <chrono>
using namespace ftxui;
using namespace std::chrono_literals;
int main() {
// Animation variables
auto duration = 2s;
auto start_time = std::chrono::steady_clock::now();
auto component = Renderer([&] {
auto now = std::chrono::steady_clock::now();
auto elapsed = now - start_time;
float progress = std::chrono::duration<float>(elapsed).count() /
std::chrono::duration<float>(duration).count();
progress = std::min(1.0f, progress);
// Easing function
auto ease_in_out = [](float t) {
return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
};
float animated_value = ease_in_out(progress);
int position = static_cast<int>(animated_value * 40);
return vbox({
text("Animation Demo") | bold | center,
separator(),
hbox({
text(std::string(position, ' ')),
text("●") | color(Color::Red),
}),
separator(),
gauge(progress) | color(Color::Blue),
text("Progress: " + std::to_string(int(progress * 100)) + "%") | center,
}) | border;
});
auto screen = ScreenInteractive::TerminalOutput();
// Animation loop
std::atomic<bool> refresh_ui_continue = true;
std::thread refresh_ui([&] {
while (refresh_ui_continue) {
std::this_thread::sleep_for(16ms);
screen.PostEvent(Event::Custom);
}
});
screen.Loop(component);
refresh_ui_continue = false;
refresh_ui.join();
return 0;
}
Advanced Features
Flexbox Layout
// Flexible layout using Flexbox
auto layout = flexbox({
{
text("Left") | border,
FlexboxConfig().Set(FlexboxConfig::Direction::Column)
.Set(FlexboxConfig::Wrap::NoWrap)
.Set(FlexboxConfig::JustifyContent::Center)
},
{
text("Center") | border | flex,
FlexboxConfig().Set(FlexboxConfig::AlignItems::Center)
},
{
text("Right") | border,
FlexboxConfig().Set(FlexboxConfig::Basis, 20)
},
});
Canvas Element
// Drawing with Canvas
auto canvas_element = canvas([](Canvas& c) {
// High resolution drawing using Braille characters
c.DrawText(10, 5, "Canvas Demo");
c.DrawLine(0, 0, 40, 20);
c.DrawCircle(20, 10, 5);
// Using block characters
c.DrawBlock(30, 15, true);
});
Modal Dialog
class ModalComponent : public ComponentBase {
private:
bool show_modal_ = false;
Component container_;
public:
ModalComponent() {
auto modal_content = Renderer([&] {
return vbox({
text("Confirm") | bold | center,
separator(),
text("Are you sure you want to exit?"),
separator(),
hbox({
button("Yes", [&] {
show_modal_ = false;
// Exit logic
}),
text(" "),
button("No", [&] {
show_modal_ = false;
}),
}) | center,
}) | border | center;
});
auto main_content = button("Exit", [&] {
show_modal_ = true;
});
container_ = Container::Tab(
{main_content, modal_content},
&show_modal_
);
}
Element Render() override {
return container_->Render();
}
bool OnEvent(Event event) override {
return container_->OnEvent(event);
}
};
Ecosystem
Related Projects
- ftxui-starter: Project template
- json-tui: JSON viewer
- git-tui: Git client
- spotify-tui: Spotify client
Build Systems
- CMake: Native support
- vcpkg: Package manager
- Conan: Package manager
- Bazel: Build system
Advantages
- Modern Design: Functional & reactive programming
- No Dependencies: Header-only for easy integration
- Rich Features: Diverse components and layouts
- High Performance: Efficient rendering
- Cross-Platform: Windows, Linux, macOS support
Limitations
- C++17 Required: Cannot use with older compilers
- Learning Curve: Understanding functional paradigm required
- Debugging: TUI app debugging is complex
- Documentation: Limited non-English resources
Comparison with Other Libraries
Feature | FTXUI | ncurses | ImTui |
---|---|---|---|
Paradigm | Functional | Imperative | Immediate Mode |
Dependencies | None | System | ncurses |
Learning Cost | Medium | High | Low |
Modernity | Very High | Low | High |
Features | Rich | Basic | Moderate |
Summary
FTXUI is an excellent library for developing TUI applications in modern C++. Its functional and reactive programming approach allows declarative construction of complex UIs, resulting in highly maintainable code. Being header-only with no dependencies makes project integration straightforward. It's particularly approachable for developers with web frontend experience, enabling efficient development of beautiful and interactive terminal applications.