こんにちは、lumenHeroです。 これまで5回にわたり、RustとRatatuiを使ったターミナルユーザインタフェース(TUI)開発の備忘録を書いてきました。
今回はその総まとめです。これまでの知識をすべて詰め込んだ「統合サンプルコード」と共に、TUI開発の核心を振り返りましょう。
1. シリーズの振り返り
まずは、これまでの旅路をおさらいします。各記事がTUIツールの重要なパーツを担っていました。
- #1 [環境構築編]: Ubuntu上で Rust × ratatui の開発環境構築方法・TUI上にHelloWorld表示まで
- #2 [文字入力編]: 【Ratatui】Rustで作るTUIツール:ユーザーからの文字入力を受け取る(Input Formの実装)
- #3 [コンボ入力編]: 【Ratatui】ショートカットキーを実装する!Ctrlキー等のコンボ入力
- #4 [レイアウト編]: 【Ratatui】Enumによるフォーカス管理と操作対象の可視化
- #5 [フォーカス管理編]: Ubuntu上で Rust × ratatui の開発環境構築方法・TUI上にHelloWorld表示まで
2. TUI開発の心臓部:状態と描画のサイクル
全記事を通して共通していたのは、「宣言的UI」という考え方です。 TUIアプリは、常に以下のサイクルで動いています。
- Event(神経): ユーザーがキーを叩く。
- State(魂): キー入力に応じて、変数(inputやfocus)を書き換える。
- Draw(骨組み): 書き換わった変数を読み取り、画面をゼロから描き直す。
このサイクルさえ理解していれば、どんなに複雑なツールでも「状態を定義して描画する」の繰り返しで構築可能です。
3. 統合サンプルコード
これまでの要素(レイアウト分割・文字入力・Ctrl+Q終了・Tabフォーカス切り替え)をすべて合体させた、実戦的なテンプレートコードです。
use crossterm::{
event::{self, Event, KeyCode, KeyEventKind, KeyModifiers},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{
backend::CrosstermBackend,
layout::{Constraint, Direction, Layout},
style::{Color, Style},
widgets::{Block, Borders, Paragraph},
Terminal,
};
use std::{error::Error, io};
#[derive(PartialEq)]
enum Focus {
Left,
Right,
}
fn main() -> Result<(), Box<dyn Error>> {
// セットアップ
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let mut terminal = Terminal::new(CrosstermBackend::new(stdout))?;
// 状態管理
let mut current_focus = Focus::Left;
let mut left_input = String::new();
let mut right_input = String::new();
loop {
terminal.draw(|f| {
// レイアウト:上下に分割し、上を左右に分割
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(0), Constraint::Length(3)])
.split(f.area());
let top_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(chunks[0]);
let get_style = |focus: Focus| {
if current_focus == focus { Style::default().fg(Color::Yellow) }
else { Style::default().fg(Color::White) }
};
// 各パネルの描画
f.render_widget(
Paragraph::new(left_input.as_str())
.block(Block::default().title(" Left Panel ").borders(Borders::ALL).style(get_style(Focus::Left))),
top_chunks[0],
);
f.render_widget(
Paragraph::new(right_input.as_str())
.block(Block::default().title(" Right Panel ").borders(Borders::ALL).style(get_style(Focus::Right))),
top_chunks[1],
);
f.render_widget(
Paragraph::new(" [Tab] Switch Focus | [Ctrl+Q] Quit ")
.block(Block::default().title(" Help ").borders(Borders::ALL)),
chunks[1],
);
})?;
// イベント処理
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press {
match key.code {
// 終了コンボ
KeyCode::Char('q') if key.modifiers.contains(KeyModifiers::CONTROL) => break,
// フォーカス切り替え
KeyCode::Tab => {
current_focus = if current_focus == Focus::Left { Focus::Right } else { Focus::Left };
}
// 入力振り分け
KeyCode::Char(c) if !key.modifiers.contains(KeyModifiers::CONTROL) => {
match current_focus {
Focus::Left => left_input.push(c),
Focus::Right => right_input.push(c),
}
}
KeyCode::Backspace => {
match current_focus {
Focus::Left => { left_input.pop(); },
Focus::Right => { right_input.pop(); },
}
}
_ => {}
}
}
}
}
// クリーンアップ
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
Ok(())
}サンプルコードをmain.rsに張り付けて実行すると以下のような画面が表示されます。

4. さいごに
全6回にわたる「基礎編」はこれで完結です。 ここまでの知識があれば、あとはRustのロジック(計算処理やファイル操作など)を組み込むだけで、あなただけのオリジナルツールが完成します。
これからは、この「型」を使って実際に役立つ成果物を作っていこうと思います。 次回のシリーズでは、「TUI電卓」や「作業ログ管理ツール」など、具体的なツールの制作過程を公開していく予定ですので、そちらもぜひチェックしてみてください!
ここまで読んでいただき、本当にありがとうございました。 ターミナルの中に、最高の道具箱を作っていきましょう!
では、また次の記事で。 lumenHero