listing.rs sync functionalities wip

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
Manos Pitsidianakis 2 weeks ago
parent f71ebb2a4c
commit 5b87998617
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -388,6 +388,7 @@ pub struct EntryStrings {
pub flag: FlagString,
pub from: FromString,
pub tags: TagString,
pub unseen: bool,
pub highlight_self: bool,
}

@ -138,7 +138,7 @@ pub struct CompactListing {
sortcmd: bool,
subsort: (SortField, SortOrder),
/// Cache current view.
data_columns: DataColumns<4>,
data_columns: DataColumns<5>,
rows_drawn: SegmentTree,
rows: RowsState<(ThreadHash, EnvelopeHash)>,
@ -234,13 +234,13 @@ impl MailListingTrait for CompactListing {
let message: String =
context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status();
if self.data_columns.columns[0].resize_with_context(message.len(), 1, context) {
let area = self.data_columns.columns[0].area();
let area_col_0 = self.data_columns.columns[0].area();
self.data_columns.columns[0].grid_mut().write_string(
message.as_str(),
self.color_cache.theme_default.fg,
self.color_cache.theme_default.bg,
self.color_cache.theme_default.attrs,
area,
area_col_0,
None,
);
}
@ -299,18 +299,20 @@ impl MailListingTrait for CompactListing {
self.sort = context.accounts[&self.cursor_pos.0].settings.account.order
}
self.length = 0;
let mut min_width = (0, 0, 0, 0);
let mut min_width = (0, 0, 0, 0, 0);
#[allow(clippy::type_complexity)]
let mut row_widths: (
SmallVec<[u8; 1024]>,
SmallVec<[u8; 1024]>,
SmallVec<[u8; 1024]>,
SmallVec<[u8; 1024]>,
SmallVec<[u8; 1024]>,
) = (
SmallVec::new(),
SmallVec::new(),
SmallVec::new(),
SmallVec::new(),
SmallVec::new(),
);
let tags_lck = account.collection.tag_index.read().unwrap();
@ -456,22 +458,25 @@ impl MailListingTrait for CompactListing {
.try_into()
.unwrap_or(255),
);
/* subject */
row_widths.3.push(
(entry_strings.flag.grapheme_width()
+ entry_strings.subject.grapheme_width()
+ 1
+ entry_strings.tags.grapheme_width())
.try_into()
.unwrap_or(255),
entry_strings
.flag
.grapheme_width()
.try_into()
.unwrap_or(255),
);
row_widths.4.push(
(entry_strings.subject.grapheme_width() + 1 + entry_strings.tags.grapheme_width())
.try_into()
.unwrap_or(255),
);
min_width.1 = min_width.1.max(entry_strings.date.grapheme_width()); /* date */
min_width.2 = min_width.2.max(entry_strings.from.grapheme_width()); /* from */
min_width.3 = min_width.3.max(
entry_strings.flag.grapheme_width()
+ entry_strings.subject.grapheme_width()
+ 1
+ entry_strings.tags.grapheme_width(),
entry_strings.flag.grapheme_width() + usize::from(entry_strings.highlight_self),
);
min_width.4 = min_width.4.max(
entry_strings.subject.grapheme_width() + 1 + entry_strings.tags.grapheme_width(),
); /* subject */
self.rows.insert_thread(
thread,
@ -487,12 +492,13 @@ impl MailListingTrait for CompactListing {
self.length += 1;
}
min_width.0 = self.length.saturating_sub(1).to_string().len();
min_width.0 = digits_of_num!(self.length.saturating_sub(1));
self.data_columns.elasticities[0].set_rigid();
self.data_columns.elasticities[1].set_rigid();
self.data_columns.elasticities[2].set_grow(15, Some(35));
self.data_columns.elasticities[3].set_rigid();
self.data_columns.elasticities[4].set_rigid();
self.data_columns
.cursor_config
.set_handle(true)
@ -510,12 +516,15 @@ impl MailListingTrait for CompactListing {
_ = self.data_columns.columns[1].resize_with_context(min_width.1, self.rows.len(), context);
/* from column */
_ = self.data_columns.columns[2].resize_with_context(min_width.2, self.rows.len(), context);
/* subject column */
// flags column
_ = self.data_columns.columns[3].resize_with_context(min_width.3, self.rows.len(), context);
// subject column
_ = self.data_columns.columns[4].resize_with_context(min_width.4, self.rows.len(), context);
self.data_columns.segment_tree[0] = row_widths.0.into();
self.data_columns.segment_tree[1] = row_widths.1.into();
self.data_columns.segment_tree[2] = row_widths.2.into();
self.data_columns.segment_tree[3] = row_widths.3.into();
self.data_columns.segment_tree[4] = row_widths.4.into();
self.rows_drawn = SegmentTree::from(
std::iter::repeat(1)
@ -527,13 +536,13 @@ impl MailListingTrait for CompactListing {
if self.length == 0 && self.filter_term.is_empty() {
let message: String = account[&self.cursor_pos.1].status();
if self.data_columns.columns[0].resize_with_context(message.len(), 1, context) {
let area = self.data_columns.columns[0].area();
let area_col_0 = self.data_columns.columns[0].area();
self.data_columns.columns[0].grid_mut().write_string(
&message,
self.color_cache.theme_default.fg,
self.color_cache.theme_default.bg,
self.color_cache.theme_default.attrs,
area,
area_col_0,
None,
);
}
@ -681,6 +690,10 @@ impl ListingTrait for CompactListing {
}
context.dirty_areas.push_back(new_area);
}
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
self.draw_relative_numbers(grid, area, top_idx, context);
context.dirty_areas.push_back(area);
}
if !self.force_draw {
return;
}
@ -700,6 +713,9 @@ impl ListingTrait for CompactListing {
/* copy table columns */
self.data_columns
.draw(grid, top_idx, self.cursor_pos.2, grid.bounds_iter(area));
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
self.draw_relative_numbers(grid, area, top_idx, context);
}
/* apply each row colors separately */
for i in top_idx..(top_idx + area.height()) {
if let Some(row_attr) = self.rows.row_attr_cache.get(&i) {
@ -980,6 +996,7 @@ impl CompactListing {
),
from: FromString(Address::display_name_slice(from)),
tags: TagString(tags_string, colors),
unseen: thread.unseen() > 0,
highlight_self,
}
}
@ -1070,7 +1087,7 @@ impl CompactListing {
}
}
let strings = self.make_entry_string(
let mut entry_strings = self.make_entry_string(
&envelope,
context,
&tags_lck,
@ -1081,161 +1098,22 @@ impl CompactListing {
highlight_self,
thread_hash,
);
entry_strings.highlight_self = should_highlight_self && {
let my_address: Address = context.accounts[&self.cursor_pos.0]
.settings
.account
.make_display_name();
envelope.recipient_any(&my_address)
};
drop(envelope);
let columns = &mut self.data_columns.columns;
let min_width = (
columns[0].area().width(),
columns[1].area().width(),
columns[2].area().width(),
columns[3].area().width(),
);
let (x, _) = {
let area = columns[0].area().nth_row(idx);
columns[0].grid_mut().write_string(
&idx.to_string(),
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
None,
)
};
for c in {
let area = columns[0].area();
columns[0].grid_mut().row_iter(area, x..min_width.0, idx)
} {
columns[0].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs)
.set_ch(' ');
}
let (x, _) = {
let area = columns[1].area().nth_row(idx);
columns[1].grid_mut().write_string(
&strings.date,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
None,
)
};
for c in {
let area = columns[1].area();
columns[1].grid_mut().row_iter(area, x..min_width.1, idx)
} {
columns[1].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs)
.set_ch(' ');
}
let (x, _) = {
let area = columns[2].area().nth_row(idx);
columns[2].grid_mut().write_string(
&strings.from,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
None,
)
};
for c in {
let area = columns[2].area();
columns[2].grid_mut().row_iter(area, x..min_width.2, idx)
} {
columns[2].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs)
.set_ch(' ');
}
let (x, _) = {
let area = columns[3].area().nth_row(idx);
columns[3].grid_mut().write_string(
&strings.flag,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
None,
)
};
let (x, _) = {
let area = columns[3].area().nth_row(idx).skip_cols(x);
columns[3].grid_mut().write_string(
&strings.subject,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
None,
)
};
if let Some(c) = columns[3].grid_mut().get_mut(x, idx) {
c.set_bg(row_attr.bg).set_attrs(row_attr.attrs).set_ch(' ');
}
let x = {
let mut x = x + 1;
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
let color = color.unwrap_or(self.color_cache.tag_default.bg);
let _x = {
let area = columns[3].area().nth_row(idx).skip_cols(x + 1);
columns[3]
.grid_mut()
.write_string(
t,
self.color_cache.tag_default.fg,
color,
self.color_cache.tag_default.attrs,
area,
None,
)
.0
+ x
+ 1
};
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
} {
columns[3].grid_mut()[c].set_bg(color);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, _x..(_x + 1), idx)
} {
columns[3].grid_mut()[c].set_bg(color).set_keep_bg(true);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, (x + 1)..(_x + 1), idx)
} {
columns[3].grid_mut()[c]
.set_keep_fg(true)
.set_keep_bg(true)
.set_keep_attrs(true);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
} {
columns[3].grid_mut()[c].set_keep_bg(true);
}
x = _x + 2;
}
x
};
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, x..min_width.3, idx)
} {
columns[3].grid_mut()[c]
.set_ch(' ')
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
for n in 0..=4 {
let area = columns[n].area().nth_row(idx);
columns[n].grid_mut().clear_area(area, row_attr);
}
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), strings);
self.rows_drawn.update(idx, 1);
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), entry_strings);
}
fn draw_rows(&mut self, context: &Context, start: usize, end: usize) {
@ -1254,6 +1132,7 @@ impl CompactListing {
self.data_columns.columns[1].area().width(),
self.data_columns.columns[2].area().width(),
self.data_columns.columns[3].area().width(),
self.data_columns.columns[3].area().width(),
);
let columns = &mut self.data_columns.columns;
@ -1344,127 +1223,105 @@ impl CompactListing {
}
}
}
let (x, _) = {
let area = columns[3].area().nth_row(idx);
columns[3].grid_mut().write_string(
{
let mut area_col_3 = columns[3].area().nth_row(idx);
area_col_3 = area_col_3.skip_cols(columns[3].grid_mut().write_string(
&strings.flag,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
area_col_3,
None,
)
};
let x = {
let mut area = columns[3].area().nth_row(idx).skip_cols(x);
));
if strings.highlight_self {
let x = columns[3]
.grid_mut()
.write_string(
mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self_flag
)
.as_ref()
.map(|s| s.as_str())
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
self.color_cache.highlight_self.fg,
row_attr.bg,
row_attr.attrs | Attr::FORCE_TEXT,
area,
None,
let (x, _) = columns[3].grid_mut().write_string(
mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self_flag
)
.0;
for row in columns[3].grid().bounds_iter(area.nth_row(0).take_cols(x)) {
for c in row {
columns[3].grid_mut()[c].set_keep_fg(true);
}
}
area = area.skip_cols(x + 1);
}
columns[3]
.grid_mut()
.write_string(
&strings.subject,
row_attr.fg,
.as_ref()
.map(|s| s.as_str())
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
self.color_cache.highlight_self.fg,
row_attr.bg,
row_attr.attrs,
area,
row_attr.attrs | Attr::FORCE_TEXT,
area_col_3,
None,
)
.0
+ x
};
#[cfg(feature = "regexp")]
{
for text_formatter in crate::conf::text_format_regexps(context, "listing.subject") {
let t = columns[3].grid_mut().insert_tag(text_formatter.tag);
for (start, end) in text_formatter.regexp.find_iter(strings.subject.as_str()) {
columns[3].grid_mut().set_tag(t, (start, idx), (end, idx));
);
for c in columns[3].grid().row_iter(area_col_3, 0..x, 0) {
columns[3].grid_mut()[c].set_keep_fg(true);
}
area_col_3 = area_col_3.skip_cols(x + 1);
}
}
let mut x = x + 1;
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
let color = color.unwrap_or(self.color_cache.tag_default.bg);
let _x = {
let area = columns[3].area().nth_row(idx).skip_cols(x + 1);
columns[3]
.grid_mut()
.write_string(
t,
self.color_cache.tag_default.fg,
color,
self.color_cache.tag_default.attrs,
area,
None,
)
.0
+ x
+ 1
};
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
} {
columns[3].grid_mut()[c].set_bg(color);
for c in columns[3].grid().row_iter(area_col_3, 0..min_width.3, 0) {
columns[3].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, _x..(_x + 1), idx)
} {
columns[3].grid_mut()[c].set_bg(color).set_keep_bg(true);
}
{
let mut area_col_4 = columns[4].area().nth_row(idx);
area_col_4 = area_col_4.skip_cols(columns[4].grid_mut().write_string(
&strings.subject,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area_col_4,
None,
));
#[cfg(feature = "regexp")]
{
for text_formatter in
crate::conf::text_format_regexps(context, "listing.subject")
{
let t = columns[4].grid_mut().insert_tag(text_formatter.tag);
for (start, end) in
text_formatter.regexp.find_iter(strings.subject.as_str())
{
columns[4].grid_mut().set_tag(t, (start, idx), (end, idx));
}
}
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, (x + 1)..(_x + 1), idx)
} {
columns[3].grid_mut()[c]
.set_keep_fg(true)
.set_keep_bg(true)
.set_keep_attrs(true);
area_col_4 = area_col_4.skip_cols(1);
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
let color = color.unwrap_or(self.color_cache.tag_default.bg);
let (x, _) = columns[4].grid_mut().write_string(
t,
self.color_cache.tag_default.fg,
color,
self.color_cache.tag_default.attrs,
area_col_4,
None,
);
for c in columns[4].grid().row_iter(area_col_4, 0..(x + 1), 0) {
columns[4].grid_mut()[c]
.set_bg(color)
.set_keep_fg(true)
.set_keep_bg(true)
.set_keep_attrs(true);
}
area_col_4 = area_col_4.skip_cols(x + 1);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
} {
columns[3].grid_mut()[c].set_keep_bg(true);
for c in columns[4].grid().row_iter(area_col_4, 0..min_width.4, 0) {
columns[4].grid_mut()[c]
.set_ch(' ')
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
x = _x + 2;
}
}
if self.length == 0 && self.filter_term.is_empty() {
let account = &context.accounts[&self.cursor_pos.0];
let message: String = account[&self.cursor_pos.1].status();
if self.data_columns.columns[0].resize_with_context(message.len(), 1, context) {
let area = self.data_columns.columns[0].area();
let area_col_0 = self.data_columns.columns[0].area();
self.data_columns.columns[0].grid_mut().write_string(
message.as_str(),
self.color_cache.theme_default.fg,
self.color_cache.theme_default.bg,
self.color_cache.theme_default.attrs,
area,
area_col_0,
None,
);
}
@ -1517,6 +1374,53 @@ impl CompactListing {
}
}
fn draw_relative_numbers(
&mut self,
grid: &mut CellBuffer,
area: Area,
top_idx: usize,
context: &Context,
) {
let width = self.data_columns.columns[0].area().width();
let area = area.take_cols(width);
let account = &context.accounts[&self.cursor_pos.0];
let threads = account.collection.get_threads(self.cursor_pos.1);
for i in 0..area.height() {
let idx = top_idx + i;
if idx >= self.length {
break;
}
let row_attr = if let Some(thread_hash) = self.get_thread_under_cursor(idx) {
let thread = threads.thread_ref(thread_hash);
row_attr!(
self.color_cache,
even: idx % 2 == 0,
unseen: thread.unseen() > 0,
highlighted: self.new_cursor_pos.2 == idx,
selected: self.rows.is_thread_selected(thread_hash)
)
} else {
row_attr!(self.color_cache, even: (top_idx + i) % 2 == 0, unseen: false, highlighted: true, selected: false)
};
grid.clear_area(area.nth_row(i), row_attr);
grid.write_string(
&if self.new_cursor_pos.2.saturating_sub(top_idx) == i {
self.new_cursor_pos.2.to_string()
} else {
(i as isize - (self.new_cursor_pos.2 - top_idx) as isize)
.abs()
.to_string()
},
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area.nth_row(i),
None,
);
}
}
fn perform_movement(&mut self, height: Option<usize>) {
let rows = height.unwrap_or(1);
if let Some(mvm) = self.movement.take() {

@ -771,6 +771,7 @@ impl ConversationsListing {
),
from: FromString(Address::display_name_slice(from)),
tags: TagString(tags_string, colors),
unseen: thread.unseen() > 0,
highlight_self: false,
}
}

@ -137,7 +137,7 @@ pub struct PlainListing {
subsort: (SortField, SortOrder),
rows: RowsState<(ThreadHash, EnvelopeHash)>,
/// Cache current view.
data_columns: DataColumns<4>,
data_columns: DataColumns<5>,
#[allow(clippy::type_complexity)]
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
@ -442,6 +442,10 @@ impl ListingTrait for PlainListing {
}
context.dirty_areas.push_back(new_area);
}
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
self.draw_relative_numbers(grid, area, top_idx);
context.dirty_areas.push_back(area);
}
if !self.force_draw {
return;
}
@ -461,6 +465,9 @@ impl ListingTrait for PlainListing {
/* copy table columns */
self.data_columns
.draw(grid, top_idx, self.cursor_pos.2, grid.bounds_iter(area));
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
self.draw_relative_numbers(grid, area, top_idx);
}
/* apply each row colors separately */
for i in top_idx..(top_idx + area.height()) {
if let Some(row_attr) = self.rows.row_attr_cache.get(&i) {
@ -696,6 +703,7 @@ impl PlainListing {
),
from: FromString(Address::display_name_slice(e.from())),
tags: TagString(tags, colors),
unseen: !e.is_seen(),
highlight_self: false,
}
}
@ -714,15 +722,28 @@ impl PlainListing {
SmallVec<[u8; 1024]>,
SmallVec<[u8; 1024]>,
SmallVec<[u8; 1024]>,
SmallVec<[u8; 1024]>,
) = (
SmallVec::new(),
SmallVec::new(),
SmallVec::new(),
SmallVec::new(),
SmallVec::new(),
);
let should_highlight_self = mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self
)
.is_true();
let my_address: Address = context.accounts[&self.cursor_pos.0]
.settings
.account
.make_display_name();
for i in iter {
if !context.accounts[&self.cursor_pos.0].contains_key(i) {
if !context.accounts[&self.cursor_pos.0].contains_key(i)
|| !threads.envelope_to_thread.contains_key(&i)
{
log::debug!("key = {}", i);
log::debug!(
"name = {} {}",
@ -755,7 +776,9 @@ impl PlainListing {
);
self.rows.row_attr_cache.insert(self.length, row_attr);
let entry_strings = self.make_entry_string(&envelope, context);
let mut entry_strings = self.make_entry_string(&envelope, context);
entry_strings.highlight_self =
should_highlight_self && { envelope.recipient_any(&my_address) };
row_widths
.0
.push(digits_of_num!(self.length).try_into().unwrap_or(255));
@ -774,20 +797,22 @@ impl PlainListing {
.unwrap_or(255),
);
row_widths.3.push(
(entry_strings.flag.grapheme_width()
+ entry_strings.subject.grapheme_width()
+ 1
+ entry_strings.tags.grapheme_width())
.try_into()
.unwrap_or(255),
entry_strings
.flag
.grapheme_width()
.try_into()
.unwrap_or(255),
); /* flags */
row_widths.4.push(
(entry_strings.subject.grapheme_width() + 1 + entry_strings.tags.grapheme_width())
.try_into()
.unwrap_or(255),
);
min_width.1 = min_width.1.max(entry_strings.date.grapheme_width()); /* date */
min_width.2 = min_width.2.max(entry_strings.from.grapheme_width()); /* from */
min_width.3 = min_width.3.max(
entry_strings.flag.grapheme_width()
+ entry_strings.subject.grapheme_width()
+ 1
+ entry_strings.tags.grapheme_width(),
min_width.3 = min_width.3.max(entry_strings.flag.grapheme_width()); /* flags */
min_width.4 = min_width.4.max(
entry_strings.subject.grapheme_width() + 1 + entry_strings.tags.grapheme_width(),
); /* tags + subject */
self.rows.insert_thread(
threads.envelope_to_thread[&i],
@ -805,6 +830,7 @@ impl PlainListing {
self.data_columns.elasticities[1].set_rigid();
self.data_columns.elasticities[2].set_grow(15, Some(35));
self.data_columns.elasticities[3].set_rigid();
self.data_columns.elasticities[4].set_rigid();
self.data_columns
.cursor_config
.set_handle(true)
@ -822,12 +848,15 @@ impl PlainListing {
_ = self.data_columns.columns[1].resize_with_context(min_width.1, self.rows.len(), context);
/* from column */
_ = self.data_columns.columns[2].resize_with_context(min_width.2, self.rows.len(), context);
/* subject column */
// flags column
_ = self.data_columns.columns[3].resize_with_context(min_width.3, self.rows.len(), context);
// subject column
_ = self.data_columns.columns[4].resize_with_context(min_width.4, self.rows.len(), context);
self.data_columns.segment_tree[0] = row_widths.0.into();
self.data_columns.segment_tree[1] = row_widths.1.into();
self.data_columns.segment_tree[2] = row_widths.2.into();
self.data_columns.segment_tree[3] = row_widths.3.into();
self.data_columns.segment_tree[4] = row_widths.4.into();
let iter = if self.filter_term.is_empty() {
Box::new(self.local_collection.iter().cloned())
@ -853,137 +882,159 @@ impl PlainListing {
let row_attr = self.rows.row_attr_cache[&idx];
let (x, _) = {
let area = columns[0].area().nth_row(idx);
columns[0].grid_mut().write_string(
&idx.to_string(),
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
None,
)
};
for c in {
let area = columns[0].area();
columns[0].grid_mut().row_iter(area, x..min_width.0, idx)
} {
columns[0].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
{
let mut area_col_0 = columns[0].area().nth_row(idx);
if !*account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
area_col_0 = area_col_0.skip_cols(columns[0].grid_mut().write_string(
&idx.to_string(),
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area_col_0,
None,
));
for c in columns[0].grid().row_iter(area_col_0, 0..min_width.0, 0) {
columns[0].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
}
}
let (x, _) = {
let area = columns[1].area().nth_row(idx);
columns[1].grid_mut().write_string(
{
let mut area_col_1 = columns[1].area().nth_row(idx);
area_col_1 = area_col_1.skip_cols(columns[1].grid_mut().write_string(
&strings.date,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
area_col_1,
None,
)
};
for c in {
let area = columns[1].area();
columns[1].grid_mut().row_iter(area, x..min_width.1, idx)
} {
columns[1].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
));
for c in columns[1].grid().row_iter(area_col_1, 0..min_width.1, 0) {
columns[1].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
}
let (x, _) = {
let area = columns[2].area().nth_row(idx);
columns[2].grid_mut().write_string(
{
let area_col_2 = columns[2].area().nth_row(idx);
let (skip_cols, _) = columns[2].grid_mut().write_string(
&strings.from,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
area_col_2,
None,
)
};
for c in {
let area = columns[2].area();
columns[2].grid_mut().row_iter(area, x..min_width.2, idx)
} {
columns[2].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs)
.set_ch(' ');
);
#[cfg(feature = "regexp")]
{
for text_formatter in crate::conf::text_format_regexps(context, "listing.from")
{
let t = columns[2].grid_mut().insert_tag(text_formatter.tag);
for (start, end) in text_formatter.regexp.find_iter(strings.from.as_str()) {
columns[2].grid_mut().set_tag(
t,
(start + skip_cols, idx),
(end + skip_cols, idx),
);
}
}
}
for c in columns[2]
.grid()
.row_iter(area_col_2, skip_cols..min_width.2, 0)
{
columns[2].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
}
let (x, _) = {
let area = columns[3].area().nth_row(idx);
columns[3].grid_mut().write_string(
{
let mut area_col_3 = columns[3].area().nth_row(idx);
area_col_3 = area_col_3.skip_cols(columns[3].grid_mut().write_string(
&strings.flag,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
area_col_3,
None,
)
};
let x = {
let area = columns[3].area().nth_row(idx).skip_cols(x);
columns[3]
.grid_mut()
.write_string(
&strings.subject,
row_attr.fg,
));
if strings.highlight_self {
let (x, _) = columns[3].grid_mut().write_string(
mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self_flag
)
.as_ref()
.map(|s| s.as_str())
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
self.color_cache.highlight_self.fg,
row_attr.bg,
row_attr.attrs,
area,
row_attr.attrs | Attr::FORCE_TEXT,
area_col_3,
None,
)
.0
+ x
};
let mut x = x + 1;
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
let color = color.unwrap_or(self.color_cache.tag_default.bg);
let _x = {
let area = columns[3].area().nth_row(idx).skip_cols(x + 1);
columns[3]
.grid_mut()
.write_string(
t,
self.color_cache.tag_default.fg,
color,
self.color_cache.tag_default.attrs,
area,
None,
)
.0
+ x
+ 1
};
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
} {
columns[3].grid_mut()[c].set_bg(color);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, _x..(_x + 1), idx)
} {
columns[3].grid_mut()[c].set_bg(color).set_keep_bg(true);
);
for c in columns[3].grid().row_iter(area_col_3, 0..x, 0) {
columns[3].grid_mut()[c].set_keep_fg(true);
}
area_col_3 = area_col_3.skip_cols(x + 1);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, (x + 1)..(_x + 1), idx)
} {
for c in columns[3].grid().row_iter(area_col_3, 0..min_width.3, 0) {
columns[3].grid_mut()[c]
.set_keep_fg(true)
.set_keep_bg(true)
.set_keep_attrs(true);
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
} {
columns[3].grid_mut()[c].set_keep_bg(true);
}
{
let mut area_col_4 = columns[4].area().nth_row(idx);
area_col_4 = area_col_4.skip_cols(columns[4].grid_mut().write_string(
&strings.subject,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area_col_4,
None,
));
#[cfg(feature = "regexp")]
{
for text_formatter in
crate::conf::text_format_regexps(context, "listing.subject")
{
let t = columns[4].grid_mut().insert_tag(text_formatter.tag);
for (start, end) in
text_formatter.regexp.find_iter(strings.subject.as_str())
{
columns[4].grid_mut().set_tag(t, (start, idx), (end, idx));
}
}
}
area_col_4 = area_col_4.skip_cols(1);
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
let color = color.unwrap_or(self.color_cache.tag_default.bg);
let (x, _) = columns[4].grid_mut().write_string(
t,
self.color_cache.tag_default.fg,
color,
self.color_cache.tag_default.attrs,
area_col_4.skip_cols(1),
None,
);
for c in columns[4].grid().row_iter(area_col_4, 0..(x + 1), 0) {
columns[4].grid_mut()[c]
.set_bg(color)
.set_keep_fg(true)
.set_keep_bg(true)
.set_keep_attrs(true);
}
area_col_4 = area_col_4.skip_cols(x + 1);
}
for c in columns[4].grid().row_iter(area_col_4, 0..min_width.4, 0) {
columns[4].grid_mut()[c]
.set_ch(' ')
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
x = _x + 2;
}
}
if self.length == 0 && self.filter_term.is_empty() {
@ -1033,21 +1084,9 @@ impl PlainListing {
let strings = self.make_entry_string(&envelope, context);
drop(envelope);
let columns = &mut self.data_columns.columns;
{
let area = columns[0].area().nth_row(idx);
columns[0].grid_mut().clear_area(area, row_attr)
};
{
let area = columns[1].area().nth_row(idx);
columns[1].grid_mut().clear_area(area, row_attr);
}
{
let area = columns[2].area().nth_row(idx);
columns[2].grid_mut().clear_area(area, row_attr);
}
{
let area = columns[3].area().nth_row(idx);
columns[3].grid_mut().clear_area(area, row_attr);
for n in 0..=4 {
let area = columns[n].area().nth_row(idx);
columns[n].grid_mut().clear_area(area, row_attr);
}
let (x, _) = {
@ -1062,8 +1101,8 @@ impl PlainListing {
)
};
for c in {
let area = columns[0].area();
columns[0].grid_mut().row_iter(area, x..area.width(), idx)
let area = columns[0].area().nth_row(idx);
columns[0].grid_mut().row_iter(area, x..area.width(), 0)
} {
columns[0].grid_mut()[c]
.set_bg(row_attr.bg)
@ -1081,8 +1120,8 @@ impl PlainListing {
)
};
for c in {
let area = columns[1].area();
columns[1].grid_mut().row_iter(area, x..area.width(), idx)
let area = columns[1].area().nth_row(idx);
columns[1].grid_mut().row_iter(area, x..area.width(), 0)
} {
columns[1].grid_mut()[c]
.set_bg(row_attr.bg)
@ -1100,85 +1139,101 @@ impl PlainListing {
)
};
for c in {
let area = columns[2].area();
columns[2].grid_mut().row_iter(area, x..area.width(), idx)
let area = columns[2].area().nth_row(idx);
columns[2].grid_mut().row_iter(area, x..area.width(), 0)
} {
columns[2].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
let (x, _) = {
let area = columns[3].area().nth_row(idx);
columns[3].grid_mut().write_string(
{
let mut area_col_3 = columns[3].area().nth_row(idx);
area_col_3 = area_col_3.skip_cols(columns[3].grid_mut().write_string(
&strings.flag,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
area_col_3,
None,
)
};
let (x, _) = {
let area = columns[3].area().nth_row(idx).skip_cols(x);
columns[3].grid_mut().write_string(
));
if strings.highlight_self {
let (x, _) = columns[3].grid_mut().write_string(
mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self_flag
)
.as_ref()
.map(|s| s.as_str())
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
self.color_cache.highlight_self.fg,
row_attr.bg,
row_attr.attrs | Attr::FORCE_TEXT,
area_col_3,
None,
);
for c in columns[3].grid().row_iter(area_col_3, 0..x, 0) {
columns[3].grid_mut()[c].set_keep_fg(true);
}
area_col_3 = area_col_3.skip_cols(x + 1);
}
for c in columns[3]
.grid()
.row_iter(area_col_3, 0..area_col_3.width(), 0)
{
columns[3].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
}
{
let mut area_col_4 = columns[4].area().nth_row(idx);
area_col_4 = area_col_4.skip_cols(columns[4].grid_mut().write_string(
&strings.subject,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area,
area_col_4,
None,
)
};
let x = {
let mut x = x + 1;
));
#[cfg(feature = "regexp")]
{
for text_formatter in crate::conf::text_format_regexps(context, "listing.subject") {
let t = columns[4].grid_mut().insert_tag(text_formatter.tag);
for (start, end) in text_formatter.regexp.find_iter(strings.subject.as_str()) {
columns[4].grid_mut().set_tag(t, (start, idx), (end, idx));
}
}
}
area_col_4 = area_col_4.skip_cols(1);
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
let color = color.unwrap_or(self.color_cache.tag_default.bg);
let (_x, _) = {
let area = columns[3].area().nth_row(idx).skip_cols(x + 1);
columns[3].grid_mut().write_string(
t,
self.color_cache.tag_default.fg,
color,
self.color_cache.tag_default.attrs,
area,
None,
)
};
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
} {
columns[3].grid_mut()[c].set_bg(color);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, _x..(_x + 1), idx)
} {
columns[3].grid_mut()[c].set_bg(color).set_keep_bg(true);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, (x + 1)..(_x + 1), idx)
} {
columns[3].grid_mut()[c].set_keep_fg(true).set_keep_bg(true);
}
for c in {
let area = columns[3].area();
columns[3].grid_mut().row_iter(area, x..(x + 1), idx)
} {
columns[3].grid_mut()[c].set_keep_bg(true);
let (x, _) = columns[4].grid_mut().write_string(
t,
self.color_cache.tag_default.fg,
color,
self.color_cache.tag_default.attrs,
area_col_4.skip_cols(1),
None,
);
for c in columns[4].grid().row_iter(area_col_4, 0..(x + 1), 0) {
columns[4].grid_mut()[c]
.set_bg(color)
.set_keep_fg(true)
.set_keep_bg(true)
.set_keep_attrs(true);
}
x = _x + 2;
area_col_4 = area_col_4.skip_cols(x + 1);
}
for c in columns[4]
.grid()
.row_iter(area_col_4, 0..area_col_4.width(), 0)
{
columns[4].grid_mut()[c]
.set_ch(' ')
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
x
};
for c in {
let area = columns[3].area();
columns[3].grid().row_iter(area, x..area.width(), idx)
} {
columns[3].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), strings);
}
@ -1229,6 +1284,49 @@ impl PlainListing {
}
}
fn draw_relative_numbers(&mut self, grid: &mut CellBuffer, area: Area, top_idx: usize) {
let width = self.data_columns.columns[0].area().width();
let area = area.take_cols(width);
for i in 0..area.height() {
if top_idx + i >= self.length {
break;
}
let row_attr = if let Some(env_hash) = self.get_env_under_cursor(top_idx + i) {
let unseen = self
.rows
.entries
.get(top_idx + i)
.map(|((_, _), strings)| strings.unseen)
.unwrap_or(false);
row_attr!(
self.color_cache,
even: (top_idx + i) % 2 == 0,
unseen: unseen,
highlighted: self.cursor_pos.2 == (top_idx + i),
selected: self.rows.selection[&env_hash]
)
} else {
row_attr!(self.color_cache, even: (top_idx + i) % 2 == 0, unseen: false, highlighted: true, selected: false)
};
grid.clear_area(area.nth_row(i), row_attr);
grid.write_string(
&if self.new_cursor_pos.2.saturating_sub(top_idx) == i {
self.new_cursor_pos.2.to_string()
} else {
(i as isize - (self.new_cursor_pos.2 - top_idx) as isize)
.abs()
.to_string()
},
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area.nth_row(i),
None,
);
}
}
fn perform_movement(&mut self, height: Option<usize>) {
let rows = height.unwrap_or(1);
if let Some(mvm) = self.movement.take() {

@ -19,12 +19,12 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use std::{cmp, convert::TryInto, iter::FromIterator};
use std::{convert::TryInto, iter::FromIterator};
use melib::{Address, SortField, SortOrder, ThreadNode, Threads};
use super::*;
use crate::{components::PageMovement, jobs::JoinHandle};
use crate::{components::PageMovement, jobs::JoinHandle, segment_tree::SegmentTree};
macro_rules! row_attr {
($color_cache:expr, even: $even:expr, unseen: $unseen:expr, highlighted: $highlighted:expr, selected: $selected:expr $(,)*) => {{
@ -146,6 +146,7 @@ pub struct ThreadListing {
filtered_selection: Vec<EnvelopeHash>,
_filtered_order: HashMap<EnvelopeHash, usize>,
data_columns: DataColumns<5>,
rows_drawn: SegmentTree,
rows: RowsState<(ThreadHash, EnvelopeHash)>,
seen_cache: IndexMap<EnvelopeHash, bool>,
/// If we must redraw on next redraw event
@ -375,11 +376,10 @@ impl MailListingTrait for ThreadListing {
.try_into()
.unwrap_or(255),
);
min_width.1 = cmp::max(min_width.1, entry_strings.date.grapheme_width()); /* date */
min_width.2 = cmp::max(min_width.2, entry_strings.from.grapheme_width()); /* from */
min_width.3 = cmp::max(min_width.3, entry_strings.flag.grapheme_width()); /* flags */
min_width.4 = cmp::max(
min_width.4,
min_width.1 = min_width.1.max(entry_strings.date.grapheme_width()); /* date */
min_width.2 = min_width.2.max(entry_strings.from.grapheme_width()); /* from */
min_width.3 = min_width.3.max(entry_strings.flag.grapheme_width()); /* flags */
min_width.4 = min_width.4.max(
entry_strings.subject.grapheme_width()
+ 1
+ entry_strings.tags.grapheme_width(),
@ -444,6 +444,12 @@ impl MailListingTrait for ThreadListing {
_ = self.data_columns.columns[4].resize_with_context(min_width.4, self.rows.len(), context);
self.data_columns.segment_tree[4] = row_widths.4.into();
self.rows_drawn = SegmentTree::from(
std::iter::repeat(1)
.take(self.rows.len())
.collect::<SmallVec<_>>(),
);
debug_assert_eq!(self.rows_drawn.array.len(), self.rows.len());
self.draw_rows(
context,
0,
@ -536,10 +542,6 @@ impl ListingTrait for ThreadListing {
if self.cursor_pos.2 != self.new_cursor_pos.2 && prev_page_no == page_no {
let old_cursor_pos = self.cursor_pos;
self.cursor_pos = self.new_cursor_pos;
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
self.draw_relative_numbers(grid, area, top_idx);
context.dirty_areas.push_back(area);
}
for &(idx, highlight) in &[(old_cursor_pos.2, false), (self.new_cursor_pos.2, true)] {
if idx >= self.length {
continue; //bounds check
@ -555,6 +557,10 @@ impl ListingTrait for ThreadListing {
}
context.dirty_areas.push_back(new_area);
}
if *account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
self.draw_relative_numbers(grid, area, top_idx);
context.dirty_areas.push_back(area);
}
if !self.force_draw {
return;
}
@ -571,12 +577,6 @@ impl ListingTrait for ThreadListing {
grid.clear_area(area, self.color_cache.theme_default);
}
self.draw_rows(
context,
top_idx,
self.length.saturating_sub(1).min(top_idx + rows - 1),
);
// Page_no has changed, so draw new page
_ = self.data_columns.recalc_widths(area.size(), top_idx);
// copy table columns
@ -759,6 +759,7 @@ impl ThreadListing {
subsort: (Default::default(), Default::default()),
color_cache: ColorCache::new(context, IndexStyle::Threaded),
data_columns: DataColumns::default(),
rows_drawn: SegmentTree::default(),
rows: RowsState::default(),
seen_cache: IndexMap::default(),
filter_term: String::new(),
@ -889,6 +890,7 @@ impl ThreadListing {
),
from: FromString(Address::display_name_slice(e.from())),
tags: TagString(tags, colors),
unseen: !e.is_seen(),
highlight_self: false,
}
}
@ -898,6 +900,12 @@ impl ThreadListing {
return;
}
debug_assert!(end >= start);
if self.rows_drawn.get_max(start, end) == 0 {
return;
}
for i in start..=end {
self.rows_drawn.update(i, 0);
}
let min_width = (
self.data_columns.columns[0].area().width(),
self.data_columns.columns[1].area().width(),
@ -905,6 +913,7 @@ impl ThreadListing {
self.data_columns.columns[3].area().width(),
self.data_columns.columns[4].area().width(),
);
let columns = &mut self.data_columns.columns;
for (idx, ((_thread_hash, env_hash), strings)) in self
.rows
@ -921,181 +930,159 @@ impl ThreadListing {
self.color_cache,
even: idx % 2 == 0,
unseen: !self.seen_cache[env_hash],
highlighted: self.cursor_pos.2 == idx,
selected: self.rows.selection[env_hash]
highlighted: false,
selected: false,
);
self.rows.row_attr_cache.insert(idx, row_attr);
{
let area = self.data_columns.columns[0].area();
let mut area_col_0 = columns[0].area().nth_row(idx);
if !*account_settings!(context[self.cursor_pos.0].listing.relative_list_indices) {
let (x, _) = self.data_columns.columns[0].grid_mut().write_string(
area_col_0 = area_col_0.skip_cols(columns[0].grid_mut().write_string(
&idx.to_string(),
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area.nth_row(idx),
area_col_0,
None,
);
for x in x..min_width.0 {
self.data_columns.columns[0].grid_mut()[(x, idx)]
));
for c in columns[0].grid().row_iter(area_col_0, 0..min_width.0, 0) {
columns[0].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
}
}
{
let area = self.data_columns.columns[1].area();
let (x, _) = self.data_columns.columns[1].grid_mut().write_string(
let mut area_col_1 = columns[1].area().nth_row(idx);
area_col_1 = area_col_1.skip_cols(columns[1].grid_mut().write_string(
&strings.date,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area.nth_row(idx),
area_col_1,
None,
);
for x in x..min_width.1 {
self.data_columns.columns[1].grid_mut()[(x, idx)]
));
for c in columns[1].grid().row_iter(area_col_1, 0..min_width.1, 0) {
columns[1].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
}
{
let area = self.data_columns.columns[2].area();
let (x, _) = self.data_columns.columns[2].grid_mut().write_string(
let area_col_2 = columns[2].area().nth_row(idx);
let (skip_cols, _) = columns[2].grid_mut().write_string(
&strings.from,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area.nth_row(idx),
area_col_2,
None,
);
#[cfg(feature = "regexp")]
{
for text_formatter in crate::conf::text_format_regexps(context, "listing.from")
{
let t = self.data_columns.columns[2]
.grid_mut()
.insert_tag(text_formatter.tag);
let t = columns[2].grid_mut().insert_tag(text_formatter.tag);
for (start, end) in text_formatter.regexp.find_iter(strings.from.as_str()) {
self.data_columns.columns[2].grid_mut().set_tag(
columns[2].grid_mut().set_tag(
t,
(start, idx),
(end, idx),
(start + skip_cols, idx),
(end + skip_cols, idx),
);
}
}
}
for x in x..min_width.2 {
self.data_columns.columns[2].grid_mut()[(x, idx)]
for c in columns[2]
.grid()
.row_iter(area_col_2, skip_cols..min_width.2, 0)
{
columns[2].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
}
{
let area = self.data_columns.columns[3].area();
let (x, _) = self.data_columns.columns[3].grid_mut().write_string(
let mut area_col_3 = columns[3].area().nth_row(idx);
area_col_3 = area_col_3.skip_cols(columns[3].grid_mut().write_string(
&strings.flag,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area.nth_row(idx),
area_col_3,
None,
);
for x in x..min_width.3 {
self.data_columns.columns[3].grid_mut()[(x, idx)]
));
if strings.highlight_self {
let (x, _) = columns[3].grid_mut().write_string(
mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self_flag
)
.as_ref()
.map(|s| s.as_str())
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
self.color_cache.highlight_self.fg,
row_attr.bg,
row_attr.attrs | Attr::FORCE_TEXT,
area_col_3,
None,
);
for c in columns[3].grid().row_iter(area_col_3, 0..x, 0) {
columns[3].grid_mut()[c].set_keep_fg(true);
}
area_col_3 = area_col_3.skip_cols(x + 1);
}
for c in columns[3].grid().row_iter(area_col_3, 0..min_width.3, 0) {
columns[3].grid_mut()[c]
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
}
}
{
let mut area = self.data_columns.columns[4].area();
if strings.highlight_self {
let x = self.data_columns.columns[4]
.grid_mut()
.write_string(
mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self_flag
)
.as_ref()
.map(|s| s.as_str())
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
self.color_cache.highlight_self.fg,
row_attr.bg,
row_attr.attrs | Attr::FORCE_TEXT,
area,
None,
)
.0;
for row in self.data_columns.columns[4]
.grid()
.bounds_iter(area.nth_row(0).take_cols(x))
{
for c in row {
self.data_columns.columns[4].grid_mut()[c].set_keep_fg(true);
}
}
area = area.skip_cols(x + 1);
}
let (x, _) = self.data_columns.columns[4].grid_mut().write_string(
let mut area_col_4 = columns[4].area().nth_row(idx);
area_col_4 = area_col_4.skip_cols(columns[4].grid_mut().write_string(
&strings.subject,
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area.nth_row(idx),
area_col_4,
None,
);
));
#[cfg(feature = "regexp")]
{
for text_formatter in
crate::conf::text_format_regexps(context, "listing.subject")
{
let t = self.data_columns.columns[4]
.grid_mut()
.insert_tag(text_formatter.tag);
let t = columns[4].grid_mut().insert_tag(text_formatter.tag);
for (start, end) in
text_formatter.regexp.find_iter(strings.subject.as_str())
{
self.data_columns.columns[4].grid_mut().set_tag(
t,
(start, idx),
(end, idx),
);
columns[4].grid_mut().set_tag(t, (start, idx), (end, idx));
}
}
}
let x = {
let mut x = x + 1;
let area = self.data_columns.columns[4].area();
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
let color = color.unwrap_or(self.color_cache.tag_default.bg);
let (_x, _) = self.data_columns.columns[4].grid_mut().write_string(
t,
self.color_cache.tag_default.fg,
color,
self.color_cache.tag_default.attrs,
area.nth_row(idx).skip_cols(x + 1),
None,
);
self.data_columns.columns[4].grid_mut()[(x, idx)].set_bg(color);
if _x < min_width.4 {
self.data_columns.columns[4].grid_mut()[(_x, idx)]
.set_bg(color)
.set_keep_bg(true);
}
for x in (x + 1).._x {
self.data_columns.columns[4].grid_mut()[(x, idx)]
.set_keep_fg(true)
.set_keep_bg(true)
.set_keep_attrs(true);
}
self.data_columns.columns[4].grid_mut()[(x, idx)].set_keep_bg(true);
x = _x + 1;
area_col_4 = area_col_4.skip_cols(1);
for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) {
let color = color.unwrap_or(self.color_cache.tag_default.bg);
let (x, _) = columns[4].grid_mut().write_string(
t,
self.color_cache.tag_default.fg,
color,
self.color_cache.tag_default.attrs,
area_col_4.skip_cols(1),
None,
);
for c in columns[4].grid().row_iter(area_col_4, 0..(x + 1), 0) {
columns[4].grid_mut()[c]
.set_bg(color)
.set_keep_fg(true)
.set_keep_bg(true)
.set_keep_attrs(true);
}
x
};
for x in x..min_width.4 {
self.data_columns.columns[4].grid_mut()[(x, idx)]
area_col_4 = area_col_4.skip_cols(x + 1);
}
for c in columns[4].grid().row_iter(area_col_4, 0..min_width.4, 0) {
columns[4].grid_mut()[c]
.set_ch(' ')
.set_bg(row_attr.bg)
.set_attrs(row_attr.attrs);
@ -1138,6 +1125,16 @@ impl ThreadListing {
.make_display_name();
envelope.recipient_any(&my_address)
};
// [ref:FIXME]: generate new tree indentation for this new row subject
// entry_strings.subject = SubjectString(Self::make_thread_entry(
// &envelope,
// indentation,
// thread_node_hash,
// &threads,
// &indentations,
// has_sibling,
// is_root,
// ));
drop(envelope);
std::mem::swap(
&mut self.rows.entries.get_mut(idx).unwrap().1.subject,
@ -1148,13 +1145,18 @@ impl ThreadListing {
let area = columns[n].area().nth_row(idx);
columns[n].grid_mut().clear_area(area, row_attr);
}
self.rows_drawn.update(idx, 1);
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), entry_strings);
}
fn draw_relative_numbers(&mut self, grid: &mut CellBuffer, area: Area, top_idx: usize) {
let width = self.data_columns.columns[0].area().width();
let area = area.take_cols(width);
for i in 0..area.height() {
if top_idx + i >= self.length {
break;
}
let row_attr = if let Some(env_hash) = self.get_env_under_cursor(top_idx + i) {
row_attr!(
self.color_cache,
@ -1167,27 +1169,7 @@ impl ThreadListing {
row_attr!(self.color_cache, even: (top_idx + i) % 2 == 0, unseen: false, highlighted: true, selected: false)
};
let idx_col_area = self.data_columns.columns[0].area();
self.data_columns.columns[0]
.grid_mut()
.clear_area(idx_col_area, row_attr);
grid.clear_area(area.nth_row(i).take_cols(width), row_attr);
self.data_columns.columns[0].grid_mut().write_string(
&if self.new_cursor_pos.2.saturating_sub(top_idx) == i {
self.new_cursor_pos.2.to_string()
} else {
(i as isize - (self.new_cursor_pos.2 - top_idx) as isize)
.abs()
.to_string()
},
row_attr.fg,
row_attr.bg,
row_attr.attrs,
idx_col_area.nth_row(i),
None,
);
grid.clear_area(area.nth_row(i), row_attr);
grid.write_string(
&if self.new_cursor_pos.2.saturating_sub(top_idx) == i {
self.new_cursor_pos.2.to_string()
@ -1199,7 +1181,7 @@ impl ThreadListing {
row_attr.fg,
row_attr.bg,
row_attr.attrs,
area.nth_row(i).take_cols(width),
area.nth_row(i),
None,
);
}
@ -1481,7 +1463,6 @@ impl Component for ThreadListing {
if self.force_draw {
/* Draw the entire list */
self.draw_list(grid, area, context);
self.force_draw = false;
}
} else {
/* Draw the entire list */
@ -1494,6 +1475,7 @@ impl Component for ThreadListing {
context.dirty_areas.push_back(area);
}
}
self.force_draw = false;
self.dirty = false;
}
@ -1658,9 +1640,9 @@ impl Component for ThreadListing {
Err(err) => {
context.replies.push_back(UIEvent::Notification {
title: Some("Could not perform search".into()),
source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
source: Some(err),
});
}
};
@ -1684,9 +1666,9 @@ impl Component for ThreadListing {
Ok(Some(Err(err))) => {
context.replies.push_back(UIEvent::Notification {
title: Some("Could not perform search".into()),
source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
source: Some(err),
});
}
}

@ -48,6 +48,7 @@ impl ScreenGeneration {
pub const NIL: Self = Self((0, 0));
#[inline]
#[must_use]
pub fn next(self) -> Self {
Self(uuid::Uuid::new_v4().as_u64_pair())
}
@ -521,6 +522,30 @@ impl<D: private::Sealed> From<&Screen<D>> for Area {
}
}
/// Convenience trait to turn both single `usize` values and `(usize, _)` positions to `x`
/// coordinate.
pub trait IntoColumns: private::Sealed {
#[must_use]
fn into(self) -> usize;
}
impl private::Sealed for usize {}
impl private::Sealed for Pos {}
impl IntoColumns for usize {
#[must_use]
fn into(self) -> usize {
self
}
}
impl IntoColumns for Pos {
#[must_use]
fn into(self) -> usize {
get_x(self)
}
}
impl Area {
#[inline]
pub fn height(&self) -> usize {
@ -545,6 +570,7 @@ impl Area {
/// Get `n`th row of `area` or its last one.
#[inline]
#[must_use]
pub fn nth_row(&self, n: usize) -> Self {
let Self {
offset,
@ -574,6 +600,7 @@ impl Area {
/// Get `n`th col of `area` or its last one.
#[inline]
#[must_use]
pub fn nth_col(&self, n: usize) -> Self {
let Self {
offset,
@ -602,6 +629,7 @@ impl Area {
}
/// Place box given by `(width, height)` in corner of `area`
#[must_use]
pub fn place_inside(&self, (width, height): (usize, usize), upper: bool, left: bool) -> Self {
if self.is_empty() || width < 3 || height < 3 {
return *self;
@ -644,6 +672,7 @@ impl Area {
/// Place given area of dimensions `(width, height)` inside `area` according
/// to given alignment
#[must_use]
pub fn align_inside(
&self,
(width, height): (usize, usize),
@ -677,6 +706,7 @@ impl Area {
/// Place box given by `dimensions` in center of `area`
#[inline]
#[must_use]
pub fn center_inside(&self, dimensions: (usize, usize)) -> Self {
self.align_inside(dimensions, Alignment::Center, Alignment::Center)
}
@ -712,6 +742,7 @@ impl Area {
/// assert_eq!(body.height(), 18);
/// ```
#[inline]
#[must_use]
pub fn skip_rows(&self, n: usize) -> Self {
let n = std::cmp::min(n, self.height());
if self.is_empty() || self.upper_left.1 + n > self.bottom_right.1 {
@ -743,6 +774,7 @@ impl Area {
/// assert_eq!(header, area.take_rows(2));
/// ```
#[inline]
#[must_use]
pub fn skip_rows_from_end(&self, n: usize) -> Self {
let n = std::cmp::min(n, self.height());
if self.is_empty() || self.bottom_right.1 < n {
@ -755,6 +787,21 @@ impl Area {
}
}
#[inline]
#[must_use]
fn _skip_cols_inner(&self, n: usize) -> Self {
let n = std::cmp::min(n, self.width());
if self.is_empty() || self.bottom_right.0 < self.upper_left.0 + n {
return self.into_empty();
}
Self {
offset: pos_inc(self.offset, (n, 0)),
upper_left: pos_inc(self.upper_left, (n, 0)),
..*self
}
}
/// Skip the first `n` rows and return the remaining area.
/// Return value will be an empty area if `n` is more than the width.
///
@ -772,17 +819,10 @@ impl Area {
/// assert_eq!(indent.width(), 118);
/// ```
#[inline]
pub fn skip_cols(&self, n: usize) -> Self {
let n = std::cmp::min(n, self.width());
if self.is_empty() || self.bottom_right.0 < self.upper_left.0 + n {
return self.into_empty();
}
Self {
offset: pos_inc(self.offset, (n, 0)),
upper_left: pos_inc(self.upper_left, (n, 0)),
..*self
}
#[must_use]
pub fn skip_cols(&self, n: impl IntoColumns) -> Self {
let n: usize = n.into();
self._skip_cols_inner(n)
}
/// Skip the last `n` rows and return the remaining area.
@ -803,6 +843,7 @@ impl Area {
/// assert_eq!(indent, area.take_cols(118));
/// ```
#[inline]
#[must_use]
pub fn skip_cols_from_end(&self, n: usize) -> Self {
let n = std::cmp::min(n, self.width());
if self.is_empty() || self.bottom_right.0 < n {
@ -816,6 +857,7 @@ impl Area {
/// Shortcut for using `Area::skip_cols` and `Area::skip_rows` together.
#[inline]
#[must_use]
pub fn skip(&self, n_cols: usize, n_rows: usize) -> Self {
self.skip_cols(n_cols).skip_rows(n_rows)
}
@ -837,6 +879,7 @@ impl Area {
/// assert_eq!(header.height(), 2);
/// ```
#[inline]
#[must_use]
pub fn take_rows(&self, n: usize) -> Self {
let n = std::cmp::min(n, self.height());
if self.is_empty() || self.bottom_right.1 < (self.height() - n) {
@ -869,6 +912,7 @@ impl Area {
/// assert_eq!(header.width(), 2);
/// ```
#[inline]
#[must_use]
pub fn take_cols(&self, n: usize) -> Self {
let n = std::cmp::min(n, self.width());
if self.is_empty() || self.bottom_right.0 < (self.width() - n) {
@ -886,41 +930,49 @@ impl Area {
/// Shortcut for using `Area::take_cols` and `Area::take_rows` together.
#[inline]
#[must_use]
pub fn take(&self, n_cols: usize, n_rows: usize) -> Self {
self.take_cols(n_cols).take_rows(n_rows)
}
#[inline]
#[must_use]
pub const fn upper_left(&self) -> Pos {
self.upper_left
}
#[inline]
#[must_use]
pub const fn bottom_right(&self) -> Pos {
self.bottom_right
}
#[inline]
#[must_use]
pub const fn upper_right(&self) -> Pos {
set_x(self.upper_left, get_x(self.bottom_right))
}
#[inline]
#[must_use]
pub const fn bottom_left(&self) -> Pos {
set_y(self.upper_left, get_y(self.bottom_right))
}
#[inline]
#[must_use]
pub const fn offset(&self) -> Pos {
self.offset
}
#[inline]
#[must_use]
pub const fn generation(&self) -> ScreenGeneration {
self.generation
}
#[inline]
#[must_use]
pub const fn new_empty(generation: ScreenGeneration) -> Self {
Self {
offset: (0, 0),
@ -934,6 +986,7 @@ impl Area {
}
#[inline]
#[must_use]
pub const fn into_empty(self) -> Self {
Self {
offset: (0, 0),
@ -945,6 +998,7 @@ impl Area {
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.empty
|| (self.upper_left.0 > self.bottom_right.0 || self.upper_left.1 > self.bottom_right.1)
@ -952,26 +1006,31 @@ impl Area {
}
#[inline(always)]
#[must_use]
const fn pos_inc(p: Pos, inc: (usize, usize)) -> Pos {
(p.0 + inc.0, p.1 + inc.1)
}
#[inline(always)]
#[must_use]
const fn get_x(p: Pos) -> usize {
p.0
}
#[inline(always)]
#[must_use]
const fn get_y(p: Pos) -> usize {
p.1
}
#[inline(always)]
#[must_use]
const fn set_x(p: Pos, new_x: usize) -> Pos {
(new_x, p.1)
}
#[inline(always)]
#[must_use]
const fn set_y(p: Pos, new_y: usize) -> Pos {
(p.0, new_y)
}

@ -214,7 +214,7 @@ impl<const N: usize> DataColumns<N> {
width_accum += self.widths[i];
}
// add column gaps
width_accum += 2 * N.saturating_sub(1);
width_accum += 1 * N.saturating_sub(1);
debug_assert!(growees >= growees_max);
if width_accum >= screen_width || screen_height == 0 || screen_width == 0 || growees == 0 {
self.width_accum = width_accum;
@ -260,7 +260,7 @@ impl<const N: usize> DataColumns<N> {
break;
}
x_offset -= self.widths[col];
x_offset = x_offset.saturating_sub(2);
x_offset = x_offset.saturating_sub(1);
}
for col in start_col..N {

@ -169,6 +169,7 @@ mod tests {
);
assert_eq!("●".grapheme_width(), 1);
assert_eq!("●📎".grapheme_width(), 3);
assert_eq!("●📎︎".grapheme_width(), 3);
assert_eq!("●\u{FE0E}📎\u{FE0E}".grapheme_width(), 3);
assert_eq!("🎃".grapheme_width(), 2);
assert_eq!("👻".grapheme_width(), 2);

Loading…
Cancel
Save