M5.7: the quick bar's Fill & Sign button shows the tool it picks

The button showed a generic pen until clicked, then swapped to Add text's
icon. It now works like the Comment, Highlight and Draw groups: it shows
the last Fill & Sign tool used (Signature at first) and clicking it picks
that tool. The flyout marks the active saved signature or initials card,
and the cards get the pointing-hand cursor like the other rows.
This commit is contained in:
Andrew committed 2026-11-03 05:51:32 -07:00
1 parent 01ba3ac9d1
commit e90c92124c
9 files changed
+69 -15

No files matched your search

+1
View File
@@ -108,6 +108,7 @@ impl PdfCraftApp {
ui_scale_applied: None,
flatten_fill_sign_on_save: false,
fill_text_size: fill_sign::TEXT_SIZE,
fill_tool: fill_sign::FillTool::Signature,
fill_text_color: [0.0; 3],
dialog: None,
updates: updates::Updates::default(),
+6 -3
View File
@@ -333,9 +333,12 @@ impl PdfCraftApp {
field if field.starts_with("field-") => QuickTool::Field(
prepare::FieldTool::from_command(&format!("form.add.{}", &field[6..])).ok_or_else(|| format!("unknown tool {field}"))?,
),
fill if fill.starts_with("fill-") => QuickTool::Fill(
fill_sign::FillTool::from_command(&format!("sign.fill.{}", &fill[5..])).ok_or_else(|| format!("unknown tool {fill}"))?,
),
fill if fill.starts_with("fill-") => {
let f =
fill_sign::FillTool::from_command(&format!("sign.fill.{}", &fill[5..])).ok_or_else(|| format!("unknown tool {fill}"))?;
self.fill_tool = f;
QuickTool::Fill(f)
}
other => {
let t = comments::CommentTool::from_command(&format!("comment.{other}")).ok_or_else(|| format!("unknown tool {other}"))?;
self.comment_prefs.group_tool[t.group()] = t;
+1
View File
@@ -697,6 +697,7 @@ impl PdfCraftApp {
}
fill if crate::fill_sign::FillTool::from_command(fill).is_some() => {
let Some(tool) = crate::fill_sign::FillTool::from_command(fill) else { return Some(false) };
self.fill_tool = tool;
self.quick_tool = crate::QuickTool::Fill(tool);
let initials = tool == crate::fill_sign::FillTool::Initials;
if (tool == crate::fill_sign::FillTool::Signature && self.signature.is_none()) || (initials && self.initials.is_none()) {
+1
View File
@@ -1349,6 +1349,7 @@ fn dialog_signature(ui: &mut egui::Ui, app: &mut PdfCraftApp, req: &mut Pending)
app.signature = d.saved();
crate::fill_sign::FillTool::Signature
};
app.fill_tool = tool;
app.quick_tool = crate::QuickTool::Fill(tool);
app.toast = None;
}
+14 -2
View File
@@ -239,8 +239,20 @@ fn core_tool_buttons(ui: &mut egui::Ui, app: &mut PdfCraftApp, cell: f32, popup_
QuickTool::Fill(f) => Some(f),
_ => None,
};
let shown = current_fill.unwrap_or(crate::fill_sign::FillTool::Text);
let resp = icons::button(ui, if current_fill.is_some() { shown.icon() } else { "pen-line" }, cell, current_fill.is_some(), tl!("Fill & Sign"));
let shown = app.fill_tool;
let tip = match shown {
crate::fill_sign::FillTool::Signature | crate::fill_sign::FillTool::Initials => {
let initials = shown == crate::fill_sign::FillTool::Initials;
let saved = if initials { app.initials.is_some() } else { app.signature.is_some() };
crate::i18n::fmt(
tl!(if saved { "Place saved {what}" } else { "Add {what}" }),
&[("what", tl!(if initials { "initials" } else { "signature" }))],
)
}
_ => tl!(shown.label()).to_string(),
};
let tip = format!("{}: {tip}", tl!("Fill & Sign"));
let resp = icons::button(ui, shown.icon(), cell, current_fill.is_some(), &tip);
let r = resp.rect;
let tri = [r.right_bottom() + vec2(-4.0, -4.0), r.right_bottom() + vec2(-9.0, -4.0), r.right_bottom() + vec2(-4.0, -9.0)];
ui.painter().add(egui::Shape::convex_polygon(tri.to_vec(), if current_fill.is_some() { t.accent } else { t.text_faint }, Stroke::NONE));
+1
View File
@@ -331,6 +331,7 @@ pub struct PdfCraftApp {
pub flatten_fill_sign_on_save: bool,
/// Point size for new Fill & Sign text and dates, kept across launches.
pub fill_text_size: f64,
pub fill_tool: fill_sign::FillTool,
/// Colour for new Fill & Sign text and dates, kept across launches.
pub fill_text_color: [f64; 3],
pub dialog: Option<Dialog>,
+9 -4
View File
@@ -373,20 +373,22 @@ pub(crate) fn signature_entries(ui: &mut egui::Ui, app: &mut crate::PdfCraftApp,
}
continue;
};
ui.horizontal(|ui| {
let on = app.quick_tool == crate::QuickTool::Fill(if i == 0 { FillTool::Signature } else { FillTool::Initials });
let wash = ui.painter().add(egui::Shape::Noop);
let row = ui.horizontal(|ui| {
let (rect, response) = ui.allocate_exact_size(vec2((ui.available_width() - 68.0).max(140.0), 52.0), Sense::click());
let use_label = label("Use {what}");
response.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, true, use_label.clone()));
response.widget_info(|| egui::WidgetInfo::selected(egui::Role::Button, true, on, use_label.clone()));
let painter = ui.painter_at(rect);
painter.rect_filled(rect, CornerRadius::same(4), Color32::WHITE);
painter.rect_stroke(
rect,
CornerRadius::same(4),
Stroke::new(1.0, if response.hovered() { t.accent } else { t.border }),
Stroke::new(1.0, if on || response.hovered() { t.accent } else { t.border }),
egui::StrokeKind::Inside,
);
paint_saved(ui, rect.shrink(8.0), saved, &mut app.saved_signature_previews[i], &format!("saved-{what}"));
if response.on_hover_text(label("Place saved {what}")).clicked() {
if crate::theme::hand(response).on_hover_text(label("Place saved {what}")).clicked() {
command = Some(use_id);
}
if crate::icons::button(ui, "pencil", 26.0, false, &label("Change {what}")).clicked() {
@@ -396,6 +398,9 @@ pub(crate) fn signature_entries(ui: &mut egui::Ui, app: &mut crate::PdfCraftApp,
command = Some(remove_id);
}
});
if on {
ui.painter().set(wash, egui::Shape::rect_filled(row.response.rect.expand(3.0), CornerRadius::same(6), t.accent_soft));
}
ui.add_space(4.0);
}
command
+1 -1
View File
@@ -3280,7 +3280,7 @@ fn hotbar_docked_left_does_not_override_quick_tools() {
});
h.run_steps(5);
// Controls in the docked bar share one layout, so none may overlap (#5890).
let labels = ["Drag hotbar", "Select (V)", "Hand (H)", "Add a sticky note", "Highlight", "Draw", "Fill & Sign", "Hotbar settings"];
let labels = ["Drag hotbar", "Select (V)", "Hand (H)", "Add a sticky note", "Highlight", "Draw", "Fill & Sign: Add signature", "Hotbar settings"];
let rects: Vec<_> = labels.iter().map(|&label| h.get_by_label(label).rect()).collect();
for (i, a) in rects.iter().enumerate() {
for (j, b) in rects.iter().enumerate().skip(i + 1) {
+35 -5
View File
@@ -2,7 +2,7 @@
use egui::{Pos2, pos2};
use egui_kittest::Harness;
use egui_kittest::kittest::Queryable;
use egui_kittest::kittest::{NodeT, Queryable};
use pdfcraft_ui_egui::fill_sign::{FillTool, SavedSig};
use pdfcraft_ui_egui::{Dialog, PdfCraftApp, QuickTool};
@@ -423,9 +423,9 @@ fn changing_saved_signatures_and_initials_preserves_placed_marks() {
let original = format!("{:?}", h.state().session.get(h.state().views[0].id).unwrap().info.annotations);
// The quick toolbar exposes replacement without changing the normal placement action.
h.get_by_label("Fill & Sign").click();
h.get_by_label("Fill & Sign: Place saved signature").click();
h.run_steps(2);
h.get_by_label("Fill & Sign").click();
h.get_by_label("Fill & Sign: Place saved signature").click();
h.run_steps(2);
h.get_by_label("Change signature").click();
h.run_steps(2);
@@ -445,9 +445,9 @@ fn changing_saved_signatures_and_initials_preserves_placed_marks() {
click(&mut h, 40.0, 200.0);
assert_eq!(items(&h).len(), 2);
h.get_by_label("Fill & Sign").click();
h.get_by_label("Fill & Sign: Place saved signature").click();
h.run_steps(2);
h.get_by_label("Fill & Sign").click();
h.get_by_label("Fill & Sign: Place saved signature").click();
h.run_steps(2);
h.get_by_label("Change initials").click();
h.run_steps(2);
@@ -1812,3 +1812,33 @@ fn mode_tabs_keep_the_hand_over_a_type_box_scrolled_under_them() {
h.run_steps(3);
assert_eq!(h.state().mode, pdfcraft_ui_egui::Mode::Convert, "the tab takes the click, not the text box");
}
#[test]
fn the_quick_bar_fill_button_shows_the_tool_it_picks() {
let mut h = harness();
h.get_by_label("Fill & Sign: Add signature").click();
h.run_steps(2);
assert_eq!(h.state().quick_tool, QuickTool::Fill(FillTool::Signature));
assert_eq!(h.state().dialog, Some(Dialog::Signature));
h.state_mut().dialog = None;
h.state_mut().signature = Some(SavedSig::Typed("Ada Lovelace".into()));
h.state_mut().quick_tool = QuickTool::Select;
h.run_steps(2);
h.get_by_label("Fill & Sign: Place saved signature").click();
h.run_steps(2);
assert_eq!(h.state().quick_tool, QuickTool::Fill(FillTool::Signature));
assert_eq!(h.state().dialog, None);
h.get_by_label("Fill & Sign: Place saved signature").click();
h.run_steps(2);
assert_eq!(h.get_by_label("Use signature").accesskit_node().toggled(), Some(egui::accesskit::Toggled::True), "the active tool's card is marked");
h.key_press(egui::Key::Escape);
h.run_steps(2);
h.state_mut().execute("sign.fill.check");
h.state_mut().quick_tool = QuickTool::Select;
h.run_steps(2);
h.get_by_label("Fill & Sign: Checkmark").click();
h.run_steps(2);
assert_eq!(h.state().quick_tool, QuickTool::Fill(FillTool::Check), "the button keeps the last Fill & Sign tool");
}