Merge PR #2599: Context menu: the ribbon's paste choices (Paste, Keep Text Only, Merge Formatting, Paste Special…) (#2583)

ROADMAP.md: kept main's status line; progress-log and change-log rows from both sides
docs/ui-parity.md: kept main's status line; change-log rows from both sides
crates/ui-egui/src/canvas.rs: kept both sides' new tests as separate functions
This commit is contained in:
aiongg
2026-10-22 01:21:16 +00:00
3 changed files with 83 additions and 0 deletions
+2
View File
@@ -151,6 +151,7 @@ Imported Word 97–2003 `.doc`/`.dot` files now ask for a supported DOCX target
| 2026-10-17 | Equation arrays centre a row that has no `&` among rows that do, as Word does, so case equations like Word's "First Property of Radicals" no longer push the aligned row right (#2617). |
| 2026-10-16 | English dialects (#1281, #1045): English (UK), (Australia), (Canada) and the other Englishes are proofed in their own spelling — *colour* is flagged in a US document and *color* in a British one, with the right form suggested first — from our own list of American/British spellings, so nothing to install. Review › Language lists them, the .docx `w:lang` keeps them, and an English interface on a British (or Canadian, Australian…) system starts new documents in that English |
| 2026-10-15 | Triple borders (tables, paragraphs, the `###` AutoFormat line) draw as three lines on screen and in PDF, across the line for vertical borders too; they used to draw as double lines (#1740) |
| 2026-10-17 | The document's right-click menu offers the ribbon's Paste menu, item for item (#2583): Paste (the clipboard's formatting kept, Word's Keep Source Formatting), Keep Text Only, Merge Formatting and Paste Special…, where it had a single Paste. Same commands and shortcuts, with the system clipboard |
| 2026-10-14 | DOCX row table border exceptions render and survive saving. Explicit cell borders win; a row exception does not change other rows. |
| 2026-10-14 | Tab stops edited on the ruler: the corner selector picks Left/Center/Right/Decimal/Bar, a click sets a stop (Alt skips the snap), dragging moves it with a guide line and position tooltip, dragging off the ruler clears it; markers draw per kind and mirror for RTL; one `para.tabs` call and one undo step per gesture |
| 2026-10-12 | Fields keep `w:dirty` through DOCX saves (#877): a begin marker flagged by a generator that couldn't compute a result (pandoc-style TOC placeholders) keeps the flag on open/save — as a buffered field or a range field — so Word still refreshes it on open; computing a result (Update Field, Update Table) clears it, like Word's F9 |
@@ -601,6 +602,7 @@ Footer page-table height and body wrapping are corrected. Other pagination gaps
| 2026-10-17 | minor | Progress log: Scale to paper size on File › Print, #2108 |
| 2026-10-17 | trivial | Non-overlapping body floating tables clear header tables; overlapping tables keep their requested position |
| 2026-10-15 | trivial | Progress log: caret inside ligatures (#1974) |
| 2026-10-17 | trivial | Progress log: the ribbon's paste choices in the context menu (#2583) |
| 2026-10-14 | trivial | Progress log: row table border exceptions |
| 2026-10-12 | minor | Czech proofing review: short-word splits rank after deletion, NOSUGGEST excludes suggestions while accepting typed forms, affix comments apply only to headers, ratios keep spaces and repeated-word messages use Czech quotes; synthetic engine end-to-end and latency checks |
| 2026-10-12 | minor | Czech spelling from the installed Hunspell dictionary, runtime-table suggestions and generic checks; Czech interface writes cs-CZ documents; no Czech hyphenation patterns bundled; estimates unchanged |
+80
View File
@@ -3167,7 +3167,12 @@ fn context_menu(app: &mut WordApp, ui: &mut Ui) {
}
item(ui, app, "Cut", "edit.cut", json!({}));
item(ui, app, "Copy", "edit.copy", json!({}));
// How to paste, chosen before pasting (#2583): the ribbon's Paste menu, item for item. Paste
// keeps the clipboard's formatting (Word's Keep Source Formatting).
item(ui, app, "Paste", "edit.paste", json!({}));
item(ui, app, "Keep Text Only", "edit.pasteText", json!({}));
item(ui, app, "Merge Formatting", "edit.pasteMerge", json!({}));
item(ui, app, "Paste Special…", "edit.pasteSpecial", json!({}));
ui.separator();
if let Some(syn) = app.canvas.context_synonyms.clone().and_then(|v| v.as_array().cloned()).filter(|a| !a.is_empty()) {
ui.menu_button(tl!("Synonyms"), |ui| {
@@ -4859,6 +4864,81 @@ mod tests {
}
}
/// #2583: the context menu offers the ribbon's Paste menu, item for item, and each pastes its
/// own way: Paste keeps the copied bold and colour (Word's Keep Source Formatting), Merge
/// Formatting keeps only the bold, Keep Text Only neither; Paste Special… opens its dialog.
#[test]
fn context_menu_offers_the_ribbons_paste_items() {
use egui_kittest::kittest::Queryable;
use wordcraft_doc::props::{Rgb, TextColor};
let red = Some(TextColor::Rgb(Rgb(0xC0, 0, 0)));
let items = [
("Paste", "edit.paste"),
("Keep Text Only", "edit.pasteText"),
("Merge Formatting", "edit.pasteMerge"),
("Paste Special…", "edit.pasteSpecial"),
];
for (choice, pasted) in [
("Paste", Some((true, red))),
("Merge Formatting", Some((true, None))),
("Keep Text Only", Some((false, None))),
("Paste Special…", None),
] {
let mut a = WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::from_text("Bold\nTarget")), Default::default());
a.session
.doc
.format_range(&Pos::body(0, 0), &Pos::body(0, 4), &|c| {
c.bold = Some(true);
c.color = red;
})
.unwrap();
a.session.relayout();
a.run("select.text", json!({"text": "Bold"})).unwrap();
a.run("edit.copy", json!({})).unwrap();
let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1440.0, 900.0)).build_ui_state(
|ui, app: &mut WordApp| {
let ctx = ui.ctx().clone();
app.logic(&ctx);
app.ui(ui);
},
a,
);
for _ in 0..6 {
h.step();
}
let c = h.state_mut().session.layout().caret(&Pos::body(1, 6)).expect("laid out");
let at = page_to_screen(h.state_mut(), c.page, c.x, c.top + c.height / 2.0).expect("on screen");
h.event(egui::Event::PointerMoved(at));
for pressed in [true, false] {
h.event(egui::Event::PointerButton { pos: at, button: egui::PointerButton::Secondary, pressed, modifiers: Default::default() });
h.step();
}
h.step();
// Each item reads as its name and its shortcut (none for Merge Formatting).
let labels: Vec<String> = items.iter().map(|(name, id)| format!("{name} {}", crate::widgets::shortcut_text(h.state(), id))).collect();
for l in &labels {
assert!(h.query_by_label(l).is_some(), "{choice}: {l:?} is offered");
}
let k = items.iter().position(|(name, _)| *name == choice).unwrap();
h.get_by_label(&labels[k]).click();
for _ in 0..3 {
h.step();
}
match pasted {
Some((bold, colour)) => {
let doc = &h.state().session.doc;
assert_eq!(doc.plain_text(StoryRef::Body), "Bold\nTargetBold", "{choice}: pasted at the end of the second paragraph");
let p = doc.para_at(&Pos::body(1, 0)).expect("the target paragraph");
assert_eq!(p.props_of_char(7).bold == Some(true), bold, "{choice}: bold");
assert_eq!(p.props_of_char(7).color, colour, "{choice}: colour");
}
None => {
assert!(matches!(h.state().dialog, Some(crate::dialogs::Dialog::PasteSpecial { .. })), "{choice}: the dialog opens");
}
}
}
}
/// #996: on macOS a Control-click is the right-click (the trackpad's and the one-button
/// mouse's way to it), so it opens the same menu and leaves the caret where the menu needs it.
#[cfg(target_os = "macos")]
+1
View File
@@ -121,6 +121,7 @@ Footnote and endnote editing keeps body and note text at normal opacity, on ligh
| 2026-10-15 | trivial | Text Highlight Color: No Color becomes the Highlight button's choice, as in Word, so the next click on the button removes highlighting instead of applying the colour picked before; the ribbon and the mini toolbar share one menu (#2105) |
| 2026-10-16 | trivial | Body and active note text keep normal opacity while editing footnotes or endnotes (#2545); header/footer dimming is unchanged |
| 2026-10-16 | trivial | Right-clicking a picture, shape, text box or chart offers Insert Caption…; a picture in a table gets a Figure caption in its cell. Page Setup has Headers and footers check boxes (Different Odd & Even Pages, Different First Page), applied on OK only if changed (#1230) |
| 2026-10-17 | trivial | Context menu: Paste, Keep Text Only, Merge Formatting and Paste Special… replace the single Paste, the same items as Home › Paste's menu (#2583). Word's context menu shows its Paste Options as a row of icons; here they are labelled items with their shortcuts, as on the ribbon |
| 2026-10-13 | trivial | New/Modify Table Style: No Border on a part that inherits its lines hides them; emptying the font box makes the part's font inherited again (#1288) |
| 2026-10-17 | minor | Insert › Drop Cap is a menu (None, Dropped, In margin, Drop Cap Options…) with a Drop Cap dialog (position, font, lines 1–10, distance 0–22 in), #2601; Modal dialogs 56 → 57 |
| 2026-10-14 | trivial | Tab stops edited on the ruler: corner kind selector, click to set, drag to move or off to clear, per-kind markers mirrored for RTL; each gesture one `para.tabs` call and one undo step; Rulers 85% → 90% |