summaryrefslogtreecommitdiffstats
path: root/ui/src
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2019-03-14 12:00:41 +0200
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-06-10 19:40:39 +0300
commitbbd1918d703645a96422eece10eb1998201412d7 (patch)
tree7fc5707696c207e6273f2bd1e16a6b8e5901edae /ui/src
parentea659896799d924c656bde1d73fa4be2004f158c (diff)
Add text reflowing in pager and compose
concerns #69
Diffstat (limited to 'ui/src')
-rw-r--r--ui/src/components/mail/compose.rs78
-rw-r--r--ui/src/components/mail/view/envelope.rs6
-rw-r--r--ui/src/components/utilities.rs101
-rw-r--r--ui/src/terminal/cells.rs8
-rw-r--r--ui/src/terminal/position.rs5
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: