fix numbering, text editor, and selection handling
Separate file/folder numbering by default, with shared numbering option in the numbering rule popup. Text editor now loads names from previous pipeline steps instead of originals, and uses absolute position so it works correctly with any numbering mode. Folders sort before files for consistent ordering. Spacebar works in the text editor textarea without triggering drag.
This commit is contained in:
@@ -49,6 +49,7 @@ pub type Result<T> = std::result::Result<T, NominaError>;
|
||||
pub struct RenameContext {
|
||||
pub index: usize,
|
||||
pub total: usize,
|
||||
pub absolute_index: usize,
|
||||
pub original_name: String,
|
||||
pub extension: String,
|
||||
pub path: PathBuf,
|
||||
@@ -65,6 +66,7 @@ impl RenameContext {
|
||||
Self {
|
||||
index,
|
||||
total: 1,
|
||||
absolute_index: index,
|
||||
original_name: String::new(),
|
||||
extension: String::new(),
|
||||
path: PathBuf::new(),
|
||||
|
||||
@@ -33,14 +33,33 @@ impl Pipeline {
|
||||
self.steps.push(PipelineStep { rule, mode });
|
||||
}
|
||||
|
||||
pub fn preview(&self, files: &[crate::FileEntry]) -> Vec<PreviewResult> {
|
||||
pub fn preview(&self, files: &[crate::FileEntry], shared_numbering: bool) -> Vec<PreviewResult> {
|
||||
// precompute per-type counts and indices when using separate numbering
|
||||
let dir_count = if shared_numbering { 0 } else { files.iter().filter(|f| f.is_dir).count() };
|
||||
let file_count = if shared_numbering { 0 } else { files.iter().filter(|f| !f.is_dir).count() };
|
||||
let mut dir_idx: usize = 0;
|
||||
let mut file_idx: usize = 0;
|
||||
|
||||
let results: Vec<PreviewResult> = files
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, file)| {
|
||||
let (index, total) = if shared_numbering {
|
||||
(i, files.len())
|
||||
} else if file.is_dir {
|
||||
let idx = dir_idx;
|
||||
dir_idx += 1;
|
||||
(idx, dir_count)
|
||||
} else {
|
||||
let idx = file_idx;
|
||||
file_idx += 1;
|
||||
(idx, file_count)
|
||||
};
|
||||
|
||||
let ctx = RenameContext {
|
||||
index: i,
|
||||
total: files.len(),
|
||||
index,
|
||||
total,
|
||||
absolute_index: i,
|
||||
original_name: file.name.clone(),
|
||||
extension: file.extension.clone(),
|
||||
path: file.path.clone(),
|
||||
@@ -183,7 +202,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let files = vec![make_file("IMG_001.jpg"), make_file("IMG_002.jpg")];
|
||||
let results = pipeline.preview(&files);
|
||||
let results = pipeline.preview(&files, true);
|
||||
assert_eq!(results[0].new_name, "photo-001.jpg");
|
||||
assert_eq!(results[1].new_name, "photo-002.jpg");
|
||||
}
|
||||
@@ -215,7 +234,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let files = vec![make_file("IMG_001.jpg")];
|
||||
let results = pipeline.preview(&files);
|
||||
let results = pipeline.preview(&files, true);
|
||||
assert_eq!(results[0].new_name, "PHOTO-001.jpg");
|
||||
}
|
||||
|
||||
@@ -238,7 +257,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let files = vec![make_file("same.txt"), make_file("same.txt")];
|
||||
let results = pipeline.preview(&files);
|
||||
let results = pipeline.preview(&files, true);
|
||||
assert!(results[0].has_conflict);
|
||||
assert!(results[1].has_conflict);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ impl TextEditorRule {
|
||||
|
||||
impl RenameRule for TextEditorRule {
|
||||
fn apply(&self, filename: &str, context: &RenameContext) -> String {
|
||||
if let Some(name) = self.names.get(context.index) {
|
||||
if let Some(name) = self.names.get(context.absolute_index) {
|
||||
if !name.is_empty() {
|
||||
return name.clone();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user