Cursive

TUITerminalViewsWidgetsHigh-level

GitHub概要

gyscos/cursive

A Text User Interface library for the Rust programming language

スター4,576
ウォッチ37
フォーク256
作成日:2015年5月9日
言語:Rust
ライセンス:MIT License

トピックス

ncursesrustterminaltui

スター履歴

gyscos/cursive Star History
データ取得日時: 2025/7/25 11:09

Cursive

Cursiveは、Rust向けの高レベルTUIフレームワークです。リテインドモードのアーキテクチャを採用し、ビューベースの直感的なAPIで複雑なターミナルアプリケーションを簡単に構築できます。使いやすさを重視しながらも、柔軟性を保つ設計が特徴です。

特徴

リテインドモードアーキテクチャ

  • ビューシステム: 階層的なビュー構造
  • イベントループ: フレームワークが管理
  • 状態管理: ビューが内部状態を保持
  • 自動レイアウト: 柔軟なレイアウトシステム

宣言的UI設計

  • コールバック駆動: イベントに対する反応を定義
  • 構造の定義: ビューの階層を明確に記述
  • フォーカス管理: 自動的なフォーカス処理
  • 入力処理: キーボード・マウス入力の抽象化

豊富なビューコンポーネント

  • 基本ビュー: TextView、EditView、Button、Checkbox
  • コンテナ: LinearLayout、StackView、Dialog
  • リスト系: ListView、SelectView、MenuPopup
  • 特殊ビュー: ProgressBar、SliderView、PaddedView

柔軟なバックエンド

  • crossterm: クロスプラットフォーム対応
  • termion: Unix系システム向け
  • pancurses: ncurses/pdcursesラッパー
  • ダミーバックエンド: テスト用

基本的な使用方法

インストール

[dependencies]
cursive = "0.21"

Hello World

use cursive::views::{Dialog, TextView};

fn main() {
    // Cursiveルートを作成
    let mut siv = cursive::default();

    // ダイアログを作成
    siv.add_layer(Dialog::around(TextView::new("Hello World!"))
        .title("My App")
        .button("Quit", |s| s.quit()));

    // イベントループを実行
    siv.run();
}

入力フォーム

use cursive::views::{Dialog, EditView, LinearLayout, TextView};
use cursive::traits::*;

fn main() {
    let mut siv = cursive::default();

    siv.add_layer(
        Dialog::new()
            .title("User Form")
            .content(
                LinearLayout::vertical()
                    .child(TextView::new("Name:"))
                    .child(EditView::new()
                        .on_submit(submit_name)
                        .with_name("name")
                        .fixed_width(20))
                    .child(TextView::new("Email:"))
                    .child(EditView::new()
                        .with_name("email")
                        .fixed_width(20))
            )
            .button("Submit", |s| {
                let name = s.call_on_name("name", |view: &mut EditView| {
                    view.get_content()
                }).unwrap();
                let email = s.call_on_name("email", |view: &mut EditView| {
                    view.get_content()
                }).unwrap();
                
                s.add_layer(
                    Dialog::text(format!("Name: {}\nEmail: {}", name, email))
                        .title("Submitted")
                        .button("Ok", |s| s.pop_layer())
                );
            })
            .button("Cancel", |s| s.quit())
    );

    siv.run();
}

fn submit_name(s: &mut cursive::Cursive, name: &str) {
    s.add_layer(
        Dialog::text(format!("Hello, {}!", name))
            .button("Ok", |s| s.pop_layer())
    );
}

メニューと選択

use cursive::views::{Dialog, SelectView};
use cursive::traits::*;

fn main() {
    let mut siv = cursive::default();

    let mut select = SelectView::new()
        .h_align(cursive::align::HAlign::Center)
        .autojump();

    select.add_all_str(vec!["Option 1", "Option 2", "Option 3"]);
    
    select.set_on_submit(|s, item| {
        s.add_layer(
            Dialog::text(format!("You selected: {}", item))
                .button("Ok", |s| s.pop_layer())
        );
    });

    siv.add_layer(
        Dialog::around(select.scrollable().fixed_size((20, 10)))
            .title("Select an option")
    );

    siv.run();
}

高度な機能

カスタムビュー

use cursive::{Printer, Vec2, View};

