Improve keyboard focus handling

This commit is contained in:
Jeremy Soller 2024-02-29 21:13:03 -07:00
parent 4690225d95
commit d2d0b02afd
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
2 changed files with 88 additions and 9 deletions

View file

@ -99,7 +99,7 @@ impl Action {
Action::Properties => Message::ToggleContextPage(ContextPage::Properties), Action::Properties => Message::ToggleContextPage(ContextPage::Properties),
Action::Rename => Message::Rename(entity_opt), Action::Rename => Message::Rename(entity_opt),
Action::RestoreFromTrash => Message::RestoreFromTrash(entity_opt), Action::RestoreFromTrash => Message::RestoreFromTrash(entity_opt),
Action::SelectAll => Message::SelectAll(entity_opt), Action::SelectAll => Message::TabMessage(entity_opt, tab::Message::SelectAll),
Action::Settings => Message::ToggleContextPage(ContextPage::Settings), Action::Settings => Message::ToggleContextPage(ContextPage::Settings),
Action::TabClose => Message::TabClose(entity_opt), Action::TabClose => Message::TabClose(entity_opt),
Action::TabNew => Message::TabNew, Action::TabNew => Message::TabNew,
@ -142,7 +142,6 @@ pub enum Message {
PendingProgress(u64, f32), PendingProgress(u64, f32),
Rename(Option<segmented_button::Entity>), Rename(Option<segmented_button::Entity>),
RestoreFromTrash(Option<segmented_button::Entity>), RestoreFromTrash(Option<segmented_button::Entity>),
SelectAll(Option<segmented_button::Entity>),
SystemThemeModeChange(cosmic_theme::ThemeMode), SystemThemeModeChange(cosmic_theme::ThemeMode),
TabActivate(segmented_button::Entity), TabActivate(segmented_button::Entity),
TabNext, TabNext,
@ -946,12 +945,6 @@ impl Application for App {
self.operation(Operation::Restore { paths }); self.operation(Operation::Restore { paths });
} }
} }
Message::SelectAll(entity_opt) => {
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
tab.select_all();
}
}
Message::SystemThemeModeChange(_theme_mode) => { Message::SystemThemeModeChange(_theme_mode) => {
return self.update_config(); return self.update_config();
} }

View file

@ -417,6 +417,7 @@ pub enum Message {
Resize(Size), Resize(Size),
RightClick(usize), RightClick(usize),
Scroll(Viewport), Scroll(Viewport),
SelectAll,
Thumbnail(PathBuf, Result<image::RgbaImage, ()>), Thumbnail(PathBuf, Result<image::RgbaImage, ()>),
ToggleShowHidden, ToggleShowHidden,
View(View), View(View),
@ -669,7 +670,7 @@ impl Tab {
pub fn select_name(&mut self, name: &str) { pub fn select_name(&mut self, name: &str) {
if let Some(ref mut items) = self.items_opt { if let Some(ref mut items) = self.items_opt {
for (i, item) in items.iter_mut().enumerate() { for item in items.iter_mut() {
if item.name == name { if item.name == name {
item.selected = true; item.selected = true;
} else { } else {
@ -788,6 +789,64 @@ impl Tab {
item.pos_opt.get() item.pos_opt.get()
} }
fn select_first_pos_opt(&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 select_last_pos_opt(&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
}
pub fn update(&mut self, message: Message, modifiers: Modifiers) -> Vec<Command> { pub fn update(&mut self, message: Message, modifiers: Modifiers) -> Vec<Command> {
let mut commands = Vec::new(); let mut commands = Vec::new();
let mut cd = None; let mut cd = None;
@ -843,6 +902,10 @@ impl Tab {
} }
} }
self.context_menu = None; self.context_menu = None;
if self.select_focus.take().is_some() {
// Unfocus currently focused button
commands.push(Command::FocusButton(widget::Id::unique()));
}
} }
Message::Config(config) => { Message::Config(config) => {
self.config = config; self.config = config;
@ -859,6 +922,10 @@ impl Tab {
Message::Drag(rect_opt) => match rect_opt { Message::Drag(rect_opt) => match rect_opt {
Some(rect) => { Some(rect) => {
self.select_rect(rect); self.select_rect(rect);
if self.select_focus.take().is_some() {
// Unfocus currently focused button
commands.push(Command::FocusButton(widget::Id::unique()));
}
} }
None => {} None => {}
}, },
@ -892,6 +959,9 @@ impl Tab {
// Ensure current item is still selected if there are no other items // Ensure current item is still selected if there are no other items
self.select_position(row, col, mod_shift); self.select_position(row, col, mod_shift);
} }
} else if let Some((row, col)) = self.select_last_pos_opt() {
// Select last item in current selection to focus it
self.select_position(row, col, mod_shift);
} else { } else {
// Select first item // Select first item
//TODO: select first in scroll //TODO: select first in scroll
@ -930,6 +1000,9 @@ impl Tab {
self.select_position(row, col, mod_shift); self.select_position(row, col, mod_shift);
} }
} }
} else if let Some((row, col)) = self.select_first_pos_opt() {
// Select first item in current selection to focus it
self.select_position(row, col, mod_shift);
} else { } else {
// Select first item // Select first item
//TODO: select first in scroll //TODO: select first in scroll
@ -952,6 +1025,9 @@ impl Tab {
self.select_position(row, col, mod_shift); self.select_position(row, col, mod_shift);
} }
} }
} else if let Some((row, col)) = self.select_last_pos_opt() {
// Select last item in current selection to focus it
self.select_position(row, col, mod_shift);
} else { } else {
// Select first item // Select first item
//TODO: select first in scroll //TODO: select first in scroll
@ -975,6 +1051,9 @@ impl Tab {
// Ensure current item is still selected if there are no other items // Ensure current item is still selected if there are no other items
self.select_position(row, col, mod_shift); self.select_position(row, col, mod_shift);
} }
} else if let Some((row, col)) = self.select_first_pos_opt() {
// Select first item in current selection to focus it
self.select_position(row, col, mod_shift);
} else { } else {
// Select first item // Select first item
//TODO: select first in scroll //TODO: select first in scroll
@ -1043,6 +1122,13 @@ impl Tab {
Message::Scroll(viewport) => { Message::Scroll(viewport) => {
self.scroll_opt = Some(viewport.absolute_offset()); self.scroll_opt = Some(viewport.absolute_offset());
} }
Message::SelectAll => {
self.select_all();
if self.select_focus.take().is_some() {
// Unfocus currently focused button
commands.push(Command::FocusButton(widget::Id::unique()));
}
}
Message::Thumbnail(path, thumbnail_res) => { Message::Thumbnail(path, thumbnail_res) => {
if let Some(ref mut items) = self.items_opt { if let Some(ref mut items) = self.items_opt {
for item in items.iter_mut() { for item in items.iter_mut() {