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:
2026-03-14 21:41:11 +02:00
parent d84274db2e
commit fe491ec427
9 changed files with 116 additions and 39 deletions

View File

@@ -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(),

View File

@@ -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);
}

View File

@@ -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();
}