pub struct MyView {
    content: String,
}

impl View for MyView {
    fn draw(&self, printer: &Printer) {
        printer.print((0, 0), &self.content);
    }

    fn required_size(&mut self, _constraint: Vec2) -> Vec2 {
        Vec2::new(self.content.len(), 1)
    }

    fn on_event(&mut self, event: cursive::event::Event) -> cursive::event::EventResult {
        match event {
            cursive::event::Event::Key(cursive::event::Key::Enter) => {
                // Handle Enter key
                cursive::event::EventResult::Consumed(None)
            }
            _ => cursive::event::EventResult::Ignored,
        }
    }
}

テーマとスタイル

use cursive::theme::{BaseColor, BorderStyle, Palette, Theme};
use cursive::views::Dialog;

fn main() {
    let mut siv = cursive::default();

    // カスタムテーマを作成
    let mut theme = Theme::default();
    theme.shadow = false;
    theme.borders = BorderStyle::Simple;
    theme.palette[Palette::Background] = BaseColor::Blue.dark();
    theme.palette[Palette::View] = BaseColor::Black.light();
    theme.palette[Palette::Primary] = BaseColor::White.light();

    siv.set_theme(theme);

    siv.add_layer(
        Dialog::text("Themed application")
            .title("Custom Theme")
            .button("Quit", |s| s.quit())
    );

    siv.run();
}

複雑なレイアウト

use cursive::views::{
    Dialog, EditView, LinearLayout, ListView, Panel, 
    ResizedView, ScrollView, TextView
};
use cursive::traits::*;

fn main() {
    let mut siv = cursive::default();

    let layout = LinearLayout::vertical()
        .child(Panel::new(
            ScrollView::new(
                TextView::new("Long text...\n".repeat(20))
            ).scroll_x(true)
        ).title("Scrollable Panel"))
        .child(ListView::new()
            .child("Item 1", TextView::new("Content 1"))
            .child("Item 2", TextView::new("Content 2"))
            .child("Item 3", EditView::new())
            .delimiter()
            .child("Item 4", TextView::new("Content 4"))
        );

    siv.add_layer(
        Dialog::around(ResizedView::with_fixed_size((50, 20), layout))
            .title("Complex Layout")
            .button("Ok", |s| s.quit())
    );

    siv.run();
}

エコシステム

実例プロジェクト

  • RustyChat: RustとCursiveで作られたチャットクライアント
  • checkline: stdinからstdoutへのチェックボックスラインピッカー
  • clock-cli: ストップウォッチとカウントダウンタイマー機能を持つ時計
  • fui: JSONフォームの定義からUIを生成

拡張ライブラリ

  • cursive-tabs: タブビューの実装
  • cursive-tree-view: ツリービューの実装
  • cursive-async-view: 非同期コンテンツ表示
  • cursive-table-view: テーブルビューの実装

設計原則

  1. 使いやすさ: シンプルなアプリはシンプルに、複雑なアプリも管理可能に
  2. Linux TTY互換性: 色やUTF-8は制限されるかもしれないが、ほとんどの機能はLinux TTYで動作
  3. 柔軟性: シンプルなUIスクリプトから複雑なリアルタイムアプリ、ゲームまで対応

利点

  • 高レベルAPI: 複雑な詳細を抽象化
  • 豊富なビュー: 多様な組み込みコンポーネント
  • イベントループ管理: フレームワークが処理
  • テーマサポート: カスタマイズ可能な外観
  • マルチバックエンド: 様々な環境に対応

制約事項

  • パフォーマンス: リテインドモードのため、特定のケースでは非効率
  • 学習曲線: 独自のビューシステムを理解する必要
  • カスタマイズ: 高度なカスタマイズは複雑
  • グラフィックス: テキストベースのみ

他のライブラリとの比較

項目CursiveRatatuitui-realm
レンダリングリテインドイミディエートコンポーネント
APIレベル
学習コスト低〜中中〜高
柔軟性非常に高
組み込み機能豊富

まとめ

Cursiveは、使いやすさを重視したRust TUIフレームワークです。リテインドモードのアーキテクチャにより、複雑なレイアウトやインタラクションを簡単に実装できます。特に、迅速なプロトタイプ開発や、フォームやメニューを多用するアプリケーションに適しています。