mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 11:37:59 -06:00
Edit text & images: a dragged image's pixels follow its frame instead of waiting for the release (part of #1671) (#1677)
This commit is contained in:
@@ -888,6 +888,11 @@ impl DocView {
|
||||
self.thumbs.get(&page).map(|t| t.tex.id())
|
||||
}
|
||||
|
||||
/// The whole-page raster on screen for `page` (the low-res backdrop of a tiled page), if any.
|
||||
pub(crate) fn page_raster(&self, page: usize) -> Option<egui::TextureId> {
|
||||
self.pages.get(&page).map(|p| p.tex.id())
|
||||
}
|
||||
|
||||
/// The print preview's picture of `page`: the sheet raster when it has arrived, otherwise the
|
||||
/// thumbnail.
|
||||
pub(crate) fn page_preview(&self, page: usize) -> Option<egui::TextureId> {
|
||||
|
||||
@@ -527,6 +527,28 @@ pub(crate) fn image_input(
|
||||
let target = Rect::from_min_max(preview.min + (raw.min - b.min) * factor, preview.min + (raw.max - b.min) * factor);
|
||||
let visible = preview.intersect(xf.rect);
|
||||
if visible.is_positive() {
|
||||
// The pixels follow the frame: the page raster's picture of the image, stretched
|
||||
// into the preview over a blank where it was. Re-rendering every frame of a drag
|
||||
// would lag; the edit renders it properly on release.
|
||||
if preview != b
|
||||
&& let Some(tex) = view.page_raster(page)
|
||||
{
|
||||
let clipped = painter.with_clip_rect(painter.clip_rect().intersect(xf.rect));
|
||||
clipped.rect_filled(b, CornerRadius::ZERO, Color32::WHITE);
|
||||
let mut mesh = egui::Mesh::with_texture(tex);
|
||||
for (from, to) in [
|
||||
(b.left_top(), preview.left_top()),
|
||||
(b.right_top(), preview.right_top()),
|
||||
(b.right_bottom(), preview.right_bottom()),
|
||||
(b.left_bottom(), preview.left_bottom()),
|
||||
] {
|
||||
let (u, v) = xf.screen_to_norm(from);
|
||||
mesh.vertices.push(egui::epaint::Vertex { pos: to, uv: Pos2::new(u, v), color: Color32::WHITE });
|
||||
}
|
||||
mesh.add_triangle(0, 1, 2);
|
||||
mesh.add_triangle(0, 2, 3);
|
||||
clipped.add(egui::Shape::mesh(mesh));
|
||||
}
|
||||
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);
|
||||
handles(preview);
|
||||
|
||||
@@ -3929,6 +3929,74 @@ fn cropped_image_corner_resize_keeps_opposite_corner_and_scales_crop() {
|
||||
assert_eq!(reopened.get(re).unwrap().page_images(0), h.state().session.get(id).unwrap().page_images(0));
|
||||
}
|
||||
|
||||
/// While a corner is dragged, the image's pixels are drawn in the blue frame, not left at their
|
||||
/// old size until release (#1671).
|
||||
#[test]
|
||||
fn image_pixels_follow_the_frame_while_a_corner_is_dragged() {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 1100.0)).build_eframe(|_cc| {
|
||||
let mut app = PdfCraftApp::new();
|
||||
app.set_option("language", "en").unwrap();
|
||||
app.open_bytes("clipped.pdf", None, clipped_artwork_with_labels_drawn(true)).unwrap();
|
||||
app
|
||||
});
|
||||
h.run_steps(4);
|
||||
assert!(h.state_mut().execute("edit.edit_text"));
|
||||
h.state_mut().views[0].set_zoom(1.2);
|
||||
h.run_steps(4);
|
||||
let page = h.state().views[0].page_screen_rect(0).unwrap();
|
||||
let at = |x: f32, y: f32| egui::pos2(page.left() + x / 200.0 * page.width(), page.top() + (300.0 - y) / 300.0 * page.height());
|
||||
let p = at(95.0, 195.0);
|
||||
h.hover_at(p);
|
||||
h.run_steps(1);
|
||||
h.drag_at(p);
|
||||
h.run_steps(1);
|
||||
h.drop_at(p);
|
||||
h.run_steps(3);
|
||||
assert!(h.state().views[0].image_selection.is_some());
|
||||
let frame = |h: &Harness<'static, PdfCraftApp>| {
|
||||
h.output().shapes.iter().find_map(|s| match &s.shape {
|
||||
egui::Shape::Rect(r) if r.stroke.color == egui::Color32::from_rgb(0x14, 0x73, 0xE6) && r.rect.width() > 20.0 => Some(r.rect),
|
||||
_ => None,
|
||||
})
|
||||
};
|
||||
// Textured meshes smaller than the page: the page raster itself always covers the page.
|
||||
let pictures = |h: &Harness<'static, PdfCraftApp>| -> Vec<egui::Rect> {
|
||||
h.output()
|
||||
.shapes
|
||||
.iter()
|
||||
.filter_map(|s| match &s.shape {
|
||||
egui::Shape::Mesh(m) if m.texture_id != egui::TextureId::default() => Some(m.calc_bounds()),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|b| b.width() < page.width() - 1.0)
|
||||
.collect()
|
||||
};
|
||||
let start = frame(&h).expect("the selected image has a frame");
|
||||
let (grip, fixed) = (start.right_top(), start.left_bottom());
|
||||
for factor in [0.6, 1.3] {
|
||||
let to = fixed + (grip - fixed) * factor;
|
||||
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 live = frame(&h).expect("the drag shows its frame");
|
||||
assert!((live.width() - start.width() * factor).abs() < 1.0, "the frame follows the grip: {live:?} from {start:?}");
|
||||
let drawn = pictures(&h);
|
||||
assert!(
|
||||
drawn.iter().any(|b| (b.min - live.min).length() < 0.5 && (b.max - live.max).length() < 0.5),
|
||||
"the picture is drawn in the frame {live:?}, not only at {drawn:?}"
|
||||
);
|
||||
h.drop_at(to);
|
||||
h.run_steps(4);
|
||||
h.key_press_modifiers(Modifiers::COMMAND, Key::Z);
|
||||
h.run_steps(4);
|
||||
}
|
||||
}
|
||||
|
||||
/// Exercise actual pointer gestures on each cropped placement, without capturing private pixels.
|
||||
#[test]
|
||||
#[ignore = "requires an explicitly supplied local PDF with cropped artwork"]
|
||||
|
||||
Reference in New Issue
Block a user