mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-21 18:40:19 +00:00
make table responsive
This commit is contained in:
parent
c3d5559234
commit
60af11bd47
6 changed files with 118 additions and 38 deletions
|
@ -21,6 +21,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Write new email [#8]
|
||||
- Reply, reply all and forward [#9] [#10] [#11]
|
||||
- Download attachments [#14]
|
||||
- Merge `Email` with `Msg` [#21]
|
||||
- List command with pagination [#19]
|
||||
- Icon in table when attachment is present [#16]
|
||||
|
||||
[unreleased]: https://github.com/soywod/himalaya
|
||||
|
||||
|
@ -37,3 +40,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
[#13]: https://github.com/soywod/himalaya/issues/13
|
||||
[#14]: https://github.com/soywod/himalaya/issues/14
|
||||
[#15]: https://github.com/soywod/himalaya/issues/15
|
||||
[#16]: https://github.com/soywod/himalaya/issues/16
|
||||
[#19]: https://github.com/soywod/himalaya/issues/19
|
||||
[#21]: https://github.com/soywod/himalaya/issues/21
|
||||
|
|
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -280,6 +280,7 @@ dependencies = [
|
|||
"rfc2047-decoder",
|
||||
"rustyline",
|
||||
"serde",
|
||||
"terminal_size",
|
||||
"toml",
|
||||
]
|
||||
|
||||
|
@ -946,6 +947,16 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bd2d183bd3fac5f5fe38ddbeb4dc9aec4a39a9d7d59e7491d900302da01cbe1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
|
|
|
@ -14,4 +14,5 @@ native-tls = "0.2"
|
|||
rfc2047-decoder = "0.1.2"
|
||||
rustyline = "7.1.0"
|
||||
serde = { version = "1.0.118", features = ["derive"] }
|
||||
terminal_size = "0.1.15"
|
||||
toml = "0.5.8"
|
||||
|
|
18
src/mbox.rs
18
src/mbox.rs
|
@ -20,17 +20,25 @@ impl Mbox {
|
|||
|
||||
impl DisplayRow for Mbox {
|
||||
fn to_row(&self) -> Vec<table::Cell> {
|
||||
use crate::table::*;
|
||||
|
||||
vec![
|
||||
table::Cell::new(&[table::BLUE], &self.delim),
|
||||
table::Cell::new(&[table::GREEN], &self.name),
|
||||
table::Cell::new(&[table::YELLOW], &self.attributes.join(", ")),
|
||||
Cell::new(&[BLUE], &self.delim),
|
||||
Cell::new(&[GREEN], &self.name),
|
||||
FlexCell::new(&[YELLOW], &self.attributes.join(", ")),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DisplayTable<'a, Mbox> for Vec<Mbox> {
|
||||
fn cols() -> &'a [&'a str] {
|
||||
&["delim", "name", "attributes"]
|
||||
fn header_row() -> Vec<table::Cell> {
|
||||
use crate::table::*;
|
||||
|
||||
vec![
|
||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "DELIM"),
|
||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "NAME"),
|
||||
FlexCell::new(&[BOLD, UNDERLINE, WHITE], "ATTRIBUTES"),
|
||||
]
|
||||
}
|
||||
|
||||
fn rows(&self) -> &Vec<Mbox> {
|
||||
|
|
35
src/msg.rs
35
src/msg.rs
|
@ -379,7 +379,10 @@ impl DisplayRow for Msg {
|
|||
let headers = parsed.get_headers();
|
||||
|
||||
let uid = &self.uid.to_string();
|
||||
let flags = String::new(); // TODO: render flags
|
||||
let flags = match self.extract_attachments().map(|vec| vec.is_empty()) {
|
||||
Ok(false) => "",
|
||||
_ => " ",
|
||||
};
|
||||
let sender = headers
|
||||
.get_first_value("reply-to")
|
||||
.or(headers.get_first_value("from"))
|
||||
|
@ -387,21 +390,33 @@ impl DisplayRow for Msg {
|
|||
let subject = headers.get_first_value("subject").unwrap_or_default();
|
||||
let date = headers.get_first_value("date").unwrap_or_default();
|
||||
|
||||
vec![
|
||||
table::Cell::new(&[table::RED], &uid),
|
||||
table::Cell::new(&[table::WHITE], &flags),
|
||||
table::Cell::new(&[table::BLUE], &sender),
|
||||
table::Cell::new(&[table::GREEN], &subject),
|
||||
table::Cell::new(&[table::YELLOW], &date),
|
||||
]
|
||||
{
|
||||
use crate::table::*;
|
||||
|
||||
vec![
|
||||
Cell::new(&[RED], &uid),
|
||||
Cell::new(&[WHITE], &flags),
|
||||
Cell::new(&[BLUE], &sender),
|
||||
FlexCell::new(&[GREEN], &subject),
|
||||
Cell::new(&[YELLOW], &date),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DisplayTable<'a, Msg> for Vec<Msg> {
|
||||
fn cols() -> &'a [&'a str] {
|
||||
&["uid", "flags", "sender", "subject", "date"]
|
||||
fn header_row() -> Vec<table::Cell> {
|
||||
use crate::table::*;
|
||||
|
||||
vec![
|
||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "UID"),
|
||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "FLAGS"),
|
||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "SENDER"),
|
||||
FlexCell::new(&[BOLD, UNDERLINE, WHITE], "SUBJECT"),
|
||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "DATE"),
|
||||
]
|
||||
}
|
||||
|
||||
fn rows(&self) -> &Vec<Msg> {
|
||||
|
|
85
src/table.rs
85
src/table.rs
|
@ -1,4 +1,5 @@
|
|||
use std::fmt;
|
||||
use terminal_size::terminal_size;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Style(u8, u8, u8);
|
||||
|
@ -33,13 +34,15 @@ impl fmt::Display for Style {
|
|||
pub struct Cell {
|
||||
pub styles: Vec<Style>,
|
||||
pub value: String,
|
||||
pub flex: bool,
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
pub fn new(styles: &[Style], value: &str) -> Cell {
|
||||
Cell {
|
||||
pub fn new(styles: &[Style], value: &str) -> Self {
|
||||
Self {
|
||||
styles: styles.to_vec(),
|
||||
value: value.to_string(),
|
||||
value: value.trim().to_string(),
|
||||
flex: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,20 +51,45 @@ impl Cell {
|
|||
}
|
||||
|
||||
pub fn render(&self, col_size: usize) -> String {
|
||||
let style_start = self
|
||||
let style_begin = self
|
||||
.styles
|
||||
.iter()
|
||||
.map(|style| format!("{}", style))
|
||||
.map(|style| style.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.concat();
|
||||
let style_end = "\x1b[0m";
|
||||
|
||||
let padding = if col_size == 0 {
|
||||
"".to_string()
|
||||
if col_size > 0 && self.printable_value_len() > col_size {
|
||||
let col_size = self
|
||||
.value
|
||||
.char_indices()
|
||||
.map(|(i, _)| i)
|
||||
.nth(col_size)
|
||||
.unwrap()
|
||||
- 2;
|
||||
|
||||
String::from(style_begin + &self.value[0..=col_size] + "… " + style_end)
|
||||
} else {
|
||||
" ".repeat(col_size - self.printable_value_len() + 1)
|
||||
};
|
||||
let padding = if col_size == 0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
" ".repeat(col_size - self.printable_value_len() + 1)
|
||||
};
|
||||
|
||||
String::from(style_start + &self.value + &padding + "\x1b[0m")
|
||||
String::from(style_begin + &self.value + &padding + style_end)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FlexCell;
|
||||
|
||||
impl FlexCell {
|
||||
pub fn new(styles: &[Style], value: &str) -> Cell {
|
||||
Cell {
|
||||
flex: true,
|
||||
..Cell::new(styles, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,22 +98,18 @@ pub trait DisplayRow {
|
|||
}
|
||||
|
||||
pub trait DisplayTable<'a, T: DisplayRow + 'a> {
|
||||
fn cols() -> &'a [&'a str];
|
||||
fn header_row() -> Vec<Cell>;
|
||||
fn rows(&self) -> &Vec<T>;
|
||||
|
||||
fn to_table(&self) -> String {
|
||||
let mut col_sizes = vec![];
|
||||
let head = Self::header_row();
|
||||
|
||||
let head = Self::cols()
|
||||
.iter()
|
||||
.map(|col| {
|
||||
let cell = Cell::new(&[BOLD, UNDERLINE, WHITE], &col.to_uppercase());
|
||||
col_sizes.push(cell.printable_value_len());
|
||||
cell
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
head.iter().for_each(|cell| {
|
||||
col_sizes.push(cell.printable_value_len());
|
||||
});
|
||||
|
||||
let mut body = self
|
||||
let mut table = self
|
||||
.rows()
|
||||
.iter()
|
||||
.map(|item| {
|
||||
|
@ -97,13 +121,28 @@ pub trait DisplayTable<'a, T: DisplayRow + 'a> {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
body.insert(0, head);
|
||||
table.insert(0, head);
|
||||
|
||||
body.iter().fold(String::new(), |output, row| {
|
||||
let term_width = terminal_size().map(|size| size.0 .0).unwrap_or(0) as usize;
|
||||
let seps_width = 2 * col_sizes.len() - 1;
|
||||
let table_width = col_sizes.iter().sum::<usize>() + seps_width;
|
||||
let diff_width = if table_width < term_width {
|
||||
0
|
||||
} else {
|
||||
table_width - term_width
|
||||
};
|
||||
|
||||
table.iter().fold(String::new(), |output, row| {
|
||||
let row_str = row
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, cell)| cell.render(col_sizes[i]))
|
||||
.map(|(i, cell)| {
|
||||
if cell.flex && col_sizes[i] > diff_width {
|
||||
cell.render(col_sizes[i] - diff_width)
|
||||
} else {
|
||||
cell.render(col_sizes[i])
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(&Cell::new(&[ext(8)], "|").render(0));
|
||||
|
||||
|
|
Loading…
Reference in a new issue