fix(ui): Copy in the selection action bar closes the bar

The bar beside selected text closed after Highlight, Underline, Strikethrough,
Add comment and Replace text because those consume the selection. Copy kept the
selection, so the bar stayed open. Remember the copied selection and hide the
bar until the selection changes; the words stay selected for ⌘C or another
action.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Mateus Bessa
2026-10-15 15:14:30 -03:00
co-authored by Claude Opus 5.5
parent dc41044ba7
commit 64ab6b67ec
2 changed files with 29 additions and 1 deletions
+14 -1
View File
@@ -371,6 +371,9 @@ pub struct DocView {
pub(crate) text_failed: HashSet<usize>,
pub find: Option<Find>,
selection: Option<Selection>,
/// The selection whose action bar was closed by its Copy button: the bar stays hidden
/// until the selection changes, as it does once a markup button consumes the selection.
actions_dismissed: Option<Selection>,
/// The drag in progress started a text selection: the wheel scrolls the page during it
/// (#721), which egui's scroll area refuses while anything is dragged.
selection_drag: bool,
@@ -649,6 +652,7 @@ impl DocView {
text_failed: HashSet::new(),
find: None,
selection: None,
actions_dismissed: None,
selection_drag: false,
clicks: 0,
last_press_time: 0.0,
@@ -955,6 +959,7 @@ impl DocView {
pub(crate) fn clear_selection(&mut self) {
self.selection = None;
self.actions_dismissed = None;
}
/// Select text on `page` from glyph `from` to glyph `to` (tests and automation).
@@ -3794,7 +3799,13 @@ fn selection_actions(
if ui.input(|input| input.pointer.is_decidedly_dragging()) {
return false;
}
let Some(selection) = view.selection else { return false };
let Some(selection) = view.selection else {
view.actions_dismissed = None;
return false;
};
if view.actions_dismissed == Some(selection) {
return false;
}
let Some(text) = view.texts.get(&selection.page) else { return false };
let Some(xf) = view.page_xform(selection.page) else { return false };
let Some(last) = selection.rects(text).last().copied() else { return false };
@@ -3831,6 +3842,8 @@ fn selection_actions(
});
let Some(action) = chosen else { return false };
if let SelectionAction::Copy = action {
// Copying closes the bar but keeps the words selected, for ⌘C or a second action.
view.actions_dismissed = Some(selection);
return true;
}
let Some((page, quads)) = view.selection_quads(info) else { return false };
+15
View File
@@ -364,6 +364,21 @@ fn selected_text_shows_actions_and_underlines_without_changing_tools() {
assert_eq!(h.state().quick_tool, QuickTool::Select);
}
#[test]
fn selected_text_copy_action_closes_the_bar_and_keeps_the_selection() {
let mut h = harness(|_| {});
h.state_mut().views[0].select_text(0, 4, 8);
h.run_steps(3);
h.get_by_label("Copy").click();
h.run_steps(3);
assert!(h.query_by_label("Add comment").is_none(), "the action bar closes after Copy");
assert!(h.state().views[0].selected_text().is_some_and(|t| !t.is_empty()), "the words stay selected");
// A new selection brings the bar back.
h.state_mut().views[0].select_text(0, 2, 6);
h.run_steps(3);
h.get_by_label("Add comment");
}
#[test]
fn selected_text_replace_action_opens_replacement_composer() {
let mut h = harness(|_| {});