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
parent
af365fa8d4
commit
db197aaffe
|
@ -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];
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue