termcandy
termcandy
is a library for writing terminal user interfaces using
imperative-style code. This means you don't have to structure your program
around an event loop, just write natural-looking control flow and let macro
magic do the rest.
This program will draw a blue ball bouncing around the screen until the user presses escape.
```rust
use std::time::{Instant, Duration}; use termcandy::{widget, select_widget}; use termcandy::events::{self, Key}; use termcandy::graphics::{Style, Color, Attrs}; use tokio::timer; use futures::Future;
fn bouncingball() -> Result<(), failure::Error> { let mut posx = 0; let mut posy = 0; let mut velx = 1; let mut vely = 1; let mut nextinstant = Instant::now(); let style = Style { fg: Color::blue(), bg: Color::default(), attrs: Attrs::bold() }; loop { selectwidget! { () = timer::Delay::new(nextinstant) => { nextinstant += Duration::frommillis(100); let (w, h) = termcandy::screensize(); if posx <= 0 { velx = 1 }; if posx >= w as i16 { velx = -1 }; if posy <= 0 { vely = 1 }; if posy >= h as i16 { vely = -1 }; posx += velx; posy += vely; }, () = events::key(Key::Esc) => return Ok(()), never = widget::draw(|surface| { surface.print("●", posx, pos_y, style) }) => never, } } }
fn main() { tokio::runtime::currentthread::blockonall(termcandy::run(bouncingball())).expect("oh no!") } ```
We could also reuse the above code to make 4 balls bounce around inside their own boxes:
```rust use termcandy::Widget; use termcandy::graphics::Rect;
fn fourbouncingballs() -> Result<(), failure::Error> { let topleft = bouncingball().resize(|w, h| { Rect { x0: 0, x1: w as i16 / 2, y0: 0, y1: h as i16 / 2 } }); let topright = bouncingball().resize(|w, h| { Rect { x0: w as i16 / 2, x1: w as i16, y0: 0, y1: h as i16 / 2 } }); let bottomleft = bouncingball().resize(|w, h| { Rect { x0: 0, x1: w as i16 / 2, y0: h as i16 / 2, y1: h as i16 } }); let bottomright = bouncingball().resize(|w, h| { Rect { x0: w as i16 / 2, x1: w as i16, y0: h as i16 / 2, y1: h as i16 } }); selectwidget! { () = topleft => (), () = topright => (), () = bottomleft => (), () = bottomright => (), never = widget::draw(|surface| { surface.drawvline(0, surface.height() as i16 - 1, 0); surface.drawvline(0, surface.height() as i16 - 1, surface.width() as i16 / 2); surface.drawvline(0, surface.height() as i16 - 1, surface.width() as i16 - 1); surface.drawhline(0, surface.width() as i16 - 1, 0); surface.drawhline(0, surface.width() as i16 - 1, surface.height() as i16 / 2); surface.drawh_line(0, surface.width() as i16 - 1, surface.height() as i16 - 1); }) => never } Ok(()) } ```