Implement shift+click selection
This commit is contained in:
parent
80aa2c31e9
commit
d8e258dfb1
1 changed files with 69 additions and 44 deletions
113
src/tab.rs
113
src/tab.rs
|
|
@ -710,7 +710,7 @@ pub struct Tab {
|
||||||
pub dnd_hovered: Option<(Location, Instant)>,
|
pub dnd_hovered: Option<(Location, Instant)>,
|
||||||
scrollable_id: widget::Id,
|
scrollable_id: widget::Id,
|
||||||
select_focus: Option<usize>,
|
select_focus: Option<usize>,
|
||||||
select_shift: Option<usize>,
|
select_range: Option<(usize, usize)>,
|
||||||
cached_selected: RefCell<Option<bool>>,
|
cached_selected: RefCell<Option<bool>>,
|
||||||
clicked: Option<usize>,
|
clicked: Option<usize>,
|
||||||
selected_clicked: bool,
|
selected_clicked: bool,
|
||||||
|
|
@ -734,7 +734,7 @@ impl Tab {
|
||||||
items_opt: None,
|
items_opt: None,
|
||||||
scrollable_id: widget::Id::unique(),
|
scrollable_id: widget::Id::unique(),
|
||||||
select_focus: None,
|
select_focus: None,
|
||||||
select_shift: None,
|
select_range: None,
|
||||||
cached_selected: RefCell::new(None),
|
cached_selected: RefCell::new(None),
|
||||||
clicked: None,
|
clicked: None,
|
||||||
dnd_hovered: None,
|
dnd_hovered: None,
|
||||||
|
|
@ -808,21 +808,19 @@ impl Tab {
|
||||||
let mut start = (row, col);
|
let mut start = (row, col);
|
||||||
let mut end = (row, col);
|
let mut end = (row, col);
|
||||||
if mod_shift {
|
if mod_shift {
|
||||||
if self.select_focus.is_none() || self.select_shift.is_none() {
|
if self.select_focus.is_none() || self.select_range.is_none() {
|
||||||
// Set select shift to initial state if necessary
|
// Set select range to initial state if necessary
|
||||||
self.select_shift = self.select_focus;
|
self.select_range = self.select_focus.map(|i| (i, i));
|
||||||
}
|
}
|
||||||
if let Some(pos) = self.select_shift_pos_opt() {
|
|
||||||
|
if let Some(pos) = self.select_range_start_pos_opt() {
|
||||||
if pos.0 < row || (pos.0 == row && pos.1 < col) {
|
if pos.0 < row || (pos.0 == row && pos.1 < col) {
|
||||||
start = pos;
|
start = pos;
|
||||||
} else {
|
} else {
|
||||||
end = pos;
|
end = pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// Clear select shift if the modifier is not set
|
|
||||||
self.select_shift = None;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
if let Some(ref mut items) = self.items_opt {
|
if let Some(ref mut items) = self.items_opt {
|
||||||
|
|
@ -843,6 +841,11 @@ impl Tab {
|
||||||
if pos == (row, col) {
|
if pos == (row, col) {
|
||||||
// Update focus if this is what we wanted to select
|
// Update focus if this is what we wanted to select
|
||||||
self.select_focus = Some(i);
|
self.select_focus = Some(i);
|
||||||
|
self.select_range = if mod_shift {
|
||||||
|
self.select_range.map(|r| (r.0, i))
|
||||||
|
} else {
|
||||||
|
Some((i, i))
|
||||||
|
};
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
item.selected = true;
|
item.selected = true;
|
||||||
|
|
@ -919,9 +922,9 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_shift_pos_opt(&self) -> Option<(usize, usize)> {
|
fn select_range_start_pos_opt(&self) -> Option<(usize, usize)> {
|
||||||
let items = self.items_opt.as_ref()?;
|
let items = self.items_opt.as_ref()?;
|
||||||
let item = items.get(self.select_shift?)?;
|
let item = items.get(self.select_range.map(|r| r.0)?)?;
|
||||||
item.pos_opt.get()
|
item.pos_opt.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1004,6 +1007,7 @@ impl Tab {
|
||||||
if mod_ctrl {
|
if mod_ctrl {
|
||||||
if Some(i) == click_i_opt && item.selected {
|
if Some(i) == click_i_opt && item.selected {
|
||||||
item.selected = false;
|
item.selected = false;
|
||||||
|
self.select_range = None;
|
||||||
}
|
}
|
||||||
} else if Some(i) != click_i_opt {
|
} else if Some(i) != click_i_opt {
|
||||||
item.selected = false;
|
item.selected = false;
|
||||||
|
|
@ -1044,43 +1048,64 @@ impl Tab {
|
||||||
if click_i_opt.is_none() {
|
if click_i_opt.is_none() {
|
||||||
self.clicked = click_i_opt;
|
self.clicked = click_i_opt;
|
||||||
}
|
}
|
||||||
let dont_unset = mod_ctrl
|
|
||||||
|| mod_shift
|
if mod_shift {
|
||||||
|| self.column_sort().is_some_and(|l| {
|
if let Some(click_i) = click_i_opt {
|
||||||
l.iter()
|
self.select_range = self
|
||||||
.any(|(e_i, e)| Some(e_i) == click_i_opt.as_ref() && e.selected)
|
.select_range
|
||||||
});
|
.map_or(Some((click_i, click_i)), |r| Some((r.0, click_i)));
|
||||||
*self.cached_selected.borrow_mut() = None;
|
if let Some(range) = self.select_range {
|
||||||
if let Some(ref mut items) = self.items_opt {
|
let min = range.0.min(range.1);
|
||||||
for (i, item) in items.iter_mut().enumerate() {
|
let max = range.0.max(range.1);
|
||||||
if Some(i) == click_i_opt {
|
if let Some(ref mut items) = self.items_opt {
|
||||||
// Filter out selection if it does not match dialog kind
|
for (i, item) in items.iter_mut().enumerate() {
|
||||||
if let Some(dialog) = &self.dialog {
|
item.selected = i >= min && i <= max;
|
||||||
let item_is_dir = item.metadata.is_dir();
|
|
||||||
if item_is_dir != dialog.is_dir() {
|
|
||||||
// Allow selecting folder if dialog is for files to make it
|
|
||||||
// possible to double click
|
|
||||||
//TODO: clear any other selection when selecting a folder
|
|
||||||
if !item_is_dir {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !item.selected {
|
}
|
||||||
self.clicked = click_i_opt;
|
self.clicked = click_i_opt;
|
||||||
item.selected = true;
|
self.select_focus = click_i_opt;
|
||||||
}
|
self.selected_clicked = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let dont_unset = mod_ctrl
|
||||||
|
|| self.column_sort().is_some_and(|l| {
|
||||||
|
l.iter()
|
||||||
|
.any(|(e_i, e)| Some(e_i) == click_i_opt.as_ref() && e.selected)
|
||||||
|
});
|
||||||
|
*self.cached_selected.borrow_mut() = None;
|
||||||
|
if let Some(ref mut items) = self.items_opt {
|
||||||
|
for (i, item) in items.iter_mut().enumerate() {
|
||||||
|
if Some(i) == click_i_opt {
|
||||||
|
// Filter out selection if it does not match dialog kind
|
||||||
|
if let Some(dialog) = &self.dialog {
|
||||||
|
let item_is_dir = item.metadata.is_dir();
|
||||||
|
if item_is_dir != dialog.is_dir() {
|
||||||
|
// Allow selecting folder if dialog is for files to make it
|
||||||
|
// possible to double click
|
||||||
|
//TODO: clear any other selection when selecting a folder
|
||||||
|
if !item_is_dir {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !item.selected {
|
||||||
|
self.clicked = click_i_opt;
|
||||||
|
item.selected = true;
|
||||||
|
self.select_range = Some((i, i));
|
||||||
|
}
|
||||||
|
|
||||||
self.selected_clicked = true;
|
self.selected_clicked = true;
|
||||||
} else if !dont_unset && item.selected {
|
} else if !dont_unset && item.selected {
|
||||||
self.clicked = click_i_opt;
|
self.clicked = click_i_opt;
|
||||||
item.selected = false;
|
item.selected = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if self.select_focus.take().is_some() {
|
||||||
if self.select_focus.take().is_some() {
|
// Unfocus currently focused button
|
||||||
// Unfocus currently focused button
|
commands.push(Command::FocusButton(widget::Id::unique()));
|
||||||
commands.push(Command::FocusButton(widget::Id::unique()));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Config(config) => {
|
Message::Config(config) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue