Turn the ratio lock with the photograph when it is turned

A quarter turn carries the crop with it — that is what makes turning a
photograph keep its composition rather than sliding the selection onto a
different part of the picture. So a rect locked to 16:9 comes out of the
turn at 9:16, of a frame whose axes have also swapped, and the lock was
left claiming landscape over a portrait rect. The next drag would then
snap it back upright and undo what the turn had just done.

The orientation switch now turns with it, on odd numbers of quarters.

`Original` is deliberately excluded, and getting that wrong flips it
twice: it is resolved against the framed size every time it is asked
for, and the turn has already swapped that frame's axes — so it has
turned by the time anything asks. `turns_with_the_frame` is the one
predicate that separates the two cases, with a test that pins both.

`CropAspect` arrived without tests of its own; it has them now, including
the round trip this fixes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-29 13:49:20 +02:00
co-authored by Claude Opus 5
parent 3bb68cff69
commit 52c655e6cf
3 changed files with 148 additions and 23 deletions
+12
View File
@@ -2636,11 +2636,23 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
let weak = window.as_weak();
let session = session.clone();
let redraw = redraw.clone();
let crop_aspect = crop_aspect.clone();
let crop_portrait = crop_portrait.clone();
window.on_rotate_quarters(move |turns| {
let Some(w) = weak.upgrade() else { return };
if let Some(s) = session.borrow_mut().as_mut() {
s.rotate_quarters(turns);
}
// TRACES: FR-DEV-3
// The crop travels with the frame, so a rect locked to 16:9 comes
// out of the turn at 9:16. The switch has to agree, or the next
// drag would snap it back upright and undo what the turn did to
// the composition. An even number of turns lands where it started.
let aspect = crop_aspect.get();
if aspect.turns_with_the_frame() && turns.rem_euclid(2) != 0 {
crop_portrait.set(!crop_portrait.get());
sync_crop_aspect(&w, &crop_aspect, &crop_portrait);
}
sync_framing(&w, &session);
redraw(&w);
});