fix(ui): Add text finishes a paragraph drag that leaves the paragraph's box

With Add text the page's own paragraphs took input only while the pointer was over one
(over_source), and their drags end in edit_text_ui::page_input. A paragraph dragged off its box
was left hanging after the release (block_drag), to land wherever the pointer next met a
paragraph; one flicked off in its first move never started, so nothing moved. main has the same
test. Add text now also acts on the paragraph the page router says holds the press, until the
button is up, so the drag ends where it is released, as with Edit text, and Add text's
new-box path does not take the press over.

New test add_text_finishes_a_paragraph_drag_that_leaves_its_box, seen failing before the fix
(the drag still pending after the release); the flick was checked by hand the same way. From
CodeRabbit's third review of #5300.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
alehelandClaude Opus 5.5 committed 2026-10-29 15:32:48 +01:00
1 parent 2929ba854e
commit 11bc8c5652
2 files changed
+39 -2

No files matched your search

+6 -2
View File
@@ -1275,9 +1275,13 @@ fn page_tool_input(ui: &mut egui::Ui, view: &mut DocView, frame: &mut PageFrame<
crate::redact_ui::page_input(cx, over_text, view)
};
let on_objects = tool == QuickTool::EditText && can_modify && crate::object_ui::page_input(cx, view);
// Add text acts on the page's own paragraphs over them, and on the one its press was on until
// the button is up: a drag that leaves the box still ends, and no new box takes it over.
let over_source = tool == QuickTool::AddText
&& pointer
.is_some_and(|p| !crate::content_ui::hits_added(&xf, info, i, added, p) && crate::edit_text_ui::over_paragraph(&xf, info, i, &lines, p));
&& (matches!(view.router.owner(i), Some(crate::page_router::Owner::Paragraph { .. }))
|| pointer.is_some_and(|p| {
!crate::content_ui::hits_added(&xf, info, i, added, p) && crate::edit_text_ui::over_paragraph(&xf, info, i, &lines, p)
}));
let source_blocked = over_source && view.content.hold_blocked();
if over_source && resp.clicked() && !source_blocked {
if let Some(edit) = view.content.finish_draft(added) {
+33
View File
@@ -3994,6 +3994,39 @@ fn add_text_opens_a_paragraph_from_just_inside_its_edge() {
assert!(h.state().views[0].content.draft.is_none(), "and starts no text box");
}
/// With Add text a paragraph dragged off its own box still moves to where the button comes up, as
/// with Edit text. Add text acted on the page's paragraphs only while the pointer was over one, so
/// the drag was left hanging, to land wherever the pointer next met a paragraph.
#[test]
fn add_text_finishes_a_paragraph_drag_that_leaves_its_box() {
let mut h = harness(2, |_| {});
h.state_mut().quick_tool = pdfcraft_ui_egui::QuickTool::AddText;
h.run_steps(2);
let id = h.state().views[0].id;
let before = h.state().session.get(id).unwrap().text_blocks(0)[0].rect;
let r = h.state().views[0].page_screen_rect(0).expect("on screen");
let k = r.width() / 200.0;
let from = egui::pos2(r.left() + ((before[0] + before[2]) / 2.0) as f32 * k, r.top() + (300.0 - ((before[1] + before[3]) / 2.0) as f32) * k);
// The drag starts in the box (8 px down), then goes 60 pt down, onto the blank page under it.
h.hover_at(from);
h.run_steps(1);
h.drag_at(from);
h.run_steps(1);
h.hover_at(from + egui::vec2(0.0, 8.0));
h.run_steps(1);
for i in 1..=4 {
h.hover_at(from + egui::vec2(0.0, 15.0 * k * i as f32));
h.run_steps(1);
}
h.drop_at(from + egui::vec2(0.0, 60.0 * k));
for _ in 0..10 {
h.run_steps(2);
}
assert!(h.state().views[0].block_drag.is_none(), "the drag is over");
let after = h.state().session.get(id).unwrap().text_blocks(0)[0].rect;
assert!(before[1] - after[1] > 50.0, "the paragraph moved down with it: {before:?} to {after:?}");
}
#[test]
fn dragging_the_rotate_knob_turns_a_page_image() {
let mut png = Vec::new();