diff --git a/src/components/mail/listing.rs b/src/components/mail/listing.rs index d3fe1b03..e5dbce68 100644 --- a/src/components/mail/listing.rs +++ b/src/components/mail/listing.rs @@ -376,6 +376,7 @@ pub trait ListingTrait: Component { _context: &Context, ) { } + fn set_command_modifier(&mut self, _is_active: bool) {} fn set_movement(&mut self, mvm: PageMovement); } @@ -647,12 +648,14 @@ impl Component for Listing { 1 } else if let Ok(amount) = self.cmd_buf.parse::() { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); amount } else { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -698,12 +701,14 @@ impl Component for Listing { 1 } else if let Ok(amount) = self.cmd_buf.parse::() { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); amount } else { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -797,12 +802,14 @@ impl Component for Listing { 1 } else if let Ok(amount) = self.cmd_buf.parse::() { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); amount } else { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -818,12 +825,14 @@ impl Component for Listing { 1 } else if let Ok(amount) = self.cmd_buf.parse::() { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); amount } else { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -839,12 +848,14 @@ impl Component for Listing { 1 } else if let Ok(mult) = self.cmd_buf.parse::() { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); mult } else { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -860,12 +871,14 @@ impl Component for Listing { 1 } else if let Ok(mult) = self.cmd_buf.parse::() { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); mult } else { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -957,12 +970,14 @@ impl Component for Listing { 1 } else if let Ok(amount) = self.cmd_buf.parse::() { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); amount } else { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -1009,12 +1024,14 @@ impl Component for Listing { 1 } else if let Ok(amount) = self.cmd_buf.parse::() { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); amount } else { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -1063,12 +1080,14 @@ impl Component for Listing { 1 } else if let Ok(amount) = self.cmd_buf.parse::() { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); amount } else { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -1131,6 +1150,7 @@ impl Component for Listing { } UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt('')) if !self.cmd_buf.is_empty() => { self.cmd_buf.clear(); + self.component.set_command_modifier(false); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); @@ -1138,6 +1158,7 @@ impl Component for Listing { } UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => { self.cmd_buf.push(c); + self.component.set_command_modifier(true); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufSet( diff --git a/src/components/mail/listing/compact.rs b/src/components/mail/listing/compact.rs index b558c10b..5f140184 100644 --- a/src/components/mail/listing/compact.rs +++ b/src/components/mail/listing/compact.rs @@ -160,6 +160,8 @@ pub struct CompactListing { color_cache: ColorCache, movement: Option, + modifier_active: bool, + modifier_command: Option, id: ComponentId, } @@ -858,6 +860,10 @@ impl ListingTrait for CompactListing { } } + fn set_command_modifier(&mut self, is_active: bool) { + self.modifier_active = is_active; + } + fn set_movement(&mut self, mvm: PageMovement) { self.movement = Some(mvm); self.set_dirty(true); @@ -897,6 +903,8 @@ impl CompactListing { view: ThreadView::default(), color_cache: ColorCache::default(), movement: None, + modifier_active: false, + modifier_command: None, id: ComponentId::new_v4(), } } @@ -1395,18 +1403,79 @@ impl Component for CompactListing { area = (set_y(upper_left, y + 1), bottom_right); } + let (upper_left, bottom_right) = area; + let rows = get_y(bottom_right) - get_y(upper_left) + 1; + + if let Some('s') = self.modifier_command.take() { + self.set_command_modifier(false); + if let Some(mvm) = self.movement.as_ref() { + match mvm { + PageMovement::Up(amount) => { + for c in self.new_cursor_pos.2.saturating_sub(*amount) + ..=self.new_cursor_pos.2 + { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::PageUp(multiplier) => { + for c in self.new_cursor_pos.2.saturating_sub(rows * multiplier) + ..=self.new_cursor_pos.2 + { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::Down(amount) => { + for c in self.new_cursor_pos.2 + ..std::cmp::min(self.length, self.new_cursor_pos.2 + amount + 1) + { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::PageDown(multiplier) => { + for c in self.new_cursor_pos.2 + ..std::cmp::min( + self.new_cursor_pos.2 + rows * multiplier + 1, + self.length, + ) + { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::Right(_) | PageMovement::Left(_) => {} + PageMovement::Home => { + for c in 0..=self.new_cursor_pos.2 { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::End => { + for c in self.new_cursor_pos.2..self.length { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + } + } + } if !self.row_updates.is_empty() { - let (upper_left, bottom_right) = area; while let Some(row) = self.row_updates.pop() { self.update_line(context, row); let row: usize = self.order[&row]; - - 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 { + 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)), @@ -1475,8 +1544,13 @@ impl Component for CompactListing { key == shortcuts[CompactListing::DESCRIPTION]["select_entry"] ) => { - let thread_hash = self.get_thread_under_cursor(self.cursor_pos.2); - self.selection.entry(thread_hash).and_modify(|e| *e = !*e); + if self.modifier_active { + self.modifier_command = Some('s'); + } else { + let thread_hash = self.get_thread_under_cursor(self.cursor_pos.2); + self.selection.entry(thread_hash).and_modify(|e| *e = !*e); + } + return true; } UIEvent::Action(ref action) => { match action { diff --git a/src/components/mail/listing/conversations.rs b/src/components/mail/listing/conversations.rs index 68dd678a..44603ae0 100644 --- a/src/components/mail/listing/conversations.rs +++ b/src/components/mail/listing/conversations.rs @@ -123,6 +123,8 @@ pub struct ConversationsListing { color_cache: ColorCache, movement: Option, + modifier_active: bool, + modifier_command: Option, id: ComponentId, } @@ -848,6 +850,10 @@ impl ListingTrait for ConversationsListing { } } + fn set_command_modifier(&mut self, is_active: bool) { + self.modifier_active = is_active; + } + fn set_movement(&mut self, mvm: PageMovement) { self.movement = Some(mvm); self.set_dirty(true); @@ -884,6 +890,8 @@ impl ConversationsListing { view: ThreadView::default(), color_cache: ColorCache::default(), movement: None, + modifier_active: false, + modifier_command: None, id: ComponentId::new_v4(), } } @@ -1201,20 +1209,81 @@ impl Component for ConversationsListing { area = (set_y(upper_left, y + 1), bottom_right); } + let (upper_left, bottom_right) = area; + let rows = (get_y(bottom_right) - get_y(upper_left) + 1) / 3; + if let Some('s') = self.modifier_command.take() { + self.set_command_modifier(false); + if let Some(mvm) = self.movement.as_ref() { + match mvm { + PageMovement::Up(amount) => { + for c in self.new_cursor_pos.2.saturating_sub(*amount) + ..=self.new_cursor_pos.2 + { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::PageUp(multiplier) => { + for c in self.new_cursor_pos.2.saturating_sub(rows * multiplier) + ..=self.new_cursor_pos.2 + { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::Down(amount) => { + for c in self.new_cursor_pos.2 + ..std::cmp::min(self.length, self.new_cursor_pos.2 + amount + 1) + { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::PageDown(multiplier) => { + for c in self.new_cursor_pos.2 + ..std::cmp::min( + self.new_cursor_pos.2 + rows * multiplier + 1, + self.length, + ) + { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::Right(_) | PageMovement::Left(_) => {} + PageMovement::Home => { + for c in 0..=self.new_cursor_pos.2 { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + PageMovement::End => { + for c in self.new_cursor_pos.2..self.length { + let thread = self.get_thread_under_cursor(c); + self.selection.entry(thread).and_modify(|e| *e = !*e); + self.row_updates.push(thread); + } + } + } + } + } if !self.row_updates.is_empty() { /* certain rows need to be updated (eg an unseen message was just set seen) * */ - let (upper_left, bottom_right) = area; while let Some(row) = self.row_updates.pop() { self.update_line(context, row); let row: usize = self.order[&row]; - let rows = (get_y(bottom_right) - get_y(upper_left) + 1) / 3; let page_no = (self.cursor_pos.2).wrapping_div(rows); let top_idx = page_no * rows; /* Update row only if it's currently visible */ - if row >= top_idx && row <= top_idx + rows { + if row >= top_idx && row < top_idx + rows { let area = ( set_y(upper_left, get_y(upper_left) + (3 * (row % rows))), set_y(bottom_right, get_y(upper_left) + (3 * (row % rows) + 2)), @@ -1288,8 +1357,12 @@ impl Component for ConversationsListing { key == shortcuts[ConversationsListing::DESCRIPTION]["select_entry"] ) => { - let thread = self.get_thread_under_cursor(self.cursor_pos.2); - self.selection.entry(thread).and_modify(|e| *e = !*e); + if self.modifier_active { + self.modifier_command = Some('s'); + } else { + let thread = self.get_thread_under_cursor(self.cursor_pos.2); + self.selection.entry(thread).and_modify(|e| *e = !*e); + } return true; } UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {