diff --git a/src/app.rs b/src/app.rs index 2fd34c3..e6836a7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -578,6 +578,7 @@ pub enum DialogPage { to: tab::Item, multiple: bool, apply_to_all: bool, + conflict_count: usize, tx: mpsc::Sender, }, SetExecutableAndLaunch { @@ -5857,6 +5858,7 @@ impl Application for App { to, multiple, apply_to_all, + conflict_count, tx, } => { let military_time = self.config.tab.military_time; @@ -5881,13 +5883,18 @@ impl Application for App { if *multiple { dialog .control( - widget::checkbox(fl!("apply-to-all"), *apply_to_all).on_toggle( + widget::checkbox( + format!("{} ({})" ,fl!("apply-to-all"), *conflict_count), + *apply_to_all, + ) + .on_toggle( |apply_to_all| { Message::DialogUpdate(DialogPage::Replace { from: from.clone(), to: to.clone(), multiple: *multiple, apply_to_all, + conflict_count: *conflict_count, tx: tx.clone(), }) }, diff --git a/src/operation/mod.rs b/src/operation/mod.rs index 0669624..8b926af 100644 --- a/src/operation/mod.rs +++ b/src/operation/mod.rs @@ -32,6 +32,7 @@ async fn handle_replace( file_from: PathBuf, file_to: PathBuf, multiple: bool, + conflict_count: usize, ) -> ReplaceResult { let item_from = match tab::item_from_path(file_from, IconSizes::default()) { Ok(ok) => ok, @@ -59,6 +60,7 @@ async fn handle_replace( to: item_to, multiple, apply_to_all: false, + conflict_count, tx, }, Some(REPLACE_BUTTON_ID.clone()), @@ -180,9 +182,9 @@ async fn copy_or_move( { let msg_tx = msg_tx.clone(); - context = context.on_replace(move |op| { + context = context.on_replace(move |op, conflict_count| { let msg_tx = msg_tx.clone(); - Box::pin(handle_replace(msg_tx, op.from.clone(), op.to.clone(), true)) + Box::pin(handle_replace(msg_tx, op.from.clone(), op.to.clone(), true, conflict_count)) }); } diff --git a/src/operation/recursive.rs b/src/operation/recursive.rs index 076a4b2..a807594 100644 --- a/src/operation/recursive.rs +++ b/src/operation/recursive.rs @@ -23,17 +23,18 @@ pub struct Context { on_replace: Pin>, pub(crate) op_sel: OperationSelection, replace_result_opt: Option, + remaining_conflicts: usize, } pub trait OnProgress: Fn(&Op, &Progress) + 'static {} impl OnProgress for F where F: Fn(&Op, &Progress) + 'static {} pub trait OnReplace: - for<'a> Fn(&'a Op) -> Pin + 'a>> + 'static + for<'a> Fn(&'a Op, usize) -> Pin + 'a>> + 'static { } impl OnReplace for F where - F: for<'a> Fn(&'a Op) -> Pin + 'a>> + 'static + F: for<'a> Fn(&'a Op, usize) -> Pin + 'a>> + 'static { } @@ -44,9 +45,10 @@ impl Context { buf: vec![0u8; 128 * 1024], controller, on_progress: Box::new(|_op, _progress| {}), - on_replace: Box::pin(|_op| Box::pin(async { ReplaceResult::Cancel })), + on_replace: Box::pin(|_op, _count| Box::pin(async { ReplaceResult::Cancel })), op_sel: OperationSelection::default(), replace_result_opt: None, + remaining_conflicts: 0, } } @@ -156,6 +158,17 @@ impl Context { cleanup_ops.reverse(); ops.append(&mut cleanup_ops); + // Count potential conflicts (files that would need replacement) + self.remaining_conflicts = ops + .iter() + .filter(|op| { + matches!( + op.kind, + OpKind::Copy | OpKind::Move { .. } | OpKind::Symlink { .. } + ) && op.to.is_file() + }) + .count(); + let total_ops = ops.len(); for (current_ops, mut op) in ops.into_iter().enumerate() { self.controller @@ -221,7 +234,7 @@ impl Context { async fn replace(&mut self, op: &Op) -> Result, Box> { let replace_result = match self.replace_result_opt { Some(result) => result, - None => (self.on_replace)(op).await, + None => (self.on_replace)(op, self.remaining_conflicts).await, }; match replace_result {