Partially implement arrow keys for grid

This commit is contained in:
Jeremy Soller 2024-02-29 13:24:57 -07:00
parent 27b29e9fd8
commit bcd5da57ab
No known key found for this signature in database
GPG key ID: D02FD439211AF56F

View file

@ -255,6 +255,7 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec<Item> {
Some(mime) if mime.type_() == "image" => None,
_ => Some(Err(())),
},
pos_opt: Cell::new(None),
rect_opt: Cell::new(None),
selected: false,
click_time: None,
@ -336,6 +337,7 @@ pub fn scan_trash(sizes: IconSizes) -> Vec<Item> {
icon_handle_grid,
icon_handle_list,
thumbnail_res_opt: Some(Err(())),
pos_opt: Cell::new(None),
rect_opt: Cell::new(None),
selected: false,
click_time: None,
@ -437,6 +439,7 @@ pub struct Item {
pub icon_handle_grid: widget::icon::Handle,
pub icon_handle_list: widget::icon::Handle,
pub thumbnail_res_opt: Option<Result<image::RgbaImage, ()>>,
pub pos_opt: Cell<Option<(usize, usize)>>,
pub rect_opt: Cell<Option<Rectangle>>,
pub selected: bool,
pub click_time: Option<Instant>,
@ -613,10 +616,87 @@ impl Tab {
}
}
fn selection_first(&self) -> Option<(usize, usize)> {
let items = self.items_opt.as_ref()?;
let mut first = None;
for item in items.iter() {
if !item.selected {
continue;
}
let (row, col) = match item.pos_opt.get() {
Some(some) => some,
None => continue,
};
first = Some(match first {
Some((first_row, first_col)) => {
if row < first_row {
(row, col)
} else if row == first_row {
(row, col.min(first_row))
} else {
(first_row, first_col)
}
}
None => (row, col),
});
}
first
}
fn selection_last(&self) -> Option<(usize, usize)> {
let items = self.items_opt.as_ref()?;
let mut last = None;
for item in items.iter() {
if !item.selected {
continue;
}
let (row, col) = match item.pos_opt.get() {
Some(some) => some,
None => continue,
};
last = Some(match last {
Some((last_row, last_col)) => {
if row > last_row {
(row, col)
} else if row == last_row {
(row, col.max(last_row))
} else {
(last_row, last_col)
}
}
None => (row, col),
});
}
last
}
fn select_position(&mut self, row: usize, col: usize, mod_shift: bool) -> bool {
let mut found = false;
if let Some(ref mut items) = self.items_opt {
for item in items.iter_mut() {
if item.pos_opt.get() == Some((row, col)) {
item.selected = true;
found = true;
} else if !mod_shift {
item.selected = false;
}
}
}
found
}
pub fn update(&mut self, message: Message, modifiers: Modifiers) -> Vec<Command> {
let mut commands = Vec::new();
let mut cd = None;
let mut history_i_opt = None;
let mod_ctrl = modifiers.contains(Modifiers::CTRL)
&& self.dialog.as_ref().map_or(true, |x| x.multiple());
let mod_shift = modifiers.contains(Modifiers::SHIFT)
&& self.dialog.as_ref().map_or(true, |x| x.multiple());
match message {
Message::Click(click_i_opt) => {
if let Some(ref mut items) = self.items_opt {
@ -655,9 +735,7 @@ impl Tab {
}
//TODO: prevent triple-click and beyond from opening file?
item.click_time = Some(Instant::now());
} else if modifiers.contains(Modifiers::CTRL)
&& self.dialog.as_ref().map_or(true, |x| x.multiple())
{
} else if mod_ctrl {
// Holding control allows multiple selection
item.click_time = None;
} else {
@ -708,54 +786,79 @@ impl Tab {
}
}
}
Message::ItemDown | Message::ItemLeft => {
//TODO: handle grid correctly
//TODO: do not wrap
if let Some(ref mut items) = self.items_opt {
let mut last_selected_opt = None;
for (i, item) in items.iter_mut().enumerate() {
if item.selected {
if !modifiers.contains(Modifiers::SHIFT) {
item.selected = false;
}
last_selected_opt = Some(i);
}
}
for (i, item) in items.iter_mut().enumerate() {
if !self.config.show_hidden && item.hidden {
continue;
}
if last_selected_opt.map_or(true, |last_selected| i > last_selected) {
item.selected = true;
break;
}
Message::ItemDown => {
if let Some((row, col)) = self.selection_last() {
//TODO: Shift modifier should select items in between
// Try to select item in next row
if !self.select_position(row + 1, col, mod_shift) {
// Ensure current item is still selected if there are no other items
self.select_position(row, col, mod_shift);
}
} else {
// Select first item
self.select_position(0, 0, mod_shift);
}
}
Message::ItemUp | Message::ItemRight => {
//TODO: handle grid correctly
//TODO: do not wrap
if let Some(ref mut items) = self.items_opt {
let mut last_selected_opt = None;
for (i, item) in items.iter_mut().enumerate().rev() {
if item.selected {
if !modifiers.contains(Modifiers::SHIFT) {
item.selected = false;
Message::ItemLeft => {
if let Some((row, col)) = self.selection_first() {
// Try to select previous item in current row
if !col
.checked_sub(1)
.map_or(false, |col| self.select_position(row, col, mod_shift))
{
// Try to select last item in previous row
if !row.checked_sub(1).map_or(false, |row| {
let mut col = 0;
if let Some(ref items) = self.items_opt {
for item in items.iter() {
match item.pos_opt.get() {
Some((item_row, item_col)) if item_row == row => {
col = col.max(item_col);
}
_ => continue,
}
}
}
last_selected_opt = Some(i);
self.select_position(row, col, mod_shift)
}) {
// Ensure current item is still selected if there are no other items
self.select_position(row, col, mod_shift);
}
}
for (i, item) in items.iter_mut().enumerate().rev() {
if !self.config.show_hidden && item.hidden {
continue;
}
if last_selected_opt.map_or(true, |last_selected| i < last_selected) {
item.selected = true;
break;
} else {
// Select first item
self.select_position(0, 0, mod_shift);
}
}
Message::ItemRight => {
if let Some((row, col)) = self.selection_last() {
// Try to select next item in current row
if !self.select_position(row, col + 1, mod_shift) {
// Try to select first item in next row
if !self.select_position(row + 1, 0, mod_shift) {
// Ensure current item is still selected if there are no other items
self.select_position(row, col, mod_shift);
}
}
} else {
// Select first item
self.select_position(0, 0, mod_shift);
}
}
Message::ItemUp => {
if let Some((row, col)) = self.selection_first() {
//TODO: Shift modifier should select items in between
// Try to select item in last row
if !row
.checked_sub(1)
.map_or(false, |row| self.select_position(row, col, mod_shift))
{
// Ensure current item is still selected if there are no other items
self.select_position(row, col, mod_shift);
}
} else {
// Select first item
self.select_position(0, 0, mod_shift);
}
}
Message::Location(location) => {
@ -801,9 +904,7 @@ impl Tab {
for (i, item) in items.iter_mut().enumerate() {
if i == click_i {
item.selected = true;
} else if modifiers.contains(Modifiers::CTRL)
&& self.dialog.as_ref().map_or(true, |x| x.multiple())
{
} else if mod_ctrl {
// Holding control allows multiple selection
} else {
item.selected = false;
@ -1157,10 +1258,12 @@ impl Tab {
let mut hidden = 0;
for (i, item) in items.iter().enumerate() {
if !show_hidden && item.hidden {
item.pos_opt.set(None);
item.rect_opt.set(None);
hidden += 1;
continue;
}
item.pos_opt.set(Some((row, col)));
item.rect_opt.set(Some(Rectangle::new(
Point::new(
(col * (GRID_ITEM_WIDTH + column_spacing as usize)) as f32,
@ -1308,10 +1411,12 @@ impl Tab {
} = self.config;
for (i, item) in items.iter().enumerate() {
if !show_hidden && item.hidden {
item.pos_opt.set(None);
item.rect_opt.set(None);
hidden += 1;
continue;
}
item.pos_opt.set(Some((count, 0)));
item.rect_opt.set(Some(Rectangle::new(
Point::new(0.0, y as f32),
Size::new(size.width, row_height as f32),