From ae9d6246e778a14402bd13fe09ef4a69426c999e Mon Sep 17 00:00:00 2001 From: abc2cb Date: Thu, 15 Oct 2026 09:50:45 +0300 Subject: [PATCH] M7: the Edit tab turns on Edit text & images, as in Acrobat (#2241) * M7: the Edit tab turns on Edit text & images, as in Acrobat Entering Edit boxes every paragraph and image on the page at once, so text is editable with one click instead of first finding Edit text & images in the tool list. The floating toolbar gets an Edit text & images button while Edit is open or the tool is in use. Leaving Edit (or turning the tool off there) applies a paragraph still open in the editor, as clicking away from it does; left open, its page kept showing the paragraph's text removed. Co-Authored-By: Claude Opus 5.5 * M7: parity notes for the Edit tab turning on Edit text & images Co-Authored-By: Claude Opus 5.5 * M7: the panel-reveal test looks for the Edit panel's own row With Edit text & images on, its name is on the floating toolbar as well as in the panel, so the label alone matched twice and no longer showed that the Tools tab was revealed. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: abc2cb <271337822+abc2cb@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 --- crates/ui-egui/src/canvas.rs | 12 ++++++++++++ crates/ui-egui/src/edit_text_ui.rs | 19 ++++++++++++------- crates/ui-egui/src/lib.rs | 21 ++++++++++++++++++++- crates/ui-egui/tests/control.rs | 3 ++- crates/ui-egui/tests/ui.rs | 24 ++++++++++++++++++++++++ parity/acrobat-features.toml | 4 ++-- 6 files changed, 72 insertions(+), 11 deletions(-) diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 2ede8f1f89..f5308792d9 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -4195,6 +4195,18 @@ fn quick_bar(app: &mut PdfCraftApp, area: Rect, ui: &mut egui::Ui) { } }); } + // Edit text & images, while the Edit workspace is open (Acrobat's Edit toolbar) + // or the tool is in use. + if app.mode == crate::Mode::Edit || app.quick_tool == QuickTool::EditText { + let on = app.quick_tool == QuickTool::EditText; + if icons::button(ui, "text-cursor-input", 32.0, on, tl!("Edit text & images")).clicked() { + if on { + app.put_down_edit_text(); + } else { + app.quick_tool = QuickTool::EditText; + } + } + } // Fill & Sign ▸ (text, marks, date, signature). let current_fill = match app.quick_tool { QuickTool::Fill(f) => Some(f), diff --git a/crates/ui-egui/src/edit_text_ui.rs b/crates/ui-egui/src/edit_text_ui.rs index 620ba2b30b..b1629318a6 100644 --- a/crates/ui-egui/src/edit_text_ui.rs +++ b/crates/ui-egui/src/edit_text_ui.rs @@ -1284,18 +1284,23 @@ pub(crate) fn overlay(ctx: &egui::Context, view: &mut DocView, info: &DocInfo) - match done { Some(apply) => { let ed = view.line_editor.take()?; - let style = ed.style(); - (apply && (ed.text != ed.original || style != pdfcraft_engine::BlockStyle::default())).then_some(Edit::EditTextBlock { - page: ed.page, - block: ed.block, - text: ed.text, - style, - }) + if apply { finished_edit(ed) } else { None } } None => None, } } +/// The edit a closed paragraph editor applies, or `None` when nothing in it changed. +pub(crate) fn finished_edit(ed: LineEditor) -> Option { + let style = ed.style(); + (ed.text != ed.original || style != pdfcraft_engine::BlockStyle::default()).then_some(Edit::EditTextBlock { + page: ed.page, + block: ed.block, + text: ed.text, + style, + }) +} + /// Caret colour that stays visible on a field filled with `fill`: black on light fills, white on dark ones. pub(crate) fn caret_color_on(fill: Color32) -> Color32 { let luma = 299 * u32::from(fill.r()) + 587 * u32::from(fill.g()) + 114 * u32::from(fill.b()); diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 6a1ddd4f7b..a40e3e4b1a 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -2131,7 +2131,9 @@ impl PdfCraftApp { } } - /// Select the workspace and its matching tool panel, just like the mode bar. + /// Select the workspace and its matching tool panel, just like the mode bar. Edit turns on + /// Edit text & images at once, as Acrobat does: every paragraph and image on the page gets its + /// box and is editable with one click. Leaving Edit puts the editing tools down again. pub(crate) fn select_mode(&mut self, mode: Mode) { self.mode = mode; self.reveal_tools(); @@ -2144,6 +2146,23 @@ impl PdfCraftApp { Mode::Sign => LeftPanel::Tool("fill_sign"), _ => LeftPanel::AllTools, }; + if mode == Mode::Edit { + self.quick_tool = QuickTool::EditText; + } else if matches!(self.quick_tool, QuickTool::EditText | QuickTool::AddText) { + self.put_down_edit_text(); + } + } + + /// Back to the Select tool from an editing tool. A paragraph still open in the editor is + /// applied, as clicking away from it does; left open, its page would keep showing the + /// paragraph's text removed with no editor over it. + pub(crate) fn put_down_edit_text(&mut self) { + self.quick_tool = QuickTool::Select; + if let Some(ed) = self.active.and_then(|i| self.views.get_mut(i)).and_then(|v| v.line_editor.take()) + && let Some(edit) = edit_text_ui::finished_edit(ed) + { + self.apply_edit(edit); + } } /// Switches workspace because the user chose to (mode bar, Read mode command). Entering Read diff --git a/crates/ui-egui/tests/control.rs b/crates/ui-egui/tests/control.rs index afa4d72bbb..847ffcc754 100644 --- a/crates/ui-egui/tests/control.rs +++ b/crates/ui-egui/tests/control.rs @@ -1346,7 +1346,8 @@ fn explicit_panel_requests_reveal_inactive_group_members() { h.get_by_label("This document has no bookmarks."); ok(&mut h, &c, "ui.command", json!({"id":"edit.edit_text"})); h.run_steps(4); - h.get_by_label("Edit text & images"); + // The Edit panel's own row: the tool's name is on the floating toolbar too while it is on. + h.get_by_label("Header and footer"); } #[test] diff --git a/crates/ui-egui/tests/ui.rs b/crates/ui-egui/tests/ui.rs index 077b3ccece..a40ac422c8 100644 --- a/crates/ui-egui/tests/ui.rs +++ b/crates/ui-egui/tests/ui.rs @@ -1308,3 +1308,27 @@ fn the_combine_tab_does_not_move_and_documents_stay_in_order() { drag_named_tab(&mut h, "Combine files", first.left_center() - egui::vec2(10.0, 0.0), true); assert_eq!(h.state().views.iter().map(|view| view.id).collect::>(), ids); } + +/// The Edit tab turns on Edit text & images at once, as Acrobat does, so every paragraph and image +/// is boxed and editable with one click; another tab puts the tool down again. +#[test] +fn the_edit_tab_turns_on_edit_text_and_images() { + use pdfcraft_ui_egui::{Mode, QuickTool}; + let mut h = harness(|app| app.open_bytes("fixture.pdf", None, FIXTURE.to_vec()).expect("fixture opens")); + assert_eq!(h.state().quick_tool, QuickTool::Select); + h.get_by_label("Edit").click(); + h.run_steps(3); + assert_eq!(h.state().mode, Mode::Edit); + assert_eq!(h.state().quick_tool, QuickTool::EditText); + h.get_by_label("All tools").click(); + h.run_steps(3); + assert_eq!(h.state().mode, Mode::AllTools); + assert_eq!(h.state().quick_tool, QuickTool::Select); + // Another tool picked inside Edit is not taken away by leaving it. + h.get_by_label("Edit").click(); + h.run_steps(3); + h.state_mut().quick_tool = QuickTool::Hand; + h.get_by_label("Convert").click(); + h.run_steps(3); + assert_eq!(h.state().quick_tool, QuickTool::Hand); +} diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index 2c84db6eb2..d0d0564c31 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -1866,8 +1866,8 @@ milestone = "M7" status = "partial" commands = ["edit.edit_text"] tools = ["text_lines", "text_paragraphs", "text_edit", "text_edit_layout", "text_font_files", "text_font_import", "text_fonts"] -evidence = ["crates/edit/src/tests.rs::paragraphs_are_found_and_rewrapped", "crates/edit/src/tests.rs::text_lines_are_found_and_replaced_in_place", "crates/ui-egui/tests/editing.rs::editing_existing_text_in_place", "crates/automation/tests/automation.rs::editing_existing_text_through_tools", "crates/edit/src/tests.rs::a_line_whose_operator_starts_the_next_stream_can_be_edited", "crates/automation/tests/automation.rs::a_line_split_across_content_streams_is_listed_and_edited", "crates/ui-egui/tests/editing.rs::selected_text_formatting_keeps_the_rest_of_the_paragraph_and_reopens", "crates/edit/src/tests.rs::marked_paragraph_boundaries_and_cross_stream_transforms_survive_rewrite", "crates/automation/tests/automation.rs::explicit_font_and_disjoint_ranges_save_reopen_through_tools", "crates/edit/src/tests.rs::disjoint_style_ranges_preserve_unselected_runs_and_validate_utf8_atomically", "crates/engine/tests/document_fonts.rs::opentype_cff_document_font_preserves_program_codes_and_rendering_after_reopen", "crates/ui-egui/tests/editing.rs::cropped_images_do_not_intercept_text_and_stay_inside_each_page", "crates/automation/tests/automation.rs::clipped_image_bounds_and_text_edit_save_reopen_through_tools", "crates/edit/src/tests.rs::mixed_styles_and_matrix_sizes_form_visual_regions_without_merging_columns", "crates/edit/src/tests.rs::rich_region_edits_preserve_styles_positions_and_delete_all_runs_after_reopen", "crates/ui-egui/tests/editing.rs::one_editor_contains_mixed_styles_and_visual_rows", "crates/ui-egui/tests/editing.rs::inline_editor_preserves_source_font_and_previews_live_bounds", "crates/fonts/src/pdf.rs::mac_subset_cmap_uses_pdf_unicode_without_changing_source_program", "crates/automation/tests/automation.rs::mixed_visual_region_edit_preview_save_reopen_and_undo_through_tools"] -notes = "click a paragraph box and retype it in place (font reused or Helvetica substituted, colour and spacing kept, rewrapped); a page's /Contents pieces are parsed joined, so a line whose operands and operator sit in different pieces is listed and edited (lines and paragraphs still don't join across pieces; Edit image still parses each piece alone); drag a box to move it or its right-edge handle to rewrap it (edit.text-move-resize); per-character formatting changes are next Since #1116: mixed-style visual regions retain source rows, subset encodings, colour and baseline anchors in one textless-background editor. Explicit paragraph/heading scopes constrain grouping; compatible graphics planes permit cross-stream regions. Full matching document fonts complete absent subset glyphs; installed/file selection, TrueType/OpenType-CFF embedding and disjoint UTF-8 range styles have save/reopen coverage. Single-face OpenType shaping/bidi retains logical text through ToUnicode/ActualText. Legacy non-SFNT preview, mixed-font/style contextual runs, full structure-tree preservation and author intent in untagged PDFs remain limited; see docs/editing-existing-text.md" +evidence = ["crates/edit/src/tests.rs::paragraphs_are_found_and_rewrapped", "crates/edit/src/tests.rs::text_lines_are_found_and_replaced_in_place", "crates/ui-egui/tests/editing.rs::editing_existing_text_in_place", "crates/automation/tests/automation.rs::editing_existing_text_through_tools", "crates/edit/src/tests.rs::a_line_whose_operator_starts_the_next_stream_can_be_edited", "crates/automation/tests/automation.rs::a_line_split_across_content_streams_is_listed_and_edited", "crates/ui-egui/tests/editing.rs::selected_text_formatting_keeps_the_rest_of_the_paragraph_and_reopens", "crates/edit/src/tests.rs::marked_paragraph_boundaries_and_cross_stream_transforms_survive_rewrite", "crates/automation/tests/automation.rs::explicit_font_and_disjoint_ranges_save_reopen_through_tools", "crates/edit/src/tests.rs::disjoint_style_ranges_preserve_unselected_runs_and_validate_utf8_atomically", "crates/engine/tests/document_fonts.rs::opentype_cff_document_font_preserves_program_codes_and_rendering_after_reopen", "crates/ui-egui/tests/editing.rs::cropped_images_do_not_intercept_text_and_stay_inside_each_page", "crates/automation/tests/automation.rs::clipped_image_bounds_and_text_edit_save_reopen_through_tools", "crates/edit/src/tests.rs::mixed_styles_and_matrix_sizes_form_visual_regions_without_merging_columns", "crates/edit/src/tests.rs::rich_region_edits_preserve_styles_positions_and_delete_all_runs_after_reopen", "crates/ui-egui/tests/editing.rs::one_editor_contains_mixed_styles_and_visual_rows", "crates/ui-egui/tests/editing.rs::inline_editor_preserves_source_font_and_previews_live_bounds", "crates/fonts/src/pdf.rs::mac_subset_cmap_uses_pdf_unicode_without_changing_source_program", "crates/automation/tests/automation.rs::mixed_visual_region_edit_preview_save_reopen_and_undo_through_tools", "crates/ui-egui/tests/ui.rs::the_edit_tab_turns_on_edit_text_and_images"] +notes = "The Edit tab turns Edit text & images on at once, as in Acrobat (leaving the tab puts the tool down and applies an edit in progress); click a paragraph box and retype it in place (font reused or Helvetica substituted, colour and spacing kept, rewrapped); a page's /Contents pieces are parsed joined, so a line whose operands and operator sit in different pieces is listed and edited (lines and paragraphs still don't join across pieces; Edit image still parses each piece alone); drag a box to move it or its right-edge handle to rewrap it (edit.text-move-resize); per-character formatting changes are next Since #1116: mixed-style visual regions retain source rows, subset encodings, colour and baseline anchors in one textless-background editor. Explicit paragraph/heading scopes constrain grouping; compatible graphics planes permit cross-stream regions. Full matching document fonts complete absent subset glyphs; installed/file selection, TrueType/OpenType-CFF embedding and disjoint UTF-8 range styles have save/reopen coverage. Single-face OpenType shaping/bidi retains logical text through ToUnicode/ActualText. Legacy non-SFNT preview, mixed-font/style contextual runs, full structure-tree preservation and author intent in untagged PDFs remain limited; see docs/editing-existing-text.md" [[feature]] id = "edit.text-reflow"