From 2e16ded5d447820701d26e27e0820c137ae1efb9 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Mon, 6 Jan 2025 19:22:15 +0100 Subject: [PATCH] System File Chooser: support macOS in class `SystemFileChooser` --- .../flatlaf/ui/FlatNativeMacLibrary.java | 5 +- .../flatlaf/util/SystemFileChooser.java | 54 ++++++++++++++++++- .../src/main/objcpp/MacFileChooser.mm | 19 ++++--- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeMacLibrary.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeMacLibrary.java index 5ceb2f61..73bef9b7 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeMacLibrary.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeMacLibrary.java @@ -72,7 +72,7 @@ public class FlatNativeMacLibrary /** @since 3.6 */ public static final int - // NSOpenPanel + // NSOpenPanel (extends NSSavePanel) FC_canChooseFiles = 1 << 0, // default FC_canChooseDirectories = 1 << 1, FC_resolvesAliases = 1 << 2, // default @@ -105,7 +105,8 @@ public class FlatNativeMacLibrary * @param optionsSet options to set; see {@code FC_*} constants * @param optionsClear options to clear; see {@code FC_*} constants * @param allowedFileTypes allowed filename extensions (e.g. "txt") - * @return file path(s) that the user selected, or {@code null} if canceled + * @return file path(s) that the user selected; an empty array if canceled; + * or {@code null} on failures (no dialog shown) * * @since 3.6 */ 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 d5d6d773..23f6a2d6 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 @@ -31,6 +31,7 @@ import javax.swing.UIManager; import javax.swing.filechooser.FileSystemView; import com.formdev.flatlaf.FlatSystemProperties; import com.formdev.flatlaf.ui.FlatNativeLinuxLibrary; +import com.formdev.flatlaf.ui.FlatNativeMacLibrary; import com.formdev.flatlaf.ui.FlatNativeWindowsLibrary; /** @@ -61,7 +62,11 @@ import com.formdev.flatlaf.ui.FlatNativeWindowsLibrary; * If user selects "Yes", the file chooser closes. If user selects "No", the file chooser stays open. * It is not possible to customize that question dialog. *
  • Save File dialog does not support multi-selection. + *
  • For selection mode {@link #DIRECTORIES_ONLY}, dialog type {@link #SAVE_DIALOG} is ignored. + * Operating system file dialogs support folder selection only in "Open" dialogs. *
  • {@link JFileChooser#FILES_AND_DIRECTORIES} is not supported. + *
  • {@link #getSelectedFiles()} returns selected file also in single selection mode. + * {@link JFileChooser#getSelectedFiles()} only in multi selection mode. * * * @author Karl Tauber @@ -289,6 +294,8 @@ public class SystemFileChooser if( SystemInfo.isWindows_10_orLater && FlatNativeWindowsLibrary.isLoaded() ) return new WindowsFileChooserProvider(); + else if( SystemInfo.isMacOS && FlatNativeMacLibrary.isLoaded() ) + return new MacFileChooserProvider(); else if( SystemInfo.isLinux && FlatNativeLinuxLibrary.isLoaded() ) return new LinuxFileChooserProvider(); else // unknown platform or FlatLaf native library not loaded @@ -413,7 +420,52 @@ public class SystemFileChooser } } - //---- class LinuxFileChooserProvider -----------------------------------.. + //---- class MacFileChooserProvider --------------------------------------- + + private static class MacFileChooserProvider + extends SystemFileChooserProvider + { + @Override + String[] showSystemDialog( Window owner, SystemFileChooser fc ) { + boolean open = (fc.getDialogType() == OPEN_DIALOG); + String nameFieldStringValue = null; + String directoryURL = null; + + // paths + File currentDirectory = fc.getCurrentDirectory(); + File selectedFile = fc.getSelectedFile(); + if( selectedFile != null ) { + if( selectedFile.isDirectory() ) + directoryURL = selectedFile.getAbsolutePath(); + else { + nameFieldStringValue = selectedFile.getName(); + directoryURL = selectedFile.getParent(); + } + } else if( currentDirectory != null ) + directoryURL = currentDirectory.getAbsolutePath(); + + // options + int optionsSet = 0; + int optionsClear = 0; + if( fc.isDirectorySelectionEnabled() ) { + optionsSet |= FlatNativeMacLibrary.FC_canChooseDirectories; + optionsClear |= FlatNativeMacLibrary.FC_canChooseFiles; + open = true; + } + if( fc.isMultiSelectionEnabled() ) + optionsSet |= FlatNativeMacLibrary.FC_allowsMultipleSelection; + if( !fc.isFileHidingEnabled() ) + optionsSet |= FlatNativeMacLibrary.FC_showsHiddenFiles; + + // show system file dialog + return FlatNativeMacLibrary.showFileChooser( open, + fc.getDialogTitle(), fc.getApproveButtonText(), null, null, + nameFieldStringValue, directoryURL, + optionsSet, optionsClear ); + } + } + + //---- class LinuxFileChooserProvider ------------------------------------- private static class LinuxFileChooserProvider extends SystemFileChooserProvider diff --git a/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/MacFileChooser.mm b/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/MacFileChooser.mm index 05fd3d67..9755c5ab 100644 --- a/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/MacFileChooser.mm +++ b/flatlaf-natives/flatlaf-natives-macos/src/main/objcpp/MacFileChooser.mm @@ -23,15 +23,23 @@ /** * @author Karl Tauber + * @since 3.6 */ +//---- helper ----------------------------------------------------------------- + #define isOptionSet( option ) ((optionsSet & com_formdev_flatlaf_ui_FlatNativeMacLibrary_ ## option) != 0) #define isOptionClear( option ) ((optionsClear & com_formdev_flatlaf_ui_FlatNativeMacLibrary_ ## option) != 0) #define isOptionSetOrClear( option ) isOptionSet( option ) || isOptionClear( option ) -// declare internal methods +// declare methods NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ); +jobjectArray newJavaStringArray( JNIEnv* env, jsize count ) { + jclass stringClass = env->FindClass( "java/lang/String" ); + return env->NewObjectArray( count, stringClass, NULL ); +} + //---- JNI methods ------------------------------------------------------------ extern "C" @@ -135,19 +143,18 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_ urls = @[url]; if( urls == NULL ) - return NULL; + return newJavaStringArray( env, 0 ); // convert URLs to Java string array jsize count = urls.count; - jclass stringClass = env->FindClass( "java/lang/String" ); - jobjectArray result = env->NewObjectArray( count, stringClass, NULL ); + jobjectArray array = newJavaStringArray( env, count ); for( int i = 0; i < count; i++ ) { jstring filename = NormalizedPathJavaFromNSString( env, [urls[i] path] ); - env->SetObjectArrayElement( result, i, filename ); + env->SetObjectArrayElement( array, i, filename ); env->DeleteLocalRef( filename ); } - return result; + return array; JNI_COCOA_EXIT() }