viewer: click to copy values, amounts, and identifiers

Company details (EIN, state number, address, every profile value), an
entry's number and amount, an account's code and balance, a document's
file name and SHA-256 copy on click: a pointer and copy icon on hover,
a check for a moment after. Amounts copy as their exact plain decimal
(fmt::plain: no "$", commas, or parentheses; "-" for negatives); where a
click already drills down (report cells, register and transaction rows,
general ledger lines), a right-click menu copies the amounts, the entry
number, or the description. The sidebar's chain check copies the
journal head and data folder.

tests/copy.rs: every display style copies back to the exact value
(property), and clicking the EIN, an entry's amount, and its number puts
exactly that on the clipboard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-14 15:38:05 -06:00
1 parent c126843ce1
commit afd427a023
10 files changed
+344 -17

No files matched your search

+32 -1
View File
@@ -1540,15 +1540,37 @@ fn render(ui: &mut Ui, t: &Table, pdf: &PdfInfo, shell: &mut Shell<'_>) {
egui::pos2(x1 - col_w + 8.0, rr.top()),
egui::pos2(x1, rr.bottom()),
);
let copy = fmt::plain(&text);
let cresp = ui.interact(
cell,
ui.id().with(("cell", ri, i)),
if c.drill.is_some() {
if c.drill.is_some() || copy.is_some() {
Sense::click()
} else {
Sense::hover()
},
);
// An amount copies as its plain decimal: a click
// where the cell doesn't drill down, the right-click
// menu where it does.
if let Some(v) = &copy {
if c.drill.is_some() {
cresp.context_menu(|ui| widgets::copy_item(ui, "Copy amount", v));
} else {
if cresp.hovered() {
ui.ctx().set_cursor_icon(egui::CursorIcon::PointingHand);
}
if cresp.clicked() {
widgets::copy_now(ui, cresp.id, v);
}
let note = if widgets::recently_copied(ui, cresp.id) {
"Copied"
} else {
"Click to copy"
};
cresp.clone().on_hover_text(note);
}
}
let hovered = c.drill.is_some() && cresp.hovered();
let vcolor = if hovered { p.accent } else { color };
let vfont = if matches!(r.kind, Kind::Line) {
@@ -1677,6 +1699,15 @@ fn gl(ui: &mut Ui, b: &Books, s: NaiveDate, e: NaiveDate, shell: &mut Shell<'_>)
color: egui::Color32|
-> egui::Response {
let (r, resp) = ui.allocate_exact_size(vec2(w, 24.0), Sense::click());
resp.context_menu(|ui| {
for v in &vals {
if let Some(id) = v.strip_prefix('#') {
widgets::copy_item(ui, "Copy entry number", &format!("#{id}"));
} else if let Some(a) = fmt::plain(v) {
widgets::copy_item(ui, &format!("Copy {v}"), &a);
}
}
});
if resp.hovered() && !strong {
ui.painter().rect_filled(r, CornerRadius::same(4), p.hover);
}