feat: handle opening network location in new window

This commit is contained in:
Ashley Wulber 2025-07-14 18:10:14 -04:00 committed by Jeremy Soller
parent c7e9828d7b
commit 08367c9ea6
5 changed files with 43 additions and 65 deletions

View file

@ -105,6 +105,7 @@ pub struct Flags {
pub state: State, pub state: State,
pub mode: Mode, pub mode: Mode,
pub locations: Vec<Location>, pub locations: Vec<Location>,
pub uris: Vec<url::Url>,
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@ -893,7 +894,6 @@ impl App {
) -> (Entity, Task<Message>) { ) -> (Entity, Task<Message>) {
#[cfg(feature = "gvfs")] #[cfg(feature = "gvfs")]
if let Location::Network(ref uri, ref name, Some(ref path)) = location { if let Location::Network(ref uri, ref name, Some(ref path)) = location {
dbg!(path, self.mounter_items.len());
let mut found = false; let mut found = false;
if let Some(key) = self if let Some(key) = self
@ -904,30 +904,18 @@ impl App {
found |= item.path().is_some_and(|p| path.starts_with(p)) found |= item.path().is_some_and(|p| path.starts_with(p))
|| item.name() == *name || item.name() == *name
|| item.uri() == *uri; || item.uri() == *uri;
dbg!(
item.is_mounted(),
item.path(),
path,
name,
item.name(),
item.uri(),
uri
);
(!item.is_mounted() && found).then(|| *k) (!item.is_mounted() && found).then(|| *k)
}) })
}) })
.or(if found { .or(if found {
dbg!("found so skip...");
None None
} else { } else {
dbg!("not found...");
// TODO do we need to choose the correct mounter? // TODO do we need to choose the correct mounter?
self.mounter_items.iter().map(|(k, _)| *k).next() self.mounter_items.iter().map(|(k, _)| *k).next()
}) })
{ {
if let Some(mounter) = MOUNTERS.get(&key) { if let Some(mounter) = MOUNTERS.get(&key) {
let location = location.clone(); let location = location.clone();
dbg!(&location);
return ( return (
Entity::null(), Entity::null(),
mounter.network_drive(uri.clone()).map(move |_| { mounter.network_drive(uri.clone()).map(move |_| {
@ -937,12 +925,9 @@ impl App {
}), }),
); );
} }
} 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,
@ -1119,7 +1104,6 @@ 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();
@ -2101,6 +2085,17 @@ impl Application for App {
} }
commands.push(app.open_tab(location, true, None)); commands.push(app.open_tab(location, true, None));
} }
for location in flags.uris {
if let Some(e) = app.nav_model.iter().find(|e| {
app.nav_model.data::<Location>(*e).is_some_and(
|l| matches!(l, Location::Network(ref uri, ..) if *uri == location.to_string()),
)
}) {
commands.push(cosmic::task::message(cosmic::Action::App(
Message::NetworkDriveOpenEntityAfterMount { entity: e },
)));
}
}
if app.tab_model.iter().next().is_none() { if app.tab_model.iter().next().is_none() {
if let Ok(current_dir) = env::current_dir() { if let Ok(current_dir) = env::current_dir() {
@ -2226,14 +2221,11 @@ 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 {
#[cfg(feature = "gvfs")] #[cfg(feature = "gvfs")]
Location::Network(uri, name, Some(path)) Location::Network(uri, name, Some(path))
if !path.try_exists().unwrap_or_default() => if !path.try_exists().unwrap_or_default() =>
{ {
dbg!(&location, path, self.mounter_items.len(), uri, path);
let mut found = false; let mut found = false;
if let Some(key) = self if let Some(key) = self
@ -2244,7 +2236,6 @@ impl Application for App {
found |= item.path().is_some_and(|p| path.starts_with(&p)) found |= item.path().is_some_and(|p| path.starts_with(&p))
|| item.name() == *name || item.name() == *name
|| item.uri() == *uri; || item.uri() == *uri;
dbg!(item.is_mounted(), item.path());
(!item.is_mounted() && found).then(|| *k) (!item.is_mounted() && found).then(|| *k)
}) })
}) })
@ -2256,9 +2247,6 @@ impl Application for App {
}) })
{ {
if let Some(mounter) = MOUNTERS.get(&key) { 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 |_| { return mounter.network_drive(uri.clone()).map(move |_| {
cosmic::Action::App(Message::NetworkDriveOpenEntityAfterMount { cosmic::Action::App(Message::NetworkDriveOpenEntityAfterMount {
entity, entity,
@ -2299,7 +2287,6 @@ impl Application for App {
_ => 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);
@ -2422,15 +2409,12 @@ impl Application for App {
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); 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) {
dbg!(&path);
let is_network = self.tab_model.data::<Tab>(entity).and_then(|tab| { let is_network = self.tab_model.data::<Tab>(entity).and_then(|tab| {
let in_current_tab = let in_current_tab = tab
tab.location.path_opt().zip(path.parent()).is_some_and( .location
|(t_path, parent)| { .path_opt()
dbg!(&parent, &t_path); .zip(path.parent())
parent == t_path .is_some_and(|(t_path, parent)| parent == t_path);
},
);
let tab = if in_current_tab { let tab = if in_current_tab {
self.tab_model self.tab_model
.data::<Tab>(self.tab_model.active()) .data::<Tab>(self.tab_model.active())
@ -2438,15 +2422,23 @@ impl Application for App {
} else { } else {
tab tab
}; };
dbg!(in_current_tab, &tab.location);
let name = Location::Path(path.clone()).title(); let name = Location::Path(path.clone()).title();
if let Location::Network(uri, _, _) = tab.location.clone() { if let Location::Network(uri, _, _) = tab
.items_opt
.as_ref()
.and_then(|items| items.iter().find(|i| i.path_opt() == Some(&path)))
.unwrap()
.location_opt
.clone()
.unwrap()
.clone()
{
Some((uri, name, path.clone())) Some((uri, name, path.clone()))
} else { } else {
None None
} }
}); });
dbg!(&is_network);
let name = Location::Path(path.clone()).title(); let name = Location::Path(path.clone()).title();
let favorite = if let Some((uri, _, _)) = is_network.clone() { let favorite = if let Some((uri, _, _)) = is_network.clone() {
Favorite::Network { uri, name, path } Favorite::Network { uri, name, path }
@ -2455,7 +2447,6 @@ impl Application for App {
}; };
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);
@ -3147,7 +3138,6 @@ 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 {
@ -3686,7 +3676,6 @@ 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 => {
@ -3694,7 +3683,6 @@ 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) {
@ -3740,7 +3728,6 @@ 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() {
@ -4206,7 +4193,6 @@ impl Application for App {
); );
} }
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) => {
@ -4232,6 +4218,9 @@ impl Application for App {
Location::Trash => { Location::Trash => {
command.arg("--trash"); command.arg("--trash");
} }
Location::Network(uri, _, Some(_)) => {
command.arg(uri);
}
Location::Network(..) => { Location::Network(..) => {
command.arg("--network"); command.arg("--network");
} }
@ -4470,7 +4459,6 @@ impl Application for App {
return self.on_nav_select(entity); return self.on_nav_select(entity);
} }
Message::NetworkDriveOpenTabAfterMount { location } => { Message::NetworkDriveOpenTabAfterMount { location } => {
dbg!("location");
return self.open_tab(location, false, None); return self.open_tab(location, false, None);
} }
} }
@ -6049,7 +6037,6 @@ 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();

View file

@ -80,6 +80,7 @@ pub fn desktop() -> Result<(), Box<dyn std::error::Error>> {
state, state,
mode: app::Mode::Desktop, mode: app::Mode::Desktop,
locations, locations,
uris: Vec::new()
}; };
cosmic::app::run::<App>(settings, flags)?; cosmic::app::run::<App>(settings, flags)?;
@ -98,6 +99,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut daemonize = true; let mut daemonize = true;
let mut locations = Vec::new(); let mut locations = Vec::new();
let mut uris = Vec::new();
for arg in env::args().skip(1) { for arg in env::args().skip(1) {
let location = if &arg == "--no-daemon" { let location = if &arg == "--no-daemon" {
daemonize = false; daemonize = false;
@ -111,14 +113,18 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
} else { } else {
//TODO: support more URLs //TODO: support more URLs
let path = match url::Url::parse(&arg) { let path = match url::Url::parse(&arg) {
Ok(url) => match url.to_file_path() { Ok(url) if url.scheme() == "file" => match url.to_file_path() {
Ok(path) => path, Ok(path) => path,
Err(()) => { Err(()) => {
log::warn!("invalid argument {:?}", arg); log::warn!("invalid argument {:?}", arg);
continue; continue;
} }
}, },
Err(_) => PathBuf::from(arg), Ok(url) => {
uris.push(url);
continue;
}
_ => PathBuf::from(arg),
}; };
match fs::canonicalize(&path) { match fs::canonicalize(&path) {
Ok(absolute) => Location::Path(absolute), Ok(absolute) => Location::Path(absolute),
@ -160,6 +166,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
state, state,
mode: app::Mode::App, mode: app::Mode::App,
locations, locations,
uris
}; };
cosmic::app::run::<App>(settings, flags)?; cosmic::app::run::<App>(settings, flags)?;

View file

@ -335,7 +335,6 @@ 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());

View file

@ -69,11 +69,7 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
items items
} }
fn network_scan( fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
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
@ -84,18 +80,9 @@ fn network_scan(
let name = info.name().to_string_lossy().to_string(); let name = info.name().to_string_lossy().to_string();
let display_name = info.display_name().to_string(); let display_name = info.display_name().to_string();
let uri = file.child(info.name()).uri().to_string();
//TODO: what is the best way to resolve shortcuts? //TODO: what is the best way to resolve shortcuts?
let location = Location::Network( let location = Location::Network(uri, display_name.clone(), file.child(info.name()).path());
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(),
file.child(info.name()).path(),
);
//TODO: support dir or file //TODO: support dir or file
let metadata = ItemMetadata::SimpleDir { entries: 0 }; let metadata = ItemMetadata::SimpleDir { entries: 0 };

View file

@ -3036,7 +3036,6 @@ 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));
@ -3137,7 +3136,6 @@ 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!(