// Copyright (c) 2021 Christopher Davis
// Copyright (c) 2022-2024 Sophie Herold
// Copyright (c) 2023 Lubosz Sarnecki
// Copyright (c) 2024 kramo
// Copyright (c) 2024 Maximiliano Sandoval
//
// 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
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// SPDX-License-Identifier: GPL-3.0-or-later
//! A widget that shows images and handles control inputs
//!
//! This widget handles showing the right section of the image
//! with a spicfic zoom level and rotation. It also handles control
//! input that change those properties.
//!
//! While this widget logically coposes the image, decoding images
//! and composing the textures happens in [`decoder`].
//!
//! [`decoder`]: crate::decoder
mod background_color;
mod drag;
mod editing;
mod input_handling;
mod loading;
mod metadata;
mod pan;
mod printing;
mod rendering;
mod rotation;
mod scrollable;
mod zoom;
use std::cell::{Cell, OnceCell, RefCell};
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::{Arc, LazyLock};
use adw::prelude::*;
use adw::subclass::prelude::*;
use glib::subclass::Signal;
use glib::{Properties, SignalGroup};
use crate::decoder::{self, tiling, Decoder, DecoderUpdate};
use crate::deps::*;
use crate::metadata::Metadata;
use crate::util::Gesture;
/// Default background color around images and behind transparent images
/// `#222226`
const BACKGROUND_COLOR_DEFAULT: gdk::RGBA = gdk::RGBA::new(34. / 255., 34. / 255., 38. / 255., 1.);
/// Background color if the default does not give enough contrast for
/// transparent images `#e8e8ea`
const BACKGROUND_COLOR_ALTERNATE: gdk::RGBA =
gdk::RGBA::new(232. / 255., 232. / 255., 234. / 255., 1.);
const BACKGROUND_COLOR_DEFAULT_LIGHT_MODE: gdk::RGBA =
gdk::RGBA::new(250. / 255., 250. / 255., 251. / 255., 1.);
const BACKGROUND_COLOR_ALTERNATE_LIGHT_MODE: gdk::RGBA =
gdk::RGBA::new(101. / 255., 101. / 255., 105. / 255., 1.);
/// Consider 3.5:1 contrast and worse to be bad contrast for a pixel
static BACKGROUND_GUESS_LOW_CONTRAST_RATIO: f32 = 3.5;
/// Consider transparent images with more than 90% pixels bad contrast as bad
/// contrast
///
/// Bad contrast image will use the `BACKGROUND_COLOR_ALTERNATE`.
static BACKGROUND_GUESS_LOW_CONTRAST_TRHESHOLD: f64 = 0.90;
/// Milliseconds
const ZOOM_ANIMATION_DURATION: u32 = 200;
/// Milliseconds
const ROTATION_ANIMATION_DURATION: u32 = 200;
/// Relative to current zoom level
const ZOOM_FACTOR_BUTTON: f64 = 1.5;
/// Zoom 30% per scroll wheel detent
const ZOOM_FACTOR_SCROLL_WHEEL: f64 = 1.3;
/// Zoom 0.5% per pixel
///
/// This is for scrolling devices that might not exist
const ZOOM_FACTOR_SCROLL_SURFACE: f64 = 1.005;
/// Relative to best-fit level
const ZOOM_FACTOR_DOUBLE_TAP: f64 = 2.5;
/// Relative to best-fit and `MAX_ZOOM_LEVEL`
const ZOOM_FACTOR_MAX_RUBBERBAND: f64 = 2.;
/// Smaller values make the band feel stiffer
const RUBBERBANDING_EXPONENT: f64 = 0.3;
/// When this scale factor is reached, rotate is deactivated
const ZOOM_GESTURE_LOCK_THRESHOLD: f64 = 1.2;
/// When this rotate angle is reached, zoom is deactivated
const ROTATE_GESTURE_LOCK_THRESHOLD: f64 = 15.;
/// Max zoom level 2000%
const MAX_ZOOM_LEVEL: f64 = 20.0;
/// Thumbnail size in application pixels
///
/// The thumbnail is currently used for drag and drop.
const THUMBNAIL_SIZE: f32 = 128.;
/// For large enough monitors occupy 40% of the screen area when opening window
/// with image
const DEFAULT_OCCUPY_SCREEN: f64 = 0.4;
/// Screens with this resolution or smaller are handles as small
const SMALL_SCREEN_AREA: f64 = 1280. * 1024.;
/// For small monitors occupy 80% of the screen area
const SMALL_OCCUPY_SCREEN: f64 = 0.8;
#[derive(Default, Debug, Clone, Copy)]
pub enum FitMode {
#[default]
BestFit,
LargeFit,
}
mod imp {
use decoder::DecoderError;
use glycin::Operations;
use super::*;
use crate::decoder::tiling::SharedFrameBuffer;
use crate::editing;
#[derive(Debug, Default, Properties)]
#[properties(wrapper_type = super::LpImage)]
pub struct LpImage {
pub(super) file: RefCell