From c11aee9bf60bc3a72e4dd116719bfd405787c656 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sun, 31 Dec 2017 16:55:27 -0800 Subject: [PATCH] WIP switching from Rustbox to Termion for terminal handling. --- Cargo.lock | 188 ++------------ Cargo.toml | 2 +- src/main.rs | 5 +- src/term_ui/mod.rs | 601 +++++++++++++++++++++------------------------ 4 files changed, 297 insertions(+), 499 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1c4d85..d46660c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = "0.0.2" dependencies = [ "docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "ropey 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustbox 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -19,16 +19,6 @@ dependencies = [ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "bitflags" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "docopt" version = "0.8.1" @@ -41,38 +31,6 @@ dependencies = [ "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "fuchsia-zircon" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "gag" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "lazy_static" version = "0.2.11" @@ -96,91 +54,24 @@ dependencies = [ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "num" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-complex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-bigint" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-complex" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-integer" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-iter" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-rational" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "quote" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "rand" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-zircon 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "redox_syscall" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex" version = "0.2.5" @@ -207,22 +98,6 @@ dependencies = [ "unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rustbox" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gag 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "termbox-sys 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-serialize" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "serde" version = "1.0.27" @@ -276,22 +151,15 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "2.2.0" +name = "termion" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "termbox-sys" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "thread_local" version = "0.3.5" @@ -334,44 +202,19 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [metadata] "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" -"checksum bitflags 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a41f80ec2e140d19e789764fdf22d0f2da98fe7e55d26f99db59cb3d2605d327" -"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5b93718f8b3e5544fcc914c43de828ca6c6ace23e0332c6080a2977b49787a" -"checksum fuchsia-zircon 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bd510087c325af53ba24f3be8f1c081b0982319adcb8b03cad764512923ccc19" -"checksum fuchsia-zircon-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "08b3a6f13ad6b96572b53ce7af74543132f1a7055ccceb6d073dd36c54481859" -"checksum gag 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c606f5c0da18075916377e73de8aec7c140e1b6110a3cf26ae1f47873529b47e" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" "checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" -"checksum num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4083e14b542ea3eb9b5f33ff48bd373a92d78687e74f4cc0a30caeb754f0ca" -"checksum num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "bdc1494b5912f088f260b775799468d9b9209ac60885d8186a547a0476289e23" -"checksum num-complex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "58de7b4bf7cf5dbecb635a5797d489864eadd03b107930cbccf9e0fd7428b47c" -"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba" -"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01" -"checksum num-rational 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "0c7cb72a95250d8a370105c828f388932373e0e94414919891a0f945222310fe" -"checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum rand 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "9e7944d95d25ace8f377da3ac7068ce517e4c646754c43a1b1849177bbf72e59" "checksum redox_syscall 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "07b8f011e3254d5a9b318fde596d409a0001c9ae4c6e7907520c2eaa4d988c99" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa" "checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" "checksum ropey 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6eb4b74f76bc72338d8aebb193e9575398703552530e329dae718e38e4bf2ca0" -"checksum rustbox 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acc40f390f6c5e2d47ff358020103deb6ebd0e98f2df2cec3cb15e054f403ed8" -"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" "checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0" "checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5" @@ -379,8 +222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0" -"checksum termbox-sys 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "e32daa27881ea4b2ef36e4972d6cdefb241c19ac48d318381b688d1a631b8760" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" @@ -388,5 +230,3 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index 6e0f63f..eb060b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,4 @@ unicode-width = "0.1" serde = "1.*" serde_derive = "1.*" docopt = "0.8" -rustbox = "0.9" \ No newline at end of file +termion = "1.5" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 5244b09..c8331a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ extern crate docopt; extern crate ropey; -extern crate rustbox; extern crate serde; #[macro_use] extern crate serde_derive; +extern crate termion; extern crate unicode_segmentation; extern crate unicode_width; @@ -50,6 +50,7 @@ fn main() { }; // Initialize and start UI - let mut ui = TermUI::new_from_editor(editor); + let mut stdin = std::io::stdin(); + let mut ui = TermUI::new_from_editor(&mut stdin, editor); ui.main_ui_loop(); } diff --git a/src/term_ui/mod.rs b/src/term_ui/mod.rs index 0cd3a9c..d7797ff 100644 --- a/src/term_ui/mod.rs +++ b/src/term_ui/mod.rs @@ -1,315 +1,288 @@ #![allow(dead_code)] -use rustbox; -use rustbox::Color; -use editor::Editor; -use std::time::Duration; -use formatter::{block_index_and_offset, LineFormatter, LINE_BLOCK_LENGTH}; -use std::char; -use std::default::Default; +use std::cell::RefCell; use std::cmp::min; +use std::io; +use std::io::Write; + +use termion; +use termion::event::{Event, Key}; +use termion::input::TermRead; +use termion::color; +use termion::raw::{IntoRawMode, RawTerminal}; + +use editor::Editor; +use formatter::{block_index_and_offset, LineFormatter, LINE_BLOCK_LENGTH}; +use self::formatter::ConsoleLineFormatter; use string_utils::{is_line_ending, line_ending_to_str, LineEnding}; use utils::digit_count; -use self::formatter::ConsoleLineFormatter; pub mod formatter; -// Key codes -const K_ENTER: u16 = 13; -const K_TAB: u16 = 9; -const K_SPACE: u16 = 32; -const K_BACKSPACE: u16 = 127; -const K_DELETE: u16 = 65522; -const K_PAGEUP: u16 = 65519; -const K_PAGEDOWN: u16 = 65518; -const K_UP: u16 = 65517; -const K_DOWN: u16 = 65516; -const K_LEFT: u16 = 65515; -const K_RIGHT: u16 = 65514; -const K_ESC: u16 = 27; -const K_CTRL_L: u16 = 12; -const K_CTRL_O: u16 = 15; -const K_CTRL_Q: u16 = 17; -const K_CTRL_S: u16 = 19; -const K_CTRL_Y: u16 = 25; -const K_CTRL_Z: u16 = 26; +/// Generalized ui loop. +macro_rules! ui_loop { + ($term_ui:ident, draw $draw:block, key_press($key:ident) $key_press:block) => { + let mut stop = false; -pub struct TermUI { - rb: rustbox::RustBox, + loop { + // Draw the editor to screen + {$draw}; + $term_ui.present(); + + // Handle input + match $term_ui.inp.next() { + Some(Ok(Event::Key($key))) => { + let status = || -> LoopStatus { + $key_press + }(); + if status == LoopStatus::Done { + stop = true; + // break; + } + } + + _ => { + // break; + } + } + + // Check for screen resize + let (w, h) = termion::terminal_size().unwrap(); + if $term_ui.width != w as usize || $term_ui.height != h as usize { + $term_ui.width = w as usize; + $term_ui.height = h as usize; + $term_ui.editor.update_dim($term_ui.height - 1, $term_ui.width); + } + + if stop || $term_ui.quit { + break; + } + } + }; +} + +pub struct TermUI<'a> { + inp: termion::input::Events>, + out: RefCell>, editor: Editor, width: usize, height: usize, + quit: bool, } -impl TermUI { - pub fn new() -> TermUI { - let rb = match rustbox::RustBox::init(rustbox::InitOptions { - buffer_stderr: true, - ..Default::default() - }) { - Ok(rbox) => rbox, - Err(_) => panic!("Could not create Rustbox instance."), - }; - let w = rb.width(); - let h = rb.height(); - let mut editor = Editor::new(ConsoleLineFormatter::new(4)); - editor.update_dim(h - 1, w); +#[derive(Debug, Copy, Clone, PartialEq)] +enum LoopStatus { + Done, + Continue, +} - TermUI { - rb: rb, - editor: editor, - width: w, - height: h, - } +impl<'a> Drop for TermUI<'a> { + fn drop(&mut self) { + self.clear(); + write!( + self.out.borrow_mut(), + "{}{}", + termion::cursor::Show, + termion::cursor::Goto(0, 0), + ).unwrap(); + } +} + +impl<'a> TermUI<'a> { + pub fn new<'b>(stdin: &'b mut io::Stdin) -> TermUI<'b> { + TermUI::new_from_editor(stdin, Editor::new(ConsoleLineFormatter::new(4))) } - pub fn new_from_editor(ed: Editor) -> TermUI { - let rb = match rustbox::RustBox::init(rustbox::InitOptions { - buffer_stderr: true, - ..Default::default() - }) { - Ok(rbox) => rbox, - Err(_) => panic!("Could not create Rustbox instance."), - }; - let w = rb.width(); - let h = rb.height(); + pub fn new_from_editor<'b>( + stdin: &'b mut io::Stdin, + ed: Editor, + ) -> TermUI<'b> { + let out = io::stdout().into_raw_mode().unwrap(); + let (w, h) = termion::terminal_size().unwrap(); let mut editor = ed; - editor.update_dim(h - 1, w); + editor.update_dim(h as usize - 1, w as usize); TermUI { - rb: rb, + inp: stdin.lock().events(), + out: RefCell::new(out), editor: editor, - width: w, - height: h, + width: w as usize, + height: h as usize, + quit: false, } } pub fn main_ui_loop(&mut self) { - // Quitting flag - let mut quit = false; - let mut resize: Option<(usize, usize)> = None; + // Hide cursor + write!(self.out.borrow_mut(), "{}", termion::cursor::Hide).unwrap(); self.editor.update_dim(self.height - 1, self.width); self.editor.formatter.set_wrap_width(self.width as usize); - loop { - // Draw the editor to screen - self.editor.update_view_dim(); - self.editor.formatter.set_wrap_width(self.editor.view_dim.1); - self.rb.clear(); - self.draw_editor(&self.editor, (0, 0), (self.height - 1, self.width - 1)); - self.rb.present(); + ui_loop!( + self, - // Handle events. We block on the first event, so that the - // program doesn't loop like crazy, but then continue pulling - // events in a non-blocking way until we run out of events - // to handle. - let mut e = self.rb.poll_event(true); // Block until we get an event - loop { - match e { - Ok(rustbox::Event::KeyEventRaw(_, key, character)) => { - // println!(" {} {} {}", modifier, key, character); - match key { - K_CTRL_Q => { - quit = true; - break; - } + // Draw + draw { + self.editor.update_view_dim(); + self.editor.formatter.set_wrap_width(self.editor.view_dim.1); + self.clear(); + self.draw_editor(&self.editor, (0, 0), (self.height - 1, self.width - 1)); + }, - K_CTRL_S => { - self.editor.save_if_dirty(); - } - - K_CTRL_Z => { - self.editor.undo(); - } - - K_CTRL_Y => { - self.editor.redo(); - } - - K_CTRL_L => { - self.go_to_line_ui_loop(); - } - - K_PAGEUP => { - self.editor.page_up(); - } - - K_PAGEDOWN => { - self.editor.page_down(); - } - - K_UP => { - self.editor.cursor_up(1); - } - - K_DOWN => { - self.editor.cursor_down(1); - } - - K_LEFT => { - self.editor.cursor_left(1); - } - - K_RIGHT => { - self.editor.cursor_right(1); - } - - K_ENTER => { - let nl = line_ending_to_str(self.editor.line_ending_type); - self.editor.insert_text_at_cursor(nl); - } - - K_SPACE => { - self.editor.insert_text_at_cursor(" "); - } - - K_TAB => { - self.editor.insert_tab_at_cursor(); - } - - K_BACKSPACE => { - self.editor.backspace_at_cursor(); - } - - K_DELETE => { - self.editor.remove_text_in_front_of_cursor(1); - } - - // Character - 0 => { - if let Option::Some(c) = char::from_u32(character) { - self.editor.insert_text_at_cursor(&c.to_string()[..]); - } - } - - _ => {} - } + // Handle input + key_press(key) { + match key { + Key::Ctrl('q') => { + self.quit = true; + return LoopStatus::Done; } - Ok(rustbox::Event::ResizeEvent(w, h)) => { - resize = Some((h as usize, w as usize)); + Key::Ctrl('s') => { + self.editor.save_if_dirty(); } - _ => { - break; + Key::Ctrl('z') => { + self.editor.undo(); + } + + Key::Ctrl('y') => { + self.editor.redo(); + } + + Key::Ctrl('l') => { + self.go_to_line_ui_loop(); + } + + Key::PageUp => { + self.editor.page_up(); + } + + Key::PageDown => { + self.editor.page_down(); + } + + Key::Up => { + self.editor.cursor_up(1); + } + + Key::Down => { + self.editor.cursor_down(1); + } + + Key::Left => { + self.editor.cursor_left(1); + } + + Key::Right => { + self.editor.cursor_right(1); + } + + Key::Char('\n') => { + let nl = line_ending_to_str(self.editor.line_ending_type); + self.editor.insert_text_at_cursor(nl); + } + + Key::Char(' ') => { + self.editor.insert_text_at_cursor(" "); + } + + Key::Char('\t') => { + self.editor.insert_tab_at_cursor(); + } + + Key::Backspace => { + self.editor.backspace_at_cursor(); + } + + Key::Delete => { + self.editor.remove_text_in_front_of_cursor(1); + } + + // Character + Key::Char(c) => { + self.editor.insert_text_at_cursor(&c.to_string()[..]); + } + + k => { + println!("{:?}", k); } } - e = self.rb.peek_event(Duration::from_millis(0), true); // Get next event (if any) + LoopStatus::Continue } - - if let Some((h, w)) = resize { - self.width = w as usize; - self.height = h as usize; - self.editor.update_dim(self.height, self.width); - } - resize = None; - - // Quit if quit flag is set - if quit { - break; - } - } + ); } fn go_to_line_ui_loop(&mut self) { - let foreground = Color::Black; - let background = Color::Cyan; + let foreground = color::Black; + let background = color::Cyan; let mut cancel = false; - let mut confirm = false; let prefix = "Jump to line: "; let mut line = String::new(); - loop { - // Draw the editor to screen - self.editor.update_view_dim(); - self.editor.formatter.set_wrap_width(self.editor.view_dim.1); - self.rb.clear(); - self.draw_editor(&self.editor, (0, 0), (self.height - 1, self.width - 1)); - for i in 0..self.width { - self.rb - .print(i, 0, rustbox::RB_NORMAL, foreground, background, " "); - } - self.rb - .print(1, 0, rustbox::RB_NORMAL, foreground, background, prefix); - self.rb.print( - prefix.len() + 1, - 0, - rustbox::RB_NORMAL, - foreground, - background, - &line[..], - ); - self.rb.present(); + ui_loop!( + self, - // Handle events. We block on the first event, so that the - // program doesn't loop like crazy, but then continue pulling - // events in a non-blocking way until we run out of events - // to handle. - let mut e = self.rb.poll_event(true); // Block until we get an event - loop { - match e { - Ok(rustbox::Event::KeyEventRaw(_, key, character)) => { - match key { - K_ESC => { - cancel = true; - break; - } + // Draw + draw { + self.editor.update_view_dim(); + self.editor.formatter.set_wrap_width(self.editor.view_dim.1); + self.clear(); + self.draw_editor(&self.editor, (0, 0), (self.height - 1, self.width - 1)); + for i in 0..self.width { + self.draw(i, 0, " ", foreground, background); + } + self.draw(1, 0, prefix, foreground, background); + self.draw( + prefix.len() + 1, + 0, + &line[..], + foreground, + background, + ); + }, - K_ENTER => { - confirm = true; - break; - } + // Handle input + key_press(key) { + match key { + Key::Esc => { + cancel = true; + return LoopStatus::Done; + } - K_BACKSPACE => { - line.pop(); - } + Key::Char('\n') => { + return LoopStatus::Done; + } - // Character - 0 => { - if let Option::Some(c) = char::from_u32(character) { - if c.is_numeric() { - line.push(c); - } - } - } + Key::Backspace => { + line.pop(); + } - _ => {} + // Character + Key::Char(c) => { + if c.is_numeric() { + line.push(c); } } - Ok(rustbox::Event::ResizeEvent(w, h)) => { - self.width = w as usize; - self.height = h as usize; - self.editor.update_dim(self.height - 1, self.width); - } - - _ => { - break; - } + _ => {} } - e = self.rb.peek_event(Duration::from_millis(0), true); // Get next event (if any) + return LoopStatus::Continue; } + ); - // Cancel if flag is set - if cancel { - break; - } - - // Jump to line! - if confirm { - if let Ok(n) = line.parse() { - let n2: usize = n; // Weird work-around: the type of n wasn't being inferred - if n2 > 0 { - self.editor.jump_to_line(n2 - 1); - } else { - self.editor.jump_to_line(0); - } + // Jump to line! + if !cancel { + if let Ok(n) = line.parse() { + let n2: usize = n; // Weird work-around: the type of n wasn't being inferred + if n2 > 0 { + self.editor.jump_to_line(n2 - 1); + } else { + self.editor.jump_to_line(0); } - break; } } } @@ -320,27 +293,19 @@ impl TermUI { c1: (usize, usize), c2: (usize, usize), ) { - let foreground = Color::Black; - let background = Color::Cyan; + let fg = color::Black; + let bg = color::Cyan; // Fill in top row with info line color for i in c1.1..(c2.1 + 1) { - self.rb - .print(i, c1.0, rustbox::RB_NORMAL, foreground, background, " "); + self.draw(i, c1.0, " ", fg, bg); } // Filename and dirty marker let filename = editor.file_path.display(); let dirty_char = if editor.dirty { "*" } else { "" }; let name = format!("{}{}", filename, dirty_char); - self.rb.print( - c1.1 + 1, - c1.0, - rustbox::RB_NORMAL, - foreground, - background, - &name[..], - ); + self.draw(c1.1 + 1, c1.0, &name[..], fg, bg); // Percentage position in document // TODO: use view instead of cursor for calculation if there is more @@ -352,14 +317,7 @@ impl TermUI { 100 }; let pstring = format!("{}%", percentage); - self.rb.print( - c2.1 - pstring.len(), - c1.0, - rustbox::RB_NORMAL, - foreground, - background, - &pstring[..], - ); + self.draw(c2.1 - pstring.len(), c1.0, &pstring[..], fg, bg); // Text encoding info and tab style let nl = match editor.line_ending_type { @@ -378,14 +336,7 @@ impl TermUI { "UTF8:{} {}:{}", nl, soft_tabs_str, editor.soft_tab_width as usize ); - self.rb.print( - c2.1 - 30, - c1.0, - rustbox::RB_NORMAL, - foreground, - background, - &info_line[..], - ); + self.draw(c2.1 - 30, c1.0, &info_line[..], fg, bg); // Draw main text editing area self.draw_editor_text(editor, (c1.0 + 1, c1.1), c2); @@ -424,8 +375,7 @@ impl TermUI { // Fill in the gutter with the appropriate background for y in c1.0..(c2.0 + 1) { for x in c1.1..(c1.1 + gutter_width - 1) { - self.rb - .print(x, y, rustbox::RB_NORMAL, Color::White, Color::Blue, " "); + self.draw(x, y, " ", color::White, color::Blue); } } @@ -436,13 +386,12 @@ impl TermUI { let lnx = c1.1 + (gutter_width - 1 - digit_count(line_num as u32, 10) as usize); let lny = screen_line as usize; if lny >= c1.0 && lny <= c2.0 { - self.rb.print( + self.draw( lnx, lny, - rustbox::RB_NORMAL, - Color::White, - Color::Blue, &format!("{}", line_num)[..], + color::White, + color::Blue, ); } } @@ -488,59 +437,42 @@ impl TermUI { // Actually print the character if is_line_ending(g) { if at_cursor { - self.rb.print( + self.draw( px as usize, py as usize, - rustbox::RB_NORMAL, - Color::Black, - Color::White, " ", + color::White, + color::Black, ); } } else if g == "\t" { for i in 0..width { let tpx = px as usize + i; if tpx <= c2.1 { - self.rb.print( + self.draw( tpx as usize, py as usize, - rustbox::RB_NORMAL, - Color::White, - Color::Black, " ", + color::White, + color::Black, ); } } if at_cursor { - self.rb.print( + self.draw( px as usize, py as usize, - rustbox::RB_NORMAL, - Color::Black, - Color::White, " ", + color::Black, + color::White, ); } } else { if at_cursor { - self.rb.print( - px as usize, - py as usize, - rustbox::RB_NORMAL, - Color::Black, - Color::White, - g, - ); + self.draw(px as usize, py as usize, g, color::Black, color::White); } else { - self.rb.print( - px as usize, - py as usize, - rustbox::RB_NORMAL, - Color::White, - Color::Black, - g, - ); + self.draw(px as usize, py as usize, g, color::White, color::Black); } } } @@ -591,15 +523,40 @@ impl TermUI { if (px >= c1.1 as isize) && (py >= c1.0 as isize) && (px <= c2.1 as isize) && (py <= c2.0 as isize) { - self.rb.print( - px as usize, - py as usize, - rustbox::RB_NORMAL, - Color::Black, - Color::White, - " ", - ); + self.draw(px as usize, py as usize, " ", color::Black, color::White); } } } + + fn clear(&self) { + write!( + self.out.borrow_mut(), + "{}{}", + color::Bg(color::Black), + termion::clear::All + ).unwrap(); + self.out.borrow_mut().flush().unwrap(); + } + + fn present(&self) { + // TODO + } + + fn draw( + &self, + x: usize, + y: usize, + text: &str, + fg: C1, + bg: C2, + ) { + write!( + self.out.borrow_mut(), + "{}{}{}{}", + termion::cursor::Goto((x + 1) as u16, (y + 1) as u16), + color::Fg(fg), + color::Bg(bg), + text + ).unwrap(); + } }