mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 12:32:52 +00:00
fix(ui): a drag from the selected picture moves it under another picture
Found in review. The selected picture's frame is its own on `main`: a drag from it moves it, though another picture lies over it there. The routed press was the top picture's, and the drag only started for the selected one's, so nothing moved. `PicturePart::OverSelected` says so: a click there selects the picture on top, as before, and a drag moves the selected one. Test, seen failing with the part planted away: `a_drag_from_the_selected_picture_under_another_moves_the_selected_one`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
3 files changed
+74
-11
No files matched your search
@@ -48,6 +48,9 @@ pub(crate) enum Owner {
|
||||
pub(crate) enum PicturePart {
|
||||
/// The picture: a click selects it, a drag moves the selected one.
|
||||
Body,
|
||||
/// A picture lying over the selected one, within the selected one's frame: a click selects the
|
||||
/// one on top, a drag moves the selected one.
|
||||
OverSelected,
|
||||
/// A resize grip in `ImageObb::handles` order (four corners and four edge midpoints).
|
||||
Corner(usize),
|
||||
/// The selected picture's rotate knob: a drag turns it.
|
||||
|
||||
@@ -611,17 +611,19 @@ pub(crate) fn body_hit(xf: &PageXform, page: usize, info: &DocInfo, images: &[pd
|
||||
}
|
||||
let (x, y) = xf.screen_to_view(p);
|
||||
let user = info.pages.get(page)?.view_to_user(x, y).map(f64::from);
|
||||
let index = images
|
||||
.iter()
|
||||
.rposition(|im| im.visible_rect.is_some_and(|r| xf.user_rect(info, page, r.map(|v| v as f32)).contains(p)) && im.contains(user))
|
||||
.or_else(|| {
|
||||
let s = view.image_selection.as_ref().filter(|s| s.page == page && s.crop.is_none())?;
|
||||
let image = images.get(s.index)?;
|
||||
quad_contains(ImageObb::from_image(image, xf, info, page).corners(), p).then_some(s.index)
|
||||
})?;
|
||||
let on_picture = |im: &pdfcraft_engine::PageImage| {
|
||||
im.visible_rect.is_some_and(|r| xf.user_rect(info, page, r.map(|v| v as f32)).contains(p)) && im.contains(user)
|
||||
};
|
||||
// The selected picture's frame is its own, a hole in it and a picture over it included.
|
||||
let framed =
|
||||
view.image_selection.as_ref().filter(|s| s.page == page && s.crop.is_none()).map(|s| s.index).filter(|i| {
|
||||
images.get(*i).is_some_and(|image| on_picture(image) || quad_contains(ImageObb::from_image(image, xf, info, page).corners(), p))
|
||||
});
|
||||
let index = images.iter().rposition(on_picture).or(framed)?;
|
||||
let selected = view.image_selection.as_ref().is_some_and(|s| s.page == page && s.index == index);
|
||||
let icon = if selected { egui::CursorIcon::Move } else { egui::CursorIcon::PointingHand };
|
||||
Some(Hit::new(Owner::Picture { index, part: PicturePart::Body }, Layer::Text, icon))
|
||||
let part = if framed.is_some_and(|i| i != index) { PicturePart::OverSelected } else { PicturePart::Body };
|
||||
Some(Hit::new(Owner::Picture { index, part }, Layer::Text, icon))
|
||||
}
|
||||
|
||||
/// What a right-click on a selected image asks for.
|
||||
@@ -785,7 +787,7 @@ pub(crate) fn image_input(cx: &ImageCx<'_>, images: &[pdfcraft_engine::PageImage
|
||||
&& ptr.drag_started
|
||||
&& let Some(o) = ptr.origin
|
||||
&& let PagePress::Mine(Owner::Picture { index, part }) = press
|
||||
&& index == i
|
||||
&& (index == i || part == PicturePart::OverSelected)
|
||||
&& let Some(s) = view.image_selection.as_mut()
|
||||
{
|
||||
match part {
|
||||
@@ -803,7 +805,7 @@ pub(crate) fn image_input(cx: &ImageCx<'_>, images: &[pdfcraft_engine::PageImage
|
||||
});
|
||||
}
|
||||
}
|
||||
PicturePart::Body => {
|
||||
PicturePart::Body | PicturePart::OverSelected => {
|
||||
s.drag = Some(ImageDrag { start: o, target: DragTarget::Move, initial_rect: b, initial_matrix: image.matrix });
|
||||
}
|
||||
PicturePart::Crop(_) => {}
|
||||
|
||||
@@ -7377,6 +7377,64 @@ fn a_right_click_on_an_open_paragraphs_frame_over_a_picture_selects_the_picture(
|
||||
assert!(h.query_by_label("Flip horizontal").is_some(), "its menu opens");
|
||||
}
|
||||
|
||||
/// A drag from the selected picture moves it, though another picture lies over it there; a click
|
||||
/// there selects the one on top.
|
||||
#[test]
|
||||
fn a_drag_from_the_selected_picture_under_another_moves_the_selected_one() {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 1400.0)).build_eframe(|_cc| {
|
||||
let mut app = PdfCraftApp::new();
|
||||
app.set_option("language", "en").unwrap();
|
||||
app.open_bytes("two.pdf", None, two_image_fixture()).unwrap();
|
||||
app
|
||||
});
|
||||
h.run_steps(4);
|
||||
// The second picture lies over the right half of the first.
|
||||
h.state_mut().apply_edit(pdfcraft_engine::Edit::EditPageImage {
|
||||
page: 0,
|
||||
index: 1,
|
||||
change: pdfcraft_engine::ImageEdit::Move([110.0, 100.0, 130.0, 120.0]),
|
||||
});
|
||||
assert!(h.state_mut().execute("edit.edit_text"));
|
||||
h.run_steps(4);
|
||||
h.state_mut().views[0].image_selection = Some(pdfcraft_ui_egui::ImageSelection::at(0, 0));
|
||||
h.run_steps(2);
|
||||
let id = h.state().views[0].id;
|
||||
let rects = |h: &Harness<'static, PdfCraftApp>| {
|
||||
let images = h.state().session.get(id).unwrap().page_images(0);
|
||||
(images[0].rect, images[1].rect)
|
||||
};
|
||||
let before = rects(&h);
|
||||
let xf = h.state().views[0].page_xform(0).unwrap();
|
||||
let doc = h.state().session.get(id).unwrap();
|
||||
let images = doc.page_images(0);
|
||||
let frame = |i: usize| pdfcraft_ui_egui::canvas::image_screen_rect(&images[i], &xf, &doc.info, 0);
|
||||
let overlap = frame(0).intersect(frame(1)).center();
|
||||
h.hover_at(overlap);
|
||||
h.run_steps(1);
|
||||
h.drag_at(overlap);
|
||||
h.run_steps(1);
|
||||
for k in 1..=4 {
|
||||
h.hover_at(overlap + egui::vec2(8.0 * k as f32, 0.0));
|
||||
h.run_steps(1);
|
||||
}
|
||||
h.drop_at(overlap + egui::vec2(32.0, 0.0));
|
||||
h.run_steps(3);
|
||||
let after = rects(&h);
|
||||
assert_ne!(after.0, before.0, "the selected picture moves");
|
||||
assert_eq!(after.1, before.1, "the one over it stays");
|
||||
let selected = h.state().views[0].image_selection.as_ref().map(|s| s.index);
|
||||
assert_eq!(selected, Some(0), "and stays selected");
|
||||
// After the move the two still overlap, a little further along.
|
||||
let xf = h.state().views[0].page_xform(0).unwrap();
|
||||
let doc = h.state().session.get(id).unwrap();
|
||||
let images = doc.page_images(0);
|
||||
let frame = |i: usize| pdfcraft_ui_egui::canvas::image_screen_rect(&images[i], &xf, &doc.info, 0);
|
||||
let overlap = frame(0).intersect(frame(1)).center();
|
||||
slipping_click(&mut h, overlap, overlap);
|
||||
let selected = h.state().views[0].image_selection.as_ref().map(|s| s.index);
|
||||
assert_eq!(selected, Some(1), "a click selects the one on top");
|
||||
}
|
||||
|
||||
/// In the page grid, Delete deletes the page chosen there. The picture selected before the grid
|
||||
/// opened is not on screen, and stays.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user