FTXUI

関数型・宣言的なスタイルでTUIを構築するモダンなC++ライブラリ。Reactに似たコンポーネントベースの設計で、ヘッダーオンリーで依存関係なし。

TUITerminalFunctionalReactiveC++

GitHub概要

ArthurSonzogni/FTXUI

:computer: C++ Functional Terminal User Interface. :heart:

スター8,531
ウォッチ81
フォーク490
作成日:2019年1月27日
言語:C++
ライセンス:MIT License

トピックス

arthursonzogniasciiascii-artcppcursesimpleterminalterminal-basedtuiuiuser-interfacexterm

スター履歴

ArthurSonzogni/FTXUI Star History
データ取得日時: 2025/7/25 11:08

FTXUI

FTXUIは、関数型かつ宣言的なスタイルでターミナルユーザーインターフェース(TUI)を構築するためのモダンなC++ライブラリです。Reactのようなコンポーネントベースの設計を採用し、ヘッダーオンリーで外部依存なしという特徴を持ちます。美しく、インタラクティブなターミナルアプリケーションを効率的に開発できます。

特徴

関数型・リアクティブプログラミング

  • 宣言的UI: Reactライクな宣言的アプローチ
  • コンポーネントベース: 再利用可能なUIコンポーネント
  • 状態管理: 自動的なUI更新とイベント処理
  • 関数型パラダイム: 純粋関数とイミュータブルなデータ

アーキテクチャ

  • screen: ターミナルへの最終レンダリング
  • dom: UI構造を定義するElementツリー
  • component: インタラクティブ要素とイベント処理
  • animation: スムーズなアニメーション機能

豊富なコンポーネント

  • 入力: Input、Checkbox、Radiobox、Slider
  • 選択: Menu、Dropdown、Toggle
  • レイアウト: Container、Scroller、Collapsible
  • 表示: Gauge、Spinner、Table

モダンな機能

  • ヘッダーオンリー: 簡単な統合、依存関係なし
  • C++17対応: モダンC++の機能を活用
  • Unicode対応: 絵文字と国際化サポート
  • マウス対応: クリック、ドラッグ、ホイールイベント

基本的な使用方法

インストール

# CMakeを使用
git clone https://github.com/ArthurSonzogni/FTXUI
mkdir build && cd build
cmake ..
make -j

# vcpkgを使用
vcpkg install ftxui

# コンパイル例
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() {
  // DOM要素の作成
  auto document = hbox({
    text("Hello"),
    text(" "),
    text("World!") | color(Color::Cyan) | bold,
  }) | border;

  // スクリーンへのレンダリング
  auto screen = Screen::Create(
    Dimension::Fixed(20),
    Dimension::Fixed(5)
  );
  
  Render(screen, document);
  screen.Print();
  
  return 0;
}

インタラクティブなアプリケーション

#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() {
  // 状態変数
  std::string input_value;
  int slider_value = 50;
  bool checkbox_checked = false;
  int menu_selected = 0;
  
  // コンポーネントの作成
  auto input = Input(&input_value, "名前を入力...");
  auto slider = Slider("音量: ", &slider_value, 0, 100, 1);
  auto checkbox = Checkbox("通知を有効にする", &checkbox_checked);
  
  std::vector<std::string> menu_items = {
    "新規作成",
    "開く",
    "保存",
    "終了"
  };
  auto menu = Menu(&menu_items, &menu_selected);
  
  // レイアウト
  auto container = Container::Vertical({
    input,
    slider,
    checkbox,
    menu,
  });
  
  // レンダラー
  auto renderer = Renderer(container, [&] {
    return vbox({
      text("設定画面") | bold | center,
      separator(),
      hbox({text("名前: "), input->Render()}),
      slider->Render(),
      checkbox->Render(),
      separator(),
      text("メニュー:"),
      menu->Render() | frame | size(HEIGHT, LESS_THAN, 10),
      separator(),
      text("入力値: " + input_value),
      text("スライダー: " + std::to_string(slider_value)),
    }) | border;
  });
  
  auto screen = ScreenInteractive::TerminalOutput();
  screen.Loop(renderer);
  
  return 0;
}

カスタムコンポーネント

#include <ftxui/component/component.hpp>
#include <ftxui/component/component_base.hpp>
#include <ftxui/dom/elements.hpp>

using namespace ftxui;

// カスタムボタンコンポーネント
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_;
};

// 使用例
int main() {
  int counter = 0;
  
  auto button = Make<CustomButton>("クリック!", [&] {
    counter++;
  });
  
  auto renderer = Renderer(button, [&] {
    return vbox({
      text("カウンター: " + std::to_string(counter)) | center,
      separator(),
      button->Render() | center,
    }) | border | size(WIDTH, EQUAL, 30);
  });
  
  auto screen = ScreenInteractive::TerminalOutput();
  screen.Loop(renderer);
  
  return 0;
}

アニメーション

#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() {
  // アニメーション変数
  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);
    
    // イージング関数
    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("アニメーションデモ") | bold | center,
      separator(),
      hbox({
        text(std::string(position, ' ')),
        text("●") | color(Color::Red),
      }),
      separator(),
      gauge(progress) | color(Color::Blue),
      text("進行: " + std::to_string(int(progress * 100)) + "%") | center,
    }) | border;
  });
  
  auto screen = ScreenInteractive::TerminalOutput();
  
  // アニメーションループ
  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;
}

高度な機能

Flexboxレイアウト

// Flexboxを使用した柔軟なレイアウト
auto layout = flexbox({
  {
    text("左側") | border,
    FlexboxConfig().Set(FlexboxConfig::Direction::Column)
                   .Set(FlexboxConfig::Wrap::NoWrap)
                   .Set(FlexboxConfig::JustifyContent::Center)
  },
  {
    text("中央") | border | flex,
    FlexboxConfig().Set(FlexboxConfig::AlignItems::Center)
  },
  {
    text("右側") | border,
    FlexboxConfig().Set(FlexboxConfig::Basis, 20)
  },
});

Canvas要素

// Canvasを使用した描画
auto canvas_element = canvas([](Canvas& c) {
  // ブレイル文字を使用した高解像度描画
  c.DrawText(10, 5, "Canvas Demo");
  c.DrawLine(0, 0, 40, 20);
  c.DrawCircle(20, 10, 5);
  
  // ブロック文字を使用
  c.DrawBlock(30, 15, true);
});

モーダルダイアログ

class ModalComponent : public ComponentBase {
private:
  bool show_modal_ = false;
  Component container_;
  
public:
  ModalComponent() {
    auto modal_content = Renderer([&] {
      return vbox({
        text("確認") | bold | center,
        separator(),
        text("本当に終了しますか?"),
        separator(),
        hbox({
          button("はい", [&] { 
            show_modal_ = false;
            // 終了処理
          }),
          text(" "),
          button("いいえ", [&] { 
            show_modal_ = false; 
          }),
        }) | center,
      }) | border | center;
    });
    
    auto main_content = button("終了", [&] {
      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);
  }
};

エコシステム

関連プロジェクト

  • ftxui-starter: プロジェクトテンプレート
  • json-tui: JSONビューア
  • git-tui: Gitクライアント
  • spotify-tui: Spotifyクライアント

ビルドシステム

  • CMake: ネイティブサポート
  • vcpkg: パッケージマネージャー
  • Conan: パッケージマネージャー
  • Bazel: ビルドシステム

利点

  • モダンな設計: 関数型・リアクティブプログラミング
  • 依存関係なし: ヘッダーオンリーで簡単統合
  • 豊富な機能: 多様なコンポーネントとレイアウト
  • 高パフォーマンス: 効率的なレンダリング
  • クロスプラットフォーム: Windows、Linux、macOS対応

制約事項

  • C++17必須: 古いコンパイラでは使用不可
  • 学習曲線: 関数型パラダイムの理解が必要
  • デバッグ: TUIアプリのデバッグは複雑
  • ドキュメント: 日本語資料が少ない

他のライブラリとの比較

項目FTXUIncursesImTui
パラダイム関数型命令型即時モード
依存関係なしシステムncurses
学習コスト
モダンさ非常に高
機能豊富基本的中程度

まとめ

FTXUIは、モダンなC++でTUIアプリケーションを開発するための優れたライブラリです。関数型・リアクティブプログラミングのアプローチにより、複雑なUIを宣言的に構築でき、保守性の高いコードを実現します。ヘッダーオンリーで依存関係がないため、プロジェクトへの統合も簡単です。Webフロントエンド開発の経験がある開発者には特に親しみやすく、美しくインタラクティブなターミナルアプリケーションを効率的に開発できます。