diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-03-14 12:00:41 +0200 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-06-10 19:40:39 +0300 |
commit | bbd1918d703645a96422eece10eb1998201412d7 (patch) | |
tree | 7fc5707696c207e6273f2bd1e16a6b8e5901edae | |
parent | ea659896799d924c656bde1d73fa4be2004f158c (diff) |
Add text reflowing in pager and compose
concerns #69
-rw-r--r-- | ui/src/components/mail/compose.rs | 78 | ||||
-rw-r--r-- | ui/src/components/mail/view/envelope.rs | 6 | ||||
-rw-r--r-- | ui/src/components/utilities.rs | 101 | ||||
-rw-r--r-- | ui/src/terminal/cells.rs | 8 | ||||
-rw-r--r-- | ui/src/terminal/position.rs | 5 |
5 files changed, 116 insertions, 82 deletions
diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index afc06f59..5b8de117 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -218,23 +218,33 @@ impl Composer { impl Component for Composer { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { - if !self.initialized { - self.update_form(); - self.initialized = true; - } clear_area(grid, area); - let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); let upper_left = set_y(upper_left, get_y(upper_left) + 1); - let header_height = self.form.len() + 1; + + if self.dirty { + self.draft.headers_mut().insert( + "From".into(), + get_display_name(context, self.account_cursor), + ); + self.dirty = false; + } + let width = if width!(area) > 80 && self.reply_context.is_some() { width!(area) / 2 } else { width!(area) }; + if !self.initialized { + self.pager.update_from_str(self.draft.body(), Some(77)); + self.update_form(); + self.initialized = true; + } + let header_height = self.form.len() + 1; + let mid = if width > 80 { let width = width - 80; let mid = if self.reply_context.is_some() { @@ -273,27 +283,19 @@ impl Component for Composer { } if self.dirty { - for i in get_x(upper_left) + mid + 1..=get_x(upper_left) + mid + 79 { + for i in get_x(upper_left) + mid + 1..=get_x(upper_left) + mid + width.saturating_sub(0) { //set_and_join_box(grid, (i, header_height), HORZ_BOUNDARY); - grid[(i, header_height)].set_fg(Color::Default); - grid[(i, header_height)].set_bg(Color::Default); + //grid[(i, header_height)].set_fg(Color::Default); + //grid[(i, header_height)].set_bg(Color::Default); } } - let header_area = (set_x(upper_left, mid + 1), (mid + 78, header_height + 1)); + let header_area = (pos_inc(upper_left, (mid + 1, 0)), (get_x(bottom_right).saturating_sub(mid), get_y(upper_left) + header_height + 1)); let body_area = ( - (mid + 1, header_height + 2), - (mid + 78, get_y(bottom_right)), + pos_inc(upper_left, (mid + 1, header_height + 2)), + pos_dec(bottom_right, ((mid, 0))), ); - if self.dirty { - self.draft.headers_mut().insert( - "From".into(), - get_display_name(context, self.account_cursor), - ); - self.dirty = false; - } - /* Regardless of view mode, do the following */ self.form.draw(grid, header_area, context); @@ -304,27 +306,18 @@ impl Component for Composer { }, ViewMode::Discard(_) => { /* Let user choose whether to quit with/without saving or cancel */ - let mid_x = width!(area) / 2; - let mid_y = height!(area) / 2; - for x in mid_x - 40..=mid_x + 40 { - for y in mid_y - 11..=mid_y + 11 { - grid[(x, y)] = Cell::default(); - } - } - - for i in mid_x - 40..=mid_x + 40 { - set_and_join_box(grid, (i, mid_y - 11), HORZ_BOUNDARY); - - set_and_join_box(grid, (i, mid_y + 11), HORZ_BOUNDARY); - } - - for i in mid_y - 11..=mid_y + 11 { - set_and_join_box(grid, (mid_x - 40, i), VERT_BOUNDARY); - - set_and_join_box(grid, (mid_x + 40, i), VERT_BOUNDARY); - } - - let area = ((mid_x - 20, mid_y - 7), (mid_x + 39, mid_y + 10)); + let mid_x = { + std::cmp::max(width!(area) / 2, width / 2) - width / 2 + }; + let mid_y = { + std::cmp::max(height!(area) / 2, 11) - 11 + }; + + let upper_left = upper_left!(body_area); + let bottom_right = bottom_right!(body_area); + let area = (pos_inc(upper_left, (mid_x, mid_y)), pos_dec(bottom_right, (mid_x, mid_y))); + create_box(grid, area); + let area = (pos_inc(upper_left, (mid_x + 2, mid_y + 2)), pos_dec(bottom_right, (mid_x.saturating_sub(2), mid_y.saturating_sub(2)))); let (_, y) = write_string_to_grid( &format!("Draft \"{:10}\"", self.draft.headers()["Subject"]), @@ -483,8 +476,7 @@ impl Component for Composer { .expect("failed to execute process"); let result = f.read_to_string(); self.draft = Draft::from_str(result.as_str()).unwrap(); - self.pager.update_from_str(self.draft.body()); - self.update_form(); + self.initialized = false; context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::Fork(ForkType::Finished), diff --git a/ui/src/components/mail/view/envelope.rs b/ui/src/components/mail/view/envelope.rs index f08c9018..e01b5496 100644 --- a/ui/src/components/mail/view/envelope.rs +++ b/ui/src/components/mail/view/envelope.rs @@ -377,9 +377,9 @@ impl Component for EnvelopeView { match u.content_type() { ContentType::MessageRfc822 => { self.mode = ViewMode::Subview; - self.subview = Some(Box::new(Pager::from_str( - &String::from_utf8_lossy(&decode_rec(u, None)).to_string(), - None, + self.subview = Some(Box::new(Pager::from_string( + String::from_utf8_lossy(&decode_rec(u, None)).to_string(), context, + None, None ))); } diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index b44071ea..bf0d8bc5 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -215,10 +215,10 @@ pub enum PageMovement { /// current view of the text. It is responsible for scrolling etc. #[derive(Default, Debug)] pub struct Pager { + text: String, cursor_pos: usize, max_cursor_pos: Option<usize>, height: usize, - width: usize, dirty: bool, content: CellBuffer, @@ -233,13 +233,18 @@ impl fmt::Display for Pager { } impl Pager { - pub fn update_from_str(&mut self, text: &str) { - let lines: Vec<&str> = text.trim().split('\n').collect(); + pub fn update_from_str(&mut self, text: &str, width: Option<usize>) { + let lines: Vec<&str> = if let Some(width) = width { + word_break_string(text, width) + } else { + text.trim().split('\n').collect() + }; + let height = lines.len() + 1; - let width = lines.iter().map(|l| l.len()).max().unwrap_or(0); + let width = width.unwrap_or_else(|| lines.iter().map(|l| l.len()).max().unwrap_or(0)); let mut content = CellBuffer::new(width, height, Cell::with_char(' ')); - //interpret_format_flowed(&text); - Pager::print_string(&mut content, text); + Pager::print_string(&mut content, lines); + self.text = text.to_string(); self.content = content; self.height = height; self.width = width; @@ -247,7 +252,7 @@ impl Pager { self.cursor_pos = 0; self.max_cursor_pos = None; } - pub fn from_string(mut text: String, context: &mut Context, cursor_pos: Option<usize>) -> Self { + pub fn from_string(mut text: String, context: &mut Context, cursor_pos: Option<usize>, width: Option<usize>) -> Self { let pager_filter: Option<&String> = context.settings.pager.filter.as_ref(); //let format_flowed: bool = context.settings.pager.format_flowed; if let Some(bin) = pager_filter { @@ -273,28 +278,45 @@ impl Pager { ).to_string(); } - let lines: Vec<&str> = text.trim().split('\n').collect(); - let height = lines.len() + 1; - let width = lines.iter().map(|l| l.len()).max().unwrap_or(0); - let mut content = CellBuffer::new(width, height, Cell::with_char(' ')); - //interpret_format_flowed(&text); - Pager::print_string(&mut content, &text); + + let content = { + let lines: Vec<&str> = if let Some(width) = width { + word_break_string(text.as_str(), width) + } else { + text.trim().split('\n').collect() + }; + + let height = lines.len() + 1; + let width = width.unwrap_or_else(|| lines.iter().map(|l| l.len()).max().unwrap_or(0)); + let mut content = CellBuffer::new(width, height, Cell::with_char(' ')); + //interpret_format_flowed(&text); + Pager::print_string(&mut content, lines); + content + }; Pager { + text: text, cursor_pos: cursor_pos.unwrap_or(0), - height, - width, + height: content.size().1, + width: content.size().0, dirty: true, content, ..Default::default() } } - pub fn from_str(s: &str, cursor_pos: Option<usize>) -> Self { - let lines: Vec<&str> = s.trim().split('\n').collect(); - let height = lines.len(); - let width = lines.iter().map(|l| l.len()).max().unwrap_or(0); + pub fn from_str(text: &str, cursor_pos: Option<usize>, width: Option<usize>) -> Self { + let lines: Vec<&str> = if let Some(width) = width { + word_break_string(text, width) + } else { + text.trim().split('\n').collect() + }; + + let height = lines.len() + 1; + let width = width.unwrap_or_else(|| lines.iter().map(|l| l.len()).max().unwrap_or(0)); let mut content = CellBuffer::new(width, height, Cell::with_char(' ')); - Pager::print_string(&mut content, s); + + Pager::print_string(&mut content, lines); Pager { + text: text.to_string(), cursor_pos: cursor_pos.unwrap_or(0), height, width, @@ -306,6 +328,7 @@ impl Pager { pub fn from_buf(content: CellBuffer, cursor_pos: Option<usize>) -> Self { let (width, height) = content.size(); Pager { + text: String::new(), cursor_pos: cursor_pos.unwrap_or(0), height, width, @@ -314,20 +337,17 @@ impl Pager { ..Default::default() } } - pub fn print_string(content: &mut CellBuffer, s: &str) { - let lines: Vec<&str> = s.trim().split('\n').collect(); - let width = lines.iter().map(|l| l.len()).max().unwrap_or(0); - if width > 0 { - for (i, l) in lines.iter().enumerate() { - write_string_to_grid( - l, - content, - Color::Default, - Color::Default, - ((0, i), (width - 1, i)), - true, + pub fn print_string(content: &mut CellBuffer, lines: Vec<&str>) { + let width = content.size().0; + for (i, l) in lines.iter().enumerate() { + write_string_to_grid( + l, + content, + Color::Default, + Color::Default, + ((0, i), (width - 1, i)), + true, ); - } } } pub fn cursor_pos(&self) -> usize { @@ -386,6 +406,17 @@ impl Component for Pager { //let pager_stop: bool = context.settings.pager.pager_stop; //let rows = y(bottom_right) - y(upper_left); //let page_length = rows / self.height; + let width = width!(area); + if width != self.width { + // Reflow text. + let lines: Vec<&str> = word_break_string(self.text.as_str(), width); + let height = lines.len() + 1; + self.width = width; + self.height = height; + self.content = CellBuffer::new(width, height, Cell::with_char(' ')); + Pager::print_string(&mut self.content, lines); + } + let pos = copy_area_with_break( grid, &self.content, @@ -788,7 +819,9 @@ impl Tabbed { } let (cols, _) = grid.size(); let cslice: &mut [Cell] = grid; - for c in cslice[(y * cols) + x - 1..(y * cols) + cols].iter_mut() { + //TODO: bounds check + let cslice_len = cslice.len(); + for c in cslice[(y * cols) + x.saturating_sub(1)..std::cmp::min((y * cols) + x.saturating_sub(1), cslice_len)].iter_mut() { c.set_bg(Color::Byte(7)); c.set_ch(' '); } diff --git a/ui/src/terminal/cells.rs b/ui/src/terminal/cells.rs index fd10a883..fff03d65 100644 --- a/ui/src/terminal/cells.rs +++ b/ui/src/terminal/cells.rs @@ -702,10 +702,14 @@ pub fn write_string_to_grid( let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); let (mut x, mut y) = upper_left; + if y == get_y(bounds) || x == get_x(bounds) { + return (x, y); + } + if y > (get_y(bottom_right)) || x > get_x(bottom_right) - || y >= get_y(bounds) - || x >= get_x(bounds) + || y > get_y(bounds) + || x > get_x(bounds) { eprintln!(" Invalid area with string {} and area {:?}", s, area); return (x, y); diff --git a/ui/src/terminal/position.rs b/ui/src/terminal/position.rs index b56b4da1..96036053 100644 --- a/ui/src/terminal/position.rs +++ b/ui/src/terminal/position.rs @@ -49,6 +49,11 @@ pub fn pos_inc(p: Pos, inc: (usize, usize)) -> Pos { (p.0 + inc.0, p.1 + inc.1) } +#[inline(always)] +pub fn pos_dec(p: Pos, dec: (usize, usize)) -> Pos { + (p.0.saturating_sub(dec.0), p.1.saturating_sub(dec.1)) +} + /// An `Area` consists of two points: the upper left and bottom right corners. /// /// Example: |