WIP: support for network browsing
This commit is contained in:
parent
cd71c895f9
commit
c3d6498042
10 changed files with 251 additions and 85 deletions
31
examples/gio-list.rs
Normal file
31
examples/gio-list.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use gio::prelude::*;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let uri = env::args().nth(1).expect("no uri provided");
|
||||
let file = gio::File::for_uri(&uri);
|
||||
for entry_res in file
|
||||
.enumerate_children("*", gio::FileQueryInfoFlags::NONE, gio::Cancellable::NONE)
|
||||
.unwrap()
|
||||
{
|
||||
let entry = entry_res.unwrap();
|
||||
println!("{:?}", entry.display_name());
|
||||
for attribute in entry.list_attributes(None) {
|
||||
println!(
|
||||
" {:?}: {:?}",
|
||||
attribute,
|
||||
entry.attribute_as_string(&attribute)
|
||||
);
|
||||
}
|
||||
|
||||
//TODO: what is the best way to resolve shortcuts?
|
||||
let child = if let Some(target_uri) =
|
||||
entry.attribute_string(gio::FILE_ATTRIBUTE_STANDARD_TARGET_URI)
|
||||
{
|
||||
gio::File::for_uri(&target_uri)
|
||||
} else {
|
||||
file.child(entry.name())
|
||||
};
|
||||
println!("{:?}", child.uri());
|
||||
}
|
||||
}
|
||||
52
src/app.rs
52
src/app.rs
|
|
@ -521,11 +521,14 @@ impl App {
|
|||
location: Location,
|
||||
selection_path: Option<PathBuf>,
|
||||
) -> Command<Message> {
|
||||
let mounters = self.mounters.clone();
|
||||
let icon_sizes = self.config.tab.icon_sizes;
|
||||
Command::perform(
|
||||
async move {
|
||||
let location2 = location.clone();
|
||||
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
||||
match tokio::task::spawn_blocking(move || location2.scan(mounters, icon_sizes))
|
||||
.await
|
||||
{
|
||||
Ok(items) => {
|
||||
message::app(Message::TabRescan(entity, location, items, selection_path))
|
||||
}
|
||||
|
|
@ -672,7 +675,10 @@ impl App {
|
|||
.size(16)
|
||||
.handle(),
|
||||
))
|
||||
.data(Location::Networks)
|
||||
.data(Location::Network(
|
||||
"network:///".to_string(),
|
||||
fl!("networks"),
|
||||
))
|
||||
.divider_above()
|
||||
});
|
||||
|
||||
|
|
@ -941,49 +947,35 @@ impl App {
|
|||
fn properties(&self, entity: Option<ContextItem>) -> Element<Message> {
|
||||
match entity {
|
||||
None => self.tab_properties(self.tab_model.active()),
|
||||
|
||||
Some(ContextItem::TabBar(entity)) => self.tab_properties(entity),
|
||||
|
||||
Some(ContextItem::NavBar(item)) => {
|
||||
let mut children = Vec::new();
|
||||
|
||||
let mut children = Vec::with_capacity(1);
|
||||
if let Some(location) = self.nav_model.data::<Location>(item) {
|
||||
if let Location::Path(path) = location {
|
||||
let parent = path.parent().unwrap_or(path);
|
||||
|
||||
for item in Location::Path(parent.to_owned()).scan(IconSizes::default()) {
|
||||
if item.path_opt() == Some(path) {
|
||||
//TODO: this should be done once, not when generating the view!
|
||||
if let Ok(item) = tab::item_from_path(path, self.config.tab.icon_sizes) {
|
||||
children.push(item.property_view(IconSizes::default()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
widget::settings::view_column(children).into()
|
||||
}
|
||||
|
||||
Some(ContextItem::BreadCrumbs(index)) => {
|
||||
let mut children = Vec::new();
|
||||
|
||||
let mut children = Vec::with_capacity(1);
|
||||
if let Some(tab) = self.tab_model.active_data::<Tab>() {
|
||||
let path = match tab.location {
|
||||
Location::Path(ref path) => Some(path),
|
||||
Location::Search(ref path, _) => Some(path),
|
||||
_ => None,
|
||||
}
|
||||
let path_opt = tab
|
||||
.location
|
||||
.path_opt()
|
||||
.and_then(|path| path.ancestors().nth(index))
|
||||
.map(|path| path.to_path_buf());
|
||||
if let Some(ref path) = path {
|
||||
let parent = path.parent().unwrap_or(path);
|
||||
|
||||
for item in Location::Path(parent.to_owned()).scan(IconSizes::default()) {
|
||||
if item.path_opt() == Some(path) {
|
||||
if let Some(ref path) = path_opt {
|
||||
//TODO: this should be done once, not when generating the view!
|
||||
if let Ok(item) = tab::item_from_path(path, self.config.tab.icon_sizes) {
|
||||
children.push(item.property_view(IconSizes::default()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
widget::settings::view_column(children).into()
|
||||
}
|
||||
}
|
||||
|
|
@ -2396,10 +2388,13 @@ impl Application for App {
|
|||
self.toasts.remove(id);
|
||||
|
||||
let mut paths = Vec::with_capacity(recently_trashed.len());
|
||||
let mounters = self.mounters.clone();
|
||||
let icon_sizes = self.config.tab.icon_sizes;
|
||||
|
||||
return cosmic::command::future(async move {
|
||||
match tokio::task::spawn_blocking(move || Location::Trash.scan(icon_sizes))
|
||||
match tokio::task::spawn_blocking(move || {
|
||||
Location::Trash.scan(mounters, icon_sizes)
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(items) => {
|
||||
|
|
@ -3549,6 +3544,7 @@ pub(crate) mod test_utils {
|
|||
|
||||
use crate::{
|
||||
config::{IconSizes, TabConfig},
|
||||
mounter::MounterMap,
|
||||
tab::Item,
|
||||
};
|
||||
|
||||
|
|
@ -3711,7 +3707,7 @@ pub(crate) mod test_utils {
|
|||
|
||||
// New tab with items
|
||||
let location = Location::Path(path.to_owned());
|
||||
let items = location.scan(IconSizes::default());
|
||||
let items = location.scan(Mounters::new(MounterMap::new()), IconSizes::default());
|
||||
let mut tab = Tab::new(location, TabConfig::default());
|
||||
tab.set_items(items);
|
||||
|
||||
|
|
|
|||
|
|
@ -376,10 +376,12 @@ struct App {
|
|||
impl App {
|
||||
fn rescan_tab(&self) -> Command<Message> {
|
||||
let location = self.tab.location.clone();
|
||||
let mounters = self.mounters.clone();
|
||||
let icon_sizes = self.tab.config.icon_sizes;
|
||||
Command::perform(
|
||||
async move {
|
||||
match tokio::task::spawn_blocking(move || location.scan(icon_sizes)).await {
|
||||
match tokio::task::spawn_blocking(move || location.scan(mounters, icon_sizes)).await
|
||||
{
|
||||
Ok(items) => message::app(Message::TabRescan(items)),
|
||||
Err(err) => {
|
||||
log::warn!("failed to rescan: {}", err);
|
||||
|
|
@ -895,7 +897,7 @@ impl Application for App {
|
|||
}
|
||||
}
|
||||
}
|
||||
DialogPage::Replace { filename } => {
|
||||
DialogPage::Replace { .. } => {
|
||||
return self.update(Message::Save(true));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ mod spawn_detached;
|
|||
use tab::Location;
|
||||
pub mod tab;
|
||||
|
||||
pub(crate) fn err_str<T: ToString>(err: T) -> String {
|
||||
err.to_string()
|
||||
}
|
||||
|
||||
pub fn home_dir() -> PathBuf {
|
||||
match dirs::home_dir() {
|
||||
Some(home) => home,
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ pub fn context_menu<'a>(
|
|||
children.push(sort_item(fl!("sort-by-size"), HeadingOptions::Size));
|
||||
}
|
||||
}
|
||||
(_, Location::Networks) => {
|
||||
(_, Location::Network(_, _)) => {
|
||||
//TODO: networks context menu?
|
||||
}
|
||||
(_, Location::Trash) => {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,15 @@ use cosmic::{
|
|||
widget, Command,
|
||||
};
|
||||
use gio::{glib, prelude::*};
|
||||
use std::{any::TypeId, future::pending, path::PathBuf, sync::Arc};
|
||||
use std::{any::TypeId, cell::Cell, future::pending, path::PathBuf, sync::Arc};
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
|
||||
use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage};
|
||||
use crate::{
|
||||
config::IconSizes,
|
||||
err_str,
|
||||
tab::{self, ItemMetadata, ItemThumbnail, Location},
|
||||
};
|
||||
|
||||
fn gio_icon_to_path(icon: &gio::Icon, size: u16) -> Option<PathBuf> {
|
||||
if let Some(themed_icon) = icon.downcast_ref::<gio::ThemedIcon>() {
|
||||
|
|
@ -21,10 +26,97 @@ fn gio_icon_to_path(icon: &gio::Icon, size: u16) -> Option<PathBuf> {
|
|||
None
|
||||
}
|
||||
|
||||
fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||
let mut items = Vec::new();
|
||||
let file = gio::File::for_uri(&uri);
|
||||
for info_res in file
|
||||
.enumerate_children("*", gio::FileQueryInfoFlags::NONE, gio::Cancellable::NONE)
|
||||
.map_err(err_str)?
|
||||
{
|
||||
let info = info_res.map_err(err_str)?;
|
||||
println!("{:?}", info.display_name());
|
||||
for attribute in info.list_attributes(None) {
|
||||
println!(
|
||||
" {:?}: {:?}: {:?}",
|
||||
attribute,
|
||||
info.attribute_type(&attribute),
|
||||
info.attribute_as_string(&attribute)
|
||||
);
|
||||
}
|
||||
|
||||
let name = info.name().to_string_lossy().to_string();
|
||||
let display_name = info.display_name().to_string();
|
||||
|
||||
//TODO: what is the best way to resolve shortcuts?
|
||||
let location = Location::Network(
|
||||
if let Some(target_uri) = info.attribute_string(gio::FILE_ATTRIBUTE_STANDARD_TARGET_URI)
|
||||
{
|
||||
target_uri.to_string()
|
||||
} else {
|
||||
file.child(info.name()).uri().to_string()
|
||||
},
|
||||
display_name.clone(),
|
||||
);
|
||||
|
||||
//TODO: support dir or file
|
||||
let metadata = ItemMetadata::SimpleDir { entries: 0 };
|
||||
|
||||
let (mime, icon_handle_grid, icon_handle_list, icon_handle_list_condensed) = {
|
||||
let file_icon = |size| {
|
||||
info.icon()
|
||||
.as_ref()
|
||||
.and_then(|icon| gio_icon_to_path(icon, size))
|
||||
.map(|path| widget::icon::from_path(path))
|
||||
.unwrap_or(
|
||||
widget::icon::from_name(if metadata.is_dir() {
|
||||
"folder"
|
||||
} else {
|
||||
"text-x-generic"
|
||||
})
|
||||
.size(size)
|
||||
.handle(),
|
||||
)
|
||||
};
|
||||
(
|
||||
//TODO: get mime from content_type?
|
||||
"inode/directory".parse().unwrap(),
|
||||
file_icon(sizes.grid()),
|
||||
file_icon(sizes.list()),
|
||||
file_icon(sizes.list_condensed()),
|
||||
)
|
||||
};
|
||||
|
||||
items.push(tab::Item {
|
||||
name,
|
||||
display_name,
|
||||
metadata,
|
||||
hidden: false,
|
||||
location_opt: Some(location),
|
||||
mime,
|
||||
icon_handle_grid,
|
||||
icon_handle_list,
|
||||
icon_handle_list_condensed,
|
||||
open_with: Vec::new(),
|
||||
thumbnail_opt: Some(ItemThumbnail::NotImage),
|
||||
button_id: widget::Id::unique(),
|
||||
pos_opt: Cell::new(None),
|
||||
rect_opt: Cell::new(None),
|
||||
selected: false,
|
||||
overlaps_drag_rect: false,
|
||||
});
|
||||
}
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
enum Cmd {
|
||||
Rescan,
|
||||
Mount(MounterItem),
|
||||
NetworkDrive(String),
|
||||
NetworkScan(
|
||||
String,
|
||||
IconSizes,
|
||||
mpsc::Sender<Result<Vec<tab::Item>, String>>,
|
||||
),
|
||||
Unmount(MounterItem),
|
||||
}
|
||||
|
||||
|
|
@ -267,6 +359,9 @@ impl Gvfs {
|
|||
}
|
||||
);
|
||||
}
|
||||
Cmd::NetworkScan(uri, sizes, items_tx) => {
|
||||
items_tx.send(network_scan(&uri, sizes)).await.unwrap();
|
||||
}
|
||||
Cmd::Unmount(mounter_item) => {
|
||||
let MounterItem::Gvfs(item) = mounter_item else { continue };
|
||||
let ItemKind::Mount = item.kind else { continue };
|
||||
|
|
@ -330,6 +425,14 @@ impl Mounter for Gvfs {
|
|||
)
|
||||
}
|
||||
|
||||
fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>> {
|
||||
let (items_tx, mut items_rx) = mpsc::channel(1);
|
||||
self.command_tx
|
||||
.send(Cmd::NetworkScan(uri.to_string(), sizes, items_tx))
|
||||
.unwrap();
|
||||
items_rx.blocking_recv()
|
||||
}
|
||||
|
||||
fn unmount(&self, item: MounterItem) -> Command<()> {
|
||||
let command_tx = self.command_tx.clone();
|
||||
Command::perform(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use cosmic::{iced::subscription, widget, Command};
|
|||
use std::{collections::BTreeMap, fmt, path::PathBuf, sync::Arc};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{config::IconSizes, tab};
|
||||
|
||||
#[cfg(feature = "gvfs")]
|
||||
mod gvfs;
|
||||
|
||||
|
|
@ -86,10 +88,11 @@ pub enum MounterMessage {
|
|||
NetworkResult(String, Result<bool, String>),
|
||||
}
|
||||
|
||||
pub trait Mounter {
|
||||
pub trait Mounter: Send + Sync {
|
||||
//TODO: send result
|
||||
fn mount(&self, item: MounterItem) -> Command<()>;
|
||||
fn network_drive(&self, uri: String) -> Command<()>;
|
||||
fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>>;
|
||||
fn unmount(&self, item: MounterItem) -> Command<()>;
|
||||
fn subscription(&self) -> subscription::Subscription<MounterMessage>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,15 +15,11 @@ use walkdir::WalkDir;
|
|||
use crate::{
|
||||
app::{ArchiveType, DialogPage, Message},
|
||||
config::IconSizes,
|
||||
fl,
|
||||
err_str, fl,
|
||||
mime_icon::mime_for_path,
|
||||
tab,
|
||||
};
|
||||
|
||||
fn err_str<T: ToString>(err: T) -> String {
|
||||
err.to_string()
|
||||
}
|
||||
|
||||
fn handle_replace(
|
||||
msg_tx: &Arc<Mutex<Sender<Message>>>,
|
||||
file_from: PathBuf,
|
||||
|
|
|
|||
107
src/tab.rs
107
src/tab.rs
|
|
@ -63,6 +63,7 @@ use crate::{
|
|||
menu,
|
||||
mime_app::{mime_apps, MimeApp},
|
||||
mime_icon::{mime_for_path, mime_icon},
|
||||
mounter::Mounters,
|
||||
mouse_area,
|
||||
};
|
||||
use unix_permissions_ext::UNIXPermissionsExt;
|
||||
|
|
@ -561,7 +562,7 @@ pub fn scan_search(tab_path: &PathBuf, term: &str, sizes: IconSizes) -> Vec<Item
|
|||
items.par_sort_unstable_by(|a, b| {
|
||||
let get_modified = |x: &Item| match &x.metadata {
|
||||
ItemMetadata::Path { metadata, .. } => metadata.modified().ok(),
|
||||
ItemMetadata::Trash { .. } => None,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Sort with latest modified first
|
||||
|
|
@ -750,9 +751,17 @@ pub fn scan_recents(sizes: IconSizes) -> Vec<Item> {
|
|||
recents.into_iter().take(50).map(|(item, _)| item).collect()
|
||||
}
|
||||
|
||||
pub fn scan_networks(sizes: IconSizes) -> Vec<Item> {
|
||||
//TODO: network folder items
|
||||
vec![]
|
||||
pub fn scan_network(uri: &str, mounters: Mounters, sizes: IconSizes) -> Vec<Item> {
|
||||
for (key, mounter) in mounters.iter() {
|
||||
match mounter.network_scan(uri, sizes) {
|
||||
Some(Ok(items)) => return items,
|
||||
Some(Err(err)) => {
|
||||
log::warn!("failed to scan networks: {}", err);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
|
@ -761,7 +770,7 @@ pub enum Location {
|
|||
Search(PathBuf, String),
|
||||
Trash,
|
||||
Recents,
|
||||
Networks,
|
||||
Network(String, String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Location {
|
||||
|
|
@ -771,7 +780,7 @@ impl std::fmt::Display for Location {
|
|||
Self::Search(path, term) => write!(f, "search {} for {}", path.display(), term),
|
||||
Self::Trash => write!(f, "trash"),
|
||||
Self::Recents => write!(f, "recents"),
|
||||
Self::Networks => write!(f, "networks"),
|
||||
Self::Network(uri, _) => write!(f, "{}", uri),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -785,13 +794,13 @@ impl Location {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn scan(&self, sizes: IconSizes) -> Vec<Item> {
|
||||
pub fn scan(&self, mounters: Mounters, sizes: IconSizes) -> Vec<Item> {
|
||||
match self {
|
||||
Self::Path(path) => scan_path(path, sizes),
|
||||
Self::Search(path, term) => scan_search(path, term, sizes),
|
||||
Self::Trash => scan_trash(sizes),
|
||||
Self::Recents => scan_recents(sizes),
|
||||
Self::Networks => scan_networks(sizes),
|
||||
Self::Network(uri, _) => scan_network(uri, mounters, sizes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -881,6 +890,12 @@ pub enum ItemMetadata {
|
|||
metadata: trash::TrashItemMetadata,
|
||||
entry: trash::TrashItem,
|
||||
},
|
||||
SimpleDir {
|
||||
entries: u64,
|
||||
},
|
||||
SimpleFile {
|
||||
size: u64,
|
||||
},
|
||||
}
|
||||
|
||||
impl ItemMetadata {
|
||||
|
|
@ -891,6 +906,8 @@ impl ItemMetadata {
|
|||
trash::TrashItemSize::Entries(_) => true,
|
||||
trash::TrashItemSize::Bytes(_) => false,
|
||||
},
|
||||
Self::SimpleDir { .. } => true,
|
||||
Self::SimpleFile { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1087,8 +1104,8 @@ impl Item {
|
|||
);
|
||||
}
|
||||
}
|
||||
ItemMetadata::Trash { .. } => {
|
||||
//TODO: trash metadata
|
||||
_ => {
|
||||
//TODO: other metadata types
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1127,8 +1144,8 @@ impl Item {
|
|||
)));
|
||||
}
|
||||
}
|
||||
ItemMetadata::Trash { .. } => {
|
||||
//TODO: trash metadata
|
||||
_ => {
|
||||
//TODO: other metadata
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1278,9 +1295,7 @@ impl Tab {
|
|||
Location::Recents => {
|
||||
fl!("recents")
|
||||
}
|
||||
Location::Networks => {
|
||||
fl!("networks")
|
||||
}
|
||||
Location::Network(_uri, display_name) => display_name.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1592,15 +1607,19 @@ impl Tab {
|
|||
.as_ref()
|
||||
.and_then(|items| click_i_opt.and_then(|click_i| items.get(click_i)))
|
||||
{
|
||||
if let Some(Location::Path(path)) = &clicked_item.location_opt {
|
||||
if let Some(location) = &clicked_item.location_opt {
|
||||
if clicked_item.metadata.is_dir() {
|
||||
cd = Some(Location::Path(path.clone()));
|
||||
cd = Some(location.clone());
|
||||
} else {
|
||||
if let Location::Path(path) = location {
|
||||
commands.push(Command::OpenFile(path.clone()));
|
||||
}
|
||||
} else {
|
||||
log::warn!("no path for item {:?}", clicked_item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn!("no location for item {:?}", clicked_item);
|
||||
}
|
||||
} else {
|
||||
log::warn!("no item for click index {:?}", click_i_opt);
|
||||
}
|
||||
|
|
@ -1990,13 +2009,15 @@ impl Tab {
|
|||
if let Some(ref mut items) = self.items_opt {
|
||||
for item in items.iter() {
|
||||
if item.selected {
|
||||
if let Some(Location::Path(path)) = &item.location_opt {
|
||||
if path.is_dir() {
|
||||
if let Some(location) = &item.location_opt {
|
||||
if item.metadata.is_dir() {
|
||||
//TODO: allow opening multiple tabs?
|
||||
cd = Some(Location::Path(path.clone()));
|
||||
cd = Some(location.clone());
|
||||
} else {
|
||||
if let Location::Path(path) = location {
|
||||
commands.push(Command::OpenFile(path.clone()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//TODO: open properties?
|
||||
}
|
||||
|
|
@ -2112,20 +2133,11 @@ impl Tab {
|
|||
}
|
||||
commands.push(Command::DropFiles(to, from))
|
||||
}
|
||||
Location::Search(_, _) => {
|
||||
log::warn!(" Copy/cut to search not supported.");
|
||||
}
|
||||
Location::Trash if matches!(from.kind, ClipboardKind::Cut) => {
|
||||
commands.push(Command::MoveToTrash(from.paths))
|
||||
}
|
||||
Location::Trash => {
|
||||
log::warn!("Copy to trash is not supported.");
|
||||
}
|
||||
Location::Recents => {
|
||||
log::warn!("Copy to recents is not supported.");
|
||||
}
|
||||
Location::Networks => {
|
||||
log::warn!("Copy to networks is not supported.");
|
||||
_ => {
|
||||
log::warn!("{:?} to {:?} is not supported.", from.kind, to);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -2254,6 +2266,8 @@ impl Tab {
|
|||
trash::TrashItemSize::Entries(entries) => (true, entries as u64),
|
||||
trash::TrashItemSize::Bytes(bytes) => (false, bytes),
|
||||
},
|
||||
ItemMetadata::SimpleDir { entries } => (true, *entries),
|
||||
ItemMetadata::SimpleFile { size } => (false, *size),
|
||||
};
|
||||
let (a_is_entry, a_size) = get_size(a.1);
|
||||
let (b_is_entry, b_size) = get_size(b.1);
|
||||
|
|
@ -2287,7 +2301,7 @@ impl Tab {
|
|||
items.sort_by(|a, b| {
|
||||
let get_modified = |x: &Item| match &x.metadata {
|
||||
ItemMetadata::Path { metadata, .. } => metadata.modified().ok(),
|
||||
ItemMetadata::Trash { .. } => None,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let a_modified = get_modified(a.1);
|
||||
|
|
@ -2588,11 +2602,14 @@ impl Tab {
|
|||
.into(),
|
||||
);
|
||||
}
|
||||
Location::Networks => {
|
||||
Location::Network(uri, display_name) => {
|
||||
children.push(
|
||||
widget::button(widget::text::heading(fl!("networks")))
|
||||
widget::button(widget::text::heading(display_name))
|
||||
.padding(space_xxxs)
|
||||
.on_press(Message::Location(Location::Networks))
|
||||
.on_press(Message::Location(Location::Network(
|
||||
uri.clone(),
|
||||
display_name.clone(),
|
||||
)))
|
||||
.style(theme::Button::Text)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -3067,13 +3084,18 @@ impl Tab {
|
|||
Ok(time) => format_time(time).to_string(),
|
||||
Err(_) => String::new(),
|
||||
},
|
||||
ItemMetadata::Trash { .. } => String::new(),
|
||||
_ => String::new(),
|
||||
};
|
||||
|
||||
let size_text = match &item.metadata {
|
||||
ItemMetadata::Path { metadata, children } => {
|
||||
if metadata.is_dir() {
|
||||
//TODO: translate
|
||||
if *children == 1 {
|
||||
format!("{} item", children)
|
||||
} else {
|
||||
format!("{} items", children)
|
||||
}
|
||||
} else {
|
||||
format_size(metadata.len())
|
||||
}
|
||||
|
|
@ -3089,6 +3111,15 @@ impl Tab {
|
|||
}
|
||||
trash::TrashItemSize::Bytes(bytes) => format_size(bytes),
|
||||
},
|
||||
ItemMetadata::SimpleDir { entries } => {
|
||||
//TODO: translate
|
||||
if *entries == 1 {
|
||||
format!("{} item", entries)
|
||||
} else {
|
||||
format!("{} items", entries)
|
||||
}
|
||||
}
|
||||
ItemMetadata::SimpleFile { size } => format_size(*size),
|
||||
};
|
||||
|
||||
let row = if condensed {
|
||||
|
|
@ -3410,7 +3441,7 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
}
|
||||
Location::Networks => {
|
||||
Location::Network(uri, display_name) if uri == "network:///" => {
|
||||
tab_column = tab_column.push(
|
||||
widget::layer_container(widget::row::with_children(vec![
|
||||
widget::horizontal_space(Length::Fill).into(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue