From f632c1b0ca5b172811b4b515083806201fddca5b Mon Sep 17 00:00:00 2001 From: komo1iddin <45395275+komo1iddin@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:26:39 +0800 Subject: [PATCH] M7: a resized page image stays blanked where the raster still has it Two cases kept the old picture on screen after a move or resize: - A page that had no tiles when the edit applied was taken as drawn again as soon as a first tile arrived, while its backdrop still had the picture. Out of date now means its whole-page raster or any tile still waits for a replacement. - A second drag before the page was drawn again took the picture's pixels from its new frame, where the raster doesn't have it, and blanked only that. It now takes them from where the raster still has the picture and blanks that, and keeps it as the source on release. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/canvas.rs | 10 +++------- crates/ui-egui/src/edit_text_ui.rs | 13 +++++++------ crates/ui-egui/tests/editing.rs | 21 ++++++++++++++++++++- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 20b7a0a2ea..13ff04ec4d 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -1046,14 +1046,10 @@ impl DocView { self.pages.get(&page).map(|p| p.tex.id()) } - /// Whether `page` is still drawn as it was before its last change: its raster, or on a tiled - /// page any of its tiles, is waiting for a replacement. + /// Whether `page` is still drawn, somewhere, as it was before its last change: its whole-page + /// raster (a tiled page's backdrop too) or any of its tiles waits for a replacement. pub(crate) fn page_out_of_date(&self, page: usize) -> bool { - if self.tiles.keys().any(|k| k.0 == page) { - self.stale_tiles.iter().any(|k| k.0 == page) - } else { - self.pages.get(&page).is_some_and(|p| p.tag == STALE_TAG) - } + self.pages.get(&page).is_some_and(|p| p.tag == STALE_TAG) || self.stale_tiles.iter().any(|k| k.0 == page) } /// The print preview's picture of `page`: the sheet raster when it has arrived, otherwise the diff --git a/crates/ui-egui/src/edit_text_ui.rs b/crates/ui-egui/src/edit_text_ui.rs index 5b5a6445f8..adfbc675ae 100644 --- a/crates/ui-egui/src/edit_text_ui.rs +++ b/crates/ui-egui/src/edit_text_ui.rs @@ -722,12 +722,13 @@ pub(crate) fn image_input( // Until the page is rendered again its raster has an applied move or resize's picture // where it was: it stays blanked there and drawn where it went. let (out_of_date, raster) = (view.page_out_of_date(page), view.page_raster(page)); + let screen = |r: Rect| Rect::from_two_pos(xf.norm_to_screen(r.min.x, r.min.y), xf.norm_to_screen(r.max.x, r.max.y)); + // Where the raster on screen has the picture: where it was before an applied move that + // hasn't been rendered yet, else its frame. Another drag starts from there too. + let drawn_at = view.image_selection.as_ref().and_then(|s| s.settled).filter(|_| out_of_date).map(|(from, _)| from); if let Some(s) = view.image_selection.as_mut().filter(|s| s.drag.is_none()) { match (s.settled, raster) { - (Some((from, to)), Some(tex)) if out_of_date => { - let screen = |r: Rect| Rect::from_two_pos(xf.norm_to_screen(r.min.x, r.min.y), xf.norm_to_screen(r.max.x, r.max.y)); - stretched_picture(painter, xf, tex, screen(from), screen(to)); - } + (Some((from, to)), Some(tex)) if out_of_date => stretched_picture(painter, xf, tex, screen(from), screen(to)), _ => s.settled = None, } } @@ -788,7 +789,7 @@ pub(crate) fn image_input( if preview != b && let Some(tex) = view.page_raster(page) { - stretched_picture(painter, xf, tex, b, preview); + stretched_picture(painter, xf, tex, drawn_at.map_or(b, screen), preview); } painter.rect_filled(visible, CornerRadius::ZERO, ACCENT.gamma_multiply(0.04)); painter.rect_stroke(visible, CornerRadius::ZERO, Stroke::new(1.5, ACCENT), egui::StrokeKind::Outside); @@ -801,7 +802,7 @@ pub(crate) fn image_input( let ((u0, v0), (u1, v1)) = (xf.screen_to_norm(r.min), xf.screen_to_norm(r.max)); Rect::from_two_pos(Pos2::new(u0, v0), Pos2::new(u1, v1)) }; - s.settled = (preview != b).then(|| (norm(b), norm(preview))); + s.settled = (preview != b).then(|| (drawn_at.unwrap_or_else(|| norm(b)), norm(preview))); } if preview != b { view.pending_edit = diff --git a/crates/ui-egui/tests/editing.rs b/crates/ui-egui/tests/editing.rs index 4a386372ec..67d22f77ae 100644 --- a/crates/ui-egui/tests/editing.rs +++ b/crates/ui-egui/tests/editing.rs @@ -4526,11 +4526,30 @@ fn a_resized_image_keeps_its_new_size_until_the_page_is_redrawn() { assert!(red(&pixels, kept) > 100, "frame {frame} after the release: the picture is not at its new size"); assert_eq!(red(&pixels, gone), 0, "frame {frame} after the release: the picture shows at its old size"); } + // Resized again before the page is drawn: the raster still has the first size, so that is + // what stays blanked while the corner moves. + let (grip, to) = (to, at(80.0, 60.0)); + let again = egui::Rect::from_two_pos(at(62.0, 42.0), at(78.0, 58.0)); + h.hover_at(grip); + h.run_steps(1); + h.drag_at(grip); + h.run_steps(1); + for k in 1..=4 { + h.hover_at(grip + (to - grip) * (k as f32 / 4.0)); + h.run_steps(1); + } + let pixels = h.render().unwrap(); + assert!(red(&pixels, again) > 20, "the second drag draws the picture at its new size"); + assert_eq!(red(&pixels, gone), 0, "the second drag shows the picture at its first size"); + h.drop_at(to); + h.run_steps(2); + let pixels = h.render().unwrap(); + assert_eq!(red(&pixels, gone), 0, "after the second release"); h.state_mut().views[0].hold_rasters = false; support::wait_until(&mut h, "the page drawn with the resized picture", |app| !app.render_pending()); h.run_steps(2); let pixels = h.render().unwrap(); - assert!(red(&pixels, kept) > 100 && red(&pixels, gone) == 0, "the page raster has the picture at its new size"); + assert!(red(&pixels, again) > 20 && red(&pixels, gone) == 0, "the page raster has the picture at its new size"); } /// Exercise actual pointer gestures on each cropped placement, without capturing private pixels.