From 03da43b0ce70bc6b442630b3306a4dcf92c69b61 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 18 Jun 2023 09:07:48 +0300 Subject: [PATCH] contacts/list: impl entry selection WIP --- src/components/contacts.rs | 5 +- .../contacts/{contact_list.rs => list.rs} | 70 +++++++++++++------ src/components/mail/listing.rs | 1 - src/conf/themes.rs | 2 + 4 files changed, 54 insertions(+), 24 deletions(-) rename src/components/contacts/{contact_list.rs => list.rs} (94%) diff --git a/src/components/contacts.rs b/src/components/contacts.rs index e992d72a..6d4c8019 100644 --- a/src/components/contacts.rs +++ b/src/components/contacts.rs @@ -25,9 +25,8 @@ use melib::Card; use super::*; -mod contact_list; - -pub use self::contact_list::*; +mod list; +pub use list::*; #[derive(Debug)] enum ViewMode { diff --git a/src/components/contacts/contact_list.rs b/src/components/contacts/list.rs similarity index 94% rename from src/components/contacts/contact_list.rs rename to src/components/contacts/list.rs index ffecf450..ff195ef6 100644 --- a/src/components/contacts/contact_list.rs +++ b/src/components/contacts/list.rs @@ -21,7 +21,7 @@ use std::cmp; -use melib::{backends::AccountHash, text_processing::TextProcessing, CardId, Draft}; +use melib::{backends::AccountHash, text_processing::TextProcessing, CardId, Draft, HeaderName}; use super::*; @@ -50,8 +50,10 @@ pub struct ContactList { initialized: bool, theme_default: ThemeAttribute, highlight_theme: ThemeAttribute, + selected_theme: ThemeAttribute, - id_positions: Vec, + cards: IndexMap, + selection: IndexMap, mode: ViewMode, dirty: bool, @@ -62,6 +64,8 @@ pub struct ContactList { menu_visibility: bool, movement: Option, cmd_buf: String, + modifier_active: bool, + modifier_command: Option, view: Option, ratio: usize, // right/(container width) * 100 id: ComponentId, @@ -91,14 +95,18 @@ impl ContactList { new_cursor_pos: 0, length: 0, account_pos: 0, - id_positions: Vec::new(), + cards: IndexMap::default(), + selection: IndexMap::default(), mode: ViewMode::List, data_columns: DataColumns::default(), theme_default: crate::conf::value(context, "theme_default"), highlight_theme: crate::conf::value(context, "highlight"), + selected_theme: crate::conf::value(context, "selected"), initialized: false, dirty: true, movement: None, + modifier_active: false, + modifier_command: None, cmd_buf: String::with_capacity(8), view: None, ratio: 90, @@ -121,12 +129,21 @@ impl ContactList { let book = &account.address_book; self.length = book.len(); - self.id_positions.clear(); - if self.id_positions.capacity() < book.len() { - self.id_positions.reserve(book.len()); + self.cards.clear(); + self.selection.clear(); + if self.cards.capacity() < book.len() { + self.cards.reserve(book.len()); + self.selection.reserve(book.len()); } self.dirty = true; - let mut min_width = ("Name".len(), "E-mail".len(), 0, "external".len(), 0, 0); + let mut min_width = ( + "Name".len(), + "E-mail".len(), + "URL".len(), + "external".len(), + 0, + 0, + ); for c in book.values() { /* name */ @@ -155,7 +172,8 @@ impl ContactList { let mut book_values = book.values().collect::>(); book_values.sort_unstable_by_key(|c| c.name()); for (idx, c) in book_values.iter().enumerate() { - self.id_positions.push(*c.id()); + self.cards.insert(*c.id(), (*c).clone()); + self.selection.insert(*c.id(), false); write_string_to_grid( c.name(), @@ -468,12 +486,7 @@ impl ContactList { self.data_columns.widths[2] = self.data_columns.columns[2].size().0; /* url */ self.data_columns.widths[3] = self.data_columns.columns[3].size().0; /* source */ - let min_col_width = std::cmp::min( - 15, - std::cmp::min(self.data_columns.widths[0], self.data_columns.widths[1]), - ); - if self.data_columns.widths[0] + self.data_columns.widths[1] + 3 * min_col_width + 8 > width - { + if self.data_columns.widths[0] + self.data_columns.widths[1] > width { let remainder = width.saturating_sub(self.data_columns.widths[0] + self.data_columns.widths[1] + 4); self.data_columns.widths[2] = remainder / 6; @@ -656,9 +669,7 @@ impl Component for ContactList { if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["edit_contact"]) && self.length > 0 => { - let account = &mut context.accounts[self.account_pos]; - let book = &mut account.address_book; - let card = book[&self.id_positions[self.cursor_pos]].clone(); + let card = self.cards[self.cursor_pos].clone(); let mut manager = ContactManager::new(context); manager.set_parent_id(self.id); manager.card = card; @@ -680,10 +691,9 @@ impl Component for ContactList { { let account = &context.accounts[self.account_pos]; let account_hash = account.hash(); - let book = &account.address_book; - let card = &book[&self.id_positions[self.cursor_pos]]; + let card = self.cards[self.cursor_pos].clone(); let mut draft: Draft = Draft::default(); - *draft.headers_mut().get_mut("To").unwrap() = + *draft.headers_mut().get_mut(HeaderName::TO).unwrap() = format!("{} <{}>", &card.name(), &card.email()); let mut composer = Composer::with_account(account_hash, context); composer.set_draft(draft); @@ -693,6 +703,17 @@ impl Component for ContactList { return true; } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::LISTING]["select_entry"]) => + { + if self.modifier_active && self.modifier_command.is_none() { + self.modifier_command = Some(Modifier::default()); + } else if let Some(c) = self.cards.get_index(self.cursor_pos) { + self.selection.entry(*c.0).and_modify(|e| *e = !*e); + self.set_dirty(true); + } + return true; + } UIEvent::Input(ref key) if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["next_account"]) => { @@ -783,6 +804,15 @@ impl Component for ContactList { .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); return true; } + UIEvent::Input(Key::Esc) + if self.selection.values().cloned().any(std::convert::identity) => + { + for c in self.selection.values_mut() { + *c = false; + } + self.set_dirty(true); + return true; + } UIEvent::Input(Key::Char(c)) if c.is_ascii_digit() => { self.cmd_buf.push(c); context diff --git a/src/components/mail/listing.rs b/src/components/mail/listing.rs index df6dc752..ec7c04d8 100644 --- a/src/components/mail/listing.rs +++ b/src/components/mail/listing.rs @@ -60,7 +60,6 @@ pub struct RowsState { pub env_to_thread: HashMap, pub thread_order: HashMap, pub env_order: HashMap, - #[allow(clippy::type_complexity)] pub entries: Vec<(T, EntryStrings)>, pub all_threads: HashSet, pub all_envelopes: HashSet, diff --git a/src/conf/themes.rs b/src/conf/themes.rs index e1822ff1..83d06d9d 100644 --- a/src/conf/themes.rs +++ b/src/conf/themes.rs @@ -251,6 +251,7 @@ const DEFAULT_KEYS: &[&str] = &[ "text.error", "text.highlight", "error_message", + "selected", "highlight", "status.bar", "status.command_bar", @@ -1316,6 +1317,7 @@ impl Default for Themes { /* rest */ add!("highlight", dark = { fg: Color::Byte(240), bg: Color::Byte(237), attrs: Attr::BOLD }, light = { fg: Color::Byte(240), bg: Color::Byte(237), attrs: Attr::BOLD }); + add!("selected", dark = { fg: "theme_default", bg: Color::Byte(237), attrs: "theme_default" }, light = { fg: "theme_default", bg: Color::Byte(237), attrs: "theme_default" }); add!("status.bar", dark = { fg: Color::Byte(123), bg: Color::Byte(26) }, light = { fg: Color::Byte(123), bg: Color::Byte(26) }); add!("status.command_bar", dark = { fg: Color::Byte(219), bg: Color::Byte(88) }, light = { fg: Color::Byte(219), bg: Color::Byte(88) });