utilities/tables: add x axis scroll support

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/312/head
Manos Pitsidianakis 2023-11-13 15:52:11 +02:00
parent bcec745c24
commit ba7a97e90b
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
6 changed files with 167 additions and 85 deletions

View File

@ -770,10 +770,10 @@ Start new mail draft in new tab.
.Pq Em m \" default value
.It Ic next_account
Go to next account.
.Pq Em h \" default value
.Pq Em H \" default value
.It Ic prev_account
Go to previous account.
.Pq Em l \" default value
.Pq Em L \" default value
.It Ic next_mailbox
Go to next mailbox.
.Pq Em J \" default value
@ -874,10 +874,10 @@ Mail contact under cursor.
.Pq Em m \" default value
.It Ic next_account
Go to next account.
.Pq Em h \" default value
.Pq Em H \" default value
.It Ic prev_account
Go to previous account.
.Pq Em l \" default value
.Pq Em L \" default value
.It Ic toggle_menu_visibility
Toggle visibility of side menu in mail list.
.Pq Em ` \" default value

View File

@ -153,10 +153,10 @@ shortcut_key_values! { "listing",
scroll_up |> "Scroll up list." |> Key::Char('k'),
scroll_down |> "Scroll down list." |> Key::Char('j'),
new_mail |> "Start new mail draft in new tab." |> Key::Char('m'),
next_account |> "Go to next account." |> Key::Char('h'),
next_account |> "Go to next account." |> Key::Char('H'),
next_mailbox |> "Go to next mailbox." |> Key::Char('J'),
next_page |> "Go to next page." |> Key::PageDown,
prev_account |> "Go to previous account." |> Key::Char('l'),
prev_account |> "Go to previous account." |> Key::Char('L'),
prev_mailbox |> "Go to previous mailbox." |> Key::Char('K'),
open_mailbox |> "Open selected mailbox" |> Key::Char('\n'),
toggle_mailbox_collapse |> "Toggle mailbox collapse in menu." |> Key::Char(' '),
@ -189,8 +189,8 @@ shortcut_key_values! { "contact-list",
edit_contact |> "Edit contact under cursor." |> Key::Char('e'),
delete_contact |> "Delete contact under cursor." |> Key::Char('d'),
mail_contact |> "Mail contact under cursor." |> Key::Char('m'),
next_account |> "Go to next account." |> Key::Char('h'),
prev_account |> "Go to previous account." |> Key::Char('l'),
next_account |> "Go to next account." |> Key::Char('H'),
prev_account |> "Go to previous account." |> Key::Char('L'),
toggle_menu_visibility |> "Toggle visibility of side menu in mail list." |> Key::Char('`')
}
}
@ -212,8 +212,8 @@ shortcut_key_values! { "general",
quit |> "Quit meli." |> Key::Char('q'),
go_to_tab |> "Go to the nth tab" |> Key::Alt('n'),
next_tab |> "Next tab." |> Key::Char('T'),
scroll_right |> "Generic scroll right (catch-all setting)" |> Key::Right,
scroll_left |> "Generic scroll left (catch-all setting)" |> Key::Left,
scroll_right |> "Generic scroll right (catch-all setting)" |> Key::Char('l'),
scroll_left |> "Generic scroll left (catch-all setting)" |>Key::Char('h'),
scroll_up |> "Generic scroll up (catch-all setting)" |> Key::Char('k'),
scroll_down |> "Generic scroll down (catch-all setting)" |> Key::Char('j'),
next_page |> "Go to next page. (catch-all setting)" |> Key::PageDown,

View File

@ -422,6 +422,45 @@ macro_rules! address_list {
}};
}
#[macro_export]
macro_rules! digits_of_num {
($num:expr) => {{
const GUESS: [usize; 65] = [
1, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8,
8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15,
15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 18, 19,
];
const TENS: [usize; 20] = [
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
10000000000000000000,
];
const SIZE_IN_BITS: usize = std::mem::size_of::<usize>() * 8;
let leading_zeros = $num.leading_zeros() as usize;
let base_two_digits: usize = SIZE_IN_BITS - leading_zeros;
let x = GUESS[base_two_digits];
x + if $num >= TENS[x] { 1 } else { 0 }
}};
}
macro_rules! column_str {
(
struct $name:ident($($t:ty),+)) => {
@ -1658,6 +1697,52 @@ impl Component for Listing {
self.component.set_movement(PageMovement::Down(amount));
return true;
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_right"]) =>
{
let amount = if self.cmd_buf.is_empty() {
1
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
self.cmd_buf.clear();
self.component.set_modifier_active(false);
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
amount
} else {
self.cmd_buf.clear();
self.component.set_modifier_active(false);
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
return true;
};
self.component.set_movement(PageMovement::Right(amount));
return true;
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_left"]) =>
{
let amount = if self.cmd_buf.is_empty() {
1
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
self.cmd_buf.clear();
self.component.set_modifier_active(false);
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
amount
} else {
self.cmd_buf.clear();
self.component.set_modifier_active(false);
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
return true;
};
self.component.set_movement(PageMovement::Left(amount));
return true;
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Shortcuts::LISTING]["prev_page"]) =>
{

View File

@ -27,60 +27,6 @@ use melib::{SortField, SortOrder, TagHash, Threads};
use super::*;
use crate::{components::PageMovement, jobs::JoinHandle};
macro_rules! digits_of_num {
($num:expr) => {{
const GUESS: [usize; 65] = [
1, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8,
8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15,
15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 18, 19,
];
const TENS: [usize; 20] = [
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
10000000000000000000,
];
const SIZE_IN_BITS: usize = std::mem::size_of::<usize>() * 8;
let leading_zeros = $num.leading_zeros() as usize;
let base_two_digits: usize = SIZE_IN_BITS - leading_zeros;
let x = GUESS[base_two_digits];
x + if $num >= TENS[x] { 1 } else { 0 }
}};
}
macro_rules! address_list {
(($name:expr) as comma_sep_list) => {{
let mut ret: String =
$name
.into_iter()
.fold(String::new(), |mut s: String, n: &Address| {
s.extend(n.to_string().chars());
s.push_str(", ");
s
});
ret.pop();
ret.pop();
ret
}};
}
macro_rules! row_attr {
($color_cache:expr, $even: expr, $unseen:expr, $highlighted:expr, $selected:expr $(,)*) => {{
let color_cache = &$color_cache;

View File

@ -445,7 +445,20 @@ impl ListingTrait for PlainListing {
self.new_cursor_pos.2 = (self.length.saturating_sub(1) / rows) * rows;
}
}
PageMovement::Right(_) | PageMovement::Left(_) => {}
PageMovement::Right(amount) => {
self.data_columns.x_offset += amount;
self.data_columns.x_offset = self.data_columns.x_offset.min(
self.data_columns
.widths
.iter()
.map(|w| w + 2)
.sum::<usize>()
.saturating_sub(2),
);
}
PageMovement::Left(amount) => {
self.data_columns.x_offset = self.data_columns.x_offset.saturating_sub(amount);
}
PageMovement::Home => {
self.new_cursor_pos.2 = 0;
}
@ -788,6 +801,18 @@ impl PlainListing {
self.rows.clear();
self.length = 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::new(),
SmallVec::new(),
SmallVec::new(),
SmallVec::new(),
);
for i in iter {
if !context.accounts[&self.cursor_pos.0].contains_key(i) {
@ -824,6 +849,28 @@ impl PlainListing {
self.rows.row_attr_cache.insert(self.length, row_attr);
let entry_strings = self.make_entry_string(&envelope, context);
row_widths.1.push(
entry_strings
.date
.grapheme_width()
.try_into()
.unwrap_or(255),
);
row_widths.2.push(
entry_strings
.from
.grapheme_width()
.try_into()
.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),
);
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(
@ -842,6 +889,9 @@ impl PlainListing {
self.length += 1;
}
row_widths
.0
.push(digits_of_num!(self.length).try_into().unwrap_or(255));
min_width.0 = self.length.saturating_sub(1).to_string().len();
@ -868,6 +918,10 @@ impl PlainListing {
_ = self.data_columns.columns[2].resize_with_context(min_width.2, self.rows.len(), context);
/* subject column */
_ = self.data_columns.columns[3].resize_with_context(min_width.3, 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();
let iter = if self.filter_term.is_empty() {
Box::new(self.local_collection.iter().cloned())

View File

@ -241,38 +241,35 @@ impl<const N: usize> DataColumns<N> {
}
pub fn draw(
&self,
&mut 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 height = total_area.height();
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;
if self.width_accum > 0 && self.x_offset + total_area.width() > self.width_accum {
self.x_offset = self.width_accum.saturating_sub(total_area.width());
}
let mut x_offset = self.x_offset;
for col in 0..N {
start_col = col;
if x_offset == 0 || self.widths[col] > x_offset {
break;
}
start_col += 1;
x_offset -= self.widths[col];
x_offset = x_offset.saturating_sub(2);
}
for col in skip_cols.0..N {
for col in start_col..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;
let column_width = self.widths[col];
if column_width == 0 {
continue;
}
@ -282,11 +279,11 @@ impl<const N: usize> DataColumns<N> {
self.columns[col]
.area()
.skip_rows(top_idx)
.skip_cols(skip_cols.1)
.take_cols(column_width),
.skip_cols(x_offset)
.take_cols(column_width - x_offset),
);
bounds.add_x(column_width + 2);
skip_cols.1 = 0;
bounds.add_x(column_width - x_offset + 2);
x_offset = 0;
}
match self.theme_config.theme {