swears 0.1.16

Use swears, not curses
Documentation
use crate::{Color, ColorString, Key};
use pancurses::{
    chtype, curs_set, def_prog_mode, def_shell_mode, doupdate, echo, endwin, init_pair, initscr,
    noecho, noraw, raw, reset_prog_mode, reset_shell_mode, resize_term, start_color, Window,
    COLOR_BLACK, COLOR_BLUE, COLOR_CYAN, COLOR_GREEN, COLOR_MAGENTA, COLOR_PAIR, COLOR_RED,
    COLOR_WHITE, COLOR_YELLOW,
};

use std::i32::{MAX, MIN};

#[derive(Debug)]
pub struct Terminal {
    win: Window,
}

impl Terminal {
    pub fn new() -> Self {
        let result = Self { win: initscr() };
        raw();
        if cfg!(target_family = "windows") {
            result.set_size(132, 43);
        }
        result.win.keypad(true);
        result.disable_echo();
        result.disable_scrolling();
        result.hide_cursor();
        result
    }

    pub fn with_color() -> Self {
        let result = Self::new();
        start_color();
        init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
        init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
        init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
        init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
        init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
        init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
        init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
        init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);

        result.win.bkgd(COLOR_PAIR(COLOR_BLACK as chtype));

        result
    }

    pub fn set_size(&self, x: i32, y: i32) {
        resize_term(y, x);
    }

    pub fn hide_cursor(&self) {
        curs_set(0);
    }
    pub fn show_cursor(&self) {
        curs_set(1);
    }

    pub fn enable_echo(&self) {
        echo();
    }
    pub fn disable_echo(&self) {
        noecho();
    }

    pub fn enable_scrolling(&self) {
        resize_term(MAX, MAX);
        self.win.setscrreg(MIN, MAX);
        self.win.scrollok(true);
    }

    pub fn disable_scrolling(&self) {
        self.win.scrollok(false);
    }

    pub fn get_key(&self) -> Option<Key> {
        if let Some(input) = self.win.getch() {
            Some(Key::from(input))
        } else {
            None
        }
    }

    pub fn set_input_timeout(&self, milliseconds: u32) {
        self.win.timeout(milliseconds as i32);
    }

    pub fn pause(&self) {
        def_prog_mode();
        noraw();
        reset_shell_mode();
        endwin();
    }

    pub fn resume(&self) {
        def_shell_mode();
        raw();
        reset_prog_mode();
        self.win.refresh();
        doupdate();
    }

    pub fn refresh(&self) {
        self.win.touch();
        self.win.refresh();
    }

    pub fn clear(&self) {
        self.win.clear();
    }

    pub fn move_print(&self, mut x: i32, mut y: i32, s: impl Into<ColorString>) {
        let x_initial = x;
        let cs = s.into();
        let color_num = match cs.to_color() {
            Color::Red => COLOR_RED,
            Color::Green => COLOR_GREEN,
            Color::Blue => COLOR_BLUE,
            Color::Cyan => COLOR_CYAN,
            Color::Magenta => COLOR_MAGENTA,
            Color::Yellow => COLOR_YELLOW,
            Color::White => COLOR_WHITE,
            Color::Black => COLOR_BLACK,
            Color::Default => COLOR_WHITE,
        };

        self.win.attrset(COLOR_PAIR(color_num as chtype));

        for ch in cs.to_string().chars() {
            if ch == '\n' {
                y += 1;
                x = x_initial;
            } else {
                x += 1;
                self.win.mvprintw(y, x, ch.to_string());
            }
        }
    }

    pub fn println(&self, s: impl Into<ColorString>) {
        let cs = s.into();
        let color = cs.to_color();
        self.print(ColorString(color, cs.to_string() + "\n\r"));
    }

    pub fn print(&self, s: impl Into<ColorString>) {
        let cs = s.into();
        let color_num = match cs.to_color() {
            Color::Red => COLOR_RED,
            Color::Green => COLOR_GREEN,
            Color::Blue => COLOR_BLUE,
            Color::Cyan => COLOR_CYAN,
            Color::Magenta => COLOR_MAGENTA,
            Color::Yellow => COLOR_YELLOW,
            Color::White => COLOR_WHITE,
            Color::Black => COLOR_BLACK,
            Color::Default => COLOR_WHITE,
        };

        self.win.attrset(COLOR_PAIR(color_num as chtype));
        let s = cs.to_string();
        let mut i = 0;
        for ch in s.chars() {
            if i as i32 + self.get_cursor_x() >= self.get_width() {
                i = 0;
                self.win.printw("\n");
            }
            self.win.printw(ch.to_string());
            i += 1;
        }

        self.win.attrset(COLOR_PAIR(COLOR_WHITE as chtype));
    }

    pub fn clear_current_line(&self) {
        self.print("\r");
        self.win.clrtoeol();
    }

    pub fn move_cursor(&self, x: i32, y: i32) {
        self.win.mv(y, x);
    }

    pub fn get_size(&self) -> (i32, i32) {
        (self.get_width(), self.get_height())
    }

    pub fn get_width(&self) -> i32 {
        self.win.get_max_x()
    }

    pub fn get_height(&self) -> i32 {
        self.win.get_max_y()
    }

    pub fn get_cursor(&self) -> (i32, i32) {
        (self.get_cursor_x(), self.get_cursor_y())
    }

    pub fn get_cursor_x(&self) -> i32 {
        self.win.get_cur_x()
    }

    pub fn get_cursor_y(&self) -> i32 {
        self.win.get_cur_y()
    }

    pub fn quit(&self) {
        endwin();
    }
}

impl Iterator for Terminal {
    type Item = Key;
    fn next(&mut self) -> Option<Key> {
        self.get_key()
    }
}

impl Iterator for &Terminal {
    type Item = Key;
    fn next(&mut self) -> Option<Key> {
        self.get_key()
    }
}

impl Drop for Terminal {
    fn drop(&mut self) {
        self.quit();
    }
}

impl Clone for Terminal {
    fn clone(&self) -> Self {
        let win = self.win.dupwin();
        Self { win }
    }
}

unsafe impl Sync for Terminal {}