fileModel: Make file info queries async
We are not entirely sure if looking up the display name might do IO for google drives.
This commit is contained in:
parent
400c7ac2c4
commit
9156533fea
3 changed files with 93 additions and 92 deletions
1
news.d/48.alpha/fixed-sort-order-not-following-nautilus
Normal file
1
news.d/48.alpha/fixed-sort-order-not-following-nautilus
Normal file
|
|
@ -0,0 +1 @@
|
|||
The sort order is now also following the one found in Files (Nautilus) if the file name contains non-ascii characters.
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) 2022-2024 Sophie Herold
|
||||
// Copyright (c) 2022 Christopher Davis
|
||||
// Copyright (c) 2023 Julian Hofer
|
||||
// Copyright (c) 2023 Gage Berz
|
||||
// Copyright (c) 2024 Not Committed Yet
|
||||
//
|
||||
// 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
|
||||
|
|
@ -36,27 +34,25 @@ use crate::util::gettext::*;
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Entry {
|
||||
/// Determines the sort order
|
||||
sort: VecDeque<glib::FilenameCollationKey>,
|
||||
file: gio::File,
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
//async
|
||||
fn new(file: gio::File) -> Self {
|
||||
async fn new(file: gio::File) -> Self {
|
||||
let mut sort = VecDeque::new();
|
||||
let mut frac_file = file.clone();
|
||||
|
||||
// Determine sort order for all path/uri segments one by one
|
||||
loop {
|
||||
match frac_file
|
||||
.query_info
|
||||
//_future
|
||||
(
|
||||
.query_info_future(
|
||||
gio::FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
|
||||
gio::FileQueryInfoFlags::NONE,
|
||||
//glib::Priority::LOW,
|
||||
gio::Cancellable::NONE
|
||||
glib::Priority::LOW,
|
||||
)
|
||||
//.await
|
||||
.await
|
||||
{
|
||||
Ok(parent) => {
|
||||
let key = glib::FilenameCollationKey::from(parent.display_name());
|
||||
|
|
@ -123,20 +119,20 @@ impl Default for LpFileModel {
|
|||
|
||||
impl LpFileModel {
|
||||
/// Create with a single element
|
||||
pub fn from_file(file: gio::File) -> Self {
|
||||
Self::from_files(vec![file])
|
||||
pub async fn from_file(file: gio::File) -> Self {
|
||||
Self::from_files(vec![file]).await
|
||||
}
|
||||
|
||||
pub fn from_files(files: Vec<gio::File>) -> Self {
|
||||
pub async fn from_files(files: Vec<gio::File>) -> Self {
|
||||
let model = Self::default();
|
||||
|
||||
{
|
||||
let mut vec = model.imp().files.borrow_mut();
|
||||
let mut stored_files: IndexMap<GString, Entry> = IndexMap::new();
|
||||
for file in files {
|
||||
vec.insert(file.uri(), Entry::new(file));
|
||||
}
|
||||
stored_files.insert(file.uri(), Entry::new(file).await);
|
||||
}
|
||||
|
||||
*model.imp().files.borrow_mut() = stored_files;
|
||||
|
||||
model
|
||||
}
|
||||
|
||||
|
|
@ -164,11 +160,10 @@ impl LpFileModel {
|
|||
}
|
||||
}
|
||||
|
||||
let new_files_result = gio::spawn_blocking(move || {
|
||||
let mut files = IndexMap::new();
|
||||
let mut new_files = IndexMap::new();
|
||||
|
||||
let enumerator = directory
|
||||
.enumerate_children(
|
||||
.enumerate_children_future(
|
||||
&format!(
|
||||
"{},{},{},{}",
|
||||
gio::FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
|
|
@ -177,12 +172,17 @@ impl LpFileModel {
|
|||
gio::FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
|
||||
),
|
||||
gio::FileQueryInfoFlags::NONE,
|
||||
gio::Cancellable::NONE,
|
||||
glib::Priority::LOW,
|
||||
)
|
||||
.await
|
||||
.context(gettext("Could not list other files in directory."))?;
|
||||
|
||||
enumerator.for_each(|info| {
|
||||
if let Ok(info) = info {
|
||||
for info in enumerator {
|
||||
match info {
|
||||
Err(err) => {
|
||||
log::warn!("Unreadable entry in directory: {err}");
|
||||
}
|
||||
Ok(info) => {
|
||||
// GVfs smb does not provide a CONTENT_TYPE if the content type is ambiguous.
|
||||
// This happens for png/apng. Since we only need to know if something is
|
||||
// probably an image, we can use the FAST_CONTENT_TYPE in these cases.
|
||||
|
|
@ -201,22 +201,12 @@ impl LpFileModel {
|
|||
// images. Usually by inspecting the magic bytes.
|
||||
if content_type.starts_with("image/") && !info.is_hidden() {
|
||||
let file = directory.child(info.name());
|
||||
files.insert(file.uri(), Entry::new(file));
|
||||
new_files.insert(file.uri(), Entry::new(file).await);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok::<_, anyhow::Error>(files)
|
||||
})
|
||||
.await;
|
||||
|
||||
let Ok(new_files) = new_files_result else {
|
||||
log::debug!("Thread listing directory canceled.");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut new_files = new_files?;
|
||||
|
||||
{
|
||||
// Here we use a nested scope so that the mutable borrow only lasts as long as
|
||||
|
|
@ -346,13 +336,32 @@ impl LpFileModel {
|
|||
});
|
||||
}
|
||||
|
||||
/// Insert a file and signal changes
|
||||
///
|
||||
/// Setting `changed` to true will always trigger a `notify::changed``
|
||||
/// notification. Otherwise, a change signal is only sent if no file with
|
||||
/// the same URI existed before.
|
||||
pub fn insert(&self, file: gio::File, mut changed: bool) {
|
||||
let obj = self.clone();
|
||||
glib::spawn_future_local(async move {
|
||||
let entry = Entry::new(file.clone()).await;
|
||||
let mut files = obj.imp().files.borrow_mut();
|
||||
changed |= files.insert(file.uri(), entry).is_none();
|
||||
if changed {
|
||||
Self::sort(&mut files);
|
||||
drop(files);
|
||||
obj.emit_by_name::<()>("changed", &[]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn file_monitor_cb(
|
||||
&self,
|
||||
event: gio::FileMonitorEvent,
|
||||
file_a: &gio::File,
|
||||
file_b: Option<&gio::File>,
|
||||
) {
|
||||
let changed = match event {
|
||||
match event {
|
||||
gio::FileMonitorEvent::Created
|
||||
| gio::FileMonitorEvent::MovedIn
|
||||
// Changing file content could theoretically make it an image
|
||||
|
|
@ -360,40 +369,25 @@ impl LpFileModel {
|
|||
| gio::FileMonitorEvent::ChangesDoneHint
|
||||
if Self::is_image_file(file_a) =>
|
||||
{
|
||||
let mut files = self.imp().files.borrow_mut();
|
||||
let changed = files.insert(file_a.uri(),Entry::new(file_a.clone())).is_none();
|
||||
if changed {
|
||||
Self::sort(&mut files);
|
||||
}
|
||||
changed
|
||||
self.insert(file_a.clone(), false);
|
||||
}
|
||||
gio::FileMonitorEvent::Deleted | gio::FileMonitorEvent::MovedOut | gio::FileMonitorEvent::Unmounted => {
|
||||
let mut files = self.imp().files.borrow_mut();
|
||||
files.shift_remove(&file_a.uri()).is_some()
|
||||
}
|
||||
gio::FileMonitorEvent::Renamed => {
|
||||
if let Some(file_b) = file_b {
|
||||
let mut changed = false;
|
||||
{
|
||||
let mut files = self.imp().files.borrow_mut();
|
||||
changed |= files.shift_remove(&file_a.uri()).is_some();
|
||||
if Self::is_image_file(file_b) {
|
||||
changed |= files.insert(file_b.uri(), Entry::new(file_b.clone())).is_none();
|
||||
if changed {
|
||||
Self::sort(&mut files);
|
||||
}
|
||||
}
|
||||
}
|
||||
changed
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if changed {
|
||||
let removed = self.imp().files.borrow_mut().shift_remove(&file_a.uri()).is_some();
|
||||
if removed {
|
||||
self.emit_by_name::<()>("changed", &[]);
|
||||
}
|
||||
}
|
||||
gio::FileMonitorEvent::Renamed => {
|
||||
if let Some(file_b) = file_b {
|
||||
{
|
||||
let changed = self.imp().files.borrow_mut().shift_remove(&file_a.uri()).is_some();
|
||||
if Self::is_image_file(file_b) {
|
||||
self.insert(file_b.clone(), changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -439,16 +439,22 @@ impl LpImageView {
|
|||
}
|
||||
|
||||
fn load_files(&self, files: Vec<gio::File>) {
|
||||
let sliding_view = self.sliding_view().editor();
|
||||
|
||||
if let Some(first) = files.first().cloned() {
|
||||
let model = LpFileModel::from_files(files);
|
||||
self.set_model(model);
|
||||
let page = self.new_image_page(&first);
|
||||
glib::spawn_future_local(glib::clone!(
|
||||
#[strong(rename_to=obj)]
|
||||
self,
|
||||
async move {
|
||||
let sliding_view = obj.sliding_view().editor();
|
||||
|
||||
let model = LpFileModel::from_files(files).await;
|
||||
obj.set_model(model);
|
||||
let page = obj.new_image_page(&first);
|
||||
|
||||
sliding_view.clear_lazy();
|
||||
sliding_view.append(&page);
|
||||
self.update_sliding_view(&first);
|
||||
obj.update_sliding_view(&first);
|
||||
}
|
||||
));
|
||||
} else {
|
||||
log::error!("File list was empty");
|
||||
}
|
||||
|
|
@ -474,18 +480,18 @@ impl LpImageView {
|
|||
return;
|
||||
}
|
||||
|
||||
let model = LpFileModel::from_file(file.clone());
|
||||
self.set_model(model);
|
||||
glib::spawn_future_local(glib::clone!(
|
||||
#[strong(rename_to = obj)]
|
||||
self,
|
||||
async move {
|
||||
let model = LpFileModel::from_file(file.clone()).await;
|
||||
obj.set_model(model);
|
||||
log::debug!("New model created");
|
||||
|
||||
self.update_sliding_view(&file);
|
||||
obj.update_sliding_view(&file);
|
||||
|
||||
// List other files in directory
|
||||
if let Some(directory) = directory {
|
||||
glib::spawn_future_local(glib::clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self,
|
||||
async move {
|
||||
if let Err(err) = obj.model().load_directory(directory.clone()).await {
|
||||
log::warn!("Failed to load directory: {}", err.root_cause());
|
||||
obj.activate_action(
|
||||
|
|
@ -504,8 +510,8 @@ impl LpImageView {
|
|||
|
||||
obj.update_sliding_view(&file);
|
||||
}
|
||||
));
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
/// Move forward or backwards
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue