term_cursor 0.2.1

A crate for handling terminal cursor movement in a platform independent way.
Documentation
extern crate term_cursor as cursor;

#[derive(Clone, Copy)]
struct Rect {
    x: i32,
    y: i32,
    width: i32,
    height: i32,
}

impl Rect {
    fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
        Self {
            x,
            y,
            width,
            height,
        }
    }
}

fn main() {
    print!("{}", cursor::Clear);
    window(Rect::new(1, 1, 50, 20), "Window title", "Window content");

    // Move the cursor down under the window, so the command prompt doesn't draw into the window.
    print!("{}", cursor::Goto(0, 26));
}

fn window(rect: Rect, title: &str, content: &str) {
    let client_rect = window_border(rect, title);
    let client_rect = window_scroll_bar(client_rect);
    print!("{}{}", cursor::Goto(client_rect.x, client_rect.y), content);
}

fn window_border(rect: Rect, title: &str) -> Rect {
    // Top border
    print!(
        "{}{}",
        cursor::Goto(rect.x, rect.y),
        cursor::Right(rect.width - 2)
    );
    for x in rect.x + 1..rect.x + rect.width - 1 {
        print!("{}", cursor::Goto(x, rect.y));
    }

    // Left and right border
    for y in rect.y + 1..rect.y + rect.height {
        print!(
            "{}{}",
            cursor::Goto(rect.x, y),
            cursor::Right(rect.width - 2)
        );
    }

    // Bottom border
    print!(
        "{}{}",
        cursor::Goto(rect.x, rect.y + rect.height),
        cursor::Right(rect.width - 2)
    );
    for x in rect.x + 1..rect.x + rect.width - 1 {
        print!("{}", cursor::Goto(x, rect.y + rect.height));
    }

    // Close button
    print!(
        "{}{}[×]",
        cursor::Goto(rect.x + rect.width, rect.y),
        cursor::Left(5)
    );

    // Title bar
    print!(
        "{}{}{}",
        cursor::Goto(rect.x + 2, rect.y + 1),
        title,
        cursor::Goto(rect.x + 1, rect.y + 2)
    );
    for _ in rect.x + 1..rect.x + rect.width - 1 {
        print!("");
    }
    print!(
        "{}{}",
        cursor::Goto(rect.x, rect.y + 2),
        cursor::Right(rect.width - 2)
    );

    Rect::new(rect.x + 2, rect.y + 3, rect.width - 4, rect.height - 3)
}

fn window_scroll_bar(rect: Rect) -> Rect {
    for y in rect.y..rect.y + rect.height {
        print!("{}│░", cursor::Goto(rect.x + rect.width - 1, y));
    }

    // Top and bottom T-connectors
    print!(
        "{}{}",
        cursor::Relative(-2, 1),
        cursor::Goto(rect.x + rect.width - 1, rect.y - 1)
    );

    // Buttons
    print!(
        "{}{}",
        cursor::Down(1),
        cursor::Goto(rect.x + rect.width, rect.y + rect.height - 1)
    );

    // Draggable bar
    let mid = rect.height / 2;
    print!(
        "{}",
        cursor::Goto(rect.x + rect.width, rect.y + (mid - mid / 2))
    );

    Rect::new(rect.x, rect.y, rect.width - 2, rect.height)
}