From 9db4c9f55e10d8cf52bf308144c3439a40744d5a Mon Sep 17 00:00:00 2001 From: Ferran Basora Date: Sun, 3 Mar 2019 18:56:00 +0000 Subject: [PATCH] New view struct to manage the UI --- src/main.rs | 128 ++++++--------------------------------------- src/state.rs | 14 +---- src/view.rs | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 124 deletions(-) create mode 100644 src/view.rs diff --git a/src/main.rs b/src/main.rs index 6044a06..91c200f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,14 +4,11 @@ extern crate clap; mod state; mod alphabets; mod colors; +mod view; use self::clap::{Arg, App}; -use std::char; -use std::default::Default; use std::process::Command; use clap::crate_version; -use rustbox::{Color, RustBox, OutputMode}; -use rustbox::Key; fn exec_command(command: String) -> std::process::Output { let args: Vec<_> = command.split(" ").collect(); @@ -96,116 +93,25 @@ fn main() { let lines = output.split("\n").collect::>(); let mut state = state::State::new(&lines, alphabet); - let mut selected; - let mut paste = false; - let mut typed_hint: String = "".to_owned(); - let matches = state.matches(reverse, unique); - let longest_hint = matches.iter().filter(|&m| m.hint.clone().is_some()).last().unwrap().hint.clone().expect("Unknown hint").len(); + let selected = { + let mut viewbox = view::View::new( + &mut state, + reverse, + unique, + position, + select_foreground_color, + foreground_color, + background_color, + hint_foreground_color, + hint_background_color + ); - { - let mut rustbox = match RustBox::init(Default::default()) { - Result::Ok(v) => v, - Result::Err(e) => panic!("{}", e), - }; + viewbox.present() + }; - rustbox.set_output_mode(OutputMode::EightBit); - - for (index, line) in state.lines.iter().enumerate() { - let clean = line.trim_end_matches(|c: char| c.is_whitespace()); - - if clean.len() > 0 { - let formatted = format!("{}\n", line).to_string(); - rustbox.print(0, index, rustbox::RB_NORMAL, Color::White, Color::Black, formatted.as_str()); - } - } - - loop { - selected = matches.last(); - - match matches.iter().enumerate().find(|&h| h.0 == state.skip) { - Some(hm) => { - selected = Some(hm.1); - } - _ => {} - } - - for mat in matches.iter() { - let selected_color = if selected == Some(mat) { - select_foreground_color - } else { - foreground_color - }; - - // Find long utf sequences and extract it from mat.x - let line = &lines[mat.y as usize]; - let prefix = &line[0..mat.x as usize]; - let extra = prefix.len() - prefix.chars().count(); - let offset = (mat.x as usize) - extra; - - rustbox.print(offset, mat.y as usize, rustbox::RB_NORMAL, selected_color, background_color, mat.text); - - if let Some(ref hint) = mat.hint { - let extra_position = if position == "left" { 0 } else { mat.text.len() - mat.hint.clone().unwrap().len() }; - - rustbox.print(offset + extra_position, mat.y as usize, rustbox::RB_BOLD, hint_foreground_color, hint_background_color, hint.as_str()); - } - } - - rustbox.present(); - - match rustbox.poll_event(false) { - Ok(rustbox::Event::KeyEvent(key)) => { - match key { - Key::Esc => { break; } - Key::Enter => { - selected = Some(matches.first().unwrap()); - - match matches.iter().enumerate().find(|&h| h.0 == state.skip) { - Some(hm) => { - selected = Some(hm.1); - } - _ => {} - } - - break; - } - Key::Up => { state.prev(); } - Key::Down => { state.next(); } - Key::Left => { state.prev(); } - Key::Right => { state.next(); } - Key::Char(ch) => { - let key = ch.to_string(); - let lower_key = key.to_lowercase(); - - typed_hint.push_str(lower_key.as_str()); - - match matches.iter().find(|mat| mat.hint == Some(typed_hint.clone())) { - Some(mat) => { - selected = Some(mat); - paste = key != lower_key; - - break; - }, - None => { - if typed_hint.len() >= longest_hint { - selected = None; - break; - } - } - } - } - _ => {} - } - } - Err(e) => panic!("{}", e), - _ => { } - } - } - } - - if let Some(mat) = selected { - exec_command(str::replace(command, "{}", mat.text)); + if let Some((text, paste)) = selected { + exec_command(str::replace(command, "{}", text.as_str())); if paste { exec_command(upcase_command.to_string()); diff --git a/src/state.rs b/src/state.rs index e48db9f..c490387 100644 --- a/src/state.rs +++ b/src/state.rs @@ -39,15 +39,13 @@ impl<'a> PartialEq for Match<'a> { pub struct State<'a> { pub lines: &'a Vec<&'a str>, alphabet: &'a str, - pub skip: usize, } impl<'a> State<'a> { pub fn new(lines: &'a Vec<&'a str>, alphabet: &'a str) -> State<'a> { State{ lines: lines, - alphabet: alphabet, - skip: 0 + alphabet: alphabet } } @@ -140,16 +138,6 @@ impl<'a> State<'a> { return matches; } - - pub fn prev(&mut self) { - if self.skip > 0 { - self.skip = self.skip - 1; - } - } - - pub fn next(&mut self) { - self.skip = self.skip + 1; - } } #[cfg(test)] diff --git a/src/view.rs b/src/view.rs new file mode 100644 index 0000000..c942863 --- /dev/null +++ b/src/view.rs @@ -0,0 +1,145 @@ +use std::char; +use std::default::Default; +use rustbox::{Color, RustBox, OutputMode}; +use rustbox::Key; +use super::*; + +pub struct View<'a> { + state: &'a mut state::State<'a>, + skip: usize, + reverse: bool, + unique: bool, + position: &'a str, + select_foreground_color: Color, + foreground_color: Color, + background_color: Color, + hint_background_color: Color, + hint_foreground_color: Color +} + +impl<'a> View<'a> { + pub fn new(state: &'a mut state::State<'a>, reverse: bool, unique: bool, position: &'a str, select_foreground_color: Color, foreground_color: Color, background_color: Color, hint_foreground_color: Color, hint_background_color: Color) -> View<'a> { + View{ + state: state, + skip: 0, + reverse: reverse, + unique: unique, + position: position, + select_foreground_color: select_foreground_color, + foreground_color: foreground_color, + background_color: background_color, + hint_foreground_color: hint_foreground_color, + hint_background_color: hint_background_color + } + } + + pub fn prev(&mut self) { + if self.skip > 0 { + self.skip = self.skip - 1; + } + } + + pub fn next(&mut self) { + self.skip = self.skip + 1; + } + + pub fn present(&mut self) -> Option<(String, bool)> { + let mut rustbox = match RustBox::init(Default::default()) { + Result::Ok(v) => v, + Result::Err(e) => panic!("{}", e), + }; + + rustbox.set_output_mode(OutputMode::EightBit); + + for (index, line) in self.state.lines.iter().enumerate() { + let clean = line.trim_end_matches(|c: char| c.is_whitespace()); + + if clean.len() > 0 { + let formatted = format!("{}\n", line).to_string(); + rustbox.print(0, index, rustbox::RB_NORMAL, Color::White, Color::Black, formatted.as_str()); + } + } + + let mut typed_hint: String = "".to_owned(); + let matches = self.state.matches(self.reverse, self.unique); + let longest_hint = matches.iter().filter(|&m| m.hint.clone().is_some()).last().unwrap().hint.clone().expect("Unknown hint").len(); + let mut selected; + + loop { + selected = matches.last(); + + match matches.iter().enumerate().find(|&h| h.0 == self.skip) { + Some(hm) => { + selected = Some(hm.1); + } + _ => {} + } + + for mat in matches.iter() { + let selected_color = if selected == Some(mat) { + self.select_foreground_color + } else { + self.foreground_color + }; + + // Find long utf sequences and extract it from mat.x + let line = &self.state.lines[mat.y as usize]; + let prefix = &line[0..mat.x as usize]; + let extra = prefix.len() - prefix.chars().count(); + let offset = (mat.x as usize) - extra; + + rustbox.print(offset, mat.y as usize, rustbox::RB_NORMAL, selected_color, self.background_color, mat.text); + + if let Some(ref hint) = mat.hint { + let extra_position = if self.position == "left" { 0 } else { mat.text.len() - mat.hint.clone().unwrap().len() }; + + rustbox.print(offset + extra_position, mat.y as usize, rustbox::RB_BOLD, self.hint_foreground_color, self.hint_background_color, hint.as_str()); + } + } + + rustbox.present(); + + match rustbox.poll_event(false) { + Ok(rustbox::Event::KeyEvent(key)) => { + match key { + Key::Esc => { break; } + Key::Enter => { + match matches.iter().enumerate().find(|&h| h.0 == self.skip) { + Some(hm) => { + return Some((hm.1.text.to_string(), false)) + } + _ => panic!("Match not found?"), + } + } + Key::Up => { self.prev(); } + Key::Down => { self.next(); } + Key::Left => { self.prev(); } + Key::Right => { self.next(); } + Key::Char(ch) => { + let key = ch.to_string(); + let lower_key = key.to_lowercase(); + + typed_hint.push_str(lower_key.as_str()); + + match matches.iter().find(|mat| mat.hint == Some(typed_hint.clone())) { + Some(mat) => { + return Some((mat.text.to_string(), key != lower_key)) + }, + None => { + if typed_hint.len() >= longest_hint { + break; + } + } + } + } + _ => {} + } + } + Err(e) => panic!("{}", e), + _ => { } + } + } + + None + } +}