Skip to main content

itools_tui/
utils.rs

1//! 工具函数模块
2//!
3//! 提供各种 TUI 开发中常用的工具函数和实用工具。
4
5use crate::style::Color;
6use std::io::Write;
7
8/// 文本处理工具
9pub mod text {
10    /// 截断文本到指定长度
11    ///
12    /// # Arguments
13    /// * `text` - 要截断的文本
14    /// * `max_length` - 最大长度
15    ///
16    /// # Returns
17    /// 截断后的文本
18    pub fn truncate(text: &str, max_length: usize) -> String {
19        text.chars().take(max_length).collect()
20    }
21
22    /// 填充文本到指定长度
23    ///
24    /// # Arguments
25    /// * `text` - 要填充的文本
26    /// * `length` - 目标长度
27    /// * `fill_char` - 填充字符
28    ///
29    /// # Returns
30    /// 填充后的文本
31    pub fn pad(text: &str, length: usize, fill_char: char) -> String {
32        if text.len() >= length {
33            text.chars().take(length).collect()
34        }
35        else {
36            format!("{}{}", text, fill_char.to_string().repeat(length - text.len()))
37        }
38    }
39
40    /// 居中文本
41    ///
42    /// # Arguments
43    /// * `text` - 要居中的文本
44    /// * `width` - 总宽度
45    ///
46    /// # Returns
47    /// 居中后的文本
48    pub fn center(text: &str, width: usize) -> String {
49        if text.len() >= width {
50            text.chars().take(width).collect()
51        }
52        else {
53            let padding = (width - text.len()) / 2;
54            format!("{}{}{}", " ".repeat(padding), text, " ".repeat(width - text.len() - padding))
55        }
56    }
57}
58
59/// 颜色转换工具
60pub mod color {
61    use super::Color;
62    use crossterm::style as crossterm_style;
63
64    /// 将 RGB 值转换为 Color 枚举
65    ///
66    /// # Arguments
67    /// * `r` - 红色通道 (0-255)
68    /// * `g` - 绿色通道 (0-255)
69    /// * `b` - 蓝色通道 (0-255)
70    ///
71    /// # Returns
72    /// Color 枚举值
73    pub fn rgb(r: u8, g: u8, b: u8) -> Color {
74        Color::Rgb(r, g, b)
75    }
76
77    /// 将 Color 转换为 crossterm 颜色
78    ///
79    /// # Arguments
80    /// * `color` - Color 枚举值
81    ///
82    /// # Returns
83    /// crossterm 颜色
84    pub fn to_crossterm_color(color: Color) -> crossterm_style::Color {
85        color.into()
86    }
87}
88
89/// 终端操作工具
90pub mod terminal_utils {
91    use crossterm::{cursor, terminal as crossterm_terminal};
92    use std::io::{self, Result as CrosstermResult, Write};
93
94    /// 清除屏幕
95    ///
96    /// # Returns
97    /// 操作结果
98    #[allow(dead_code)]
99    pub fn clear() -> CrosstermResult<()> {
100        let mut stdout = io::stdout();
101        write!(stdout, "{}", crossterm_terminal::Clear(crossterm_terminal::ClearType::All))?;
102        stdout.flush()?;
103        Ok(())
104    }
105
106    /// 移动光标
107    ///
108    /// # Arguments
109    /// * `x` - x 坐标
110    /// * `y` - y 坐标
111    ///
112    /// # Returns
113    /// 操作结果
114    #[allow(dead_code)]
115    pub fn move_cursor(x: u16, y: u16) -> CrosstermResult<()> {
116        let mut stdout = io::stdout();
117        write!(stdout, "{}", cursor::MoveTo(x, y))?;
118        stdout.flush()?;
119        Ok(())
120    }
121
122    /// 隐藏光标
123    ///
124    /// # Returns
125    /// 操作结果
126    #[allow(dead_code)]
127    pub fn hide_cursor() -> CrosstermResult<()> {
128        let mut stdout = io::stdout();
129        write!(stdout, "{}", cursor::Hide)?;
130        stdout.flush()?;
131        Ok(())
132    }
133
134    /// 显示光标
135    ///
136    /// # Returns
137    /// 操作结果
138    #[allow(dead_code)]
139    pub fn show_cursor() -> CrosstermResult<()> {
140        let mut stdout = io::stdout();
141        write!(stdout, "{}", cursor::Show)?;
142        stdout.flush()?;
143        Ok(())
144    }
145}
146
147/// 数学工具
148pub mod math {
149    /// 限制值在指定范围内
150    ///
151    /// # Arguments
152    /// * `value` - 要限制的值
153    /// * `min` - 最小值
154    /// * `max` - 最大值
155    ///
156    /// # Returns
157    /// 限制后的值
158    pub fn clamp<T: PartialOrd>(value: T, min: T, max: T) -> T {
159        if value < min {
160            min
161        }
162        else if value > max {
163            max
164        }
165        else {
166            value
167        }
168    }
169
170    /// 线性插值
171    ///
172    /// # Arguments
173    /// * `start` - 起始值
174    /// * `end` - 结束值
175    /// * `t` - 插值参数 (0.0-1.0)
176    ///
177    /// # Returns
178    /// 插值结果
179    pub fn lerp(start: f64, end: f64, t: f64) -> f64 {
180        start + (end - start) * t
181    }
182}
183
184/// 交互式选择菜单
185pub fn select<T>(items: &[T], prompt: &str) -> Option<usize>
186where
187    T: std::fmt::Display,
188{
189    println!("{}", prompt);
190    for (i, item) in items.iter().enumerate() {
191        println!("{}: {}", i + 1, item);
192    }
193
194    loop {
195        print!("请选择 (1-{}): ", items.len());
196        std::io::stdout().flush().unwrap();
197
198        let mut input = String::new();
199        std::io::stdin().read_line(&mut input).unwrap();
200
201        match input.trim().parse::<usize>() {
202            Ok(choice) if choice >= 1 && choice <= items.len() => {
203                return Some(choice - 1);
204            }
205            _ => {
206                println!("无效的选择,请重新输入");
207            }
208        }
209    }
210}
211
212/// 显示加载动画
213pub fn loading_animation(duration: std::time::Duration, message: &str) {
214    let chars = ["|", "/", "-", "\\"];
215    let start = std::time::Instant::now();
216
217    print!("{}", message);
218    std::io::stdout().flush().unwrap();
219
220    let mut i = 0;
221    while start.elapsed() < duration {
222        print!("\r{}{}", message, chars[i % chars.len()]);
223        std::io::stdout().flush().unwrap();
224        std::thread::sleep(std::time::Duration::from_millis(100));
225        i += 1;
226    }
227
228    print!("\r{}{}", message, " ");
229    println!();
230}