From 2a3dc47291837662909b550b3cbd30f44ca0049b Mon Sep 17 00:00:00 2001 From: jpeskov <101976824+jpeskov@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:31:56 +0530 Subject: [PATCH] fix(print): restore previous paper size after leaving Label mode (#3564) Choosing Label under Page Sizing & Handling swaps the paper to the 4 x 6 in label stock. Leaving Label for Size, Poster, Multiple or Booklet now gives back the paper chosen before, unless another paper was picked while in Label. Fixes #3564. Co-Authored-By: Claude --- crates/ui-egui/src/print_ui.rs | 33 ++++++++-- crates/ui-egui/tests/print.rs | 112 +++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 6 deletions(-) diff --git a/crates/ui-egui/src/print_ui.rs b/crates/ui-egui/src/print_ui.rs index f4e039d51..de2dacf30 100644 --- a/crates/ui-egui/src/print_ui.rs +++ b/crates/ui-egui/src/print_ui.rs @@ -76,6 +76,8 @@ pub struct PrintDraft { pub as_image: bool, pub dpi: f64, pub paper: usize, + /// The paper chosen before Label put label stock in, so leaving Label gives it back (#3564). + pub paper_before_label: Option, /// The sheet shown in the preview (0-based). pub sheet: usize, pub current_page: usize, @@ -129,6 +131,7 @@ impl Default for PrintDraft { as_image: false, dpi: 300.0, paper: 0, + paper_before_label: None, sheet: 0, current_page: 0, driver_options: None, @@ -143,6 +146,26 @@ impl Default for PrintDraft { } impl PrintDraft { + /// Choose Page Sizing & Handling. Label loads label stock and remembers the paper it + /// replaced; leaving Label gives that paper back, unless another one was picked meanwhile. + pub fn set_handling(&mut self, handling: Handling) { + let leaving_label = self.handling == Handling::Label && handling != Handling::Label; + self.handling = handling; + self.sheet = 0; + if handling == Handling::Label { + // A label goes on label stock. + if self.paper != print::LABEL_PAPER { + self.paper_before_label = Some(self.paper); + self.paper = print::LABEL_PAPER; + } + } else if leaving_label + && let Some(paper) = self.paper_before_label.take() + && self.paper == print::LABEL_PAPER + { + self.paper = paper; + } + } + /// The engine settings for this draft (page count and labels from the document). pub fn settings(&self, count: usize, labels: &[String]) -> Result { if self.handling == Handling::Multiple && self.order == PageOrder::CutStack && self.duplex != spool::Duplex::Off { @@ -913,6 +936,7 @@ pub(crate) fn body( }); // Label stock gets the label layout (cut, turned upright, fitted). if d.paper != before && d.paper == print::LABEL_PAPER { + d.paper_before_label = Some(before); d.handling = Handling::Label; d.sheet = 0; } @@ -942,6 +966,8 @@ pub(crate) fn body( && let Some(paper) = matched { d.paper = paper; + // A paper picked outright: nothing for leaving Label to give back. + d.paper_before_label = None; d.handling = Handling::Size; d.size = SizeMode::Actual; d.orientation = Orientation::Auto; @@ -990,12 +1016,7 @@ pub(crate) fn body( (Handling::Label, tl!("Label")), ] { if widgets::mode_tab(ui, label, d.handling == h).clicked() { - d.handling = h; - d.sheet = 0; - // A label goes on label stock. - if h == Handling::Label { - d.paper = print::LABEL_PAPER; - } + d.set_handling(h); } } }); diff --git a/crates/ui-egui/tests/print.rs b/crates/ui-egui/tests/print.rs index f3d10238c..e4da2a0c6 100644 --- a/crates/ui-egui/tests/print.rs +++ b/crates/ui-egui/tests/print.rs @@ -426,3 +426,115 @@ fn an_empty_printer_list_says_none_were_found() { h.get_by_label_contains("No printers were found"); assert!(!h.get_by_role_and_label(Role::Button, "Save as PDF").accesskit_node().is_disabled()); } + +/// `PAPERS` index of the named sheet. +fn paper(name: &str) -> usize { + pdfcraft_engine::print::PAPERS.iter().position(|p| p.0 == name).unwrap() +} + +/// Label loads label stock; leaving it gives the paper the person had back (#3564). +#[test] +fn leaving_label_gives_back_the_paper_it_replaced() { + use pdfcraft_engine::print::LABEL_PAPER; + use pdfcraft_ui_egui::PrintHandling; + let a4 = paper("A4"); + let mut h = harness(); + assert!(h.state_mut().execute("print.dialog")); + h.run_steps(3); + for (mode, tab) in + [(PrintHandling::Size, "Size"), (PrintHandling::Poster, "Poster"), (PrintHandling::Multiple, "Multiple"), (PrintHandling::Booklet, "Booklet")] + { + h.state_mut().print_draft.paper = a4; + h.state_mut().print_draft.handling = PrintHandling::Size; + h.run_steps(2); + h.get_by_role_and_label(Role::Button, "Label").click(); + h.run_steps(2); + assert_eq!(h.state().print_draft.handling, PrintHandling::Label); + assert_eq!(h.state().print_draft.paper, LABEL_PAPER, "Label puts label stock in"); + h.get_by_role_and_label(Role::Button, tab).click(); + h.run_steps(2); + assert_eq!(h.state().print_draft.handling, mode); + assert_eq!(h.state().print_draft.paper, a4, "{tab} gives A4 back after Label"); + h.get_by_value("A4"); + } +} + +/// A paper picked while in Label is the person's choice, and leaving Label keeps it. +#[test] +fn a_paper_picked_while_in_label_is_kept_when_leaving_it() { + use pdfcraft_engine::print::LABEL_PAPER; + use pdfcraft_ui_egui::{PrintDraft, PrintHandling}; + let (a4, letter) = (paper("A4"), paper("US Letter")); + let mut d = PrintDraft { paper: a4, ..Default::default() }; + d.set_handling(PrintHandling::Label); + assert_eq!(d.paper, LABEL_PAPER); + d.paper = letter; + d.set_handling(PrintHandling::Size); + assert_eq!(d.paper, letter); + // A second round trip from the kept paper gives that paper back. + d.set_handling(PrintHandling::Label); + d.set_handling(PrintHandling::Poster); + assert_eq!(d.paper, letter); + // Clicking Label again while in Label doesn't forget the paper it replaced. + d.set_handling(PrintHandling::Label); + d.set_handling(PrintHandling::Label); + d.set_handling(PrintHandling::Multiple); + assert_eq!(d.paper, letter); +} + +/// Picking the label stock from the Paper menu switches to Label (the existing behaviour); leaving +/// Label afterwards gives back the paper picked before it. +#[test] +fn label_stock_picked_from_the_paper_menu_is_undone_by_leaving_label() { + use pdfcraft_engine::print::{LABEL_PAPER, PAPERS}; + use pdfcraft_ui_egui::PrintHandling; + let a4 = paper("A4"); + let mut h = harness(); + assert!(h.state_mut().execute("print.dialog")); + h.run_steps(3); + h.state_mut().print_draft.paper = a4; + h.run_steps(2); + h.get_by_value("A4").click(); + h.run_steps(2); + // The label stock is last in a menu that scrolls: bring it into view (it settles over a few + // frames) before clicking it. + h.get_by_label(PAPERS[LABEL_PAPER].0).scroll_to_me(); + h.run_steps(4); + h.get_by_label(PAPERS[LABEL_PAPER].0).click(); + h.run_steps(2); + assert_eq!(h.state().print_draft.paper, LABEL_PAPER); + assert_eq!(h.state().print_draft.handling, PrintHandling::Label); + h.get_by_role_and_label(Role::Button, "Size").click(); + h.run_steps(2); + assert_eq!(h.state().print_draft.handling, PrintHandling::Size); + assert_eq!(h.state().print_draft.paper, a4); +} + +/// The same through the Paper menu: US Letter picked while in Label stays when Size is chosen, +/// rather than the A4 Label replaced coming back. +#[test] +fn a_paper_picked_from_the_menu_while_in_label_is_kept() { + use pdfcraft_engine::print::{LABEL_PAPER, PAPERS}; + use pdfcraft_ui_egui::PrintHandling; + let (a4, letter) = (paper("A4"), paper("US Letter")); + let mut h = harness(); + assert!(h.state_mut().execute("print.dialog")); + h.run_steps(3); + h.state_mut().print_draft.paper = a4; + h.run_steps(2); + h.get_by_role_and_label(Role::Button, "Label").click(); + h.run_steps(2); + assert_eq!(h.state().print_draft.paper, LABEL_PAPER); + h.get_by_value(PAPERS[LABEL_PAPER].0).click(); + h.run_steps(2); + h.get_by_label("US Letter").scroll_to_me(); + h.run_steps(4); + h.get_by_label("US Letter").click(); + h.run_steps(2); + assert_eq!(h.state().print_draft.paper, letter); + h.get_by_role_and_label(Role::Button, "Size").click(); + h.run_steps(2); + assert_eq!(h.state().print_draft.handling, PrintHandling::Size); + assert_eq!(h.state().print_draft.paper, letter, "the paper picked in Label is kept"); + h.get_by_value("US Letter"); +}