use std::iter; use std::cmp; use std::str::Lines; use std::slice::Iter; use util::{string_with_repeat, IteratorOptionalExt}; use rustbox::{RustBox}; use rustbox::keyboard::Key; use super::common::{Rect, Canceled}; use super::RustBoxEx::{RustBoxEx, Style}; use super::input::Input; enum ToLinesIter<'a> { StringLines(Lines<'a>), SliceLines(Iter<'a, String>) } pub trait ToLines { fn to_lines<'a>(&'a self) -> ToLinesIter<'a>; } impl ToLines for String { fn to_lines<'a>(&'a self) -> ToLinesIter<'a> { ToLinesIter::StringLines(self.lines()) } } impl ToLines for Vec { fn to_lines<'a>(&'a self) -> ToLinesIter<'a> { ToLinesIter::SliceLines(self.iter()) } } impl<'a> Iterator for ToLinesIter<'a> { type Item = &'a str; fn next(&mut self) -> Option<&'a str> { match *self { ToLinesIter::StringLines(ref mut lines) => lines.next(), ToLinesIter::SliceLines(ref mut lines) => lines.next().map(|x| &x[..]), } } } impl<'a> DoubleEndedIterator for ToLinesIter<'a> { fn next_back(&mut self) -> Option<&'a str> { match *self { ToLinesIter::StringLines(ref mut lines) => lines.next_back(), ToLinesIter::SliceLines(ref mut lines) => lines.next_back().map(|x| &x[..]), } } } pub enum OverlayActions { Cancel, } pub struct OverlayText { text: Box, reverse: bool, pub on_cancel: Canceled, } impl OverlayText { pub fn with_text(text: String, rev: bool) -> OverlayText { OverlayText { text: Box::new(text), reverse: rev, on_cancel: Default::default(), } } pub fn with_logs(text: Vec, rev: bool) -> OverlayText { OverlayText { text: Box::new(text), reverse: rev, on_cancel: Default::default(), } } pub fn input(&mut self, input: &Input, key: Key) -> bool { let action = if let Some(action) = input.overlay_input(key) { action } else { return false; }; match action { OverlayActions::Cancel => { self.on_cancel.signal(None); true } } } pub fn draw(&mut self, rb: &RustBox, area: Rect, has_focus: bool) { let repeat: iter::Repeat> = iter::repeat(None); let iter = self.text.to_lines().optional(self.reverse, |it| it.rev(), |it| it).map( // Chomp the width of each line |line| Some(&line[0..cmp::min(line.len(), (area.width) as usize)]) ) .chain(repeat) // Take only as many lines as needed .take((area.height) as usize) // And count them .enumerate(); for (i, opt_line) in iter { // Clean the line rb.print_style(area.left as usize, (area.top + i as isize) as usize, Style::Default, &string_with_repeat(' ', (area.width) as usize)); // And draw the text if there is one if let Some(line) = opt_line { rb.print_style(area.left as usize, (area.top + i as isize) as usize, Style::Default, line); } } if has_focus { rb.set_cursor(0, 0); } } }