Add box drawing in threadview with fluid layout
parent
a4ac421bca
commit
c5992b707d
|
@ -80,8 +80,18 @@ impl Backends {
|
|||
}
|
||||
|
||||
pub struct RefreshEvent {
|
||||
pub hash: u64,
|
||||
pub folder: String,
|
||||
hash: u64,
|
||||
folder: String,
|
||||
}
|
||||
|
||||
impl RefreshEvent {
|
||||
pub fn hash(&self) -> u64 {
|
||||
self.hash
|
||||
}
|
||||
|
||||
pub fn folder(&self) -> &str {
|
||||
self.folder.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
/// A `RefreshEventConsumer` is a boxed closure that must be used to consume a `RefreshEvent` and
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
[accounts.account-name]
|
||||
root_folder = "/path/to/root/folder"
|
||||
sent_folder = "/path/to/sent/folder" # optional
|
||||
format = "Maildir"
|
||||
threaded = true
|
||||
|
||||
[accounts.account-name.folders]
|
||||
"Inbox" = "inbox"
|
||||
"Sent" = "sent"
|
||||
"Drafts" = "drafts"
|
||||
"foobar-devel" = "devbar-fooel"
|
||||
|
||||
#[pager]
|
||||
#pager_ratio = 80
|
||||
#filter = "/usr/bin/pygmentize"
|
|
@ -69,10 +69,10 @@ impl Component for Composer {
|
|||
|
||||
if self.dirty {
|
||||
for i in get_y(upper_left)..=get_y(bottom_right) {
|
||||
grid[(mid, i)].set_ch(VERT_BOUNDARY);
|
||||
set_and_join_box(grid, (mid, i), VERT_BOUNDARY);
|
||||
grid[(mid, i)].set_fg(Color::Default);
|
||||
grid[(mid, i)].set_bg(Color::Default);
|
||||
grid[(mid + 80, i)].set_ch(VERT_BOUNDARY);
|
||||
set_and_join_box(grid, (mid + 80, i), VERT_BOUNDARY);
|
||||
grid[(mid + 80, i)].set_fg(Color::Default);
|
||||
grid[(mid + 80, i)].set_bg(Color::Default);
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ impl Component for Composer {
|
|||
|
||||
if self.dirty {
|
||||
for i in get_x(upper_left) + mid + 1..=get_x(upper_left) + mid + 79 {
|
||||
grid[(i, header_height)].set_ch(HORZ_BOUNDARY);
|
||||
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);
|
||||
}
|
||||
|
@ -151,6 +151,7 @@ impl Component for Composer {
|
|||
fn is_dirty(&self) -> bool {
|
||||
self.dirty || self.pager.is_dirty()
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self) {
|
||||
self.dirty = true;
|
||||
self.pager.set_dirty();
|
||||
|
|
|
@ -175,35 +175,46 @@ impl CompactListing {
|
|||
}
|
||||
|
||||
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let threads = &mailbox.threads;
|
||||
let container = threads.root_set()[idx];
|
||||
let container = &threads.containers()[container];
|
||||
let i = if let Some(i) = container.message() {
|
||||
i
|
||||
} else {
|
||||
threads.containers()[container.first_child().unwrap()]
|
||||
.message()
|
||||
.unwrap()
|
||||
};
|
||||
let root_envelope: &Envelope = &mailbox.collection[i];
|
||||
let fg_color = if !root_envelope.is_seen() {
|
||||
Color::Byte(0)
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
let bg_color = if self.cursor_pos.2 == idx {
|
||||
Color::Byte(246)
|
||||
} else if !root_envelope.is_seen() {
|
||||
Color::Byte(251)
|
||||
} else if idx % 2 == 0 {
|
||||
Color::Byte(236)
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
change_colors(grid, area, fg_color, bg_color);
|
||||
if idx == self.cursor_pos.2 {
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let threads = &mailbox.threads;
|
||||
let container = threads.root_set()[idx];
|
||||
let container = &threads.containers()[container];
|
||||
let i = if let Some(i) = container.message() {
|
||||
i
|
||||
} else {
|
||||
threads.containers()[container.first_child().unwrap()]
|
||||
.message()
|
||||
.unwrap()
|
||||
};
|
||||
let root_envelope: &Envelope = &mailbox.collection[i];
|
||||
let fg_color = if !root_envelope.is_seen() {
|
||||
Color::Byte(0)
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
let bg_color = if self.cursor_pos.2 == idx {
|
||||
Color::Byte(246)
|
||||
} else if !root_envelope.is_seen() {
|
||||
Color::Byte(251)
|
||||
} else if idx % 2 == 0 {
|
||||
Color::Byte(236)
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
change_colors(grid, area, fg_color, bg_color);
|
||||
return;
|
||||
}
|
||||
|
||||
let (width, height) = self.content.size();
|
||||
copy_area(
|
||||
grid,
|
||||
&self.content,
|
||||
area,
|
||||
((0, idx), (width - 1, height - 1)),
|
||||
);
|
||||
}
|
||||
|
||||
/// Draw the list of `Envelope`s.
|
||||
|
|
|
@ -22,12 +22,11 @@
|
|||
use super::*;
|
||||
use std::cmp;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct ThreadEntry {
|
||||
index: (usize, usize, usize),
|
||||
/// (indentation, container index, line number in listing)
|
||||
indentation: usize,
|
||||
content: CellBuffer,
|
||||
|
||||
msg_idx: usize,
|
||||
}
|
||||
|
||||
|
@ -40,6 +39,7 @@ pub struct ThreadView {
|
|||
dirty: bool,
|
||||
coordinates: (usize, usize, usize),
|
||||
mailview: MailView,
|
||||
show_mailview: bool,
|
||||
entries: Vec<ThreadEntry>,
|
||||
content: CellBuffer,
|
||||
initiated: bool,
|
||||
|
@ -47,6 +47,7 @@ pub struct ThreadView {
|
|||
|
||||
impl ThreadView {
|
||||
pub fn new(coordinates: (usize, usize, usize), context: &Context) -> Self {
|
||||
/* stack to push thread messages in order in order to pop and print them later */
|
||||
let mut stack: Vec<(usize, usize)> = Vec::with_capacity(32);
|
||||
let mailbox = &context.accounts[coordinates.0][coordinates.1]
|
||||
.as_ref()
|
||||
|
@ -64,6 +65,7 @@ impl ThreadView {
|
|||
initiated: false,
|
||||
coordinates,
|
||||
mailview: MailView::default(),
|
||||
show_mailview: true,
|
||||
entries: Vec::new(),
|
||||
cursor_pos: 1,
|
||||
new_cursor_pos: 0,
|
||||
|
@ -85,58 +87,88 @@ impl ThreadView {
|
|||
stack.push((ind + 1, i));
|
||||
}
|
||||
}
|
||||
view.new_expanded_pos = view.entries.len() - 1;
|
||||
view.new_expanded_pos = view.entries.len().saturating_sub(1);
|
||||
view.expanded_pos = view.new_expanded_pos + 1;
|
||||
|
||||
let height = 2 * view.entries.len();
|
||||
let height = 2 * view.entries.len() + 1;
|
||||
let mut width = 0;
|
||||
|
||||
let mut strings: Vec<String> = Vec::with_capacity(view.entries.len());
|
||||
|
||||
let mut highlight_reply_subjects: Vec<Option<usize>> =
|
||||
Vec::with_capacity(view.entries.len());
|
||||
for e in &view.entries {
|
||||
let envelope: &Envelope = &mailbox.collection[e.msg_idx];
|
||||
strings.push(format!(
|
||||
" {}{} - {}",
|
||||
" ".repeat(e.index.0 * 4),
|
||||
envelope.date_as_str(),
|
||||
envelope.field_from_to_string()
|
||||
));
|
||||
let container = &threads.containers()[e.index.1];
|
||||
let string = if container.show_subject() {
|
||||
let subject = envelope.subject();
|
||||
highlight_reply_subjects.push(Some(subject.len()));
|
||||
format!(
|
||||
" {}{} - {} {}",
|
||||
" ".repeat(e.index.0 * 4),
|
||||
envelope.date_as_str(),
|
||||
envelope.field_from_to_string(),
|
||||
envelope.subject(),
|
||||
)
|
||||
} else {
|
||||
highlight_reply_subjects.push(None);
|
||||
format!(
|
||||
" {}{} - {}",
|
||||
" ".repeat(e.index.0 * 4),
|
||||
envelope.date_as_str(),
|
||||
envelope.field_from_to_string(),
|
||||
)
|
||||
};
|
||||
strings.push(string);
|
||||
width = cmp::max(
|
||||
width,
|
||||
e.index.0 + strings.last().as_ref().unwrap().len() + 1,
|
||||
e.index.0 * 4 + strings.last().as_ref().unwrap().len() + 2,
|
||||
);
|
||||
}
|
||||
let mut content = CellBuffer::new(width, height, Cell::default());
|
||||
for (y, e) in view.entries.iter().enumerate() {
|
||||
/* Box character drawing stuff */
|
||||
if y > 0 && content.get_mut(e.index.0 * 4, 2 * y - 1).is_some() {
|
||||
let index = (e.index.0 * 4, 2 * y - 1);
|
||||
if content[index].ch() == ' ' {
|
||||
let mut ctr = 1;
|
||||
while content[(e.index.0 * 4 + ctr, 2 * y - 1)].ch() == ' ' {
|
||||
content[(e.index.0 * 4 + ctr, 2 * y - 1)].set_ch(HORZ_BOUNDARY);
|
||||
set_and_join_box(
|
||||
&mut content,
|
||||
(e.index.0 * 4 + ctr, 2 * y - 1),
|
||||
HORZ_BOUNDARY,
|
||||
);
|
||||
ctr += 1;
|
||||
}
|
||||
content[index].set_ch(_TOP_LEFT_CORNER);
|
||||
} else {
|
||||
content[index].set_ch(LIGHT_DOWN_AND_HORIZONTAL);
|
||||
};
|
||||
set_and_join_box(&mut content, index, HORZ_BOUNDARY);
|
||||
}
|
||||
}
|
||||
|
||||
write_string_to_grid(
|
||||
&strings[y],
|
||||
&mut content,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
((e.index.0 + 1, 2 * y), (width - 1, height - 1)),
|
||||
((e.index.0 * 4 + 1, 2 * y), (width - 1, height - 1)),
|
||||
true,
|
||||
);
|
||||
content[(e.index.0 * 4, 2 * y)].set_ch(VERT_BOUNDARY);
|
||||
for i in (e.index.0 * 4)..width {
|
||||
content[(i, 2 * y + 1)].set_ch(HORZ_BOUNDARY);
|
||||
if let Some(len) = highlight_reply_subjects[y] {
|
||||
let index = e.index.0 * 4 + 1 + strings[y].len() - len;
|
||||
let area = ((index, 2 * y), (width - 2, 2 * y));
|
||||
let fg_color = Color::Byte(33);
|
||||
let bg_color = Color::Default;
|
||||
change_colors(&mut content, area, fg_color, bg_color);
|
||||
}
|
||||
content[(e.index.0 * 4, 2 * y + 1)].set_ch(LIGHT_UP_AND_HORIZONTAL);
|
||||
set_and_join_box(&mut content, (e.index.0 * 4, 2 * y), VERT_BOUNDARY);
|
||||
set_and_join_box(&mut content, (e.index.0 * 4, 2 * y + 1), VERT_BOUNDARY);
|
||||
for i in ((e.index.0 * 4) + 1)..width - 1 {
|
||||
set_and_join_box(&mut content, (i, 2 * y + 1), HORZ_BOUNDARY);
|
||||
}
|
||||
set_and_join_box(&mut content, (width - 1, 2 * y + 1), VERT_BOUNDARY);
|
||||
}
|
||||
|
||||
for y in 0..height - 1 {
|
||||
set_and_join_box(&mut content, (width - 1, y), VERT_BOUNDARY);
|
||||
}
|
||||
//view.mailview = MailView::new((view.coordinates.0, view.coordinates.1, view.entries[view.expanded_pos].msg_idx), None, None);
|
||||
view.content = content;
|
||||
view.new_cursor_pos = view.new_expanded_pos;
|
||||
view
|
||||
|
@ -155,74 +187,32 @@ impl ThreadView {
|
|||
.message()
|
||||
.unwrap()
|
||||
};
|
||||
let envelope: &Envelope = &mailbox.collection[msg_idx];
|
||||
let op = context.accounts[self.coordinates.0]
|
||||
.backend
|
||||
.operation(envelope.hash());
|
||||
let body = envelope.body(op);
|
||||
|
||||
let mut body_text: String = " \n".repeat(6);
|
||||
body_text.push_str(&String::from_utf8_lossy(&decode_rec(&body, None)));
|
||||
let mut buf = CellBuffer::from(body_text.as_str()).split_newlines();
|
||||
|
||||
let date = format!("Date: {}\n", envelope.date_as_str());
|
||||
let from = format!("From: {}\n", envelope.field_from_to_string());
|
||||
let message_id = &format!("Message-ID: <{}>\n\n", envelope.message_id_raw());
|
||||
let mut width = [date.len(), from.len(), message_id.len(), buf.size().0]
|
||||
.iter()
|
||||
.cloned()
|
||||
.max()
|
||||
.unwrap_or(1);
|
||||
let height = buf.size().1;
|
||||
if width > buf.size().0 {
|
||||
buf.resize(width, height, Cell::default());
|
||||
width -= 1;
|
||||
} else {
|
||||
width = buf.size().0 - 1;
|
||||
}
|
||||
|
||||
write_string_to_grid(
|
||||
&date,
|
||||
&mut buf,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
((ind, 0), (width, height)),
|
||||
true,
|
||||
);
|
||||
write_string_to_grid(
|
||||
&from,
|
||||
&mut buf,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
((ind, 1), (width, height)),
|
||||
true,
|
||||
);
|
||||
write_string_to_grid(
|
||||
&message_id,
|
||||
&mut buf,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
((ind, 2), (width, height)),
|
||||
true,
|
||||
);
|
||||
|
||||
ThreadEntry {
|
||||
index: (ind, idx, order),
|
||||
indentation: ind,
|
||||
content: buf,
|
||||
|
||||
msg_idx,
|
||||
}
|
||||
}
|
||||
|
||||
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize) {
|
||||
let fg_color = Color::Default;
|
||||
let bg_color = if self.cursor_pos == idx {
|
||||
Color::Byte(246)
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
change_colors(grid, area, fg_color, bg_color);
|
||||
if idx == self.cursor_pos {
|
||||
let fg_color = Color::Default;
|
||||
let bg_color = Color::Byte(246);
|
||||
change_colors(grid, area, fg_color, bg_color);
|
||||
return;
|
||||
}
|
||||
|
||||
let (width, height) = self.content.size();
|
||||
copy_area(
|
||||
grid,
|
||||
&self.content,
|
||||
area,
|
||||
(
|
||||
(self.entries[idx].index.0 * 4 + 1, 2 * idx),
|
||||
(width - 1, height - 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Draw the list
|
||||
|
@ -232,7 +222,6 @@ impl ThreadView {
|
|||
let (width, height) = self.content.size();
|
||||
if height == 0 {
|
||||
clear_area(grid, area);
|
||||
copy_area(grid, &self.content, area, ((0, 0), (width - 1, 0)));
|
||||
context.dirty_areas.push_back(area);
|
||||
return;
|
||||
}
|
||||
|
@ -242,23 +231,38 @@ impl ThreadView {
|
|||
|
||||
let top_idx = page_no * rows;
|
||||
|
||||
/* This closure (written for code clarity, should be inlined by the compiler) returns the
|
||||
* **line** of an entry in the grid. */
|
||||
let entries = &self.entries;
|
||||
let get_entry_area = |idx: usize| {
|
||||
(
|
||||
(
|
||||
entries[idx].index.0 * 4 + 1 + get_x(upper_left),
|
||||
get_y(upper_left) + 2 * (idx % rows),
|
||||
),
|
||||
(
|
||||
cmp::min(get_x(upper_left) + width - 2, get_x(bottom_right)),
|
||||
get_y(upper_left) + 2 * (idx % rows),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
/* If cursor position has changed, remove the highlight from the previous position and
|
||||
* apply it in the new one. */
|
||||
if prev_page_no == page_no {
|
||||
let old_cursor_pos = self.cursor_pos;
|
||||
self.cursor_pos = self.new_cursor_pos;
|
||||
for &idx in &[old_cursor_pos, self.new_cursor_pos] {
|
||||
let new_area = (
|
||||
set_y(upper_left, get_y(upper_left) + 2 * (idx % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + 2 * (idx % rows)),
|
||||
);
|
||||
if idx >= self.entries.len() {
|
||||
continue;
|
||||
}
|
||||
let new_area = get_entry_area(idx);
|
||||
self.highlight_line(grid, new_area, idx);
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
return;
|
||||
} else if self.cursor_pos != self.new_cursor_pos {
|
||||
self.cursor_pos = self.new_cursor_pos;
|
||||
}
|
||||
self.cursor_pos = self.new_cursor_pos;
|
||||
|
||||
/* Page_no has changed, so draw new page */
|
||||
copy_area(
|
||||
|
@ -267,53 +271,28 @@ impl ThreadView {
|
|||
area,
|
||||
((0, 2 * top_idx), (width - 1, height - 1)),
|
||||
);
|
||||
self.highlight_line(
|
||||
grid,
|
||||
(
|
||||
set_y(upper_left, get_y(upper_left) + 2 * (self.cursor_pos % rows)),
|
||||
set_y(
|
||||
bottom_right,
|
||||
get_y(upper_left) + 2 * (self.cursor_pos % rows),
|
||||
),
|
||||
),
|
||||
self.cursor_pos,
|
||||
);
|
||||
self.highlight_line(grid, get_entry_area(self.cursor_pos), self.cursor_pos);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ThreadView {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// TODO display subject/info
|
||||
write!(f, "view thread")
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for ThreadView {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
fn draw_vert(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
let upper_left = upper_left!(area);
|
||||
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
let total_rows = get_y(bottom_right) - get_y(upper_left);
|
||||
let pager_ratio = context.runtime_settings.pager.pager_ratio;
|
||||
let bottom_entity_rows = (pager_ratio * total_rows) / 100;
|
||||
|
||||
if bottom_entity_rows > total_rows {
|
||||
clear_area(grid, area);
|
||||
context.dirty_areas.push_back(area);
|
||||
return;
|
||||
}
|
||||
|
||||
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
|
||||
let mid = get_x(upper_left) + self.content.size().0;
|
||||
|
||||
if !self.dirty {
|
||||
self.mailview
|
||||
.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
|
||||
let upper_left = (mid + 1, get_y(upper_left) + 1);
|
||||
if self.show_mailview {
|
||||
self.mailview
|
||||
.draw(grid, (upper_left, bottom_right), context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.dirty = false;
|
||||
|
||||
/* First draw the thread subject on the first row */
|
||||
let y = {
|
||||
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
|
||||
.as_ref()
|
||||
|
@ -351,27 +330,197 @@ impl Component for ThreadView {
|
|||
return;
|
||||
}
|
||||
|
||||
/* if this is the first ever draw, there is nothing on the grid to update so populate it
|
||||
* first */
|
||||
if !self.initiated {
|
||||
clear_area(grid, (set_y(upper_left, y - 1), bottom_right));
|
||||
let (width, height) = self.content.size();
|
||||
copy_area(
|
||||
grid,
|
||||
&self.content,
|
||||
(set_y(upper_left, y), set_y(bottom_right, mid - 1)),
|
||||
((0, 0), (width - 1, height - 1)),
|
||||
);
|
||||
for x in get_x(upper_left)..=get_x(bottom_right) {
|
||||
grid[(x, mid)].set_ch(HORZ_BOUNDARY);
|
||||
}
|
||||
if let Some(cell) = grid.get_mut(get_x(upper_left).saturating_sub(1), mid) {
|
||||
if cell.ch() == VERT_BOUNDARY {
|
||||
cell.set_ch(LIGHT_VERTICAL_AND_RIGHT);
|
||||
if self.show_mailview {
|
||||
let area = (set_y(upper_left, y), set_x(bottom_right, mid - 1));
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
let rows = (get_y(bottom_right) - get_y(upper_left) + 1) / 2;
|
||||
let page_no = (self.new_cursor_pos).wrapping_div(rows);
|
||||
let top_idx = page_no * rows;
|
||||
|
||||
copy_area(
|
||||
grid,
|
||||
&self.content,
|
||||
area,
|
||||
((0, 2 * top_idx), (width - 1, height - 1)),
|
||||
);
|
||||
for y in get_y(upper_left)..=get_y(bottom_right) {
|
||||
set_and_join_box(grid, (mid, y), VERT_BOUNDARY);
|
||||
}
|
||||
} else {
|
||||
let area = (set_y(upper_left, y), bottom_right);
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
let rows = (get_y(bottom_right) - get_y(upper_left) + 1) / 2;
|
||||
let page_no = (self.new_cursor_pos).wrapping_div(rows);
|
||||
let top_idx = page_no * rows;
|
||||
copy_area(
|
||||
grid,
|
||||
&self.content,
|
||||
area,
|
||||
((0, 2 * top_idx), (width - 1, height - 1)),
|
||||
);
|
||||
}
|
||||
context.dirty_areas.push_back(area);
|
||||
self.initiated = true;
|
||||
}
|
||||
|
||||
if self.show_mailview {
|
||||
self.draw_list(
|
||||
grid,
|
||||
(set_y(upper_left, y), set_x(bottom_right, mid - 1)),
|
||||
context,
|
||||
);
|
||||
let upper_left = (mid + 1, get_y(upper_left) + 1);
|
||||
self.mailview
|
||||
.draw(grid, (upper_left, bottom_right), context);
|
||||
} else {
|
||||
self.draw_list(grid, (set_y(upper_left, y), bottom_right), context);
|
||||
}
|
||||
}
|
||||
fn draw_horz(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
let total_rows = height!(area);
|
||||
|
||||
let pager_ratio = context.runtime_settings.pager.pager_ratio;
|
||||
let bottom_entity_rows = (pager_ratio * total_rows) / 100;
|
||||
|
||||
if bottom_entity_rows > total_rows {
|
||||
clear_area(grid, area);
|
||||
context.dirty_areas.push_back(area);
|
||||
return;
|
||||
}
|
||||
|
||||
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
|
||||
|
||||
if !self.dirty {
|
||||
if self.show_mailview {
|
||||
self.mailview
|
||||
.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.dirty = false;
|
||||
|
||||
/* First draw the thread subject on the first row */
|
||||
let y = {
|
||||
let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let threads = &mailbox.threads;
|
||||
let container = &threads.containers()[threads.root_set()[self.coordinates.2]];
|
||||
let i = if let Some(i) = container.message() {
|
||||
i
|
||||
} else {
|
||||
threads.containers()[container.first_child().unwrap()]
|
||||
.message()
|
||||
.unwrap()
|
||||
};
|
||||
let envelope: &Envelope = &mailbox.collection[i];
|
||||
|
||||
let (x, y) = write_string_to_grid(
|
||||
&envelope.subject(),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
area,
|
||||
true,
|
||||
);
|
||||
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);
|
||||
}
|
||||
//context.dirty_areas.push_back(((0,0), set_y(bottom_right, y)));
|
||||
y + 2
|
||||
};
|
||||
|
||||
let (width, height) = self.content.size();
|
||||
if height == 0 || height == self.cursor_pos || width == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
/* if this is the first ever draw, there is nothing on the grid to update so populate it
|
||||
* first */
|
||||
if !self.initiated {
|
||||
clear_area(grid, (set_y(upper_left, y - 1), bottom_right));
|
||||
let (width, height) = self.content.size();
|
||||
|
||||
if self.show_mailview {
|
||||
let area = (set_y(upper_left, y), set_y(bottom_right, mid - 1));
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
let rows = (get_y(bottom_right) - get_y(upper_left) + 1) / 2;
|
||||
let page_no = (self.new_cursor_pos).wrapping_div(rows);
|
||||
let top_idx = page_no * rows;
|
||||
|
||||
copy_area(
|
||||
grid,
|
||||
&self.content,
|
||||
area,
|
||||
((0, 2 * top_idx), (width - 1, height - 1)),
|
||||
);
|
||||
for x in get_x(upper_left)..=get_x(bottom_right) {
|
||||
set_and_join_box(grid, (x, mid), HORZ_BOUNDARY);
|
||||
}
|
||||
} else {
|
||||
let area = (set_y(upper_left, y), bottom_right);
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
let rows = (get_y(bottom_right) - get_y(upper_left) + 1) / 2;
|
||||
let page_no = (self.new_cursor_pos).wrapping_div(rows);
|
||||
let top_idx = page_no * rows;
|
||||
copy_area(
|
||||
grid,
|
||||
&self.content,
|
||||
area,
|
||||
((0, 2 * top_idx), (width - 1, height - 1)),
|
||||
);
|
||||
}
|
||||
context.dirty_areas.push_back(area);
|
||||
self.initiated = true;
|
||||
}
|
||||
if self.show_mailview {
|
||||
self.draw_list(
|
||||
grid,
|
||||
(set_y(upper_left, y), set_y(bottom_right, mid - 1)),
|
||||
context,
|
||||
);
|
||||
self.mailview
|
||||
.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
|
||||
} else {
|
||||
self.draw_list(grid, (set_y(upper_left, y), bottom_right), context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ThreadView {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// TODO display subject/info
|
||||
write!(f, "view thread")
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for ThreadView {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
let total_rows = height!(area);
|
||||
let total_cols = width!(area);
|
||||
if total_rows < 24 || total_cols < 80 {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If user has selected another mail to view, change to it */
|
||||
if self.new_expanded_pos != self.expanded_pos {
|
||||
self.expanded_pos = self.new_expanded_pos;
|
||||
self.mailview = MailView::new(
|
||||
|
@ -384,13 +533,12 @@ impl Component for ThreadView {
|
|||
None,
|
||||
);
|
||||
}
|
||||
self.draw_list(
|
||||
grid,
|
||||
(set_y(upper_left, y), set_y(bottom_right, mid - 1)),
|
||||
context,
|
||||
);
|
||||
self.mailview
|
||||
.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
|
||||
|
||||
if total_cols >= self.content.size().0 + 74 {
|
||||
self.draw_vert(grid, area, context);
|
||||
} else {
|
||||
self.draw_horz(grid, area, context);
|
||||
}
|
||||
}
|
||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
||||
if self.mailview.process_event(event, context) {
|
||||
|
@ -399,7 +547,7 @@ impl Component for ThreadView {
|
|||
match event.event_type {
|
||||
UIEventType::Input(Key::Up) => {
|
||||
if self.cursor_pos > 0 {
|
||||
self.new_cursor_pos -= 1;
|
||||
self.new_cursor_pos = self.new_cursor_pos.saturating_sub(1);
|
||||
self.dirty = true;
|
||||
}
|
||||
return true;
|
||||
|
@ -414,7 +562,15 @@ impl Component for ThreadView {
|
|||
}
|
||||
UIEventType::Input(Key::Char('\n')) => {
|
||||
self.new_expanded_pos = self.cursor_pos;
|
||||
self.dirty = true;
|
||||
self.show_mailview = true;
|
||||
self.initiated = false;
|
||||
self.set_dirty();
|
||||
return true;
|
||||
}
|
||||
UIEventType::Input(Key::Char('p')) => {
|
||||
self.show_mailview = !self.show_mailview;
|
||||
self.initiated = false;
|
||||
self.set_dirty();
|
||||
return true;
|
||||
}
|
||||
UIEventType::Resize => {
|
||||
|
|
|
@ -103,165 +103,6 @@ pub trait Component: Display + Debug {
|
|||
fn set_dirty(&mut self);
|
||||
}
|
||||
|
||||
// TODO: word break.
|
||||
pub fn copy_area_with_break(
|
||||
grid_dest: &mut CellBuffer,
|
||||
grid_src: &CellBuffer,
|
||||
dest: Area,
|
||||
src: Area,
|
||||
) -> Pos {
|
||||
if !is_valid_area!(dest) || !is_valid_area!(src) {
|
||||
eprintln!(
|
||||
"BUG: Invalid areas in copy_area:\n src: {:?}\n dest: {:?}",
|
||||
src, dest
|
||||
);
|
||||
return upper_left!(dest);
|
||||
}
|
||||
let mut ret = bottom_right!(dest);
|
||||
let mut src_x = get_x(upper_left!(src));
|
||||
let mut src_y = get_y(upper_left!(src));
|
||||
|
||||
'y_: for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) {
|
||||
'x_: for x in get_x(upper_left!(dest))..=get_x(bottom_right!(dest)) {
|
||||
if grid_src[(src_x, src_y)].ch() == '\n' {
|
||||
src_y += 1;
|
||||
src_x = 0;
|
||||
if src_y >= get_y(bottom_right!(src)) {
|
||||
ret.1 = y;
|
||||
break 'y_;
|
||||
}
|
||||
continue 'y_;
|
||||
}
|
||||
|
||||
grid_dest[(x, y)] = grid_src[(src_x, src_y)];
|
||||
src_x += 1;
|
||||
if src_x >= get_x(bottom_right!(src)) {
|
||||
src_y += 1;
|
||||
src_x = 0;
|
||||
if src_y >= get_y(bottom_right!(src)) {
|
||||
//clear_area(grid_dest, ((get_x(upper_left!(dest)), y), bottom_right!(dest)));
|
||||
ret.1 = y;
|
||||
break 'y_;
|
||||
}
|
||||
break 'x_;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Copy a source `Area` to a destination.
|
||||
pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area, src: Area) -> Pos {
|
||||
if !is_valid_area!(dest) || !is_valid_area!(src) {
|
||||
eprintln!(
|
||||
"BUG: Invalid areas in copy_area:\n src: {:?}\n dest: {:?}",
|
||||
src, dest
|
||||
);
|
||||
return upper_left!(dest);
|
||||
}
|
||||
|
||||
let mut ret = bottom_right!(dest);
|
||||
let mut src_x = get_x(upper_left!(src));
|
||||
let mut src_y = get_y(upper_left!(src));
|
||||
let (cols, rows) = grid_src.size();
|
||||
if src_x >= cols || src_y >= rows {
|
||||
eprintln!("DEBUG: src area outside of grid_src in copy_area",);
|
||||
return upper_left!(dest);
|
||||
}
|
||||
|
||||
for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) {
|
||||
'for_x: for x in get_x(upper_left!(dest))..=get_x(bottom_right!(dest)) {
|
||||
grid_dest[(x, y)] = grid_src[(src_x, src_y)];
|
||||
if src_x >= get_x(bottom_right!(src)) {
|
||||
break 'for_x;
|
||||
}
|
||||
src_x += 1;
|
||||
}
|
||||
src_x = get_x(upper_left!(src));
|
||||
if src_y >= get_y(bottom_right!(src)) {
|
||||
clear_area(
|
||||
grid_dest,
|
||||
((get_x(upper_left!(dest)), y), bottom_right!(dest)),
|
||||
);
|
||||
ret.1 = y;
|
||||
break;
|
||||
}
|
||||
src_y += 1;
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Change foreground and background colors in an `Area`
|
||||
pub fn change_colors(grid: &mut CellBuffer, area: Area, fg_color: Color, bg_color: Color) {
|
||||
if !is_valid_area!(area) {
|
||||
eprintln!("BUG: Invalid area in change_colors:\n area: {:?}", area);
|
||||
return;
|
||||
}
|
||||
for y in get_y(upper_left!(area))..=get_y(bottom_right!(area)) {
|
||||
for x in get_x(upper_left!(area))..=get_x(bottom_right!(area)) {
|
||||
grid[(x, y)].set_fg(fg_color);
|
||||
grid[(x, y)].set_bg(bg_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write an `&str` to a `CellBuffer` in a specified `Area` with the passed colors.
|
||||
fn write_string_to_grid(
|
||||
s: &str,
|
||||
grid: &mut CellBuffer,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
area: Area,
|
||||
line_break: bool,
|
||||
) -> Pos {
|
||||
let bounds = grid.size();
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
let (mut x, mut y) = upper_left;
|
||||
if y > (get_y(bottom_right))
|
||||
|| x > get_x(bottom_right)
|
||||
|| y > get_y(bounds)
|
||||
|| x > get_x(bounds)
|
||||
{
|
||||
eprintln!(" Invalid area with string {} and area {:?}", s, area);
|
||||
return (x, y);
|
||||
}
|
||||
'char: for c in s.chars() {
|
||||
grid[(x, y)].set_ch(c);
|
||||
grid[(x, y)].set_fg(fg_color);
|
||||
grid[(x, y)].set_bg(bg_color);
|
||||
x += 1;
|
||||
|
||||
if x == (get_x(bottom_right)) + 1 || x > get_x(bounds) {
|
||||
x = get_x(upper_left);
|
||||
y += 1;
|
||||
if y > (get_y(bottom_right)) || y > get_y(bounds) {
|
||||
return (x, y - 1);
|
||||
}
|
||||
if !line_break {
|
||||
break 'char;
|
||||
}
|
||||
}
|
||||
}
|
||||
(x, y)
|
||||
}
|
||||
|
||||
/// Completely clear an `Area` with an empty char and the terminal's default colors.
|
||||
fn clear_area(grid: &mut CellBuffer, area: Area) {
|
||||
if !is_valid_area!(area) {
|
||||
return;
|
||||
}
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
for y in get_y(upper_left)..=get_y(bottom_right) {
|
||||
for x in get_x(upper_left)..=get_x(bottom_right) {
|
||||
grid[(x, y)].set_ch(' ');
|
||||
grid[(x, y)].set_bg(Color::Default);
|
||||
grid[(x, y)].set_fg(Color::Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn new_draft(_context: &mut Context) -> Vec<u8> {
|
||||
// TODO: Generate proper message-id https://www.jwz.org/doc/mid.html
|
||||
let mut v = String::with_capacity(500);
|
||||
|
@ -271,3 +112,255 @@ fn new_draft(_context: &mut Context) -> Vec<u8> {
|
|||
v.push_str("Message-Id: \n\n");
|
||||
v.into_bytes()
|
||||
}
|
||||
|
||||
fn bin_to_ch(b: u32) -> char {
|
||||
match b {
|
||||
0b0001 => '╶',
|
||||
0b0010 => '╵',
|
||||
0b0011 => '└',
|
||||
0b0100 => '╴',
|
||||
0b0101 => '─',
|
||||
0b0110 => '┘',
|
||||
0b0111 => '┴',
|
||||
0b1000 => '╷',
|
||||
0b1001 => '┌',
|
||||
0b1010 => '│',
|
||||
0b1011 => '├',
|
||||
0b1100 => '┐',
|
||||
0b1101 => '┬',
|
||||
0b1110 => '┤',
|
||||
0b1111 => '┼',
|
||||
x => unreachable!(format!("unreachable bin_to_ch(x), x = {:b}", x)),
|
||||
}
|
||||
}
|
||||
|
||||
fn ch_to_bin(ch: char) -> Option<u32> {
|
||||
match ch {
|
||||
'└' => Some(0b0011),
|
||||
'─' => Some(0b0101),
|
||||
'┘' => Some(0b0110),
|
||||
'┴' => Some(0b0111),
|
||||
'┌' => Some(0b1001),
|
||||
|
||||
'│' => Some(0b1010),
|
||||
|
||||
'├' => Some(0b1011),
|
||||
'┐' => Some(0b1100),
|
||||
'┬' => Some(0b1101),
|
||||
|
||||
'┤' => Some(0b1110),
|
||||
|
||||
'┼' => Some(0b1111),
|
||||
'╷' => Some(0b1000),
|
||||
|
||||
'╵' => Some(0b0010),
|
||||
'╴' => Some(0b0100),
|
||||
'╶' => Some(0b0001),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(never_loop)]
|
||||
fn set_and_join_vert(grid: &mut CellBuffer, idx: Pos) -> u32 {
|
||||
let (x, y) = idx;
|
||||
let mut bin_set = 0b1010;
|
||||
/* Check left side
|
||||
*
|
||||
* 1
|
||||
* -> 2 │ 0
|
||||
* 3
|
||||
*/
|
||||
loop {
|
||||
if x > 0 {
|
||||
if let Some(cell) = grid.get_mut(x - 1, y) {
|
||||
if let Some(adj) = ch_to_bin(cell.ch()) {
|
||||
if (adj & 0b0001) > 0 {
|
||||
bin_set |= 0b0100;
|
||||
break;
|
||||
} else if adj == 0b0100 {
|
||||
cell.set_ch(bin_to_ch(0b0101));
|
||||
bin_set |= 0b0100;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bin_set &= 0b1011;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check right side
|
||||
*
|
||||
* 1
|
||||
* 2 │ 0 <-
|
||||
* 3
|
||||
*/
|
||||
loop {
|
||||
if let Some(cell) = grid.get_mut(x + 1, y) {
|
||||
if let Some(adj) = ch_to_bin(cell.ch()) {
|
||||
if (adj & 0b0100) > 0 {
|
||||
bin_set |= 0b0001;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bin_set &= 0b1110;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set upper side
|
||||
*
|
||||
* 1 <-
|
||||
* 2 │ 0
|
||||
* 3
|
||||
*/
|
||||
loop {
|
||||
if y > 0 {
|
||||
if let Some(cell) = grid.get_mut(x, y - 1) {
|
||||
if let Some(adj) = ch_to_bin(cell.ch()) {
|
||||
cell.set_ch(bin_to_ch(adj | 0b1000));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bin_set &= 0b1101;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set bottom side
|
||||
*
|
||||
* 1
|
||||
* 2 │ 0
|
||||
* 3 <-
|
||||
*/
|
||||
loop {
|
||||
if let Some(cell) = grid.get_mut(x, y + 1) {
|
||||
if let Some(adj) = ch_to_bin(cell.ch()) {
|
||||
cell.set_ch(bin_to_ch(adj | 0b0010));
|
||||
break;
|
||||
}
|
||||
}
|
||||
bin_set &= 0b0111;
|
||||
break;
|
||||
}
|
||||
|
||||
if bin_set == 0 {
|
||||
bin_set = 0b1010;
|
||||
}
|
||||
|
||||
bin_set
|
||||
}
|
||||
|
||||
#[allow(never_loop)]
|
||||
fn set_and_join_horz(grid: &mut CellBuffer, idx: Pos) -> u32 {
|
||||
let (x, y) = idx;
|
||||
let mut bin_set = 0b0101;
|
||||
/* Check upper side
|
||||
*
|
||||
* 1 <-
|
||||
* 2 ─ 0
|
||||
* 3
|
||||
*/
|
||||
loop {
|
||||
if y > 0 {
|
||||
if let Some(cell) = grid.get_mut(x, y - 1) {
|
||||
if let Some(adj) = ch_to_bin(cell.ch()) {
|
||||
if (adj & 0b1000) > 0 {
|
||||
bin_set |= 0b0010;
|
||||
break;
|
||||
} else if adj == 0b0010 {
|
||||
bin_set |= 0b0010;
|
||||
cell.set_ch(bin_to_ch(0b1010));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bin_set &= 0b1101;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check bottom side
|
||||
*
|
||||
* 1
|
||||
* 2 ─ 0
|
||||
* 3 <-
|
||||
*/
|
||||
loop {
|
||||
if let Some(cell) = grid.get_mut(x, y + 1) {
|
||||
if let Some(adj) = ch_to_bin(cell.ch()) {
|
||||
if (adj & 0b0010) > 0 {
|
||||
bin_set |= 0b1000;
|
||||
break;
|
||||
} else if adj == 0b1000 {
|
||||
cell.set_ch(bin_to_ch(0b1010));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bin_set &= 0b0111;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set left side
|
||||
*
|
||||
* 1
|
||||
* -> 2 ─ 0
|
||||
* 3
|
||||
*/
|
||||
loop {
|
||||
if x > 0 {
|
||||
if let Some(cell) = grid.get_mut(x - 1, y) {
|
||||
if let Some(adj) = ch_to_bin(cell.ch()) {
|
||||
cell.set_ch(bin_to_ch(adj | 0b0001));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bin_set &= 0b1011;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set right side
|
||||
*
|
||||
* 1
|
||||
* 2 ─ 0 <-
|
||||
* 3
|
||||
*/
|
||||
loop {
|
||||
if let Some(cell) = grid.get_mut(x + 1, y) {
|
||||
if let Some(adj) = ch_to_bin(cell.ch()) {
|
||||
cell.set_ch(bin_to_ch(adj | 0b0100));
|
||||
break;
|
||||
}
|
||||
}
|
||||
bin_set &= 0b1110;
|
||||
break;
|
||||
}
|
||||
|
||||
if bin_set == 0 {
|
||||
bin_set = 0b0101;
|
||||
}
|
||||
|
||||
bin_set
|
||||
}
|
||||
|
||||
fn set_and_join_box(grid: &mut CellBuffer, idx: Pos, ch: char) {
|
||||
/* Connected sides:
|
||||
*
|
||||
* 1
|
||||
* 2 c 0
|
||||
* 3
|
||||
*
|
||||
* #3210
|
||||
* 0b____
|
||||
*/
|
||||
|
||||
let bin_set = match ch {
|
||||
'│' => set_and_join_vert(grid, idx),
|
||||
'─' => set_and_join_horz(grid, idx),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
grid[idx].set_ch(bin_to_ch(bin_set));
|
||||
}
|
||||
|
|
|
@ -197,7 +197,7 @@ pub struct Pager {
|
|||
cursor_pos: usize,
|
||||
max_cursor_pos: Option<usize>,
|
||||
height: usize,
|
||||
|
||||
|
||||
width: usize,
|
||||
dirty: bool,
|
||||
content: CellBuffer,
|
||||
|
@ -332,6 +332,9 @@ impl Component for Pager {
|
|||
self.cursor_pos = self.cursor_pos.saturating_sub(height);
|
||||
}
|
||||
PagerMovement::PageDown => {
|
||||
/* This might "overflow" beyond the max_cursor_pos boundary if it's not yet
|
||||
* set. TODO: Rework the page up/down stuff
|
||||
*/
|
||||
if self.cursor_pos + 2 * height + 1 < self.height {
|
||||
self.cursor_pos += height;
|
||||
} else {
|
||||
|
@ -349,12 +352,12 @@ impl Component for Pager {
|
|||
Some(max) if max <= self.cursor_pos => {
|
||||
self.cursor_pos -= 1;
|
||||
return;
|
||||
},
|
||||
}
|
||||
Some(max) if max >= height => {
|
||||
self.cursor_pos = 0;
|
||||
return;
|
||||
}
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
clear_area(grid, area);
|
||||
|
|
|
@ -551,3 +551,162 @@ pub enum Attr {
|
|||
UnderlineReverse = 0b110,
|
||||
BoldReverseUnderline = 0b111,
|
||||
}
|
||||
|
||||
// TODO: word break.
|
||||
pub fn copy_area_with_break(
|
||||
grid_dest: &mut CellBuffer,
|
||||
grid_src: &CellBuffer,
|
||||
dest: Area,
|
||||
src: Area,
|
||||
) -> Pos {
|
||||
if !is_valid_area!(dest) || !is_valid_area!(src) {
|
||||
eprintln!(
|
||||
"BUG: Invalid areas in copy_area:\n src: {:?}\n dest: {:?}",
|
||||
src, dest
|
||||
);
|
||||
return upper_left!(dest);
|
||||
}
|
||||
let mut ret = bottom_right!(dest);
|
||||
let mut src_x = get_x(upper_left!(src));
|
||||
let mut src_y = get_y(upper_left!(src));
|
||||
|
||||
'y_: for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) {
|
||||
'x_: for x in get_x(upper_left!(dest))..=get_x(bottom_right!(dest)) {
|
||||
if grid_src[(src_x, src_y)].ch() == '\n' {
|
||||
src_y += 1;
|
||||
src_x = 0;
|
||||
if src_y >= get_y(bottom_right!(src)) {
|
||||
ret.1 = y;
|
||||
break 'y_;
|
||||
}
|
||||
continue 'y_;
|
||||
}
|
||||
|
||||
grid_dest[(x, y)] = grid_src[(src_x, src_y)];
|
||||
src_x += 1;
|
||||
if src_x >= get_x(bottom_right!(src)) {
|
||||
src_y += 1;
|
||||
src_x = 0;
|
||||
if src_y >= get_y(bottom_right!(src)) {
|
||||
//clear_area(grid_dest, ((get_x(upper_left!(dest)), y), bottom_right!(dest)));
|
||||
ret.1 = y;
|
||||
break 'y_;
|
||||
}
|
||||
break 'x_;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Copy a source `Area` to a destination.
|
||||
pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area, src: Area) -> Pos {
|
||||
if !is_valid_area!(dest) || !is_valid_area!(src) {
|
||||
eprintln!(
|
||||
"BUG: Invalid areas in copy_area:\n src: {:?}\n dest: {:?}",
|
||||
src, dest
|
||||
);
|
||||
return upper_left!(dest);
|
||||
}
|
||||
|
||||
let mut ret = bottom_right!(dest);
|
||||
let mut src_x = get_x(upper_left!(src));
|
||||
let mut src_y = get_y(upper_left!(src));
|
||||
let (cols, rows) = grid_src.size();
|
||||
if src_x >= cols || src_y >= rows {
|
||||
eprintln!("DEBUG: src area outside of grid_src in copy_area",);
|
||||
return upper_left!(dest);
|
||||
}
|
||||
|
||||
for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) {
|
||||
'for_x: for x in get_x(upper_left!(dest))..=get_x(bottom_right!(dest)) {
|
||||
grid_dest[(x, y)] = grid_src[(src_x, src_y)];
|
||||
if src_x >= get_x(bottom_right!(src)) {
|
||||
break 'for_x;
|
||||
}
|
||||
src_x += 1;
|
||||
}
|
||||
src_x = get_x(upper_left!(src));
|
||||
if src_y >= get_y(bottom_right!(src)) {
|
||||
clear_area(
|
||||
grid_dest,
|
||||
((get_x(upper_left!(dest)), y), bottom_right!(dest)),
|
||||
);
|
||||
ret.1 = y;
|
||||
break;
|
||||
}
|
||||
src_y += 1;
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Change foreground and background colors in an `Area`
|
||||
pub fn change_colors(grid: &mut CellBuffer, area: Area, fg_color: Color, bg_color: Color) {
|
||||
if !is_valid_area!(area) {
|
||||
eprintln!("BUG: Invalid area in change_colors:\n area: {:?}", area);
|
||||
return;
|
||||
}
|
||||
for y in get_y(upper_left!(area))..=get_y(bottom_right!(area)) {
|
||||
for x in get_x(upper_left!(area))..=get_x(bottom_right!(area)) {
|
||||
grid[(x, y)].set_fg(fg_color);
|
||||
grid[(x, y)].set_bg(bg_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write an `&str` to a `CellBuffer` in a specified `Area` with the passed colors.
|
||||
pub fn write_string_to_grid(
|
||||
s: &str,
|
||||
grid: &mut CellBuffer,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
area: Area,
|
||||
line_break: bool,
|
||||
) -> Pos {
|
||||
let bounds = grid.size();
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
let (mut x, mut y) = upper_left;
|
||||
if y > (get_y(bottom_right))
|
||||
|| x > get_x(bottom_right)
|
||||
|| y > get_y(bounds)
|
||||
|| x > get_x(bounds)
|
||||
{
|
||||
eprintln!(" Invalid area with string {} and area {:?}", s, area);
|
||||
return (x, y);
|
||||
}
|
||||
'char: for c in s.chars() {
|
||||
grid[(x, y)].set_ch(c);
|
||||
grid[(x, y)].set_fg(fg_color);
|
||||
grid[(x, y)].set_bg(bg_color);
|
||||
x += 1;
|
||||
|
||||
if x == (get_x(bottom_right)) + 1 || x > get_x(bounds) {
|
||||
x = get_x(upper_left);
|
||||
y += 1;
|
||||
if y > (get_y(bottom_right)) || y > get_y(bounds) {
|
||||
return (x, y - 1);
|
||||
}
|
||||
if !line_break {
|
||||
break 'char;
|
||||
}
|
||||
}
|
||||
}
|
||||
(x, y)
|
||||
}
|
||||
|
||||
/// Completely clear an `Area` with an empty char and the terminal's default colors.
|
||||
pub fn clear_area(grid: &mut CellBuffer, area: Area) {
|
||||
if !is_valid_area!(area) {
|
||||
return;
|
||||
}
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
for y in get_y(upper_left)..=get_y(bottom_right) {
|
||||
for x in get_x(upper_left)..=get_x(bottom_right) {
|
||||
grid[(x, y)].set_ch(' ');
|
||||
grid[(x, y)].set_bg(Color::Default);
|
||||
grid[(x, y)].set_fg(Color::Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ pub enum ThreadEvent {
|
|||
|
||||
impl From<RefreshEvent> for ThreadEvent {
|
||||
fn from(event: RefreshEvent) -> Self {
|
||||
ThreadEvent::RefreshMailbox { hash: event.hash }
|
||||
ThreadEvent::RefreshMailbox { hash: event.hash() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue