fix(ui): with Edit text, what has no menu gives way to a picture under it

Found in review. With Edit text, a right-click on added text or on an
open paragraph's frame went to that, which has no menu: the picture under
it was unselected and the page's menu opened. `main` opens the picture's.
Those give way to a picture under them, as paragraphs do, and to nothing
else: a comment under added text stays unselected, as before.

A comment in `draw_pages` said a selected Prepare field keeps its menu
off the pages. Since the page menu follows the right-click, it stays
selected and the menu is the page's.

Tests, each seen failing with the fix reverted:
`a_right_click_on_added_text_over_a_picture_selects_the_picture`,
`a_right_click_on_an_open_paragraphs_frame_over_a_picture_selects_the_picture`.
`a_right_click_with_edit_text_on_added_text_over_a_comment_selects_no_comment`
is a pin, seen failing with added text dropped from the hits outright.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
alehelandClaude Opus 5.5 committed 2026-11-04 19:37:47 +01:00
1 parent a5ed8c68bf
commit 79205fe4ac
4 files changed
+71 -4

No files matched your search

+1 -1
View File
@@ -1676,7 +1676,7 @@ fn draw_pages(
// Set again below by the page right-clicked: a right-click off the pages (between them,
// beside one) anchors no page's menu, which then acts on the current page. It deselects the
// comment, and Edit text's picture, too (below), as one on a page's empty part does. A
// selected Prepare field keeps its menu, off the pages as on them.
// selected Prepare field stays selected, and the menu is the page's there too.
view.comments.context_at = None;
view.menu_for = None;
}
+13 -3
View File
@@ -462,6 +462,9 @@ impl PageHits<'_> {
/// whatever the field tool. Links count for the tools that reach them (Select; the Link tool,
/// below). Edit text's paragraphs have no menu, and give way to the picture under them (the
/// user's ruling): with Edit text, a picture under a link or a paragraph takes the right-click.
/// Nor have added content or an open paragraph's frame: those give way to a picture under them,
/// and to nothing else. (Where objects are selected together, the page view unselects the picture
/// a right-click selects, and the menu is the page's.)
/// A highlight over a link is the highlight's ([`comments::context_hit`]).
/// The Link tool edits links and reaches no comment: on a document it can change, its
/// right-click is what its press there would take (`under`, the hits at `p`), when that is
@@ -473,11 +476,18 @@ impl PageHits<'_> {
{
return Some(press.owner);
}
let reached = self
let reached: Vec<_> = self
.hits(view, p, true, None)
.into_iter()
.filter(|h| (h.layer != Layer::Link || h.layer.reached_by(self.tool)) && !matches!(h.owner, Owner::Paragraph { .. }));
top(reached, QuickTool::Select).map(|h| h.owner)
.filter(|h| (h.layer != Layer::Link || h.layer.reached_by(self.tool)) && !matches!(h.owner, Owner::Paragraph { .. }))
.collect();
let on_top = top(reached.iter().cloned(), QuickTool::Select)?.owner;
if self.tool == QuickTool::EditText && matches!(on_top, Owner::Added { .. } | Owner::Objects { part: crate::page_router::ObjectsPart::Frame })
{
let pictures = reached.into_iter().filter(|h| matches!(h.owner, Owner::Picture { .. }));
return Some(top(pictures, QuickTool::Select).map_or(on_top, |h| h.owner));
}
Some(on_top)
}
/// The hits at `p`, bottom up: links, comments; Edit text's pictures under its paragraphs (text
+41
View File
@@ -7336,6 +7336,47 @@ fn a_right_click_on_text_over_a_picture_selects_the_picture() {
assert_eq!(selected, Some((0, 0)), "the picture under the text is selected for its menu");
}
/// With Edit text, a right-click on added text over a picture is the picture's, which is selected
/// for its menu: added text has no menu of its own. It unselected the picture and opened the page's.
#[test]
fn a_right_click_on_added_text_over_a_picture_selects_the_picture() {
let mut h = picture_pages(false);
h.state_mut().views[0].image_selection = None;
assert!(h.state_mut().apply_edit(pdfcraft_engine::Edit::AddText {
page: 0,
text: pdfcraft_engine::AddedText { rect: [60.0, 190.0, 150.0, 205.0], text: "Added".into(), ..Default::default() },
}));
h.run_steps(3);
let on_it = page_at(&h, 0, 100.0, 198.0);
right_click_at(&mut h, on_it);
let selected = h.state().views[0].image_selection.as_ref().map(|s| (s.page, s.index));
assert_eq!(selected, Some((0, 0)), "the picture under the added text is selected for its menu");
assert!(h.query_by_label("Flip horizontal").is_some(), "its menu opens");
}
/// With Edit text, a right-click on the frame of a paragraph open for typing over a picture is the
/// picture's too. It unselected the picture and opened the page's menu.
#[test]
fn a_right_click_on_an_open_paragraphs_frame_over_a_picture_selects_the_picture() {
let mut h = picture_pages(false);
h.state_mut().views[0].image_selection = None;
h.run_steps(2);
// "Overlay" lies on the picture, at x 40, y 220.
let overlay = page_at(&h, 0, 50.0, 224.0);
slipping_click(&mut h, overlay, overlay);
assert!(h.state().views[0].line_editor.is_some(), "the paragraph is open for typing");
let id = h.state().views[0].id;
let blocks = h.state().session.get(id).unwrap().text_blocks(0);
let block =
blocks.iter().find(|b| b.rect[0] < 50.0 && 50.0 < b.rect[2] && b.rect[1] < 224.0 && 224.0 < b.rect[3]).expect("the paragraph opened").clone();
// Its frame runs just outside its box.
let frame = page_at(&h, 0, block.rect[0] as f32, ((block.rect[1] + block.rect[3]) / 2.0) as f32) - egui::vec2(3.0, 0.0);
right_click_at(&mut h, frame);
let selected = h.state().views[0].image_selection.as_ref().map(|s| (s.page, s.index));
assert_eq!(selected, Some((0, 0)), "the picture under the frame is selected for its menu");
assert!(h.query_by_label("Flip horizontal").is_some(), "its menu opens");
}
/// In crop mode, a right-click on another picture selects it for its menu, as without crop mode. It
/// went to crop mode's whole-page hit, which unselected every picture.
#[test]
+16
View File
@@ -1579,6 +1579,22 @@ fn a_right_click_with_edit_text_on_a_paragraph_under_a_comment_selects_the_comme
assert_eq!(h.state().views[0].comments.selected, None, "the paragraph beside the comment leaves it unselected");
}
/// With Edit text, a right-click on added text over a comment selects no comment: what has no menu
/// gives way to a picture under it and to nothing else, as before.
#[test]
fn a_right_click_with_edit_text_on_added_text_over_a_comment_selects_no_comment() {
let mut h = open(comment_on_text_pdf(), "150", false);
assert!(h.state_mut().execute("edit.edit_text"));
assert!(h.state_mut().apply_edit(pdfcraft_engine::Edit::AddText {
page: 0,
text: pdfcraft_engine::AddedText { rect: [110.0, 296.0, 190.0, 311.0], text: "Added".into(), ..Default::default() },
}));
h.run_steps(4);
let on_added = at(&h, 150.0, 303.0);
right_click(&mut h, on_added);
assert_eq!(h.state().views[0].comments.selected, None, "the comment under the added text is not selected");
}
/// With a Prepare field tool a right-click is the tool's, as before: a comment under it is not
/// selected.
#[test]