metadata: Centralize many loose ends
This commit is contained in:
parent
b6d578cc0a
commit
fe43bc3a4c
21 changed files with 179 additions and 195 deletions
|
|
@ -198,8 +198,6 @@ mod imp {
|
|||
|
||||
/// Currently EXIF data
|
||||
pub(super) metadata: RefCell<Metadata>,
|
||||
/// Image dimension details for SVGs
|
||||
pub(super) dimension_details: RefCell<decoder::ImageDimensionDetails>,
|
||||
|
||||
#[property(get)]
|
||||
_image_size_available: bool,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2023 Sophie Herold
|
||||
// Copyright (c) 2023-2024 Sophie Herold
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -62,11 +62,7 @@ impl imp::LpImage {
|
|||
}
|
||||
|
||||
// Shortcut for formats that don't support transparency
|
||||
if !obj
|
||||
.metadata()
|
||||
.format()
|
||||
.map_or(true, |x| x.is_potentially_transparent())
|
||||
{
|
||||
if !obj.metadata().is_potentially_transparent() {
|
||||
log::debug!("This format does not support transparency");
|
||||
return Some(Self::default_background_color());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2023 Sophie Herold
|
||||
// Copyright (c) 2023-2024 Sophie Herold
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -45,41 +45,48 @@ impl imp::LpImage {
|
|||
gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL);
|
||||
|
||||
scroll_controller.connect_scroll(glib::clone!(@weak obj => @default-return glib::Propagation::Proceed, move |event, _, y| {
|
||||
// use Ctrl key as modifier for vertical scrolling
|
||||
if event.current_event_state().contains(gdk::ModifierType::CONTROL_MASK)
|
||||
|| event.current_event_state().contains(gdk::ModifierType::SHIFT_MASK)
|
||||
{
|
||||
// propagate event to scrolled window
|
||||
return glib::Propagation::Proceed;
|
||||
}
|
||||
let state = event.current_event_state();
|
||||
|
||||
// touchpads do zoom via gestures only
|
||||
if event.current_event_device().map(|x| x.source())
|
||||
== Some(gdk::InputSource::Touchpad)
|
||||
if event.current_event_device().map(|x| x.source()) == Some(gdk::InputSource::Touchpad)
|
||||
{
|
||||
// propagate event to scrolled window
|
||||
return glib::Propagation::Proceed;
|
||||
// Touchpads do zoom via gestures, expect when Ctrl key is pressed
|
||||
if !state.contains(gdk::ModifierType::CONTROL_MASK) {
|
||||
// propagate event to scrolled window
|
||||
return glib::Propagation::Proceed;
|
||||
}
|
||||
} else {
|
||||
// use Ctrl key as modifier for vertical scrolling
|
||||
if state.contains(gdk::ModifierType::CONTROL_MASK)
|
||||
|| state.contains(gdk::ModifierType::SHIFT_MASK)
|
||||
{
|
||||
// propagate event to scrolled window
|
||||
return glib::Propagation::Proceed;
|
||||
}
|
||||
}
|
||||
|
||||
// Use exponential scaling since zoom is always multiplicative with the existing value
|
||||
// This is the right thing since `exp(n/2)^2 == exp(n)` (two small steps are the same as one larger step)
|
||||
let (zoom_factor, animated) = match event.unit() {
|
||||
gdk::ScrollUnit::Wheel => (f64::exp( - y * f64::ln(ZOOM_FACTOR_SCROLL_WHEEL)), y.abs() >= 1.),
|
||||
gdk::ScrollUnit::Surface => (f64::exp( - y * f64::ln(ZOOM_FACTOR_SCROLL_SURFACE)), false),
|
||||
gdk::ScrollUnit::Wheel => (
|
||||
f64::exp(-y * f64::ln(ZOOM_FACTOR_SCROLL_WHEEL)),
|
||||
y.abs() >= 1.,
|
||||
),
|
||||
gdk::ScrollUnit::Surface => {
|
||||
(f64::exp(-y * f64::ln(ZOOM_FACTOR_SCROLL_SURFACE)), false)
|
||||
}
|
||||
unknown_unit => {
|
||||
log::warn!("Ignoring unknown scroll unit: {unknown_unit}");
|
||||
(1., false)
|
||||
}
|
||||
};
|
||||
|
||||
let zoom =
|
||||
obj.imp().zoom_target.get() * zoom_factor;
|
||||
let zoom = obj.imp().zoom_target.get() * zoom_factor;
|
||||
|
||||
if animated {
|
||||
obj.zoom_to(zoom);
|
||||
} else {
|
||||
obj.imp().zoom_to_full(zoom, false, false);
|
||||
}
|
||||
if animated {
|
||||
obj.zoom_to(zoom);
|
||||
} else {
|
||||
obj.imp().zoom_to_full(zoom, false, false);
|
||||
}
|
||||
|
||||
// do not propagate event to scrolled window
|
||||
glib::Propagation::Stop
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2023 Sophie Herold
|
||||
// Copyright (c) 2023-2024 Sophie Herold
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -68,14 +68,13 @@ impl imp::LpImage {
|
|||
match update {
|
||||
DecoderUpdate::Metadata(metadata) => {
|
||||
log::debug!("Received metadata");
|
||||
self.metadata.borrow_mut().merge(metadata);
|
||||
self.metadata.borrow_mut().merge(*metadata);
|
||||
self.emmit_metadata_changed();
|
||||
|
||||
obj.reset_rotation();
|
||||
}
|
||||
DecoderUpdate::Dimensions(dimension_details) => {
|
||||
DecoderUpdate::Dimensions => {
|
||||
log::debug!("Received dimensions: {:?}", self.original_dimensions());
|
||||
self.dimension_details.replace(dimension_details);
|
||||
obj.notify_image_size_available();
|
||||
self.configure_best_fit();
|
||||
self.request_tiles();
|
||||
|
|
@ -105,10 +104,6 @@ impl imp::LpImage {
|
|||
DecoderUpdate::Error(err) => {
|
||||
self.set_error(Some(err));
|
||||
}
|
||||
DecoderUpdate::Format(format) => {
|
||||
self.metadata.borrow_mut().set_format(format);
|
||||
self.emmit_metadata_changed();
|
||||
}
|
||||
DecoderUpdate::Animated => {
|
||||
if self.still.get() {
|
||||
// Just load the first frame
|
||||
|
|
|
|||
|
|
@ -83,10 +83,6 @@ impl imp::LpImage {
|
|||
}
|
||||
|
||||
impl LpImage {
|
||||
pub fn dimension_details(&self) -> decoder::ImageDimensionDetails {
|
||||
self.imp().dimension_details.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn file(&self) -> Option<gio::File> {
|
||||
self.imp().file.borrow().clone()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2023 Sophie Herold
|
||||
// Copyright (c) 2023-2024 Sophie Herold
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -65,7 +65,7 @@ impl WidgetImpl for imp::LpImage {
|
|||
|
||||
let applicable_zoom = self.applicable_zoom();
|
||||
|
||||
let scaling_filter = if obj.metadata().format().map_or(false, |x| x.is_svg()) {
|
||||
let scaling_filter = if obj.metadata().is_svg() {
|
||||
// Looks better in SVG animations and avoids rendering issues
|
||||
gsk::ScalingFilter::Linear
|
||||
} else if applicable_zoom < 1. {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2023 Sophie Herold
|
||||
// Copyright (c) 2023-2024 Sophie Herold
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -88,7 +88,7 @@ impl imp::LpImage {
|
|||
pub(super) fn max_zoom(&self) -> f64 {
|
||||
let obj = self.obj();
|
||||
|
||||
if obj.metadata().format().map_or(false, |x| x.is_svg()) {
|
||||
if obj.metadata().is_svg() {
|
||||
let (width, height) = self.original_dimensions();
|
||||
// Avoid division by 0
|
||||
let long_side = f64::max(1., i32::max(width, height) as f64);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2023 Sophie Herold
|
||||
// Copyright (c) 2023-2024 Sophie Herold
|
||||
// Copyright (c) 2023 Julian Hofer
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -27,7 +27,7 @@ use glib::Properties;
|
|||
use gtk::CompositeTemplate;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use crate::decoder::{self, ImageDimensionDetails};
|
||||
use crate::decoder::{self};
|
||||
use crate::deps::*;
|
||||
use crate::util::gettext::*;
|
||||
use crate::widgets::{LpImage, LpPrintPreview};
|
||||
|
|
@ -370,7 +370,7 @@ mod imp {
|
|||
imp.title.set_title(&gettext_f(
|
||||
// Translators: {} is a placeholder for the filename
|
||||
"Print “{}”",
|
||||
&[&basename],
|
||||
[basename],
|
||||
));
|
||||
if let Some(printer) = operation.print_settings().and_then(|x| x.printer()) {
|
||||
imp.title.set_subtitle(&printer);
|
||||
|
|
@ -514,7 +514,8 @@ impl LpPrint {
|
|||
|
||||
pub fn original_size(&self) -> (f64, f64) {
|
||||
let image = self.image();
|
||||
if let ImageDimensionDetails::Svg(_, Some((w, h))) = image.dimension_details() {
|
||||
let metadata = image.metadata();
|
||||
if let Some((w, h)) = metadata.dimensions_inch() {
|
||||
let dpi = self.dpi();
|
||||
|
||||
((w * dpi).round(), (h * dpi).round())
|
||||
|
|
@ -897,7 +898,7 @@ impl LpPrint {
|
|||
let texture_scale = self.user_width() / orig_width;
|
||||
let cairo_scale = cairo_dpi / self.dpi();
|
||||
|
||||
let texture = if image.metadata().format().map_or(false, |x| x.is_svg()) {
|
||||
let texture = if image.metadata().is_svg() {
|
||||
// Render SVG to exact needed sizes
|
||||
// TODO: This should be async
|
||||
decoder::formats::Svg::render_print(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2022-2023 Sophie Herold
|
||||
// Copyright (c) 2022-2024 Sophie Herold
|
||||
// Copyright (c) 2022 Christopher Davis
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -23,9 +23,9 @@ use adw::subclass::prelude::*;
|
|||
use glib::Properties;
|
||||
use gtk::CompositeTemplate;
|
||||
|
||||
use crate::decoder::ImageDimensionDetails;
|
||||
use crate::deps::*;
|
||||
use crate::util;
|
||||
use crate::util::gettext::*;
|
||||
use crate::util::{self};
|
||||
use crate::widgets::image::LpImage;
|
||||
|
||||
const FALLBACK: &str = "–";
|
||||
|
|
@ -177,7 +177,7 @@ mod imp {
|
|||
|
||||
// Image info
|
||||
Self::update_row(&self.image_size, self.image_size());
|
||||
Self::update_row(&self.image_format, metadata.format_name());
|
||||
Self::update_row(&self.image_format, self.format_name());
|
||||
Self::update_row(&self.file_size, metadata.file_size());
|
||||
|
||||
// Dates
|
||||
|
|
@ -222,12 +222,36 @@ mod imp {
|
|||
.and_then(|p| util::get_file_display_name(&p))
|
||||
}
|
||||
|
||||
fn format_name(&self) -> Option<String> {
|
||||
let image = self.image.borrow();
|
||||
let metadata = image.as_ref()?.metadata();
|
||||
|
||||
let mut format = metadata.format_name().unwrap_or(gettext("Unknown"));
|
||||
|
||||
if metadata.alpha_channel().unwrap_or(false) {
|
||||
// Translators: Addition of image being transparent to format name
|
||||
format.push_str(&gettext(", transparent"));
|
||||
}
|
||||
|
||||
if metadata.grayscale().unwrap_or(false) {
|
||||
// Translators: Addition of image being grayscale to format name
|
||||
format.push_str(&gettext(", grayscale"));
|
||||
}
|
||||
|
||||
if let Some(bit_depth) = metadata.bit_depth() {
|
||||
// Translators: Addition of bit size to format name
|
||||
format.push_str(&gettext_f(", {}\u{202F}bit", [bit_depth.to_string()]));
|
||||
}
|
||||
|
||||
Some(format)
|
||||
}
|
||||
|
||||
fn image_size(&self) -> Option<String> {
|
||||
let obj = self.obj();
|
||||
|
||||
if let Some(image) = obj.image() {
|
||||
match image.dimension_details() {
|
||||
ImageDimensionDetails::Svg(string, _) => Some(string),
|
||||
match image.metadata().dimensions_text() {
|
||||
Some(string) => Some(string.to_string()),
|
||||
_ => {
|
||||
let (width, height) = image.image_size();
|
||||
if width > 0 && height > 0 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue