2023-12-06 23:45:02 +01:00
|
|
|
// Copyright (c) 2021 Christopher Davis
|
2025-01-31 14:10:59 +01:00
|
|
|
// Copyright (c) 2022-2025 Sophie Herold
|
2023-04-19 20:02:01 +02:00
|
|
|
// Copyright (c) 2023 Lubosz Sarnecki
|
2024-11-12 00:35:05 +01:00
|
|
|
// Copyright (c) 2024 kramo
|
2024-04-03 01:41:21 +02:00
|
|
|
// Copyright (c) 2024 Maximiliano Sandoval
|
2021-12-13 18:19:23 -08:00
|
|
|
//
|
|
|
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
//
|
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
2023-03-06 22:59:40 +01:00
|
|
|
//! 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
|
|
|
|
|
|
2023-12-26 22:28:45 +01:00
|
|
|
mod background_color;
|
2023-12-05 15:57:18 +01:00
|
|
|
mod drag;
|
2024-10-04 14:05:11 +02:00
|
|
|
mod editing;
|
2023-12-05 15:57:18 +01:00
|
|
|
mod input_handling;
|
|
|
|
|
mod loading;
|
|
|
|
|
mod metadata;
|
|
|
|
|
mod pan;
|
|
|
|
|
mod printing;
|
|
|
|
|
mod rendering;
|
|
|
|
|
mod rotation;
|
|
|
|
|
mod scrollable;
|
|
|
|
|
mod zoom;
|
|
|
|
|
|
2023-12-06 23:45:02 +01:00
|
|
|
use std::cell::{Cell, OnceCell, RefCell};
|
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
|
use std::ops::Deref;
|
2024-09-27 02:20:32 +02:00
|
|
|
use std::sync::{Arc, LazyLock};
|
2021-12-21 16:09:48 -08:00
|
|
|
|
2023-11-29 21:15:36 +01:00
|
|
|
use adw::prelude::*;
|
|
|
|
|
use adw::subclass::prelude::*;
|
2023-12-04 12:52:40 +01:00
|
|
|
use glib::subclass::Signal;
|
2023-12-06 22:44:18 +01:00
|
|
|
use glib::{Properties, SignalGroup};
|
2021-12-13 18:19:23 -08:00
|
|
|
|
2023-12-06 23:45:02 +01:00
|
|
|
use crate::decoder::{self, tiling, Decoder, DecoderUpdate};
|
|
|
|
|
use crate::deps::*;
|
|
|
|
|
use crate::metadata::Metadata;
|
|
|
|
|
use crate::util::Gesture;
|
2022-11-03 17:40:06 +01:00
|
|
|
|
2023-04-02 22:22:00 +02:00
|
|
|
/// Default background color around images and behind transparent images
|
2024-11-11 15:16:47 +00:00
|
|
|
/// `#222226`
|
|
|
|
|
const BACKGROUND_COLOR_DEFAULT: gdk::RGBA = gdk::RGBA::new(34. / 255., 34. / 255., 38. / 255., 1.);
|
2023-11-29 21:15:36 +01:00
|
|
|
/// Background color if the default does not give enough contrast for
|
2024-11-11 15:16:47 +00:00
|
|
|
/// transparent images `#e8e8ea`
|
2024-02-25 10:36:44 +01:00
|
|
|
const BACKGROUND_COLOR_ALTERNATE: gdk::RGBA =
|
2024-11-11 15:16:47 +00:00
|
|
|
gdk::RGBA::new(232. / 255., 232. / 255., 234. / 255., 1.);
|
2023-04-02 22:22:00 +02:00
|
|
|
|
2024-02-25 10:36:44 +01:00
|
|
|
const BACKGROUND_COLOR_DEFAULT_LIGHT_MODE: gdk::RGBA =
|
2024-11-11 15:16:47 +00:00
|
|
|
gdk::RGBA::new(250. / 255., 250. / 255., 251. / 255., 1.);
|
2024-02-25 10:36:44 +01:00
|
|
|
const BACKGROUND_COLOR_ALTERNATE_LIGHT_MODE: gdk::RGBA =
|
2024-11-11 15:16:47 +00:00
|
|
|
gdk::RGBA::new(101. / 255., 101. / 255., 105. / 255., 1.);
|
2023-04-11 01:26:42 +02:00
|
|
|
|
2023-04-02 22:22:00 +02:00
|
|
|
/// Consider 3.5:1 contrast and worse to be bad contrast for a pixel
|
|
|
|
|
static BACKGROUND_GUESS_LOW_CONTRAST_RATIO: f32 = 3.5;
|
2023-11-29 21:15:36 +01:00
|
|
|
/// Consider transparent images with more than 90% pixels bad contrast as bad
|
|
|
|
|
/// contrast
|
2023-04-02 22:22:00 +02:00
|
|
|
///
|
|
|
|
|
/// Bad contrast image will use the `BACKGROUND_COLOR_ALTERNATE`.
|
|
|
|
|
static BACKGROUND_GUESS_LOW_CONTRAST_TRHESHOLD: f64 = 0.90;
|
|
|
|
|
|
2023-02-06 18:40:32 +01:00
|
|
|
/// Milliseconds
|
2022-11-28 03:23:06 +01:00
|
|
|
const ZOOM_ANIMATION_DURATION: u32 = 200;
|
2023-02-06 18:40:32 +01:00
|
|
|
/// Milliseconds
|
2022-11-28 03:23:06 +01:00
|
|
|
const ROTATION_ANIMATION_DURATION: u32 = 200;
|
2022-11-10 19:39:23 +01:00
|
|
|
|
2023-02-06 18:40:32 +01:00
|
|
|
/// Relative to current zoom level
|
2022-11-28 03:23:06 +01:00
|
|
|
const ZOOM_FACTOR_BUTTON: f64 = 1.5;
|
2023-03-01 02:17:10 +01:00
|
|
|
/// 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;
|
2022-11-28 03:23:06 +01:00
|
|
|
|
2023-02-06 18:40:32 +01:00
|
|
|
/// Relative to best-fit level
|
|
|
|
|
const ZOOM_FACTOR_DOUBLE_TAP: f64 = 2.5;
|
|
|
|
|
|
2023-02-07 21:56:00 +01:00
|
|
|
/// Relative to best-fit and `MAX_ZOOM_LEVEL`
|
|
|
|
|
const ZOOM_FACTOR_MAX_RUBBERBAND: f64 = 2.;
|
|
|
|
|
/// Smaller values make the band feel stiffer
|
2023-03-17 01:21:45 +01:00
|
|
|
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.;
|
2023-02-07 21:56:00 +01:00
|
|
|
|
2023-02-06 18:40:32 +01:00
|
|
|
/// Max zoom level 2000%
|
2022-11-28 03:23:06 +01:00
|
|
|
const MAX_ZOOM_LEVEL: f64 = 20.0;
|
2022-11-10 19:39:23 +01:00
|
|
|
|
2023-03-07 20:48:09 +01:00
|
|
|
/// Thumbnail size in application pixels
|
|
|
|
|
///
|
|
|
|
|
/// The thumbnail is currently used for drag and drop.
|
|
|
|
|
const THUMBNAIL_SIZE: f32 = 128.;
|
|
|
|
|
|
2024-01-28 15:41:00 +01:00
|
|
|
/// For large enough monitors occupy 40% of the screen area when opening window
|
|
|
|
|
/// with image
|
2024-01-19 16:42:49 +01:00
|
|
|
const DEFAULT_OCCUPY_SCREEN: f64 = 0.4;
|
2024-01-19 16:40:29 +01:00
|
|
|
|
|
|
|
|
/// Screens with this resolution or smaller are handles as small
|
2024-01-19 16:42:49 +01:00
|
|
|
const SMALL_SCREEN_AREA: f64 = 1280. * 1024.;
|
2024-01-19 16:40:29 +01:00
|
|
|
|
|
|
|
|
/// For small monitors occupy 80% of the screen area
|
|
|
|
|
const SMALL_OCCUPY_SCREEN: f64 = 0.8;
|
|
|
|
|
|
2025-01-29 20:00:18 +01:00
|
|
|
#[derive(Default, Debug, Clone, Copy, glib::Variant, glib::Enum, PartialEq, Eq)]
|
|
|
|
|
#[enum_type(name = "LpFitMode")]
|
2023-12-25 20:18:26 +01:00
|
|
|
pub enum FitMode {
|
|
|
|
|
#[default]
|
|
|
|
|
BestFit,
|
|
|
|
|
LargeFit,
|
2025-01-29 20:00:18 +01:00
|
|
|
/// Allow zoom to be set smaller than best fit. Allow arbitrary zoom
|
|
|
|
|
/// operations until zoom is larger than best-fit, which resets to `BestFit`
|
|
|
|
|
/// mode.`
|
|
|
|
|
ExactVolatile,
|
2023-12-25 20:18:26 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-13 18:19:23 -08:00
|
|
|
mod imp {
|
2024-07-19 23:19:34 +02:00
|
|
|
use decoder::DecoderError;
|
2024-10-04 14:05:11 +02:00
|
|
|
use glycin::Operations;
|
2024-07-19 23:19:34 +02:00
|
|
|
|
2021-12-13 18:19:23 -08:00
|
|
|
use super::*;
|
2023-12-26 22:07:46 +01:00
|
|
|
use crate::decoder::tiling::SharedFrameBuffer;
|
2024-06-19 00:36:54 +02:00
|
|
|
use crate::editing;
|
2021-12-13 18:19:23 -08:00
|
|
|
|
2023-11-29 01:49:14 +01:00
|
|
|
#[derive(Debug, Default, Properties)]
|
|
|
|
|
#[properties(wrapper_type = super::LpImage)]
|
2021-12-21 10:55:02 -08:00
|
|
|
pub struct LpImage {
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) file: RefCell<Option<gio::File>>,
|
2024-06-19 00:36:54 +02:00
|
|
|
/// Contains a file from which to reload the image after animation has
|
|
|
|
|
/// finished.
|
|
|
|
|
pub(super) queued_reload: RefCell<Option<gio::File>>,
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(get)]
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) is_deleted: Cell<bool>,
|
2024-07-19 23:19:34 +02:00
|
|
|
#[property(get, builder(DecoderError::None))]
|
|
|
|
|
pub(super) specific_error: Cell<DecoderError>,
|
decoder: Use image-rs, libheif-rs, librsvg
This adds proper support for a lot of image formats including SVGs.
It will be the basis for many features to come. The technical core
is an asynchronous renderer that can arrange textures in smaller tiles
which might be generated on demand.
Closes #34, #40, #41, #49
Basis for fixing the following issues #7, #27, #36, #43, #68, #71
2023-03-04 03:14:52 +01:00
|
|
|
/// Set to true when image is ready for displaying
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(get)]
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) is_loaded: Cell<bool>,
|
decoder: Use image-rs, libheif-rs, librsvg
This adds proper support for a lot of image formats including SVGs.
It will be the basis for many features to come. The technical core
is an asynchronous renderer that can arrange textures in smaller tiles
which might be generated on demand.
Closes #34, #40, #41, #49
Basis for fixing the following issues #7, #27, #36, #43, #68, #71
2023-03-04 03:14:52 +01:00
|
|
|
/// Set if an error has occurred, shown on error_page
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(get)]
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) error: RefCell<Option<String>>,
|
2023-11-29 01:49:14 +01:00
|
|
|
pub(super) background_color: RefCell<Option<gdk::RGBA>>,
|
2023-12-25 20:18:26 +01:00
|
|
|
pub(super) fixed_background_color: RefCell<Option<gdk::RGBA>>,
|
|
|
|
|
/// Animations disabled
|
|
|
|
|
pub(super) still: Cell<bool>,
|
2024-12-21 01:16:24 +01:00
|
|
|
/// Editor can be used on image
|
|
|
|
|
#[property(get)]
|
|
|
|
|
pub(super) editable: Cell<bool>,
|
2023-11-29 01:49:14 +01:00
|
|
|
|
|
|
|
|
/// Track changes to this image
|
|
|
|
|
pub(super) file_monitor: RefCell<Option<gio::FileMonitor>>,
|
2023-12-26 22:07:46 +01:00
|
|
|
pub(super) frame_buffer: Arc<SharedFrameBuffer>,
|
2024-08-16 13:29:27 +02:00
|
|
|
pub(super) previous_frame_buffer: SharedFrameBuffer,
|
2023-11-29 01:49:14 +01:00
|
|
|
pub(super) decoder: RefCell<Option<Arc<Decoder>>>,
|
2024-10-04 14:05:11 +02:00
|
|
|
pub(super) overwrite_dimensions: Cell<Option<(u32, u32)>>,
|
2022-11-16 20:14:39 +01:00
|
|
|
|
2022-11-03 17:40:06 +01:00
|
|
|
/// Rotation final value (can differ from `rotation` during animation)
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) rotation_target: Cell<f64>,
|
2022-11-03 17:40:06 +01:00
|
|
|
/// Rotated presentation of original image in degrees clockwise
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(get, set = Self::set_rotation, explicit_notify)]
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) rotation: Cell<f64>,
|
2022-11-03 17:40:06 +01:00
|
|
|
// Animates the `rotation` property
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) rotation_animation: OnceCell<adw::TimedAnimation>,
|
2023-11-29 01:49:14 +01:00
|
|
|
|
2022-11-03 17:40:06 +01:00
|
|
|
/// Mirrored presentation of original image
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(get, set = Self::set_mirrored, explicit_notify)]
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) mirrored: Cell<bool>,
|
2022-11-10 19:39:23 +01:00
|
|
|
|
|
|
|
|
/// Displayed zoom level
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(get, set = Self::set_zoom, explicit_notify)]
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) zoom: Cell<f64>,
|
|
|
|
|
pub(super) zoom_animation: OnceCell<adw::TimedAnimation>,
|
2023-11-29 21:15:36 +01:00
|
|
|
/// Targeted zoom level, might differ from `zoom` when animation is
|
|
|
|
|
/// running
|
2024-09-09 22:55:39 +02:00
|
|
|
#[property(get, set = Self::set_zoom_target)]
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) zoom_target: Cell<f64>,
|
2024-01-04 00:40:14 +01:00
|
|
|
/// Point in image that should stay under the cursor during animation.
|
|
|
|
|
/// The value is in image coordinates.
|
2024-01-03 13:51:54 +01:00
|
|
|
pub(super) zoom_cursor_target: Cell<Option<(f64, f64)>>,
|
2022-11-10 19:39:23 +01:00
|
|
|
|
|
|
|
|
/// Always fit image into window, causes `zoom` to change automatically
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(get, set)]
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) best_fit: Cell<bool>,
|
2023-12-25 20:18:26 +01:00
|
|
|
/// Determines what `best-fit` does
|
2025-01-29 20:00:18 +01:00
|
|
|
#[property(get, set=Self::set_fit_mode, builder(FitMode::default()))]
|
2023-12-25 20:18:26 +01:00
|
|
|
pub(super) fit_mode: Cell<FitMode>,
|
2022-11-28 03:23:06 +01:00
|
|
|
/// Max zoom level is reached, stored to only send signals on change
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(get, set)]
|
|
|
|
|
pub(super) is_max_zoom: Cell<bool>,
|
2022-11-10 19:39:23 +01:00
|
|
|
|
|
|
|
|
/// Horizontal scrolling
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(override_interface = gtk::Scrollable, get , set = Self::set_hadjustment)]
|
|
|
|
|
pub(super) hadjustment: RefCell<gtk::Adjustment>,
|
2022-11-10 19:39:23 +01:00
|
|
|
/// Vertical scrolling
|
2023-11-29 01:49:14 +01:00
|
|
|
#[property(override_interface = gtk::Scrollable, get , set = Self::set_vadjustment)]
|
|
|
|
|
pub(super) vadjustment: RefCell<gtk::Adjustment>,
|
|
|
|
|
|
|
|
|
|
#[property(override_interface = gtk::Scrollable, get = Self::scroll_policy, set = Self::set_ignore_scroll_policy)]
|
|
|
|
|
pub(super) _hscroll_policy: PhantomData<gtk::ScrollablePolicy>,
|
|
|
|
|
#[property(override_interface = gtk::Scrollable, get = Self::scroll_policy, set = Self::set_ignore_scroll_policy)]
|
|
|
|
|
pub(super) _vscroll_policy: PhantomData<gtk::ScrollablePolicy>,
|
2022-11-10 19:39:23 +01:00
|
|
|
|
2022-11-03 17:40:06 +01:00
|
|
|
/// Currently EXIF data
|
2023-12-04 12:52:40 +01:00
|
|
|
pub(super) metadata: RefCell<Metadata>,
|
2022-11-10 19:39:23 +01:00
|
|
|
|
2024-01-19 16:03:24 +01:00
|
|
|
#[property(get=Self::image_size_available)]
|
2023-11-29 01:49:14 +01:00
|
|
|
_image_size_available: bool,
|
|
|
|
|
|
2022-11-10 19:39:23 +01:00
|
|
|
/// Current pointer position
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) pointer_position: Cell<Option<(f64, f64)>>,
|
2022-11-10 19:39:23 +01:00
|
|
|
|
|
|
|
|
/// Position of fingers during zoom gesture
|
|
|
|
|
///
|
|
|
|
|
/// Required for calculating delta when moving window on touchscreen.
|
|
|
|
|
/// On touchpad this is only the initial value used as the zoom target.
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) zoom_gesture_center: Cell<Option<(f64, f64)>>,
|
2022-11-10 19:39:23 +01:00
|
|
|
/// Required for calculating delta while moving window around
|
2023-03-14 18:56:35 +01:00
|
|
|
pub(super) last_drag_value: Cell<Option<(f64, f64)>>,
|
2022-11-10 19:39:23 +01:00
|
|
|
|
2023-03-17 01:21:45 +01:00
|
|
|
/// Ticks callback for animated image formats
|
2023-03-15 22:35:59 +01:00
|
|
|
pub(super) tick_callback: RefCell<Option<gtk::TickCallbackId>>,
|
2023-03-17 01:21:45 +01:00
|
|
|
/// Frame block time for currently shown frame
|
2023-03-15 22:35:59 +01:00
|
|
|
pub(super) last_animated_frame: Cell<i64>,
|
|
|
|
|
|
2023-03-17 01:21:45 +01:00
|
|
|
/// Gesture, zoom or rotate, used for the duration of the gesture
|
|
|
|
|
pub(super) locked_gestured: Cell<Option<Gesture>>,
|
|
|
|
|
|
2023-12-05 15:57:18 +01:00
|
|
|
pub(super) widget_dimensions: Cell<(i32, i32)>,
|
2023-12-06 22:44:18 +01:00
|
|
|
pub(super) scaling: Cell<f64>,
|
|
|
|
|
pub(super) surface_signals: OnceCell<SignalGroup>,
|
2024-02-08 00:32:09 +01:00
|
|
|
|
|
|
|
|
/// Number of snapshots created, debug only
|
|
|
|
|
pub(super) nth_snapshot: Cell<u8>,
|
2024-06-19 00:36:54 +02:00
|
|
|
|
|
|
|
|
/// Editing queue
|
|
|
|
|
pub(super) editing_queue: editing::Queue,
|
2024-10-18 15:51:34 +02:00
|
|
|
pub(super) operations: RefCell<Option<Arc<Operations>>>,
|
2021-12-13 18:19:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[glib::object_subclass]
|
2021-12-21 10:55:02 -08:00
|
|
|
impl ObjectSubclass for LpImage {
|
|
|
|
|
const NAME: &'static str = "LpImage";
|
2021-12-13 18:19:23 -08:00
|
|
|
type ParentType = gtk::Widget;
|
2021-12-21 10:55:02 -08:00
|
|
|
type Type = super::LpImage;
|
2022-11-10 19:39:23 +01:00
|
|
|
type Interfaces = (gtk::Scrollable,);
|
2023-12-25 20:18:26 +01:00
|
|
|
|
|
|
|
|
fn class_init(klass: &mut Self::Class) {
|
|
|
|
|
klass.set_css_name("lpimage")
|
|
|
|
|
}
|
2021-12-13 18:19:23 -08:00
|
|
|
}
|
|
|
|
|
|
2021-12-21 10:55:02 -08:00
|
|
|
impl ObjectImpl for LpImage {
|
2021-12-13 18:19:23 -08:00
|
|
|
fn properties() -> &'static [glib::ParamSpec] {
|
2023-11-29 01:49:14 +01:00
|
|
|
Self::derived_properties()
|
2021-12-13 18:19:23 -08:00
|
|
|
}
|
|
|
|
|
|
2023-11-29 01:49:14 +01:00
|
|
|
fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
|
|
|
|
self.derived_set_property(id, value, pspec)
|
2021-12-13 18:19:23 -08:00
|
|
|
}
|
|
|
|
|
|
2023-11-29 01:49:14 +01:00
|
|
|
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
|
|
|
|
self.derived_property(id, pspec)
|
2021-12-13 18:19:23 -08:00
|
|
|
}
|
|
|
|
|
|
2023-12-04 12:52:40 +01:00
|
|
|
fn signals() -> &'static [Signal] {
|
2024-09-27 02:20:32 +02:00
|
|
|
static SIGNALS: LazyLock<Vec<Signal>> =
|
|
|
|
|
LazyLock::new(|| vec![Signal::builder("metadata-changed").build()]);
|
2023-12-04 12:52:40 +01:00
|
|
|
SIGNALS.as_ref()
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 21:11:43 +01:00
|
|
|
fn constructed(&self) {
|
2022-11-10 19:39:23 +01:00
|
|
|
self.parent_constructed();
|
|
|
|
|
|
2023-02-12 02:07:42 +01:00
|
|
|
let obj = self.obj();
|
2021-12-13 18:19:23 -08:00
|
|
|
obj.set_hexpand(true);
|
|
|
|
|
obj.set_vexpand(true);
|
2022-11-10 19:39:23 +01:00
|
|
|
obj.set_overflow(gtk::Overflow::Hidden);
|
2022-11-03 17:40:06 +01:00
|
|
|
|
2022-11-10 19:39:23 +01:00
|
|
|
self.zoom.set(1.);
|
|
|
|
|
self.zoom_target.set(1.);
|
|
|
|
|
self.best_fit.set(true);
|
2023-12-26 23:24:19 +01:00
|
|
|
self.scaling.set(self.scaling());
|
2022-11-10 19:39:23 +01:00
|
|
|
|
2023-12-05 15:57:18 +01:00
|
|
|
self.connect_input_handling();
|
2023-03-27 06:30:02 +02:00
|
|
|
|
2023-12-06 22:44:18 +01:00
|
|
|
let surface_signals = self
|
|
|
|
|
.surface_signals
|
|
|
|
|
.get_or_init(SignalGroup::new::<gdk::Surface>);
|
|
|
|
|
|
|
|
|
|
surface_signals.connect_notify_local(
|
|
|
|
|
Some("scale"),
|
2024-06-18 00:37:38 +02:00
|
|
|
glib::clone!(
|
|
|
|
|
#[weak]
|
|
|
|
|
obj,
|
|
|
|
|
move |_, _| {
|
|
|
|
|
log::debug!("Scale changed signal");
|
|
|
|
|
obj.queue_resize();
|
|
|
|
|
}
|
|
|
|
|
),
|
2023-12-06 22:44:18 +01:00
|
|
|
);
|
|
|
|
|
|
2024-06-18 00:37:38 +02:00
|
|
|
obj.connect_realize(glib::clone!(
|
|
|
|
|
#[weak]
|
|
|
|
|
surface_signals,
|
|
|
|
|
move |obj| {
|
|
|
|
|
surface_signals.set_target(obj.native().and_then(|x| x.surface()).as_ref());
|
|
|
|
|
}
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
obj.connect_unrealize(glib::clone!(
|
|
|
|
|
#[weak]
|
|
|
|
|
surface_signals,
|
|
|
|
|
move |_| {
|
|
|
|
|
surface_signals.set_target(gdk::Surface::NONE);
|
|
|
|
|
}
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
adw::StyleManager::default().connect_dark_notify(glib::clone!(
|
|
|
|
|
#[weak]
|
|
|
|
|
obj,
|
|
|
|
|
move |_| {
|
|
|
|
|
glib::spawn_future_local(async move {
|
|
|
|
|
let imp = obj.imp();
|
|
|
|
|
|
|
|
|
|
let color = imp.background_color_guess().await;
|
|
|
|
|
imp.set_background_color(color);
|
|
|
|
|
if obj.is_mapped() {
|
|
|
|
|
obj.queue_draw();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
));
|
2022-11-10 19:39:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn dispose(&self) {
|
decoder: Use image-rs, libheif-rs, librsvg
This adds proper support for a lot of image formats including SVGs.
It will be the basis for many features to come. The technical core
is an asynchronous renderer that can arrange textures in smaller tiles
which might be generated on demand.
Closes #34, #40, #41, #49
Basis for fixing the following issues #7, #27, #36, #43, #68, #71
2023-03-04 03:14:52 +01:00
|
|
|
log::debug!("Disposing LpImage");
|
|
|
|
|
|
2023-02-20 23:44:17 +01:00
|
|
|
// remove target from zoom animation because it's property of this object
|
2023-12-26 23:24:19 +01:00
|
|
|
self.rotation_animation()
|
2022-11-10 19:39:23 +01:00
|
|
|
.set_target(&adw::CallbackAnimationTarget::new(|_| {}));
|
2023-12-26 23:24:19 +01:00
|
|
|
self.zoom_animation()
|
2022-11-10 19:39:23 +01:00
|
|
|
.set_target(&adw::CallbackAnimationTarget::new(|_| {}));
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-13 18:19:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glib::wrapper! {
|
2022-11-10 19:39:23 +01:00
|
|
|
pub struct LpImage(ObjectSubclass<imp::LpImage>)
|
|
|
|
|
@extends gtk::Widget,
|
|
|
|
|
@implements gtk::Scrollable;
|
2021-12-13 18:19:23 -08:00
|
|
|
}
|
2023-12-25 20:18:26 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
2024-09-15 21:06:23 +02:00
|
|
|
|
|
|
|
|
pub fn duplicate_from(&self, original: &LpImage) {
|
|
|
|
|
let original_imp = original.imp();
|
|
|
|
|
|
|
|
|
|
let imp = self.imp();
|
|
|
|
|
|
|
|
|
|
*imp.file.borrow_mut() = original_imp.file.borrow().clone();
|
|
|
|
|
imp.frame_buffer
|
|
|
|
|
.swap(Arc::new((*(original_imp.frame_buffer).load_full()).clone()));
|
|
|
|
|
}
|
2023-12-25 20:18:26 +01:00
|
|
|
}
|