print: Use LpImage for preview

This commit is contained in:
Sophie Herold 2023-12-25 20:18:26 +01:00
parent e387d8dc66
commit 6827a9c176
9 changed files with 143 additions and 44 deletions

View file

@ -117,6 +117,13 @@ const MAX_ZOOM_LEVEL: f64 = 20.0;
/// The thumbnail is currently used for drag and drop.
const THUMBNAIL_SIZE: f32 = 128.;
#[derive(Default, Debug, Clone, Copy)]
pub enum FitMode {
#[default]
BestFit,
LargeFit,
}
mod imp {
use super::*;
@ -134,8 +141,10 @@ mod imp {
/// Set if an error has occurred, shown on error_page
#[property(get)]
pub(super) error: RefCell<Option<String>>,
//pub(super) format: RefCell<Option<decoder::ImageFormat>>,
pub(super) background_color: RefCell<Option<gdk::RGBA>>,
pub(super) fixed_background_color: RefCell<Option<gdk::RGBA>>,
/// Animations disabled
pub(super) still: Cell<bool>,
/// Track changes to this image
pub(super) file_monitor: RefCell<Option<gio::FileMonitor>>,
@ -170,6 +179,8 @@ mod imp {
/// Always fit image into window, causes `zoom` to change automatically
#[property(get, set)]
pub(super) best_fit: Cell<bool>,
/// Determines what `best-fit` does
pub(super) fit_mode: Cell<FitMode>,
/// Max zoom level is reached, stored to only send signals on change
#[property(get, set)]
pub(super) is_max_zoom: Cell<bool>,
@ -224,6 +235,10 @@ mod imp {
type ParentType = gtk::Widget;
type Type = super::LpImage;
type Interfaces = (gtk::Scrollable,);
fn class_init(klass: &mut Self::Class) {
klass.set_css_name("lpimage")
}
}
impl ObjectImpl for LpImage {
@ -310,3 +325,16 @@ glib::wrapper! {
@extends gtk::Widget,
@implements gtk::Scrollable;
}
impl LpImage {
pub fn new() -> Self {
glib::Object::new()
}
/// Rerturns new not animated image
pub fn new_still() -> Self {
let image = Self::new();
image.imp().still.replace(true);
image
}
}

View file

@ -22,10 +22,20 @@ impl LpImage {
///
/// Returns the default color if no one has been guessed yet
pub fn background_color(&self) -> gdk::RGBA {
(*self.imp().background_color.borrow()).unwrap_or_else(Self::default_background_color)
let imp = self.imp();
if let Some(fixed) = *imp.fixed_background_color.borrow() {
return fixed;
}
(*imp.background_color.borrow()).unwrap_or_else(Self::default_background_color)
}
pub fn set_background_color(&self, color: Option<gdk::RGBA>) {
pub fn set_fixed_background_color(&self, color: Option<gdk::RGBA>) {
self.imp().fixed_background_color.replace(color);
}
pub(super) fn set_background_color(&self, color: Option<gdk::RGBA>) {
self.imp().background_color.replace(color);
}
@ -51,6 +61,12 @@ impl LpImage {
/// For non-transparent images this always returns
/// `BACKGROUND_COLOR_DEFAULT`
pub async fn background_color_guess(&self) -> Option<gdk::RGBA> {
let imp = self.imp();
if imp.fixed_background_color.borrow().is_some() {
return None;
}
// Shortcut for formats that don't support transparency
if !self
.metadata()

View file

@ -146,9 +146,14 @@ impl LpImage {
self.emmit_metadata_changed();
}
DecoderUpdate::Animated => {
let callback_id = self
if imp.still.get() {
// Just load the first frame
self.imp().frame_buffer.next_frame();
} else {
let callback_id = self
.add_tick_callback(glib::clone!(@weak self as obj => @default-return glib::ControlFlow::Break, move |_, clock| obj.tick_callback(clock)));
imp.tick_callback.replace(Some(callback_id));
imp.tick_callback.replace(Some(callback_id));
}
}
DecoderUpdate::UnsupportedFormat => {
self.set_unsupported(true);

View file

@ -49,16 +49,23 @@ impl LpImage {
let texture_aspect_ratio = image_width / image_height;
let widget_aspect_ratio = self.width() as f64 / self.height() as f64;
let max_zoom_factor = match self.imp().fit_mode.get() {
// Do not allow to zoom larger than original size
FitMode::BestFit => 1.,
// Allow arbitrary zoom
FitMode::LargeFit => f64::MAX,
};
let default_zoom = if texture_aspect_ratio > widget_aspect_ratio {
(self.width() as f64 / image_width).min(1.)
(self.width() as f64 / image_width).min(max_zoom_factor)
} else {
(self.height() as f64 / image_height).min(1.)
(self.height() as f64 / image_height).min(max_zoom_factor)
};
let rotated_zoom = if 1. / texture_aspect_ratio > widget_aspect_ratio {
(self.width() as f64 / image_height).min(1.)
(self.width() as f64 / image_height).min(max_zoom_factor)
} else {
(self.height() as f64 / image_width).min(1.)
(self.height() as f64 / image_width).min(max_zoom_factor)
};
rotated * rotated_zoom + (1. - rotated) * default_zoom
@ -80,6 +87,11 @@ impl LpImage {
self.imp().best_fit.get()
}
pub fn set_fit_mode(&self, fit_mode: FitMode) {
self.imp().fit_mode.replace(fit_mode);
self.configure_best_fit();
}
pub(super) fn applicable_zoom(&self) -> f64 {
decoder::tiling::zoom_normalize(self.zoom()) / self.scaling()
}

View file

@ -479,11 +479,15 @@ glib::wrapper! {
impl LpPrint {
pub fn new(
image: LpImage,
image1: LpImage,
parent_window: gtk::Window,
print_settings: Option<gtk::PrintSettings>,
page_setup: Option<gtk::PageSetup>,
) -> Self {
let image = LpImage::new_still();
image.set_fixed_background_color(Some(gdk::RGBA::new(0.94, 0.94, 0.94, 1.)));
let image2 = image.clone();
glib::spawn_future_local(async move { image2.load(&image1.file().unwrap()).await });
let print_settings = print_settings.unwrap_or_default();
let obj: Self = glib::Object::builder()

View file

@ -17,14 +17,17 @@
//! Print preview widget that shows a fake page with the image on it
use std::cell::RefCell;
use adw::subclass::prelude::*;
use glib::Properties;
use gtk::prelude::*;
use once_cell::sync::OnceCell;
use crate::deps::*;
use crate::widgets::LpPrint;
use super::image;
mod imp_page {
use super::*;
@ -47,42 +50,35 @@ mod imp_page {
self.parent_constructed();
self.obj().set_overflow(gtk::Overflow::Hidden);
}
fn dispose(&self) {
self.obj().first_child().unwrap().unparent();
}
}
impl WidgetImpl for LpPrintPreviewPage {
fn snapshot(&self, snapshot: &gtk::Snapshot) {
snapshot.save();
let scale = self.print().user_scale_original() * self.display_scale();
fn size_allocate(&self, _width: i32, _height: i32, _baseline: i32) {
let margin_left = self.print().effective_margin_left() * self.display_scale();
let margin_top = self.print().effective_margin_top() * self.display_scale();
let width = self.print().user_width() * self.display_scale();
let height = self.print().user_height() * self.display_scale();
if let Some(texture) = self.print().image().print_data(scale) {
let area = graphene::Rect::new(
margin_left as f32,
margin_top as f32,
texture.width() as f32,
texture.height() as f32,
);
let image = self.print().image();
snapshot.append_texture(&texture, &area);
// Border around image
snapshot.append_inset_shadow(
&gsk::RoundedRect::from_rect(area, 0.),
&gdk::RGBA::new(0., 0., 0., 0.3),
0.,
0.,
1.,
0.,
);
}
snapshot.restore();
image.allocate(
width as i32,
height as i32,
-1,
Some(
gsk::Transform::new()
.translate(&graphene::Point::new(margin_left as f32, margin_top as f32)),
),
);
}
}
impl LpPrintPreviewPage {
fn preview(&self) -> super::LpPrintPreview {
pub(super) fn preview(&self) -> super::LpPrintPreview {
self.obj().parent().unwrap().downcast().unwrap()
}
@ -99,13 +95,14 @@ mod imp_page {
}
mod imp {
use super::*;
#[derive(Debug, Default, Properties)]
#[properties(wrapper_type = super::LpPrintPreview)]
pub struct LpPrintPreview {
#[property(get, set)]
print: OnceCell<LpPrint>,
#[property(type = LpPrint, get, set = Self::set_print)]
print: RefCell<Option<LpPrint>>,
page: LpPrintPreviewPage,
}
@ -127,6 +124,10 @@ mod imp {
self.page.insert_after(&*self.obj(), gtk::Widget::NONE);
}
fn dispose(&self) {
self.page.unparent();
}
fn properties() -> &'static [glib::ParamSpec] {
Self::derived_properties()
}
@ -152,11 +153,21 @@ mod imp {
baseline,
);
self.page.queue_draw();
self.page.queue_resize();
}
}
impl LpPrintPreview {
fn set_print(&self, print: LpPrint) {
let page = self.page.clone();
print.connect_image_notify(move |_| {
page.init();
});
self.print.replace(Some(print));
self.obj().notify_print();
}
fn aspect_preserving_size(&self, for_width: i32, for_height: i32) -> (i32, i32) {
let print = self.obj().print();
@ -182,6 +193,17 @@ impl Default for LpPrintPreviewPage {
}
}
impl LpPrintPreviewPage {
fn init(&self) {
let imp = self.imp();
let image = imp.preview().print().image();
image.set_sensitive(false);
image.set_parent(self);
image.set_fit_mode(image::FitMode::LargeFit);
}
}
glib::wrapper! {
pub struct LpPrintPreview(ObjectSubclass<imp::LpPrintPreview>)
@extends gtk::Widget;