fileModel: Use GFile with URI as key
Part-of: <https://gitlab.gnome.org/Incubator/loupe/-/merge_requests/174>
This commit is contained in:
parent
3b0c32ecee
commit
032707a210
10 changed files with 164 additions and 188 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1918,6 +1918,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"safe-transmute",
|
"safe-transmute",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ safe-transmute = "0.11.2"
|
||||||
image-rs = { package = "image", version = "0.24.6" }
|
image-rs = { package = "image", version = "0.24.6" }
|
||||||
libheif-rs = "0.19.0"
|
libheif-rs = "0.19.0"
|
||||||
librsvg = { git = "https://gitlab.gnome.org/GNOME/librsvg.git", rev = "2.56.0" }
|
librsvg = { git = "https://gitlab.gnome.org/GNOME/librsvg.git", rev = "2.56.0" }
|
||||||
|
url = "2.3.1"
|
||||||
|
|
||||||
[dependencies.ashpd]
|
[dependencies.ashpd]
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,8 @@ mod imp {
|
||||||
fn open(&self, files: &[gio::File], _hint: &str) {
|
fn open(&self, files: &[gio::File], _hint: &str) {
|
||||||
let application = self.obj();
|
let application = self.obj();
|
||||||
for file in files {
|
for file in files {
|
||||||
let Some(path) = file.path() else { log::error!("File has no path {}", file.uri()); return; };
|
|
||||||
|
|
||||||
let win = LpWindow::new(&*application);
|
let win = LpWindow::new(&*application);
|
||||||
win.set_image_from_path(&path);
|
win.set_image_from_file(&file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,11 @@ use crate::deps::*;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use crate::util::gettext::*;
|
use crate::util::gettext::*;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
use gio::subclass::prelude::*;
|
use gio::subclass::prelude::*;
|
||||||
use indexmap::IndexSet;
|
use glib::GString;
|
||||||
|
use indexmap::{IndexMap, IndexSet};
|
||||||
use once_cell::sync::{Lazy, OnceCell};
|
use once_cell::sync::{Lazy, OnceCell};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
|
@ -38,8 +37,8 @@ mod imp {
|
||||||
pub struct LpFileModel {
|
pub struct LpFileModel {
|
||||||
/// Use and IndexSet such that we can put the elements into an arbitrary order
|
/// Use and IndexSet such that we can put the elements into an arbitrary order
|
||||||
/// while still having fast hash access via `PathBuf`.
|
/// while still having fast hash access via `PathBuf`.
|
||||||
pub(super) files: RefCell<IndexSet<PathBuf>>,
|
pub(super) files: RefCell<IndexMap<GString, gio::File>>,
|
||||||
pub(super) directory: OnceCell<PathBuf>,
|
pub(super) directory: OnceCell<gio::File>,
|
||||||
/// Track file changes.
|
/// Track file changes.
|
||||||
pub(super) monitor: OnceCell<gio::FileMonitor>,
|
pub(super) monitor: OnceCell<gio::FileMonitor>,
|
||||||
}
|
}
|
||||||
|
|
@ -71,12 +70,12 @@ impl Default for LpFileModel {
|
||||||
|
|
||||||
impl LpFileModel {
|
impl LpFileModel {
|
||||||
/// Create with a single element
|
/// 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 model = Self::default();
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut vec = model.imp().files.borrow_mut();
|
let mut vec = model.imp().files.borrow_mut();
|
||||||
vec.insert(path.to_path_buf());
|
vec.insert(file.uri(), file.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
model
|
model
|
||||||
|
|
@ -85,10 +84,10 @@ impl LpFileModel {
|
||||||
/// Load all file from the given directory
|
/// Load all file from the given directory
|
||||||
///
|
///
|
||||||
/// This can be used if there are already files present
|
/// 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();
|
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_directory(gio::FileMonitorFlags::WATCH_MOVES, gio::Cancellable::NONE)?;
|
||||||
|
|
||||||
monitor.connect_changed(
|
monitor.connect_changed(
|
||||||
|
|
@ -99,9 +98,9 @@ impl LpFileModel {
|
||||||
self.imp().monitor.set(monitor).unwrap();
|
self.imp().monitor.set(monitor).unwrap();
|
||||||
|
|
||||||
let new_files_result = gio::spawn_blocking(move || {
|
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(
|
.enumerate_children(
|
||||||
&format!(
|
&format!(
|
||||||
"{},{}",
|
"{},{}",
|
||||||
|
|
@ -118,9 +117,8 @@ impl LpFileModel {
|
||||||
if let Some(content_type) = info.content_type().map(|t| t.to_string()) {
|
if let Some(content_type) = info.content_type().map(|t| t.to_string()) {
|
||||||
// Filter out non-images; For now we support "all" image types.
|
// Filter out non-images; For now we support "all" image types.
|
||||||
if content_type.starts_with("image/") {
|
if content_type.starts_with("image/") {
|
||||||
let path = PathBuf::from_iter([directory.clone(), info.name()]);
|
let file = directory.child(info.name());
|
||||||
log::debug!("{path:?} is an image, adding to the list");
|
files.insert(file.uri(), file);
|
||||||
files.insert(path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
// 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();
|
let mut files = self.imp().files.borrow_mut();
|
||||||
for path in files.iter() {
|
for (uri, file) in files.iter() {
|
||||||
new_files.insert(path.clone());
|
new_files.insert(uri.clone(), file.clone());
|
||||||
}
|
}
|
||||||
*files = new_files;
|
*files = new_files;
|
||||||
// Then sort by name.
|
// Then sort by name.
|
||||||
|
|
@ -152,47 +150,49 @@ impl LpFileModel {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn directory(&self) -> Option<PathBuf> {
|
pub fn directory(&self) -> Option<gio::File> {
|
||||||
self.imp().directory.get().cloned()
|
self.imp().directory.get().cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index_of(&self, path: &Path) -> Option<usize> {
|
pub fn index_of(&self, file: &gio::File) -> Option<usize> {
|
||||||
self.imp().files.borrow().get_index_of(path)
|
self.imp().files.borrow().get_index_of(&file.uri())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn n_files(&self) -> usize {
|
pub fn n_files(&self) -> usize {
|
||||||
self.imp().files.borrow().len()
|
self.imp().files.borrow().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, path: &Path) -> bool {
|
pub fn contains(&self, file: &gio::File) -> bool {
|
||||||
self.imp().files.borrow().contains(path)
|
self.imp().files.borrow().contains_key(&file.uri())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn before(&self, path: &Path) -> Option<PathBuf> {
|
pub fn before(&self, file: &gio::File) -> Option<gio::File> {
|
||||||
let index = self.index_of(path)?;
|
let index = self.index_of(&file)?;
|
||||||
|
|
||||||
self.imp()
|
self.imp()
|
||||||
.files
|
.files
|
||||||
.borrow()
|
.borrow()
|
||||||
.get_index(index.checked_sub(1)?)
|
.get_index(index.checked_sub(1)?)
|
||||||
|
.map(|x| x.1)
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn after(&self, path: &Path) -> Option<PathBuf> {
|
pub fn after(&self, file: &gio::File) -> Option<gio::File> {
|
||||||
let index = self.index_of(path)?;
|
let index = self.index_of(file)?;
|
||||||
|
|
||||||
self.imp()
|
self.imp()
|
||||||
.files
|
.files
|
||||||
.borrow()
|
.borrow()
|
||||||
.get_index(index.checked_add(1)?)
|
.get_index(index.checked_add(1)?)
|
||||||
|
.map(|x| x.1)
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `n` elements before and after given path
|
/// Returns `n` elements before and after given path
|
||||||
pub fn files_around(&self, path: &Path, n: usize) -> IndexSet<PathBuf> {
|
pub fn files_around(&self, file: &gio::File, n: usize) -> IndexMap<GString, gio::File> {
|
||||||
let Some(index) = self.index_of(path) else {
|
let Some(index) = self.index_of(file) else {
|
||||||
log::error!("Path not in model: {path:?}");
|
log::error!("URI not in model: {}", file.uri());
|
||||||
return IndexSet::new();
|
return IndexMap::new();
|
||||||
};
|
};
|
||||||
|
|
||||||
let reduce = if index < n { n - index } else { 0 };
|
let reduce = if index < n { n - index } else { 0 };
|
||||||
|
|
@ -203,23 +203,23 @@ impl LpFileModel {
|
||||||
.iter()
|
.iter()
|
||||||
.skip(index.saturating_sub(n))
|
.skip(index.saturating_sub(n))
|
||||||
.take(2 * n + 1 - reduce)
|
.take(2 * n + 1 - reduce)
|
||||||
.cloned()
|
.map(|(k, v)| (k.clone(), v.clone()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return first path
|
/// Return first path
|
||||||
pub fn first(&self) -> Option<PathBuf> {
|
pub fn first(&self) -> Option<gio::File> {
|
||||||
self.imp().files.borrow().first().cloned()
|
self.imp().files.borrow().first().map(|x| x.1).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns last path
|
/// Returns last path
|
||||||
pub fn last(&self) -> Option<PathBuf> {
|
pub fn last(&self) -> Option<gio::File> {
|
||||||
self.imp().files.borrow().last().cloned()
|
self.imp().files.borrow().last().map(|x| x.1).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Currently sorts by name
|
/// Currently sorts by name
|
||||||
fn sort(files: &mut IndexSet<PathBuf>) {
|
fn sort(files: &mut IndexMap<GString, gio::File>) {
|
||||||
files.sort_by(|x, y| util::compare_by_name(x, y));
|
files.sort_by(|_, x, _, y| util::compare_by_name(&x.uri(), &y.uri()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if a file is added
|
/// Determines if a file is added
|
||||||
|
|
@ -253,12 +253,6 @@ impl LpFileModel {
|
||||||
file_a: &gio::File,
|
file_a: &gio::File,
|
||||||
file_b: Option<&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 {
|
let changed = match event {
|
||||||
gio::FileMonitorEvent::Created
|
gio::FileMonitorEvent::Created
|
||||||
| gio::FileMonitorEvent::MovedIn
|
| gio::FileMonitorEvent::MovedIn
|
||||||
|
|
@ -268,7 +262,7 @@ impl LpFileModel {
|
||||||
if Self::is_image_file(file_a) =>
|
if Self::is_image_file(file_a) =>
|
||||||
{
|
{
|
||||||
let mut files = self.imp().files.borrow_mut();
|
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 {
|
if changed {
|
||||||
Self::sort(&mut files);
|
Self::sort(&mut files);
|
||||||
}
|
}
|
||||||
|
|
@ -276,20 +270,20 @@ impl LpFileModel {
|
||||||
}
|
}
|
||||||
gio::FileMonitorEvent::Deleted | gio::FileMonitorEvent::MovedOut | gio::FileMonitorEvent::Unmounted => {
|
gio::FileMonitorEvent::Deleted | gio::FileMonitorEvent::MovedOut | gio::FileMonitorEvent::Unmounted => {
|
||||||
let mut files = self.imp().files.borrow_mut();
|
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 {
|
if changed {
|
||||||
Self::sort(&mut files);
|
Self::sort(&mut files);
|
||||||
}
|
}
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
gio::FileMonitorEvent::Renamed => {
|
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 changed = false;
|
||||||
{
|
{
|
||||||
let mut files = self.imp().files.borrow_mut();
|
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) {
|
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 {
|
if changed {
|
||||||
Self::sort(&mut files);
|
Self::sort(&mut files);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,7 @@ pub async fn query_attributes_future(
|
||||||
.context("Failed to query attributes")
|
.context("Failed to query attributes")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare_by_name(file_a: &Path, file_b: &Path) -> std::cmp::Ordering {
|
pub fn compare_by_name(name_a: &glib::GString, name_b: &glib::GString) -> std::cmp::Ordering {
|
||||||
let name_a = file_a.display().to_string();
|
|
||||||
let name_b = file_b.display().to_string();
|
|
||||||
|
|
||||||
let key_a = glib::FilenameCollationKey::from(name_a);
|
let key_a = glib::FilenameCollationKey::from(name_a);
|
||||||
let key_b = glib::FilenameCollationKey::from(name_b);
|
let key_b = glib::FilenameCollationKey::from(name_b);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ use once_cell::sync::Lazy;
|
||||||
use once_cell::unsync::OnceCell;
|
use once_cell::unsync::OnceCell;
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Default background color around images and behind transparent images
|
/// Default background color around images and behind transparent images
|
||||||
|
|
@ -742,9 +742,8 @@ glib::wrapper! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LpImage {
|
impl LpImage {
|
||||||
pub async fn load(&self, path: &Path) {
|
pub async fn load(&self, file: &gio::File) {
|
||||||
let path = path.to_path_buf();
|
let path = file.path().unwrap();
|
||||||
let file = gio::File::for_path(&path);
|
|
||||||
self.set_file(&file);
|
self.set_file(&file);
|
||||||
|
|
||||||
if !path.is_file() {
|
if !path.is_file() {
|
||||||
|
|
@ -963,7 +962,7 @@ impl LpImage {
|
||||||
let file = file_a.clone();
|
let file = file_a.clone();
|
||||||
// TODO: error handling is missing
|
// TODO: error handling is missing
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
obj.load(&file.path().unwrap()).await;
|
obj.load(&file).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
gio::FileMonitorEvent::Deleted
|
gio::FileMonitorEvent::Deleted
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ use gtk::prelude::*;
|
||||||
use gtk::CompositeTemplate;
|
use gtk::CompositeTemplate;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -148,23 +148,21 @@ glib::wrapper! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LpImagePage {
|
impl LpImagePage {
|
||||||
pub fn from_path(path: &Path) -> Self {
|
pub fn from_file(file: &gio::File) -> Self {
|
||||||
let obj = glib::Object::new::<Self>();
|
let obj = glib::Object::new::<Self>();
|
||||||
let path = path.to_path_buf();
|
|
||||||
let file = gio::File::for_path(&path);
|
|
||||||
|
|
||||||
obj.imp().image.set_file(&file);
|
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();
|
let imp = obj.imp();
|
||||||
imp.image.load(&path).await;
|
imp.image.load(&file).await;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(&self) -> PathBuf {
|
pub fn file(&self) -> gio::File {
|
||||||
self.image().path().unwrap()
|
self.image().file().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scrolled_window(&self) -> gtk::ScrolledWindow {
|
pub fn scrolled_window(&self) -> gtk::ScrolledWindow {
|
||||||
|
|
|
||||||
|
|
@ -228,12 +228,12 @@ glib::wrapper! {
|
||||||
|
|
||||||
#[gtk::template_callbacks]
|
#[gtk::template_callbacks]
|
||||||
impl LpImageView {
|
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:
|
// Add image to recently used file. Does not work in Flatpaks:
|
||||||
// <https://github.com/flatpak/xdg-desktop-portal/issues/215>
|
// <https://github.com/flatpak/xdg-desktop-portal/issues/215>
|
||||||
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}");
|
log::error!("Failed to load path: {err}");
|
||||||
self.activate_action("win.show-toast", Some(&(err.to_string(), 1).to_variant()))
|
self.activate_action("win.show-toast", Some(&(err.to_string(), 1).to_variant()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -244,39 +244,43 @@ impl LpImageView {
|
||||||
// that holds a `gio::File` for each child within the same directory as the
|
// 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,
|
// file we pass. This model will update with changes to the directory,
|
||||||
// and in turn we'll update our `adw::Carousel`.
|
// 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 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 {
|
if is_same_directory {
|
||||||
log::debug!("Re-using old model and navigating to the current file");
|
log::debug!("Re-using old model and navigating to the current file");
|
||||||
self.navigate_to_path(path);
|
self.navigate_to_file(file);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let model = LpFileModel::from_path(path);
|
let model = LpFileModel::from_file(file);
|
||||||
self.set_model(model);
|
self.set_model(model);
|
||||||
log::debug!("New model created");
|
log::debug!("New model created");
|
||||||
|
|
||||||
let page = LpImagePage::from_path(path);
|
let page = LpImagePage::from_file(file);
|
||||||
|
|
||||||
sliding_view.clear();
|
sliding_view.clear();
|
||||||
sliding_view.append(&page);
|
sliding_view.append(&page);
|
||||||
|
|
||||||
// List other files in directory
|
// List other files in directory
|
||||||
if let Some(directory) = directory {
|
if let Some(directory) = directory {
|
||||||
let path = path.to_path_buf();
|
spawn(glib::clone!(@weak self as obj, @strong file => async move {
|
||||||
spawn(glib::clone!(@weak self as obj, @strong path => async move {
|
if let Err(err) = obj.model().load_directory(directory.clone()).await {
|
||||||
if let Err(err) = obj.model().load_directory(directory).await {
|
|
||||||
log::warn!("Failed to load directory: {err}");
|
log::warn!("Failed to load directory: {err}");
|
||||||
obj.activate_action("win.show-toast", Some(&(err.to_string(), 1).to_variant()))
|
obj.activate_action("win.show-toast", Some(&(err.to_string(), 1).to_variant()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.update_sliding_view(&path);
|
obj.update_sliding_view(&file);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,52 +289,54 @@ impl LpImageView {
|
||||||
|
|
||||||
/// Move forward or backwards
|
/// Move forward or backwards
|
||||||
pub fn navigate(&self, direction: Direction) {
|
pub fn navigate(&self, direction: Direction) {
|
||||||
if let Some(current_path) = self.current_path() {
|
if let Some(current_file) = self.current_file() {
|
||||||
let new_path = match direction {
|
let new_file = match direction {
|
||||||
Direction::Forward => self.model().after(¤t_path),
|
Direction::Forward => self.model().after(¤t_file),
|
||||||
Direction::Back => self.model().before(¤t_path),
|
Direction::Back => self.model().before(¤t_file),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(new_path) = new_path {
|
if let Some(new_file) = new_file {
|
||||||
self.scroll_sliding_view(&new_path);
|
self.scroll_sliding_view(&new_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Jump to position
|
/// Jump to position
|
||||||
pub fn jump(&self, position: Position) {
|
pub fn jump(&self, position: Position) {
|
||||||
let new_path = match position {
|
let new_file = match position {
|
||||||
Position::First => self.model().first(),
|
Position::First => self.model().first(),
|
||||||
Position::Last => self.model().last(),
|
Position::Last => self.model().last(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(new_path) = new_path {
|
if let Some(new_file) = new_file {
|
||||||
self.navigate_to_path(&new_path);
|
self.navigate_to_file(&new_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used for drag and drop
|
/// 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();
|
let sliding_view = self.sliding_view();
|
||||||
|
|
||||||
if Some(new_path.to_path_buf()) == self.current_path() {
|
if let Some(current_file) = self.current_file() {
|
||||||
return;
|
if new_file.equal(¤t_file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(new_index) = self.model().index_of(new_path) else {
|
let Some(new_index) = self.model().index_of(new_file) else {
|
||||||
log::warn!("Could not navigate to file {new_path:?}");
|
log::warn!("Could not navigate to file {}", new_file.uri());
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_index = self
|
let current_index = self
|
||||||
.current_path()
|
.current_file()
|
||||||
.and_then(|x| self.model().index_of(&x))
|
.and_then(|x| self.model().index_of(&x))
|
||||||
.unwrap_or_default();
|
.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
|
page
|
||||||
} else {
|
} else {
|
||||||
let page = LpImagePage::from_path(new_path);
|
let page = LpImagePage::from_file(new_file);
|
||||||
|
|
||||||
if new_index > current_index {
|
if new_index > current_index {
|
||||||
sliding_view.append(&page);
|
sliding_view.append(&page);
|
||||||
|
|
@ -341,34 +347,37 @@ impl LpImageView {
|
||||||
page
|
page
|
||||||
};
|
};
|
||||||
|
|
||||||
log::debug!("Scrolling to page for {new_path:?}");
|
log::debug!("Scrolling to page for {}", new_file.uri());
|
||||||
self.imp().preserve_content.set(true);
|
self.imp().preserve_content.set(true);
|
||||||
sliding_view.scroll_to(&page);
|
sliding_view.scroll_to(&page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures the sliding view contains the correct images
|
/// Ensures the sliding view contains the correct images
|
||||||
fn update_sliding_view(&self, current_path: &Path) {
|
fn update_sliding_view(&self, current_file: &gio::File) {
|
||||||
log::debug!("Updating sliding_view neighbors for current path {current_path:?}");
|
log::debug!(
|
||||||
|
"Updating sliding_view neighbors for current path {}",
|
||||||
|
current_file.uri()
|
||||||
|
);
|
||||||
let sliding_view = self.sliding_view();
|
let sliding_view = self.sliding_view();
|
||||||
|
|
||||||
self.imp().preserve_content.set(false);
|
self.imp().preserve_content.set(false);
|
||||||
|
|
||||||
let existing = sliding_view.pages();
|
let existing = dbg!(sliding_view.pages());
|
||||||
let target = self.model().files_around(current_path, BUFFER);
|
let target = dbg!(self.model().files_around(current_file, BUFFER));
|
||||||
|
|
||||||
// remove old pages
|
// remove old pages
|
||||||
for (path, page) in &existing {
|
for (uri, page) in &existing {
|
||||||
if !target.contains(path) {
|
if !target.contains_key(uri) {
|
||||||
sliding_view.remove(page);
|
sliding_view.remove(dbg!(page));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add missing pages or put in correct position
|
// add missing pages or put in correct position
|
||||||
for (position, path) in target.iter().enumerate() {
|
for (position, (_, file)) in target.iter().enumerate() {
|
||||||
if let Some(page) = existing.get(path) {
|
if let Some(page) = existing.get(&file.uri()) {
|
||||||
sliding_view.move_to(page, position);
|
sliding_view.move_to(page, dbg!(position));
|
||||||
} else {
|
} 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");
|
self.notify("is-next-available");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_sliding_view(&self, path: &Path) {
|
fn scroll_sliding_view(&self, file: &gio::File) {
|
||||||
let Some(current_page) = self.sliding_view().pages().remove(path) else {
|
let Some(current_page) = self.sliding_view().pages().remove(&file.uri()) else {
|
||||||
log::error!("Current path not available in sliding_view for scrolling: {path:?}");
|
log::error!("Current path not available in sliding_view for scrolling: {}", file.uri());
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -402,15 +411,15 @@ impl LpImageView {
|
||||||
|
|
||||||
/// Handle files are added or removed from directory
|
/// Handle files are added or removed from directory
|
||||||
fn model_content_changed_cb(&self) {
|
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
|
// LpImage did not get the update yet
|
||||||
// Update will be handled by current_image_path_changed
|
// Update will be handled by current_image_path_changed
|
||||||
if !self.model().contains(¤t_path) {
|
if !self.model().contains(¤t_file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_sliding_view(¤t_path);
|
self.update_sliding_view(¤t_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle current image being moved or deleted
|
/// Handle current image being moved or deleted
|
||||||
|
|
@ -424,9 +433,9 @@ impl LpImageView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(current_path) = self.current_path() {
|
if let Some(current_file) = self.current_file() {
|
||||||
if self.model().contains(¤t_path) && !self.imp().preserve_content.get() {
|
if self.model().contains(¤t_file) && !self.imp().preserve_content.get() {
|
||||||
self.update_sliding_view(¤t_path);
|
self.update_sliding_view(¤t_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -446,8 +455,11 @@ impl LpImageView {
|
||||||
self.imp().sliding_view.current_page()
|
self.imp().sliding_view.current_page()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_path(&self) -> Option<PathBuf> {
|
pub fn current_uri(&self) -> Option<glib::GString> {
|
||||||
self.imp().sliding_view.current_page().map(|x| x.path())
|
self.imp()
|
||||||
|
.sliding_view
|
||||||
|
.current_page()
|
||||||
|
.map(|x| x.file().uri())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_file(&self) -> Option<gio::File> {
|
pub fn current_file(&self) -> Option<gio::File> {
|
||||||
|
|
@ -459,8 +471,8 @@ impl LpImageView {
|
||||||
|
|
||||||
/// Returns `true` if there is an image before the current one
|
/// Returns `true` if there is an image before the current one
|
||||||
pub fn is_previous_available(&self) -> bool {
|
pub fn is_previous_available(&self) -> bool {
|
||||||
if let Some(path) = self.current_path() {
|
if let Some(file) = self.current_file() {
|
||||||
self.model().index_of(&path) != Some(0)
|
self.model().index_of(&file) != Some(0)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -468,9 +480,9 @@ impl LpImageView {
|
||||||
|
|
||||||
/// Returns `true` if there is an image after the current one
|
/// Returns `true` if there is an image after the current one
|
||||||
pub fn is_next_available(&self) -> bool {
|
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();
|
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 {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -488,7 +500,7 @@ impl LpImageView {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !self.imp().preserve_content.get() {
|
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 self.imp().preserve_content.get() {
|
||||||
if let Some(new_page) = ¤t_page {
|
if let Some(new_page) = ¤t_page {
|
||||||
self.update_sliding_view(&new_page.path());
|
self.update_sliding_view(&new_page.file());
|
||||||
} else {
|
} else {
|
||||||
log::error!("No LpImagePage");
|
log::error!("No LpImagePage");
|
||||||
}
|
}
|
||||||
|
|
@ -508,7 +520,7 @@ impl LpImageView {
|
||||||
// Reset zoom and rotation of other pages
|
// Reset zoom and rotation of other pages
|
||||||
if let Some(new_page) = ¤t_page {
|
if let Some(new_page) = ¤t_page {
|
||||||
let mut other_pages = self.sliding_view().pages();
|
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 {
|
for (_, page) in other_pages {
|
||||||
let image = page.image();
|
let image = page.image();
|
||||||
image.reset_rotation();
|
image.reset_rotation();
|
||||||
|
|
@ -552,18 +564,18 @@ impl LpImageView {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_background(&self) -> anyhow::Result<()> {
|
pub async fn set_background(&self) -> anyhow::Result<()> {
|
||||||
let background = self
|
let uri = self
|
||||||
.current_path()
|
.current_file()
|
||||||
.and_then(|p| std::fs::File::open(p).ok())
|
.and_then(|f| url::Url::parse(f.uri().as_str()).ok())
|
||||||
.context(gettext("Failed to open new background image"))?;
|
.context("Invalid URL for background image")?;
|
||||||
let native = self.native().expect("View should have a GtkNative");
|
let native = self.native().context("View should have a GtkNative")?;
|
||||||
let id = WindowIdentifier::from_native(&native).await;
|
let id = WindowIdentifier::from_native(&native).await;
|
||||||
|
|
||||||
match wallpaper::WallpaperRequest::default()
|
match wallpaper::WallpaperRequest::default()
|
||||||
.set_on(wallpaper::SetOn::Background)
|
.set_on(wallpaper::SetOn::Background)
|
||||||
.show_preview(true)
|
.show_preview(true)
|
||||||
.identifier(id)
|
.identifier(id)
|
||||||
.build_file(&background)
|
.build_uri(&uri)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
// We use `1` here because we can't pass enums directly as GVariants,
|
// 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<()> {
|
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::<gtk::Window>()
|
|
||||||
.context("Could not downcast to GtkWindow")?;
|
|
||||||
operation.run(gtk::PrintOperationAction::PrintDialog, Some(window))?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use indexmap::IndexMap;
|
||||||
use once_cell::sync::{Lazy, OnceCell};
|
use once_cell::sync::{Lazy, OnceCell};
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::path::{Path, PathBuf};
|
//use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
static SCROLL_DAMPING_RATIO: f64 = 1.;
|
static SCROLL_DAMPING_RATIO: f64 = 1.;
|
||||||
static SCROLL_MASS: f64 = 0.5;
|
static SCROLL_MASS: f64 = 0.5;
|
||||||
|
|
@ -266,6 +266,11 @@ impl LpSlidingView {
|
||||||
|
|
||||||
/// Add page to the specified position
|
/// Add page to the specified position
|
||||||
pub fn insert(&self, page: &LpImagePage, pos: usize) {
|
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 current_index = self.current_index();
|
||||||
|
|
||||||
let previous_sibling = self.imp().pages.borrow().get(pos).cloned();
|
let previous_sibling = self.imp().pages.borrow().get(pos).cloned();
|
||||||
|
|
@ -290,7 +295,7 @@ impl LpSlidingView {
|
||||||
}
|
}
|
||||||
self.shift_position(current_index);
|
self.shift_position(current_index);
|
||||||
} else {
|
} 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
|
/// Gives all pages with hash access via their path
|
||||||
pub fn pages(&self) -> IndexMap<PathBuf, LpImagePage> {
|
pub fn pages(&self) -> IndexMap<glib::GString, LpImagePage> {
|
||||||
self.imp()
|
self.imp()
|
||||||
.pages
|
.pages
|
||||||
.borrow()
|
.borrow()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|x| (x.path(), x))
|
.map(|x| (x.file().uri(), x))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, path: &Path) -> Option<LpImagePage> {
|
pub fn get(&self, file: &gio::File) -> Option<LpImagePage> {
|
||||||
|
let uri = file.uri();
|
||||||
self.imp()
|
self.imp()
|
||||||
.pages
|
.pages
|
||||||
.borrow()
|
.borrow()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|x| x.path() == path)
|
.find(|x| x.file().uri() == uri)
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,15 +369,17 @@ impl LpSlidingView {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_to_velocity(&self, page: &LpImagePage, initial_velocity: f64) {
|
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_value_from(self.position());
|
animation.set_initial_velocity(initial_velocity);
|
||||||
animation.set_value_to(index as f64 - self.position_shift());
|
animation.play();
|
||||||
animation.set_initial_velocity(initial_velocity);
|
self.set_current_page(Some(page));
|
||||||
animation.play();
|
} else {
|
||||||
self.set_current_page(Some(page));
|
log::error!("Page not in LpSlidingView {}", page.file().uri());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Animates removal of current page
|
/// Animates removal of current page
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ mod imp {
|
||||||
.map(|t| t.to_string())
|
.map(|t| t.to_string())
|
||||||
.filter(|t| t.starts_with("image/"))
|
.filter(|t| t.starts_with("image/"))
|
||||||
.is_some() {
|
.is_some() {
|
||||||
obj.set_image_from_path(&file.path().unwrap());
|
obj.set_image_from_file(&file);
|
||||||
} else {
|
} else {
|
||||||
obj.show_toast(
|
obj.show_toast(
|
||||||
gettext_f("“{}” is not a valid image.", &[&info.display_name()]),
|
gettext_f("“{}” is not a valid image.", &[&info.display_name()]),
|
||||||
|
|
@ -434,17 +434,16 @@ impl LpWindow {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
if let Ok(file) = chooser.open_future(Some(self)).await {
|
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 {
|
} else {
|
||||||
// TODO: ui error
|
log::debug!("File dialog canceled or file not readable");
|
||||||
log::error!("Failed to choose file");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn open_with(&self) {
|
async fn open_with(&self) {
|
||||||
let imp = self.imp();
|
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));
|
let launcher = gtk::FileLauncher::new(Some(file));
|
||||||
if let Err(e) = launcher.launch_future(Some(self)).await {
|
if let Err(e) = launcher.launch_future(Some(self)).await {
|
||||||
if !e.matches(gtk::DialogError::Dismissed) {
|
if !e.matches(gtk::DialogError::Dismissed) {
|
||||||
|
|
@ -491,7 +490,7 @@ impl LpWindow {
|
||||||
|
|
||||||
async fn trash(&self) {
|
async fn trash(&self) {
|
||||||
let image_view = self.image_view();
|
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; };
|
else { log::error!("No file to trash"); return; };
|
||||||
|
|
||||||
let result = file.trash_future(glib::Priority::default()).await;
|
let result = file.trash_future(glib::Priority::default()).await;
|
||||||
|
|
@ -507,7 +506,7 @@ impl LpWindow {
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
let result = crate::util::untrash(&path).await;
|
let result = crate::util::untrash(&path).await;
|
||||||
match result {
|
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) => {
|
Err(err) => {
|
||||||
log::error!("Failed to untrash {path:?}: {err}");
|
log::error!("Failed to untrash {path:?}: {err}");
|
||||||
win.show_toast(
|
win.show_toast(
|
||||||
|
|
@ -583,11 +582,11 @@ impl LpWindow {
|
||||||
imp.toast_overlay.add_toast(toast);
|
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();
|
let imp = self.imp();
|
||||||
|
|
||||||
log::debug!("Loading file: {path:?}");
|
log::debug!("Loading file: {}", file.uri());
|
||||||
imp.image_view.set_image_from_path(path);
|
imp.image_view.set_image_from_file(file);
|
||||||
self.set_actions_enabled(true);
|
self.set_actions_enabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue