1 个不稳定版本

0.1.0 2019年3月28日

#127#escaping


termcandy 中使用

LGPL-2.0-only

11KB
167

termcandy

termcandy 是一个使用命令式代码编写终端用户界面的库。这意味着你不需要围绕事件循环来结构化你的程序,只需编写自然流动的代码,让宏魔法来做其余的工作。

示例

此程序将绘制一个蓝色的球,它在屏幕上弹跳,直到用户按下Esc键。

#![type_length_limit="30000000"]
#![feature(proc_macro_hygiene)]
#![feature(never_type)]
#![feature(generators)]
#![feature(label_break_value)]

use std::time::Duration;
use termcandy::{widget, select_widget};
use termcandy::input::{self, Key};
use termcandy::graphics::{Style, Color, Attrs};
use tokio::time::{self, Instant};

#[widget]
fn bouncing_ball() {
    let mut pos_x = 0;
    let mut pos_y = 0;
    let mut vel_x = 1;
    let mut vel_y = 1;
    let mut next_instant = Instant::now();
    let style = Style { fg: Color::blue(), bg: Color::default(), attrs: Attrs::bold() };
    loop {
        select_widget! {
            () = time::sleep_until(next_instant) => {
                next_instant += Duration::from_millis(200);
                let (w, h) = termcandy::screen_size();
                if pos_x <= 0 { vel_x = 1 };
                if pos_x >= w as i16 { vel_x = -1 };
                if pos_y <= 0 { vel_y = 1 };
                if pos_y >= h as i16 { vel_y = -1 };
                pos_x += vel_x;
                pos_y += vel_y;
            },
            () = input::key(Key::Esc) => return,
            never = widget::draw(|surface| {
                surface.print("", pos_x, pos_y, style)
            }) => never,
        }
    }
}

#[tokio::main]
async fn main() {
    termcandy::run(four_bouncing_balls()).await.unwrap();
}

我们还可以将屏幕分为4个角,并重复使用上面的代码,使4个球在这4个角弹跳。请注意,此函数不需要任何循环即可编写。如果你在运行时调整终端窗口的大小,它将相应地做出反应。

use termcandy::Widget;
use termcandy::graphics::Rect;

#[widget]
fn four_bouncing_balls() {
    let top_left = bouncing_ball().resize(|w, h| {
        Rect { x0: 0, x1: w as i16 / 2, y0: 0, y1: h as i16 / 2 }
    });
    let top_right = bouncing_ball().resize(|w, h| {
        Rect { x0: w as i16 / 2, x1: w as i16, y0: 0, y1: h as i16 / 2 }
    });
    let bottom_left = bouncing_ball().resize(|w, h| {
        Rect { x0: 0, x1: w as i16 / 2, y0: h as i16 / 2, y1: h as i16 }
    });
    let bottom_right = bouncing_ball().resize(|w, h| {
        Rect { x0: w as i16 / 2, x1: w as i16, y0: h as i16 / 2, y1: h as i16 }
    });
    select_widget! {
        () = top_left => (),
        () = top_right => (),
        () = bottom_left => (),
        () = bottom_right => (),
        never = widget::draw(|surface| {
            surface.draw_v_line(0, surface.height() as i16 - 1, 0);
            surface.draw_v_line(0, surface.height() as i16 - 1, surface.width() as i16 / 2);
            surface.draw_v_line(0, surface.height() as i16 - 1, surface.width() as i16 - 1);
            surface.draw_h_line(0, surface.width() as i16 - 1, 0);
            surface.draw_h_line(0, surface.width() as i16 - 1, surface.height() as i16 / 2);
            surface.draw_h_line(0, surface.width() as i16 - 1, surface.height() as i16 - 1);
        }) => never
    }
}

依赖关系

~2MB
~46K SLoC