ui/MailView: implement headers_sticky option

Kind of hacky, I don't like the way it is done but I'm willing to
compromise.
jmap
Manos Pitsidianakis 2019-11-24 20:38:30 +02:00
parent af365fa8d4
commit db197aaffe
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 209 additions and 199 deletions

View File

@ -77,6 +77,9 @@ pub struct MailView {
dirty: bool, dirty: bool,
mode: ViewMode, mode: ViewMode,
expand_headers: bool, expand_headers: bool,
headers_no: usize,
headers_cursor: usize,
force_draw_headers: bool,
cmd_buf: String, cmd_buf: String,
id: ComponentId, id: ComponentId,
@ -116,6 +119,10 @@ impl MailView {
mode: ViewMode::Normal, mode: ViewMode::Normal,
expand_headers: false, expand_headers: false,
headers_no: 5,
headers_cursor: 0,
force_draw_headers: false,
cmd_buf: String::with_capacity(4), cmd_buf: String::with_capacity(4),
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
} }
@ -300,7 +307,7 @@ impl MailView {
impl Component for MailView { impl Component for MailView {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.is_dirty() { if !self.is_dirty() && !self.force_draw_headers {
return; return;
} }
let upper_left = upper_left!(area); let upper_left = upper_left!(area);
@ -344,114 +351,60 @@ impl Component for MailView {
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
get_y(upper_left) - 1 get_y(upper_left) - 1
} else { } else {
let (x, y) = write_string_to_grid( let height_p = self.pager.size().1;
&format!("Date: {}", envelope.date_as_str()),
grid, let height = height!(area) - self.headers_no - 1;
header_fg,
Color::Default, self.headers_no = 0;
Attr::Default, let mut skip_header_ctr = self.headers_cursor;
area, let sticky = context.settings.pager.headers_sticky || height_p < height;
Some(get_x(upper_left)), let (_, mut y) = upper_left;
); macro_rules! print_header {
for x in x..=get_x(bottom_right) { ($($string:expr)+) => {
grid[(x, y)].set_ch(' '); $({
grid[(x, y)].set_bg(Color::Default); if sticky || skip_header_ctr == 0 {
grid[(x, y)].set_fg(Color::Default); let (_x, _y) = write_string_to_grid(
&$string,
grid,
header_fg,
Color::Default,
Attr::Default,
(set_y(upper_left, y), bottom_right),
Some(get_x(upper_left)),
);
for x in _x..=get_x(bottom_right) {
grid[(x, _y)].set_ch(' ');
grid[(x, _y)].set_bg(Color::Default);
grid[(x, _y)].set_fg(Color::Default);
}
y = _y + 1;
} else {
skip_header_ctr -= 1;
}
self.headers_no += 1;
})+
};
} }
let (x, y) = write_string_to_grid( print_header!(
&format!("From: {}", envelope.field_from_to_string()), format!("Date: {}", envelope.date_as_str())
grid, format!("From: {}", envelope.field_from_to_string())
header_fg, format!("To: {}", envelope.field_to_to_string())
Color::Default, format!("Subject: {}", envelope.subject())
Attr::Default, format!("Message-ID: <{}>", envelope.message_id_raw())
(set_y(upper_left, y + 1), bottom_right),
Some(get_x(upper_left)),
); );
for x in x..=get_x(bottom_right) {
grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default);
grid[(x, y)].set_fg(Color::Default);
}
let (x, y) = write_string_to_grid(
&format!("To: {}", envelope.field_to_to_string()),
grid,
header_fg,
Color::Default,
Attr::Default,
(set_y(upper_left, y + 1), bottom_right),
Some(get_x(upper_left)),
);
for x in x..=get_x(bottom_right) {
grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default);
grid[(x, y)].set_fg(Color::Default);
}
let (x, y) = write_string_to_grid(
&format!("Subject: {}", envelope.subject()),
grid,
header_fg,
Color::Default,
Attr::Default,
(set_y(upper_left, y + 1), bottom_right),
Some(get_x(upper_left)),
);
for x in x..=get_x(bottom_right) {
grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default);
grid[(x, y)].set_fg(Color::Default);
}
let (x, mut y) = write_string_to_grid(
&format!("Message-ID: <{}>", envelope.message_id_raw()),
grid,
header_fg,
Color::Default,
Attr::Default,
(set_y(upper_left, y + 1), bottom_right),
Some(get_x(upper_left)),
);
for x in x..=get_x(bottom_right) {
grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default);
grid[(x, y)].set_fg(Color::Default);
}
if self.expand_headers && envelope.in_reply_to().is_some() { if self.expand_headers && envelope.in_reply_to().is_some() {
let (x, _y) = write_string_to_grid( print_header!(
&format!("In-Reply-To: {}", envelope.in_reply_to_display().unwrap()), format!("In-Reply-To: {}", envelope.in_reply_to_display().unwrap())
grid, format!(
header_fg,
Color::Default,
Attr::Default,
(set_y(upper_left, y + 1), bottom_right),
Some(get_x(upper_left)),
);
for x in x..=get_x(bottom_right) {
grid[(x, _y)].set_ch(' ');
grid[(x, _y)].set_bg(Color::Default);
grid[(x, _y)].set_fg(Color::Default);
}
let (x, _y) = write_string_to_grid(
&format!(
"References: {}", "References: {}",
envelope envelope
.references() .references()
.iter() .iter()
.map(std::string::ToString::to_string) .map(std::string::ToString::to_string)
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(", ") .join(", ")
), )
grid,
header_fg,
Color::Default,
Attr::Default,
(set_y(upper_left, _y + 1), bottom_right),
Some(get_x(upper_left)),
); );
for x in x..=get_x(bottom_right) {
grid[(x, _y)].set_ch(' ');
grid[(x, _y)].set_bg(Color::Default);
grid[(x, _y)].set_fg(Color::Default);
}
y = _y;
} }
if let Some(list_management::ListActions { if let Some(list_management::ListActions {
ref id, ref id,
@ -461,106 +414,129 @@ impl Component for MailView {
}) = list_management::ListActions::detect(&envelope) }) = list_management::ListActions::detect(&envelope)
{ {
let mut x = get_x(upper_left); let mut x = get_x(upper_left);
y += 1;
if let Some(id) = id { if let Some(id) = id {
let (_x, _) = write_string_to_grid( if sticky || skip_header_ctr == 0 {
"List-ID: ", let (_x, _) = write_string_to_grid(
grid, "List-ID: ",
header_fg, grid,
Color::Default, header_fg,
Attr::Default, Color::Default,
(set_y(upper_left, y), bottom_right), Attr::Default,
None, (set_y(upper_left, y), bottom_right),
); None,
let (_x, _y) = write_string_to_grid( );
id, let (_x, _y) = write_string_to_grid(
grid, id,
Color::Default, grid,
Color::Default, Color::Default,
Attr::Default, Color::Default,
((_x, y), bottom_right), Attr::Default,
None, ((_x, y), bottom_right),
); None,
x = _x; );
if _y != y { x = _x;
x = get_x(upper_left); if _y != y {
x = get_x(upper_left);
}
y = _y;
} }
y = _y; self.headers_no += 1;
} }
if archive.is_some() || post.is_some() || unsubscribe.is_some() { if sticky || skip_header_ctr == 0 {
let (_x, _y) = write_string_to_grid( if archive.is_some() || post.is_some() || unsubscribe.is_some() {
" Available actions: [ ", let (_x, _y) = write_string_to_grid(
grid, " Available actions: [ ",
header_fg, grid,
Color::Default, header_fg,
Attr::Default, Color::Default,
((x, y), bottom_right), Attr::Default,
Some(get_x(upper_left)), ((x, y), bottom_right),
); Some(get_x(upper_left)),
x = _x; );
y = _y; x = _x;
} y = _y;
if archive.is_some() {
let (_x, _y) = write_string_to_grid(
"list-archive, ",
grid,
Color::Default,
Color::Default,
Attr::Default,
((x, y), bottom_right),
Some(get_x(upper_left)),
);
x = _x;
y = _y;
}
if post.is_some() {
let (_x, _y) = write_string_to_grid(
"list-post, ",
grid,
Color::Default,
Color::Default,
Attr::Default,
((x, y), bottom_right),
Some(get_x(upper_left)),
);
x = _x;
y = _y;
}
if unsubscribe.is_some() {
let (_x, _y) = write_string_to_grid(
"list-unsubscribe, ",
grid,
Color::Default,
Color::Default,
Attr::Default,
((x, y), bottom_right),
Some(get_x(upper_left)),
);
x = _x;
y = _y;
}
if archive.is_some() || post.is_some() || unsubscribe.is_some() {
if x >= 2 {
grid[(x - 2, y)].set_ch(' ');
} }
if x > 0 { if archive.is_some() {
grid[(x - 1, y)].set_fg(header_fg); let (_x, _y) = write_string_to_grid(
grid[(x - 1, y)].set_bg(Color::Default); "list-archive, ",
grid[(x - 1, y)].set_ch(']'); grid,
Color::Default,
Color::Default,
Attr::Default,
((x, y), bottom_right),
Some(get_x(upper_left)),
);
x = _x;
y = _y;
} }
} if post.is_some() {
for x in x..=get_x(bottom_right) { let (_x, _y) = write_string_to_grid(
grid[(x, y)].set_ch(' '); "list-post, ",
grid[(x, y)].set_bg(Color::Default); grid,
grid[(x, y)].set_fg(Color::Default); Color::Default,
Color::Default,
Attr::Default,
((x, y), bottom_right),
Some(get_x(upper_left)),
);
x = _x;
y = _y;
}
if unsubscribe.is_some() {
let (_x, _y) = write_string_to_grid(
"list-unsubscribe, ",
grid,
Color::Default,
Color::Default,
Attr::Default,
((x, y), bottom_right),
Some(get_x(upper_left)),
);
x = _x;
y = _y;
}
if archive.is_some() || post.is_some() || unsubscribe.is_some() {
if x >= 2 {
grid[(x - 2, y)].set_ch(' ');
}
if x > 0 {
grid[(x - 1, y)].set_fg(header_fg);
grid[(x - 1, y)].set_bg(Color::Default);
grid[(x - 1, y)].set_ch(']');
}
}
for x in x..=get_x(bottom_right) {
grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default);
grid[(x, y)].set_fg(Color::Default);
}
y += 1;
} }
} }
clear_area(grid, (set_y(upper_left, y + 1), set_y(bottom_right, y + 1))); self.force_draw_headers = false;
clear_area(grid, (set_y(upper_left, y), set_y(bottom_right, y + 1)));
context context
.dirty_areas .dirty_areas
.push_back((upper_left, set_y(bottom_right, y + 1))); .push_back((upper_left, set_y(bottom_right, y + 3)));
y + 1 if !context.settings.pager.headers_sticky {
let height_p = self.pager.size().1;
let height = height!(area) - y - 1;
if self.pager.cursor_pos() >= self.headers_no {
get_y(upper_left)
} else if height_p > height && self.headers_cursor < self.headers_no + 1 {
y + 1
} else if self.headers_cursor == 0 {
y + 1
} else if height_p < height {
y + 1
} else {
get_y(upper_left)
}
} else {
y + 1
}
} }
}; };
@ -632,12 +608,12 @@ impl Component for MailView {
match self.mode { match self.mode {
ViewMode::Subview if self.subview.is_some() => { ViewMode::Subview if self.subview.is_some() => {
if let Some(s) = self.subview.as_mut() { if let Some(s) = self.subview.as_mut() {
s.draw(grid, (set_y(upper_left, y + 1), bottom_right), context); s.draw(grid, (set_y(upper_left, y), bottom_right), context);
} }
} }
_ => { _ => {
self.pager self.pager
.draw(grid, (set_y(upper_left, y + 1), bottom_right), context); .draw(grid, (set_y(upper_left, y), bottom_right), context);
} }
} }
if let ViewMode::ContactSelector(ref mut s) = self.mode { if let ViewMode::ContactSelector(ref mut s) = self.mode {
@ -647,6 +623,7 @@ impl Component for MailView {
} }
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
let shortcuts = self.get_shortcuts(context);
match self.mode { match self.mode {
ViewMode::Subview => { ViewMode::Subview => {
if let Some(s) = self.subview.as_mut() { if let Some(s) = self.subview.as_mut() {
@ -676,11 +653,39 @@ impl Component for MailView {
return true; return true;
} }
} }
_ => { _ => match event {
if self.pager.process_event(event, context) { UIEvent::Input(ref k)
if k == shortcuts[Pager::DESCRIPTION]["scroll_up"]
&& !context.settings.pager.headers_sticky
&& self.headers_cursor <= self.headers_no =>
{
self.force_draw_headers = true;
if self.pager.cursor_pos() == 0 {
self.headers_cursor = self.headers_cursor.saturating_sub(1);
} else {
if self.pager.process_event(event, context) {
return true;
}
}
self.pager.set_dirty();
return true; return true;
} }
} UIEvent::Input(ref k)
if k == shortcuts[Pager::DESCRIPTION]["scroll_down"]
&& !context.settings.pager.headers_sticky
&& self.headers_cursor < self.headers_no =>
{
self.force_draw_headers = true;
self.headers_cursor += 1;
self.pager.set_dirty();
return true;
}
_ => {
if self.pager.process_event(event, context) {
return true;
}
}
},
} }
let shortcuts = &self.get_shortcuts(context)[MailView::DESCRIPTION]; let shortcuts = &self.get_shortcuts(context)[MailView::DESCRIPTION];

View File

@ -303,7 +303,7 @@ impl fmt::Display for Pager {
} }
impl Pager { impl Pager {
const DESCRIPTION: &'static str = "pager"; pub const DESCRIPTION: &'static str = "pager";
pub fn set_reflow(&mut self, new_val: Reflow) { pub fn set_reflow(&mut self, new_val: Reflow) {
self.reflow = new_val; self.reflow = new_val;
} }
@ -470,9 +470,14 @@ impl Pager {
} }
} }
} }
pub fn cursor_pos(&self) -> usize { pub fn cursor_pos(&self) -> usize {
self.cursor.1 self.cursor.1
} }
pub fn size(&self) -> (usize, usize) {
(self.width, self.height)
}
} }
impl Component for Pager { impl Component for Pager {