Say so when a crop leaves a mask outside the frame

Cropping tighter past a mask layer made it invisible without a word:
the layer stayed in the panel and the sidecar, and its adjustment went
on landing on pixels nobody would see again.

When a crop is let go, develop now measures what the gesture did to the
mask stack (dr_pipeline::orphan) and, if any layer is now entirely or
mostly outside the frame, shows a notice over the photograph: how many
layers, their names, "Undo crop" and "Keep crop". The crop is already
applied and nothing waits on the answer.

The crop overlay gains a release callback carrying the rect the press
began from, so the measurement runs once per gesture and never on the
drag's per-frame changes. Choosing a ratio is measured the same way,
being a crop committed in one click.

"Undo crop" is the ordinary undo, and the notice is tied to the history
revision it was raised at: the redraw that follows any history move
clears it, so the crop and its warning go back as one step. A second
drag folded into the same step is measured from where that step began.
A crop that strands nothing shows nothing.
This commit is contained in:
2026-09-24 21:52:03 -04:00
parent fc0ea8824d
commit c3d1f83b96
9 changed files with 432 additions and 46 deletions
+10
View File
@@ -607,7 +607,12 @@ fn apply_crop_aspect(
portrait: &Rc<Cell<bool>>,
) {
let Some(s) = session.borrow_mut().as_mut().map(|s| {
let before = s.framing();
s.set_crop_locked(s.crop(), aspect.get(), portrait.get(), (0.5, 0.5));
// TRACES: FR-DEV-17
// A ratio chosen is a crop committed in one click — there is no drag
// to wait for the end of — so it is measured here, as a release is.
s.notice_hidden_masks(&before);
(s.crop(), s.framing_edits_image())
}) else {
return;
@@ -2198,6 +2203,11 @@ fn build_render_now(
steps.set_rows(slint::ModelRc::new(slint::VecModel::from(s.history_rows())));
steps.set_undo_label(s.undo_label().into());
}
// TRACES: FR-DEV-17
// The notice about a crop that stranded a mask, on the path every
// history move takes, so an undo that takes the crop back takes
// the notice with it in the same frame.
develop_ui::sync_crop_notice(window, s);
// TRACES: FR-DEV-5
// The snapshots, on the same path. Rebuilt only when the list
// would read differently, for the reason the steps are: a held