feat: retry opening network locations after mounting
This commit is contained in:
parent
7711b4bcc0
commit
c7e9828d7b
9 changed files with 333 additions and 56 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1482,6 +1482,7 @@ dependencies = [
|
||||||
name = "cosmic-files"
|
name = "cosmic-files"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"bzip2",
|
"bzip2",
|
||||||
"chrono",
|
"chrono",
|
||||||
"compio",
|
"compio",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ rust-version = "1.85.0"
|
||||||
vergen = { version = "8", features = ["git", "gitcl"] }
|
vergen = { version = "8", features = ["git", "gitcl"] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1"
|
||||||
chrono = { version = "0.4", features = ["unstable-locales"] }
|
chrono = { version = "0.4", features = ["unstable-locales"] }
|
||||||
icu = { version = "1.5.0", features = [
|
icu = { version = "1.5.0", features = [
|
||||||
"experimental",
|
"experimental",
|
||||||
|
|
|
||||||
216
src/app.rs
216
src/app.rs
|
|
@ -334,6 +334,12 @@ pub enum Message {
|
||||||
NavMenuAction(NavMenuAction),
|
NavMenuAction(NavMenuAction),
|
||||||
NetworkAuth(MounterKey, String, MounterAuth, mpsc::Sender<MounterAuth>),
|
NetworkAuth(MounterKey, String, MounterAuth, mpsc::Sender<MounterAuth>),
|
||||||
NetworkDriveInput(String),
|
NetworkDriveInput(String),
|
||||||
|
NetworkDriveOpenEntityAfterMount {
|
||||||
|
entity: Entity,
|
||||||
|
},
|
||||||
|
NetworkDriveOpenTabAfterMount {
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
NetworkDriveSubmit,
|
NetworkDriveSubmit,
|
||||||
NetworkResult(MounterKey, String, Result<bool, String>),
|
NetworkResult(MounterKey, String, Result<bool, String>),
|
||||||
NewItem(Option<Entity>, bool),
|
NewItem(Option<Entity>, bool),
|
||||||
|
|
@ -885,6 +891,58 @@ impl App {
|
||||||
activate: bool,
|
activate: bool,
|
||||||
selection_paths: Option<Vec<PathBuf>>,
|
selection_paths: Option<Vec<PathBuf>>,
|
||||||
) -> (Entity, Task<Message>) {
|
) -> (Entity, Task<Message>) {
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
if let Location::Network(ref uri, ref name, Some(ref path)) = location {
|
||||||
|
dbg!(path, self.mounter_items.len());
|
||||||
|
let mut found = false;
|
||||||
|
|
||||||
|
if let Some(key) = self
|
||||||
|
.mounter_items
|
||||||
|
.iter()
|
||||||
|
.find_map(|(k, items)| {
|
||||||
|
items.iter().find_map(|item| {
|
||||||
|
found |= item.path().is_some_and(|p| path.starts_with(p))
|
||||||
|
|| item.name() == *name
|
||||||
|
|| item.uri() == *uri;
|
||||||
|
dbg!(
|
||||||
|
item.is_mounted(),
|
||||||
|
item.path(),
|
||||||
|
path,
|
||||||
|
name,
|
||||||
|
item.name(),
|
||||||
|
item.uri(),
|
||||||
|
uri
|
||||||
|
);
|
||||||
|
(!item.is_mounted() && found).then(|| *k)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or(if found {
|
||||||
|
dbg!("found so skip...");
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
dbg!("not found...");
|
||||||
|
// TODO do we need to choose the correct mounter?
|
||||||
|
self.mounter_items.iter().map(|(k, _)| *k).next()
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if let Some(mounter) = MOUNTERS.get(&key) {
|
||||||
|
let location = location.clone();
|
||||||
|
dbg!(&location);
|
||||||
|
return (
|
||||||
|
Entity::null(),
|
||||||
|
mounter.network_drive(uri.clone()).map(move |_| {
|
||||||
|
cosmic::Action::App(Message::NetworkDriveOpenTabAfterMount {
|
||||||
|
location: location.clone(),
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dbg!("huh...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg!(&location);
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
location.clone(),
|
location.clone(),
|
||||||
self.config.tab,
|
self.config.tab,
|
||||||
|
|
@ -1061,6 +1119,7 @@ impl App {
|
||||||
location: Location,
|
location: Location,
|
||||||
selection_paths: Option<Vec<PathBuf>>,
|
selection_paths: Option<Vec<PathBuf>>,
|
||||||
) -> Task<Message> {
|
) -> Task<Message> {
|
||||||
|
dbg!("rescan", &location);
|
||||||
log::info!("rescan_tab {entity:?} {location:?} {selection_paths:?}");
|
log::info!("rescan_tab {entity:?} {location:?} {selection_paths:?}");
|
||||||
let icon_sizes = self.config.tab.icon_sizes;
|
let icon_sizes = self.config.tab.icon_sizes;
|
||||||
let mounter_items = self.mounter_items.clone();
|
let mounter_items = self.mounter_items.clone();
|
||||||
|
|
@ -1303,7 +1362,12 @@ impl App {
|
||||||
})
|
})
|
||||||
.size(16),
|
.size(16),
|
||||||
)
|
)
|
||||||
.data(Location::Path(path.clone()))
|
.data(match favorite {
|
||||||
|
Favorite::Network { uri, name, path } => {
|
||||||
|
Location::Network(uri.clone(), name.clone(), Some(path.to_owned()))
|
||||||
|
}
|
||||||
|
_ => Location::Path(path.clone()),
|
||||||
|
})
|
||||||
.data(FavoriteIndex(favorite_i))
|
.data(FavoriteIndex(favorite_i))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1327,6 +1391,7 @@ impl App {
|
||||||
.data(Location::Network(
|
.data(Location::Network(
|
||||||
"network:///".to_string(),
|
"network:///".to_string(),
|
||||||
fl!("networks"),
|
fl!("networks"),
|
||||||
|
None,
|
||||||
))
|
))
|
||||||
.divider_above()
|
.divider_above()
|
||||||
});
|
});
|
||||||
|
|
@ -1346,7 +1411,13 @@ impl App {
|
||||||
nav_model = nav_model.insert(|mut b| {
|
nav_model = nav_model.insert(|mut b| {
|
||||||
b = b.text(item.name()).data(MounterData(key, item.clone()));
|
b = b.text(item.name()).data(MounterData(key, item.clone()));
|
||||||
if let Some(path) = item.path() {
|
if let Some(path) = item.path() {
|
||||||
b = b.data(Location::Path(path.clone()));
|
b = b.data(Location::Network(
|
||||||
|
item.uri().to_string(),
|
||||||
|
item.name(),
|
||||||
|
Some(path),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
b = b.data(Location::Network(item.uri().to_string(), item.name(), None));
|
||||||
}
|
}
|
||||||
if let Some(icon) = item.icon(true) {
|
if let Some(icon) = item.icon(true) {
|
||||||
b = b.icon(widget::icon::icon(icon).size(16));
|
b = b.icon(widget::icon::icon(icon).size(16));
|
||||||
|
|
@ -2155,35 +2226,85 @@ impl Application for App {
|
||||||
fn on_nav_select(&mut self, entity: Entity) -> Task<Self::Message> {
|
fn on_nav_select(&mut self, entity: Entity) -> Task<Self::Message> {
|
||||||
self.nav_model.activate(entity);
|
self.nav_model.activate(entity);
|
||||||
if let Some(location) = self.nav_model.data::<Location>(entity) {
|
if let Some(location) = self.nav_model.data::<Location>(entity) {
|
||||||
|
dbg!(&location);
|
||||||
|
|
||||||
let should_open = match location {
|
let should_open = match location {
|
||||||
Location::Path(path) => match path.try_exists() {
|
#[cfg(feature = "gvfs")]
|
||||||
Ok(true) => true,
|
Location::Network(uri, name, Some(path))
|
||||||
Ok(false) => {
|
if !path.try_exists().unwrap_or_default() =>
|
||||||
log::warn!("failed to open favorite, path does not exist: {:?}", path);
|
{
|
||||||
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
dbg!(&location, path, self.mounter_items.len(), uri, path);
|
||||||
path: path.clone(),
|
let mut found = false;
|
||||||
entity,
|
|
||||||
});
|
if let Some(key) = self
|
||||||
false
|
.mounter_items
|
||||||
|
.iter()
|
||||||
|
.find_map(|(k, items)| {
|
||||||
|
items.iter().find_map(|item| {
|
||||||
|
found |= item.path().is_some_and(|p| path.starts_with(&p))
|
||||||
|
|| item.name() == *name
|
||||||
|
|| item.uri() == *uri;
|
||||||
|
dbg!(item.is_mounted(), item.path());
|
||||||
|
(!item.is_mounted() && found).then(|| *k)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or(if found {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// TODO do we need to choose the correct mounter?
|
||||||
|
self.mounter_items.iter().map(|(k, _)| *k).next()
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if let Some(mounter) = MOUNTERS.get(&key) {
|
||||||
|
dbg!(&location);
|
||||||
|
// TODO can we detect an error and handle failure?
|
||||||
|
let location = location.clone();
|
||||||
|
return mounter.network_drive(uri.clone()).map(move |_| {
|
||||||
|
cosmic::Action::App(Message::NetworkDriveOpenEntityAfterMount {
|
||||||
|
entity,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
|
||||||
log::warn!("failed to open favorite for path: {:?}, {}", path, err);
|
log::warn!("failed to open favorite, path does not exist: {:?}", path);
|
||||||
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
entity,
|
entity,
|
||||||
});
|
});
|
||||||
false
|
false
|
||||||
|
}
|
||||||
|
Location::Path(path) | Location::Network(_, _, Some(path)) => {
|
||||||
|
match path.try_exists() {
|
||||||
|
Ok(true) => true,
|
||||||
|
Ok(false) => {
|
||||||
|
log::warn!("failed to open favorite, path does not exist: {:?}", path);
|
||||||
|
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
||||||
|
path: path.clone(),
|
||||||
|
entity,
|
||||||
|
});
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("failed to open favorite for path: {:?}, {}", path, err);
|
||||||
|
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
||||||
|
path: path.clone(),
|
||||||
|
entity,
|
||||||
|
});
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dbg!(should_open);
|
||||||
if should_open {
|
if should_open {
|
||||||
let message = Message::TabMessage(None, tab::Message::Location(location.clone()));
|
let message = Message::TabMessage(None, tab::Message::Location(location.clone()));
|
||||||
return self.update(message);
|
return self.update(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(data) = self.nav_model.data::<MounterData>(entity) {
|
if let Some(data) = self.nav_model.data::<MounterData>(entity) {
|
||||||
if let Some(mounter) = MOUNTERS.get(&data.0) {
|
if let Some(mounter) = MOUNTERS.get(&data.0) {
|
||||||
return mounter
|
return mounter
|
||||||
|
|
@ -2296,10 +2417,45 @@ impl Application for App {
|
||||||
match message {
|
match message {
|
||||||
Message::AddToSidebar(entity_opt) => {
|
Message::AddToSidebar(entity_opt) => {
|
||||||
let mut favorites = self.config.favorites.clone();
|
let mut favorites = self.config.favorites.clone();
|
||||||
|
// check if the selected entity is in the current tab
|
||||||
|
// else just use the selected entity and check its location
|
||||||
|
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||||
|
|
||||||
for path in self.selected_paths(entity_opt) {
|
for path in self.selected_paths(entity_opt) {
|
||||||
let favorite = Favorite::from_path(path);
|
dbg!(&path);
|
||||||
|
let is_network = self.tab_model.data::<Tab>(entity).and_then(|tab| {
|
||||||
|
let in_current_tab =
|
||||||
|
tab.location.path_opt().zip(path.parent()).is_some_and(
|
||||||
|
|(t_path, parent)| {
|
||||||
|
dbg!(&parent, &t_path);
|
||||||
|
parent == t_path
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let tab = if in_current_tab {
|
||||||
|
self.tab_model
|
||||||
|
.data::<Tab>(self.tab_model.active())
|
||||||
|
.unwrap_or(tab)
|
||||||
|
} else {
|
||||||
|
tab
|
||||||
|
};
|
||||||
|
dbg!(in_current_tab, &tab.location);
|
||||||
|
let name = Location::Path(path.clone()).title();
|
||||||
|
if let Location::Network(uri, _, _) = tab.location.clone() {
|
||||||
|
Some((uri, name, path.clone()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dbg!(&is_network);
|
||||||
|
let name = Location::Path(path.clone()).title();
|
||||||
|
let favorite = if let Some((uri, _, _)) = is_network.clone() {
|
||||||
|
Favorite::Network { uri, name, path }
|
||||||
|
} else {
|
||||||
|
Favorite::from_path(path)
|
||||||
|
};
|
||||||
if !favorites.iter().any(|f| f == &favorite) {
|
if !favorites.iter().any(|f| f == &favorite) {
|
||||||
favorites.push(favorite);
|
favorites.push(favorite);
|
||||||
|
dbg!(&favorites);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
config_set!(favorites, favorites);
|
config_set!(favorites, favorites);
|
||||||
|
|
@ -2991,6 +3147,7 @@ impl Application for App {
|
||||||
Message::OpenInNewTab(entity_opt) => {
|
Message::OpenInNewTab(entity_opt) => {
|
||||||
return Task::batch(self.selected_paths(entity_opt).into_iter().filter_map(
|
return Task::batch(self.selected_paths(entity_opt).into_iter().filter_map(
|
||||||
|path| {
|
|path| {
|
||||||
|
dbg!(&path);
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
Some(self.open_tab(Location::Path(path), false, None))
|
Some(self.open_tab(Location::Path(path), false, None))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3510,6 +3667,7 @@ impl Application for App {
|
||||||
config.show_hidden = !config.show_hidden;
|
config.show_hidden = !config.show_hidden;
|
||||||
return self.update(Message::TabConfig(config));
|
return self.update(Message::TabConfig(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::TabMessage(entity_opt, tab_message) => {
|
Message::TabMessage(entity_opt, tab_message) => {
|
||||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||||
|
|
||||||
|
|
@ -3528,6 +3686,7 @@ impl Application for App {
|
||||||
for tab_command in tab_commands {
|
for tab_command in tab_commands {
|
||||||
match tab_command {
|
match tab_command {
|
||||||
tab::Command::Action(action) => {
|
tab::Command::Action(action) => {
|
||||||
|
dbg!(&action);
|
||||||
commands.push(self.update(action.message(Some(entity))));
|
commands.push(self.update(action.message(Some(entity))));
|
||||||
}
|
}
|
||||||
tab::Command::AddNetworkDrive => {
|
tab::Command::AddNetworkDrive => {
|
||||||
|
|
@ -3535,6 +3694,7 @@ impl Application for App {
|
||||||
self.set_show_context(true);
|
self.set_show_context(true);
|
||||||
}
|
}
|
||||||
tab::Command::AddToSidebar(path) => {
|
tab::Command::AddToSidebar(path) => {
|
||||||
|
dbg!("add to sidebar");
|
||||||
let mut favorites = self.config.favorites.clone();
|
let mut favorites = self.config.favorites.clone();
|
||||||
let favorite = Favorite::from_path(path);
|
let favorite = Favorite::from_path(path);
|
||||||
if !favorites.iter().any(|f| f == &favorite) {
|
if !favorites.iter().any(|f| f == &favorite) {
|
||||||
|
|
@ -3580,6 +3740,7 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
tab::Command::OpenFile(paths) => self.open_file(&paths),
|
tab::Command::OpenFile(paths) => self.open_file(&paths),
|
||||||
tab::Command::OpenInNewTab(path) => {
|
tab::Command::OpenInNewTab(path) => {
|
||||||
|
dbg!(&path);
|
||||||
commands.push(self.open_tab(Location::Path(path.clone()), false, None));
|
commands.push(self.open_tab(Location::Path(path.clone()), false, None));
|
||||||
}
|
}
|
||||||
tab::Command::OpenInNewWindow(path) => match env::current_exe() {
|
tab::Command::OpenInNewWindow(path) => match env::current_exe() {
|
||||||
|
|
@ -4037,14 +4198,15 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
NavMenuAction::OpenInNewTab(entity) => {
|
NavMenuAction::OpenInNewTab(entity) => {
|
||||||
match self.nav_model.data::<Location>(entity) {
|
match self.nav_model.data::<Location>(entity) {
|
||||||
Some(Location::Network(ref uri, ref display_name)) => {
|
Some(Location::Network(ref uri, ref display_name, path)) => {
|
||||||
return self.open_tab(
|
return self.open_tab(
|
||||||
Location::Network(uri.clone(), display_name.clone()),
|
Location::Network(uri.clone(), display_name.clone(), path.clone()),
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(Location::Path(ref path)) => {
|
Some(Location::Path(ref path)) => {
|
||||||
|
dbg!(path);
|
||||||
return self.open_tab(Location::Path(path.clone()), false, None);
|
return self.open_tab(Location::Path(path.clone()), false, None);
|
||||||
}
|
}
|
||||||
Some(Location::Recents) => {
|
Some(Location::Recents) => {
|
||||||
|
|
@ -4304,6 +4466,13 @@ impl Application for App {
|
||||||
Message::DragId(id) => {
|
Message::DragId(id) => {
|
||||||
return window::drag(id);
|
return window::drag(id);
|
||||||
}
|
}
|
||||||
|
Message::NetworkDriveOpenEntityAfterMount { entity } => {
|
||||||
|
return self.on_nav_select(entity);
|
||||||
|
}
|
||||||
|
Message::NetworkDriveOpenTabAfterMount { location } => {
|
||||||
|
dbg!("location");
|
||||||
|
return self.open_tab(location, false, None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
|
|
@ -5880,6 +6049,7 @@ pub(crate) mod test_utils {
|
||||||
nested: usize,
|
nested: usize,
|
||||||
name_len: usize,
|
name_len: usize,
|
||||||
) -> io::Result<(TempDir, Tab)> {
|
) -> io::Result<(TempDir, Tab)> {
|
||||||
|
dbg!("new tab...");
|
||||||
let fs = simple_fs(files, hidden, dirs, nested, name_len)?;
|
let fs = simple_fs(files, hidden, dirs, nested, name_len)?;
|
||||||
let path = fs.path();
|
let path = fs.path();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,11 @@ pub enum Favorite {
|
||||||
Pictures,
|
Pictures,
|
||||||
Videos,
|
Videos,
|
||||||
Path(PathBuf),
|
Path(PathBuf),
|
||||||
|
Network {
|
||||||
|
uri: String,
|
||||||
|
name: String,
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Favorite {
|
impl Favorite {
|
||||||
|
|
@ -95,6 +100,7 @@ impl Favorite {
|
||||||
Self::Pictures => dirs::picture_dir(),
|
Self::Pictures => dirs::picture_dir(),
|
||||||
Self::Videos => dirs::video_dir(),
|
Self::Videos => dirs::video_dir(),
|
||||||
Self::Path(path) => Some(path.clone()),
|
Self::Path(path) => Some(path.clone()),
|
||||||
|
Self::Network { path, .. } => Some(path.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
} else if &arg == "--recents" {
|
} else if &arg == "--recents" {
|
||||||
Location::Recents
|
Location::Recents
|
||||||
} else if &arg == "--network" {
|
} else if &arg == "--network" {
|
||||||
Location::Network("network:///".to_string(), fl!("networks"))
|
Location::Network("network:///".to_string(), fl!("networks"), None)
|
||||||
} else {
|
} else {
|
||||||
//TODO: support more URLs
|
//TODO: support more URLs
|
||||||
let path = match url::Url::parse(&arg) {
|
let path = match url::Url::parse(&arg) {
|
||||||
|
|
|
||||||
13
src/menu.rs
13
src/menu.rs
|
|
@ -152,7 +152,11 @@ pub fn context_menu<'a>(
|
||||||
match (&tab.mode, &tab.location) {
|
match (&tab.mode, &tab.location) {
|
||||||
(
|
(
|
||||||
tab::Mode::App | tab::Mode::Desktop,
|
tab::Mode::App | tab::Mode::Desktop,
|
||||||
Location::Desktop(..) | Location::Path(..) | Location::Search(..) | Location::Recents,
|
Location::Desktop(..)
|
||||||
|
| Location::Path(..)
|
||||||
|
| Location::Search(..)
|
||||||
|
| Location::Recents
|
||||||
|
| Location::Network(_, _, Some(_)),
|
||||||
) => {
|
) => {
|
||||||
if selected_trash_only {
|
if selected_trash_only {
|
||||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
children.push(menu_item(fl!("open"), Action::Open).into());
|
||||||
|
|
@ -298,7 +302,11 @@ pub fn context_menu<'a>(
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
tab::Mode::Dialog(dialog_kind),
|
tab::Mode::Dialog(dialog_kind),
|
||||||
Location::Desktop(..) | Location::Path(..) | Location::Search(..) | Location::Recents,
|
Location::Desktop(..)
|
||||||
|
| Location::Path(..)
|
||||||
|
| Location::Search(..)
|
||||||
|
| Location::Recents
|
||||||
|
| Location::Network(_, _, Some(_)),
|
||||||
) => {
|
) => {
|
||||||
if selected > 0 {
|
if selected > 0 {
|
||||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||||
|
|
@ -327,6 +335,7 @@ pub fn context_menu<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, Location::Network(..)) => {
|
(_, Location::Network(..)) => {
|
||||||
|
dbg!(selected, &tab.mode);
|
||||||
if selected > 0 {
|
if selected > 0 {
|
||||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
children.push(menu_item(fl!("open"), Action::Open).into());
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,13 @@ use cosmic::{
|
||||||
widget, Task,
|
widget, Task,
|
||||||
};
|
};
|
||||||
use gio::{glib, prelude::*};
|
use gio::{glib, prelude::*};
|
||||||
use std::{any::TypeId, cell::Cell, future::pending, path::PathBuf, sync::Arc};
|
use std::{
|
||||||
|
any::TypeId,
|
||||||
|
cell::Cell,
|
||||||
|
future::pending,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{mpsc, Mutex};
|
||||||
|
|
||||||
use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage};
|
use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage};
|
||||||
|
|
@ -30,6 +36,7 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
|
||||||
let mut items = MounterItems::new();
|
let mut items = MounterItems::new();
|
||||||
for (i, mount) in monitor.mounts().into_iter().enumerate() {
|
for (i, mount) in monitor.mounts().into_iter().enumerate() {
|
||||||
items.push(MounterItem::Gvfs(Item {
|
items.push(MounterItem::Gvfs(Item {
|
||||||
|
uri: MountExt::root(&mount).uri().to_string(),
|
||||||
kind: ItemKind::Mount,
|
kind: ItemKind::Mount,
|
||||||
index: i,
|
index: i,
|
||||||
name: MountExt::name(&mount).to_string(),
|
name: MountExt::name(&mount).to_string(),
|
||||||
|
|
@ -44,7 +51,12 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
|
||||||
// Volumes with mounts are already listed by mount
|
// Volumes with mounts are already listed by mount
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let uri = VolumeExt::activation_root(&volume)
|
||||||
|
.map(|f| f.uri().to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
items.push(MounterItem::Gvfs(Item {
|
items.push(MounterItem::Gvfs(Item {
|
||||||
|
// TODO can we get URI for volumes with no mount?
|
||||||
|
uri,
|
||||||
kind: ItemKind::Volume,
|
kind: ItemKind::Volume,
|
||||||
index: i,
|
index: i,
|
||||||
name: VolumeExt::name(&volume).to_string(),
|
name: VolumeExt::name(&volume).to_string(),
|
||||||
|
|
@ -57,7 +69,11 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
|
||||||
items
|
items
|
||||||
}
|
}
|
||||||
|
|
||||||
fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
fn network_scan(
|
||||||
|
uri: &str,
|
||||||
|
sizes: IconSizes,
|
||||||
|
path: Option<&Path>,
|
||||||
|
) -> Result<Vec<tab::Item>, String> {
|
||||||
let file = gio::File::for_uri(uri);
|
let file = gio::File::for_uri(uri);
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
for info_res in file
|
for info_res in file
|
||||||
|
|
@ -77,6 +93,7 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||||
file.child(info.name()).uri().to_string()
|
file.child(info.name()).uri().to_string()
|
||||||
},
|
},
|
||||||
display_name.clone(),
|
display_name.clone(),
|
||||||
|
file.child(info.name()).path(),
|
||||||
);
|
);
|
||||||
|
|
||||||
//TODO: support dir or file
|
//TODO: support dir or file
|
||||||
|
|
@ -193,11 +210,15 @@ fn mount_op(uri: String, event_tx: mpsc::UnboundedSender<Event>) -> gio::MountOp
|
||||||
enum Cmd {
|
enum Cmd {
|
||||||
Items(IconSizes, mpsc::Sender<MounterItems>),
|
Items(IconSizes, mpsc::Sender<MounterItems>),
|
||||||
Rescan,
|
Rescan,
|
||||||
Mount(MounterItem),
|
Mount(
|
||||||
NetworkDrive(String),
|
MounterItem,
|
||||||
|
tokio::sync::oneshot::Sender<anyhow::Result<()>>,
|
||||||
|
),
|
||||||
|
NetworkDrive(String, tokio::sync::oneshot::Sender<anyhow::Result<()>>),
|
||||||
NetworkScan(
|
NetworkScan(
|
||||||
String,
|
String,
|
||||||
IconSizes,
|
IconSizes,
|
||||||
|
Option<PathBuf>,
|
||||||
mpsc::Sender<Result<Vec<tab::Item>, String>>,
|
mpsc::Sender<Result<Vec<tab::Item>, String>>,
|
||||||
),
|
),
|
||||||
Unmount(MounterItem),
|
Unmount(MounterItem),
|
||||||
|
|
@ -220,6 +241,7 @@ enum ItemKind {
|
||||||
//TODO: better method of matching items
|
//TODO: better method of matching items
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
|
uri: String,
|
||||||
kind: ItemKind,
|
kind: ItemKind,
|
||||||
index: usize,
|
index: usize,
|
||||||
name: String,
|
name: String,
|
||||||
|
|
@ -238,6 +260,10 @@ impl Item {
|
||||||
self.is_mounted
|
self.is_mounted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uri(&self) -> String {
|
||||||
|
self.uri.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn icon(&self, symbolic: bool) -> Option<widget::icon::Handle> {
|
pub fn icon(&self, symbolic: bool) -> Option<widget::icon::Handle> {
|
||||||
if symbolic {
|
if symbolic {
|
||||||
self.icon_symbolic_opt.as_ref()
|
self.icon_symbolic_opt.as_ref()
|
||||||
|
|
@ -318,9 +344,15 @@ impl Gvfs {
|
||||||
Cmd::Rescan => {
|
Cmd::Rescan => {
|
||||||
event_tx.send(Event::Items(items(&monitor, IconSizes::default()))).unwrap();
|
event_tx.send(Event::Items(items(&monitor, IconSizes::default()))).unwrap();
|
||||||
}
|
}
|
||||||
Cmd::Mount(mounter_item) => {
|
Cmd::Mount(mounter_item, complete_tx) => {
|
||||||
let MounterItem::Gvfs(ref item) = mounter_item else { continue };
|
let MounterItem::Gvfs(ref item) = mounter_item else {
|
||||||
let ItemKind::Volume = item.kind else { continue };
|
_ = complete_tx.send(Err(anyhow::anyhow!("No mounter item")));
|
||||||
|
continue
|
||||||
|
};
|
||||||
|
let ItemKind::Volume = item.kind else {
|
||||||
|
_ = complete_tx.send(Err(anyhow::anyhow!("No mounter volume")));
|
||||||
|
continue
|
||||||
|
};
|
||||||
for (i, volume) in monitor.volumes().into_iter().enumerate() {
|
for (i, volume) in monitor.volumes().into_iter().enumerate() {
|
||||||
if i != item.index {
|
if i != item.index {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -345,17 +377,23 @@ impl Gvfs {
|
||||||
move |res| {
|
move |res| {
|
||||||
log::info!("mount {}: result {:?}", name, res);
|
log::info!("mount {}: result {:?}", name, res);
|
||||||
event_tx.send(Event::MountResult(mounter_item, match res {
|
event_tx.send(Event::MountResult(mounter_item, match res {
|
||||||
Ok(()) => Ok(true),
|
Ok(()) => {
|
||||||
Err(err) => match err.kind::<gio::IOErrorEnum>() {
|
_ = complete_tx.send(Ok(()));
|
||||||
|
Ok(true)
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
_ = complete_tx.send(Err(anyhow::anyhow!("{err:?}")));
|
||||||
|
match err.kind::<gio::IOErrorEnum>() {
|
||||||
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
||||||
_ => Err(format!("{}", err))
|
_ => Err(format!("{}", err))
|
||||||
}
|
}}
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cmd::NetworkDrive(uri) => {
|
Cmd::NetworkDrive(uri, result_tx) => {
|
||||||
let file = gio::File::for_uri(&uri);
|
let file = gio::File::for_uri(&uri);
|
||||||
let mount_op = mount_op(uri.clone(), event_tx.clone());
|
let mount_op = mount_op(uri.clone(), event_tx.clone());
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
|
|
@ -366,16 +404,20 @@ impl Gvfs {
|
||||||
move |res| {
|
move |res| {
|
||||||
log::info!("network drive {}: result {:?}", uri, res);
|
log::info!("network drive {}: result {:?}", uri, res);
|
||||||
event_tx.send(Event::NetworkResult(uri, match res {
|
event_tx.send(Event::NetworkResult(uri, match res {
|
||||||
Ok(()) => Ok(true),
|
Ok(()) => {
|
||||||
Err(err) => match err.kind::<gio::IOErrorEnum>() {
|
_ = result_tx.send(Ok(()));
|
||||||
|
Ok(true)},
|
||||||
|
Err(err) => {
|
||||||
|
_ = result_tx.send(Err(anyhow::anyhow!("{err:?}")));
|
||||||
|
match err.kind::<gio::IOErrorEnum>() {
|
||||||
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
||||||
_ => Err(format!("{}", err))
|
_ => Err(format!("{}", err))
|
||||||
}
|
}}
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Cmd::NetworkScan(uri, sizes, items_tx) => {
|
Cmd::NetworkScan(uri, sizes, path, items_tx) => {
|
||||||
let file = gio::File::for_uri(&uri);
|
let file = gio::File::for_uri(&uri);
|
||||||
let needs_mount = match file.find_enclosing_mount(gio::Cancellable::NONE) {
|
let needs_mount = match file.find_enclosing_mount(gio::Cancellable::NONE) {
|
||||||
Ok(_) => false,
|
Ok(_) => false,
|
||||||
|
|
@ -468,9 +510,16 @@ impl Mounter for Gvfs {
|
||||||
let command_tx = self.command_tx.clone();
|
let command_tx = self.command_tx.clone();
|
||||||
Task::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
command_tx.send(Cmd::Mount(item)).unwrap();
|
let (res_tx, res_rx) = tokio::sync::oneshot::channel();
|
||||||
|
|
||||||
|
command_tx.send(Cmd::Mount(item, res_tx)).unwrap();
|
||||||
|
res_rx.await
|
||||||
|
},
|
||||||
|
|x| {
|
||||||
|
if let Err(err) = x {
|
||||||
|
log::error!("{err:?}");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|x| x,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -478,16 +527,33 @@ impl Mounter for Gvfs {
|
||||||
let command_tx = self.command_tx.clone();
|
let command_tx = self.command_tx.clone();
|
||||||
Task::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
command_tx.send(Cmd::NetworkDrive(uri)).unwrap();
|
let (res_tx, res_rx) = tokio::sync::oneshot::channel();
|
||||||
|
|
||||||
|
command_tx.send(Cmd::NetworkDrive(uri, res_tx)).unwrap();
|
||||||
|
res_rx.await
|
||||||
|
},
|
||||||
|
|x| {
|
||||||
|
if let Err(err) = x {
|
||||||
|
log::error!("{err:?}");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|x| x,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>> {
|
fn network_scan(
|
||||||
|
&self,
|
||||||
|
uri: &str,
|
||||||
|
sizes: IconSizes,
|
||||||
|
path: Option<&Path>,
|
||||||
|
) -> Option<Result<Vec<tab::Item>, String>> {
|
||||||
let (items_tx, mut items_rx) = mpsc::channel(1);
|
let (items_tx, mut items_rx) = mpsc::channel(1);
|
||||||
self.command_tx
|
self.command_tx
|
||||||
.send(Cmd::NetworkScan(uri.to_string(), sizes, items_tx))
|
.send(Cmd::NetworkScan(
|
||||||
|
uri.to_string(),
|
||||||
|
sizes,
|
||||||
|
path.map(|p| p.to_owned()),
|
||||||
|
items_tx,
|
||||||
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
items_rx.blocking_recv()
|
items_rx.blocking_recv()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
use cosmic::{iced::Subscription, widget, Task};
|
use cosmic::{iced::Subscription, widget, Task};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::{collections::BTreeMap, fmt, path::PathBuf, sync::Arc};
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
fmt,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::{config::IconSizes, tab};
|
use crate::{config::IconSizes, tab};
|
||||||
|
|
@ -55,6 +60,14 @@ impl MounterItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uri(&self) -> String {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
Self::Gvfs(item) => item.uri(),
|
||||||
|
Self::None => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_mounted(&self) -> bool {
|
pub fn is_mounted(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "gvfs")]
|
#[cfg(feature = "gvfs")]
|
||||||
|
|
@ -95,7 +108,12 @@ pub trait Mounter: Send + Sync {
|
||||||
//TODO: send result
|
//TODO: send result
|
||||||
fn mount(&self, item: MounterItem) -> Task<()>;
|
fn mount(&self, item: MounterItem) -> Task<()>;
|
||||||
fn network_drive(&self, uri: String) -> Task<()>;
|
fn network_drive(&self, uri: String) -> Task<()>;
|
||||||
fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>>;
|
fn network_scan(
|
||||||
|
&self,
|
||||||
|
uri: &str,
|
||||||
|
sizes: IconSizes,
|
||||||
|
path: Option<&Path>,
|
||||||
|
) -> Option<Result<Vec<tab::Item>, String>>;
|
||||||
fn unmount(&self, item: MounterItem) -> Task<()>;
|
fn unmount(&self, item: MounterItem) -> Task<()>;
|
||||||
fn subscription(&self) -> Subscription<MounterMessage>;
|
fn subscription(&self) -> Subscription<MounterMessage>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
src/tab.rs
20
src/tab.rs
|
|
@ -1205,9 +1205,9 @@ pub fn scan_recents(sizes: IconSizes) -> Vec<Item> {
|
||||||
recents.into_iter().take(50).map(|(item, _)| item).collect()
|
recents.into_iter().take(50).map(|(item, _)| item).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scan_network(uri: &str, sizes: IconSizes) -> Vec<Item> {
|
pub fn scan_network(uri: &str, sizes: IconSizes, path: Option<&Path>) -> Vec<Item> {
|
||||||
for (_key, mounter) in MOUNTERS.iter() {
|
for (_key, mounter) in MOUNTERS.iter() {
|
||||||
match mounter.network_scan(uri, sizes) {
|
match mounter.network_scan(uri, sizes, path) {
|
||||||
Some(Ok(items)) => return items,
|
Some(Ok(items)) => return items,
|
||||||
Some(Err(err)) => {
|
Some(Err(err)) => {
|
||||||
log::warn!("failed to scan {:?}: {}", uri, err);
|
log::warn!("failed to scan {:?}: {}", uri, err);
|
||||||
|
|
@ -1361,7 +1361,7 @@ impl From<Location> for EditLocation {
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub enum Location {
|
pub enum Location {
|
||||||
Desktop(PathBuf, String, DesktopConfig),
|
Desktop(PathBuf, String, DesktopConfig),
|
||||||
Network(String, String),
|
Network(String, String, Option<PathBuf>),
|
||||||
Path(PathBuf),
|
Path(PathBuf),
|
||||||
Recents,
|
Recents,
|
||||||
Search(PathBuf, String, bool, Instant),
|
Search(PathBuf, String, bool, Instant),
|
||||||
|
|
@ -1413,6 +1413,7 @@ impl Location {
|
||||||
Self::Desktop(path, ..) => Some(path),
|
Self::Desktop(path, ..) => Some(path),
|
||||||
Self::Path(path) => Some(path),
|
Self::Path(path) => Some(path),
|
||||||
Self::Search(path, ..) => Some(path),
|
Self::Search(path, ..) => Some(path),
|
||||||
|
Self::Network(_, _, path) => path.as_ref(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1426,6 +1427,8 @@ impl Location {
|
||||||
Self::Search(_, term, show_hidden, time) => {
|
Self::Search(_, term, show_hidden, time) => {
|
||||||
Self::Search(path, term.clone(), *show_hidden, *time)
|
Self::Search(path, term.clone(), *show_hidden, *time)
|
||||||
}
|
}
|
||||||
|
Self::Network(id, name, path) => Self::Network(id.clone(), name.clone(), path.clone()),
|
||||||
|
|
||||||
other => other.clone(),
|
other => other.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1442,7 +1445,7 @@ 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, _, path) => scan_network(uri, sizes, path.as_deref()),
|
||||||
};
|
};
|
||||||
let parent_item_opt = match self.path_opt() {
|
let parent_item_opt = match self.path_opt() {
|
||||||
Some(path) => match item_from_path(path, sizes) {
|
Some(path) => match item_from_path(path, sizes) {
|
||||||
|
|
@ -1479,7 +1482,7 @@ impl Location {
|
||||||
Self::Recents => {
|
Self::Recents => {
|
||||||
fl!("recents")
|
fl!("recents")
|
||||||
}
|
}
|
||||||
Self::Network(_uri, display_name) => display_name.clone(),
|
Self::Network(display_name, ..) => display_name.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3033,6 +3036,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
if !item.selected {
|
if !item.selected {
|
||||||
self.clicked = click_i_opt;
|
self.clicked = click_i_opt;
|
||||||
|
dbg!(&item.location_opt);
|
||||||
item.selected = true;
|
item.selected = true;
|
||||||
}
|
}
|
||||||
self.select_range = Some((i, i));
|
self.select_range = Some((i, i));
|
||||||
|
|
@ -3133,6 +3137,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
LocationMenuAction::AddToSidebar(ancestor_index) => {
|
LocationMenuAction::AddToSidebar(ancestor_index) => {
|
||||||
if let Some(path) = path_for_index(ancestor_index) {
|
if let Some(path) = path_for_index(ancestor_index) {
|
||||||
|
dbg!(&path);
|
||||||
commands.push(Command::AddToSidebar(path));
|
commands.push(Command::AddToSidebar(path));
|
||||||
} else {
|
} else {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
|
|
@ -4512,13 +4517,14 @@ impl Tab {
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Location::Network(uri, display_name) => {
|
Location::Network(uri, display_name, path) => {
|
||||||
children.push(
|
children.push(
|
||||||
widget::button::custom(widget::text::heading(display_name))
|
widget::button::custom(widget::text::heading(display_name))
|
||||||
.padding(space_xxxs)
|
.padding(space_xxxs)
|
||||||
.on_press(Message::Location(Location::Network(
|
.on_press(Message::Location(Location::Network(
|
||||||
uri.clone(),
|
uri.clone(),
|
||||||
display_name.clone(),
|
display_name.clone(),
|
||||||
|
path.clone(),
|
||||||
)))
|
)))
|
||||||
.class(theme::Button::Text)
|
.class(theme::Button::Text)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -5421,7 +5427,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Location::Network(uri, _display_name) if uri == "network:///" => {
|
Location::Network(uri, _display_name, path) if uri == "network:///" => {
|
||||||
tab_column = tab_column.push(
|
tab_column = tab_column.push(
|
||||||
widget::layer_container(widget::row::with_children(vec![
|
widget::layer_container(widget::row::with_children(vec![
|
||||||
widget::horizontal_space().into(),
|
widget::horizontal_space().into(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue