Constrain minimum zoom level

This commit is contained in:
pieterd 2025-12-23 13:41:44 +01:00 • committed by Sophie Herold
parent 6e2f38de56
commit f34f9dd988
3 changed files with 51 additions and 2 deletions

View file

@ -126,6 +126,10 @@ const SMALL_SCREEN_AREA: f64 = 1280. * 1024.;
/// For small monitors occupy 80% of the screen area /// For small monitors occupy 80% of the screen area
const SMALL_OCCUPY_SCREEN: f64 = 0.8; const SMALL_OCCUPY_SCREEN: f64 = 0.8;
/// The smallest edge of the image should not be rendered at a pixel size below
/// this value, unless the image itself is smaller than this.
const SMALLEST_RENDERED_PIXEL_SIZE: f64 = 16.0;
#[derive(Default, Debug, Clone, Copy, glib::Variant, glib::Enum, PartialEq, Eq)] #[derive(Default, Debug, Clone, Copy, glib::Variant, glib::Enum, PartialEq, Eq)]
#[enum_type(name = "LpFitMode")] #[enum_type(name = "LpFitMode")]
pub enum FitMode { pub enum FitMode {
@ -206,7 +210,10 @@ mod imp {
/// Determines what `best-fit` does /// Determines what `best-fit` does
#[property(get, set=Self::set_fit_mode, builder(FitMode::default()))] #[property(get, set=Self::set_fit_mode, builder(FitMode::default()))]
pub(super) fit_mode: Cell<FitMode>, pub(super) fit_mode: Cell<FitMode>,
/// Max zoom level is reached, stored to only send signals on change /// Min zoom level is reached, stored to only send signals on change
#[property(get, set)]
pub(super) is_min_zoom: Cell<bool>,
/// Analogous to is_min_zoom
#[property(get, set)] #[property(get, set)]
pub(super) is_max_zoom: Cell<bool>, pub(super) is_max_zoom: Cell<bool>,

View file

@ -95,6 +95,27 @@ impl imp::LpImage {
decoder::tiling::zoom_normalize(zoom) / self.scaling() decoder::tiling::zoom_normalize(zoom) / self.scaling()
} }
/// Minimal zoom allowed for this image
pub(super) fn min_zoom(&self) -> f64 {
let (width, height) = self.untransformed_dimensions();
// If the image is smaller than the smallest rendered pixel size,
// block zooming out entirely
if (width as f64) < SMALLEST_RENDERED_PIXEL_SIZE
|| (height as f64) < SMALLEST_RENDERED_PIXEL_SIZE
{
1.0
}
// Prevent the rendered size from going below SMALLEST_RENDERED_PIXEL_SIZE
// Original size * zoom factor = rendered size
// <=> Zoom factor = rendered size / original size
else if width < height {
SMALLEST_RENDERED_PIXEL_SIZE / (width as f64)
} else {
SMALLEST_RENDERED_PIXEL_SIZE / (height as f64)
}
}
/// Maximal zoom allowed for this image /// Maximal zoom allowed for this image
pub(super) fn max_zoom(&self) -> f64 { pub(super) fn max_zoom(&self) -> f64 {
let obj = self.obj(); let obj = self.obj();
@ -214,6 +235,15 @@ impl imp::LpImage {
tracing::trace!(zoom, animated, snap_best_fit, force_cursor_center, fit_mode=?obj.fit_mode(), "Zoom instruction"); tracing::trace!(zoom, animated, snap_best_fit, force_cursor_center, fit_mode=?obj.fit_mode(), "Zoom instruction");
let min_zoom = self.min_zoom();
if zoom <= min_zoom {
tracing::trace!(zoom, min_zoom, "Zoom reached minimum zoom level");
zoom = min_zoom;
obj.set_is_min_zoom(true);
} else {
obj.set_is_min_zoom(false);
}
let max_zoom = self.max_zoom(); let max_zoom = self.max_zoom();
if zoom >= max_zoom { if zoom >= max_zoom {
tracing::trace!(zoom, max_zoom, "Zoom reached maximum zoom level"); tracing::trace!(zoom, max_zoom, "Zoom reached maximum zoom level");

View file

@ -257,6 +257,18 @@ mod imp {
), ),
); );
current_image_signals.connect_closure(
"notify::is-min-zoom",
false,
glib::closure_local!(
#[watch]
obj,
move |_: &LpImage, _: &glib::ParamSpec| {
obj.update_image_actions_status();
}
),
);
current_image_signals.connect_closure( current_image_signals.connect_closure(
"notify::is-max-zoom", "notify::is-max-zoom",
false, false,
@ -759,7 +771,7 @@ impl LpImageWindow {
let can_zoom_out = self let can_zoom_out = self
.image_view() .image_view()
.current_image() .current_image()
.is_some_and(|image| !image.is_best_fit()); .is_some_and(|image| !image.is_best_fit() && !image.is_min_zoom());
let can_zoom_in = self let can_zoom_in = self
.image_view() .image_view()
.current_image() .current_image()