Crates.io Build Status

Tuikit

Tuikit is a TUI library for writing terminal UI applications. Highlights:

Tuikit is modeld after termbox which views the terminal as a table of fixed-size cells and input being a stream of structured messages.

WARNING: The library is not stable yet, the API might change.

Usage

In your Cargo.toml add the following:

toml [dependencies] tuikit = "*"

And if you'd like to use the latest snapshot version:

toml [dependencies] tuikit = { git = "https://github.com/lotabout/tuikit.git" }

Here is an example (could also be run by cargo run --example hello-world):

```rust use tuikit::prelude::*; use std::cmp::{min, max};

fn main() { let 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();
}

} ```

Layout

tuikit provides HSplit, VSplit and Win for managing layouts:

  1. HSplit allow you to split area horizontally into pieces.
  2. VSplit works just like HSplit but splits vertically.
  3. Win do not split, it could have maring, padding and border.

For example:

```rust use tuikit::prelude::*;

struct Model(String);

impl Draw for Model { fn draw(&self, canvas: &mut Canvas) -> Result<()> { let (width, height) = canvas.size()?; let messagewidth = self.0.len(); let left = (width - messagewidth) / 2; let top = height / 2; let _ = canvas.print(top, left, &self.0); Ok(()) } }

fn main() { let term = Term::withheight(TermHeight::Percent(50)).unwrap(); let model = Model("Hey, I'm in middle!".tostring());

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();
}

} ```

The split algorithm is simple:

  1. Both HSplit and VSplit will take several Split where a Split would contains:
    1. basis, the original size
    2. grow, the factor to grow if there is still enough room
    3. shrink, the factor to shrink if there is not enough room
  2. HSplit/VSplit will count the total width/height(basis) of the split items
  3. Judge if the current width/height is enough or not for the split items
  4. shrink/grow the split items according to their grow/shrink: factor / sum(factors)
  5. If still not enough room, the last one(s) would be set width/height 0

References

Tuikit borrows ideas from lots of other projects: