Rust终端UI开发库crosstermion的使用,跨平台终端控制与样式管理的高效解决方案
Rust终端UI开发库crosstermion的使用,跨平台终端控制与样式管理的高效解决方案
Crosstermion是一个实用工具库,用于统一termion
和crossterm
两个库的部分类型,使开发者能够轻松构建在Unix系统上使用轻量级termion
库,而在Windows系统上使用crossterm
库的应用程序。
当前提供的功能
Key
类型和input_stream
(异步)用于接收按键输入AlternativeRawTerminal
结合了替代屏幕和原始模式- 通过
crossterm
或termion
后端创建tui
或tui-react
终端的方法
如何实现颜色和样式?
使用tui
- 使用
tui
时,将获得跨后端的原生颜色和样式支持
不使用tui
- 使用**
color
**功能获取ansi_term
的额外颜色工具 - 或者使用
ansi_term
、colored
或termcolor
也能正常工作
如何在Windows上使用crossterm
而在Unix上使用termion
?
目前没有简单的方法,因为cargo
总是会构建所有依赖项,即使它们不应该在你的平台上使用。这会导致termion
和crossterm
都被构建,这在Windows上是致命的。因此,在创建发布构建时,必须手动选择功能开关,即默认排除所有需要TUI的功能,让用户启用他们需要的功能。
compile_error!(...)
宏可以用来通知用户是否需要功能选择。或者,确保即使没有选择任何后端也能编译通过。
最后,也可以直接选择总是针对crossterm
进行编译。
功能
所有功能都是可叠加的,但如果它们相互排斥(例如tui-react
和tui
,或crossterm
和termion
),将选择更通用的功能。
相互排斥的功能
- crossterm
- 提供从
crossbeam::event::KeyEvent
的Key
转换支持和AlternativeRawTerminal
- 提供线程化的按键输入通道
- input-async-crossterm(附加)
- 为crossterm添加原生异步功能,由于
mio
无需额外线程 - 注意线程化按键输入始终支持
- 为crossterm添加原生异步功能,由于
- 提供从
- termion
- 提供从
termion::event::Key
的Key
转换支持和AlternativeRawTerminal
- 提供线程化的按键输入通道
- input-async(附加)
- 生成线程并通过futures Stream提供输入事件
- 注意线程化按键输入始终支持
- 提供从
使用tui_
(相互排斥)
- tui-termion(隐含
termion
功能)- 结合
tui
和termion
,提供带有termion
后端的tui::Terminal
- 结合
- tui-crossterm(隐含
crossterm
功能)- 结合
tui
和crossterm
,提供带有crossterm
后端的tui::Terminal
- 结合
使用tui-react
(相互排斥)
- tui-react-termion(隐含
termion
功能)- 结合
tui-react
和termion
,提供带有termion
后端的tui::Terminal
- 结合
- tui-react-crossterm(隐含
crossterm
功能)- 结合
tui-react
和crossterm
,提供带有crossterm
后端的tui::Terminal
- 结合
color
- 添加对基于
ansi_term
的条件着色的支持。该库小巧,简洁,允许零拷贝绘制字节和UTF-8字符串,同时支持Windows 10。
光标移动(相互排斥)
- crossterm
- 使用crossterm实现光标移动
- termion
- 使用termion实现光标移动
完整示例代码
use crosstermion::{input::Key, terminal::AlternativeRawTerminal};
use std::io::{stdout, Write};
fn main() -> std::io::Result<()> {
// 创建替代屏幕和原始模式的终端
let _terminal = AlternativeRawTerminal::new(stdout())?;
// 简单的终端输出示例
println!("Hello, Crosstermion!");
println!("Press any key to exit...");
// 等待按键输入
let _key = Key::read()?;
Ok(())
}
安装
在项目目录中运行以下Cargo命令:
cargo add crosstermion
或者将以下行添加到Cargo.toml:
crosstermion = "0.14.0"
更完整的示例代码
下面是一个更完整的示例,展示了如何使用crosstermion创建带颜色和样式的终端UI:
use crosstermion::{
input::Key,
terminal::{AlternativeRawTerminal, Clear, ClearType},
style::{Color, Stylize},
};
use std::io::{stdout, Write};
fn main() -> std::io::Result<()> {
// 创建替代屏幕和原始模式的终端
let mut terminal = AlternativeRawTerminal::new(stdout())?;
// 清除屏幕
terminal.execute(Clear(ClearType::All))?;
// 带颜色的文本输出
writeln!(
terminal,
"{} {}",
"Hello".with(Color::Red).bold(),
"Crosstermion!".with(Color::Green).underline()
)?;
// 光标定位示例(需要启用cursor-move功能)
#[cfg(feature = "cursor-move")]
{
terminal.execute(crosstermion::cursor::MoveTo(10, 10))?;
writeln!(terminal, "Cursor moved to (10,10)")?;
}
// 等待按键输入
println!("Press any key to exit...");
let _key = Key::read()?;
Ok(())
}
这个示例展示了:
- 创建原始模式终端
- 清除屏幕
- 使用颜色和样式输出文本
- 可选的光标移动功能
- 等待用户按键输入
要运行这个示例,需要在Cargo.toml中添加以下依赖:
crosstermion = { version = "0.14.0", features = ["color"] }
如果需要光标移动功能,可以添加:
crosstermion = { version = "0.14.0", features = ["color", "cursor-move"] }
Rust终端UI开发库crosstermion的使用
介绍
crosstermion是一个基于crossterm的Rust库,提供了跨平台的终端控制与样式管理功能。它简化了终端UI开发,支持Windows、macOS和Linux系统,是构建命令行界面(CLI)应用程序的强大工具。
主要特性
- 跨平台支持
- 终端样式控制(颜色、背景、文本属性)
- 光标操作和定位
- 终端输入处理
- 屏幕缓冲区管理
- 异步支持
使用方法
添加依赖
首先在Cargo.toml中添加依赖:
[dependencies]
crosstermion = "0.10"
基本示例
use crosstermion::{
color, cursor,
event::{read, Event, KeyCode},
execute,
style::{Print, Stylize},
terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType},
Result,
};
fn main() -> Result<()> {
// 启用原始模式以获得更好的控制
enable_raw_mode()?;
// 使用execute!宏执行多个命令
execute!(
std::io::stdout(),
Clear(ClearType::All), // 清屏
cursor::MoveTo(5, 5), // 移动光标到(5,5)
Print("Hello".red().on_blue()), // 红色文字蓝色背景
cursor::MoveToNextLine(1), // 下移一行
Print("Press 'q' to quit".bold()) // 加粗文字
)?;
// 事件循环
loop {
if let Event::Key(key) = read()? {
if key.code == KeyCode::Char('q') {
break;
}
}
}
// 恢复终端
disable_raw_mode()?;
Ok(())
}
更复杂的UI示例
use crosstermion::{
color::{self, Color},
cursor,
execute,
style::{Print, Stylize},
terminal::{Clear, ClearType},
Result,
};
fn draw_menu() -> Result<()> {
let items = ["Option 1", "Option 2", "Option 3", "Exit"];
execute!(
std::io::stdout(),
Clear(CClearType::All),
cursor::MoveTo(10, 5),
Print("Main Menu".bold().underlined()),
)?;
for (i, item) in items.iter().enumerate() {
let y = 7 + i as u16;
execute!(
std::io::stdout(),
cursor::MoveTo(10, y),
Print(format!("{}. {}", i + 1, item))
)?;
}
Ok(())
}
fn main() -> Result<()> {
draw_menu()?;
Ok(())
}
处理用户输入
use crosstermion::{
event::{read, Event, KeyCode},
Result,
};
fn handle_input() -> Result<()> {
loop {
match read()? {
Event::Key(key) => match key.code {
KeyCode::Char('1') => println!("Option 1 selected"),
KeyCode::Char('2') => println!("Option 2 selected"),
KeyCode::Char('3') => println!("Option 3 selected"),
KeyCode::Char('q') | KeyCode::Esc => break,
_ => {}
},
_ => {}
}
}
Ok(())
}
样式组合
use crosstermion::{execute, style::{Print, Stylize}};
fn apply_styles() -> crosstermion::Result<()> {
execute!(
std::io::stdout(),
Print("Bold and underlined".bold().underlined()),
Print("\n"),
Print("Red on yellow".red().on_yellow()),
Print("\n"),
Print("Blinking text".blink())
)?;
Ok(())
}
高级功能
使用屏幕缓冲区
use crosstermion::{
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
execute
};
fn alternate_screen() -> crosstermion::Result<()> {
execute!(std::io::stdout(), EnterAlternateScreen)?;
// 在这里绘制你的UI
execute!(std::io::stdout(), LeaveAlternateScreen)?;
Ok(())
}
异步支持
use crosstermion::{
event::{poll, read, Event, KeyCode},
terminal,
Result,
};
use std::time::Duration;
async fn async_input() -> Result<()> {
terminal::enable_raw_mode()?;
loop {
if poll(Duration::from_millis(100))? {
if let Event::Key(key) = read()? {
if key.code == KeyCode::Char('q') {
break;
}
}
}
}
terminal::disable_raw_mode()?;
Ok(())
}
完整示例demo
下面是一个结合了上述功能的完整终端应用示例:
use crosstermion::{
color,
cursor,
event::{poll, read, Event, KeyCode},
execute,
style::{Print, Stylize},
terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
Clear, ClearType,
},
Result,
};
use std::time::Duration;
// 主菜单结构
struct Menu {
items: Vec<String>,
selected: usize,
}
impl Menu {
fn new(items: Vec<&str>) -> Self {
Menu {
items: items.iter().map(|s| s.to_string()).collect(),
selected: 0,
}
}
// 绘制菜单
fn draw(&self) -> Result<()> {
execute!(
std::io::stdout(),
Clear(ClearType::All),
cursor::MoveTo(10, 5),
Print("Main Menu".bold().underlined().cyan()),
)?;
for (i, item) in self.items.iter().enumerate() {
let y = 7 + i as u16;
let style = if i == self.selected {
item.on_white().black()
} else {
item.stylize()
};
execute!(
std::io::stdout(),
cursor::MoveTo(10, y),
Print(format!("> {}. {}", i + 1, item)),
style
)?;
}
execute!(
std::io::stdout(),
cursor::MoveTo(10, 12),
Print("Use arrow keys to navigate, Enter to select, Q to quit".dim())
)?;
Ok(())
}
// 处理键盘输入
fn handle_input(&mut self) -> Result<bool> {
if poll(Duration::from_millis(100))? {
if let Event::Key(key) = read()? {
match key.code {
KeyCode::Up => {
if self.selected > 0 {
self.selected -= 1;
}
}
KeyCode::Down => {
if self.selected < self.items.len() - 1 {
self.selected += 1;
}
}
KeyCode::Enter => {
execute!(
std::io::stdout(),
Clear(ClearType::All),
cursor::MoveTo(10, 5),
Print(format!("You selected: {}", self.items[self.selected]).green()),
cursor::MoveTo(10, 7),
Print("Press any key to continue...".yellow())
)?;
read()?;
return Ok(true);
}
KeyCode::Char('q') | KeyCode::Esc => return Ok(false),
_ => {}
}
}
}
Ok(true)
}
}
#[tokio::main]
async fn main() -> Result<()> {
// 初始化终端
enable_raw_mode()?;
execute!(std::io::stdout(), EnterAlternateScreen)?;
// 创建菜单
let mut menu = Menu::new(vec![
"Open File",
"Save File",
"Settings",
"Exit Program",
]);
// 主循环
loop {
menu.draw()?;
if !menu.handle_input()? {
break;
}
}
// 清理终端
execute!(std::io::stdout(), LeaveAlternateScreen)?;
disable_raw_mode()?;
Ok(())
}
注意事项
- 使用原始模式时(
enable_raw_mode
),确保在程序退出前调用disable_raw_mode
恢复终端 - 对于复杂UI,考虑使用
EnterAlternateScreen
和LeaveAlternateScreen
来防止污染用户终端 - 错误处理很重要,确保正确处理所有可能的终端操作错误
crosstermion提供了强大而灵活的功能来创建丰富的终端应用程序,从简单的文本样式到复杂的交互式UI都可以轻松实现。