windows: Dialog QoL improvements (#33241)

张小白 created

Just like in the previous PR #33230, we need to properly set up modal
windows to make them work as expected.

Before this PR, when you opened an "Open File" or "Save File" dialog,
clicking the main window would steal focus from the modal, even though
the main window wasn’t actually interactive.

With this PR, clicking the main window while a modal is open does
nothing — as it should — until the modal is closed.

#### Before



https://github.com/user-attachments/assets/9c6bdff0-1c46-49c1-a5ff-751c52c7d613

#### After



https://github.com/user-attachments/assets/8776bd28-85ff-4f32-8390-bcf5b4eec1fe





Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/platform.rs | 29 +++++++++++++++++----
1 file changed, 23 insertions(+), 6 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -295,6 +295,18 @@ impl WindowsPlatform {
             .log_err()
             .unwrap_or_default()
     }
+
+    fn find_current_active_window(&self) -> Option<HWND> {
+        let active_window_hwnd = unsafe { GetActiveWindow() };
+        if active_window_hwnd.is_invalid() {
+            return None;
+        }
+        self.raw_window_handles
+            .read()
+            .iter()
+            .find(|&&hwnd| hwnd == active_window_hwnd)
+            .copied()
+    }
 }
 
 impl Platform for WindowsPlatform {
@@ -473,9 +485,10 @@ impl Platform for WindowsPlatform {
         options: PathPromptOptions,
     ) -> Receiver<Result<Option<Vec<PathBuf>>>> {
         let (tx, rx) = oneshot::channel();
+        let window = self.find_current_active_window();
         self.foreground_executor()
             .spawn(async move {
-                let _ = tx.send(file_open_dialog(options));
+                let _ = tx.send(file_open_dialog(options, window));
             })
             .detach();
 
@@ -485,9 +498,10 @@ impl Platform for WindowsPlatform {
     fn prompt_for_new_path(&self, directory: &Path) -> Receiver<Result<Option<PathBuf>>> {
         let directory = directory.to_owned();
         let (tx, rx) = oneshot::channel();
+        let window = self.find_current_active_window();
         self.foreground_executor()
             .spawn(async move {
-                let _ = tx.send(file_save_dialog(directory));
+                let _ = tx.send(file_save_dialog(directory, window));
             })
             .detach();
 
@@ -754,7 +768,10 @@ fn open_target_in_explorer(target: &str) {
     }
 }
 
-fn file_open_dialog(options: PathPromptOptions) -> Result<Option<Vec<PathBuf>>> {
+fn file_open_dialog(
+    options: PathPromptOptions,
+    window: Option<HWND>,
+) -> Result<Option<Vec<PathBuf>>> {
     let folder_dialog: IFileOpenDialog =
         unsafe { CoCreateInstance(&FileOpenDialog, None, CLSCTX_ALL)? };
 
@@ -768,7 +785,7 @@ fn file_open_dialog(options: PathPromptOptions) -> Result<Option<Vec<PathBuf>>>
 
     unsafe {
         folder_dialog.SetOptions(dialog_options)?;
-        if folder_dialog.Show(None).is_err() {
+        if folder_dialog.Show(window).is_err() {
             // User cancelled
             return Ok(None);
         }
@@ -790,7 +807,7 @@ fn file_open_dialog(options: PathPromptOptions) -> Result<Option<Vec<PathBuf>>>
     Ok(Some(paths))
 }
 
-fn file_save_dialog(directory: PathBuf) -> Result<Option<PathBuf>> {
+fn file_save_dialog(directory: PathBuf, window: Option<HWND>) -> Result<Option<PathBuf>> {
     let dialog: IFileSaveDialog = unsafe { CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)? };
     if !directory.to_string_lossy().is_empty() {
         if let Some(full_path) = directory.canonicalize().log_err() {
@@ -806,7 +823,7 @@ fn file_save_dialog(directory: PathBuf) -> Result<Option<PathBuf>> {
             pszName: windows::core::w!("All files"),
             pszSpec: windows::core::w!("*.*"),
         }])?;
-        if dialog.Show(None).is_err() {
+        if dialog.Show(window).is_err() {
             // User cancelled
             return Ok(None);
         }