mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-29 20:47:32 -06:00
A plain click on a page cleared the text selection whatever modifiers were held, so there was no way to extend a selection with Shift+click as in Acrobat. Now, when Shift is held and there is a text selection on the clicked page, the click moves the selection head to the nearest text position and keeps the anchor. Without Shift, or with no selection on that page, a click still clears the selection. Selections remain single-page: cross-page selection (the issue's second request) is not part of this change. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e5b37b8b9e
commit
9acb29e418
@@ -1758,7 +1758,14 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) {
|
||||
view.selection = Some(Selection { page: i, anchor: first, head: last });
|
||||
}
|
||||
} else if resp.clicked() && !over_link {
|
||||
view.selection = None;
|
||||
// ⇧-click extends a selection on this page to the click, keeping its
|
||||
// anchor (#527). Otherwise (no shift, or no selection on this page; a
|
||||
// selection cannot yet span pages) a click clears the selection.
|
||||
let shift = ui.input(|inp| inp.modifiers.shift);
|
||||
match (view.selection.as_mut().filter(|s| shift && s.page == i), text.nearest(vx, vy)) {
|
||||
(Some(sel), Some(h)) => sel.head = h,
|
||||
_ => view.selection = None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,3 +85,57 @@ fn quick_clicks_with_the_highlighter_mark_only_the_word() {
|
||||
assert_eq!(marks, [("Highlight", 1)], "one highlight, on the double-clicked word");
|
||||
assert!(app.views[0].selected_text().is_none());
|
||||
}
|
||||
|
||||
/// Press at `from`, move to `to` and release there.
|
||||
fn drag(h: &mut Harness<'static, PdfCraftApp>, from: Pos2, to: Pos2) {
|
||||
h.event(Event::PointerButton { pos: from, button: PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE });
|
||||
h.run_steps(1);
|
||||
for k in 1..=4 {
|
||||
h.hover_at(from + (to - from) * (k as f32 / 4.0));
|
||||
h.run_steps(1);
|
||||
}
|
||||
h.event(Event::PointerButton { pos: to, button: PointerButton::Primary, pressed: false, modifiers: Modifiers::NONE });
|
||||
h.run_steps(1);
|
||||
}
|
||||
|
||||
/// Click at `pos` with Shift held.
|
||||
fn shift_click(h: &mut Harness<'static, PdfCraftApp>, pos: Pos2) {
|
||||
h.event(Event::ModifiersChanged(Modifiers::SHIFT));
|
||||
h.hover_at(pos);
|
||||
h.run_steps(1);
|
||||
for pressed in [true, false] {
|
||||
h.event(Event::PointerButton { pos, button: PointerButton::Primary, pressed, modifiers: Modifiers::SHIFT });
|
||||
}
|
||||
h.run_steps(1);
|
||||
h.event(Event::ModifiersChanged(Modifiers::NONE));
|
||||
h.run_steps(1);
|
||||
}
|
||||
|
||||
/// Issue #527: ⇧-click extends the text selection to the click, keeping its anchor.
|
||||
#[test]
|
||||
fn shift_click_extends_the_selection_from_its_anchor() {
|
||||
let (mut h, _) = harness(|_| {});
|
||||
let glyph = |h: &Harness<'static, PdfCraftApp>, g: usize| h.state().views[0].glyph_screen_pos(0, g).expect("glyph on screen");
|
||||
// Drag over "quick" (glyphs 4..=8).
|
||||
let (q, k) = (glyph(&h, 4), glyph(&h, 8));
|
||||
drag(&mut h, q, k);
|
||||
assert_eq!(h.state().views[0].selected_text().as_deref(), Some("quick"));
|
||||
h.run_steps(60);
|
||||
// ⇧-click on the "n" of "brown": the selection runs from the anchor to it.
|
||||
let n = glyph(&h, 14);
|
||||
shift_click(&mut h, n);
|
||||
assert_eq!(h.state().views[0].selected_text().as_deref(), Some("quick brown"));
|
||||
h.run_steps(60);
|
||||
// ⇧-click before the anchor: the anchor stays, the selection now runs back to the click.
|
||||
let t = glyph(&h, 0);
|
||||
shift_click(&mut h, t);
|
||||
assert_eq!(h.state().views[0].selected_text().as_deref(), Some("The q"));
|
||||
h.run_steps(60);
|
||||
// A plain click still clears it.
|
||||
click(&mut h, n);
|
||||
assert_eq!(h.state().views[0].selected_text(), None);
|
||||
h.run_steps(60);
|
||||
// ⇧-click with nothing selected selects nothing.
|
||||
shift_click(&mut h, n);
|
||||
assert_eq!(h.state().views[0].selected_text(), None);
|
||||
}
|
||||
|
||||
@@ -1478,8 +1478,8 @@ milestone = "M3"
|
||||
status = "shipped"
|
||||
commands = []
|
||||
tools = []
|
||||
evidence = ["crates/ui-egui/tests/ui.rs::drag_selects_text_and_copy_returns_it", "crates/ui-egui/tests/ui.rs::selection_works_in_every_view_rotation", "crates/ui-egui/tests/text_select.rs::quick_clicks_select_a_word_then_the_line_then_the_page", "crates/ui-egui/tests/text_select.rs::quick_clicks_with_the_highlighter_mark_only_the_word"]
|
||||
notes = "Drag to select; with the Select tool, quick clicks select a word (two), the line (three) or all the text on the page (four). The markup and Redact tools mark a double-clicked word and stop there. Missing: paragraph selection, and ⌘A across the whole document (it selects the current page)."
|
||||
evidence = ["crates/ui-egui/tests/ui.rs::drag_selects_text_and_copy_returns_it", "crates/ui-egui/tests/ui.rs::selection_works_in_every_view_rotation", "crates/ui-egui/tests/text_select.rs::quick_clicks_select_a_word_then_the_line_then_the_page", "crates/ui-egui/tests/text_select.rs::quick_clicks_with_the_highlighter_mark_only_the_word", "crates/ui-egui/tests/text_select.rs::shift_click_extends_the_selection_from_its_anchor"]
|
||||
notes = "Drag to select; ⇧-click extends the selection to the click, keeping its anchor (same page only); with the Select tool, quick clicks select a word (two), the line (three) or all the text on the page (four). The markup and Redact tools mark a double-clicked word and stop there. Missing: selection across pages (dragging or ⇧-clicking onto another page), paragraph selection, and ⌘A across the whole document (it selects the current page)."
|
||||
|
||||
[[feature]]
|
||||
id = "view.copy-text"
|
||||
|
||||
Reference in New Issue
Block a user