From 6365e2ed476aa9ddd1b7bb137f32af6e6575f142 Mon Sep 17 00:00:00 2001 From: RiceDee <345114586+RiceDee@users.noreply.github.com> Date: Sun, 1 Nov 2026 16:42:02 +1100 Subject: [PATCH 1/3] fix(ui): centre the quick-tools bar in the margin beside the page (#5635) The bar stood a fixed 14 pt from the side panel, so its gap to the page changed with zoom and window size. It now centres in the margin between the panel and the page when the margin holds it, within the first 100 pt; otherwise it keeps 14 pt in. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/canvas.rs | 4 +++- crates/ui-egui/src/canvas/bars.rs | 39 ++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 5625587a69..62fc39ccae 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -3486,7 +3486,9 @@ fn apply_frame_requests(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui, // Read mode hides the tools (#2646), and full screen is the page and nothing else (#1459); // the quick tools come back with the window. if app.mode != crate::Mode::Read && !app.full_screen { - quick_bar(app, avail, ui); + // The page nearest the left edge, whose margin the bar sits in. + let page_left = app.views[index].screen_rects.iter().map(|(_, r)| r.left()).reduce(f32::min); + quick_bar(app, avail, page_left, ui); } if let Some((source, file, destination)) = remote_open { app.open_remote_link(source, file, destination); diff --git a/crates/ui-egui/src/canvas/bars.rs b/crates/ui-egui/src/canvas/bars.rs index 2472366672..c5b4586946 100644 --- a/crates/ui-egui/src/canvas/bars.rs +++ b/crates/ui-egui/src/canvas/bars.rs @@ -276,10 +276,23 @@ fn notice_bar(ui: &mut egui::Ui, t: &Tokens, icon: &str, icon_color: Color32, te }); } -/// The floating quick-action bar at the left edge of the document area. -pub(super) fn quick_bar(app: &mut PdfCraftApp, area: Rect, ui: &mut egui::Ui) { +const QUICK_BAR_WIDTH: f32 = 46.0; +const QUICK_BAR_EDGE: f32 = 14.0; + +/// How far from the side panel the quick-action bar stands, given the width of the margin between +/// the panel and the page: centred in it when it holds the bar, else 14 pt in (a page filling the +/// width). +fn quick_bar_offset(margin: f32) -> f32 { + // shortcut: centred in at most 100 pt of margin, so a wide margin keeps the bar by the panel. + let margin = margin.min(100.0); + if margin >= QUICK_BAR_WIDTH + 2.0 * 8.0 { (margin - QUICK_BAR_WIDTH) / 2.0 } else { QUICK_BAR_EDGE } +} + +/// The floating quick-action bar in the margin between the side panel and the page. +pub(super) fn quick_bar(app: &mut PdfCraftApp, area: Rect, page_left: Option, ui: &mut egui::Ui) { let t = Tokens::get(ui.ctx()); - let pos = area.left_top() + vec2(14.0, 14.0); + let x = quick_bar_offset(page_left.map_or(0.0, |left| left - area.left())); + let pos = area.left_top() + vec2(x, QUICK_BAR_EDGE); egui::Area::new(egui::Id::unique("quick-bar")).order(egui::Order::Middle).fixed_pos(pos).show(ui.ctx(), |ui| { egui::Frame::NONE .fill(t.card) @@ -496,3 +509,23 @@ pub(super) fn quick_bar(app: &mut PdfCraftApp, area: Rect, ui: &mut egui::Ui) { }); }); } + +#[cfg(test)] +mod tests { + use super::quick_bar_offset; + + #[test] + fn the_quick_bar_centres_in_the_margin_that_holds_it() { + assert_eq!(quick_bar_offset(72.0), 13.0); + assert_eq!(quick_bar_offset(62.0), 8.0); + // A wide margin centres in its first 100 pt, not all of it. + assert_eq!(quick_bar_offset(400.0), 27.0); + } + + #[test] + fn the_quick_bar_keeps_14_pt_in_when_the_margin_cannot_hold_it() { + for margin in [61.9, 30.0, 0.0, -50.0, f32::NEG_INFINITY] { + assert_eq!(quick_bar_offset(margin), 14.0, "{margin}"); + } + } +} From db0383b97c5b55cfe99718e95c64a20cb15b8e0f Mon Sep 17 00:00:00 2001 From: RiceDee <345114586+RiceDee@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:12:07 +1100 Subject: [PATCH 2/3] fix(ui): the quick bar centres by its rendered width, and a NaN margin keeps 14 pt (#5635) Fill & Sign's Text and Date tools widen the bar, so the fit check and the centring now use last frame's width of the bar. A NaN margin used to count as 100 pt through `f32::min`. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/canvas/bars.rs | 36 ++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/crates/ui-egui/src/canvas/bars.rs b/crates/ui-egui/src/canvas/bars.rs index c5b4586946..8fed30ffea 100644 --- a/crates/ui-egui/src/canvas/bars.rs +++ b/crates/ui-egui/src/canvas/bars.rs @@ -276,24 +276,29 @@ fn notice_bar(ui: &mut egui::Ui, t: &Tokens, icon: &str, icon_color: Color32, te }); } +/// The bar's narrowest form: one 32 pt button, 6 pt margins and a 1 pt border each side. const QUICK_BAR_WIDTH: f32 = 46.0; const QUICK_BAR_EDGE: f32 = 14.0; /// How far from the side panel the quick-action bar stands, given the width of the margin between -/// the panel and the page: centred in it when it holds the bar, else 14 pt in (a page filling the -/// width). -fn quick_bar_offset(margin: f32) -> f32 { +/// the panel and the page and the bar's own width: centred in the margin when it holds the bar, +/// else 14 pt in (a page filling the width). +fn quick_bar_offset(margin: f32, width: f32) -> f32 { // shortcut: centred in at most 100 pt of margin, so a wide margin keeps the bar by the panel. - let margin = margin.min(100.0); - if margin >= QUICK_BAR_WIDTH + 2.0 * 8.0 { (margin - QUICK_BAR_WIDTH) / 2.0 } else { QUICK_BAR_EDGE } + // `f32::min` returns the other operand for NaN, which would pass the fit check below. + let margin = if margin.is_nan() { 0.0 } else { margin.min(100.0) }; + if margin >= width + 2.0 * 8.0 { (margin - width) / 2.0 } else { QUICK_BAR_EDGE } } /// The floating quick-action bar in the margin between the side panel and the page. pub(super) fn quick_bar(app: &mut PdfCraftApp, area: Rect, page_left: Option, ui: &mut egui::Ui) { let t = Tokens::get(ui.ctx()); - let x = quick_bar_offset(page_left.map_or(0.0, |left| left - area.left())); + let id = egui::Id::unique("quick-bar"); + // Last frame's bar: Fill & Sign's Text and Date tools add a wider row of controls to it. + let width = ui.ctx().memory(|m| m.area_rect(id)).map_or(QUICK_BAR_WIDTH, |bar| bar.width().max(QUICK_BAR_WIDTH)); + let x = quick_bar_offset(page_left.map_or(0.0, |left| left - area.left()), width); let pos = area.left_top() + vec2(x, QUICK_BAR_EDGE); - egui::Area::new(egui::Id::unique("quick-bar")).order(egui::Order::Middle).fixed_pos(pos).show(ui.ctx(), |ui| { + egui::Area::new(id).order(egui::Order::Middle).fixed_pos(pos).show(ui.ctx(), |ui| { egui::Frame::NONE .fill(t.card) .stroke(Stroke::new(1.0, t.border)) @@ -516,16 +521,23 @@ mod tests { #[test] fn the_quick_bar_centres_in_the_margin_that_holds_it() { - assert_eq!(quick_bar_offset(72.0), 13.0); - assert_eq!(quick_bar_offset(62.0), 8.0); + assert_eq!(quick_bar_offset(72.0, 46.0), 13.0); + assert_eq!(quick_bar_offset(62.0, 46.0), 8.0); // A wide margin centres in its first 100 pt, not all of it. - assert_eq!(quick_bar_offset(400.0), 27.0); + assert_eq!(quick_bar_offset(400.0, 46.0), 27.0); + } + + #[test] + fn a_wider_quick_bar_centres_by_its_own_width_and_needs_a_wider_margin() { + // Fill & Sign's Text tool widens the bar to 70 pt: it centres by that, and 72 pt no longer holds it. + assert_eq!(quick_bar_offset(120.0, 70.0), 15.0); + assert_eq!(quick_bar_offset(72.0, 70.0), 14.0); } #[test] fn the_quick_bar_keeps_14_pt_in_when_the_margin_cannot_hold_it() { - for margin in [61.9, 30.0, 0.0, -50.0, f32::NEG_INFINITY] { - assert_eq!(quick_bar_offset(margin), 14.0, "{margin}"); + for margin in [61.9, 30.0, 0.0, -50.0, f32::NEG_INFINITY, f32::NAN] { + assert_eq!(quick_bar_offset(margin, 46.0), 14.0, "{margin}"); } } } From 1bb7da1372b410774ec25f42db8382a70beca231 Mon Sep 17 00:00:00 2001 From: RiceDee <345114586+RiceDee@users.noreply.github.com> Date: Sun, 1 Nov 2026 18:50:54 +1100 Subject: [PATCH 3/3] test(ui): quick_bar_offset is pinned at the margin that just holds the bar, a point less, and 1000 pt (#5635) Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/canvas/bars.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/canvas/bars.rs b/crates/ui-egui/src/canvas/bars.rs index 8fed30ffea..c3ddd4b9ef 100644 --- a/crates/ui-egui/src/canvas/bars.rs +++ b/crates/ui-egui/src/canvas/bars.rs @@ -282,7 +282,8 @@ const QUICK_BAR_EDGE: f32 = 14.0; /// How far from the side panel the quick-action bar stands, given the width of the margin between /// the panel and the page and the bar's own width: centred in the margin when it holds the bar, -/// else 14 pt in (a page filling the width). +/// else 14 pt in (a page filling the width). With no page on screen the caller passes a margin of 0, +/// which is 14 pt too. fn quick_bar_offset(margin: f32, width: f32) -> f32 { // shortcut: centred in at most 100 pt of margin, so a wide margin keeps the bar by the panel. // `f32::min` returns the other operand for NaN, which would pass the fit check below. @@ -527,6 +528,14 @@ mod tests { assert_eq!(quick_bar_offset(400.0, 46.0), 27.0); } + #[test] + fn the_margin_that_just_holds_the_bar_centres_and_a_point_less_does_not() { + // 46 pt of bar and 8 pt to spare each side: 62 pt. + assert_eq!(quick_bar_offset(62.0, 46.0), 8.0); + assert_eq!(quick_bar_offset(61.0, 46.0), 14.0); + assert_eq!(quick_bar_offset(1000.0, 46.0), 27.0); + } + #[test] fn a_wider_quick_bar_centres_by_its_own_width_and_needs_a_wider_margin() { // Fill & Sign's Text tool widens the bar to 70 pt: it centres by that, and 72 pt no longer holds it.