diff --git a/CHANGELOG.md b/CHANGELOG.md index 00e52c37..e505b8fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ FlatLaf Change Log - System File Chooser: - Update current filter before invoking approve callback and after closing dialog. (issue #1065) + - Fixed: System and Swing file dialogs were shown at the same time if + application has no other displayable window. (issue #1078) - On Linux: Check whether required GSettings schemas are installed to avoid application crash (occurred on NixOS with Plasma/KDE desktop). (issue #1069) - ComboBox: Added UI property `ComboBox.buttonFocusedEditableBackground`. (issue diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/SystemFileChooser.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/SystemFileChooser.java index 3c63d232..f05b892c 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/SystemFileChooser.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/SystemFileChooser.java @@ -18,6 +18,8 @@ package com.formdev.flatlaf.util; import java.awt.Component; import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; import java.awt.KeyboardFocusManager; import java.awt.SecondaryLoop; import java.awt.Toolkit; @@ -743,6 +745,9 @@ public class SystemFileChooser } private int showDialogImpl( Component parent ) { + if( !EventQueue.isDispatchThread() ) + throw new IllegalStateException( "Must be invoked from the AWT/Swing event dispatch thread" ); + Window owner = (parent instanceof Window) ? (Window) parent : (parent != null) ? SwingUtilities.windowForComponent( parent ) : null; @@ -791,6 +796,16 @@ public class SystemFileChooser { @Override public File[] showDialog( Window owner, SystemFileChooser fc ) { + // if there is no displayable window, then AWT's auto-shutdown feature + // quits our secondary event loop (see below) immediately + // https://docs.oracle.com/en/java/javase/25/docs/api/java.desktop/java/awt/doc-files/AWTThreadIssues.html#Autoshutdown + Window dummyWindow = null; + if( !hasDisplayableWindow( owner ) ) { + // create a (not visible) displayable window to avoid AWT auto-shutdown + dummyWindow = new Window( (Frame) null ); + dummyWindow.addNotify(); + } + AtomicReference filenamesRef = new AtomicReference<>(); // create secondary event look and invoke system file dialog on a new thread @@ -801,6 +816,10 @@ public class SystemFileChooser }, "FlatLaf SystemFileChooser" ).start(); secondaryLoop.enter(); + // dispose dummy window to allow AWT to auto-shutdown + if( dummyWindow != null ) + dummyWindow.dispose(); + String[] filenames = filenamesRef.get(); // fallback to Swing file chooser if system file dialog failed or is not available @@ -837,6 +856,17 @@ public class SystemFileChooser files[i] = fsv.createFileObject( filenames[i] ); return files; } + + private static boolean hasDisplayableWindow( Window owner ) { + if( owner != null && owner.isDisplayable() ) + return true; + + for( Window window : Window.getWindows() ) { + if( window.isDisplayable() ) + return true; + } + return false; + } } //---- class WindowsFileChooserProvider -----------------------------------