listing: select multiple messages with a motion
- Press a number (movement multiplier) - Press "select_entry" shortcut (default: v) - Press a movement (arrow keys, PageUp/Down, Home/End) - Resulting selection will be symmetric difference of previous selection plus all the entries traversed with movementmemfd
parent
9e20f6556a
commit
ed27ed604c
|
@ -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::<usize>() {
|
||||
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::<usize>() {
|
||||
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::<usize>() {
|
||||
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::<usize>() {
|
||||
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::<usize>() {
|
||||
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::<usize>() {
|
||||
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::<usize>() {
|
||||
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::<usize>() {
|
||||
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::<usize>() {
|
||||
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(
|
||||
|
|
|
@ -160,6 +160,8 @@ pub struct CompactListing {
|
|||
color_cache: ColorCache,
|
||||
|
||||
movement: Option<PageMovement>,
|
||||
modifier_active: bool,
|
||||
modifier_command: Option<char>,
|
||||
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 {
|
||||
|
|
|
@ -123,6 +123,8 @@ pub struct ConversationsListing {
|
|||
color_cache: ColorCache,
|
||||
|
||||
movement: Option<PageMovement>,
|
||||
modifier_active: bool,
|
||||
modifier_command: Option<char>,
|
||||
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) => {
|
||||
|
|
Loading…
Reference in New Issue