Merge branch 'undo-redo'

This commit is contained in:
2026-08-17 09:26:23 +02:00
5 changed files with 779 additions and 2 deletions
+52
View File
@@ -256,6 +256,11 @@ fn reset_view_state(window: &AppWindow) {
window.set_flip_h(false);
window.set_flip_v(false);
window.set_framing_modified(false);
// A photograph that failed to decode has no session, so nothing below
// will speak for it — and the buttons would otherwise keep offering the
// previous image's history.
window.set_can_undo(false);
window.set_can_redo(false);
}
/// Push the framing back to the geometry panel.
@@ -961,6 +966,15 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
Rc::new(move |window: &AppWindow, draft: bool| {
let mut slot = session.borrow_mut();
let Some(s) = slot.as_mut() else { return };
// TRACES: FR-DEV-5
// Whether undo has anywhere to go, pushed from here because every
// edit ends in a redraw and nothing else is on all of their paths:
// the parameter callbacks sync rows, the framing ones sync the
// geometry panel, and a paste arrives through neither.
window.set_can_undo(s.can_undo());
window.set_can_redo(s.can_redo());
let (mut w, mut h) = *viewport.borrow();
// **Half resolution while the gesture is still moving.**
@@ -1505,6 +1519,44 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
});
}
// ---- undo and redo (FR-DEV-5) ---------------------------------------
//
// Thin, because the history lives in the session and every mutator there
// records into it — see `DevelopSession::history`. What is left for the
// interface is the refresh a paste also needs: the controls are showing
// values that have just moved underneath them.
//
// `can-undo` and `can-redo` are not set here; `render_now` pushes them on
// every redraw, which is every path that can change them.
{
let weak = window.as_weak();
let session = session.clone();
let redraw = redraw.clone();
let rows = rows.clone();
window.on_undo(move || {
let Some(w) = weak.upgrade() else { return };
let stepped = session.borrow_mut().as_mut().is_some_and(|s| s.undo());
if stepped {
sync_rows(&w, &rows, &session);
redraw(&w);
}
});
}
{
let weak = window.as_weak();
let session = session.clone();
let redraw = redraw.clone();
let rows = rows.clone();
window.on_redo(move || {
let Some(w) = weak.upgrade() else { return };
let stepped = session.borrow_mut().as_mut().is_some_and(|s| s.redo());
if stepped {
sync_rows(&w, &rows, &session);
redraw(&w);
}
});
}
// ---- zoom, pan and crop ---------------------------------------------
//
// Zoom and pan are viewing state and touch no parameter, so unlike the