parent
ea9063de0f
commit
f33c4bd470
9 changed files with 368 additions and 80 deletions
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
use crate::deps::*;
|
||||
|
||||
use crate::decoder::tiling::FrameBufferExt;
|
||||
use crate::decoder::{self, tiling, Decoder, DecoderUpdate};
|
||||
use crate::i18n::i18n;
|
||||
use crate::image_metadata::LpImageMetadata;
|
||||
|
|
@ -84,7 +85,7 @@ mod imp {
|
|||
pub(super) is_deleted: Cell<bool>,
|
||||
/// Track changes to this image
|
||||
pub(super) file_monitor: RefCell<Option<gio::FileMonitor>>,
|
||||
pub(super) tiles: Arc<ArcSwap<tiling::TilingStore>>,
|
||||
pub(super) frame_buffer: Arc<ArcSwap<tiling::FrameBuffer>>,
|
||||
pub(super) decoder: RefCell<Option<Arc<Decoder>>>,
|
||||
pub(super) format: RefCell<Option<decoder::ImageFormat>>,
|
||||
|
||||
|
|
@ -139,6 +140,9 @@ mod imp {
|
|||
/// Required for calculating delta while moving window around
|
||||
pub(super) last_drag_value: Cell<Option<(f64, f64)>>,
|
||||
|
||||
pub(super) tick_callback: RefCell<Option<gtk::TickCallbackId>>,
|
||||
pub(super) last_animated_frame: Cell<i64>,
|
||||
|
||||
widget_dimensions: Cell<(i32, i32)>,
|
||||
}
|
||||
|
||||
|
|
@ -549,7 +553,7 @@ mod imp {
|
|||
widget.snapshot_rotate_mirror(snapshot, widget.rotation() as f32, widget.mirrored());
|
||||
|
||||
// Add texture(s)
|
||||
self.tiles
|
||||
self.frame_buffer
|
||||
.load()
|
||||
.add_to_snapshot(snapshot, applicable_zoom, &render_options);
|
||||
|
||||
|
|
@ -655,7 +659,7 @@ impl LpImage {
|
|||
|
||||
log::debug!("Loading file {path:?}");
|
||||
|
||||
let tiles = &self.imp().tiles;
|
||||
let tiles = &self.imp().frame_buffer;
|
||||
|
||||
let (decoder, mut decoder_update) = match Decoder::new(file.clone(), tiles.clone()).await {
|
||||
Ok(x) => x,
|
||||
|
|
@ -705,7 +709,7 @@ impl LpImage {
|
|||
}
|
||||
|
||||
self.queue_draw();
|
||||
imp.tiles.rcu(|tiles| {
|
||||
imp.frame_buffer.rcu(|tiles| {
|
||||
let mut new_tiles = (**tiles).clone();
|
||||
// TODO: Use an area larger than the viewport
|
||||
new_tiles.cleanup(self.imp().zoom_target.get(), self.viewport());
|
||||
|
|
@ -718,10 +722,43 @@ impl LpImage {
|
|||
DecoderUpdate::Format(format) => {
|
||||
imp.format.replace(Some(format));
|
||||
self.notify("format-name");
|
||||
|
||||
// Do the animation part for animated formats
|
||||
if format.is_animated() {
|
||||
let callback_id = self
|
||||
.add_tick_callback(glib::clone!(@weak self as obj => @default-return glib::Continue(false), move |_, clock| obj.tick_callback(clock)));
|
||||
imp.tick_callback.replace(Some(callback_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Roughly called for every frame if image is visible
|
||||
///
|
||||
/// We handle advancing to the next frame for animated GIFs etc here.
|
||||
fn tick_callback(&self, clock: &gdk::FrameClock) -> glib::Continue {
|
||||
// Do not animate if not visible
|
||||
if !self.is_mapped() {
|
||||
return glib::Continue(true);
|
||||
}
|
||||
|
||||
let elapsed = clock.frame_time() - self.imp().last_animated_frame.get();
|
||||
let duration = std::time::Duration::from_micros(elapsed as u64);
|
||||
|
||||
// Check if it's time to show the next frame
|
||||
if self.imp().frame_buffer.frame_timeout(duration) {
|
||||
// Just draw since frame_timeout updated to new frame
|
||||
self.queue_draw();
|
||||
self.imp().last_animated_frame.set(clock.frame_time());
|
||||
if let Some(decoder) = self.imp().decoder.borrow().as_ref() {
|
||||
// Decode new frame and load it into buffer
|
||||
decoder.fill_frame_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
glib::Continue(true)
|
||||
}
|
||||
|
||||
pub fn is_loaded(&self) -> bool {
|
||||
self.imp().is_loaded.get()
|
||||
}
|
||||
|
|
@ -851,7 +888,7 @@ impl LpImage {
|
|||
);
|
||||
|
||||
self.imp()
|
||||
.tiles
|
||||
.frame_buffer
|
||||
.load()
|
||||
.add_to_snapshot(&snapshot, scale as f64, &render_options);
|
||||
|
||||
|
|
@ -1222,7 +1259,7 @@ impl LpImage {
|
|||
}
|
||||
|
||||
fn original_dimensions(&self) -> (i32, i32) {
|
||||
if let Some((width, height)) = self.imp().tiles.load().original_dimensions {
|
||||
if let Some((width, height)) = self.imp().frame_buffer.load().original_dimensions() {
|
||||
(width as i32, height as i32)
|
||||
} else {
|
||||
(0, 0)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
//! A sliding view widget for images
|
||||
//!
|
||||
//! This widget it similar to `AdwCarousel`.
|
||||
//! Currently, there is no swipe-gesture support since it's not working with
|
||||
//! `GtkScrolledWindow` anyways.
|
||||
|
||||
use crate::deps::*;
|
||||
use crate::widgets::LpImagePage;
|
||||
|
|
@ -152,15 +150,26 @@ mod imp {
|
|||
let position_shift = self.position_shift.get() as f32;
|
||||
|
||||
for (page_index, page) in self.pages.borrow().iter().enumerate() {
|
||||
let page_position = page_index as f32;
|
||||
|
||||
// reverse page order for RTL languages
|
||||
let direction_sign = if self.is_rtl() { -1. } else { 1. };
|
||||
|
||||
// This positions the pages within the carousel and shifts them
|
||||
// according to the position that should currently be shown.
|
||||
let x = direction_sign
|
||||
* (page_index as f32 - scroll_position - position_shift)
|
||||
* (page_position - scroll_position - position_shift)
|
||||
* (width as f32 + self.page_spacing(width));
|
||||
|
||||
// Only show visible images
|
||||
if page_position == (scroll_position + position_shift).floor()
|
||||
|| page_position == (scroll_position + position_shift).ceil()
|
||||
{
|
||||
page.set_visible(true);
|
||||
} else {
|
||||
page.set_visible(false);
|
||||
}
|
||||
|
||||
let transform = gsk::Transform::new().translate(&graphene::Point::new(x, 0.));
|
||||
page.allocate(width, height, 0, Some(transform));
|
||||
page.image().allocate(width, height, 0, None);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue