chore: clippy
This commit is contained in:
parent
339e4c104e
commit
1e25e7dd69
11 changed files with 938 additions and 981 deletions
748
src/app.rs
748
src/app.rs
|
|
@ -1230,11 +1230,11 @@ impl App {
|
|||
};
|
||||
for item in items {
|
||||
if item.selected {
|
||||
if let Some(path) = item.path_opt() {
|
||||
if op_sel.selected.contains(path) || op_sel.ignored.contains(path) {
|
||||
// Ignore if path in selected or ignored paths
|
||||
continue;
|
||||
}
|
||||
if let Some(path) = item.path_opt()
|
||||
&& (op_sel.selected.contains(path) || op_sel.ignored.contains(path))
|
||||
{
|
||||
// Ignore if path in selected or ignored paths
|
||||
continue;
|
||||
}
|
||||
|
||||
// Return if there is a previous selection not matching
|
||||
|
|
@ -1891,50 +1891,47 @@ impl App {
|
|||
children.push(item.preview_view(Some(&self.mime_app_cache), military_time));
|
||||
}
|
||||
PreviewKind::Location(location) => {
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
|
||||
if let Some(items) = tab.items_opt() {
|
||||
for item in items {
|
||||
if item.location_opt.as_ref() == Some(location) {
|
||||
children.push(
|
||||
item.preview_view(Some(&self.mime_app_cache), military_time),
|
||||
);
|
||||
// Only show one property view to avoid issues like hangs when generating
|
||||
// preview images on thousands of files
|
||||
break;
|
||||
}
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity)
|
||||
&& let Some(items) = tab.items_opt()
|
||||
{
|
||||
for item in items {
|
||||
if item.location_opt.as_ref() == Some(location) {
|
||||
children
|
||||
.push(item.preview_view(Some(&self.mime_app_cache), military_time));
|
||||
// Only show one property view to avoid issues like hangs when generating
|
||||
// preview images on thousands of files
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PreviewKind::Selected => {
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
|
||||
if let Some(items) = tab.items_opt() {
|
||||
let preview_opt = {
|
||||
let mut selected = items.iter().filter(|item| item.selected);
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity)
|
||||
&& let Some(items) = tab.items_opt()
|
||||
{
|
||||
let preview_opt = {
|
||||
let mut selected = items.iter().filter(|item| item.selected);
|
||||
|
||||
match (selected.next(), selected.next()) {
|
||||
// At least two selected items
|
||||
(Some(_), Some(_)) => Some(tab.multi_preview_view()),
|
||||
// Exactly one selected item
|
||||
(Some(item), None) => Some(
|
||||
item.preview_view(Some(&self.mime_app_cache), military_time),
|
||||
),
|
||||
// No selected items
|
||||
_ => None,
|
||||
match (selected.next(), selected.next()) {
|
||||
// At least two selected items
|
||||
(Some(_), Some(_)) => Some(tab.multi_preview_view()),
|
||||
// Exactly one selected item
|
||||
(Some(item), None) => {
|
||||
Some(item.preview_view(Some(&self.mime_app_cache), military_time))
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(preview) = preview_opt {
|
||||
children.push(preview);
|
||||
// No selected items
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
if children.is_empty() {
|
||||
if let Some(item) = &tab.parent_item_opt {
|
||||
children.push(
|
||||
item.preview_view(Some(&self.mime_app_cache), military_time),
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(preview) = preview_opt {
|
||||
children.push(preview);
|
||||
}
|
||||
|
||||
if children.is_empty()
|
||||
&& let Some(item) = &tab.parent_item_opt
|
||||
{
|
||||
children.push(item.preview_view(Some(&self.mime_app_cache), military_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2062,11 +2059,11 @@ impl App {
|
|||
.map(|favorite| {
|
||||
if let Favorite::Path(path) = favorite {
|
||||
for (from, to) in path_changes.iter().map(|(f, t)| (f.as_ref(), t.as_ref())) {
|
||||
if path.starts_with(from) {
|
||||
if let Ok(relative) = path.strip_prefix(from) {
|
||||
favorites_changed = true;
|
||||
return Favorite::from_path(to.join(relative));
|
||||
}
|
||||
if path.starts_with(from)
|
||||
&& let Ok(relative) = path.strip_prefix(from)
|
||||
{
|
||||
favorites_changed = true;
|
||||
return Favorite::from_path(to.join(relative));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2235,17 +2232,16 @@ impl Application for App {
|
|||
let mut commands = vec![app.update_config()];
|
||||
|
||||
for location in flags.locations {
|
||||
if let Some(path) = location.path_opt() {
|
||||
if path.is_file() {
|
||||
if let Some(parent) = path.parent() {
|
||||
commands.push(app.open_tab(
|
||||
Location::Path(parent.to_path_buf()),
|
||||
true,
|
||||
Some(vec![path.clone()]),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(path) = location.path_opt()
|
||||
&& path.is_file()
|
||||
&& let Some(parent) = path.parent()
|
||||
{
|
||||
commands.push(app.open_tab(
|
||||
Location::Path(parent.to_path_buf()),
|
||||
true,
|
||||
Some(vec![path.clone()]),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
commands.push(app.open_tab(location, true, None));
|
||||
}
|
||||
|
|
@ -2405,14 +2401,13 @@ impl Application for App {
|
|||
// TODO do we need to choose the correct mounter?
|
||||
self.mounter_items.keys().copied().next()
|
||||
})
|
||||
&& let Some(mounter) = MOUNTERS.get(&key)
|
||||
{
|
||||
if let Some(mounter) = MOUNTERS.get(&key) {
|
||||
return mounter.network_drive(uri.clone()).map(move |()| {
|
||||
cosmic::Action::App(Message::NetworkDriveOpenEntityAfterMount {
|
||||
entity,
|
||||
})
|
||||
});
|
||||
}
|
||||
return mounter.network_drive(uri.clone()).map(move |()| {
|
||||
cosmic::Action::App(Message::NetworkDriveOpenEntityAfterMount {
|
||||
entity,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
log::warn!(
|
||||
|
|
@ -2468,12 +2463,12 @@ impl Application for App {
|
|||
return self.update(message);
|
||||
}
|
||||
}
|
||||
if let Some(data) = self.nav_model.data::<MounterData>(entity) {
|
||||
if let Some(mounter) = MOUNTERS.get(&data.0) {
|
||||
return mounter
|
||||
.mount(data.1.clone())
|
||||
.map(|()| cosmic::action::none());
|
||||
}
|
||||
if let Some(data) = self.nav_model.data::<MounterData>(entity)
|
||||
&& let Some(mounter) = MOUNTERS.get(&data.0)
|
||||
{
|
||||
return mounter
|
||||
.mount(data.1.clone())
|
||||
.map(|()| cosmic::action::none());
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -2505,11 +2500,11 @@ impl Application for App {
|
|||
}
|
||||
|
||||
// Close gallery mode if open
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
if tab.gallery {
|
||||
tab.gallery = false;
|
||||
return Task::none();
|
||||
}
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||
&& tab.gallery
|
||||
{
|
||||
tab.gallery = false;
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
// Close menus and context panes in order per message
|
||||
|
|
@ -2635,22 +2630,22 @@ impl Application for App {
|
|||
}
|
||||
Message::Compress(entity_opt) => {
|
||||
let paths: Box<[_]> = self.selected_paths(entity_opt).collect();
|
||||
if let Some(current_path) = paths.first() {
|
||||
if let Some(destination) = current_path.parent().zip(current_path.file_stem()) {
|
||||
let to = destination.0.to_path_buf();
|
||||
let name = destination.1.to_str().unwrap_or_default().to_string();
|
||||
let archive_type = ArchiveType::default();
|
||||
return self.push_dialog(
|
||||
DialogPage::Compress {
|
||||
paths,
|
||||
to,
|
||||
name,
|
||||
archive_type,
|
||||
password: None,
|
||||
},
|
||||
Some(self.dialog_text_input.clone()),
|
||||
);
|
||||
}
|
||||
if let Some(current_path) = paths.first()
|
||||
&& let Some(destination) = current_path.parent().zip(current_path.file_stem())
|
||||
{
|
||||
let to = destination.0.to_path_buf();
|
||||
let name = destination.1.to_str().unwrap_or_default().to_string();
|
||||
let archive_type = ArchiveType::default();
|
||||
return self.push_dialog(
|
||||
DialogPage::Compress {
|
||||
paths,
|
||||
to,
|
||||
name,
|
||||
archive_type,
|
||||
password: None,
|
||||
},
|
||||
Some(self.dialog_text_input.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
Message::Config(config) => {
|
||||
|
|
@ -2664,10 +2659,10 @@ impl Application for App {
|
|||
}
|
||||
}
|
||||
Message::Copy(entity_opt) => {
|
||||
if let Some(entity) = entity_opt {
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
tab.refresh_cut(&[]);
|
||||
}
|
||||
if let Some(entity) = entity_opt
|
||||
&& let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||
{
|
||||
tab.refresh_cut(&[]);
|
||||
}
|
||||
let paths = self.selected_paths(entity_opt);
|
||||
let contents = ClipboardCopy::new(ClipboardKind::Copy, paths);
|
||||
|
|
@ -2975,22 +2970,21 @@ impl Application for App {
|
|||
DialogResult::Cancel => {}
|
||||
DialogResult::Open(selected_paths) => {
|
||||
let mut archive_paths = None;
|
||||
if let Some(file_dialog) = &self.file_dialog_opt {
|
||||
if let Some(window) = self.windows.remove(&file_dialog.window_id()) {
|
||||
if let WindowKind::FileDialog(paths) = window.kind {
|
||||
archive_paths = paths;
|
||||
}
|
||||
}
|
||||
if let Some(file_dialog) = &self.file_dialog_opt
|
||||
&& let Some(window) = self.windows.remove(&file_dialog.window_id())
|
||||
&& let WindowKind::FileDialog(paths) = window.kind
|
||||
{
|
||||
archive_paths = paths;
|
||||
}
|
||||
if let Some(archive_paths) = archive_paths {
|
||||
if !selected_paths.is_empty() {
|
||||
self.file_dialog_opt = None;
|
||||
return self.operation(Operation::Extract {
|
||||
paths: archive_paths,
|
||||
to: selected_paths[0].clone(),
|
||||
password: None,
|
||||
});
|
||||
}
|
||||
if let Some(archive_paths) = archive_paths
|
||||
&& !selected_paths.is_empty()
|
||||
{
|
||||
self.file_dialog_opt = None;
|
||||
return self.operation(Operation::Extract {
|
||||
paths: archive_paths,
|
||||
to: selected_paths[0].clone(),
|
||||
password: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3020,56 +3014,53 @@ impl Application for App {
|
|||
&& !modifiers.control()
|
||||
&& !modifiers.alt()
|
||||
&& matches!(key, Key::Character(_))
|
||||
&& let Some(text) = text
|
||||
{
|
||||
if let Some(text) = text {
|
||||
match self.config.type_to_search {
|
||||
TypeToSearch::Recursive => {
|
||||
let mut term =
|
||||
self.search_get().unwrap_or_default().to_string();
|
||||
term.push_str(&text);
|
||||
return self.search_set_active(Some(term));
|
||||
}
|
||||
TypeToSearch::EnterPath => {
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
let location = tab
|
||||
.edit_location
|
||||
.as_ref()
|
||||
.map_or_else(|| &tab.location, |x| &x.location);
|
||||
// Try to add text to end of location
|
||||
if let Location::Network(uri, ..) = location {
|
||||
let mut uri_string = uri.clone();
|
||||
uri_string.push_str(&text);
|
||||
tab.edit_location =
|
||||
Some(location.with_uri(uri_string).into());
|
||||
} else if let Some(path) = location.path_opt() {
|
||||
let mut path_string =
|
||||
path.to_string_lossy().into_owned();
|
||||
path_string.push_str(&text);
|
||||
tab.edit_location =
|
||||
Some(location.with_path(path_string.into()).into());
|
||||
}
|
||||
match self.config.type_to_search {
|
||||
TypeToSearch::Recursive => {
|
||||
let mut term = self.search_get().unwrap_or_default().to_string();
|
||||
term.push_str(&text);
|
||||
return self.search_set_active(Some(term));
|
||||
}
|
||||
TypeToSearch::EnterPath => {
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
let location = tab
|
||||
.edit_location
|
||||
.as_ref()
|
||||
.map_or_else(|| &tab.location, |x| &x.location);
|
||||
// Try to add text to end of location
|
||||
if let Location::Network(uri, ..) = location {
|
||||
let mut uri_string = uri.clone();
|
||||
uri_string.push_str(&text);
|
||||
tab.edit_location =
|
||||
Some(location.with_uri(uri_string).into());
|
||||
} else if let Some(path) = location.path_opt() {
|
||||
let mut path_string = path.to_string_lossy().into_owned();
|
||||
path_string.push_str(&text);
|
||||
tab.edit_location =
|
||||
Some(location.with_path(path_string.into()).into());
|
||||
}
|
||||
}
|
||||
TypeToSearch::SelectByPrefix => {
|
||||
// Reset buffer if timeout elapsed
|
||||
if let Some(last_key) = self.type_select_last_key {
|
||||
if last_key.elapsed() >= tab::TYPE_SELECT_TIMEOUT {
|
||||
self.type_select_prefix.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
TypeToSearch::SelectByPrefix => {
|
||||
// Reset buffer if timeout elapsed
|
||||
if let Some(last_key) = self.type_select_last_key
|
||||
&& last_key.elapsed() >= tab::TYPE_SELECT_TIMEOUT
|
||||
{
|
||||
self.type_select_prefix.clear();
|
||||
}
|
||||
|
||||
// Accumulate character and select
|
||||
self.type_select_prefix.push_str(&text.to_lowercase());
|
||||
self.type_select_last_key = Some(Instant::now());
|
||||
// Accumulate character and select
|
||||
self.type_select_prefix.push_str(&text.to_lowercase());
|
||||
self.type_select_last_key = Some(Instant::now());
|
||||
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
tab.select_by_prefix(&self.type_select_prefix);
|
||||
if let Some(offset) = tab.select_focus_scroll() {
|
||||
return scrollable::scroll_to(
|
||||
tab.scrollable_id.clone(),
|
||||
offset,
|
||||
);
|
||||
}
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
tab.select_by_prefix(&self.type_select_prefix);
|
||||
if let Some(offset) = tab.select_focus_scroll() {
|
||||
return scrollable::scroll_to(
|
||||
tab.scrollable_id.clone(),
|
||||
offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3106,21 +3097,22 @@ impl Application for App {
|
|||
let mut unmounted = Vec::new();
|
||||
if let Some(old_items) = self.mounter_items.get(&mounter_key) {
|
||||
for old_item in old_items {
|
||||
if let Some(old_path) = old_item.path() {
|
||||
if old_item.is_mounted() {
|
||||
let mut still_mounted = false;
|
||||
for item in &mounter_items {
|
||||
if let Some(path) = item.path() {
|
||||
if path == old_path && item.is_mounted() {
|
||||
still_mounted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !still_mounted {
|
||||
unmounted.push(old_path);
|
||||
if let Some(old_path) = old_item.path()
|
||||
&& old_item.is_mounted()
|
||||
{
|
||||
let mut still_mounted = false;
|
||||
for item in &mounter_items {
|
||||
if let Some(path) = item.path()
|
||||
&& path == old_path
|
||||
&& item.is_mounted()
|
||||
{
|
||||
still_mounted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !still_mounted {
|
||||
unmounted.push(old_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3254,17 +3246,17 @@ impl Application for App {
|
|||
}
|
||||
Message::NewItem(entity_opt, dir) => {
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
if let Some(path) = tab.location.path_opt() {
|
||||
return Task::batch([
|
||||
self.dialog_pages.push_back(DialogPage::NewItem {
|
||||
parent: path.clone(),
|
||||
name: String::new(),
|
||||
dir,
|
||||
}),
|
||||
widget::text_input::focus(self.dialog_text_input.clone()),
|
||||
]);
|
||||
}
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||
&& let Some(path) = tab.location.path_opt()
|
||||
{
|
||||
return Task::batch([
|
||||
self.dialog_pages.push_back(DialogPage::NewItem {
|
||||
parent: path.clone(),
|
||||
name: String::new(),
|
||||
dir,
|
||||
}),
|
||||
widget::text_input::focus(self.dialog_text_input.clone()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "notify")]
|
||||
|
|
@ -3277,57 +3269,57 @@ impl Application for App {
|
|||
let mut needs_reload = Vec::new();
|
||||
let entities: Box<[_]> = self.tab_model.iter().collect();
|
||||
for entity in entities {
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
if let Some(path) = tab.location.path_opt() {
|
||||
let mut contains_change = false;
|
||||
for event in &events {
|
||||
for event_path in &event.paths {
|
||||
if event_path.starts_with(path) {
|
||||
if let notify::EventKind::Modify(
|
||||
notify::event::ModifyKind::Metadata(_)
|
||||
| notify::event::ModifyKind::Data(_),
|
||||
) = event.kind
|
||||
{
|
||||
// If metadata or data changed, find the matching item and reload it
|
||||
//TODO: this could be further optimized by looking at what exactly changed
|
||||
if let Some(items) = &mut tab.items_opt {
|
||||
for item in items.iter_mut() {
|
||||
if item.path_opt() == Some(event_path) {
|
||||
//TODO: reload more, like mime types?
|
||||
match fs::metadata(event_path) {
|
||||
Ok(new_metadata) => {
|
||||
if let ItemMetadata::Path {
|
||||
metadata,
|
||||
..
|
||||
} = &mut item.metadata
|
||||
{
|
||||
*metadata = new_metadata;
|
||||
}
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"failed to reload metadata for {}: {}",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||
&& let Some(path) = tab.location.path_opt()
|
||||
{
|
||||
let mut contains_change = false;
|
||||
for event in &events {
|
||||
for event_path in &event.paths {
|
||||
if event_path.starts_with(path) {
|
||||
if let notify::EventKind::Modify(
|
||||
notify::event::ModifyKind::Metadata(_)
|
||||
| notify::event::ModifyKind::Data(_),
|
||||
) = event.kind
|
||||
{
|
||||
// If metadata or data changed, find the matching item and reload it
|
||||
//TODO: this could be further optimized by looking at what exactly changed
|
||||
if let Some(items) = &mut tab.items_opt {
|
||||
for item in items.iter_mut() {
|
||||
if item.path_opt() == Some(event_path) {
|
||||
//TODO: reload more, like mime types?
|
||||
match fs::metadata(event_path) {
|
||||
Ok(new_metadata) => {
|
||||
if let ItemMetadata::Path {
|
||||
metadata,
|
||||
..
|
||||
} = &mut item.metadata
|
||||
{
|
||||
*metadata = new_metadata;
|
||||
}
|
||||
}
|
||||
//TODO item.thumbnail_opt =
|
||||
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"failed to reload metadata for {}: {}",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
//TODO item.thumbnail_opt =
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Any other events reload the whole tab
|
||||
contains_change = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Any other events reload the whole tab
|
||||
contains_change = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if contains_change {
|
||||
needs_reload.push((entity, tab.location.clone()));
|
||||
}
|
||||
}
|
||||
if contains_change {
|
||||
needs_reload.push((entity, tab.location.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3351,20 +3343,21 @@ impl Application for App {
|
|||
if let Some(terminal) = self.mime_app_cache.terminal() {
|
||||
let mut paths = Box::from([]);
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
if let Some(path) = tab.location.path_opt() {
|
||||
if let Some(items) = tab.items_opt() {
|
||||
paths =
|
||||
items
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
if item.selected { item.path_opt() } else { None }
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
if paths.is_empty() {
|
||||
paths = Box::from([path]);
|
||||
}
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||
&& let Some(path) = tab.location.path_opt()
|
||||
{
|
||||
if let Some(items) = tab.items_opt() {
|
||||
paths = items
|
||||
.iter()
|
||||
.filter_map(
|
||||
|item| {
|
||||
if item.selected { item.path_opt() } else { None }
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
}
|
||||
if paths.is_empty() {
|
||||
paths = Box::from([path]);
|
||||
}
|
||||
}
|
||||
for path in paths {
|
||||
|
|
@ -3454,30 +3447,30 @@ impl Application for App {
|
|||
},
|
||||
Message::OpenWithDialog(entity_opt) => {
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
|
||||
if let Some(items) = tab.items_opt() {
|
||||
for item in items {
|
||||
if !item.selected {
|
||||
continue;
|
||||
}
|
||||
let Some(path) = item.path_opt() else {
|
||||
continue;
|
||||
};
|
||||
return self.push_dialog(
|
||||
DialogPage::OpenWith {
|
||||
path: path.clone(),
|
||||
mime: item.mime.clone(),
|
||||
selected: 0,
|
||||
store_opt: "x-scheme-handler/mime"
|
||||
.parse::<mime_guess::Mime>()
|
||||
.ok()
|
||||
.and_then(|mime| {
|
||||
self.mime_app_cache.get(&mime).first().cloned()
|
||||
}),
|
||||
},
|
||||
Some(CONFIRM_OPEN_WITH_BUTTON_ID.clone()),
|
||||
);
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity)
|
||||
&& let Some(items) = tab.items_opt()
|
||||
{
|
||||
for item in items {
|
||||
if !item.selected {
|
||||
continue;
|
||||
}
|
||||
let Some(path) = item.path_opt() else {
|
||||
continue;
|
||||
};
|
||||
return self.push_dialog(
|
||||
DialogPage::OpenWith {
|
||||
path: path.clone(),
|
||||
mime: item.mime.clone(),
|
||||
selected: 0,
|
||||
store_opt: "x-scheme-handler/mime"
|
||||
.parse::<mime_guess::Mime>()
|
||||
.ok()
|
||||
.and_then(|mime| {
|
||||
self.mime_app_cache.get(&mime).first().cloned()
|
||||
}),
|
||||
},
|
||||
Some(CONFIRM_OPEN_WITH_BUTTON_ID.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3488,19 +3481,18 @@ impl Application for App {
|
|||
}
|
||||
Message::Paste(entity_opt) => {
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
if let Some(path) = tab.location.path_opt() {
|
||||
let to = path.clone();
|
||||
return clipboard::read_data::<ClipboardPaste>().map(move |contents_opt| {
|
||||
match contents_opt {
|
||||
Some(contents) => cosmic::action::app(Message::PasteContents(
|
||||
to.clone(),
|
||||
contents,
|
||||
)),
|
||||
None => cosmic::action::none(),
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||
&& let Some(path) = tab.location.path_opt()
|
||||
{
|
||||
let to = path.clone();
|
||||
return clipboard::read_data::<ClipboardPaste>().map(move |contents_opt| {
|
||||
match contents_opt {
|
||||
Some(contents) => {
|
||||
cosmic::action::app(Message::PasteContents(to.clone(), contents))
|
||||
}
|
||||
});
|
||||
}
|
||||
None => cosmic::action::none(),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Message::PasteContents(to, mut contents) => {
|
||||
|
|
@ -3733,38 +3725,38 @@ impl Application for App {
|
|||
}
|
||||
Message::Rename(entity_opt) => {
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
if let Some(items) = tab.items_opt() {
|
||||
let selected: Box<[_]> = items
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
if item.selected {
|
||||
item.path_opt().cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||
&& let Some(items) = tab.items_opt()
|
||||
{
|
||||
let selected: Box<[_]> = items
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
if item.selected {
|
||||
item.path_opt().cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if !selected.is_empty() {
|
||||
//TODO: batch rename
|
||||
let tasks = selected
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
let parent = path.parent()?.to_path_buf();
|
||||
let name = path.file_name()?.to_str()?.to_string();
|
||||
let dir = path.is_dir();
|
||||
Some(self.dialog_pages.push_back(DialogPage::RenameItem {
|
||||
from: path,
|
||||
parent,
|
||||
name,
|
||||
dir,
|
||||
}))
|
||||
})
|
||||
.collect();
|
||||
if !selected.is_empty() {
|
||||
//TODO: batch rename
|
||||
let tasks = selected
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
let parent = path.parent()?.to_path_buf();
|
||||
let name = path.file_name()?.to_str()?.to_string();
|
||||
let dir = path.is_dir();
|
||||
Some(self.dialog_pages.push_back(DialogPage::RenameItem {
|
||||
from: path,
|
||||
parent,
|
||||
name,
|
||||
dir,
|
||||
}))
|
||||
})
|
||||
.chain(std::iter::once(widget::text_input::focus(
|
||||
self.dialog_text_input.clone(),
|
||||
)));
|
||||
return Task::batch(tasks);
|
||||
}
|
||||
.chain(std::iter::once(widget::text_input::focus(
|
||||
self.dialog_text_input.clone(),
|
||||
)));
|
||||
return Task::batch(tasks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3787,15 +3779,15 @@ impl Application for App {
|
|||
Message::RestoreFromTrash(entity_opt) => {
|
||||
let mut trash_items = Vec::new();
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
if let Some(items) = tab.items_opt() {
|
||||
for item in items {
|
||||
if item.selected {
|
||||
if let ItemMetadata::Trash { entry, .. } = &item.metadata {
|
||||
trash_items.push(entry.clone());
|
||||
} else {
|
||||
//TODO: error on trying to restore non-trash file?
|
||||
}
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||
&& let Some(items) = tab.items_opt()
|
||||
{
|
||||
for item in items {
|
||||
if item.selected {
|
||||
if let ItemMetadata::Trash { entry, .. } = &item.metadata {
|
||||
trash_items.push(entry.clone());
|
||||
} else {
|
||||
//TODO: error on trying to restore non-trash file?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3840,13 +3832,13 @@ impl Application for App {
|
|||
|
||||
// Close old context menu
|
||||
let active = self.tab_model.active();
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(active) {
|
||||
if tab.context_menu.is_some() {
|
||||
tasks.push(self.update(Message::TabMessage(
|
||||
Some(active),
|
||||
tab::Message::ContextMenu(None, None),
|
||||
)));
|
||||
}
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(active)
|
||||
&& tab.context_menu.is_some()
|
||||
{
|
||||
tasks.push(self.update(Message::TabMessage(
|
||||
Some(active),
|
||||
tab::Message::ContextMenu(None, None),
|
||||
)));
|
||||
}
|
||||
|
||||
// Activate new tab
|
||||
|
|
@ -4042,10 +4034,10 @@ impl Application for App {
|
|||
// Destroy previous popup
|
||||
let mut window_ids = Vec::new();
|
||||
for (window_id, window) in &self.windows {
|
||||
if let WindowKind::ContextMenu(e, _) = &window.kind {
|
||||
if *e == entity {
|
||||
window_ids.push(*window_id);
|
||||
}
|
||||
if let WindowKind::ContextMenu(e, _) = &window.kind
|
||||
&& *e == entity
|
||||
{
|
||||
window_ids.push(*window_id);
|
||||
}
|
||||
}
|
||||
for window_id in window_ids {
|
||||
|
|
@ -4453,12 +4445,12 @@ impl Application for App {
|
|||
}
|
||||
}
|
||||
Message::NavBarClose(entity) => {
|
||||
if let Some(data) = self.nav_model.data::<MounterData>(entity) {
|
||||
if let Some(mounter) = MOUNTERS.get(&data.0) {
|
||||
return mounter
|
||||
.unmount(data.1.clone())
|
||||
.map(|()| cosmic::action::none());
|
||||
}
|
||||
if let Some(data) = self.nav_model.data::<MounterData>(entity)
|
||||
&& let Some(mounter) = MOUNTERS.get(&data.0)
|
||||
{
|
||||
return mounter
|
||||
.unmount(data.1.clone())
|
||||
.map(|()| cosmic::action::none());
|
||||
}
|
||||
}
|
||||
Message::NavBarContext(entity) => {
|
||||
|
|
@ -4755,15 +4747,14 @@ impl Application for App {
|
|||
if let Some(p) = paths.next() {
|
||||
{
|
||||
for (k, mounter_items) in &self.mounter_items {
|
||||
if let Some(mounter) = MOUNTERS.get(k) {
|
||||
if let Some(item) = mounter_items
|
||||
if let Some(mounter) = MOUNTERS.get(k)
|
||||
&& let Some(item) = mounter_items
|
||||
.iter()
|
||||
.find(|&item| item.path().is_some_and(|path| path == p))
|
||||
{
|
||||
return mounter
|
||||
.unmount(item.clone())
|
||||
.map(|()| cosmic::action::none());
|
||||
}
|
||||
{
|
||||
return mounter
|
||||
.unmount(item.clone())
|
||||
.map(|()| cosmic::action::none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4786,15 +4777,14 @@ impl Application for App {
|
|||
}
|
||||
Message::SaveSortNames => {
|
||||
self.must_save_sort_names = false;
|
||||
if let Some(state_handler) = self.state_handler.as_ref() {
|
||||
if let Err(err) = state_handler
|
||||
if let Some(state_handler) = self.state_handler.as_ref()
|
||||
&& let Err(err) = state_handler
|
||||
.set::<&FxOrderMap<String, (HeadingOptions, bool)>>(
|
||||
"sort_names",
|
||||
&self.state.sort_names,
|
||||
)
|
||||
{
|
||||
log::warn!("Failed to save sort names: {err:?}");
|
||||
}
|
||||
{
|
||||
log::warn!("Failed to save sort names: {err:?}");
|
||||
}
|
||||
}
|
||||
Message::NetworkDriveOpenEntityAfterMount { entity } => {
|
||||
|
|
@ -4890,13 +4880,13 @@ impl Application for App {
|
|||
fn dialog(&self) -> Option<Element<'_, Message>> {
|
||||
//TODO: should gallery view just be a dialog?
|
||||
let entity = self.tab_model.active();
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
|
||||
if tab.gallery {
|
||||
return Some(
|
||||
tab.gallery_view()
|
||||
.map(move |x| Message::TabMessage(Some(entity), x)),
|
||||
);
|
||||
}
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity)
|
||||
&& tab.gallery
|
||||
{
|
||||
return Some(
|
||||
tab.gallery_view()
|
||||
.map(move |x| Message::TabMessage(Some(entity), x)),
|
||||
);
|
||||
}
|
||||
let dialog_page = self.dialog_pages.front()?;
|
||||
|
||||
|
|
@ -5778,19 +5768,19 @@ impl Application for App {
|
|||
|
||||
let mut tab_column = widget::column::with_capacity(4);
|
||||
|
||||
if self.core.is_condensed() {
|
||||
if let Some(term) = self.search_get() {
|
||||
tab_column = tab_column.push(
|
||||
widget::container(
|
||||
widget::text_input::search_input("", term)
|
||||
.width(Length::Fill)
|
||||
.id(self.search_id.clone())
|
||||
.on_clear(Message::SearchClear)
|
||||
.on_input(Message::SearchInput),
|
||||
)
|
||||
.padding(space_xxs),
|
||||
);
|
||||
}
|
||||
if self.core.is_condensed()
|
||||
&& let Some(term) = self.search_get()
|
||||
{
|
||||
tab_column = tab_column.push(
|
||||
widget::container(
|
||||
widget::text_input::search_input("", term)
|
||||
.width(Length::Fill)
|
||||
.id(self.search_id.clone())
|
||||
.on_clear(Message::SearchClear)
|
||||
.on_input(Message::SearchInput),
|
||||
)
|
||||
.padding(space_xxs),
|
||||
);
|
||||
}
|
||||
|
||||
if self.tab_model.len() > 1 {
|
||||
|
|
@ -6088,14 +6078,14 @@ impl Application for App {
|
|||
let should_rescan =
|
||||
events.iter().any(|event| !event.kind.is_access());
|
||||
|
||||
if should_rescan {
|
||||
if let Err(e) = futures::executor::block_on(async {
|
||||
if should_rescan
|
||||
&& let Err(e) = futures::executor::block_on(async {
|
||||
output.send(Message::RescanTrash).await
|
||||
}) {
|
||||
log::warn!(
|
||||
"trash needs to be rescanned but sending message failed: {e:?}"
|
||||
);
|
||||
}
|
||||
})
|
||||
{
|
||||
log::warn!(
|
||||
"trash needs to be rescanned but sending message failed: {e:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -6173,14 +6163,12 @@ impl Application for App {
|
|||
|| kind.is_modify()
|
||||
|| kind.is_remove()
|
||||
|| kind.is_other()
|
||||
}) && let Err(e) = futures::executor::block_on(async {
|
||||
output.send(Message::RescanRecents).await
|
||||
}) {
|
||||
if let Err(e) = futures::executor::block_on(async {
|
||||
output.send(Message::RescanRecents).await
|
||||
}) {
|
||||
log::warn!(
|
||||
"open recents tabs need to be updated but sending message failed: {e:?}"
|
||||
);
|
||||
}
|
||||
log::warn!(
|
||||
"open recents tabs need to be updated but sending message failed: {e:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -6297,17 +6285,16 @@ impl Application for App {
|
|||
let mut selected_previews = Vec::new();
|
||||
match self.mode {
|
||||
Mode::App => {
|
||||
if self.core.window.show_context {
|
||||
if let ContextPage::Preview(entity_opt, PreviewKind::Selected) =
|
||||
if self.core.window.show_context
|
||||
&& let ContextPage::Preview(entity_opt, PreviewKind::Selected) =
|
||||
self.context_page
|
||||
{
|
||||
selected_previews
|
||||
.push(Some(entity_opt.unwrap_or_else(|| self.tab_model.active())));
|
||||
}
|
||||
{
|
||||
selected_previews
|
||||
.push(Some(entity_opt.unwrap_or_else(|| self.tab_model.active())));
|
||||
}
|
||||
}
|
||||
Mode::Desktop => {
|
||||
for window_kind in self.windows.iter().map(|(_, window)| &window.kind) {
|
||||
for window_kind in self.windows.values().map(|window| &window.kind) {
|
||||
if let WindowKind::Preview(entity_opt, _) = window_kind {
|
||||
selected_previews
|
||||
.push(Some(entity_opt.unwrap_or_else(|| self.tab_model.active())));
|
||||
|
|
@ -6322,8 +6309,7 @@ impl Application for App {
|
|||
tab.subscription(
|
||||
selected_previews
|
||||
.iter()
|
||||
.find(|preview| preview.as_ref() == Some(entity).as_ref())
|
||||
.is_some(),
|
||||
.any(|preview| preview.as_ref() == Some(entity).as_ref()),
|
||||
)
|
||||
.with(entity)
|
||||
.map(|(entity, tab_msg)| Message::TabMessage(Some(entity), tab_msg)),
|
||||
|
|
|
|||
296
src/dialog.rs
296
src/dialog.rs
|
|
@ -179,11 +179,11 @@ impl<T: AsRef<str>> From<T> for DialogLabel {
|
|||
});
|
||||
}
|
||||
|
||||
if let Some(span) = spans.last_mut() {
|
||||
if underline == span.underline {
|
||||
span.text.push(c);
|
||||
continue;
|
||||
}
|
||||
if let Some(span) = spans.last_mut()
|
||||
&& underline == span.underline
|
||||
{
|
||||
span.text.push(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
spans.push(DialogLabelSpan {
|
||||
|
|
@ -718,10 +718,10 @@ impl App {
|
|||
children.push(preview);
|
||||
}
|
||||
|
||||
if children.is_empty() {
|
||||
if let Some(item) = &self.tab.parent_item_opt {
|
||||
children.push(item.preview_view(None, military_time));
|
||||
}
|
||||
if children.is_empty()
|
||||
&& let Some(item) = &self.tab.parent_item_opt
|
||||
{
|
||||
children.push(item.preview_view(None, military_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1279,12 +1279,12 @@ impl Application for App {
|
|||
return self.update(message);
|
||||
}
|
||||
|
||||
if let Some(data) = self.nav_model.data::<MounterData>(entity) {
|
||||
if let Some(mounter) = MOUNTERS.get(&data.0) {
|
||||
return mounter
|
||||
.mount(data.1.clone())
|
||||
.map(|()| cosmic::action::none());
|
||||
}
|
||||
if let Some(data) = self.nav_model.data::<MounterData>(entity)
|
||||
&& let Some(mounter) = MOUNTERS.get(&data.0)
|
||||
{
|
||||
return mounter
|
||||
.mount(data.1.clone())
|
||||
.map(|()| cosmic::action::none());
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -1322,10 +1322,10 @@ impl Application for App {
|
|||
|
||||
// Close the dialog if the focused widget is the dialog's main text input instead of
|
||||
// unfocussing the widget.
|
||||
if let operation::Outcome::Some(focused) = operation::focusable::find_focused().finish() {
|
||||
if self.dialog_text_input == focused {
|
||||
return self.update(Message::Cancel);
|
||||
}
|
||||
if let operation::Outcome::Some(focused) = operation::focusable::find_focused().finish()
|
||||
&& self.dialog_text_input == focused
|
||||
{
|
||||
return self.update(Message::Cancel);
|
||||
}
|
||||
|
||||
self.update(Message::Cancel)
|
||||
|
|
@ -1419,14 +1419,14 @@ impl Application for App {
|
|||
}
|
||||
|
||||
// Check key binds from accept label
|
||||
if let Some(key_bind) = &self.accept_label.key_bind_opt {
|
||||
if key_bind.matches(modifiers, &key) {
|
||||
return self.update(if self.flags.kind.save() {
|
||||
Message::Save(false)
|
||||
} else {
|
||||
Message::Open
|
||||
});
|
||||
}
|
||||
if let Some(key_bind) = &self.accept_label.key_bind_opt
|
||||
&& key_bind.matches(modifiers, &key)
|
||||
{
|
||||
return self.update(if self.flags.kind.save() {
|
||||
Message::Save(false)
|
||||
} else {
|
||||
Message::Open
|
||||
});
|
||||
}
|
||||
|
||||
// Uncaptured keys with only shift modifiers go to the search or location box
|
||||
|
|
@ -1434,45 +1434,44 @@ impl Application for App {
|
|||
&& !modifiers.control()
|
||||
&& !modifiers.alt()
|
||||
&& matches!(key, Key::Character(_))
|
||||
&& let Some(text) = text
|
||||
{
|
||||
if let Some(text) = text {
|
||||
match self.flags.config.type_to_search {
|
||||
TypeToSearch::Recursive => {
|
||||
let mut term = self.search_get().unwrap_or_default().to_string();
|
||||
term.push_str(&text);
|
||||
return self.search_set(Some(term));
|
||||
match self.flags.config.type_to_search {
|
||||
TypeToSearch::Recursive => {
|
||||
let mut term = self.search_get().unwrap_or_default().to_string();
|
||||
term.push_str(&text);
|
||||
return self.search_set(Some(term));
|
||||
}
|
||||
TypeToSearch::EnterPath => {
|
||||
let location = (self.tab.edit_location)
|
||||
.as_ref()
|
||||
.map_or_else(|| &self.tab.location, |x| &x.location);
|
||||
// Try to add text to end of location
|
||||
if let Some(path) = location.path_opt() {
|
||||
let mut path_string = path.to_string_lossy().to_string();
|
||||
path_string.push_str(&text);
|
||||
self.tab.edit_location =
|
||||
Some(location.with_path(PathBuf::from(path_string)).into());
|
||||
}
|
||||
TypeToSearch::EnterPath => {
|
||||
let location = (self.tab.edit_location)
|
||||
.as_ref()
|
||||
.map_or_else(|| &self.tab.location, |x| &x.location);
|
||||
// Try to add text to end of location
|
||||
if let Some(path) = location.path_opt() {
|
||||
let mut path_string = path.to_string_lossy().to_string();
|
||||
path_string.push_str(&text);
|
||||
self.tab.edit_location =
|
||||
Some(location.with_path(PathBuf::from(path_string)).into());
|
||||
}
|
||||
}
|
||||
TypeToSearch::SelectByPrefix => {
|
||||
// Reset buffer if timeout elapsed
|
||||
if let Some(last_key) = self.type_select_last_key
|
||||
&& last_key.elapsed() >= tab::TYPE_SELECT_TIMEOUT
|
||||
{
|
||||
self.type_select_prefix.clear();
|
||||
}
|
||||
TypeToSearch::SelectByPrefix => {
|
||||
// Reset buffer if timeout elapsed
|
||||
if let Some(last_key) = self.type_select_last_key {
|
||||
if last_key.elapsed() >= tab::TYPE_SELECT_TIMEOUT {
|
||||
self.type_select_prefix.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate character and select
|
||||
self.type_select_prefix.push_str(&text.to_lowercase());
|
||||
self.type_select_last_key = Some(Instant::now());
|
||||
// Accumulate character and select
|
||||
self.type_select_prefix.push_str(&text.to_lowercase());
|
||||
self.type_select_last_key = Some(Instant::now());
|
||||
|
||||
self.tab.select_by_prefix(&self.type_select_prefix);
|
||||
if let Some(offset) = self.tab.select_focus_scroll() {
|
||||
return scrollable::scroll_to(
|
||||
self.tab.scrollable_id.clone(),
|
||||
offset,
|
||||
);
|
||||
}
|
||||
self.tab.select_by_prefix(&self.type_select_prefix);
|
||||
if let Some(offset) = self.tab.select_focus_scroll() {
|
||||
return scrollable::scroll_to(
|
||||
self.tab.scrollable_id.clone(),
|
||||
offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1486,21 +1485,22 @@ impl Application for App {
|
|||
let mut unmounted = Vec::new();
|
||||
if let Some(old_items) = self.mounter_items.get(&mounter_key) {
|
||||
for old_item in old_items {
|
||||
if let Some(old_path) = old_item.path() {
|
||||
if old_item.is_mounted() {
|
||||
let mut still_mounted = false;
|
||||
for item in &mounter_items {
|
||||
if let Some(path) = item.path() {
|
||||
if path == old_path && item.is_mounted() {
|
||||
still_mounted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !still_mounted {
|
||||
unmounted.push(Location::Path(old_path));
|
||||
if let Some(old_path) = old_item.path()
|
||||
&& old_item.is_mounted()
|
||||
{
|
||||
let mut still_mounted = false;
|
||||
for item in &mounter_items {
|
||||
if let Some(path) = item.path()
|
||||
&& path == old_path
|
||||
&& item.is_mounted()
|
||||
{
|
||||
still_mounted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !still_mounted {
|
||||
unmounted.push(Location::Path(old_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1601,16 +1601,16 @@ impl Application for App {
|
|||
let mut paths = Vec::new();
|
||||
if let Some(items) = self.tab.items_opt() {
|
||||
for item in items {
|
||||
if item.selected {
|
||||
if let Some(path) = item.path_opt() {
|
||||
paths.push(path.clone());
|
||||
let _ = update_recently_used(
|
||||
path,
|
||||
Self::APP_ID.to_string(),
|
||||
"cosmic-files".to_string(),
|
||||
None,
|
||||
);
|
||||
}
|
||||
if item.selected
|
||||
&& let Some(path) = item.path_opt()
|
||||
{
|
||||
paths.push(path.clone());
|
||||
let _ = update_recently_used(
|
||||
path,
|
||||
Self::APP_ID.to_string(),
|
||||
"cosmic-files".to_string(),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1640,11 +1640,11 @@ impl Application for App {
|
|||
}
|
||||
|
||||
// If we are in directory mode, return the current directory
|
||||
if self.flags.kind.is_dir() {
|
||||
if let Location::Path(tab_path) = &self.tab.location {
|
||||
self.result_opt = Some(DialogResult::Open(vec![tab_path.clone()]));
|
||||
return window::close(self.flags.window_id);
|
||||
}
|
||||
if self.flags.kind.is_dir()
|
||||
&& let Location::Path(tab_path) = &self.tab.location
|
||||
{
|
||||
self.result_opt = Some(DialogResult::Open(vec![tab_path.clone()]));
|
||||
return window::close(self.flags.window_id);
|
||||
}
|
||||
}
|
||||
Message::Preview => {
|
||||
|
|
@ -1654,26 +1654,24 @@ impl Application for App {
|
|||
});
|
||||
}
|
||||
Message::Save(replace) => {
|
||||
if let DialogKind::SaveFile { filename } = &self.flags.kind {
|
||||
if !filename.is_empty() {
|
||||
if let Some(tab_path) = self.tab.location.path_opt() {
|
||||
let path = tab_path.join(filename);
|
||||
if path.is_dir() {
|
||||
// cd to directory
|
||||
let message = Message::TabMessage(tab::Message::Location(
|
||||
Location::Path(path),
|
||||
));
|
||||
return self.update(message);
|
||||
} else if !replace && path.exists() {
|
||||
self.dialog_pages.push_back(DialogPage::Replace {
|
||||
filename: filename.clone(),
|
||||
});
|
||||
return widget::button::focus(REPLACE_BUTTON_ID.clone());
|
||||
}
|
||||
self.result_opt = Some(DialogResult::Open(vec![path]));
|
||||
return window::close(self.flags.window_id);
|
||||
}
|
||||
if let DialogKind::SaveFile { filename } = &self.flags.kind
|
||||
&& !filename.is_empty()
|
||||
&& let Some(tab_path) = self.tab.location.path_opt()
|
||||
{
|
||||
let path = tab_path.join(filename);
|
||||
if path.is_dir() {
|
||||
// cd to directory
|
||||
let message =
|
||||
Message::TabMessage(tab::Message::Location(Location::Path(path)));
|
||||
return self.update(message);
|
||||
} else if !replace && path.exists() {
|
||||
self.dialog_pages.push_back(DialogPage::Replace {
|
||||
filename: filename.clone(),
|
||||
});
|
||||
return widget::button::focus(REPLACE_BUTTON_ID.clone());
|
||||
}
|
||||
self.result_opt = Some(DialogResult::Open(vec![path]));
|
||||
return window::close(self.flags.window_id);
|
||||
}
|
||||
}
|
||||
Message::ScrollTab(scroll_speed) => {
|
||||
|
|
@ -1703,16 +1701,14 @@ impl Application for App {
|
|||
let tab_commands = self.tab.update(tab_message, self.modifiers);
|
||||
|
||||
// Update filename box when anything is selected
|
||||
if let DialogKind::SaveFile { filename } = &mut self.flags.kind {
|
||||
if let Some(click_i) = click_i_opt {
|
||||
if let Some(items) = self.tab.items_opt() {
|
||||
if let Some(item) = items.get(click_i) {
|
||||
if item.selected && !item.metadata.is_dir() {
|
||||
filename.clone_from(&item.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let DialogKind::SaveFile { filename } = &mut self.flags.kind
|
||||
&& let Some(click_i) = click_i_opt
|
||||
&& let Some(items) = self.tab.items_opt()
|
||||
&& let Some(item) = items.get(click_i)
|
||||
&& item.selected
|
||||
&& !item.metadata.is_dir()
|
||||
{
|
||||
filename.clone_from(&item.name);
|
||||
}
|
||||
|
||||
let mut commands = Vec::new();
|
||||
|
|
@ -1840,34 +1836,34 @@ impl Application for App {
|
|||
Message::TabRescan(location, parent_item_opt, mut items, selection_paths) => {
|
||||
if location == self.tab.location {
|
||||
// Filter
|
||||
if let Some(filter_i) = self.filter_selected {
|
||||
if let Some(filter) = self.filters.get(filter_i) {
|
||||
// Parse globs (Mime implements PartialEq with &str, so no need to parse)
|
||||
let mut parsed_globs = Vec::new();
|
||||
let mut mimes = Vec::new();
|
||||
for pattern in &filter.patterns {
|
||||
match pattern {
|
||||
DialogFilterPattern::Glob(value) => {
|
||||
match glob::Pattern::new(value) {
|
||||
Ok(glob) => parsed_globs.push(glob),
|
||||
Err(err) => {
|
||||
log::warn!("failed to parse glob {value:?}: {err}");
|
||||
}
|
||||
if let Some(filter_i) = self.filter_selected
|
||||
&& let Some(filter) = self.filters.get(filter_i)
|
||||
{
|
||||
// Parse globs (Mime implements PartialEq with &str, so no need to parse)
|
||||
let mut parsed_globs = Vec::new();
|
||||
let mut mimes = Vec::new();
|
||||
for pattern in &filter.patterns {
|
||||
match pattern {
|
||||
DialogFilterPattern::Glob(value) => {
|
||||
match glob::Pattern::new(value) {
|
||||
Ok(glob) => parsed_globs.push(glob),
|
||||
Err(err) => {
|
||||
log::warn!("failed to parse glob {value:?}: {err}");
|
||||
}
|
||||
}
|
||||
DialogFilterPattern::Mime(value) => mimes.push(value.as_str()),
|
||||
}
|
||||
DialogFilterPattern::Mime(value) => mimes.push(value.as_str()),
|
||||
}
|
||||
}
|
||||
|
||||
items.retain(|item| {
|
||||
// Directories are always shown
|
||||
item.metadata.is_dir()
|
||||
items.retain(|item| {
|
||||
// Directories are always shown
|
||||
item.metadata.is_dir()
|
||||
// Check for mime type match (first because it is faster)
|
||||
|| mimes.iter().copied().any(|mime| mime == item.mime)
|
||||
// Check for glob match (last because it is slower)
|
||||
|| parsed_globs.iter().any(|glob| glob.matches(&item.name))
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Select based on filename
|
||||
|
|
@ -1944,19 +1940,19 @@ impl Application for App {
|
|||
|
||||
let mut col = widget::column::with_capacity(2);
|
||||
|
||||
if self.core.is_condensed() {
|
||||
if let Some(term) = self.search_get() {
|
||||
col = col.push(
|
||||
widget::container(
|
||||
widget::text_input::search_input("", term)
|
||||
.width(Length::Fill)
|
||||
.id(self.search_id.clone())
|
||||
.on_clear(Message::SearchClear)
|
||||
.on_input(Message::SearchInput),
|
||||
)
|
||||
.padding(space_xxs),
|
||||
);
|
||||
}
|
||||
if self.core.is_condensed()
|
||||
&& let Some(term) = self.search_get()
|
||||
{
|
||||
col = col.push(
|
||||
widget::container(
|
||||
widget::text_input::search_input("", term)
|
||||
.width(Length::Fill)
|
||||
.id(self.search_id.clone())
|
||||
.on_clear(Message::SearchClear)
|
||||
.on_input(Message::SearchInput),
|
||||
)
|
||||
.padding(space_xxs),
|
||||
);
|
||||
}
|
||||
|
||||
col = col.push(
|
||||
|
|
|
|||
|
|
@ -392,16 +392,16 @@ impl LargeImageManager {
|
|||
generation: u64,
|
||||
) -> bool {
|
||||
// Check if this decode is still current (not superseded by a newer one)
|
||||
if let Some(¤t_gen) = self.decode_generations.get(&path) {
|
||||
if generation != current_gen {
|
||||
log::info!(
|
||||
"Discarding outdated decode for {} (generation {} != current {})",
|
||||
path.display(),
|
||||
generation,
|
||||
current_gen
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if let Some(¤t_gen) = self.decode_generations.get(&path)
|
||||
&& generation != current_gen
|
||||
{
|
||||
log::info!(
|
||||
"Discarding outdated decode for {} (generation {} != current {})",
|
||||
path.display(),
|
||||
generation,
|
||||
current_gen
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
log::info!(
|
||||
|
|
@ -556,7 +556,7 @@ impl LargeImageManager {
|
|||
|
||||
/// Check if sufficient memory is available, clearing cache if needed.
|
||||
/// Returns true if memory is available, false otherwise.
|
||||
fn ensure_memory_available(&mut self, path: &PathBuf, width: u32, height: u32) -> bool {
|
||||
fn ensure_memory_available(&mut self, path: &Path, width: u32, height: u32) -> bool {
|
||||
let (has_memory, error_opt) = check_memory_available(width, height);
|
||||
|
||||
if has_memory {
|
||||
|
|
@ -565,7 +565,7 @@ impl LargeImageManager {
|
|||
|
||||
if self.cache_is_empty() {
|
||||
if let Some(error_msg) = error_opt {
|
||||
self.store_error(path.clone(), error_msg);
|
||||
self.store_error(path.to_path_buf(), error_msg);
|
||||
log::warn!(
|
||||
"Cannot load {}: insufficient memory and cache is empty",
|
||||
path.display()
|
||||
|
|
@ -588,7 +588,7 @@ impl LargeImageManager {
|
|||
}
|
||||
|
||||
if let Some(error_msg) = error_opt_after {
|
||||
self.store_error(path.clone(), error_msg);
|
||||
self.store_error(path.to_path_buf(), error_msg);
|
||||
log::warn!(
|
||||
"Cannot load {}: insufficient memory even after cache clear",
|
||||
path.display()
|
||||
|
|
|
|||
|
|
@ -62,10 +62,10 @@ pub static LOCALE: LazyLock<Locale> = LazyLock::new(|| {
|
|||
}
|
||||
|
||||
// Try language-only fallback (e.g., "en" from "en-US")
|
||||
if let Some(lang) = cleaned_locale.split('-').next() {
|
||||
if let Ok(locale) = Locale::try_from_str(lang) {
|
||||
return locale;
|
||||
}
|
||||
if let Some(lang) = cleaned_locale.split('-').next()
|
||||
&& let Ok(locale) = Locale::try_from_str(lang)
|
||||
{
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ impl MimeAppCache {
|
|||
for (mime, filenames) in list.removed_associations.iter() {
|
||||
for filename in filenames {
|
||||
log::trace!("remove {mime}={filename}");
|
||||
if let Some(apps) = self.cache.get_mut(&mime) {
|
||||
if let Some(apps) = self.cache.get_mut(mime) {
|
||||
apps.retain(|x| !filename_eq(&x.path, filename));
|
||||
}
|
||||
}
|
||||
|
|
@ -319,7 +319,7 @@ impl MimeAppCache {
|
|||
for (mime, filenames) in list.default_apps.iter() {
|
||||
for filename in filenames {
|
||||
log::trace!("default {mime}={filename}");
|
||||
if let Some(apps) = self.cache.get_mut(&mime) {
|
||||
if let Some(apps) = self.cache.get_mut(mime) {
|
||||
let mut found = false;
|
||||
for app in apps.iter_mut() {
|
||||
if filename_eq(&app.path, filename) {
|
||||
|
|
|
|||
|
|
@ -23,12 +23,11 @@ fn resolve_uri(uri: &str) -> (String, gio::File) {
|
|||
TARGET_URI_ATTRIBUTE,
|
||||
gio::FileQueryInfoFlags::NONE,
|
||||
gio::Cancellable::NONE,
|
||||
) {
|
||||
if let Some(resolved_uri) = file_info.attribute_as_string(TARGET_URI_ATTRIBUTE) {
|
||||
let resolved_uri = String::from(resolved_uri);
|
||||
let file = gio::File::for_uri(&resolved_uri);
|
||||
return (resolved_uri, file);
|
||||
}
|
||||
) && let Some(resolved_uri) = file_info.attribute_as_string(TARGET_URI_ATTRIBUTE)
|
||||
{
|
||||
let resolved_uri = String::from(resolved_uri);
|
||||
let file = gio::File::for_uri(&resolved_uri);
|
||||
return (resolved_uri, file);
|
||||
}
|
||||
|
||||
(uri.to_string(), file)
|
||||
|
|
@ -60,7 +59,7 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
|
|||
gio::Cancellable::NONE,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|info| Some(info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE)))
|
||||
.map(|info| info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE))
|
||||
.unwrap_or(true); // Default to remote if query fails
|
||||
|
||||
MounterItem::Gvfs(Item {
|
||||
|
|
@ -457,9 +456,9 @@ impl Gvfs {
|
|||
log::info!("mount {name}: result {res:?}");
|
||||
// Update the mounter_item with mount information after successful mount
|
||||
let mut updated_item = mounter_item.clone();
|
||||
if res.is_ok() {
|
||||
if let MounterItem::Gvfs(ref mut item) = updated_item {
|
||||
if let Some(mount) = volume_for_callback.get_mount() {
|
||||
if res.is_ok()
|
||||
&& let MounterItem::Gvfs(ref mut item) = updated_item
|
||||
&& let Some(mount) = volume_for_callback.get_mount() {
|
||||
let root = MountExt::root(&mount);
|
||||
item.path_opt = root.path();
|
||||
item.is_mounted = true;
|
||||
|
|
@ -469,14 +468,9 @@ impl Gvfs {
|
|||
gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE,
|
||||
gio::Cancellable::NONE,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|info| {
|
||||
Some(info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE))
|
||||
})
|
||||
.ok().map(|info| info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE))
|
||||
.unwrap_or(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
event_tx.send(Event::MountResult(updated_item, match res {
|
||||
Ok(()) => {
|
||||
_ = complete_tx.send(Ok(()));
|
||||
|
|
|
|||
|
|
@ -246,19 +246,18 @@ struct State {
|
|||
|
||||
impl State {
|
||||
fn drag_rect(&self, cursor: mouse::Cursor) -> Option<Rectangle> {
|
||||
if let Some(drag_source) = self.drag_initiated {
|
||||
if let Some(position) = cursor.position().or(self.last_virtual_position) {
|
||||
if position.distance(drag_source) > 1.0 {
|
||||
let min_x = drag_source.x.min(position.x);
|
||||
let max_x = drag_source.x.max(position.x);
|
||||
let min_y = drag_source.y.min(position.y);
|
||||
let max_y = drag_source.y.max(position.y);
|
||||
return Some(Rectangle::new(
|
||||
Point::new(min_x, min_y),
|
||||
Size::new(max_x - min_x, max_y - min_y),
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(drag_source) = self.drag_initiated
|
||||
&& let Some(position) = cursor.position().or(self.last_virtual_position)
|
||||
&& position.distance(drag_source) > 1.0
|
||||
{
|
||||
let min_x = drag_source.x.min(position.x);
|
||||
let max_x = drag_source.x.max(position.x);
|
||||
let min_y = drag_source.y.min(position.y);
|
||||
let max_y = drag_source.y.max(position.y);
|
||||
return Some(Rectangle::new(
|
||||
Point::new(min_x, min_y),
|
||||
Size::new(max_x - min_x, max_y - min_y),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -527,12 +526,12 @@ fn update<Message: Clone>(
|
|||
let offset = layout.virtual_offset();
|
||||
let layout_bounds = layout.bounds();
|
||||
|
||||
let viewport_changed = state.viewport.map_or(true, |v| v != *viewport);
|
||||
let viewport_changed = state.viewport != Some(*viewport);
|
||||
|
||||
if let Some(message) = widget.on_resize.as_ref() {
|
||||
if viewport_changed {
|
||||
shell.publish(message(*viewport));
|
||||
}
|
||||
if let Some(message) = widget.on_resize.as_ref()
|
||||
&& viewport_changed
|
||||
{
|
||||
shell.publish(message(*viewport));
|
||||
}
|
||||
|
||||
state.viewport = Some(*viewport);
|
||||
|
|
@ -664,113 +663,112 @@ fn update<Message: Clone>(
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_right_press.as_ref() {
|
||||
if matches!(
|
||||
if let Some(message) = widget.on_right_press.as_ref()
|
||||
&& matches!(
|
||||
event,
|
||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right))
|
||||
) {
|
||||
let point_opt = if widget.on_right_press_window_position {
|
||||
cursor.position_over(layout_bounds).map(|mut p| {
|
||||
p.x -= offset.x;
|
||||
p.y -= offset.y;
|
||||
p
|
||||
})
|
||||
} else {
|
||||
cursor.position_in(layout_bounds)
|
||||
};
|
||||
shell.publish(message(point_opt));
|
||||
)
|
||||
{
|
||||
let point_opt = if widget.on_right_press_window_position {
|
||||
cursor.position_over(layout_bounds).map(|mut p| {
|
||||
p.x -= offset.x;
|
||||
p.y -= offset.y;
|
||||
p
|
||||
})
|
||||
} else {
|
||||
cursor.position_in(layout_bounds)
|
||||
};
|
||||
shell.publish(message(point_opt));
|
||||
|
||||
if widget.on_right_press_no_capture {
|
||||
return event::Status::Ignored;
|
||||
}
|
||||
return event::Status::Captured;
|
||||
if widget.on_right_press_no_capture {
|
||||
return event::Status::Ignored;
|
||||
}
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_right_release.as_ref() {
|
||||
if matches!(
|
||||
if let Some(message) = widget.on_right_release.as_ref()
|
||||
&& matches!(
|
||||
event,
|
||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Right))
|
||||
) {
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
)
|
||||
{
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_middle_press.as_ref() {
|
||||
if matches!(
|
||||
if let Some(message) = widget.on_middle_press.as_ref()
|
||||
&& matches!(
|
||||
event,
|
||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle))
|
||||
) {
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
)
|
||||
{
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_middle_release.as_ref() {
|
||||
if matches!(
|
||||
if let Some(message) = widget.on_middle_release.as_ref()
|
||||
&& matches!(
|
||||
event,
|
||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Middle))
|
||||
) {
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
)
|
||||
{
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_back_press.as_ref() {
|
||||
if matches!(
|
||||
if let Some(message) = widget.on_back_press.as_ref()
|
||||
&& matches!(
|
||||
event,
|
||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Back))
|
||||
) {
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
)
|
||||
{
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_back_release.as_ref() {
|
||||
if matches!(
|
||||
if let Some(message) = widget.on_back_release.as_ref()
|
||||
&& matches!(
|
||||
event,
|
||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Back))
|
||||
) {
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
)
|
||||
{
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_forward_press.as_ref() {
|
||||
if matches!(
|
||||
if let Some(message) = widget.on_forward_press.as_ref()
|
||||
&& matches!(
|
||||
event,
|
||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Forward))
|
||||
) {
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
)
|
||||
{
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_forward_release.as_ref() {
|
||||
if matches!(
|
||||
if let Some(message) = widget.on_forward_release.as_ref()
|
||||
&& matches!(
|
||||
event,
|
||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Forward))
|
||||
) {
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
)
|
||||
{
|
||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Some(on_scroll) = widget.on_scroll.as_ref() {
|
||||
if let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event {
|
||||
if let Some(message) = on_scroll(*delta) {
|
||||
shell.publish(message);
|
||||
return event::Status::Captured;
|
||||
}
|
||||
}
|
||||
if let Some(on_scroll) = widget.on_scroll.as_ref()
|
||||
&& let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event
|
||||
&& let Some(message) = on_scroll(*delta)
|
||||
{
|
||||
shell.publish(message);
|
||||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Some((message, drag_rect)) = widget.on_drag.as_ref().zip(state.drag_rect(cursor)) {
|
||||
|
|
|
|||
|
|
@ -957,10 +957,10 @@ impl Operation {
|
|||
let dir_name = get_directory_name(file_name);
|
||||
let mut new_dir = to.join(dir_name);
|
||||
|
||||
if new_dir.exists() {
|
||||
if let Some(new_dir_parent) = new_dir.parent() {
|
||||
new_dir = copy_unique_path(&new_dir, new_dir_parent);
|
||||
}
|
||||
if new_dir.exists()
|
||||
&& let Some(new_dir_parent) = new_dir.parent()
|
||||
{
|
||||
new_dir = copy_unique_path(&new_dir, new_dir_parent);
|
||||
}
|
||||
|
||||
op_sel.ignored.push(path.clone());
|
||||
|
|
|
|||
|
|
@ -138,10 +138,10 @@ impl Context {
|
|||
}),
|
||||
is_cleanup: false,
|
||||
};
|
||||
if matches!(method, Method::Move { .. }) {
|
||||
if let Some(cleanup_op) = op.move_cleanup_op() {
|
||||
cleanup_ops.push(cleanup_op);
|
||||
}
|
||||
if matches!(method, Method::Move { .. })
|
||||
&& let Some(cleanup_op) = op.move_cleanup_op()
|
||||
{
|
||||
cleanup_ops.push(cleanup_op);
|
||||
}
|
||||
if let Some(parent) = op.to.parent() {
|
||||
target_dirs.insert(parent.to_path_buf());
|
||||
|
|
|
|||
621
src/tab.rs
621
src/tab.rs
|
|
@ -934,43 +934,43 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec<Item> {
|
|||
|
||||
#[cfg(feature = "gvfs")]
|
||||
{
|
||||
if let Ok(path_meta) = fs::metadata(tab_path) {
|
||||
if fs_kind(&path_meta) == FsKind::Gvfs {
|
||||
let file = gio::File::for_path(tab_path);
|
||||
if let Ok(path_meta) = fs::metadata(tab_path)
|
||||
&& fs_kind(&path_meta) == FsKind::Gvfs
|
||||
{
|
||||
let file = gio::File::for_path(tab_path);
|
||||
|
||||
// gio crate expects a comma delimited string
|
||||
let attr_string = [
|
||||
gio::FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME.as_str(),
|
||||
gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE.as_str(),
|
||||
gio::FILE_ATTRIBUTE_TIME_MODIFIED.as_str(),
|
||||
gio::FILE_ATTRIBUTE_STANDARD_SIZE.as_str(),
|
||||
gio::FILE_ATTRIBUTE_STANDARD_TYPE.as_str(),
|
||||
gio::FILE_ATTRIBUTE_STANDARD_NAME.as_str(),
|
||||
]
|
||||
.join(",");
|
||||
// gio crate expects a comma delimited string
|
||||
let attr_string = [
|
||||
gio::FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME.as_str(),
|
||||
gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE.as_str(),
|
||||
gio::FILE_ATTRIBUTE_TIME_MODIFIED.as_str(),
|
||||
gio::FILE_ATTRIBUTE_STANDARD_SIZE.as_str(),
|
||||
gio::FILE_ATTRIBUTE_STANDARD_TYPE.as_str(),
|
||||
gio::FILE_ATTRIBUTE_STANDARD_NAME.as_str(),
|
||||
]
|
||||
.join(",");
|
||||
|
||||
match gio::prelude::FileExt::enumerate_children(
|
||||
&file,
|
||||
attr_string.as_str(),
|
||||
gio::FileQueryInfoFlags::NONE,
|
||||
gio::Cancellable::NONE,
|
||||
) {
|
||||
Ok(res) => {
|
||||
remote_scannable = true;
|
||||
items = res
|
||||
.filter_map(|file| {
|
||||
let file = file.ok()?;
|
||||
Some(item_from_gvfs_info(tab_path.join(file.name()), file, sizes))
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"could not enumerate {} via gio: {}",
|
||||
tab_path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
match gio::prelude::FileExt::enumerate_children(
|
||||
&file,
|
||||
attr_string.as_str(),
|
||||
gio::FileQueryInfoFlags::NONE,
|
||||
gio::Cancellable::NONE,
|
||||
) {
|
||||
Ok(res) => {
|
||||
remote_scannable = true;
|
||||
items = res
|
||||
.filter_map(|file| {
|
||||
let file = file.ok()?;
|
||||
Some(item_from_gvfs_info(tab_path.join(file.name()), file, sizes))
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"could not enumerate {} via gio: {}",
|
||||
tab_path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1410,11 +1410,11 @@ impl EditLocation {
|
|||
self.selected = Some(selected);
|
||||
|
||||
// Automatically resolve if there is only one completion
|
||||
if completions.len() == 1 {
|
||||
if let Some(resolved) = self.resolve() {
|
||||
self.location = resolved;
|
||||
self.selected = None;
|
||||
}
|
||||
if completions.len() == 1
|
||||
&& let Some(resolved) = self.resolve()
|
||||
{
|
||||
self.location = resolved;
|
||||
self.selected = None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -2033,10 +2033,10 @@ impl ItemThumbnail {
|
|||
if let Some((item_thumbnail, temp_file)) =
|
||||
Self::generate_thumbnail_external(path, &mime, thumbnail_size, thumbnail_dir)
|
||||
{
|
||||
if let Ok(cache) = thumbnail_cacher {
|
||||
if let Err(err) = cache.update_with_temp_file(temp_file) {
|
||||
log::warn!("failed to update cache for {}: {}", path.display(), err);
|
||||
}
|
||||
if let Ok(cache) = thumbnail_cacher
|
||||
&& let Err(err) = cache.update_with_temp_file(temp_file)
|
||||
{
|
||||
log::warn!("failed to update cache for {}: {}", path.display(), err);
|
||||
}
|
||||
return item_thumbnail;
|
||||
}
|
||||
|
|
@ -2076,16 +2076,15 @@ impl ItemThumbnail {
|
|||
// If we weren't able to create a thumbnail, but we should have
|
||||
// been able to, create a fail marker so that it isn't tried the
|
||||
// next time.
|
||||
if let Ok(cacher) = thumbnail_cacher {
|
||||
if tried_supported_file {
|
||||
if let Err(err) = cacher.create_fail_marker() {
|
||||
log::warn!(
|
||||
"failed to create thumbnail fail marker for {}: {}",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Ok(cacher) = thumbnail_cacher
|
||||
&& tried_supported_file
|
||||
&& let Err(err) = cacher.create_fail_marker()
|
||||
{
|
||||
log::warn!(
|
||||
"failed to create thumbnail fail marker for {}: {}",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
Self::NotImage
|
||||
|
|
@ -2273,13 +2272,13 @@ impl Item {
|
|||
widget::button::icon(widget::icon::from_name("go-next-symbolic"))
|
||||
.on_press(Message::ItemRight),
|
||||
);
|
||||
if self.can_gallery() {
|
||||
if let Some(_path) = self.path_opt() {
|
||||
row = row.push(
|
||||
widget::button::icon(widget::icon::from_name("view-fullscreen-symbolic"))
|
||||
.on_press(Message::Gallery(true)),
|
||||
);
|
||||
}
|
||||
if self.can_gallery()
|
||||
&& let Some(_path) = self.path_opt()
|
||||
{
|
||||
row = row.push(
|
||||
widget::button::icon(widget::icon::from_name("view-fullscreen-symbolic"))
|
||||
.on_press(Message::Gallery(true)),
|
||||
);
|
||||
}
|
||||
row.into()
|
||||
}
|
||||
|
|
@ -2445,11 +2444,11 @@ impl Item {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(path) = self.path_opt() {
|
||||
if let Ok(img) = image::image_dimensions(path) {
|
||||
let (width, height) = img;
|
||||
details = details.push(widget::text::body(format!("{width}x{height}")));
|
||||
}
|
||||
if let Some(path) = self.path_opt()
|
||||
&& let Ok(img) = image::image_dimensions(path)
|
||||
{
|
||||
let (width, height) = img;
|
||||
details = details.push(widget::text::body(format!("{width}x{height}")));
|
||||
}
|
||||
column = column.push(details);
|
||||
|
||||
|
|
@ -2637,12 +2636,11 @@ async fn calculate_dir_size(path: &Path, controller: Controller) -> Result<u64,
|
|||
.map_err(|s| OperationError::from_state(s, &controller))?;
|
||||
|
||||
//TODO: report more errors?
|
||||
if let Ok(entry) = entry_res {
|
||||
if let Ok(metadata) = entry.metadata() {
|
||||
if metadata.is_file() {
|
||||
total += metadata.len();
|
||||
}
|
||||
}
|
||||
if let Ok(entry) = entry_res
|
||||
&& let Ok(metadata) = entry.metadata()
|
||||
&& metadata.is_file()
|
||||
{
|
||||
total += metadata.len();
|
||||
}
|
||||
|
||||
// Yield in case this process takes a while.
|
||||
|
|
@ -2768,10 +2766,10 @@ impl Tab {
|
|||
let selected = self.selected_locations();
|
||||
for item in &mut items {
|
||||
item.selected = false;
|
||||
if let Some(location) = &item.location_opt {
|
||||
if selected.contains(location) {
|
||||
item.selected = true;
|
||||
}
|
||||
if let Some(location) = &item.location_opt
|
||||
&& selected.contains(location)
|
||||
{
|
||||
item.selected = true;
|
||||
}
|
||||
}
|
||||
self.items_opt = Some(items);
|
||||
|
|
@ -2790,10 +2788,9 @@ impl Tab {
|
|||
for item in items.iter_mut() {
|
||||
item.cut = false;
|
||||
if let Some(location_path) = item.location_opt.as_ref().and_then(Location::path_opt)
|
||||
&& locations.contains(location_path)
|
||||
{
|
||||
if locations.contains(location_path) {
|
||||
item.cut = true;
|
||||
}
|
||||
item.cut = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2883,11 +2880,11 @@ impl Tab {
|
|||
if let Some(ref mut items) = self.items_opt {
|
||||
for (i, item) in items.iter_mut().enumerate() {
|
||||
item.selected = false;
|
||||
if let Some(path) = item.path_opt() {
|
||||
if paths.contains(path) {
|
||||
item.selected = true;
|
||||
self.select_focus = Some(i);
|
||||
}
|
||||
if let Some(path) = item.path_opt()
|
||||
&& paths.contains(path)
|
||||
{
|
||||
item.selected = true;
|
||||
self.select_focus = Some(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3087,10 +3084,10 @@ impl Tab {
|
|||
return Vec::new();
|
||||
};
|
||||
|
||||
if let Some((w, h)) = original_dims {
|
||||
if !should_use_tiling(*w, *h) {
|
||||
return Vec::new();
|
||||
}
|
||||
if let Some((w, h)) = original_dims
|
||||
&& !should_use_tiling(*w, *h)
|
||||
{
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let Some(path) = item.path_opt() else {
|
||||
|
|
@ -3391,15 +3388,13 @@ impl Tab {
|
|||
self.date_time_formatter = date_time_formatter(self.config.military_time);
|
||||
self.time_formatter = time_formatter(self.config.military_time);
|
||||
}
|
||||
if show_hidden_changed {
|
||||
if let Location::Search(path, term, ..) = &self.location {
|
||||
cd = Some(Location::Search(
|
||||
path.clone(),
|
||||
term.clone(),
|
||||
self.config.show_hidden,
|
||||
Instant::now(),
|
||||
));
|
||||
}
|
||||
if show_hidden_changed && let Location::Search(path, term, ..) = &self.location {
|
||||
cd = Some(Location::Search(
|
||||
path.clone(),
|
||||
term.clone(),
|
||||
self.config.show_hidden,
|
||||
Instant::now(),
|
||||
));
|
||||
}
|
||||
// Unhighlight all items when config changes
|
||||
if let Some(ref mut items) = self.items_opt {
|
||||
|
|
@ -3420,11 +3415,12 @@ impl Tab {
|
|||
self.location_context_menu_index = None;
|
||||
|
||||
//TODO: hack for clearing selecting when right clicking empty space
|
||||
if self.context_menu.is_some() && self.last_right_click.take().is_none() {
|
||||
if let Some(ref mut items) = self.items_opt {
|
||||
for item in items.iter_mut() {
|
||||
item.selected = false;
|
||||
}
|
||||
if self.context_menu.is_some()
|
||||
&& self.last_right_click.take().is_none()
|
||||
&& let Some(ref mut items) = self.items_opt
|
||||
{
|
||||
for item in items.iter_mut() {
|
||||
item.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3512,11 +3508,11 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
Message::EditLocationComplete(selected) => {
|
||||
if let Some(mut edit_location) = self.edit_location.take() {
|
||||
if !matches!(edit_location.location, Location::Network(..)) {
|
||||
edit_location.selected = Some(selected);
|
||||
cd = edit_location.resolve();
|
||||
}
|
||||
if let Some(mut edit_location) = self.edit_location.take()
|
||||
&& !matches!(edit_location.location, Location::Network(..))
|
||||
{
|
||||
edit_location.selected = Some(selected);
|
||||
cd = edit_location.resolve();
|
||||
}
|
||||
}
|
||||
Message::EditLocationEnable => {
|
||||
|
|
@ -3532,11 +3528,11 @@ impl Tab {
|
|||
&& edit_location
|
||||
.completions
|
||||
.as_ref()
|
||||
.map_or(false, |completions| !completions.is_empty())
|
||||
.is_some_and(|completions| !completions.is_empty())
|
||||
&& edit_location
|
||||
.location
|
||||
.path_opt()
|
||||
.map_or(false, |path| !path.exists())
|
||||
.is_some_and(|path| !path.exists())
|
||||
{
|
||||
edit_location.selected = Some(0);
|
||||
}
|
||||
|
|
@ -3634,19 +3630,19 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
Message::GoNext => {
|
||||
if let Some(history_i) = self.history_i.checked_add(1) {
|
||||
if let Some(location) = self.history.get(history_i) {
|
||||
cd = Some(location.clone());
|
||||
history_i_opt = Some(history_i);
|
||||
}
|
||||
if let Some(history_i) = self.history_i.checked_add(1)
|
||||
&& let Some(location) = self.history.get(history_i)
|
||||
{
|
||||
cd = Some(location.clone());
|
||||
history_i_opt = Some(history_i);
|
||||
}
|
||||
}
|
||||
Message::GoPrevious => {
|
||||
if let Some(history_i) = self.history_i.checked_sub(1) {
|
||||
if let Some(location) = self.history.get(history_i) {
|
||||
cd = Some(location.clone());
|
||||
history_i_opt = Some(history_i);
|
||||
}
|
||||
if let Some(history_i) = self.history_i.checked_sub(1)
|
||||
&& let Some(location) = self.history.get(history_i)
|
||||
{
|
||||
cd = Some(location.clone());
|
||||
history_i_opt = Some(history_i);
|
||||
}
|
||||
}
|
||||
Message::ItemDown => {
|
||||
|
|
@ -3825,10 +3821,10 @@ impl Tab {
|
|||
Message::LocationUp => {
|
||||
// Sets location to the path's parent
|
||||
// Does nothing if path is root or location is Trash
|
||||
if let Location::Path(ref path) = self.location {
|
||||
if let Some(parent) = path.parent() {
|
||||
cd = Some(Location::Path(parent.to_owned()));
|
||||
}
|
||||
if let Location::Path(ref path) = self.location
|
||||
&& let Some(parent) = path.parent()
|
||||
{
|
||||
cd = Some(Location::Path(parent.to_owned()));
|
||||
}
|
||||
}
|
||||
Message::Open(path_opt) => {
|
||||
|
|
@ -3870,27 +3866,25 @@ impl Tab {
|
|||
match mode {
|
||||
Mode::App => {
|
||||
if is_only_one_selected {
|
||||
return ResolveResult::Cd(location.clone());
|
||||
ResolveResult::Cd(location.clone())
|
||||
} else {
|
||||
return ResolveResult::OpenInTab(path_opt.cloned());
|
||||
ResolveResult::OpenInTab(path_opt.cloned())
|
||||
}
|
||||
}
|
||||
Mode::Desktop => {
|
||||
return match location {
|
||||
Location::Trash => ResolveResult::OpenTrash,
|
||||
_ => ResolveResult::Open(path_opt.cloned()),
|
||||
};
|
||||
}
|
||||
Mode::Desktop => match location {
|
||||
Location::Trash => ResolveResult::OpenTrash,
|
||||
_ => ResolveResult::Open(path_opt.cloned()),
|
||||
},
|
||||
Mode::Dialog(_) => {
|
||||
if is_only_one_selected {
|
||||
return ResolveResult::Cd(location.clone());
|
||||
ResolveResult::Cd(location.clone())
|
||||
} else {
|
||||
return ResolveResult::Skip;
|
||||
ResolveResult::Skip
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ResolveResult::Open(path_opt.cloned());
|
||||
ResolveResult::Open(path_opt.cloned())
|
||||
}
|
||||
}
|
||||
let mut open_files = Vec::new();
|
||||
|
|
@ -3935,14 +3929,13 @@ impl Tab {
|
|||
if mod_ctrl || mod_shift {
|
||||
self.update(Message::Click(click_i_opt), modifiers);
|
||||
}
|
||||
if let Some(ref mut items) = self.items_opt {
|
||||
if !click_i_opt
|
||||
if let Some(ref mut items) = self.items_opt
|
||||
&& !click_i_opt
|
||||
.is_some_and(|click_i| items.get(click_i).is_some_and(|x| x.selected))
|
||||
{
|
||||
// If item not selected, clear selection on other items
|
||||
for (i, item) in items.iter_mut().enumerate() {
|
||||
item.selected = Some(i) == click_i_opt;
|
||||
}
|
||||
{
|
||||
// If item not selected, clear selection on other items
|
||||
for (i, item) in items.iter_mut().enumerate() {
|
||||
item.selected = Some(i) == click_i_opt;
|
||||
}
|
||||
}
|
||||
//TODO: hack for clearing selecting when right clicking empty space
|
||||
|
|
@ -3990,12 +3983,12 @@ impl Tab {
|
|||
}
|
||||
Message::Resize(viewport) => {
|
||||
// Scroll to ensure focused item still in view
|
||||
if self.viewport_opt.map(|v| v.size()) != Some(viewport.size()) {
|
||||
if let Some(offset) = self.select_focus_scroll() {
|
||||
commands.push(Command::Iced(
|
||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
||||
));
|
||||
}
|
||||
if self.viewport_opt.map(|v| v.size()) != Some(viewport.size())
|
||||
&& let Some(offset) = self.select_focus_scroll()
|
||||
{
|
||||
commands.push(Command::Iced(
|
||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
||||
));
|
||||
}
|
||||
|
||||
self.viewport_opt = Some(viewport);
|
||||
|
|
@ -4099,20 +4092,17 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
Message::SelectLast => {
|
||||
if let Some(ref items) = self.items_opt {
|
||||
if let Some(last_pos) = items.iter().filter_map(|item| item.pos_opt.get()).max()
|
||||
{
|
||||
if self.select_position(last_pos.0, last_pos.1, mod_shift) {
|
||||
if let Some(offset) = self.select_focus_scroll() {
|
||||
commands.push(Command::Iced(
|
||||
scrollable::scroll_to(self.scrollable_id.clone(), offset)
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
if let Some(id) = self.select_focus_id() {
|
||||
commands.push(Command::Iced(widget::button::focus(id).into()));
|
||||
}
|
||||
}
|
||||
if let Some(ref items) = self.items_opt
|
||||
&& let Some(last_pos) = items.iter().filter_map(|item| item.pos_opt.get()).max()
|
||||
&& self.select_position(last_pos.0, last_pos.1, mod_shift)
|
||||
{
|
||||
if let Some(offset) = self.select_focus_scroll() {
|
||||
commands.push(Command::Iced(
|
||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
||||
));
|
||||
}
|
||||
if let Some(id) = self.select_focus_id() {
|
||||
commands.push(Command::Iced(widget::button::focus(id).into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4136,13 +4126,13 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
Message::TabComplete(path, completions) => {
|
||||
if let Some(edit_location) = &mut self.edit_location {
|
||||
if edit_location.location.path_opt() == Some(&path) {
|
||||
edit_location.completions = Some(completions);
|
||||
commands.push(Command::Iced(
|
||||
widget::text_input::focus(self.edit_location_id.clone()).into(),
|
||||
));
|
||||
}
|
||||
if let Some(edit_location) = &mut self.edit_location
|
||||
&& edit_location.location.path_opt() == Some(&path)
|
||||
{
|
||||
edit_location.completions = Some(completions);
|
||||
commands.push(Command::Iced(
|
||||
widget::text_input::focus(self.edit_location_id.clone()).into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Message::Thumbnail(path, thumbnail) => {
|
||||
|
|
@ -4276,10 +4266,10 @@ impl Tab {
|
|||
}
|
||||
Message::DirectorySize(path, dir_size) => {
|
||||
let location = Location::Path(path);
|
||||
if let Some(ref mut item) = self.parent_item_opt {
|
||||
if item.location_opt.as_ref() == Some(&location) {
|
||||
item.dir_size.clone_from(&dir_size);
|
||||
}
|
||||
if let Some(ref mut item) = self.parent_item_opt
|
||||
&& item.location_opt.as_ref() == Some(&location)
|
||||
{
|
||||
item.dir_size.clone_from(&dir_size);
|
||||
}
|
||||
if let Some(ref mut items) = self.items_opt {
|
||||
for item in items.iter_mut() {
|
||||
|
|
@ -4317,13 +4307,12 @@ impl Tab {
|
|||
} else {
|
||||
// Select parent if location is not directory
|
||||
let mut selected_paths = None;
|
||||
if let Some(path) = location.path_opt() {
|
||||
if !path.is_dir() {
|
||||
if let Some(parent) = path.parent() {
|
||||
selected_paths = Some(vec![path.clone()]);
|
||||
location = location.with_path(parent.to_path_buf());
|
||||
}
|
||||
}
|
||||
if let Some(path) = location.path_opt()
|
||||
&& !path.is_dir()
|
||||
&& let Some(parent) = path.parent()
|
||||
{
|
||||
selected_paths = Some(vec![path.clone()]);
|
||||
location = location.with_path(parent.to_path_buf());
|
||||
}
|
||||
if location != self.location || selected_paths.is_some() {
|
||||
if location.path_opt().is_none_or(|path| path.is_dir()) {
|
||||
|
|
@ -4531,99 +4520,93 @@ impl Tab {
|
|||
//TODO: display error messages when image not found?
|
||||
let mut name_opt = None;
|
||||
let mut element_opt: Option<Element<Message>> = None;
|
||||
if let Some(index) = self.select_focus {
|
||||
if let Some(items) = &self.items_opt {
|
||||
if let Some(item) = items.get(index) {
|
||||
name_opt = Some(widget::text::heading(&item.display_name));
|
||||
match item
|
||||
.thumbnail_opt
|
||||
.as_ref()
|
||||
.unwrap_or(&ItemThumbnail::NotImage)
|
||||
{
|
||||
ItemThumbnail::NotImage => {}
|
||||
ItemThumbnail::Image(handle, original_dims) => {
|
||||
// Determine which image to show based on async decode state
|
||||
let mut is_loading = false;
|
||||
let mut error_msg_opt = None;
|
||||
let image_handle = if let Some(path) = item.path_opt() {
|
||||
if let Some(error_msg) = self.large_image_manager.get_error(path) {
|
||||
error_msg_opt = Some(error_msg.clone());
|
||||
handle.clone()
|
||||
} else if self.large_image_manager.is_decoding(path) {
|
||||
// Currently decoding (initial or re-decode) --> show cached/thumbnail with loading indicator
|
||||
is_loading = true;
|
||||
// Use decoded handle if available (re-decode), otherwise thumbnail (initial decode)
|
||||
self.large_image_manager
|
||||
.get_decoded(path)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| handle.clone())
|
||||
} else if let Some(decoded_handle) =
|
||||
self.large_image_manager.get_decoded(path)
|
||||
{
|
||||
// Decoded and not currently decoding --> use it
|
||||
decoded_handle.clone()
|
||||
} else if let Some((w, h)) = original_dims {
|
||||
// Check if image needs tiling
|
||||
if should_use_tiling(*w, *h) {
|
||||
// Large image --> show thumbnail only
|
||||
handle.clone()
|
||||
} else {
|
||||
// Normal-sized image --> load full resolution directly
|
||||
widget::image::Handle::from_path(path)
|
||||
}
|
||||
} else {
|
||||
// No dimensions available --> show thumbnail
|
||||
handle.clone()
|
||||
}
|
||||
} else {
|
||||
if let Some(index) = self.select_focus
|
||||
&& let Some(items) = &self.items_opt
|
||||
&& let Some(item) = items.get(index)
|
||||
{
|
||||
name_opt = Some(widget::text::heading(&item.display_name));
|
||||
match item
|
||||
.thumbnail_opt
|
||||
.as_ref()
|
||||
.unwrap_or(&ItemThumbnail::NotImage)
|
||||
{
|
||||
ItemThumbnail::NotImage => {}
|
||||
ItemThumbnail::Image(handle, original_dims) => {
|
||||
// Determine which image to show based on async decode state
|
||||
let mut is_loading = false;
|
||||
let mut error_msg_opt = None;
|
||||
let image_handle = if let Some(path) = item.path_opt() {
|
||||
if let Some(error_msg) = self.large_image_manager.get_error(path) {
|
||||
error_msg_opt = Some(error_msg.clone());
|
||||
handle.clone()
|
||||
} else if self.large_image_manager.is_decoding(path) {
|
||||
// Currently decoding (initial or re-decode) --> show cached/thumbnail with loading indicator
|
||||
is_loading = true;
|
||||
// Use decoded handle if available (re-decode), otherwise thumbnail (initial decode)
|
||||
self.large_image_manager
|
||||
.get_decoded(path)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| handle.clone())
|
||||
} else if let Some(decoded_handle) =
|
||||
self.large_image_manager.get_decoded(path)
|
||||
{
|
||||
// Decoded and not currently decoding --> use it
|
||||
decoded_handle.clone()
|
||||
} else if let Some((w, h)) = original_dims {
|
||||
// Check if image needs tiling
|
||||
if should_use_tiling(*w, *h) {
|
||||
// Large image --> show thumbnail only
|
||||
handle.clone()
|
||||
};
|
||||
} else {
|
||||
// Normal-sized image --> load full resolution directly
|
||||
widget::image::Handle::from_path(path)
|
||||
}
|
||||
} else {
|
||||
// No dimensions available --> show thumbnail
|
||||
handle.clone()
|
||||
}
|
||||
} else {
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
let content: cosmic::Element<'_, Message> =
|
||||
if let Some(error_msg) = error_msg_opt {
|
||||
widget::column()
|
||||
.push(widget::image(image_handle))
|
||||
.push(widget::text(format!("⚠ {}", error_msg)).size(13))
|
||||
.padding(space_xs)
|
||||
.align_x(cosmic::iced::Alignment::Center)
|
||||
.into()
|
||||
} else if is_loading {
|
||||
widget::column()
|
||||
.push(widget::image(image_handle))
|
||||
.push(widget::text("Loading higher resolution...").size(14))
|
||||
.padding(space_xs)
|
||||
.align_x(cosmic::iced::Alignment::Center)
|
||||
.into()
|
||||
} else {
|
||||
//TODO: use widget::image::viewer, when its zoom can be reset
|
||||
widget::image(image_handle).into()
|
||||
};
|
||||
let content: cosmic::Element<'_, Message> =
|
||||
if let Some(error_msg) = error_msg_opt {
|
||||
widget::column()
|
||||
.push(widget::image(image_handle))
|
||||
.push(widget::text(format!("⚠ {}", error_msg)).size(13))
|
||||
.padding(space_xs)
|
||||
.align_x(cosmic::iced::Alignment::Center)
|
||||
.into()
|
||||
} else if is_loading {
|
||||
widget::column()
|
||||
.push(widget::image(image_handle))
|
||||
.push(widget::text("Loading higher resolution...").size(14))
|
||||
.padding(space_xs)
|
||||
.align_x(cosmic::iced::Alignment::Center)
|
||||
.into()
|
||||
} else {
|
||||
//TODO: use widget::image::viewer, when its zoom can be reset
|
||||
widget::image(image_handle).into()
|
||||
};
|
||||
|
||||
element_opt =
|
||||
Some(widget::container(content).center(Length::Fill).into());
|
||||
}
|
||||
ItemThumbnail::Svg(handle) => {
|
||||
element_opt = Some(
|
||||
widget::svg(handle.clone())
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
ItemThumbnail::Text(text) => {
|
||||
element_opt = Some(
|
||||
widget::container(
|
||||
widget::text_editor(text).padding(space_xxs).class(
|
||||
cosmic::theme::iced::TextEditor::Custom(Box::new(
|
||||
text_editor_class,
|
||||
)),
|
||||
),
|
||||
)
|
||||
.center(Length::Fill)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
element_opt = Some(widget::container(content).center(Length::Fill).into());
|
||||
}
|
||||
ItemThumbnail::Svg(handle) => {
|
||||
element_opt = Some(
|
||||
widget::svg(handle.clone())
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
ItemThumbnail::Text(text) => {
|
||||
element_opt = Some(
|
||||
widget::container(widget::text_editor(text).padding(space_xxs).class(
|
||||
cosmic::theme::iced::TextEditor::Custom(Box::new(text_editor_class)),
|
||||
))
|
||||
.center(Length::Fill)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4862,32 +4845,32 @@ impl Tab {
|
|||
);
|
||||
let mut popover =
|
||||
widget::popover(text_input).position(widget::popover::Position::Bottom);
|
||||
if let Some(completions) = &edit_location.completions {
|
||||
if !completions.is_empty() {
|
||||
let mut column =
|
||||
widget::column::with_capacity(completions.len()).padding(space_xxs);
|
||||
for (i, (name, _path)) in completions.iter().enumerate() {
|
||||
let selected = edit_location.selected == Some(i);
|
||||
column = column.push(
|
||||
widget::button::custom(widget::text::body(name))
|
||||
//TODO: match to design
|
||||
.class(if selected {
|
||||
theme::Button::Standard
|
||||
} else {
|
||||
theme::Button::HeaderBar
|
||||
})
|
||||
.on_press(Message::EditLocationComplete(i))
|
||||
.padding(space_xxs)
|
||||
.width(Length::Fill),
|
||||
);
|
||||
}
|
||||
popover = popover.popup(
|
||||
widget::container(column)
|
||||
.class(theme::Container::Dropdown)
|
||||
//TODO: This is a hack to get the popover to be the right width
|
||||
.max_width(size.width - 140.0),
|
||||
if let Some(completions) = &edit_location.completions
|
||||
&& !completions.is_empty()
|
||||
{
|
||||
let mut column =
|
||||
widget::column::with_capacity(completions.len()).padding(space_xxs);
|
||||
for (i, (name, _path)) in completions.iter().enumerate() {
|
||||
let selected = edit_location.selected == Some(i);
|
||||
column = column.push(
|
||||
widget::button::custom(widget::text::body(name))
|
||||
//TODO: match to design
|
||||
.class(if selected {
|
||||
theme::Button::Standard
|
||||
} else {
|
||||
theme::Button::HeaderBar
|
||||
})
|
||||
.on_press(Message::EditLocationComplete(i))
|
||||
.padding(space_xxs)
|
||||
.width(Length::Fill),
|
||||
);
|
||||
}
|
||||
popover = popover.popup(
|
||||
widget::container(column)
|
||||
.class(theme::Container::Dropdown)
|
||||
//TODO: This is a hack to get the popover to be the right width
|
||||
.max_width(size.width - 140.0),
|
||||
);
|
||||
}
|
||||
row = row.push(popover);
|
||||
let mut column = widget::column::with_capacity(4).padding([0, space_s]);
|
||||
|
|
@ -5913,13 +5896,13 @@ impl Tab {
|
|||
.wayland_on_right_press_window_position();
|
||||
|
||||
let mut popover = widget::popover(mouse_area);
|
||||
if let Some(point) = self.context_menu {
|
||||
if !cfg!(feature = "wayland") || !crate::is_wayland() {
|
||||
let context_menu = menu::context_menu(self, key_binds, &modifiers);
|
||||
popover = popover
|
||||
.popup(context_menu)
|
||||
.position(widget::popover::Position::Point(point));
|
||||
}
|
||||
if let Some(point) = self.context_menu
|
||||
&& (!cfg!(feature = "wayland") || !crate::is_wayland())
|
||||
{
|
||||
let context_menu = menu::context_menu(self, key_binds, modifiers);
|
||||
popover = popover
|
||||
.popup(context_menu)
|
||||
.position(widget::popover::Position::Point(point));
|
||||
}
|
||||
|
||||
let mut tab_column = widget::column::with_capacity(3);
|
||||
|
|
@ -5939,21 +5922,21 @@ impl Tab {
|
|||
}
|
||||
match &self.location {
|
||||
Location::Trash => {
|
||||
if let Some(items) = self.items_opt() {
|
||||
if !items.is_empty() {
|
||||
tab_column = tab_column.push(
|
||||
widget::layer_container(widget::row::with_children([
|
||||
widget::horizontal_space().into(),
|
||||
widget::button::standard(fl!("empty-trash"))
|
||||
.on_press(Message::EmptyTrash)
|
||||
.into(),
|
||||
]))
|
||||
.padding([space_xxs, space_xs])
|
||||
.layer(cosmic_theme::Layer::Primary)
|
||||
.apply(widget::container)
|
||||
.padding([0, 0, 7, 0]),
|
||||
);
|
||||
}
|
||||
if let Some(items) = self.items_opt()
|
||||
&& !items.is_empty()
|
||||
{
|
||||
tab_column = tab_column.push(
|
||||
widget::layer_container(widget::row::with_children([
|
||||
widget::horizontal_space().into(),
|
||||
widget::button::standard(fl!("empty-trash"))
|
||||
.on_press(Message::EmptyTrash)
|
||||
.into(),
|
||||
]))
|
||||
.padding([space_xxs, space_xs])
|
||||
.layer(cosmic_theme::Layer::Primary)
|
||||
.apply(widget::container)
|
||||
.padding([0, 0, 7, 0]),
|
||||
);
|
||||
}
|
||||
}
|
||||
Location::Network(uri, _display_name, _path) if uri == "network:///" => {
|
||||
|
|
@ -6250,10 +6233,10 @@ impl Tab {
|
|||
let mut selected_items: Vec<&Item> =
|
||||
items.iter().filter(|item| item.selected).collect();
|
||||
|
||||
if selected_items.is_empty() {
|
||||
if let Some(p) = self.parent_item_opt.as_ref() {
|
||||
selected_items.push(p)
|
||||
}
|
||||
if selected_items.is_empty()
|
||||
&& let Some(p) = self.parent_item_opt.as_ref()
|
||||
{
|
||||
selected_items.push(p)
|
||||
}
|
||||
for item in selected_items {
|
||||
// Item must have a path
|
||||
|
|
|
|||
|
|
@ -66,10 +66,10 @@ impl ThumbnailCacher {
|
|||
if let (Some(cache_base_dir), Ok(metadata)) = (
|
||||
THUMBNAIL_CACHE_BASE_DIR.as_ref(),
|
||||
std::fs::metadata(&self.file_path),
|
||||
) {
|
||||
if metadata.is_file() && self.file_path.starts_with(cache_base_dir) {
|
||||
return CachedThumbnail::Valid((self.file_path.clone(), None));
|
||||
}
|
||||
) && metadata.is_file()
|
||||
&& self.file_path.starts_with(cache_base_dir)
|
||||
{
|
||||
return CachedThumbnail::Valid((self.file_path.clone(), None));
|
||||
}
|
||||
|
||||
// Use cached thumbnail if it is valid.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue