Caption a photograph with its name, not with how it is stored
A grid cell is about four words wide and `.CR2` spent one of them saying something the photographer already knows: in a RAW library every frame ends the same way, so the extension distinguishes nothing while taking room from the part that does. The caption elides from the end under pressure, so an extension can push the digits that actually identify a frame off the visible part of its own label. Dropped in the view, not in the model. `LibraryCell::name` keeps the true filename and `remote_path` the full path, because both are used to find the file again and a stem is not a filename. The case it is wrong for, recorded rather than discovered later: a library holding `IMG_1234.CR2` beside `IMG_1234.JPG` now shows two cells captioned `IMG_1234`. They remain two rows with two thumbnails and two entries in the info panel, and a RAW+JPEG pair is usually one photograph anyway — but the caption alone no longer separates them. Only the last dot goes, and only when something precedes it: `2026.08.23-a.dng` keeps its dates, and `.hidden` keeps its leading dot, because that dot is how the name starts rather than an extension. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2008,7 +2008,7 @@ fn load_window(window: &AppWindow, ctl: &Rc<LibraryController>) {
|
||||
period_heading: heading.into(),
|
||||
// A freshly loaded window has no drag in flight.
|
||||
lifted: false,
|
||||
name: c.name.as_str().into(),
|
||||
name: without_extension(&c.name).into(),
|
||||
thumbnail: carried.map(|h| h.thumbnail.clone()).unwrap_or_default(),
|
||||
has_thumb: carried.is_some_and(|h| h.has_thumb),
|
||||
unavailable: carried.is_some_and(|h| h.unavailable),
|
||||
@@ -5500,3 +5500,53 @@ mod tests {
|
||||
assert_eq!(paths, vec!["a.CR2", "b.CR2"]);
|
||||
}
|
||||
}
|
||||
|
||||
/// TRACES: FR-UI-2
|
||||
/// A filename as a caption: without the part that says how it is stored.
|
||||
///
|
||||
/// A grid cell is about four words wide, and `.CR2` spends one of them saying
|
||||
/// something the photographer already knows — every frame in a RAW library
|
||||
/// ends the same way, so the extension distinguishes nothing while taking
|
||||
/// room from the part that does. The name is elided under pressure, and it is
|
||||
/// the *end* that goes, so an extension can push the digits that identify a
|
||||
/// frame off the visible part of its own label.
|
||||
///
|
||||
/// Display only. `LibraryCell::name` keeps the true filename and
|
||||
/// `remote_path` the full path, because both are used to find the file again
|
||||
/// and a stem is not a filename.
|
||||
///
|
||||
/// **The case this is wrong for**, worth knowing before it is reported: a
|
||||
/// library holding `IMG_1234.CR2` beside `IMG_1234.JPG` shows two cells
|
||||
/// captioned `IMG_1234`. They are still two rows with two thumbnails and two
|
||||
/// entries in the info panel, and RAW+JPEG pairs are usually shot to be one
|
||||
/// photograph anyway — but the caption alone no longer separates them.
|
||||
fn without_extension(name: &str) -> &str {
|
||||
match name.rsplit_once('.') {
|
||||
// The guard is for a leading dot: `.hidden` splits to an empty stem,
|
||||
// and a dotfile's name starts with that dot rather than ending with an
|
||||
// extension. A *trailing* dot needs no guard — `odd.` splits to `odd`,
|
||||
// which is the better caption anyway.
|
||||
Some((stem, _)) if !stem.is_empty() => stem,
|
||||
_ => name,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod display_name_tests {
|
||||
use super::without_extension;
|
||||
|
||||
#[test]
|
||||
fn an_extension_is_dropped_and_nothing_else_is() {
|
||||
assert_eq!(without_extension("IMG_1234.CR2"), "IMG_1234");
|
||||
assert_eq!(without_extension("IMG_1234.jpeg"), "IMG_1234");
|
||||
// Only the last one: a name can contain dots and they are part of it.
|
||||
assert_eq!(without_extension("2026.08.23-a.dng"), "2026.08.23-a");
|
||||
// Nothing to drop.
|
||||
assert_eq!(without_extension("IMG_1234"), "IMG_1234");
|
||||
// A dotfile is not an extensionless name with an extension.
|
||||
assert_eq!(without_extension(".hidden"), ".hidden");
|
||||
// A trailing dot is an empty extension, and dropping it is right.
|
||||
assert_eq!(without_extension("odd."), "odd");
|
||||
assert_eq!(without_extension(""), "");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user