Show parent folder in details, part of #541
This commit is contained in:
parent
a5dfffcd72
commit
2b4a14dfe2
3 changed files with 120 additions and 69 deletions
32
src/app.rs
32
src/app.rs
|
|
@ -314,7 +314,13 @@ pub enum Message {
|
||||||
TabConfig(TabConfig),
|
TabConfig(TabConfig),
|
||||||
TabMessage(Option<Entity>, tab::Message),
|
TabMessage(Option<Entity>, tab::Message),
|
||||||
TabNew,
|
TabNew,
|
||||||
TabRescan(Entity, Location, Vec<tab::Item>, Option<PathBuf>),
|
TabRescan(
|
||||||
|
Entity,
|
||||||
|
Location,
|
||||||
|
Option<tab::Item>,
|
||||||
|
Vec<tab::Item>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
),
|
||||||
TabView(Option<Entity>, tab::View),
|
TabView(Option<Entity>, tab::View),
|
||||||
ToggleContextPage(ContextPage),
|
ToggleContextPage(ContextPage),
|
||||||
ToggleFoldersFirst,
|
ToggleFoldersFirst,
|
||||||
|
|
@ -587,9 +593,13 @@ impl App {
|
||||||
async move {
|
async move {
|
||||||
let location2 = location.clone();
|
let location2 = location.clone();
|
||||||
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
||||||
Ok(items) => {
|
Ok((parent_item_opt, items)) => message::app(Message::TabRescan(
|
||||||
message::app(Message::TabRescan(entity, location, items, selection_path))
|
entity,
|
||||||
}
|
location,
|
||||||
|
parent_item_opt,
|
||||||
|
items,
|
||||||
|
selection_path,
|
||||||
|
)),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("failed to rescan: {}", err);
|
log::warn!("failed to rescan: {}", err);
|
||||||
message::none()
|
message::none()
|
||||||
|
|
@ -1148,6 +1158,12 @@ impl App {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if children.is_empty() {
|
||||||
|
if let Some(item) = &tab.parent_item_opt {
|
||||||
|
children
|
||||||
|
.push(item.preview_view(tab.config.icon_sizes, context_drawer));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2609,10 +2625,11 @@ impl Application for App {
|
||||||
};
|
};
|
||||||
return self.open_tab(location, true, None);
|
return self.open_tab(location, true, None);
|
||||||
}
|
}
|
||||||
Message::TabRescan(entity, location, items, selection_path) => {
|
Message::TabRescan(entity, location, parent_item_opt, items, selection_path) => {
|
||||||
match self.tab_model.data_mut::<Tab>(entity) {
|
match self.tab_model.data_mut::<Tab>(entity) {
|
||||||
Some(tab) => {
|
Some(tab) => {
|
||||||
if location == tab.location {
|
if location == tab.location {
|
||||||
|
tab.parent_item_opt = parent_item_opt;
|
||||||
tab.set_items(items);
|
tab.set_items(items);
|
||||||
if let Some(selection_path) = selection_path {
|
if let Some(selection_path) = selection_path {
|
||||||
tab.select_path(selection_path);
|
tab.select_path(selection_path);
|
||||||
|
|
@ -2654,7 +2671,7 @@ impl Application for App {
|
||||||
match tokio::task::spawn_blocking(move || Location::Trash.scan(icon_sizes))
|
match tokio::task::spawn_blocking(move || Location::Trash.scan(icon_sizes))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(items) => {
|
Ok((_parent_item_opt, items)) => {
|
||||||
for path in &*recently_trashed {
|
for path in &*recently_trashed {
|
||||||
for item in &items {
|
for item in &items {
|
||||||
if let ItemMetadata::Trash { ref entry, .. } = item.metadata {
|
if let ItemMetadata::Trash { ref entry, .. } = item.metadata {
|
||||||
|
|
@ -4243,8 +4260,9 @@ pub(crate) mod test_utils {
|
||||||
|
|
||||||
// New tab with items
|
// New tab with items
|
||||||
let location = Location::Path(path.to_owned());
|
let location = Location::Path(path.to_owned());
|
||||||
let items = location.scan(IconSizes::default());
|
let (parent_item_opt, items) = location.scan(IconSizes::default());
|
||||||
let mut tab = Tab::new(location, TabConfig::default());
|
let mut tab = Tab::new(location, TabConfig::default());
|
||||||
|
tab.parent_item_opt = parent_item_opt;
|
||||||
tab.set_items(items);
|
tab.set_items(items);
|
||||||
|
|
||||||
// Ensure correct number of directories as a sanity check
|
// Ensure correct number of directories as a sanity check
|
||||||
|
|
|
||||||
137
src/dialog.rs
137
src/dialog.rs
|
|
@ -325,7 +325,7 @@ enum Message {
|
||||||
SearchClear,
|
SearchClear,
|
||||||
SearchInput(String),
|
SearchInput(String),
|
||||||
TabMessage(tab::Message),
|
TabMessage(tab::Message),
|
||||||
TabRescan(Vec<tab::Item>),
|
TabRescan(Location, Option<tab::Item>, Vec<tab::Item>),
|
||||||
TabView(tab::View),
|
TabView(tab::View),
|
||||||
ToggleFoldersFirst,
|
ToggleFoldersFirst,
|
||||||
ZoomDefault,
|
ZoomDefault,
|
||||||
|
|
@ -494,6 +494,11 @@ impl App {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if children.is_empty() {
|
||||||
|
if let Some(item) = &self.tab.parent_item_opt {
|
||||||
|
children.push(item.preview_view(self.tab.config.icon_sizes, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -505,8 +510,11 @@ impl App {
|
||||||
let icon_sizes = self.tab.config.icon_sizes;
|
let icon_sizes = self.tab.config.icon_sizes;
|
||||||
Command::perform(
|
Command::perform(
|
||||||
async move {
|
async move {
|
||||||
match tokio::task::spawn_blocking(move || location.scan(icon_sizes)).await {
|
let location2 = location.clone();
|
||||||
Ok(items) => message::app(Message::TabRescan(items)),
|
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
||||||
|
Ok((parent_item_opt, items)) => {
|
||||||
|
message::app(Message::TabRescan(location, parent_item_opt, items))
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("failed to rescan: {}", err);
|
log::warn!("failed to rescan: {}", err);
|
||||||
message::none()
|
message::none()
|
||||||
|
|
@ -1408,74 +1416,85 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
return Command::batch(commands);
|
return Command::batch(commands);
|
||||||
}
|
}
|
||||||
Message::TabRescan(mut items) => {
|
Message::TabRescan(location, parent_item_opt, mut items) => {
|
||||||
// Filter
|
if location == self.tab.location {
|
||||||
if let Some(filter_i) = self.filter_selected {
|
// Filter
|
||||||
if let Some(filter) = self.filters.get(filter_i) {
|
if let Some(filter_i) = self.filter_selected {
|
||||||
// Parse filters
|
if let Some(filter) = self.filters.get(filter_i) {
|
||||||
let mut parsed_globs = Vec::new();
|
// Parse filters
|
||||||
let mut parsed_mimes = Vec::new();
|
let mut parsed_globs = Vec::new();
|
||||||
for pattern in filter.patterns.iter() {
|
let mut parsed_mimes = Vec::new();
|
||||||
match pattern {
|
for pattern in filter.patterns.iter() {
|
||||||
DialogFilterPattern::Glob(value) => {
|
match pattern {
|
||||||
match glob::Pattern::new(value) {
|
DialogFilterPattern::Glob(value) => {
|
||||||
Ok(glob) => parsed_globs.push(glob),
|
match glob::Pattern::new(value) {
|
||||||
Err(err) => {
|
Ok(glob) => parsed_globs.push(glob),
|
||||||
log::warn!("failed to parse glob {:?}: {}", value, err);
|
Err(err) => {
|
||||||
|
log::warn!(
|
||||||
|
"failed to parse glob {:?}: {}",
|
||||||
|
value,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
DialogFilterPattern::Mime(value) => {
|
||||||
DialogFilterPattern::Mime(value) => {
|
match mime_guess::Mime::from_str(value) {
|
||||||
match mime_guess::Mime::from_str(value) {
|
Ok(mime) => parsed_mimes.push(mime),
|
||||||
Ok(mime) => parsed_mimes.push(mime),
|
Err(err) => {
|
||||||
Err(err) => {
|
log::warn!(
|
||||||
log::warn!("failed to parse mime {:?}: {}", value, err);
|
"failed to parse mime {:?}: {}",
|
||||||
|
value,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items.retain(|item| {
|
||||||
|
if item.metadata.is_dir() {
|
||||||
|
// Directories are always shown
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for mime type match (first because it is faster)
|
||||||
|
for mime in parsed_mimes.iter() {
|
||||||
|
if mime == &item.mime {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for glob match (last because it is slower)
|
||||||
|
for glob in parsed_globs.iter() {
|
||||||
|
if glob.matches(&item.name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No filters matched
|
||||||
|
false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
items.retain(|item| {
|
|
||||||
if item.metadata.is_dir() {
|
|
||||||
// Directories are always shown
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for mime type match (first because it is faster)
|
|
||||||
for mime in parsed_mimes.iter() {
|
|
||||||
if mime == &item.mime {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for glob match (last because it is slower)
|
|
||||||
for glob in parsed_globs.iter() {
|
|
||||||
if glob.matches(&item.name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No filters matched
|
|
||||||
false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Select based on filename
|
// Select based on filename
|
||||||
if let DialogKind::SaveFile { filename } = &self.flags.kind {
|
if let DialogKind::SaveFile { filename } = &self.flags.kind {
|
||||||
for item in items.iter_mut() {
|
for item in items.iter_mut() {
|
||||||
item.selected = &item.name == filename;
|
item.selected = &item.name == filename;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.tab.set_items(items);
|
self.tab.parent_item_opt = parent_item_opt;
|
||||||
|
self.tab.set_items(items);
|
||||||
|
|
||||||
// Reset focus on location change
|
// Reset focus on location change
|
||||||
if self.search_get().is_some() {
|
if self.search_get().is_some() {
|
||||||
return widget::text_input::focus(self.search_id.clone());
|
return widget::text_input::focus(self.search_id.clone());
|
||||||
} else {
|
} else {
|
||||||
return widget::text_input::focus(self.filename_id.clone());
|
return widget::text_input::focus(self.filename_id.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::TabView(view) => {
|
Message::TabView(view) => {
|
||||||
|
|
|
||||||
20
src/tab.rs
20
src/tab.rs
|
|
@ -909,8 +909,8 @@ impl Location {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scan(&self, sizes: IconSizes) -> Vec<Item> {
|
pub fn scan(&self, sizes: IconSizes) -> (Option<Item>, Vec<Item>) {
|
||||||
match self {
|
let items = match self {
|
||||||
Self::Desktop(path, display, desktop_config) => {
|
Self::Desktop(path, display, desktop_config) => {
|
||||||
scan_desktop(path, display, *desktop_config, sizes)
|
scan_desktop(path, display, *desktop_config, sizes)
|
||||||
}
|
}
|
||||||
|
|
@ -922,7 +922,19 @@ impl Location {
|
||||||
Self::Trash => scan_trash(sizes),
|
Self::Trash => scan_trash(sizes),
|
||||||
Self::Recents => scan_recents(sizes),
|
Self::Recents => scan_recents(sizes),
|
||||||
Self::Network(uri, _) => scan_network(uri, sizes),
|
Self::Network(uri, _) => scan_network(uri, sizes),
|
||||||
}
|
};
|
||||||
|
let parent_item_opt = match self.path_opt() {
|
||||||
|
Some(path) => match item_from_path(path, sizes) {
|
||||||
|
Ok(item) => Some(item),
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("failed to get item for {:?}: {}", path, err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//TODO: support other locations?
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
(parent_item_opt, items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1535,6 +1547,7 @@ pub struct Tab {
|
||||||
pub sort_name: HeadingOptions,
|
pub sort_name: HeadingOptions,
|
||||||
pub sort_direction: bool,
|
pub sort_direction: bool,
|
||||||
pub gallery: bool,
|
pub gallery: bool,
|
||||||
|
pub(crate) parent_item_opt: Option<Item>,
|
||||||
pub(crate) items_opt: Option<Vec<Item>>,
|
pub(crate) items_opt: Option<Vec<Item>>,
|
||||||
pub dnd_hovered: Option<(Location, Instant)>,
|
pub dnd_hovered: Option<(Location, Instant)>,
|
||||||
scrollable_id: widget::Id,
|
scrollable_id: widget::Id,
|
||||||
|
|
@ -1607,6 +1620,7 @@ impl Tab {
|
||||||
sort_name: HeadingOptions::Name,
|
sort_name: HeadingOptions::Name,
|
||||||
sort_direction: true,
|
sort_direction: true,
|
||||||
gallery: false,
|
gallery: false,
|
||||||
|
parent_item_opt: None,
|
||||||
items_opt: None,
|
items_opt: None,
|
||||||
scrollable_id: widget::Id::unique(),
|
scrollable_id: widget::Id::unique(),
|
||||||
select_focus: None,
|
select_focus: None,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue