Add table UI widget
parent
bd22f986f0
commit
104352e595
|
@ -46,7 +46,7 @@ pub struct ContactList {
|
|||
new_cursor_pos: usize,
|
||||
account_pos: usize,
|
||||
length: usize,
|
||||
data_columns: DataColumns,
|
||||
data_columns: DataColumns<4>,
|
||||
initialized: bool,
|
||||
theme_default: ThemeAttribute,
|
||||
highlight_theme: ThemeAttribute,
|
||||
|
|
|
@ -58,6 +58,7 @@ pub struct RowsState<T> {
|
|||
pub entries: Vec<(T, EntryStrings)>,
|
||||
pub all_threads: HashSet<ThreadHash>,
|
||||
pub all_envelopes: HashSet<EnvelopeHash>,
|
||||
pub row_attr_cache: HashMap<usize, ThemeAttribute>,
|
||||
}
|
||||
|
||||
impl<T> RowsState<T> {
|
||||
|
@ -72,6 +73,7 @@ impl<T> RowsState<T> {
|
|||
self.entries.clear();
|
||||
self.all_threads.clear();
|
||||
self.all_envelopes.clear();
|
||||
self.row_attr_cache.clear();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -232,13 +234,6 @@ impl Default for Modifier {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct DataColumns {
|
||||
pub columns: Box<[CellBuffer; 12]>,
|
||||
pub widths: [usize; 12], // widths of columns calculated in first draw and after size changes
|
||||
pub segment_tree: Box<[SegmentTree; 12]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
/// Save theme colors to avoid looking them up again and again from settings
|
||||
struct ColorCache {
|
||||
|
@ -255,8 +250,6 @@ struct ColorCache {
|
|||
odd_unseen: ThemeAttribute,
|
||||
odd_highlighted: ThemeAttribute,
|
||||
odd_selected: ThemeAttribute,
|
||||
attachment_flag: ThemeAttribute,
|
||||
thread_snooze_flag: ThemeAttribute,
|
||||
tag_default: ThemeAttribute,
|
||||
|
||||
/* Conversations */
|
||||
|
@ -2078,7 +2071,7 @@ impl Listing {
|
|||
ScrollBar::default().set_show_arrows(true).draw(
|
||||
grid,
|
||||
(
|
||||
pos_inc(upper_left!(area), (width!(area), 0)),
|
||||
pos_inc(upper_left!(area), (width!(area).saturating_sub(1), 0)),
|
||||
bottom_right!(area),
|
||||
),
|
||||
context,
|
||||
|
|
|
@ -168,7 +168,7 @@ pub struct CompactListing {
|
|||
sortcmd: bool,
|
||||
subsort: (SortField, SortOrder),
|
||||
/// Cache current view.
|
||||
data_columns: DataColumns,
|
||||
data_columns: DataColumns<4>,
|
||||
rows_drawn: SegmentTree,
|
||||
rows: RowsState<(ThreadHash, EnvelopeHash)>,
|
||||
|
||||
|
@ -222,7 +222,7 @@ impl MailListingTrait for CompactListing {
|
|||
} else {
|
||||
if let Some(env_hashes) = self
|
||||
.get_thread_under_cursor(self.cursor_pos.2)
|
||||
.and_then(|thread| self.rows.thread_to_env.get(&thread).map(|v| v.clone()))
|
||||
.and_then(|thread| self.rows.thread_to_env.get(&thread).cloned())
|
||||
{
|
||||
cursor_iter = Some(env_hashes.into_iter());
|
||||
} else {
|
||||
|
@ -261,8 +261,6 @@ impl MailListingTrait for CompactListing {
|
|||
odd_highlighted: crate::conf::value(context, "mail.listing.compact.odd_highlighted"),
|
||||
even: crate::conf::value(context, "mail.listing.compact.even"),
|
||||
odd: crate::conf::value(context, "mail.listing.compact.odd"),
|
||||
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
|
||||
thread_snooze_flag: crate::conf::value(context, "mail.listing.thread_snooze_flag"),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
..self.color_cache
|
||||
|
@ -394,6 +392,14 @@ impl MailListingTrait for CompactListing {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
self.length % 2 == 0,
|
||||
threads.thread_ref(thread).unseen() > 0,
|
||||
false,
|
||||
false
|
||||
);
|
||||
self.rows.row_attr_cache.insert(self.length, row_attr);
|
||||
|
||||
let entry_strings = self.make_entry_string(&root_envelope, context, &threads, thread);
|
||||
row_widths
|
||||
|
@ -451,6 +457,21 @@ impl MailListingTrait for CompactListing {
|
|||
|
||||
min_width.0 = self.length.saturating_sub(1).to_string().len();
|
||||
|
||||
self.data_columns.elasticities[0].set_rigid();
|
||||
self.data_columns.elasticities[1].set_rigid();
|
||||
self.data_columns.elasticities[2].set_grow(5, Some(35));
|
||||
self.data_columns.elasticities[3].set_rigid();
|
||||
self.data_columns
|
||||
.cursor_config
|
||||
.set_handle(true)
|
||||
.set_even_odd_theme(
|
||||
self.color_cache.even_highlighted,
|
||||
self.color_cache.odd_highlighted,
|
||||
);
|
||||
self.data_columns
|
||||
.theme_config
|
||||
.set_even_odd_theme(self.color_cache.even, self.color_cache.odd);
|
||||
|
||||
/* index column */
|
||||
self.data_columns.columns[0] =
|
||||
CellBuffer::new_with_context(min_width.0, self.rows.len(), None, context);
|
||||
|
@ -629,26 +650,27 @@ impl ListingTrait for CompactListing {
|
|||
let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
|
||||
|
||||
let top_idx = page_no * rows;
|
||||
self.draw_rows(
|
||||
context,
|
||||
top_idx,
|
||||
cmp::min(self.length.saturating_sub(1), top_idx + rows - 1),
|
||||
);
|
||||
let end_idx = cmp::min(self.length.saturating_sub(1), top_idx + rows - 1);
|
||||
self.draw_rows(context, top_idx, end_idx);
|
||||
|
||||
/* If cursor position has changed, remove the highlight from the previous position and
|
||||
* apply it in the new one. */
|
||||
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;
|
||||
for idx in &[old_cursor_pos.2, self.new_cursor_pos.2] {
|
||||
if *idx >= self.length {
|
||||
for &(idx, highlight) in &[(old_cursor_pos.2, false), (self.new_cursor_pos.2, true)] {
|
||||
if idx >= self.length {
|
||||
continue; //bounds check
|
||||
}
|
||||
let new_area = (
|
||||
set_y(upper_left, get_y(upper_left) + (*idx % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (*idx % rows)),
|
||||
);
|
||||
self.highlight_line(grid, new_area, *idx, context);
|
||||
let new_area = nth_row_area(area, idx % rows);
|
||||
self.data_columns
|
||||
.draw(grid, idx, self.cursor_pos.2, grid.bounds_iter(new_area));
|
||||
if highlight {
|
||||
let row_attr = row_attr!(self.color_cache, idx % 2 == 0, false, true, false);
|
||||
change_colors(grid, new_area, row_attr.fg, row_attr.bg);
|
||||
} else if let Some(row_attr) = self.rows.row_attr_cache.get(&idx) {
|
||||
change_colors(grid, new_area, row_attr.fg, row_attr.bg);
|
||||
}
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
if !self.force_draw {
|
||||
|
@ -662,115 +684,37 @@ impl ListingTrait for CompactListing {
|
|||
self.cursor_pos.2 = self.new_cursor_pos.2;
|
||||
}
|
||||
|
||||
let width = width!(area);
|
||||
self.data_columns.widths = Default::default();
|
||||
self.data_columns.widths[0] = self.data_columns.columns[0].size().0;
|
||||
self.data_columns.widths[1] = self.data_columns.columns[1].size().0; /* date*/
|
||||
self.data_columns.widths[2] = self.data_columns.columns[2].size().0; /* from */
|
||||
self.data_columns.widths[3] = self.data_columns.columns[3].size().0; /* subject */
|
||||
|
||||
let min_col_width = std::cmp::min(
|
||||
15,
|
||||
std::cmp::min(self.data_columns.widths[3], self.data_columns.widths[2]),
|
||||
);
|
||||
if self.data_columns.widths[0] + self.data_columns.widths[1] + 2 * min_col_width + 4 > width
|
||||
{
|
||||
let remainder = width
|
||||
.saturating_sub(self.data_columns.widths[0])
|
||||
.saturating_sub(self.data_columns.widths[1])
|
||||
.saturating_sub(2 * 2);
|
||||
self.data_columns.widths[2] = remainder / 6;
|
||||
} else {
|
||||
let remainder = width
|
||||
.saturating_sub(self.data_columns.widths[0])
|
||||
.saturating_sub(self.data_columns.widths[1])
|
||||
.saturating_sub(3 * 2);
|
||||
if min_col_width + self.data_columns.widths[3] > remainder {
|
||||
self.data_columns.widths[2] = min_col_width;
|
||||
}
|
||||
}
|
||||
for i in 0..3 {
|
||||
/* Set column widths to their maximum value width in the range
|
||||
* [top_idx, top_idx + rows]. By using a segment tree the query is O(logn), which is
|
||||
* great!
|
||||
*/
|
||||
self.data_columns.widths[i] =
|
||||
self.data_columns.segment_tree[i].get_max(top_idx, top_idx + rows - 1) as usize;
|
||||
}
|
||||
if self.data_columns.widths.iter().sum::<usize>() > width {
|
||||
let diff = self.data_columns.widths.iter().sum::<usize>() - width;
|
||||
if self.data_columns.widths[2] > 2 * diff {
|
||||
self.data_columns.widths[2] -= diff;
|
||||
} else {
|
||||
self.data_columns.widths[2] = std::cmp::max(
|
||||
15,
|
||||
self.data_columns.widths[2].saturating_sub((2 * diff) / 3),
|
||||
);
|
||||
}
|
||||
}
|
||||
clear_area(grid, area, self.color_cache.theme_default);
|
||||
/* Page_no has changed, so draw new page */
|
||||
let mut x = get_x(upper_left);
|
||||
let mut flag_x = 0;
|
||||
for i in 0..4 {
|
||||
let column_width = self.data_columns.widths[i];
|
||||
if i == 3 {
|
||||
flag_x = x;
|
||||
}
|
||||
if column_width == 0 {
|
||||
continue;
|
||||
}
|
||||
copy_area(
|
||||
grid,
|
||||
&self.data_columns.columns[i],
|
||||
(set_x(upper_left, x), bottom_right),
|
||||
(
|
||||
(0, top_idx),
|
||||
(column_width.saturating_sub(1), self.length - 1),
|
||||
),
|
||||
);
|
||||
x += column_width + 2; // + SEPARATOR
|
||||
if x > get_x(bottom_right) {
|
||||
break;
|
||||
_ = self
|
||||
.data_columns
|
||||
.recalc_widths((width!(area), height!(area)), top_idx);
|
||||
clear_area(grid, area, self.color_cache.theme_default);
|
||||
/* copy table columns */
|
||||
self.data_columns
|
||||
.draw(grid, top_idx, self.cursor_pos.2, grid.bounds_iter(area));
|
||||
/* apply each row colors separately */
|
||||
for i in top_idx..(top_idx + height!(area)) {
|
||||
if let Some(row_attr) = self.rows.row_attr_cache.get(&i) {
|
||||
change_colors(grid, nth_row_area(area, i % rows), row_attr.fg, row_attr.bg);
|
||||
}
|
||||
}
|
||||
|
||||
let account = &context.accounts[&self.cursor_pos.0];
|
||||
let threads = account.collection.get_threads(self.cursor_pos.1);
|
||||
for r in 0..cmp::min(self.length - top_idx, rows) {
|
||||
if let Some(thread_hash) = self.get_thread_under_cursor(r + top_idx) {
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
(r + top_idx) % 2 == 0,
|
||||
threads.thread_ref(thread_hash).unseen() > 0,
|
||||
self.cursor_pos.2 == (r + top_idx),
|
||||
self.rows.is_thread_selected(thread_hash)
|
||||
);
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
pos_inc(upper_left, (0, r)),
|
||||
(flag_x.saturating_sub(1), get_y(upper_left) + r),
|
||||
),
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
);
|
||||
for x in flag_x..get_x(bottom_right) {
|
||||
grid[(x, get_y(upper_left) + r)].set_bg(row_attr.bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.highlight_line(
|
||||
/* highlight cursor */
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
self.cursor_pos.2 % 2 == 0,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
);
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
set_y(upper_left, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
),
|
||||
self.cursor_pos.2,
|
||||
context,
|
||||
nth_row_area(area, self.cursor_pos.2 % rows),
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
);
|
||||
|
||||
/* clear gap if available height is more than count of entries */
|
||||
if top_idx + rows > self.length {
|
||||
clear_area(
|
||||
grid,
|
||||
|
@ -1050,43 +994,6 @@ impl CompactListing {
|
|||
fn update_line(&mut self, context: &Context, env_hash: EnvelopeHash) {
|
||||
let account = &context.accounts[&self.cursor_pos.0];
|
||||
|
||||
let selected_flag_len = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.selected_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_SELECTED_FLAG)
|
||||
.grapheme_width();
|
||||
let thread_snoozed_flag_len = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.thread_snoozed_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_SNOOZED_FLAG)
|
||||
.grapheme_width();
|
||||
let unseen_flag_len = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.unseen_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_UNSEEN_FLAG)
|
||||
.grapheme_width();
|
||||
let attachment_flag_len = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.attachment_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_ATTACHMENT_FLAG)
|
||||
.grapheme_width();
|
||||
|
||||
if !account.contains_key(env_hash) {
|
||||
/* The envelope has been renamed or removed, so wait for the appropriate event to
|
||||
* arrive */
|
||||
|
@ -1102,8 +1009,9 @@ impl CompactListing {
|
|||
idx % 2 == 0,
|
||||
thread.unseen() > 0,
|
||||
false,
|
||||
false,
|
||||
self.rows.is_thread_selected(thread_hash)
|
||||
);
|
||||
self.rows.row_attr_cache.insert(idx, row_attr);
|
||||
let strings = self.make_entry_string(&envelope, context, &threads, thread_hash);
|
||||
drop(envelope);
|
||||
let columns = &mut self.data_columns.columns;
|
||||
|
@ -1204,25 +1112,6 @@ impl CompactListing {
|
|||
for c in columns[3].row_iter(x..min_width.3, idx) {
|
||||
columns[3][c].set_ch(' ').set_bg(row_attr.bg);
|
||||
}
|
||||
/* Set fg color for flags */
|
||||
let mut x = 0;
|
||||
if self.rows.selection.get(&env_hash).cloned().unwrap_or(false) {
|
||||
x += selected_flag_len;
|
||||
}
|
||||
if thread.snoozed() {
|
||||
for x in x..(x + thread_snoozed_flag_len) {
|
||||
columns[3][(x, idx)].set_fg(self.color_cache.thread_snooze_flag.fg);
|
||||
}
|
||||
x += thread_snoozed_flag_len;
|
||||
}
|
||||
if thread.unseen() > 0 {
|
||||
x += unseen_flag_len;
|
||||
}
|
||||
if thread.has_attachments() {
|
||||
for x in x..(x + attachment_flag_len) {
|
||||
columns[3][(x, idx)].set_fg(self.color_cache.attachment_flag.fg);
|
||||
}
|
||||
}
|
||||
*self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), strings);
|
||||
self.rows_drawn.update(idx, 1);
|
||||
}
|
||||
|
@ -1246,48 +1135,8 @@ impl CompactListing {
|
|||
self.data_columns.columns[2].size().0,
|
||||
self.data_columns.columns[3].size().0,
|
||||
);
|
||||
let account = &context.accounts[&self.cursor_pos.0];
|
||||
|
||||
let threads = account.collection.get_threads(self.cursor_pos.1);
|
||||
|
||||
let selected_flag_len = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.selected_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_SELECTED_FLAG)
|
||||
.grapheme_width();
|
||||
let thread_snoozed_flag_len = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.thread_snoozed_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_SNOOZED_FLAG)
|
||||
.grapheme_width();
|
||||
let unseen_flag_len = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.unseen_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_UNSEEN_FLAG)
|
||||
.grapheme_width();
|
||||
let attachment_flag_len = mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
.listing
|
||||
.attachment_flag
|
||||
)
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(super::DEFAULT_ATTACHMENT_FLAG)
|
||||
.grapheme_width();
|
||||
|
||||
for (idx, ((thread_hash, root_env_hash), strings)) in self
|
||||
for (idx, ((_thread_hash, root_env_hash), strings)) in self
|
||||
.rows
|
||||
.entries
|
||||
.iter()
|
||||
|
@ -1306,14 +1155,7 @@ impl CompactListing {
|
|||
|
||||
panic!();
|
||||
}
|
||||
let thread = threads.thread_ref(*thread_hash);
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
idx % 2 == 0,
|
||||
thread.unseen() > 0,
|
||||
self.cursor_pos.2 == idx,
|
||||
self.rows.selection[root_env_hash]
|
||||
);
|
||||
let row_attr = self.rows.row_attr_cache[&idx];
|
||||
let (x, _) = write_string_to_grid(
|
||||
&idx.to_string(),
|
||||
&mut self.data_columns.columns[0],
|
||||
|
@ -1428,33 +1270,6 @@ impl CompactListing {
|
|||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
/* Set fg color for flags */
|
||||
let mut x = 0;
|
||||
if self
|
||||
.rows
|
||||
.selection
|
||||
.get(root_env_hash)
|
||||
.cloned()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
x += selected_flag_len;
|
||||
}
|
||||
if thread.snoozed() {
|
||||
for x in x..(x + thread_snoozed_flag_len) {
|
||||
self.data_columns.columns[3][(x, idx)]
|
||||
.set_fg(self.color_cache.thread_snooze_flag.fg);
|
||||
}
|
||||
x += thread_snoozed_flag_len;
|
||||
}
|
||||
if thread.unseen() > 0 {
|
||||
x += unseen_flag_len;
|
||||
}
|
||||
if thread.has_attachments() {
|
||||
for x in x..(x + attachment_flag_len) {
|
||||
self.data_columns.columns[3][(x, idx)]
|
||||
.set_fg(self.color_cache.attachment_flag.fg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1726,19 +1541,23 @@ impl Component for CompactListing {
|
|||
}
|
||||
|
||||
if !self.rows.row_updates.is_empty() {
|
||||
while let Some(row) = self.rows.row_updates.pop() {
|
||||
self.update_line(context, row);
|
||||
let row: usize = self.rows.env_order[&row];
|
||||
while let Some(env_hash) = self.rows.row_updates.pop() {
|
||||
self.update_line(context, env_hash);
|
||||
let row: usize = self.rows.env_order[&env_hash];
|
||||
let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
|
||||
|
||||
let top_idx = page_no * rows;
|
||||
if row >= top_idx && row < top_idx + rows {
|
||||
let area = (
|
||||
set_y(upper_left, get_y(upper_left) + (row % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (row % rows)),
|
||||
let new_area = nth_row_area(area, row % rows);
|
||||
self.data_columns.draw(
|
||||
grid,
|
||||
row,
|
||||
self.cursor_pos.2,
|
||||
grid.bounds_iter(new_area),
|
||||
);
|
||||
self.highlight_line(grid, area, row, context);
|
||||
context.dirty_areas.push_back(area);
|
||||
let row_attr = self.rows.row_attr_cache[&row];
|
||||
change_colors(grid, new_area, row_attr.fg, row_attr.bg);
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
}
|
||||
if self.force_draw {
|
||||
|
@ -1842,11 +1661,11 @@ impl Component for CompactListing {
|
|||
{
|
||||
if self.modifier_active && self.modifier_command.is_none() {
|
||||
self.modifier_command = Some(Modifier::default());
|
||||
} else {
|
||||
if let Some(thread_hash) = self.get_thread_under_cursor(self.cursor_pos.2) {
|
||||
self.rows
|
||||
.update_selection_with_thread(thread_hash, |e| *e = !*e);
|
||||
}
|
||||
} else if let Some(thread_hash) =
|
||||
self.get_thread_under_cursor(self.cursor_pos.2)
|
||||
{
|
||||
self.rows
|
||||
.update_selection_with_thread(thread_hash, |e| *e = !*e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1916,11 +1735,6 @@ impl Component for CompactListing {
|
|||
),
|
||||
even: crate::conf::value(context, "mail.listing.compact.even"),
|
||||
odd: crate::conf::value(context, "mail.listing.compact.odd"),
|
||||
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
|
||||
thread_snooze_flag: crate::conf::value(
|
||||
context,
|
||||
"mail.listing.thread_snooze_flag",
|
||||
),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
..self.color_cache
|
||||
|
|
|
@ -151,7 +151,7 @@ impl MailListingTrait for ConversationsListing {
|
|||
} else {
|
||||
if let Some(env_hashes) = self
|
||||
.get_thread_under_cursor(self.cursor_pos.2)
|
||||
.and_then(|thread| self.rows.thread_to_env.get(&thread).map(|v| v.clone()))
|
||||
.and_then(|thread| self.rows.thread_to_env.get(&thread).cloned())
|
||||
{
|
||||
cursor_iter = Some(env_hashes.into_iter());
|
||||
} else {
|
||||
|
@ -187,8 +187,6 @@ impl MailListingTrait for ConversationsListing {
|
|||
selected: crate::conf::value(context, "mail.listing.conversations.selected"),
|
||||
unseen: crate::conf::value(context, "mail.listing.conversations.unseen"),
|
||||
highlighted: crate::conf::value(context, "mail.listing.conversations.highlighted"),
|
||||
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
|
||||
thread_snooze_flag: crate::conf::value(context, "mail.listing.thread_snooze_flag"),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
..self.color_cache
|
||||
};
|
||||
|
@ -1306,10 +1304,8 @@ impl Component for ConversationsListing {
|
|||
{
|
||||
if self.modifier_active && self.modifier_command.is_none() {
|
||||
self.modifier_command = Some(Modifier::default());
|
||||
} else {
|
||||
if let Some(thread) = self.get_thread_under_cursor(self.cursor_pos.2) {
|
||||
self.rows.update_selection_with_thread(thread, |e| *e = !*e);
|
||||
}
|
||||
} else if let Some(thread) = self.get_thread_under_cursor(self.cursor_pos.2) {
|
||||
self.rows.update_selection_with_thread(thread, |e| *e = !*e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1440,11 +1436,6 @@ impl Component for ConversationsListing {
|
|||
context,
|
||||
"mail.listing.conversations.highlighted",
|
||||
),
|
||||
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
|
||||
thread_snooze_flag: crate::conf::value(
|
||||
context,
|
||||
"mail.listing.thread_snooze_flag",
|
||||
),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
..self.color_cache
|
||||
};
|
||||
|
|
|
@ -130,7 +130,7 @@ pub struct PlainListing {
|
|||
subsort: (SortField, SortOrder),
|
||||
rows: RowsState<(ThreadHash, EnvelopeHash)>,
|
||||
/// Cache current view.
|
||||
data_columns: DataColumns,
|
||||
data_columns: DataColumns<4>,
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
||||
|
@ -167,9 +167,9 @@ impl MailListingTrait for PlainListing {
|
|||
.values()
|
||||
.cloned()
|
||||
.any(std::convert::identity);
|
||||
dbg!(is_selection_empty);
|
||||
if is_selection_empty {
|
||||
return dbg!(self.get_env_under_cursor(self.cursor_pos.2))
|
||||
return self
|
||||
.get_env_under_cursor(self.cursor_pos.2)
|
||||
.into_iter()
|
||||
.collect::<_>();
|
||||
}
|
||||
|
@ -205,8 +205,6 @@ impl MailListingTrait for PlainListing {
|
|||
odd_highlighted: crate::conf::value(context, "mail.listing.plain.odd_highlighted"),
|
||||
even_selected: crate::conf::value(context, "mail.listing.plain.even_selected"),
|
||||
odd_selected: crate::conf::value(context, "mail.listing.plain.odd_selected"),
|
||||
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
|
||||
thread_snooze_flag: crate::conf::value(context, "mail.listing.thread_snooze_flag"),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
..self.color_cache
|
||||
|
@ -448,15 +446,19 @@ impl ListingTrait for PlainListing {
|
|||
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;
|
||||
for idx in &[old_cursor_pos.2, self.new_cursor_pos.2] {
|
||||
if *idx >= self.length {
|
||||
for &(idx, highlight) in &[(old_cursor_pos.2, false), (self.new_cursor_pos.2, true)] {
|
||||
if idx >= self.length {
|
||||
continue; //bounds check
|
||||
}
|
||||
let new_area = (
|
||||
set_y(upper_left, get_y(upper_left) + (*idx % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (*idx % rows)),
|
||||
);
|
||||
self.highlight_line(grid, new_area, *idx, context);
|
||||
let new_area = nth_row_area(area, idx % rows);
|
||||
self.data_columns
|
||||
.draw(grid, idx, self.cursor_pos.2, grid.bounds_iter(new_area));
|
||||
if highlight {
|
||||
let row_attr = row_attr!(self.color_cache, idx % 2 == 0, false, true, false);
|
||||
change_colors(grid, new_area, row_attr.fg, row_attr.bg);
|
||||
} else if let Some(row_attr) = self.rows.row_attr_cache.get(&idx) {
|
||||
change_colors(grid, new_area, row_attr.fg, row_attr.bg);
|
||||
}
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
return;
|
||||
|
@ -468,107 +470,37 @@ impl ListingTrait for PlainListing {
|
|||
self.cursor_pos.2 = self.new_cursor_pos.2;
|
||||
}
|
||||
|
||||
let width = width!(area);
|
||||
self.data_columns.widths = Default::default();
|
||||
self.data_columns.widths[0] = self.data_columns.columns[0].size().0;
|
||||
self.data_columns.widths[1] = self.data_columns.columns[1].size().0; /* date*/
|
||||
self.data_columns.widths[2] = self.data_columns.columns[2].size().0; /* from */
|
||||
self.data_columns.widths[3] = self.data_columns.columns[3].size().0; /* subject */
|
||||
|
||||
let min_col_width = std::cmp::min(
|
||||
15,
|
||||
std::cmp::min(self.data_columns.widths[3], self.data_columns.widths[2]),
|
||||
);
|
||||
if self.data_columns.widths[0] + self.data_columns.widths[1] + 2 * min_col_width + 4 > width
|
||||
{
|
||||
let remainder = width
|
||||
.saturating_sub(self.data_columns.widths[0])
|
||||
.saturating_sub(self.data_columns.widths[1])
|
||||
.saturating_sub(2 * 2);
|
||||
self.data_columns.widths[2] = remainder / 6;
|
||||
} else {
|
||||
let remainder = width
|
||||
.saturating_sub(self.data_columns.widths[0])
|
||||
.saturating_sub(self.data_columns.widths[1])
|
||||
.saturating_sub(3 * 2);
|
||||
if min_col_width + self.data_columns.widths[3] > remainder {
|
||||
self.data_columns.widths[2] = min_col_width;
|
||||
}
|
||||
}
|
||||
clear_area(grid, area, self.color_cache.theme_default);
|
||||
/* Page_no has changed, so draw new page */
|
||||
let mut x = get_x(upper_left);
|
||||
let mut flag_x = 0;
|
||||
for i in 0..4 {
|
||||
let column_width = self.data_columns.widths[i];
|
||||
if i == 3 {
|
||||
flag_x = x;
|
||||
_ = self
|
||||
.data_columns
|
||||
.recalc_widths((width!(area), height!(area)), top_idx);
|
||||
clear_area(grid, area, self.color_cache.theme_default);
|
||||
/* copy table columns */
|
||||
self.data_columns
|
||||
.draw(grid, top_idx, self.cursor_pos.2, grid.bounds_iter(area));
|
||||
/* apply each row colors separately */
|
||||
for i in top_idx..(top_idx + height!(area)) {
|
||||
if let Some(row_attr) = self.rows.row_attr_cache.get(&i) {
|
||||
change_colors(grid, nth_row_area(area, i % rows), row_attr.fg, row_attr.bg);
|
||||
}
|
||||
if column_width == 0 {
|
||||
continue;
|
||||
}
|
||||
copy_area(
|
||||
grid,
|
||||
&self.data_columns.columns[i],
|
||||
(set_x(upper_left, x), bottom_right),
|
||||
(
|
||||
(0, top_idx),
|
||||
(column_width.saturating_sub(1), self.length - 1),
|
||||
),
|
||||
);
|
||||
x += column_width + 2; // + SEPARATOR
|
||||
if x > get_x(bottom_right) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for r in 0..cmp::min(self.length - top_idx, rows) {
|
||||
let (fg_color, bg_color) = {
|
||||
let c = &self.data_columns.columns[0][(0, r + top_idx)];
|
||||
(c.fg(), c.bg())
|
||||
};
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
pos_inc(upper_left, (0, r)),
|
||||
(flag_x.saturating_sub(1), get_y(upper_left) + r),
|
||||
),
|
||||
fg_color,
|
||||
bg_color,
|
||||
);
|
||||
for c in grid.row_iter(
|
||||
flag_x
|
||||
..std::cmp::min(
|
||||
get_x(bottom_right),
|
||||
flag_x + 2 + self.data_columns.widths[3],
|
||||
),
|
||||
get_y(upper_left) + r,
|
||||
) {
|
||||
grid[c].set_bg(bg_color);
|
||||
}
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
(
|
||||
flag_x + 2 + self.data_columns.widths[3],
|
||||
get_y(upper_left) + r,
|
||||
),
|
||||
(get_x(bottom_right), get_y(upper_left) + r),
|
||||
),
|
||||
fg_color,
|
||||
bg_color,
|
||||
);
|
||||
}
|
||||
|
||||
self.highlight_line(
|
||||
/* highlight cursor */
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
self.cursor_pos.2 % 2 == 0,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
);
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
set_y(upper_left, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
),
|
||||
self.cursor_pos.2,
|
||||
context,
|
||||
nth_row_area(area, self.cursor_pos.2 % rows),
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
);
|
||||
|
||||
/* clear gap if available height is more than count of entries */
|
||||
if top_idx + rows > self.length {
|
||||
clear_area(
|
||||
grid,
|
||||
|
@ -845,6 +777,14 @@ impl PlainListing {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
self.length % 2 == 0,
|
||||
!envelope.is_seen(),
|
||||
false,
|
||||
false
|
||||
);
|
||||
self.rows.row_attr_cache.insert(self.length, row_attr);
|
||||
|
||||
let entry_strings = self.make_entry_string(&envelope, context);
|
||||
min_width.1 = cmp::max(min_width.1, entry_strings.date.grapheme_width()); /* date */
|
||||
|
@ -868,6 +808,21 @@ impl PlainListing {
|
|||
|
||||
min_width.0 = self.length.saturating_sub(1).to_string().len();
|
||||
|
||||
self.data_columns.elasticities[0].set_rigid();
|
||||
self.data_columns.elasticities[1].set_rigid();
|
||||
self.data_columns.elasticities[2].set_grow(5, Some(35));
|
||||
self.data_columns.elasticities[3].set_rigid();
|
||||
self.data_columns
|
||||
.cursor_config
|
||||
.set_handle(true)
|
||||
.set_even_odd_theme(
|
||||
self.color_cache.even_highlighted,
|
||||
self.color_cache.odd_highlighted,
|
||||
);
|
||||
self.data_columns
|
||||
.theme_config
|
||||
.set_even_odd_theme(self.color_cache.even, self.color_cache.odd);
|
||||
|
||||
/* index column */
|
||||
self.data_columns.columns[0] =
|
||||
CellBuffer::new_with_context(min_width.0, self.rows.len(), None, context);
|
||||
|
@ -903,14 +858,7 @@ impl PlainListing {
|
|||
panic!();
|
||||
}
|
||||
|
||||
let envelope: EnvelopeRef = context.accounts[&self.cursor_pos.0].collection.get_env(i);
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
idx % 2 == 0,
|
||||
!envelope.is_seen(),
|
||||
false,
|
||||
false
|
||||
);
|
||||
let row_attr = self.rows.row_attr_cache[&idx];
|
||||
|
||||
let (x, _) = write_string_to_grid(
|
||||
&idx.to_string(),
|
||||
|
@ -998,17 +946,6 @@ impl PlainListing {
|
|||
for c in columns[3].row_iter(x..min_width.3, idx) {
|
||||
columns[3][c].set_bg(row_attr.bg).set_attrs(row_attr.attrs);
|
||||
}
|
||||
/* Set fg color for flags */
|
||||
let mut x = 0;
|
||||
if self.rows.selection.get(&i).cloned().unwrap_or(false) {
|
||||
x += 1;
|
||||
}
|
||||
if !envelope.is_seen() {
|
||||
x += 1;
|
||||
}
|
||||
if envelope.has_attachments() {
|
||||
columns[3][(x, idx)].set_fg(self.color_cache.attachment_flag.fg);
|
||||
}
|
||||
}
|
||||
if self.length == 0 && self.filter_term.is_empty() {
|
||||
let message: String = account[&self.cursor_pos.1].status();
|
||||
|
@ -1091,19 +1028,34 @@ impl Component for PlainListing {
|
|||
|
||||
if !self.rows.row_updates.is_empty() {
|
||||
let (upper_left, bottom_right) = area;
|
||||
while let Some(row) = self.rows.row_updates.pop() {
|
||||
let row: usize = self.rows.env_order[&row];
|
||||
while let Some(env_hash) = self.rows.row_updates.pop() {
|
||||
let row: usize = self.rows.env_order[&env_hash];
|
||||
let rows = get_y(bottom_right) - get_y(upper_left) + 1;
|
||||
let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
|
||||
|
||||
let top_idx = page_no * rows;
|
||||
if row >= top_idx && row <= top_idx + rows {
|
||||
let area = (
|
||||
set_y(upper_left, get_y(upper_left) + (row % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (row % rows)),
|
||||
let new_area = nth_row_area(area, row % rows);
|
||||
self.data_columns.draw(
|
||||
grid,
|
||||
row,
|
||||
self.cursor_pos.2,
|
||||
grid.bounds_iter(new_area),
|
||||
);
|
||||
self.highlight_line(grid, area, row, context);
|
||||
context.dirty_areas.push_back(area);
|
||||
let envelope: EnvelopeRef = context.accounts[&self.cursor_pos.0]
|
||||
.collection
|
||||
.get_env(env_hash);
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
row % 2 == 0,
|
||||
!envelope.is_seen(),
|
||||
false,
|
||||
self.rows.selection[&env_hash]
|
||||
);
|
||||
self.rows.row_attr_cache.insert(row, row_attr);
|
||||
|
||||
change_colors(grid, new_area, row_attr.fg, row_attr.bg);
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
}
|
||||
if self.force_draw {
|
||||
|
@ -1196,10 +1148,8 @@ impl Component for PlainListing {
|
|||
{
|
||||
if self.modifier_active && self.modifier_command.is_none() {
|
||||
self.modifier_command = Some(Modifier::default());
|
||||
} else {
|
||||
if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) {
|
||||
self.rows.update_selection_with_env(env_hash, |e| *e = !*e);
|
||||
}
|
||||
} else if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) {
|
||||
self.rows.update_selection_with_env(env_hash, |e| *e = !*e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1243,11 +1193,6 @@ impl Component for PlainListing {
|
|||
),
|
||||
even_selected: crate::conf::value(context, "mail.listing.plain.even_selected"),
|
||||
odd_selected: crate::conf::value(context, "mail.listing.plain.odd_selected"),
|
||||
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
|
||||
thread_snooze_flag: crate::conf::value(
|
||||
context,
|
||||
"mail.listing.thread_snooze_flag",
|
||||
),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
..self.color_cache
|
||||
|
@ -1332,9 +1277,7 @@ impl Component for PlainListing {
|
|||
.cloned()
|
||||
.any(std::convert::identity) =>
|
||||
{
|
||||
for v in self.rows.selection.values_mut() {
|
||||
*v = false;
|
||||
}
|
||||
self.rows.clear_selection();
|
||||
self.dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -121,9 +121,9 @@ pub struct ThreadListing {
|
|||
crate::jobs::JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>,
|
||||
)>,
|
||||
|
||||
data_columns: DataColumns,
|
||||
data_columns: DataColumns<5>,
|
||||
rows_drawn: SegmentTree,
|
||||
rows: RowsState<(bool, bool, ThreadHash, EnvelopeHash)>,
|
||||
rows: RowsState<(ThreadHash, EnvelopeHash)>,
|
||||
/// If we must redraw on next redraw event
|
||||
dirty: bool,
|
||||
/// If `self.view` is focused or not.
|
||||
|
@ -189,8 +189,6 @@ impl MailListingTrait for ThreadListing {
|
|||
odd_highlighted: crate::conf::value(context, "mail.listing.plain.odd_highlighted"),
|
||||
even: crate::conf::value(context, "mail.listing.plain.even"),
|
||||
odd: crate::conf::value(context, "mail.listing.plain.odd"),
|
||||
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
|
||||
thread_snooze_flag: crate::conf::value(context, "mail.listing.thread_snooze_flag"),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
..self.color_cache
|
||||
|
@ -358,15 +356,18 @@ impl MailListingTrait for ThreadListing {
|
|||
); /* tags + subject */
|
||||
self.rows.insert_thread(
|
||||
threads.envelope_to_thread[&env_hash],
|
||||
(
|
||||
envelope.is_seen(),
|
||||
envelope.has_attachments(),
|
||||
threads.envelope_to_thread[&env_hash],
|
||||
env_hash,
|
||||
),
|
||||
(threads.envelope_to_thread[&env_hash], env_hash),
|
||||
smallvec::smallvec![env_hash],
|
||||
entry_strings,
|
||||
);
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
idx % 2 == 0,
|
||||
!envelope.is_seen(),
|
||||
false,
|
||||
false,
|
||||
);
|
||||
self.rows.row_attr_cache.insert(idx, row_attr);
|
||||
idx += 1;
|
||||
} else {
|
||||
continue;
|
||||
|
@ -389,6 +390,23 @@ impl MailListingTrait for ThreadListing {
|
|||
}
|
||||
}
|
||||
min_width.0 = idx.saturating_sub(1).to_string().len();
|
||||
|
||||
self.data_columns.elasticities[0].set_rigid();
|
||||
self.data_columns.elasticities[1].set_rigid();
|
||||
self.data_columns.elasticities[2].set_grow(5, Some(35));
|
||||
self.data_columns.elasticities[3].set_rigid();
|
||||
self.data_columns.elasticities[4].set_rigid();
|
||||
self.data_columns
|
||||
.cursor_config
|
||||
.set_handle(true)
|
||||
.set_even_odd_theme(
|
||||
self.color_cache.even_highlighted,
|
||||
self.color_cache.odd_highlighted,
|
||||
);
|
||||
self.data_columns
|
||||
.theme_config
|
||||
.set_even_odd_theme(self.color_cache.even, self.color_cache.odd);
|
||||
|
||||
/* index column */
|
||||
self.data_columns.columns[0] =
|
||||
CellBuffer::new_with_context(min_width.0, self.rows.len(), None, context);
|
||||
|
@ -496,41 +514,24 @@ impl ListingTrait for ThreadListing {
|
|||
cmp::min(self.length.saturating_sub(1), top_idx + rows - 1),
|
||||
);
|
||||
|
||||
/*
|
||||
if !self.initialised {
|
||||
self.initialised = false;
|
||||
copy_area(
|
||||
grid,
|
||||
&self.content,
|
||||
area,
|
||||
((0, top_idx), (MAX_COLS - 1, self.length)),
|
||||
);
|
||||
self.highlight_line(
|
||||
grid,
|
||||
(
|
||||
set_y(upper_left, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
),
|
||||
self.cursor_pos.2,
|
||||
context,
|
||||
);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
*/
|
||||
/* If cursor position has changed, remove the highlight from the previous position and
|
||||
* apply it in the new one. */
|
||||
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;
|
||||
for idx in &[old_cursor_pos.2, self.new_cursor_pos.2] {
|
||||
if *idx >= self.length {
|
||||
for &(idx, highlight) in &[(old_cursor_pos.2, false), (self.new_cursor_pos.2, true)] {
|
||||
if idx >= self.length {
|
||||
continue; //bounds check
|
||||
}
|
||||
let new_area = (
|
||||
set_y(upper_left, get_y(upper_left) + (*idx % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (*idx % rows)),
|
||||
);
|
||||
self.highlight_line(grid, new_area, *idx, context);
|
||||
let new_area = nth_row_area(area, idx % rows);
|
||||
self.data_columns
|
||||
.draw(grid, idx, self.cursor_pos.2, grid.bounds_iter(new_area));
|
||||
if highlight {
|
||||
let row_attr = row_attr!(self.color_cache, idx % 2 == 0, false, true, false);
|
||||
change_colors(grid, new_area, row_attr.fg, row_attr.bg);
|
||||
} else if let Some(row_attr) = self.rows.row_attr_cache.get(&idx) {
|
||||
change_colors(grid, new_area, row_attr.fg, row_attr.bg);
|
||||
}
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
return;
|
||||
|
@ -544,148 +545,45 @@ impl ListingTrait for ThreadListing {
|
|||
self.cursor_pos.2 = self.new_cursor_pos.2;
|
||||
}
|
||||
|
||||
let width = width!(area);
|
||||
self.data_columns.widths = Default::default();
|
||||
self.data_columns.widths[0] = self.data_columns.columns[0].size().0;
|
||||
self.data_columns.widths[1] = self.data_columns.columns[1].size().0; /* date*/
|
||||
self.data_columns.widths[2] = self.data_columns.columns[2].size().0; /* from */
|
||||
self.data_columns.widths[3] = self.data_columns.columns[3].size().0; /* flags */
|
||||
self.data_columns.widths[4] = self.data_columns.columns[4].size().0; /* subject */
|
||||
|
||||
let min_col_width = std::cmp::min(
|
||||
15,
|
||||
std::cmp::min(self.data_columns.widths[4], self.data_columns.widths[2]),
|
||||
);
|
||||
if self.data_columns.widths[0] + self.data_columns.widths[1] + 3 * min_col_width + 8 > width
|
||||
{
|
||||
let remainder = width
|
||||
.saturating_sub(self.data_columns.widths[0])
|
||||
.saturating_sub(self.data_columns.widths[1])
|
||||
.saturating_sub(4);
|
||||
self.data_columns.widths[2] = remainder / 6;
|
||||
self.data_columns.widths[4] =
|
||||
((2 * remainder) / 3).saturating_sub(self.data_columns.widths[3]);
|
||||
} else {
|
||||
let remainder = width
|
||||
.saturating_sub(self.data_columns.widths[0])
|
||||
.saturating_sub(self.data_columns.widths[1])
|
||||
.saturating_sub(8);
|
||||
if min_col_width + self.data_columns.widths[4] > remainder {
|
||||
self.data_columns.widths[4] =
|
||||
remainder.saturating_sub(min_col_width + self.data_columns.widths[3]);
|
||||
self.data_columns.widths[2] = min_col_width;
|
||||
}
|
||||
}
|
||||
for &i in &[2, 4] {
|
||||
/* Set From and Subject column widths to their maximum value width in the range
|
||||
* [top_idx, top_idx + rows]. By using a segment tree the query is O(logn), which is
|
||||
* great!
|
||||
*/
|
||||
self.data_columns.widths[i] =
|
||||
self.data_columns.segment_tree[i].get_max(top_idx, top_idx + rows) as usize;
|
||||
}
|
||||
if self.data_columns.widths.iter().sum::<usize>() > width {
|
||||
let diff = self.data_columns.widths.iter().sum::<usize>() - width;
|
||||
if self.data_columns.widths[2] > 2 * diff {
|
||||
self.data_columns.widths[2] -= diff;
|
||||
} else {
|
||||
self.data_columns.widths[2] = std::cmp::max(
|
||||
15,
|
||||
self.data_columns.widths[2].saturating_sub((2 * diff) / 3),
|
||||
);
|
||||
self.data_columns.widths[4] = std::cmp::max(
|
||||
15,
|
||||
self.data_columns.widths[4].saturating_sub(diff / 3 + diff % 3),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ = self
|
||||
.data_columns
|
||||
.recalc_widths((width!(area), height!(area)), top_idx);
|
||||
clear_area(grid, area, self.color_cache.theme_default);
|
||||
/* Page_no has changed, so draw new page */
|
||||
let mut x = get_x(upper_left);
|
||||
let mut flag_x = 0;
|
||||
for i in 0..self.data_columns.columns.len() {
|
||||
let column_width = self.data_columns.columns[i].size().0;
|
||||
if i == 3 {
|
||||
flag_x = x;
|
||||
}
|
||||
if self.data_columns.widths[i] == 0 {
|
||||
continue;
|
||||
}
|
||||
copy_area(
|
||||
grid,
|
||||
&self.data_columns.columns[i],
|
||||
(
|
||||
set_x(upper_left, x),
|
||||
set_x(
|
||||
bottom_right,
|
||||
std::cmp::min(get_x(bottom_right), x + (self.data_columns.widths[i])),
|
||||
),
|
||||
),
|
||||
(
|
||||
(0, top_idx),
|
||||
(column_width.saturating_sub(1), self.length - 1),
|
||||
),
|
||||
);
|
||||
x += self.data_columns.widths[i] + 2; // + SEPARATOR
|
||||
if x > get_x(bottom_right) {
|
||||
break;
|
||||
self.data_columns
|
||||
.draw(grid, top_idx, self.cursor_pos.2, grid.bounds_iter(area));
|
||||
|
||||
/* Page_no has changed, so draw new page */
|
||||
_ = self
|
||||
.data_columns
|
||||
.recalc_widths((width!(area), height!(area)), top_idx);
|
||||
clear_area(grid, area, self.color_cache.theme_default);
|
||||
/* copy table columns */
|
||||
self.data_columns
|
||||
.draw(grid, top_idx, self.cursor_pos.2, grid.bounds_iter(area));
|
||||
/* apply each row colors separately */
|
||||
for i in top_idx..(top_idx + height!(area)) {
|
||||
if let Some(row_attr) = self.rows.row_attr_cache.get(&i) {
|
||||
change_colors(grid, nth_row_area(area, i % rows), row_attr.fg, row_attr.bg);
|
||||
}
|
||||
}
|
||||
|
||||
for r in 0..cmp::min(self.length - top_idx, rows) {
|
||||
let (fg_color, bg_color) = {
|
||||
let c = &self.data_columns.columns[0][(0, r + top_idx)];
|
||||
if let Some(env_hash) = self.get_env_under_cursor(r + top_idx) {
|
||||
if self.rows.selection[&env_hash] {
|
||||
(c.fg(), self.color_cache.selected.bg)
|
||||
} else {
|
||||
(c.fg(), c.bg())
|
||||
}
|
||||
} else {
|
||||
(c.fg(), c.bg())
|
||||
}
|
||||
};
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
pos_inc(upper_left, (0, r)),
|
||||
(flag_x.saturating_sub(1), get_y(upper_left) + r),
|
||||
),
|
||||
fg_color,
|
||||
bg_color,
|
||||
);
|
||||
for x in flag_x
|
||||
..std::cmp::min(
|
||||
get_x(bottom_right),
|
||||
flag_x + 2 + self.data_columns.widths[3],
|
||||
)
|
||||
{
|
||||
grid[(x, get_y(upper_left) + r)].set_bg(bg_color);
|
||||
}
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
(
|
||||
flag_x + 2 + self.data_columns.widths[3],
|
||||
get_y(upper_left) + r,
|
||||
),
|
||||
(get_x(bottom_right), get_y(upper_left) + r),
|
||||
),
|
||||
fg_color,
|
||||
bg_color,
|
||||
);
|
||||
}
|
||||
|
||||
self.highlight_line(
|
||||
/* highlight cursor */
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
self.cursor_pos.2 % 2 == 0,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
);
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
set_y(upper_left, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
),
|
||||
self.cursor_pos.2,
|
||||
context,
|
||||
nth_row_area(area, self.cursor_pos.2 % rows),
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
);
|
||||
|
||||
/* clear gap if available height is more than count of entries */
|
||||
if top_idx + rows > self.length {
|
||||
clear_area(
|
||||
grid,
|
||||
|
@ -697,24 +595,6 @@ impl ListingTrait for ThreadListing {
|
|||
);
|
||||
}
|
||||
context.dirty_areas.push_back(area);
|
||||
/*
|
||||
copy_area(
|
||||
grid,
|
||||
&self.content,
|
||||
area,
|
||||
((0, top_idx), (MAX_COLS - 1, self.length)),
|
||||
);
|
||||
self.highlight_line(
|
||||
grid,
|
||||
(
|
||||
set_y(upper_left, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (self.cursor_pos.2 % rows)),
|
||||
),
|
||||
self.cursor_pos.2,
|
||||
context,
|
||||
);
|
||||
context.dirty_areas.push_back(area);
|
||||
*/
|
||||
}
|
||||
|
||||
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
|
||||
|
@ -994,7 +874,7 @@ impl ThreadListing {
|
|||
self.data_columns.columns[4].size().0,
|
||||
);
|
||||
|
||||
for (idx, ((is_seen, has_attachments, _thread_hash, env_hash), strings)) in self
|
||||
for (idx, ((_thread_hash, env_hash), strings)) in self
|
||||
.rows
|
||||
.entries
|
||||
.iter()
|
||||
|
@ -1013,13 +893,7 @@ impl ThreadListing {
|
|||
|
||||
panic!();
|
||||
}
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
idx % 2 == 0,
|
||||
!*is_seen,
|
||||
self.cursor_pos.2 == idx,
|
||||
self.rows.selection[&env_hash],
|
||||
);
|
||||
let row_attr = self.rows.row_attr_cache[&idx];
|
||||
let (x, _) = write_string_to_grid(
|
||||
&idx.to_string(),
|
||||
&mut self.data_columns.columns[0],
|
||||
|
@ -1139,9 +1013,6 @@ impl ThreadListing {
|
|||
.set_bg(row_attr.bg)
|
||||
.set_attrs(row_attr.attrs);
|
||||
}
|
||||
if *has_attachments {
|
||||
self.data_columns.columns[3][(0, idx)].set_fg(self.color_cache.attachment_flag.fg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1315,15 +1186,29 @@ impl Component for ThreadListing {
|
|||
let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
|
||||
let top_idx = page_no * rows;
|
||||
|
||||
while let Some(row) = self.rows.row_updates.pop() {
|
||||
let row: usize = self.rows.env_order[&row];
|
||||
while let Some(env_hash) = self.rows.row_updates.pop() {
|
||||
let row: usize = self.rows.env_order[&env_hash];
|
||||
|
||||
if row >= top_idx && row <= top_idx + rows {
|
||||
let new_area = (
|
||||
set_y(upper_left, get_y(upper_left) + (row % rows)),
|
||||
set_y(bottom_right, get_y(upper_left) + (row % rows)),
|
||||
let new_area = nth_row_area(area, row % rows);
|
||||
self.data_columns.draw(
|
||||
grid,
|
||||
row,
|
||||
self.cursor_pos.2,
|
||||
grid.bounds_iter(new_area),
|
||||
);
|
||||
self.highlight_line(grid, new_area, row, context);
|
||||
let envelope: EnvelopeRef = context.accounts[&self.cursor_pos.0]
|
||||
.collection
|
||||
.get_env(env_hash);
|
||||
let row_attr = row_attr!(
|
||||
self.color_cache,
|
||||
row % 2 == 0,
|
||||
!envelope.is_seen(),
|
||||
false,
|
||||
self.rows.selection[&env_hash]
|
||||
);
|
||||
self.rows.row_attr_cache.insert(row, row_attr);
|
||||
change_colors(grid, new_area, row_attr.fg, row_attr.bg);
|
||||
context.dirty_areas.push_back(new_area);
|
||||
}
|
||||
}
|
||||
|
@ -1486,11 +1371,6 @@ impl Component for ThreadListing {
|
|||
),
|
||||
even: crate::conf::value(context, "mail.listing.plain.even"),
|
||||
odd: crate::conf::value(context, "mail.listing.plain.odd"),
|
||||
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
|
||||
thread_snooze_flag: crate::conf::value(
|
||||
context,
|
||||
"mail.listing.thread_snooze_flag",
|
||||
),
|
||||
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
|
||||
theme_default: crate::conf::value(context, "theme_default"),
|
||||
..self.color_cache
|
||||
|
@ -1559,7 +1439,7 @@ impl Component for ThreadListing {
|
|||
}
|
||||
self.rows.rename_env(*old_hash, *new_hash);
|
||||
if let Some(&row) = self.rows.env_order.get(new_hash) {
|
||||
(self.rows.entries[row].0).3 = *new_hash;
|
||||
(self.rows.entries[row].0).1 = *new_hash;
|
||||
}
|
||||
|
||||
self.dirty = true;
|
||||
|
@ -1623,10 +1503,8 @@ impl Component for ThreadListing {
|
|||
{
|
||||
if self.modifier_active && self.modifier_command.is_none() {
|
||||
self.modifier_command = Some(Modifier::default());
|
||||
} else {
|
||||
if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) {
|
||||
self.rows.update_selection_with_env(env_hash, |e| *e = !*e);
|
||||
}
|
||||
} else if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) {
|
||||
self.rows.update_selection_with_env(env_hash, |e| *e = !*e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -564,7 +564,10 @@ impl ThreadView {
|
|||
if rows < visibles.len() {
|
||||
ScrollBar::default().set_show_arrows(true).draw(
|
||||
grid,
|
||||
(pos_inc(upper_left!(area), (width!(area), 0)), bottom_right),
|
||||
(
|
||||
pos_inc(upper_left!(area), (width!(area).saturating_sub(1), 0)),
|
||||
bottom_right,
|
||||
),
|
||||
context,
|
||||
2 * self.cursor_pos,
|
||||
rows,
|
||||
|
@ -618,7 +621,10 @@ impl ThreadView {
|
|||
if rows < visibles.len() {
|
||||
ScrollBar::default().set_show_arrows(true).draw(
|
||||
grid,
|
||||
(pos_inc(upper_left!(area), (width!(area), 0)), bottom_right),
|
||||
(
|
||||
pos_inc(upper_left!(area), (width!(area).saturating_sub(1), 0)),
|
||||
bottom_right,
|
||||
),
|
||||
context,
|
||||
2 * self.cursor_pos,
|
||||
rows,
|
||||
|
|
|
@ -36,6 +36,9 @@ pub use self::layouts::*;
|
|||
mod dialogs;
|
||||
pub use self::dialogs::*;
|
||||
|
||||
mod tables;
|
||||
pub use self::tables::*;
|
||||
|
||||
use crate::jobs::JobId;
|
||||
use std::collections::HashSet;
|
||||
|
||||
|
@ -1048,7 +1051,10 @@ impl Component for Tabbed {
|
|||
ScrollBar::default().set_show_arrows(true).draw(
|
||||
grid,
|
||||
(
|
||||
pos_inc(upper_left!(inner_area), (width!(inner_area), 0)),
|
||||
pos_inc(
|
||||
upper_left!(inner_area),
|
||||
(width!(inner_area).saturating_sub(1), 0),
|
||||
),
|
||||
bottom_right!(inner_area),
|
||||
),
|
||||
context,
|
||||
|
@ -1300,7 +1306,10 @@ impl Component for Tabbed {
|
|||
ScrollBar::default().set_show_arrows(true).draw(
|
||||
grid,
|
||||
(
|
||||
pos_inc(upper_left!(inner_area), (width!(inner_area), 0)),
|
||||
pos_inc(
|
||||
upper_left!(inner_area),
|
||||
(width!(inner_area).saturating_sub(1), 0),
|
||||
),
|
||||
bottom_right!(inner_area),
|
||||
),
|
||||
context,
|
||||
|
|
|
@ -340,7 +340,7 @@ impl Pager {
|
|||
.text_lines
|
||||
.iter()
|
||||
.skip(self.cursor.1)
|
||||
.take(height!(area) + 1)
|
||||
.take(height!(area))
|
||||
{
|
||||
write_string_to_grid(
|
||||
l,
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2022 Manos Pitsidianakis
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* meli is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! UI components to display tabular lists. */
|
||||
use super::*;
|
||||
use crate::segment_tree::SegmentTree;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub enum ColumnElasticity {
|
||||
#[default]
|
||||
Rigid,
|
||||
Grow {
|
||||
min: usize,
|
||||
max: Option<usize>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ColumnElasticity {
|
||||
pub fn set_rigid(&mut self) {
|
||||
*self = Self::Rigid;
|
||||
}
|
||||
pub fn set_grow(&mut self, min: usize, max: Option<usize>) {
|
||||
*self = Self::Grow { min, max };
|
||||
}
|
||||
}
|
||||
|
||||
/*#[derive(Debug, Default, Clone)]
|
||||
pub enum TableRowFormat {
|
||||
#[default]
|
||||
None,
|
||||
Fill(FormatTag),
|
||||
Range {
|
||||
start: usize,
|
||||
end: usize,
|
||||
val: FormatTag,
|
||||
},
|
||||
}
|
||||
|
||||
impl TableRowFormat {
|
||||
pub const SELECTED: u8 = 0;
|
||||
pub const UNREAD: u8 = 1;
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct TableThemeConfig {
|
||||
pub theme: TableTheme,
|
||||
//pub row_formats: HashMap<usize, SmallVec<[(u8, TableRowFormat); 6]>>,
|
||||
}
|
||||
|
||||
impl TableThemeConfig {
|
||||
pub fn set_single_theme(&mut self, value: ThemeAttribute) -> &mut Self {
|
||||
self.theme = TableTheme::Single(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_even_odd_theme(&mut self, even: ThemeAttribute, odd: ThemeAttribute) -> &mut Self {
|
||||
self.theme = TableTheme::EvenOdd { even, odd };
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TableTheme {
|
||||
Single(ThemeAttribute),
|
||||
EvenOdd {
|
||||
even: ThemeAttribute,
|
||||
odd: ThemeAttribute,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for TableTheme {
|
||||
fn default() -> Self {
|
||||
Self::Single(ThemeAttribute::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct TableCursorConfig {
|
||||
pub handle: bool,
|
||||
pub theme: TableTheme,
|
||||
}
|
||||
|
||||
impl TableCursorConfig {
|
||||
pub fn set_handle(&mut self, value: bool) -> &mut Self {
|
||||
self.handle = value;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_single_theme(&mut self, value: ThemeAttribute) -> &mut Self {
|
||||
self.theme = TableTheme::Single(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_even_odd_theme(&mut self, even: ThemeAttribute, odd: ThemeAttribute) -> &mut Self {
|
||||
self.theme = TableTheme::EvenOdd { even, odd };
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DataColumns<const N: usize> {
|
||||
pub cursor_config: TableCursorConfig,
|
||||
pub theme_config: TableThemeConfig,
|
||||
pub columns: Box<[CellBuffer; N]>,
|
||||
/// widths of columns calculated in first draw and after size changes
|
||||
pub widths: [usize; N],
|
||||
pub elasticities: [ColumnElasticity; N],
|
||||
pub x_offset: usize,
|
||||
pub width_accum: usize,
|
||||
pub segment_tree: Box<[SegmentTree; N]>,
|
||||
}
|
||||
|
||||
// Workaround because Default derive doesn't work for const generic array lengths yet.
|
||||
impl<const N: usize> Default for DataColumns<N> {
|
||||
fn default() -> Self {
|
||||
fn init_array<T, const N: usize>(cl: impl Fn() -> T) -> [T; N] {
|
||||
// https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element
|
||||
let mut data: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
for elem in &mut data[..] {
|
||||
elem.write(cl());
|
||||
}
|
||||
let ptr = &data as *const [MaybeUninit<T>; N];
|
||||
std::mem::forget(data);
|
||||
unsafe { (ptr as *const [T; N]).read() }
|
||||
}
|
||||
Self {
|
||||
cursor_config: TableCursorConfig::default(),
|
||||
theme_config: TableThemeConfig::default(),
|
||||
columns: Box::new(init_array(CellBuffer::default)),
|
||||
widths: [0_usize; N],
|
||||
elasticities: [ColumnElasticity::default(); N],
|
||||
x_offset: 0,
|
||||
width_accum: 0,
|
||||
segment_tree: Box::new(init_array(SegmentTree::default)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DataColumns<N> {
|
||||
pub fn recalc_widths(
|
||||
&mut self,
|
||||
(screen_width, screen_height): (usize, usize),
|
||||
top_idx: usize,
|
||||
) -> usize {
|
||||
let mut width_accum = 0;
|
||||
let mut growees = 0;
|
||||
let mut growees_max = 0;
|
||||
let grow_minmax = None;
|
||||
for i in 0..N {
|
||||
if screen_height == 0 {
|
||||
self.widths[i] = 0;
|
||||
continue;
|
||||
}
|
||||
self.widths[i] =
|
||||
self.segment_tree[i].get_max(top_idx, top_idx + screen_height - 1) as usize;
|
||||
if self.widths[i] == 0 {
|
||||
self.widths[i] = self.columns[i].cols;
|
||||
}
|
||||
match self.elasticities[i] {
|
||||
ColumnElasticity::Rigid => {}
|
||||
ColumnElasticity::Grow {
|
||||
min,
|
||||
max: Some(max),
|
||||
} => {
|
||||
self.widths[i] = std::cmp::max(min, std::cmp::min(max, self.widths[i]));
|
||||
growees += 1;
|
||||
}
|
||||
ColumnElasticity::Grow { min, max: None } => {
|
||||
self.widths[i] = std::cmp::max(min, self.widths[i]);
|
||||
growees += 1;
|
||||
growees_max += 1;
|
||||
}
|
||||
}
|
||||
width_accum += self.widths[i];
|
||||
}
|
||||
// add column gaps
|
||||
width_accum += 2 * N.saturating_sub(1);
|
||||
debug_assert!(growees >= growees_max);
|
||||
debug_assert!(grow_minmax.is_none() || growees_max > 0);
|
||||
if width_accum >= screen_width || screen_height == 0 || screen_width == 0 || growees == 0 {
|
||||
self.width_accum = width_accum;
|
||||
return width_accum;
|
||||
}
|
||||
let distribute = screen_width - width_accum;
|
||||
let maxmins = growees_max * grow_minmax.unwrap_or(0);
|
||||
let part = if maxmins != 0 && growees_max < growees {
|
||||
distribute.saturating_sub(maxmins) / (growees - growees_max)
|
||||
} else if maxmins != 0 {
|
||||
distribute.saturating_sub(maxmins) / growees_max + grow_minmax.unwrap_or(0)
|
||||
} else {
|
||||
distribute / growees
|
||||
};
|
||||
|
||||
for i in 0..N {
|
||||
match self.elasticities[i] {
|
||||
ColumnElasticity::Rigid => {}
|
||||
ColumnElasticity::Grow {
|
||||
min: _,
|
||||
max: Some(_),
|
||||
} => {}
|
||||
ColumnElasticity::Grow { min: _, max: None } => {
|
||||
self.widths[i] += part;
|
||||
width_accum += part;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.width_accum = width_accum;
|
||||
width_accum
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&self,
|
||||
grid: &mut CellBuffer,
|
||||
top_idx: usize,
|
||||
cursor_pos: usize,
|
||||
mut bounds: BoundsIterator,
|
||||
) {
|
||||
let mut _relative_x_offset = 0;
|
||||
let mut skip_cols = (0, 0);
|
||||
let mut start_col = 0;
|
||||
let total_area = bounds.area();
|
||||
let (width, height) = (width!(total_area), height!(total_area));
|
||||
while _relative_x_offset < self.x_offset && start_col < N {
|
||||
_relative_x_offset += self.widths[start_col] + 2;
|
||||
if self.x_offset <= _relative_x_offset {
|
||||
skip_cols.0 = start_col;
|
||||
skip_cols.1 = _relative_x_offset - self.x_offset;
|
||||
_relative_x_offset = self.x_offset;
|
||||
break;
|
||||
}
|
||||
start_col += 1;
|
||||
}
|
||||
|
||||
for col in skip_cols.0..N {
|
||||
if bounds.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut column_width = self.widths[col];
|
||||
if column_width > bounds.width {
|
||||
column_width = bounds.width;
|
||||
} else if column_width == 0 {
|
||||
skip_cols.1 = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut column_area = bounds.add_x(column_width + 2);
|
||||
copy_area(
|
||||
grid,
|
||||
&self.columns[col],
|
||||
column_area.area(),
|
||||
(
|
||||
(skip_cols.1, top_idx),
|
||||
(
|
||||
column_width.saturating_sub(1),
|
||||
self.columns[col].rows.saturating_sub(1),
|
||||
),
|
||||
),
|
||||
);
|
||||
let gap_area = column_area.add_x(column_width);
|
||||
match self.theme_config.theme {
|
||||
TableTheme::Single(row_attr) => {
|
||||
change_colors(grid, gap_area.area(), row_attr.fg, row_attr.bg);
|
||||
}
|
||||
TableTheme::EvenOdd { even, odd } => {
|
||||
change_colors(grid, gap_area.area(), even.fg, even.bg);
|
||||
let mut top_idx = top_idx;
|
||||
for row in gap_area {
|
||||
if top_idx % 2 != 0 {
|
||||
change_colors(grid, row.area(), odd.fg, odd.bg);
|
||||
}
|
||||
top_idx += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
skip_cols.1 = 0;
|
||||
}
|
||||
if self.cursor_config.handle && (top_idx..(top_idx + height)).contains(&cursor_pos) {
|
||||
let offset = cursor_pos - top_idx;
|
||||
let row_attr = match self.cursor_config.theme {
|
||||
TableTheme::Single(attr) => attr,
|
||||
TableTheme::EvenOdd { even, odd: _ } if cursor_pos % 2 == 0 => even,
|
||||
TableTheme::EvenOdd { even: _, odd } => odd,
|
||||
};
|
||||
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
pos_inc(upper_left!(total_area), (0, offset)),
|
||||
pos_inc(upper_left!(total_area), (width, offset)),
|
||||
),
|
||||
row_attr.fg,
|
||||
row_attr.bg,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
use super::{position::*, Color};
|
||||
use crate::state::Context;
|
||||
use crate::ThemeAttribute;
|
||||
use melib::text_processing::wcwidth;
|
||||
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
@ -57,9 +58,9 @@ pub struct ScrollRegion {
|
|||
/// index, `Cellbuffer[y][x]`, corresponds to a column within a row and thus the x-axis.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct CellBuffer {
|
||||
cols: usize,
|
||||
rows: usize,
|
||||
buf: Vec<Cell>,
|
||||
pub cols: usize,
|
||||
pub rows: usize,
|
||||
pub buf: Vec<Cell>,
|
||||
pub default_cell: Cell,
|
||||
/// ASCII-only flag.
|
||||
pub ascii_drawing: bool,
|
||||
|
@ -357,6 +358,8 @@ impl CellBuffer {
|
|||
/// See `BoundsIterator` documentation.
|
||||
pub fn bounds_iter(&self, area: Area) -> BoundsIterator {
|
||||
BoundsIterator {
|
||||
width: width!(area),
|
||||
height: height!(area),
|
||||
rows: std::cmp::min(self.rows.saturating_sub(1), get_y(upper_left!(area)))
|
||||
..(std::cmp::min(self.rows, get_y(bottom_right!(area)) + 1)),
|
||||
cols: (
|
||||
|
@ -373,9 +376,14 @@ impl CellBuffer {
|
|||
row,
|
||||
col: std::cmp::min(self.cols.saturating_sub(1), bounds.start)
|
||||
..(std::cmp::min(self.cols, bounds.end)),
|
||||
_width: bounds.len(),
|
||||
}
|
||||
} else {
|
||||
RowIterator { row, col: 0..0 }
|
||||
RowIterator {
|
||||
row,
|
||||
col: 0..0,
|
||||
_width: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1247,8 +1255,10 @@ pub fn clear_area(grid: &mut CellBuffer, area: Area, attributes: crate::conf::Th
|
|||
/// grid[c].set_ch('w');
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct RowIterator {
|
||||
row: usize,
|
||||
_width: usize,
|
||||
col: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
|
@ -1263,17 +1273,64 @@ pub struct RowIterator {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BoundsIterator {
|
||||
rows: std::ops::Range<usize>,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
cols: (usize, usize),
|
||||
}
|
||||
|
||||
impl BoundsIterator {
|
||||
const EMPTY: Self = BoundsIterator {
|
||||
rows: 0..0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
cols: (0, 0),
|
||||
};
|
||||
|
||||
pub fn area(&self) -> Area {
|
||||
(
|
||||
(self.cols.0, self.rows.start),
|
||||
(
|
||||
std::cmp::max(self.cols.0, self.cols.1.saturating_sub(1)),
|
||||
std::cmp::max(self.rows.start, self.rows.end.saturating_sub(1)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.width == 0 || self.height == 0 || self.rows.len() == 0
|
||||
}
|
||||
|
||||
pub fn add_x(&mut self, x: usize) -> Self {
|
||||
if x == 0 {
|
||||
return Self::EMPTY;
|
||||
}
|
||||
|
||||
let ret = Self {
|
||||
rows: self.rows.clone(),
|
||||
width: self.width.saturating_sub(x),
|
||||
height: self.height,
|
||||
cols: self.cols,
|
||||
};
|
||||
if self.cols.0 + x < self.cols.1 && self.width > x {
|
||||
self.cols.0 += x;
|
||||
self.width -= x;
|
||||
return ret;
|
||||
}
|
||||
*self = Self::EMPTY;
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for BoundsIterator {
|
||||
type Item = RowIterator;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(next_row) = self.rows.next() {
|
||||
Some(RowIterator {
|
||||
row: next_row,
|
||||
_width: self.width,
|
||||
col: self.cols.0..self.cols.1,
|
||||
})
|
||||
} else {
|
||||
|
@ -1305,6 +1362,10 @@ impl RowIterator {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn area(&self) -> Area {
|
||||
((self.col.start, self.row), (self.col.end, self.row))
|
||||
}
|
||||
}
|
||||
|
||||
pub use boundaries::create_box;
|
||||
|
@ -1739,6 +1800,26 @@ impl core::cmp::PartialOrd for FormatTag {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ThemeAttribute> for FormatTag {
|
||||
fn from(val: ThemeAttribute) -> Self {
|
||||
let ThemeAttribute { fg, bg, attrs, .. } = val;
|
||||
Self {
|
||||
fg: Some(fg),
|
||||
bg: Some(bg),
|
||||
attrs: Some(attrs),
|
||||
priority: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatTag {
|
||||
#[inline(always)]
|
||||
pub fn set_priority(mut self, new_val: u8) -> Self {
|
||||
self.priority = new_val;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Hash, Clone, PartialEq, Eq)]
|
||||
pub enum WidgetWidth {
|
||||
Unset,
|
||||
|
|
|
@ -76,6 +76,7 @@ macro_rules! height {
|
|||
($a:expr) => {
|
||||
($crate::get_y($crate::bottom_right!($a)))
|
||||
.saturating_sub($crate::get_y($crate::upper_left!($a)))
|
||||
+ 1
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -93,6 +94,7 @@ macro_rules! width {
|
|||
($a:expr) => {
|
||||
($crate::get_x($crate::bottom_right!($a)))
|
||||
.saturating_sub($crate::get_x($crate::upper_left!($a)))
|
||||
+ 1
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -252,3 +254,12 @@ pub fn place_in_area(area: Area, (width, height): (usize, usize), upper: bool, l
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// Get `n`th row of `area` or its last one.
|
||||
pub fn nth_row_area(area: Area, n: usize) -> Area {
|
||||
let (upper_left, bottom_right) = area;
|
||||
let (_, max_y) = bottom_right;
|
||||
let y = std::cmp::min(max_y, get_y(upper_left) + n);
|
||||
(set_y(upper_left, y), set_y(bottom_right, y))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue