collective

Browse / github.com/storytold/pdfcraft / patch

M7: the quick bar's pin keeps Add text selected after Done (#4863)

Add text: keep the tool selected after placing text (pin, like the comment tools) ### Description With **Edit a PDF ▸ Add text**, finishing a text box switches the app back to the Select tool. To add the next piece of text you have to pick Add text again. When filling in a form or labelling many spots on a page, that means re-selecting the tool for every entry. The comment tools already solve this: the quick bar has a pin button ("Keep tool selected") that keeps the tool active after each use, and the choice is remembered across restarts (`comment_tool_pinned`). Add text has no equivalent. ### Steps 1. Open a PDF and choose **Edit a PDF ▸ Add text**. 2. Click on the pag

Status
open
Score
0.00
Adopters
0 other operators
Author
AdrianTM (imported from pull request #4904, open)
Base
1dd2737b47a9c1c9562f0af563053d1bf510e4bf
Size
+60 −9 in 7 files
Review
pending
Shared
2026-10-28
Id
9c14530c0e4f7a7e0131433c

How it went

No other operator has reported using it yet.

Diff

raw · ROADMAP.md crates/ui-egui/src/canvas.rs crates/ui-egui/src/canvas/bars.rs crates/ui-egui/src/tools/comments.rs crates/ui-egui/src/tools/comments/menu.rs crates/ui-egui/tests/it/content.rs parity/acrobat-features.toml

diff --git a/ROADMAP.md b/ROADMAP.md--- a/ROADMAP.md+++ b/ROADMAP.md@@ -742,6 +742,8 @@ Newest first: the date, what moved, and the new overall estimate.  - **2026-10-21 (M4, #3865):** A new bookmark records where the reader is: the top-left of the selected text, or the top of the window, as `/XYZ left top null` (zoom kept), instead of the page top (`Edit::AddBookmark` takes `left` and `top`; `bookmark_add` takes `top` and `left`; `PageInfo::xyz_for_view`). +- **2026-10-26 (M7, #4863):** The quick bar's "Keep tool selected" pin, shared with the comment tools and saved with the same setting, now also applies to Add text: with it on, Done leaves Add text selected so each click starts another text box; off, Done goes back to Select as before.+ - **2026-10-18 (renderer, hayro patches 19/35):** A mesh shading (types 4–7) is sampled over its own triangles instead of the whole path: a BricsCAD plan with 310 one-triangle `sh` fills took 2 s per 1024 px tile at every zoom (and hit the 20 s watchdog on a busy machine); now about 70 ms, so zooming into dense topology drawings stays fluid. Estimates unchanged.  - **2026-10-22 (PR #3227 integration):** M11: integrate upstream `80b7d215`; retain partial target-version compatibility and upstream content-boundary and image-size regression coverage. Scope and estimates unchanged.diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs--- a/crates/ui-egui/src/canvas.rs+++ b/crates/ui-egui/src/canvas.rs@@ -2474,8 +2474,9 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) {     if stamp_placed {         tool = QuickTool::Select;     }-    // Text: each click on the page starts a box (#74); Done in the editor goes back to selecting.-    if text_done && tool == QuickTool::AddText {+    // Text: each click on the page starts a box (#74); Done in the editor goes back to selecting,+    // unless the quick bar's pin keeps the tool (#4863).+    if text_done && tool == QuickTool::AddText && !app.comment_prefs.pinned {         tool = QuickTool::Select;     }     // One field, then back to selecting (Acrobat's default without "Keep tools pinned").diff --git a/crates/ui-egui/src/canvas/bars.rs b/crates/ui-egui/src/canvas/bars.rs--- a/crates/ui-egui/src/canvas/bars.rs+++ b/crates/ui-egui/src/canvas/bars.rs@@ -480,6 +480,11 @@ pub(super) fn quick_bar(app: &mut PdfCraftApp, area: Rect, ui: &mut egui::Ui) {                                 endings: None,                             });                         }+                    } else if app.quick_tool == QuickTool::AddText {+                        // The same pin as the comment tools': each click on the page starts another text box.+                        let (r, _) = ui.allocate_exact_size(vec2(32.0, 9.0), Sense::hover());+                        ui.painter().hline(r.x_range().shrink(6.0), r.center().y, Stroke::new(1.0, t.divider));+                        comments::pin_button(ui, &mut app.comment_prefs);                     }                 });             });diff --git a/crates/ui-egui/src/tools/comments.rs b/crates/ui-egui/src/tools/comments.rs--- a/crates/ui-egui/src/tools/comments.rs+++ b/crates/ui-egui/src/tools/comments.rs@@ -39,7 +39,7 @@ pub(crate) use input::{fill_grabs, keys, nudge_keys, page_after_text, page_input pub use menu::{CanvasAction, fill_picker, recent_custom_colours, remember_custom_colour, status_badge, swatch_grid}; #[cfg(test)] use menu::{RECENT_CUSTOM, same_colour};-pub(crate) use menu::{attachment_items, context_menu, quick_bar_controls};+pub(crate) use menu::{attachment_items, context_menu, pin_button, quick_bar_controls}; pub(crate) use paint::paint_page; #[cfg(test)] use paint::{changing_points, drawn_shape, edited_in_view};diff --git a/crates/ui-egui/src/tools/comments/menu.rs b/crates/ui-egui/src/tools/comments/menu.rs--- a/crates/ui-egui/src/tools/comments/menu.rs+++ b/crates/ui-egui/src/tools/comments/menu.rs@@ -279,13 +279,18 @@ pub fn fill_picker(ui: &mut egui::Ui, current: Option<Rgb>) -> Option<Option<Rgb     picked } +/// The quick bar's "Keep tool selected" pin: one preference for the comment tools and Add text.+pub(crate) fn pin_button(ui: &mut egui::Ui, prefs: &mut CommentPrefs) {+    if icons::button(ui, "pin", 32.0, prefs.pinned, if prefs.pinned { tl!("Keep tool selected: on") } else { tl!("Keep tool selected") }).clicked() {+        prefs.pinned = !prefs.pinned;+    }+}+ /// The comment tools' extra quick-bar controls: pin, colour, fill, opacity and thickness. pub(crate) fn quick_bar_controls(ui: &mut egui::Ui, tool: CommentTool, prefs: &mut CommentPrefs) -> Option<Rgb> {     let style = prefs.style(tool);     let mut selected_color = None;-    if icons::button(ui, "pin", 32.0, prefs.pinned, if prefs.pinned { tl!("Keep tool selected: on") } else { tl!("Keep tool selected") }).clicked() {-        prefs.pinned = !prefs.pinned;-    }+    pin_button(ui, prefs);     let (r, resp) = ui.allocate_exact_size(vec2(32.0, 32.0), Sense::click());     ui.painter().circle_filled(r.center(), 9.0, color32(style.color));     ui.painter().circle_stroke(r.center(), 9.0, Stroke::new(1.0, Color32::from_black_alpha(50)));diff --git a/crates/ui-egui/tests/it/content.rs b/crates/ui-egui/tests/it/content.rs--- a/crates/ui-egui/tests/it/content.rs+++ b/crates/ui-egui/tests/it/content.rs@@ -206,6 +206,44 @@ fn clicking_elsewhere_keeps_the_text_and_starts_a_new_box() {     assert!(h.state().views[0].content.draft.is_none()); } +/// #4863: the quick bar's pin keeps Add text selected after Done, so the next click starts another+/// box; with the pin off, Done goes back to Select.+#[test]+fn the_pin_keeps_add_text_selected_after_done() {+    let _gpu = gpu();+    let mut h = harness();+    assert!(h.state_mut().execute("edit.text"));+    h.run_steps(2);+    // The same button the comment tools have, shared with them.+    h.get_by_label("Keep tool selected").click();+    h.run_steps(3);+    assert!(h.state().comment_prefs.pinned);+    h.get_by_label("Keep tool selected: on");+    for (i, word) in ["First", "Second"].into_iter().enumerate() {+        let p = at(&h, 40.0, 300.0 - 60.0 * i as f32);+        click(&mut h, p);+        assert!(h.state().views[0].content.draft.is_some(), "{word}: the editor opened");+        h.event(egui::Event::Text(word.into()));+        h.run_steps(2);+        h.get_by_label("Done adding text").click();+        h.run_steps(3);+        assert_eq!(h.state().quick_tool, QuickTool::AddText, "{word}: still adding text");+    }+    assert_eq!(texts(&h), ["First", "Second"]);+    // Unpinned, Done goes back to selecting.+    h.get_by_label("Keep tool selected: on").click();+    h.run_steps(3);+    assert!(!h.state().comment_prefs.pinned);+    let p = at(&h, 40.0, 120.0);+    click(&mut h, p);+    h.event(egui::Event::Text("Third".into()));+    h.run_steps(2);+    h.get_by_label("Done adding text").click();+    h.run_steps(3);+    assert_eq!(h.state().quick_tool, QuickTool::Select);+    assert_eq!(texts(&h), ["First", "Second", "Third"]);+}+ #[test] fn typing_moving_styling_and_deleting_added_content() {     let mut h = harness();diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml--- a/parity/acrobat-features.toml+++ b/parity/acrobat-features.toml@@ -2022,8 +2022,8 @@ milestone = "M7" status = "shipped" commands = ["edit.text"] tools = ["page_add_text", "content_update"]-evidence = ["crates/ui-egui/tests/it/content.rs::typing_moving_styling_and_deleting_added_content", "crates/ui-egui/tests/it/content.rs::clicking_elsewhere_keeps_the_text_and_starts_a_new_box", "crates/automation/tests/it/automation.rs::adding_content_through_tools", "crates/edit/src/tests.rs::added_text_and_images_are_page_content_that_stays_editable", "crates/engine/src/tests.rs::added_text_and_images_render_and_stay_editable", "crates/edit/src/tests.rs::added_arabic_text_is_shaped_in_display_order_and_stays_searchable", "crates/automation/tests/it/automation.rs::thai_text_through_tools_saves_reopens_extracts_and_renders", "crates/ui-egui/tests/it/content.rs::typing_thai_text_keeps_marks_and_commits_to_page", "crates/automation/tests/it/automation.rs::added_arabic_text_keeps_copy_search_and_editor_order_after_retyping_and_reopen", "crates/edit/src/tests.rs::added_hebrew_text_is_drawn_right_to_left_and_stays_searchable", "crates/automation/tests/it/automation.rs::hebrew_text_through_tools_saves_reopens_extracts_and_renders"]-notes = "page content, editable later (retype, move, restyle); standard fonts, Windows-1252 text. Arabic text is shaped with the craft-fonts Arab face (Type 3 outlines with ToUnicode) and laid out in bidirectional display order; per-line ActualText keeps copy, search and editor text in typing order through retyping and save/reopen; it is drawn regular whatever the bold/italic setting, and typing in the box is still left to right (misc.rtl-ui). Hebrew (#128) takes the same path with the craft-fonts Hebr face (Noto Sans Hebrew): right-to-left display order, points and cantillation kept on their letter as one glyph, ToUnicode and per-line ActualText; regular style only, and right-to-left scripts other than Arabic and Hebrew are not covered. Thai Add Text uses the craft-fonts Thai face, with shaped clusters and ToUnicode; Thai is regular style, wrapping preserves clusters but is not dictionary-based, and forms/Fill and Sign are not covered by this change. Each click on the page keeps the box being typed and starts a new one; Done and Discard beside the box"+evidence = ["crates/ui-egui/tests/it/content.rs::typing_moving_styling_and_deleting_added_content", "crates/ui-egui/tests/it/content.rs::clicking_elsewhere_keeps_the_text_and_starts_a_new_box", "crates/automation/tests/it/automation.rs::adding_content_through_tools", "crates/edit/src/tests.rs::added_text_and_images_are_page_content_that_stays_editable", "crates/engine/src/tests.rs::added_text_and_images_render_and_stay_editable", "crates/edit/src/tests.rs::added_arabic_text_is_shaped_in_display_order_and_stays_searchable", "crates/automation/tests/it/automation.rs::thai_text_through_tools_saves_reopens_extracts_and_renders", "crates/ui-egui/tests/it/content.rs::typing_thai_text_keeps_marks_and_commits_to_page", "crates/automation/tests/it/automation.rs::added_arabic_text_keeps_copy_search_and_editor_order_after_retyping_and_reopen", "crates/edit/src/tests.rs::added_hebrew_text_is_drawn_right_to_left_and_stays_searchable", "crates/automation/tests/it/automation.rs::hebrew_text_through_tools_saves_reopens_extracts_and_renders", "crates/ui-egui/tests/it/content.rs::the_pin_keeps_add_text_selected_after_done"]+notes = "page content, editable later (retype, move, restyle); standard fonts, Windows-1252 text. Arabic text is shaped with the craft-fonts Arab face (Type 3 outlines with ToUnicode) and laid out in bidirectional display order; per-line ActualText keeps copy, search and editor text in typing order through retyping and save/reopen; it is drawn regular whatever the bold/italic setting, and typing in the box is still left to right (misc.rtl-ui). Hebrew (#128) takes the same path with the craft-fonts Hebr face (Noto Sans Hebrew): right-to-left display order, points and cantillation kept on their letter as one glyph, ToUnicode and per-line ActualText; regular style only, and right-to-left scripts other than Arabic and Hebrew are not covered. Thai Add Text uses the craft-fonts Thai face, with shaped clusters and ToUnicode; Thai is regular style, wrapping preserves clusters but is not dictionary-based, and forms/Fill and Sign are not covered by this change. Each click on the page keeps the box being typed and starts a new one; Done and Discard beside the box. With the quick bar's pin on (the same \"Keep tool selected\" button and setting as the comment tools, #4863) Done leaves Add text selected, so each click on the page starts another box; unpinned, Done goes back to Select. The pin does not change what Esc does (it cancels the open box)."  [[feature]] id = "edit.find-replace"@@ -3944,7 +3944,7 @@ status = "shipped" commands = ["comment.line"] tools = [] evidence = ["crates/ui-egui/tests/it/comment_pin.rs::pin_from_quick_bar_survives_restart_and_unpinning", "crates/ui-egui/tests/it/comment_pin.rs::legacy_settings_keep_the_default_and_other_preferences", "crates/ui-egui/tests/it/comment_pin.rs::malformed_pin_values_preserve_the_current_choice", "crates/ui-egui/tests/it/control.rs::comment_pin_is_reported_and_persistent"]-notes = "The annotation quick bar's shared pin survives restarts as a boolean setting (#4253); unpinning is remembered too. Old settings default to one-shot use, and malformed values keep the current choice. UI-only preference, reachable through opt-in ui.set key=comment-tool-pinned and reported by ui.state.comment_tool_pinned. Startup uses Select; the shared pin applies when an annotation tool is picked. Independent per-tool pins and restoring the active tool are outside this preference."+notes = "The annotation quick bar's shared pin survives restarts as a boolean setting (#4253); unpinning is remembered too. Old settings default to one-shot use, and malformed values keep the current choice. UI-only preference, reachable through opt-in ui.set key=comment-tool-pinned and reported by ui.state.comment_tool_pinned. Startup uses Select; the shared pin applies when an annotation tool is picked, and also keeps Add text selected after Done (#4863). Independent per-tool pins and restoring the active tool are outside this preference."  [[feature]] id = "comment.author-identity"