29 个版本
0.5.0 | 2022 年 10 月 28 日 |
---|---|
0.4.6 | 2022 年 5 月 4 日 |
0.4.5 | 2021 年 2 月 15 日 |
0.4.2 | 2020 年 10 月 20 日 |
0.2.6 | 2019 年 3 月 30 日 |
#135 in 命令行界面
16,309 每月下载量
用于 115 个 crate(11 个直接使用)
205KB
5K SLoC
Tuikit
Tuikit 是一个用于编写终端 UI 应用的 TUI 库。特点
- 线程安全。
- 支持非全屏模式以及全屏模式。
- 支持
Alt
键、鼠标事件等。 - 缓冲以提高渲染效率。
Tuikit 是基于 termbox 设计的,它将终端视为固定大小单元格的表格,并将输入视为结构化消息流。
警告:该库尚不稳定,API 可能会更改。
使用方法
在你的 Cargo.toml
中添加以下内容
[dependencies]
tuikit = "*"
如果你想要使用最新的快照版本
[dependencies]
tuikit = { git = "https://github.com/lotabout/tuikit.git" }
以下是一个示例(也可以通过 cargo run --example hello-world
运行)
use tuikit::prelude::*;
use std::cmp::{min, max};
fn main() {
let term: Term<()> = Term::with_height(TermHeight::Percent(30)).unwrap();
let mut row = 1;
let mut col = 0;
let _ = term.print(0, 0, "press arrow key to move the text, (q) to quit");
let _ = term.present();
while let Ok(ev) = term.poll_event() {
let _ = term.clear();
let _ = term.print(0, 0, "press arrow key to move the text, (q) to quit");
let (width, height) = term.term_size().unwrap();
match ev {
Event::Key(Key::ESC) | Event::Key(Key::Char('q')) => break,
Event::Key(Key::Up) => row = max(row-1, 1),
Event::Key(Key::Down) => row = min(row+1, height-1),
Event::Key(Key::Left) => col = max(col, 1)-1,
Event::Key(Key::Right) => col = min(col+1, width-1),
_ => {}
}
let attr = Attr{ fg: Color::RED, ..Attr::default() };
let _ = term.print_with_attr(row, col, "Hello World! 你好!今日は。", attr);
let _ = term.set_cursor(row, col);
let _ = term.present();
}
}
布局
tuikit
提供了 HSplit
、VSplit
和 Win
来管理布局
HSplit
允许你将区域水平分割成多个部分。VSplit
与HSplit
类似,但垂直分割。Win
不进行分割,它可以有边距、填充和边框。
例如
use tuikit::prelude::*;
struct Model(String);
impl Draw for Model {
fn draw(&self, canvas: &mut dyn Canvas) -> DrawResult<()> {
let (width, height) = canvas.size()?;
let message_width = self.0.len();
let left = (width - message_width) / 2;
let top = height / 2;
let _ = canvas.print(top, left, &self.0);
Ok(())
}
}
impl Widget for Model{}
fn main() {
let term: Term<()> = Term::with_height(TermHeight::Percent(50)).unwrap();
let model = Model("middle!".to_string());
while let Ok(ev) = term.poll_event() {
if let Event::Key(Key::Char('q')) = ev {
break;
}
let _ = term.print(0, 0, "press 'q' to exit");
let hsplit = HSplit::default()
.split(
VSplit::default()
.basis(Size::Percent(30))
.split(Win::new(&model).border(true).basis(Size::Percent(30)))
.split(Win::new(&model).border(true).basis(Size::Percent(30)))
)
.split(Win::new(&model).border(true));
let _ = term.draw(&hsplit);
let _ = term.present();
}
}
分割算法很简单
- 无论是
HSplit
还是VSplit
,都会获取多个Split
,其中Split
会包含- 基本,原始大小
- 增长,如果还有足够的空间,增长的因素
- 收缩,如果空间不足,收缩的因素
HSplit 或 VSplit
将计算分割项的总宽度/高度(基本)- 判断当前宽度/高度是否足够用于分割项
- 根据它们的增长/收缩:
factor / sum(factors)
缩放分割项 - 如果仍然空间不足,最后一个(些)将被设置为宽度/高度为0
参考
Tuikit
从许多其他项目中借鉴了想法
依赖项
~2.5MB
~46K SLoC