-
Notifications
You must be signed in to change notification settings - Fork 493
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Missing SetFileTypeChoices
method for FileSavePicker
#3225
Comments
Looks like the method is called windows-rs/crates/libs/windows/src/Windows/Storage/Pickers/mod.rs Lines 745 to 746 in 88a80f2
|
Well, that method retrieves the file types, but I want to set that property. Wait, do I need to add them manually? Something like this? let map = dialog.FileTypeChoices()?;
map.add(...); |
That is what the sample does in the docs. |
Sorry to bother you, there are let map = dialog.FileTypeChoices()?;
map.add(...);
dialog.SetFileTypeChoices(map)?; So I thought the method was missing, my apologies! |
No prob, some of these APIs can be a little confusing. This "property" cannot be set but instead always returns a reference that must be updated to "set" the property. 🤷 |
Really sorry to bother you again, but could you please guide me how to create an let picker = FileSavePicker::new()?;
let map: IMap<HSTRING, IVector<HSTRING>> = picker.FileTypeChoices()?; When I call let vector_view: IVectorView<HSTRING> = rust_vec.try_into()?;
map.insert(&HSTRING::from("Example"), &vector_view)?; But it didn't work. I also searched for a solution but couldn't find one. Could you please guide me on how to create an |
Ah, well https://github.com/microsoft/windows-rs/releases/0.46.0 added stock collection implementations but only for the "view" collection interfaces. I still need to add stock collection implementations for the read-write collection interfaces. |
The "Windows.Storage" APIs do however have a number of problems. You may be better off using the older and more reliable COM APIs that I wrote about here: Here's a simple example: use windows::{core::*, Win32::System::Com::*, Win32::UI::Shell::Common::*, Win32::UI::Shell::*};
fn main() -> Result<()> {
unsafe {
CoIncrementMTAUsage()?;
let dialog: IFileSaveDialog = CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)?;
dialog.SetFileTypes(&[
COMDLG_FILTERSPEC {
pszName: w!("Text files"),
pszSpec: w!("*.txt"),
},
COMDLG_FILTERSPEC {
pszName: w!("All files"),
pszSpec: w!("*.*"),
},
])?;
dialog.Show(None)?;
Ok(())
}
} |
Here's a complete example: #3226 |
Thank you so much for your advice. Actually, I’m contributing to an open-source project called Zed, where I mainly work on the Windows implementation. The code for opening the save file dialog that I implemented is using However, I've encountered a particularly baffling bug recently, and I’m completely stumped. That’s why I started experimenting with the The reproduce steps:
Here is the relevant code: // `CoInit` is called when app launched
fn file_save_dialog(directory: PathBuf) -> Result<Option<PathBuf>> {
let dialog: IFileSaveDialog = unsafe { CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)? };
if let Some(full_path) = directory.canonicalize().log_err() {
let full_path = full_path.to_string_lossy().to_string();
if !full_path.is_empty() {
let path_item: IShellItem =
unsafe { SHCreateItemFromParsingName(&HSTRING::from(&full_path), None)? };
unsafe { dialog.SetFolder(&path_item).log_err() };
}
}
unsafe {
if dialog.Show(None).is_err() {
// User cancelled
return Ok(None);
}
}
let shell_item = unsafe { dialog.GetResult()? };
let file_path_string = unsafe { shell_item.GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING)?.to_string()? };
Ok(Some(PathBuf::from(file_path_string)))
} The video, when the bottom right corner shows Screen.Recording.2024-08-28.004352.mp4 |
It has also been reported that older versions of the Pinyin input method do not have this issue. And I have tried every possible solution I could think of, such as setting the owner window for the dialog, adjusting options, and more. I've also searched online extensively but haven't found a viable solution. Additionally, I reviewed the code from Chrome, but it doesn't differ significantly from my current implementation. Since Chrome and VSCode don't seem to have this issue, I suspect they might have applied a fix elsewhere. Further investigation is still needed. I wanted to provide this bit of background information, along with some of my frustrations. Really appreciate your help and advice, thank you! |
@JunkuiZhang Hm, I can't reproduce this with Pinyin input method enabled. Can you reproduce this with the sample @kennykerr linked to? Can you also share what OS version you're running in the video above? Is the window usable if it appears outside the bounds of the Zed main window? (You can shift-right-click on the taskbar icon > move > [keyboard keys] to shift it around.) |
@kennykerr's example works perfectly on my machine, dose not have this bug. (I suspect it might be related to the message loop?).
My Windows version is Windows 11 Home Chinese Edition.
The save file dialog still freezes even when it doesn't overlap with the Zed window, video: Screen.Recording.2024-08-28.014950.mp4BTW, it is odd that the open file dialog ( |
@JunkuiZhang I can reproduce this on 27686.rs_prerelease.240809-2254, but only with the new Microsoft Pinyin candidate window enabled (which is on by default). If I turn it off and fallback to the old version, Zed works as expected. I can't reproduce this in the Rust sample, so more investigation is needed. |
@riverar Sorry for the late reply. Since I'm in China, it was already 5 a.m. here when you sent your last message, so I was probably dreaming at the time. Zed has several bugs on Windows related to the message loop, not just this one. For example, consider the following code: loop {
while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE) {
TranslateMessage(...);
DispatchMessageW(...);
}
window.draw();
} In this case, if vertical synchronization is enabled, the window should render at a steady 60fps. However, in Zed, even with vertical sync enabled, the rendering rate skyrockets uncontrollably, regardless of whether the rendering backend is Vulkan, DX11, or DX12.
Yes, if I turn on the compatibility button, |
@jonwis Anyone working on IME we can route to? I filled out the user-initiated feedback survey that appeared when switching back to the legacy IME, but that likely went into a black hole. |
@riverar I sincerely apologize for the disturbance. After conducting some tests, I discovered that the issue is caused by the To reproduce the issue: make sure the Pinyin input method is enabled, then run the project using Specifically, My question might be a bit naive, sorry! Is that using |
Not sure if this will help, but typically you need to call |
Really thanks for your advices! In
Are you suggesting using |
After changing to call |
I wasn't able to reproduce it, but I know nothing of Chinese input. Perhaps @oldnewthing has observed this before? |
The key thing here is turning on Pinyin input method, and as @riverar said, this only happens with the new Microsoft Pinyin candidate window enabled (which is on by default). If it is turned off and fallback to the old version, the bug is gone. Screen.Recording.2024-09-12.234341.mp4 |
BTW, really thank you for the help! |
Ah, yes that repros! 🙂 I will say it hangs for some seconds but eventually becomes responsive. I'm not sure what's going on there. Will try to find somebody who does. |
Minimal repro for reference.
[dependencies.windows]
version = "0.58"
features = [
"System",
"Win32_Graphics_Gdi",
"Win32_System_Com",
"Win32_System_LibraryLoader",
"Win32_System_WinRT",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
] use windows::{
core::*, System::*, Win32::Foundation::*, Win32::Graphics::Gdi::*, Win32::System::Com::*,
Win32::System::LibraryLoader::*, Win32::System::WinRT::*, Win32::UI::Shell::*,
Win32::UI::WindowsAndMessaging::*,
};
fn main() -> Result<()> {
unsafe {
CoInitialize(None).ok()?;
let options = DispatcherQueueOptions {
dwSize: std::mem::size_of::<DispatcherQueueOptions>() as u32,
threadType: DQTYPE_THREAD_CURRENT,
apartmentType: DQTAT_COM_NONE,
};
let _controller = CreateDispatcherQueueController(options)?;
let instance = GetModuleHandleA(None)?;
let window_class = s!("window");
let wc = WNDCLASSA {
hCursor: LoadCursorW(None, IDC_ARROW)?,
hInstance: instance.into(),
lpszClassName: window_class,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(wndproc),
..Default::default()
};
RegisterClassA(&wc);
CreateWindowExA(
WINDOW_EX_STYLE::default(),
window_class,
s!("This is a sample window"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
None,
None,
instance,
None,
)?;
let mut message = MSG::default();
while GetMessageA(&mut message, None, 0, 0).into() {
DispatchMessageA(&message);
}
Ok(())
}
}
fn show_dialog() -> Result<()> {
let handler = DispatcherQueueHandler::new(move || unsafe {
let dialog: IFileSaveDialog = CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)?;
_ = dialog.Show(None);
Ok(())
});
let queue = DispatcherQueue::GetForCurrentThread()?;
queue.TryEnqueue(&handler)?;
Ok(())
}
extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
unsafe {
match message {
WM_PAINT => {
_ = ValidateRect(window, None);
LRESULT(0)
}
WM_KEYDOWN => {
show_dialog().unwrap();
LRESULT(0)
}
WM_DESTROY => {
PostQuitMessage(0);
LRESULT(0)
}
_ => DefWindowProcA(window, message, wparam, lparam),
}
}
} |
On my machine, the
It would be wonderful if you could find an expert to look into this issue. I'm looking forward to hearing some good news! |
Preliminary investigation seems to indicate that this is a known issue with the dispatcher queue - if possible you should avoid using it. |
Oh no... This is unfortunate news. Is there a similar API in Win32 or WinRT that serves the same function as |
You could write a simple closure queue that you drain from the message loop. |
You mean some code like this? // Dispatcher
fn dispatch_on_main() {
sender.send(runnable);
SetEvent(dispatch_event);
}
// UI thread
loop {
let wait_result = unsafe {
MsgWaitForMultipleObjects(Some(&[dispatch_event]), false, INFINITE, QS_ALLINPUT)
};
match wait_result {
0 => {
for runnable in receiver.drain() { runnable.run(); }
}
1 => {
// Message loop here
}
}
} |
It's worth a shot and you have one less dependency. 😊 |
Actually, this was the original implementation. However, we later switched to using |
Happy to look, although I have no particular expertise in IME or this bug. 🤷 |
No worries, you are the expert in every sense to me! |
Summary
As mentioned in the remarks section of Microsoft's documentation at here, you must specify one or more file types using the
FileTypeChoices
property before calling thePickSaveFileAsync
method. Typically, I would use theSetFileTypeChoices
method to set this property, but I haven't been able to find it. Could you please advise me on how to set this property?Crate manifest
No response
Crate code
No response
The text was updated successfully, but these errors were encountered: