windows: Fix RevealInFileManager
(#36592)
Closes #36314
This PR takes inspiration from [Electron’s
implementation](dd54e84a58/shell/common/platform_util_win.cc (L268-L314)
).
Before and after:
https://github.com/user-attachments/assets/53eec5d3-23c7-4ee1-8477-e524b0538f60
Release Notes:
- N/A
This commit is contained in:
parent
3dd362978a
commit
8ef9ecc91f
1 changed files with 67 additions and 44 deletions
|
@ -1,5 +1,6 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
ffi::OsStr,
|
||||
mem::ManuallyDrop,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
|
@ -460,13 +461,15 @@ impl Platform for WindowsPlatform {
|
|||
}
|
||||
|
||||
fn open_url(&self, url: &str) {
|
||||
if url.is_empty() {
|
||||
return;
|
||||
}
|
||||
let url_string = url.to_string();
|
||||
self.background_executor()
|
||||
.spawn(async move {
|
||||
if url_string.is_empty() {
|
||||
return;
|
||||
}
|
||||
open_target(url_string.as_str());
|
||||
open_target(&url_string)
|
||||
.with_context(|| format!("Opening url: {}", url_string))
|
||||
.log_err();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
@ -514,37 +517,29 @@ impl Platform for WindowsPlatform {
|
|||
}
|
||||
|
||||
fn reveal_path(&self, path: &Path) {
|
||||
let Ok(file_full_path) = path.canonicalize() else {
|
||||
log::error!("unable to parse file path");
|
||||
return;
|
||||
};
|
||||
self.background_executor()
|
||||
.spawn(async move {
|
||||
let Some(path) = file_full_path.to_str() else {
|
||||
return;
|
||||
};
|
||||
if path.is_empty() {
|
||||
if path.as_os_str().is_empty() {
|
||||
return;
|
||||
}
|
||||
open_target_in_explorer(path);
|
||||
let path = path.to_path_buf();
|
||||
self.background_executor()
|
||||
.spawn(async move {
|
||||
open_target_in_explorer(&path)
|
||||
.with_context(|| format!("Revealing path {} in explorer", path.display()))
|
||||
.log_err();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn open_with_system(&self, path: &Path) {
|
||||
let Ok(full_path) = path.canonicalize() else {
|
||||
log::error!("unable to parse file full path: {}", path.display());
|
||||
if path.as_os_str().is_empty() {
|
||||
return;
|
||||
};
|
||||
}
|
||||
let path = path.to_path_buf();
|
||||
self.background_executor()
|
||||
.spawn(async move {
|
||||
let Some(full_path_str) = full_path.to_str() else {
|
||||
return;
|
||||
};
|
||||
if full_path_str.is_empty() {
|
||||
return;
|
||||
};
|
||||
open_target(full_path_str);
|
||||
open_target(&path)
|
||||
.with_context(|| format!("Opening {} with system", path.display()))
|
||||
.log_err();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
@ -735,39 +730,67 @@ pub(crate) struct WindowCreationInfo {
|
|||
pub(crate) disable_direct_composition: bool,
|
||||
}
|
||||
|
||||
fn open_target(target: &str) {
|
||||
unsafe {
|
||||
let ret = ShellExecuteW(
|
||||
fn open_target(target: impl AsRef<OsStr>) -> Result<()> {
|
||||
let target = target.as_ref();
|
||||
let ret = unsafe {
|
||||
ShellExecuteW(
|
||||
None,
|
||||
windows::core::w!("open"),
|
||||
&HSTRING::from(target),
|
||||
None,
|
||||
None,
|
||||
SW_SHOWDEFAULT,
|
||||
);
|
||||
)
|
||||
};
|
||||
if ret.0 as isize <= 32 {
|
||||
log::error!("Unable to open target: {}", std::io::Error::last_os_error());
|
||||
}
|
||||
Err(anyhow::anyhow!(
|
||||
"Unable to open target: {}",
|
||||
std::io::Error::last_os_error()
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn open_target_in_explorer(target: &str) {
|
||||
fn open_target_in_explorer(target: &Path) -> Result<()> {
|
||||
let dir = target.parent().context("No parent folder found")?;
|
||||
let desktop = unsafe { SHGetDesktopFolder()? };
|
||||
|
||||
let mut dir_item = std::ptr::null_mut();
|
||||
unsafe {
|
||||
let ret = ShellExecuteW(
|
||||
desktop.ParseDisplayName(
|
||||
HWND::default(),
|
||||
None,
|
||||
windows::core::w!("open"),
|
||||
windows::core::w!("explorer.exe"),
|
||||
&HSTRING::from(format!("/select,{}", target).as_str()),
|
||||
&HSTRING::from(dir),
|
||||
None,
|
||||
SW_SHOWDEFAULT,
|
||||
);
|
||||
if ret.0 as isize <= 32 {
|
||||
log::error!(
|
||||
"Unable to open target in explorer: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
&mut dir_item,
|
||||
std::ptr::null_mut(),
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut file_item = std::ptr::null_mut();
|
||||
unsafe {
|
||||
desktop.ParseDisplayName(
|
||||
HWND::default(),
|
||||
None,
|
||||
&HSTRING::from(target),
|
||||
None,
|
||||
&mut file_item,
|
||||
std::ptr::null_mut(),
|
||||
)?;
|
||||
}
|
||||
|
||||
let highlight = [file_item as *const _];
|
||||
unsafe { SHOpenFolderAndSelectItems(dir_item as _, Some(&highlight), 0) }.or_else(|err| {
|
||||
if err.code().0 == ERROR_FILE_NOT_FOUND.0 as i32 {
|
||||
// On some systems, the above call mysteriously fails with "file not
|
||||
// found" even though the file is there. In these cases, ShellExecute()
|
||||
// seems to work as a fallback (although it won't select the file).
|
||||
open_target(dir).context("Opening target parent folder")
|
||||
} else {
|
||||
Err(anyhow::anyhow!("Can not open target path: {}", err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn file_open_dialog(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue