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.