Add text reflowing in pager and compose

concerns #69
embed
Manos Pitsidianakis 2019-03-14 12:00:41 +02:00
parent ea65989679
commit bbd1918d70
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
5 changed files with 115 additions and 81 deletions

View File

@ -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();
}
}
let mid_x = {
std::cmp::max(width!(area) / 2, width / 2) - width / 2
};
let mid_y = {
std::cmp::max(height!(area) / 2, 11) - 11
};
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 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),

View File

@ -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
)));
}

View File

@ -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(' ');
}

View File

@ -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);

View File

@ -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: