diff --git a/data/gtk/print.ui b/data/gtk/print.ui
index f883b91..8b38e1a 100644
--- a/data/gtk/print.ui
+++ b/data/gtk/print.ui
@@ -21,6 +21,9 @@
@@ -110,15 +102,13 @@
@@ -129,7 +119,6 @@
Width
- 2
diff --git a/src/widgets/print.rs b/src/widgets/print.rs
index a0ffec2..c74b943 100644
--- a/src/widgets/print.rs
+++ b/src/widgets/print.rs
@@ -19,6 +19,7 @@
use crate::decoder;
use crate::deps::*;
+use crate::util::gettext::*;
use crate::widgets::{LpImage, LpPrintPreview};
use adw::prelude::*;
@@ -29,6 +30,70 @@ use once_cell::sync::OnceCell;
use std::cell::{Cell, RefCell};
+#[derive(Default, Clone, Copy)]
+struct PageAlignment {
+ horizontal: HAlignment,
+ vertical: VAlignment,
+}
+
+#[derive(Default, Clone, Copy)]
+enum HAlignment {
+ Left,
+ #[default]
+ Center,
+ Right,
+}
+
+#[derive(Default, Clone, Copy)]
+enum VAlignment {
+ Top,
+ #[default]
+ Middle,
+ Bottom,
+}
+
+impl From<&str> for PageAlignment {
+ fn from(s: &str) -> Self {
+ let alignment = match s {
+ "top" => (HAlignment::Center, VAlignment::Top),
+ "center" => (HAlignment::Center, VAlignment::Middle),
+ "bottom" => (HAlignment::Center, VAlignment::Bottom),
+ "left" => (HAlignment::Left, VAlignment::Middle),
+ "right" => (HAlignment::Right, VAlignment::Middle),
+ pos => {
+ log::error!("Unkown alignment '{pos}'");
+ (HAlignment::default(), VAlignment::default())
+ }
+ };
+
+ Self::from(alignment)
+ }
+}
+
+impl From<(HAlignment, VAlignment)> for PageAlignment {
+ fn from((horizontal, vertical): (HAlignment, VAlignment)) -> Self {
+ Self {
+ horizontal,
+ vertical,
+ }
+ }
+}
+
+impl std::fmt::Display for PageAlignment {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ let s = match (self.horizontal, self.vertical) {
+ (HAlignment::Center, VAlignment::Top) => gettext("Top"),
+ (HAlignment::Center, VAlignment::Middle) => gettext("Center"),
+ (HAlignment::Center, VAlignment::Bottom) => gettext("Bottom"),
+ (HAlignment::Left, VAlignment::Middle) => gettext("Left"),
+ (HAlignment::Right, VAlignment::Middle) => gettext("Right"),
+ _ => String::from("Unsupported (Error)"),
+ };
+
+ f.write_str(&s)
+ }
+}
+
mod imp {
use super::*;
@@ -36,19 +101,25 @@ mod imp {
#[template(file = "../../data/gtk/print.ui")]
#[properties(wrapper_type = super::LpPrint)]
pub struct LpPrint {
+ #[template_child]
+ pub(super) title: TemplateChild,
#[template_child]
pub(super) preview: TemplateChild,
// ListBox entries
+ #[template_child]
+ pub(super) alignment: TemplateChild,
+
+ #[template_child]
+ pub(super) margin_horizontal: TemplateChild,
+ #[template_child]
+ pub(super) margin_vertical: TemplateChild,
+
#[template_child]
pub(super) width: TemplateChild,
#[template_child]
pub(super) scale: TemplateChild,
#[template_child]
- pub(super) margin_left: TemplateChild,
- #[template_child]
- pub(super) margin_top: TemplateChild,
- #[template_child]
pub(super) unit: TemplateChild,
#[property(get, set, builder().construct_only())]
@@ -84,14 +155,6 @@ mod imp {
print.back().await;
});
- klass.install_action("print.center-horizontally", None, |print, _, _| {
- print.center_horizontally();
- });
-
- klass.install_action("print.center-vertically", None, |print, _, _| {
- print.center_vertically();
- });
-
klass.install_property_action("print.orientation", "orientation");
}
@@ -111,13 +174,31 @@ mod imp {
obj.set_transient_for(Some(&obj.parent_window()));
obj.set_modal(true);
+ self.alignment.connect_selected_notify(
+ glib::clone!(@weak obj => move |_| obj.on_margin_vertical_changed()),
+ );
+ self.alignment
+ .set_expression(Some(gtk::ClosureExpression::new::(
+ [] as [gtk::Expression; 0],
+ glib::closure!(
+ |s: gtk::StringObject| PageAlignment::from(s.string().as_str()).to_string()
+ ),
+ )));
+
+ gtk::ClosureExpression::new::(
+ [] as [gtk::Expression; 0],
+ glib::closure!(
+ |s: gtk::StringObject| PageAlignment::from(s.string().as_str()).to_string()
+ ),
+ );
+
self.width
.connect_value_notify(glib::clone!(@weak obj => move |_| obj.on_width_changed()));
- self.margin_left.connect_value_notify(
- glib::clone!(@weak obj => move |_| obj.on_margin_left_changed()),
+ self.margin_horizontal.connect_value_notify(
+ glib::clone!(@weak obj => move |_| obj.on_margin_horizontal_changed()),
);
- self.margin_top.connect_value_notify(
- glib::clone!(@weak obj => move |_| obj.on_margin_top_changed()),
+ self.margin_vertical.connect_value_notify(
+ glib::clone!(@weak obj => move |_| obj.on_margin_vertical_changed()),
);
self.print_operation
@@ -135,36 +216,52 @@ mod imp {
op.set_n_pages(1);
});
- self.print_operation.connect_draw_page(
- glib::clone!(@weak obj => move |_operation, _context, _page_nr| {
- let imp = obj.imp();
+ self.print_operation.connect_draw_page(glib::clone!(@weak obj =>
+ move |operation, _context, _page_nr| {
+ let imp = obj.imp();
- imp.width.adjustment().set_step_increment(1.);
- imp.width.adjustment().set_page_increment(5.);
- imp.margin_left.adjustment().set_step_increment(1.);
- imp.margin_left.adjustment().set_page_increment(5.);
- imp.margin_top.adjustment().set_step_increment(1.);
- imp.margin_top.adjustment().set_page_increment(5.);
+ let basename = obj
+ .image()
+ .file()
+ .and_then(|f| f.basename())
+ .map(|x| x.display().to_string())
+ .unwrap_or_default();
+ imp.title.set_title(&gettext_f(
+ // Translators: {} is a placeholder for the filename
+ "Print {}",
+ &[&basename],
+ ));
+ if let Some(printer) = operation.print_settings().and_then(|x| x.printer()) {
+ imp.title.set_subtitle(&printer);
+ }
- obj.set_ranges();
+ imp.width.adjustment().set_step_increment(1.);
+ imp.width.adjustment().set_page_increment(5.);
+ imp.margin_horizontal.adjustment().set_step_increment(1.);
+ imp.margin_horizontal.adjustment().set_page_increment(5.);
+ imp.margin_vertical.adjustment().set_step_increment(1.);
+ imp.margin_vertical.adjustment().set_page_increment(5.);
- imp.width.set_value(obj.image().image_size().0 as f64);
- obj.center_horizontally();
+ obj.set_ranges();
- obj.unit_selected();
+ imp.width.set_value(obj.image().image_size().0 as f64);
+ // TODO: Select center
+ //obj.center_horizontally();
- imp.print_operation.cancel();
+ obj.unit_selected();
- let orientation = match obj.page_setup().orientation() {
- gtk::PageOrientation::Portrait => "portrait",
- gtk::PageOrientation::Landscape => "landscape",
- _ => "other",
- };
- obj.set_orientation(orientation);
+ imp.print_operation.cancel();
- obj.set_visible(true);
- }),
- );
+ let orientation = match obj.page_setup().orientation() {
+ gtk::PageOrientation::Portrait => "portrait",
+ gtk::PageOrientation::Landscape => "landscape",
+ _ => "other",
+ };
+ obj.set_orientation(orientation);
+
+ obj.set_visible(true);
+ }
+ ));
}
fn properties() -> &'static [glib::ParamSpec] {
@@ -233,11 +330,29 @@ impl LpPrint {
self.draw_preview();
}
+ fn user_alignment(&self) -> PageAlignment {
+ let s = self
+ .imp()
+ .alignment
+ .selected_item()
+ .and_then(|x| x.downcast::().ok())
+ .map(|x| x.string())
+ .unwrap_or_default();
+
+ PageAlignment::from(s.as_str())
+ }
+
/// Returns user selected image width in pixels
pub fn user_width(&self) -> f64 {
f64::max(1., self.imp().width.value() / self.unit_factor())
}
+ pub fn user_height(&self) -> f64 {
+ let (_, orig_height) = self.image().image_size();
+
+ f64::max(1., orig_height as f64 * self.user_scale())
+ }
+
/// Returns scaling of the original image based on selected image size
pub fn user_scale(&self) -> f64 {
let (orig_width, _) = self.image().image_size();
@@ -245,22 +360,50 @@ impl LpPrint {
self.user_width() / orig_width as f64
}
- pub fn on_margin_left_changed(&self) {
+ pub fn on_margin_horizontal_changed(&self) {
self.draw_preview();
}
/// Returns user selected left margin in pixels
- pub fn user_margin_left(&self) -> f64 {
- self.imp().margin_left.value() / self.unit_factor()
+ pub fn user_margin_horizontal(&self) -> f64 {
+ self.imp().margin_horizontal.value() / self.unit_factor()
}
- pub fn on_margin_top_changed(&self) {
+ pub fn effective_margin_left(&self) -> f64 {
+ match self.user_alignment().horizontal {
+ HAlignment::Left => self.user_margin_horizontal(),
+ HAlignment::Center => (self.paper_width() - self.user_width()) / 2.,
+ HAlignment::Right => {
+ self.paper_width() - self.user_width() - self.user_margin_horizontal()
+ }
+ }
+ }
+
+ pub fn paper_width(&self) -> f64 {
+ self.page_setup().paper_width(gtk::Unit::Inch) * self.dpi()
+ }
+
+ pub fn paper_height(&self) -> f64 {
+ self.page_setup().paper_height(gtk::Unit::Inch) * self.dpi()
+ }
+
+ pub fn on_margin_vertical_changed(&self) {
self.draw_preview();
}
/// Returns user selected top margin in pixels
- pub fn user_margin_top(&self) -> f64 {
- self.imp().margin_top.value() / self.unit_factor()
+ pub fn user_margin_vertical(&self) -> f64 {
+ self.imp().margin_vertical.value() / self.unit_factor()
+ }
+
+ pub fn effective_margin_top(&self) -> f64 {
+ match self.user_alignment().vertical {
+ VAlignment::Top => self.user_margin_vertical(),
+ VAlignment::Middle => (self.paper_height() - self.user_height()) / 2.,
+ VAlignment::Bottom => {
+ self.paper_height() - self.user_height() - self.user_margin_vertical()
+ }
+ }
}
/// Redraws the preview widget child
@@ -268,27 +411,6 @@ impl LpPrint {
self.imp().preview.queue_resize();
}
- /// Centers the image horizontally
- fn center_horizontally(&self) {
- let margin_left =
- (self.page_setup().paper_width(gtk::Unit::Inch) * self.dpi() - self.user_width()) / 2.;
-
- self.imp()
- .margin_left
- .set_value(margin_left * self.unit_factor());
- }
-
- /// Centers the image vertically
- fn center_vertically(&self) {
- let user_height = self.image().image_size().1 as f64 * self.user_scale();
- let margin_top =
- (self.page_setup().paper_height(gtk::Unit::Inch) * self.dpi() - user_height) / 2.;
-
- self.imp()
- .margin_top
- .set_value(margin_top * self.unit_factor());
- }
-
/// Sets the allowed ranges for the spin button based on current unit
fn set_ranges(&self) {
let imp = self.imp();
@@ -300,15 +422,18 @@ impl LpPrint {
self.page_setup().page_width(gtk::Unit::Inch) * self.dpi() * self.unit_factor();
imp.width.set_range(self.unit_factor(), max_width);
- let min_left_margin =
- self.page_setup().left_margin(gtk::Unit::Inch) * self.dpi() * self.unit_factor();
- imp.margin_left
- .set_range(min_left_margin, min_left_margin + max_width);
+ let min_horizontal_margin = f64::max(
+ self.page_setup().left_margin(gtk::Unit::Inch),
+ self.page_setup().right_margin(gtk::Unit::Inch),
+ ) * self.dpi()
+ * self.unit_factor();
+ imp.margin_horizontal
+ .set_range(min_horizontal_margin, min_horizontal_margin + max_width);
- let min_top_margin =
+ let min_vertical_margin =
self.page_setup().top_margin(gtk::Unit::Inch) * self.dpi() * self.unit_factor();
- imp.margin_top
- .set_range(min_top_margin, min_top_margin + max_height);
+ imp.margin_vertical
+ .set_range(min_vertical_margin, min_vertical_margin + max_height);
}
#[template_callback]
@@ -339,8 +464,8 @@ impl LpPrint {
let update_factor = unit_factor / self.unit_factor();
let width = imp.width.value() * update_factor;
- let margin_left = imp.margin_left.value() * update_factor;
- let margin_top = imp.margin_top.value() * update_factor;
+ let margin_horizontal = imp.margin_horizontal.value() * update_factor;
+ let margin_vertical = imp.margin_vertical.value() * update_factor;
self.set_unit_factor(unit_factor);
self.set_ranges();
@@ -348,11 +473,11 @@ impl LpPrint {
imp.width.set_digits(digits);
imp.width.set_value(width);
- imp.margin_left.set_digits(digits);
- imp.margin_left.set_value(margin_left);
+ imp.margin_horizontal.set_digits(digits);
+ imp.margin_horizontal.set_value(margin_horizontal);
- imp.margin_top.set_digits(digits);
- imp.margin_top.set_value(margin_top);
+ imp.margin_vertical.set_digits(digits);
+ imp.margin_vertical.set_value(margin_vertical);
}
pub fn run(&self) {
@@ -461,10 +586,10 @@ impl LpPrint {
let cairo_context = print_context.cairo_context();
cairo_context.scale(cairo_scale, cairo_scale);
- let margin_left =
- self.user_margin_left() - self.page_setup().left_margin(gtk::Unit::Inch) * self.dpi();
- let margin_top =
- self.user_margin_top() - self.page_setup().top_margin(gtk::Unit::Inch) * self.dpi();
+ let margin_left = self.effective_margin_left()
+ - self.page_setup().left_margin(gtk::Unit::Inch) * self.dpi();
+ let margin_top = self.effective_margin_top()
+ - self.page_setup().top_margin(gtk::Unit::Inch) * self.dpi();
cairo_context
.set_source_surface(cairo_surface, margin_left, margin_top)
diff --git a/src/widgets/print_preview.rs b/src/widgets/print_preview.rs
index 83e25d1..c2d15ea 100644
--- a/src/widgets/print_preview.rs
+++ b/src/widgets/print_preview.rs
@@ -79,8 +79,8 @@ mod imp {
snapshot.save();
let scale = obj.print().user_scale() * self.display_scale();
- let margin_left = obj.print().user_margin_left() * self.display_scale();
- let margin_top = obj.print().user_margin_top() * self.display_scale();
+ let margin_left = obj.print().effective_margin_left() * self.display_scale();
+ let margin_top = obj.print().effective_margin_top() * self.display_scale();
if let Some(texture) = self.obj().print().image().print_data(scale) {
let area = graphene::Rect::new(