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:
1 parent
8ac4e5438d
commit
eba85f9c77
10 files changed
+344
-17
No files matched your search
@@ -114,15 +114,35 @@ pub fn amount_cell(
|
||||
} else {
|
||||
theme::regular(14.0)
|
||||
};
|
||||
// An amount copies as its plain decimal: a click when the cell doesn't
|
||||
// drill down, the right-click menu when it does.
|
||||
let copy = crate::fmt::plain(text);
|
||||
let (rect, resp) = ui.allocate_exact_size(
|
||||
vec2(width, 22.0),
|
||||
if drill {
|
||||
if drill || copy.is_some() {
|
||||
Sense::click()
|
||||
} else {
|
||||
Sense::hover()
|
||||
},
|
||||
);
|
||||
let p = pal(ui.ctx());
|
||||
let copy_id = resp.id.with("copy");
|
||||
if let Some(v) = © {
|
||||
if drill {
|
||||
resp.context_menu(|ui| copy_item(ui, "Copy amount", v));
|
||||
} else if resp.clicked() {
|
||||
copy_now(ui, copy_id, v);
|
||||
}
|
||||
}
|
||||
let copied = copy.is_some() && !drill && recently_copied(ui, copy_id);
|
||||
if copy.is_some() && !drill && resp.hovered() {
|
||||
ui.ctx().set_cursor_icon(egui::CursorIcon::PointingHand);
|
||||
}
|
||||
let resp = if copy.is_some() && !drill {
|
||||
resp.on_hover_text(if copied { "Copied" } else { "Click to copy" })
|
||||
} else {
|
||||
resp
|
||||
};
|
||||
let hovered = drill && resp.hovered();
|
||||
let galley = ui.painter().layout_no_wrap(
|
||||
text.to_string(),
|
||||
@@ -143,6 +163,15 @@ pub fn amount_cell(
|
||||
Stroke::new(1.0, p.accent),
|
||||
);
|
||||
}
|
||||
if copied {
|
||||
ui.painter().text(
|
||||
egui::pos2(pos.x - 4.0, rect.center().y),
|
||||
egui::Align2::RIGHT_CENTER,
|
||||
egui_phosphor::regular::CHECK,
|
||||
egui::FontId::proportional(12.0),
|
||||
p.positive,
|
||||
);
|
||||
}
|
||||
ui.painter().galley(pos, galley, color);
|
||||
resp
|
||||
}
|
||||
@@ -223,6 +252,73 @@ pub fn empty(ui: &mut Ui, icon: &str, title: &str, text: &str) {
|
||||
});
|
||||
}
|
||||
|
||||
/// Put `value` on the clipboard, remembering when under `id` (for the
|
||||
/// brief "Copied" check).
|
||||
pub fn copy_now(ui: &Ui, id: egui::Id, value: &str) {
|
||||
ui.ctx().copy_text(value.to_string());
|
||||
let now = ui.input(|i| i.time);
|
||||
ui.ctx().data_mut(|d| d.insert_temp(id, now));
|
||||
}
|
||||
|
||||
/// Whether `id` was copied in the last moment; keeps repainting until the
|
||||
/// check fades.
|
||||
pub fn recently_copied(ui: &Ui, id: egui::Id) -> bool {
|
||||
let at: Option<f64> = ui.ctx().data(|d| d.get_temp(id));
|
||||
let now = ui.input(|i| i.time);
|
||||
let recent = at.is_some_and(|t| now - t < 1.2);
|
||||
if recent {
|
||||
ui.ctx()
|
||||
.request_repaint_after(std::time::Duration::from_millis(200));
|
||||
}
|
||||
recent
|
||||
}
|
||||
|
||||
/// A value that copies itself when clicked: a pointer and a copy icon on
|
||||
/// hover, a check for a moment after.
|
||||
pub fn copyable(ui: &mut Ui, text: RichText, value: &str) -> Response {
|
||||
let id = ui.id().with(("copy", value));
|
||||
let p = pal(ui.ctx());
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 4.0;
|
||||
let r = ui.add(egui::Label::new(text).sense(Sense::click()));
|
||||
let recent = recently_copied(ui, id);
|
||||
let (slot, _) = ui.allocate_exact_size(vec2(14.0, 14.0), Sense::hover());
|
||||
if r.hovered() || recent {
|
||||
ui.painter().text(
|
||||
slot.center(),
|
||||
egui::Align2::CENTER_CENTER,
|
||||
if recent {
|
||||
egui_phosphor::regular::CHECK
|
||||
} else {
|
||||
egui_phosphor::regular::COPY
|
||||
},
|
||||
egui::FontId::proportional(12.0),
|
||||
if recent { p.positive } else { p.muted },
|
||||
);
|
||||
}
|
||||
if r.hovered() {
|
||||
ui.ctx().set_cursor_icon(egui::CursorIcon::PointingHand);
|
||||
}
|
||||
let r = r.on_hover_text(if recent { "Copied" } else { "Click to copy" });
|
||||
if r.clicked() {
|
||||
copy_now(ui, id, value);
|
||||
}
|
||||
r
|
||||
})
|
||||
.inner
|
||||
}
|
||||
|
||||
/// A "Copy …" item for a right-click menu.
|
||||
pub fn copy_item(ui: &mut Ui, label: &str, value: &str) {
|
||||
if ui
|
||||
.button(format!("{} {label}", egui_phosphor::regular::COPY))
|
||||
.clicked()
|
||||
{
|
||||
ui.ctx().copy_text(value.to_string());
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
|
||||
/// "Copy prompt": puts an agent prompt on the clipboard.
|
||||
pub fn copy_prompt(ui: &mut Ui, prompt: &str) {
|
||||
let p = pal(ui.ctx());
|
||||
|
||||
Reference in new issue
Block a user