From 032707a210472001cbfc8eb0a9a744f2ab1f8954 Mon Sep 17 00:00:00 2001 From: Sophie Herold Date: Wed, 12 Apr 2023 01:11:59 +0200 Subject: [PATCH] fileModel: Use GFile with URI as key Part-of: --- Cargo.lock | 1 + Cargo.toml | 1 + src/application.rs | 4 +- src/file_model.rs | 88 +++++++++--------- src/util/mod.rs | 5 +- src/widgets/image.rs | 9 +- src/widgets/image_page.rs | 14 ++- src/widgets/image_view.rs | 175 ++++++++++++++++-------------------- src/widgets/sliding_view.rs | 36 +++++--- src/window.rs | 19 ++-- 10 files changed, 164 insertions(+), 188 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4176f31..15a4fc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1918,6 +1918,7 @@ dependencies = [ "log", "once_cell", "safe-transmute", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 93b28c9..6408dc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ safe-transmute = "0.11.2" image-rs = { package = "image", version = "0.24.6" } libheif-rs = "0.19.0" librsvg = { git = "https://gitlab.gnome.org/GNOME/librsvg.git", rev = "2.56.0" } +url = "2.3.1" [dependencies.ashpd] version = "0.4.0" diff --git a/src/application.rs b/src/application.rs index b7111d8..3624dbc 100644 --- a/src/application.rs +++ b/src/application.rs @@ -72,10 +72,8 @@ mod imp { fn open(&self, files: &[gio::File], _hint: &str) { let application = self.obj(); for file in files { - let Some(path) = file.path() else { log::error!("File has no path {}", file.uri()); return; }; - let win = LpWindow::new(&*application); - win.set_image_from_path(&path); + win.set_image_from_file(&file); } } } diff --git a/src/file_model.rs b/src/file_model.rs index 1588e66..d770c97 100644 --- a/src/file_model.rs +++ b/src/file_model.rs @@ -21,12 +21,11 @@ use crate::deps::*; use crate::util; use crate::util::gettext::*; -use std::path::{Path, PathBuf}; - use anyhow::Context; use gio::prelude::*; use gio::subclass::prelude::*; -use indexmap::IndexSet; +use glib::GString; +use indexmap::{IndexMap, IndexSet}; use once_cell::sync::{Lazy, OnceCell}; use std::cell::RefCell; @@ -38,8 +37,8 @@ mod imp { pub struct LpFileModel { /// Use and IndexSet such that we can put the elements into an arbitrary order /// while still having fast hash access via `PathBuf`. - pub(super) files: RefCell>, - pub(super) directory: OnceCell, + pub(super) files: RefCell>, + pub(super) directory: OnceCell, /// Track file changes. pub(super) monitor: OnceCell, } @@ -71,12 +70,12 @@ impl Default for LpFileModel { impl LpFileModel { /// Create with a single element - pub fn from_path(path: &Path) -> Self { + pub fn from_file(file: &gio::File) -> Self { let model = Self::default(); { let mut vec = model.imp().files.borrow_mut(); - vec.insert(path.to_path_buf()); + vec.insert(file.uri(), file.clone()); } model @@ -85,10 +84,10 @@ impl LpFileModel { /// Load all file from the given directory /// /// This can be used if there are already files present - pub async fn load_directory(&self, directory: PathBuf) -> anyhow::Result<()> { + pub async fn load_directory(&self, directory: gio::File) -> anyhow::Result<()> { self.imp().directory.set(directory.clone()).unwrap(); - let monitor = gio::File::for_path(&directory) + let monitor = directory .monitor_directory(gio::FileMonitorFlags::WATCH_MOVES, gio::Cancellable::NONE)?; monitor.connect_changed( @@ -99,9 +98,9 @@ impl LpFileModel { self.imp().monitor.set(monitor).unwrap(); let new_files_result = gio::spawn_blocking(move || { - let mut files = IndexSet::new(); + let mut files = IndexMap::new(); - let enumerator = gio::File::for_path(&directory) + let enumerator = directory .enumerate_children( &format!( "{},{}", @@ -118,9 +117,8 @@ impl LpFileModel { if let Some(content_type) = info.content_type().map(|t| t.to_string()) { // Filter out non-images; For now we support "all" image types. if content_type.starts_with("image/") { - let path = PathBuf::from_iter([directory.clone(), info.name()]); - log::debug!("{path:?} is an image, adding to the list"); - files.insert(path); + let file = directory.child(info.name()); + files.insert(file.uri(), file); } } } @@ -141,8 +139,8 @@ impl LpFileModel { // Here we use a nested scope so that the mutable borrow only lasts as long as we need it let mut files = self.imp().files.borrow_mut(); - for path in files.iter() { - new_files.insert(path.clone()); + for (uri, file) in files.iter() { + new_files.insert(uri.clone(), file.clone()); } *files = new_files; // Then sort by name. @@ -152,47 +150,49 @@ impl LpFileModel { Ok(()) } - pub fn directory(&self) -> Option { + pub fn directory(&self) -> Option { self.imp().directory.get().cloned() } - pub fn index_of(&self, path: &Path) -> Option { - self.imp().files.borrow().get_index_of(path) + pub fn index_of(&self, file: &gio::File) -> Option { + self.imp().files.borrow().get_index_of(&file.uri()) } pub fn n_files(&self) -> usize { self.imp().files.borrow().len() } - pub fn contains(&self, path: &Path) -> bool { - self.imp().files.borrow().contains(path) + pub fn contains(&self, file: &gio::File) -> bool { + self.imp().files.borrow().contains_key(&file.uri()) } - pub fn before(&self, path: &Path) -> Option { - let index = self.index_of(path)?; + pub fn before(&self, file: &gio::File) -> Option { + let index = self.index_of(&file)?; self.imp() .files .borrow() .get_index(index.checked_sub(1)?) + .map(|x| x.1) .cloned() } - pub fn after(&self, path: &Path) -> Option { - let index = self.index_of(path)?; + pub fn after(&self, file: &gio::File) -> Option { + let index = self.index_of(file)?; self.imp() .files .borrow() .get_index(index.checked_add(1)?) + .map(|x| x.1) .cloned() } /// Returns `n` elements before and after given path - pub fn files_around(&self, path: &Path, n: usize) -> IndexSet { - let Some(index) = self.index_of(path) else { - log::error!("Path not in model: {path:?}"); - return IndexSet::new(); + pub fn files_around(&self, file: &gio::File, n: usize) -> IndexMap { + let Some(index) = self.index_of(file) else { + log::error!("URI not in model: {}", file.uri()); + return IndexMap::new(); }; let reduce = if index < n { n - index } else { 0 }; @@ -203,23 +203,23 @@ impl LpFileModel { .iter() .skip(index.saturating_sub(n)) .take(2 * n + 1 - reduce) - .cloned() + .map(|(k, v)| (k.clone(), v.clone())) .collect() } /// Return first path - pub fn first(&self) -> Option { - self.imp().files.borrow().first().cloned() + pub fn first(&self) -> Option { + self.imp().files.borrow().first().map(|x| x.1).cloned() } /// Returns last path - pub fn last(&self) -> Option { - self.imp().files.borrow().last().cloned() + pub fn last(&self) -> Option { + self.imp().files.borrow().last().map(|x| x.1).cloned() } /// Currently sorts by name - fn sort(files: &mut IndexSet) { - files.sort_by(|x, y| util::compare_by_name(x, y)); + fn sort(files: &mut IndexMap) { + files.sort_by(|_, x, _, y| util::compare_by_name(&x.uri(), &y.uri())); } /// Determines if a file is added @@ -253,12 +253,6 @@ impl LpFileModel { file_a: &gio::File, file_b: Option<&gio::File>, ) { - let Some(path_a) = file_a.path() else { - log::error!("File has no path: {event}"); - return; - }; - let path_b = file_b.and_then(|x| x.path()); - let changed = match event { gio::FileMonitorEvent::Created | gio::FileMonitorEvent::MovedIn @@ -268,7 +262,7 @@ impl LpFileModel { if Self::is_image_file(file_a) => { let mut files = self.imp().files.borrow_mut(); - let changed = files.insert(path_a); + let changed = files.insert(file_a.uri(),file_a.clone()).is_some(); if changed { Self::sort(&mut files); } @@ -276,20 +270,20 @@ impl LpFileModel { } gio::FileMonitorEvent::Deleted | gio::FileMonitorEvent::MovedOut | gio::FileMonitorEvent::Unmounted => { let mut files = self.imp().files.borrow_mut(); - let changed = files.remove(&path_a); + let changed = files.remove(&file_a.uri()).is_some(); if changed { Self::sort(&mut files); } changed } gio::FileMonitorEvent::Renamed => { - if let (Some(path_b), Some(file_b)) = (path_b, file_b) { + if let Some(file_b) = file_b { let mut changed = false; { let mut files = self.imp().files.borrow_mut(); - changed |= files.remove(&path_a); + changed |= files.remove(&file_a.uri()).is_some(); if Self::is_image_file(file_b) { - changed |= files.insert(path_b); + changed |= files.insert(file_b.uri(), file_b.clone()).is_some(); if changed { Self::sort(&mut files); } diff --git a/src/util/mod.rs b/src/util/mod.rs index bbaaffd..b6e73d0 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -66,10 +66,7 @@ pub async fn query_attributes_future( .context("Failed to query attributes") } -pub fn compare_by_name(file_a: &Path, file_b: &Path) -> std::cmp::Ordering { - let name_a = file_a.display().to_string(); - let name_b = file_b.display().to_string(); - +pub fn compare_by_name(name_a: &glib::GString, name_b: &glib::GString) -> std::cmp::Ordering { let key_a = glib::FilenameCollationKey::from(name_a); let key_b = glib::FilenameCollationKey::from(name_b); diff --git a/src/widgets/image.rs b/src/widgets/image.rs index a965bb9..1dfccf3 100644 --- a/src/widgets/image.rs +++ b/src/widgets/image.rs @@ -43,7 +43,7 @@ use once_cell::sync::Lazy; use once_cell::unsync::OnceCell; use std::cell::{Cell, RefCell}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::Arc; /// Default background color around images and behind transparent images @@ -742,9 +742,8 @@ glib::wrapper! { } impl LpImage { - pub async fn load(&self, path: &Path) { - let path = path.to_path_buf(); - let file = gio::File::for_path(&path); + pub async fn load(&self, file: &gio::File) { + let path = file.path().unwrap(); self.set_file(&file); if !path.is_file() { @@ -963,7 +962,7 @@ impl LpImage { let file = file_a.clone(); // TODO: error handling is missing spawn(async move { - obj.load(&file.path().unwrap()).await; + obj.load(&file).await; }); } gio::FileMonitorEvent::Deleted diff --git a/src/widgets/image_page.rs b/src/widgets/image_page.rs index 72c5c38..3aa18c8 100644 --- a/src/widgets/image_page.rs +++ b/src/widgets/image_page.rs @@ -31,7 +31,7 @@ use gtk::prelude::*; use gtk::CompositeTemplate; use once_cell::sync::Lazy; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; mod imp { use super::*; @@ -148,23 +148,21 @@ glib::wrapper! { } impl LpImagePage { - pub fn from_path(path: &Path) -> Self { + pub fn from_file(file: &gio::File) -> Self { let obj = glib::Object::new::(); - let path = path.to_path_buf(); - let file = gio::File::for_path(&path); obj.imp().image.set_file(&file); - spawn(clone!(@weak obj, @strong path => async move { + spawn(clone!(@weak obj, @strong file => async move { let imp = obj.imp(); - imp.image.load(&path).await; + imp.image.load(&file).await; })); obj } - pub fn path(&self) -> PathBuf { - self.image().path().unwrap() + pub fn file(&self) -> gio::File { + self.image().file().unwrap() } pub fn scrolled_window(&self) -> gtk::ScrolledWindow { diff --git a/src/widgets/image_view.rs b/src/widgets/image_view.rs index 6b5c33f..1b163d0 100644 --- a/src/widgets/image_view.rs +++ b/src/widgets/image_view.rs @@ -228,12 +228,12 @@ glib::wrapper! { #[gtk::template_callbacks] impl LpImageView { - pub fn set_image_from_path(&self, path: &Path) { + pub fn set_image_from_file(&self, file: &gio::File) { // Add image to recently used file. Does not work in Flatpaks: // - gtk::RecentManager::default().add_item(&gio::File::for_path(path).uri()); + gtk::RecentManager::default().add_item(&file.uri()); - if let Err(err) = self.load_path(path) { + if let Err(err) = self.load_file(file) { log::error!("Failed to load path: {err}"); self.activate_action("win.show-toast", Some(&(err.to_string(), 1).to_variant())) .unwrap(); @@ -244,39 +244,43 @@ impl LpImageView { // that holds a `gio::File` for each child within the same directory as the // file we pass. This model will update with changes to the directory, // and in turn we'll update our `adw::Carousel`. - fn load_path(&self, path: &Path) -> anyhow::Result<()> { + fn load_file(&self, file: &gio::File) -> anyhow::Result<()> { let sliding_view = self.sliding_view(); - let directory = path.parent().map(|x| x.to_path_buf()); + let directory = file.parent(); - let is_same_directory = directory == self.model().directory(); + let is_same_directory = if let (Some(d1), Some(d2)) = (&directory, self.model().directory()) + { + d1.equal(&d2) + } else { + false + }; if is_same_directory { log::debug!("Re-using old model and navigating to the current file"); - self.navigate_to_path(path); + self.navigate_to_file(file); return Ok(()); } - let model = LpFileModel::from_path(path); + let model = LpFileModel::from_file(file); self.set_model(model); log::debug!("New model created"); - let page = LpImagePage::from_path(path); + let page = LpImagePage::from_file(file); sliding_view.clear(); sliding_view.append(&page); // List other files in directory if let Some(directory) = directory { - let path = path.to_path_buf(); - spawn(glib::clone!(@weak self as obj, @strong path => async move { - if let Err(err) = obj.model().load_directory(directory).await { + spawn(glib::clone!(@weak self as obj, @strong file => async move { + if let Err(err) = obj.model().load_directory(directory.clone()).await { log::warn!("Failed to load directory: {err}"); obj.activate_action("win.show-toast", Some(&(err.to_string(), 1).to_variant())) .unwrap(); return; } - obj.update_sliding_view(&path); + obj.update_sliding_view(&file); })); } @@ -285,52 +289,54 @@ impl LpImageView { /// Move forward or backwards pub fn navigate(&self, direction: Direction) { - if let Some(current_path) = self.current_path() { - let new_path = match direction { - Direction::Forward => self.model().after(¤t_path), - Direction::Back => self.model().before(¤t_path), + if let Some(current_file) = self.current_file() { + let new_file = match direction { + Direction::Forward => self.model().after(¤t_file), + Direction::Back => self.model().before(¤t_file), }; - if let Some(new_path) = new_path { - self.scroll_sliding_view(&new_path); + if let Some(new_file) = new_file { + self.scroll_sliding_view(&new_file); } } } /// Jump to position pub fn jump(&self, position: Position) { - let new_path = match position { + let new_file = match position { Position::First => self.model().first(), Position::Last => self.model().last(), }; - if let Some(new_path) = new_path { - self.navigate_to_path(&new_path); + if let Some(new_file) = new_file { + self.navigate_to_file(&new_file); } } /// Used for drag and drop - fn navigate_to_path(&self, new_path: &Path) { + fn navigate_to_file(&self, new_file: &gio::File) { let sliding_view = self.sliding_view(); - if Some(new_path.to_path_buf()) == self.current_path() { - return; + if let Some(current_file) = self.current_file() { + if new_file.equal(¤t_file) { + return; + } } - let Some(new_index) = self.model().index_of(new_path) else { - log::warn!("Could not navigate to file {new_path:?}"); + let Some(new_index) = self.model().index_of(new_file) else { + log::warn!("Could not navigate to file {}", new_file.uri()); return; }; let current_index = self - .current_path() + .current_file() .and_then(|x| self.model().index_of(&x)) .unwrap_or_default(); - let page = if let Some(page) = sliding_view.get(new_path) { + let page = if let Some(page) = sliding_view.get(&new_file) { page } else { - let page = LpImagePage::from_path(new_path); + let page = LpImagePage::from_file(new_file); if new_index > current_index { sliding_view.append(&page); @@ -341,34 +347,37 @@ impl LpImageView { page }; - log::debug!("Scrolling to page for {new_path:?}"); + log::debug!("Scrolling to page for {}", new_file.uri()); self.imp().preserve_content.set(true); sliding_view.scroll_to(&page); } /// Ensures the sliding view contains the correct images - fn update_sliding_view(&self, current_path: &Path) { - log::debug!("Updating sliding_view neighbors for current path {current_path:?}"); + fn update_sliding_view(&self, current_file: &gio::File) { + log::debug!( + "Updating sliding_view neighbors for current path {}", + current_file.uri() + ); let sliding_view = self.sliding_view(); self.imp().preserve_content.set(false); - let existing = sliding_view.pages(); - let target = self.model().files_around(current_path, BUFFER); + let existing = dbg!(sliding_view.pages()); + let target = dbg!(self.model().files_around(current_file, BUFFER)); // remove old pages - for (path, page) in &existing { - if !target.contains(path) { - sliding_view.remove(page); + for (uri, page) in &existing { + if !target.contains_key(uri) { + sliding_view.remove(dbg!(page)); } } // add missing pages or put in correct position - for (position, path) in target.iter().enumerate() { - if let Some(page) = existing.get(path) { - sliding_view.move_to(page, position); + for (position, (_, file)) in target.iter().enumerate() { + if let Some(page) = existing.get(&file.uri()) { + sliding_view.move_to(page, dbg!(position)); } else { - sliding_view.insert(&LpImagePage::from_path(path), position); + sliding_view.insert(&LpImagePage::from_file(file), dbg!(position)); } } @@ -376,9 +385,9 @@ impl LpImageView { self.notify("is-next-available"); } - fn scroll_sliding_view(&self, path: &Path) { - let Some(current_page) = self.sliding_view().pages().remove(path) else { - log::error!("Current path not available in sliding_view for scrolling: {path:?}"); + fn scroll_sliding_view(&self, file: &gio::File) { + let Some(current_page) = self.sliding_view().pages().remove(&file.uri()) else { + log::error!("Current path not available in sliding_view for scrolling: {}", file.uri()); return; }; @@ -402,15 +411,15 @@ impl LpImageView { /// Handle files are added or removed from directory fn model_content_changed_cb(&self) { - let Some(current_path) = self.current_path() else { return; }; + let Some(current_file) = self.current_file() else { return; }; // LpImage did not get the update yet // Update will be handled by current_image_path_changed - if !self.model().contains(¤t_path) { + if !self.model().contains(¤t_file) { return; } - self.update_sliding_view(¤t_path); + self.update_sliding_view(¤t_file); } /// Handle current image being moved or deleted @@ -424,9 +433,9 @@ impl LpImageView { } } - if let Some(current_path) = self.current_path() { - if self.model().contains(¤t_path) && !self.imp().preserve_content.get() { - self.update_sliding_view(¤t_path); + if let Some(current_file) = self.current_file() { + if self.model().contains(¤t_file) && !self.imp().preserve_content.get() { + self.update_sliding_view(¤t_file); } } } @@ -446,8 +455,11 @@ impl LpImageView { self.imp().sliding_view.current_page() } - pub fn current_path(&self) -> Option { - self.imp().sliding_view.current_page().map(|x| x.path()) + pub fn current_uri(&self) -> Option { + self.imp() + .sliding_view + .current_page() + .map(|x| x.file().uri()) } pub fn current_file(&self) -> Option { @@ -459,8 +471,8 @@ impl LpImageView { /// Returns `true` if there is an image before the current one pub fn is_previous_available(&self) -> bool { - if let Some(path) = self.current_path() { - self.model().index_of(&path) != Some(0) + if let Some(file) = self.current_file() { + self.model().index_of(&file) != Some(0) } else { false } @@ -468,9 +480,9 @@ impl LpImageView { /// Returns `true` if there is an image after the current one pub fn is_next_available(&self) -> bool { - if let Some(path) = self.current_path() { + if let Some(file) = self.current_file() { let model = self.model(); - model.index_of(&path) != Some(model.n_files().saturating_sub(1)) + model.index_of(&file) != Some(model.n_files().saturating_sub(1)) } else { false } @@ -488,7 +500,7 @@ impl LpImageView { }; if !self.imp().preserve_content.get() { - self.update_sliding_view(&new_page.path()); + self.update_sliding_view(&new_page.file()); } } @@ -499,7 +511,7 @@ impl LpImageView { if self.imp().preserve_content.get() { if let Some(new_page) = ¤t_page { - self.update_sliding_view(&new_page.path()); + self.update_sliding_view(&new_page.file()); } else { log::error!("No LpImagePage"); } @@ -508,7 +520,7 @@ impl LpImageView { // Reset zoom and rotation of other pages if let Some(new_page) = ¤t_page { let mut other_pages = self.sliding_view().pages(); - other_pages.remove(&new_page.path()); + other_pages.remove(&new_page.file().uri()); for (_, page) in other_pages { let image = page.image(); image.reset_rotation(); @@ -552,18 +564,18 @@ impl LpImageView { } pub async fn set_background(&self) -> anyhow::Result<()> { - let background = self - .current_path() - .and_then(|p| std::fs::File::open(p).ok()) - .context(gettext("Failed to open new background image"))?; - let native = self.native().expect("View should have a GtkNative"); + let uri = self + .current_file() + .and_then(|f| url::Url::parse(f.uri().as_str()).ok()) + .context("Invalid URL for background image")?; + let native = self.native().context("View should have a GtkNative")?; let id = WindowIdentifier::from_native(&native).await; match wallpaper::WallpaperRequest::default() .set_on(wallpaper::SetOn::Background) .show_preview(true) .identifier(id) - .build_file(&background) + .build_uri(&uri) .await { // We use `1` here because we can't pass enums directly as GVariants, @@ -590,37 +602,6 @@ impl LpImageView { } pub fn print(&self) -> anyhow::Result<()> { - let operation = gtk::PrintOperation::new(); - let path = self.current_path().context("No file")?; - let pb = gdk_pixbuf::Pixbuf::from_file(path)?; - - let setup = gtk::PageSetup::default(); - let page_size = gtk::PaperSize::new(Some(gtk::PAPER_NAME_A4)); - setup.set_paper_size(&page_size); - operation.set_default_page_setup(Some(&setup)); - - let settings = gtk::PrintSettings::default(); - operation.set_print_settings(Some(&settings)); - - operation.connect_begin_print(move |op, _| { - op.set_n_pages(1); - }); - - // FIXME: Rework all of this after reading https://cairographics.org/manual/cairo-Image-Surfaces.html - // Since I don't know cairo; See also eog-print.c - operation.connect_draw_page(clone!(@weak pb => move |_, ctx, _| { - let cr = ctx.cairo_context(); - cr.set_source_pixbuf(&pb, 0.0, 0.0); - let _ = cr.paint(); - })); - - log::debug!("Running print operation..."); - let root = self.root().context("Could not get root for widget")?; - let window = root - .downcast_ref::() - .context("Could not downcast to GtkWindow")?; - operation.run(gtk::PrintOperationAction::PrintDialog, Some(window))?; - Ok(()) } diff --git a/src/widgets/sliding_view.rs b/src/widgets/sliding_view.rs index c5daef0..f1dd837 100644 --- a/src/widgets/sliding_view.rs +++ b/src/widgets/sliding_view.rs @@ -11,7 +11,7 @@ use indexmap::IndexMap; use once_cell::sync::{Lazy, OnceCell}; use std::cell::{Cell, RefCell}; -use std::path::{Path, PathBuf}; +//use std::path::{Path, PathBuf}; static SCROLL_DAMPING_RATIO: f64 = 1.; static SCROLL_MASS: f64 = 0.5; @@ -266,6 +266,11 @@ impl LpSlidingView { /// Add page to the specified position pub fn insert(&self, page: &LpImagePage, pos: usize) { + if pos > self.n_pages() { + log::error!("Invalid insert position {pos} for {}", page.file().uri()); + return; + } + let current_index = self.current_index(); let previous_sibling = self.imp().pages.borrow().get(pos).cloned(); @@ -290,7 +295,7 @@ impl LpSlidingView { } self.shift_position(current_index); } else { - log::error!("Page to move not found"); + log::error!("Page to move not found: {}", page.file().uri()); } } @@ -338,22 +343,23 @@ impl LpSlidingView { } /// Gives all pages with hash access via their path - pub fn pages(&self) -> IndexMap { + pub fn pages(&self) -> IndexMap { self.imp() .pages .borrow() .iter() .cloned() - .map(|x| (x.path(), x)) + .map(|x| (x.file().uri(), x)) .collect() } - pub fn get(&self, path: &Path) -> Option { + pub fn get(&self, file: &gio::File) -> Option { + let uri = file.uri(); self.imp() .pages .borrow() .iter() - .find(|x| x.path() == path) + .find(|x| x.file().uri() == uri) .cloned() } @@ -363,15 +369,17 @@ impl LpSlidingView { } pub fn scroll_to_velocity(&self, page: &LpImagePage, initial_velocity: f64) { - let index = self.index_of(page).unwrap(); + if let Some(index) = self.index_of(page) { + let animation = self.scroll_animation(); - let animation = self.scroll_animation(); - - animation.set_value_from(self.position()); - animation.set_value_to(index as f64 - self.position_shift()); - animation.set_initial_velocity(initial_velocity); - animation.play(); - self.set_current_page(Some(page)); + animation.set_value_from(self.position()); + animation.set_value_to(index as f64 - self.position_shift()); + animation.set_initial_velocity(initial_velocity); + animation.play(); + self.set_current_page(Some(page)); + } else { + log::error!("Page not in LpSlidingView {}", page.file().uri()); + } } /// Animates removal of current page diff --git a/src/window.rs b/src/window.rs index e76ae54..ea843f0 100644 --- a/src/window.rs +++ b/src/window.rs @@ -349,7 +349,7 @@ mod imp { .map(|t| t.to_string()) .filter(|t| t.starts_with("image/")) .is_some() { - obj.set_image_from_path(&file.path().unwrap()); + obj.set_image_from_file(&file); } else { obj.show_toast( gettext_f("“{}” is not a valid image.", &[&info.display_name()]), @@ -434,17 +434,16 @@ impl LpWindow { .build(); if let Ok(file) = chooser.open_future(Some(self)).await { - self.set_image_from_path(&file.path().unwrap()); + self.set_image_from_file(&file); } else { - // TODO: ui error - log::error!("Failed to choose file"); + log::debug!("File dialog canceled or file not readable"); } } async fn open_with(&self) { let imp = self.imp(); - if let Some(ref file) = imp.image_view.current_path().map(gio::File::for_path) { + if let Some(ref file) = imp.image_view.current_file() { let launcher = gtk::FileLauncher::new(Some(file)); if let Err(e) = launcher.launch_future(Some(self)).await { if !e.matches(gtk::DialogError::Dismissed) { @@ -491,7 +490,7 @@ impl LpWindow { async fn trash(&self) { let image_view = self.image_view(); - let (Some(file), Some(path)) = (image_view.current_file(), image_view.current_path()) + let (Some(file), Some(path)) = (image_view.current_file(), image_view.current_file().and_then(|x| x.path())) else { log::error!("No file to trash"); return; }; let result = file.trash_future(glib::Priority::default()).await; @@ -507,7 +506,7 @@ impl LpWindow { spawn(async move { let result = crate::util::untrash(&path).await; match result { - Ok(()) => win.image_view().set_image_from_path(&path), + Ok(()) => win.image_view().set_image_from_file(&gio::File::for_path(&path)), Err(err) => { log::error!("Failed to untrash {path:?}: {err}"); win.show_toast( @@ -583,11 +582,11 @@ impl LpWindow { imp.toast_overlay.add_toast(toast); } - pub fn set_image_from_path(&self, path: &Path) { + pub fn set_image_from_file(&self, file: &gio::File) { let imp = self.imp(); - log::debug!("Loading file: {path:?}"); - imp.image_view.set_image_from_path(path); + log::debug!("Loading file: {}", file.uri()); + imp.image_view.set_image_from_file(file); self.set_actions_enabled(true); }