diff --git a/src/app.rs b/src/app.rs index adcb10d..2800dc4 100644 --- a/src/app.rs +++ b/src/app.rs @@ -31,6 +31,7 @@ use cosmic::{ window::{self, Event as WindowEvent, Id as WindowId}, }, iced_runtime::clipboard, + iced_widget::button::focus, style, surface, theme, widget::{ self, @@ -58,7 +59,7 @@ use std::{ path::{Path, PathBuf}, pin::Pin, process, - sync::{Arc, Mutex}, + sync::{Arc, LazyLock, Mutex}, time::{self, Duration, Instant}, }; use tokio::sync::mpsc; @@ -91,6 +92,9 @@ use crate::{ }; use crate::{config::State, dialog::DialogSettings}; +static PERMANENT_DELETE_BUTTON_ID: LazyLock = + LazyLock::new(|| widget::Id::new("permanent-delete-button")); + #[derive(Clone, Debug)] pub enum Mode { App, @@ -315,7 +319,7 @@ pub enum Message { DialogComplete, Eject, FileDialogMessage(DialogMessage), - DialogPush(DialogPage), + DialogPush(DialogPage, Option), DialogUpdate(DialogPage), DialogUpdateComplete(DialogPage), ExtractHere(Option), @@ -694,6 +698,15 @@ pub struct App { } impl App { + fn push_dialog(&mut self, page: DialogPage, focus_id: Option) -> Task { + let t = self.dialog_pages.push_back(page); + if let Some(focus_id) = focus_id { + Task::batch(vec![t, focus(focus_id)]) + } else { + t + } + } + fn open_file(&mut self, paths: &[impl AsRef]) -> Task { let mut tasks = Vec::new(); @@ -1091,9 +1104,12 @@ impl App { let mut tasks = Vec::new(); if !dialog_paths.is_empty() { - tasks.push(self.dialog_pages.push_back(DialogPage::PermanentlyDelete { - paths: dialog_paths, - })); + tasks.push(self.update(Message::DialogPush( + DialogPage::PermanentlyDelete { + paths: dialog_paths, + }, + Some(PERMANENT_DELETE_BUTTON_ID.clone()), + ))); } if !trash_paths.is_empty() { tasks.push(self.operation(Operation::Delete { paths: trash_paths })); @@ -2855,8 +2871,8 @@ impl Application for App { return Task::batch(tasks); } } - Message::DialogPush(dialog_page) => { - return self.dialog_pages.push_back(dialog_page); + Message::DialogPush(dialog_page, focused_id) => { + return self.push_dialog(dialog_page, focused_id); } Message::DialogUpdate(dialog_page) => { self.dialog_pages.update_front(dialog_page); @@ -3342,17 +3358,20 @@ impl Application for App { let Some(path) = item.path_opt() else { continue; }; - return self.update(Message::DialogPush(DialogPage::OpenWith { - path: path.to_path_buf(), - mime: item.mime.clone(), - selected: 0, - store_opt: "x-scheme-handler/mime" - .parse::() - .ok() - .and_then(|mime| { - self.mime_app_cache.get(&mime).first().cloned() - }), - })); + return self.push_dialog( + DialogPage::OpenWith { + path: path.to_path_buf(), + mime: item.mime.clone(), + selected: 0, + store_opt: "x-scheme-handler/mime" + .parse::() + .ok() + .and_then(|mime| { + self.mime_app_cache.get(&mime).first().cloned() + }), + }, + None, // TODO which id to focus? + ); } } } @@ -3530,9 +3549,10 @@ impl Application for App { Message::PermanentlyDelete(entity_opt) => { let paths = self.selected_paths(entity_opt); if !paths.is_empty() { - return self - .dialog_pages - .push_back(DialogPage::PermanentlyDelete { paths }); + return self.push_dialog( + DialogPage::PermanentlyDelete { paths }, + Some(PERMANENT_DELETE_BUTTON_ID.clone()), + ); } } Message::Preview(entity_opt) => { @@ -4404,17 +4424,20 @@ impl Application for App { { match tab::item_from_path(&path, IconSizes::default()) { Ok(item) => { - return self.update(Message::DialogPush(DialogPage::OpenWith { - path: path.to_path_buf(), - mime: item.mime.clone(), - selected: 0, - store_opt: "x-scheme-handler/mime" - .parse::() - .ok() - .and_then(|mime| { - self.mime_app_cache.get(&mime).first().cloned() - }), - })); + return self.push_dialog( + DialogPage::OpenWith { + path: path.to_path_buf(), + mime: item.mime.clone(), + selected: 0, + store_opt: "x-scheme-handler/mime" + .parse::() + .ok() + .and_then(|mime| { + self.mime_app_cache.get(&mime).first().cloned() + }), + }, + None, + ); } Err(err) => { log::warn!("failed to get item for path {:?}: {}", path, err); @@ -5283,7 +5306,8 @@ impl Application for App { .title(fl!("permanently-delete-question")) .primary_action( widget::button::destructive(fl!("delete")) - .on_press(Message::DialogComplete), + .on_press(Message::DialogComplete) + .id(PERMANENT_DELETE_BUTTON_ID.clone()), ) .secondary_action( widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), diff --git a/src/operation/mod.rs b/src/operation/mod.rs index 7d7e525..2f8b1ed 100644 --- a/src/operation/mod.rs +++ b/src/operation/mod.rs @@ -53,13 +53,16 @@ async fn handle_replace( let _ = msg_tx .lock() .await - .send(Message::DialogPush(DialogPage::Replace { - from: item_from, - to: item_to, - multiple, - apply_to_all: false, - tx, - })) + .send(Message::DialogPush( + DialogPage::Replace { + from: item_from, + to: item_to, + multiple, + apply_to_all: false, + tx, + }, + None, // TODO which widget to focus? + )) .await; rx.recv().await.unwrap_or(ReplaceResult::Cancel) } @@ -1198,7 +1201,7 @@ mod tests { let handle_messages = async move { while let Some(msg) = rx.next().await { match msg { - Message::DialogPush(DialogPage::Replace { tx, .. }) => { + Message::DialogPush(DialogPage::Replace { tx, .. }, _id_to_focus) => { debug!("[{id}] Replace request"); tx.send(ReplaceResult::Cancel) .await