System File Chooser: added "Format" combobox on macOS (if using more than one filter)

This commit is contained in:
Karl Tauber
2025-01-07 14:37:58 +01:00
parent 2e16ded5d4
commit 9af7f95197
6 changed files with 358 additions and 99 deletions

View File

@@ -21,6 +21,8 @@ extern "C" {
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_resolvesAliases 4L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_allowsMultipleSelection
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_allowsMultipleSelection 8L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_accessoryViewDisclosed
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_accessoryViewDisclosed 16L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsTagField
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsTagField 256L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canCreateDirectories
@@ -35,6 +37,8 @@ extern "C" {
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_allowsOtherFileTypes 8192L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_treatsFilePackagesAsDirectories
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_treatsFilePackagesAsDirectories 16384L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showSingleFilterField
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showSingleFilterField 16777216L
/*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: setWindowRoundedBorder
@@ -78,10 +82,10 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_togg
/*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: showFileChooser
* Signature: (ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II[Ljava/lang/String;)[Ljava/lang/String;
* Signature: (ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;III[Ljava/lang/String;)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_showFileChooser
(JNIEnv *, jclass, jboolean, jstring, jstring, jstring, jstring, jstring, jstring, jint, jint, jobjectArray);
(JNIEnv *, jclass, jboolean, jstring, jstring, jstring, jstring, jstring, jstring, jstring, jint, jint, jint, jobjectArray);
#ifdef __cplusplus
}

View File

@@ -26,6 +26,80 @@
* @since 3.6
*/
//---- class FileChooserDelegate ----------------------------------------------
@interface FileChooserDelegate : NSObject {
NSArray* _filters;
}
@property (nonatomic, assign) NSSavePanel* dialog;
- (void)initFilterAccessoryView: (NSMutableArray*)filters :(int)filterIndex
:(NSString*)filterFieldLabel :(bool)showSingleFilterField;
- (void)selectFormat: (id)sender;
- (void)selectFormatAtIndex: (int)index;
@end
@implementation FileChooserDelegate
- (void)initFilterAccessoryView: (NSMutableArray*)filters :(int)filterIndex
:(NSString*)filterFieldLabel :(bool)showSingleFilterField
{
_filters = filters;
// get filter names
NSArray* filterNames = filters.lastObject;
[filters removeLastObject];
// do not add filter/format combobox if there is only one filter
if( filters.count <= 1 && !showSingleFilterField ) {
[self selectFormatAtIndex:0];
return;
}
// create label
NSTextField* label = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 60, 22 )];
label.stringValue = (filterFieldLabel != NULL) ? filterFieldLabel : @"Format:";
label.editable = NO;
label.bordered = NO;
label.bezeled = NO;
label.drawsBackground = NO;
// create combobox
NSPopUpButton* popupButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect( 50, 2, 140, 22 ) pullsDown:NO];
[popupButton addItemsWithTitles:filterNames];
[popupButton selectItemAtIndex:MIN( MAX( filterIndex, 0 ), filterNames.count - 1 )];
[popupButton setTarget:self];
[popupButton setAction:@selector(selectFormat:)];
// create view
NSView* accessoryView = [[NSView alloc] initWithFrame:NSMakeRect( 0, 0, 200, 32 )];
[accessoryView addSubview:label];
[accessoryView addSubview:popupButton];
[_dialog setAccessoryView:accessoryView];
// initial filter
[self selectFormatAtIndex:filterIndex];
}
- (void)selectFormat:(id)sender {
NSPopUpButton* popupButton = (NSPopUpButton*) sender;
[self selectFormatAtIndex:popupButton.indexOfSelectedItem];
}
- (void)selectFormatAtIndex: (int)index {
index = MIN( MAX( index, 0 ), _filters.count - 1 );
NSArray* fileTypes = [_filters objectAtIndex:index];
// use deprecated allowedFileTypes instead of newer allowedContentTypes (since macOS 11+)
// to support older macOS versions 10.14+ and because of some problems with allowedContentTypes:
// https://github.com/chromium/chromium/blob/d8e0032963b7ca4728ff4117933c0feb3e479b7a/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm#L209-232
_dialog.allowedFileTypes = [fileTypes containsObject:@"*"] ? nil : fileTypes;
}
@end
//---- helper -----------------------------------------------------------------
#define isOptionSet( option ) ((optionsSet & com_formdev_flatlaf_ui_FlatNativeMacLibrary_ ## option) != 0)
@@ -40,14 +114,55 @@ jobjectArray newJavaStringArray( JNIEnv* env, jsize count ) {
return env->NewObjectArray( count, stringClass, NULL );
}
static NSMutableArray* initFilters( JNIEnv* env, jobjectArray fileTypes ) {
jint length = env->GetArrayLength( fileTypes );
if( length <= 0 )
return NULL;
NSMutableArray* filterNames = [NSMutableArray array];
NSMutableArray* filters = [NSMutableArray array];
NSString* filterName = NULL;
NSMutableArray* filter = NULL;
for( int i = 0; i < length; i++ ) {
jstring jstr = (jstring) env->GetObjectArrayElement( fileTypes, i );
if( jstr == NULL ) {
if( filter != NULL ) {
if( filter.count > 0 ) {
[filterNames addObject:filterName];
[filters addObject:filter];
}
filterName = NULL;
filter = NULL;
}
continue;
}
NSString* str = JavaToNSString( env, jstr );
env->DeleteLocalRef( jstr );
if( filter == NULL ) {
filterName = str;
filter = [NSMutableArray array];
} else
[filter addObject:str];
}
if( filters.count == 0 )
return NULL;
// add filter names to array (removed again after creating combobox)
[filters addObject:filterNames];
return filters;
}
//---- JNI methods ------------------------------------------------------------
extern "C"
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_showFileChooser
( JNIEnv* env, jclass cls, jboolean open,
jstring title, jstring prompt, jstring message, jstring nameFieldLabel,
jstring nameFieldStringValue, jstring directoryURL,
jint optionsSet, jint optionsClear, jobjectArray allowedFileTypes )
jstring title, jstring prompt, jstring message, jstring filterFieldLabel,
jstring nameFieldLabel, jstring nameFieldStringValue, jstring directoryURL,
jint optionsSet, jint optionsClear, jint fileTypeIndex, jobjectArray fileTypes )
{
JNI_COCOA_ENTER()
@@ -55,21 +170,11 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_
NSString* nsTitle = JavaToNSString( env, title );
NSString* nsPrompt = JavaToNSString( env, prompt );
NSString* nsMessage = JavaToNSString( env, message );
NSString* nsFilterFieldLabel = JavaToNSString( env, filterFieldLabel );
NSString* nsNameFieldLabel = JavaToNSString( env, nameFieldLabel );
NSString* nsNameFieldStringValue = JavaToNSString( env, nameFieldStringValue );
NSString* nsDirectoryURL = JavaToNSString( env, directoryURL );
NSArray* nsAllowedFileTypes = NULL;
jsize len = env->GetArrayLength( allowedFileTypes );
if( len > 0 ) {
NSMutableArray* nsArray = [NSMutableArray arrayWithCapacity:len];
for( int i = 0; i < len; i++ ) {
jstring str = (jstring) env->GetObjectArrayElement( allowedFileTypes, i );
nsArray[i] = JavaToNSString( env, str );
env->DeleteLocalRef( str );
}
nsAllowedFileTypes = nsArray;
}
NSMutableArray* filters = initFilters( env, fileTypes );
NSArray* urls = NULL;
NSArray** purls = &urls;
@@ -93,6 +198,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_
if( nsDirectoryURL != NULL )
dialog.directoryURL = [NSURL fileURLWithPath:nsDirectoryURL isDirectory:YES];
// set open options
if( open ) {
NSOpenPanel* openDialog = (NSOpenPanel*) dialog;
@@ -109,6 +215,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_
openDialog.allowsMultipleSelection = isOptionSet( FC_allowsMultipleSelection );
}
// set options
if( isOptionSetOrClear( FC_showsTagField ) )
dialog.showsTagField = isOptionSet( FC_showsTagField );
if( isOptionSetOrClear( FC_canCreateDirectories ) )
@@ -124,13 +231,21 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_
if( isOptionSetOrClear( FC_treatsFilePackagesAsDirectories ) )
dialog.treatsFilePackagesAsDirectories = isOptionSet( FC_treatsFilePackagesAsDirectories );
// use deprecated allowedFileTypes instead of newer allowedContentTypes (since macOS 11+)
// to support older macOS versions 10.14+ and because of some problems with allowedContentTypes:
// https://github.com/chromium/chromium/blob/d8e0032963b7ca4728ff4117933c0feb3e479b7a/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm#L209-232
if( nsAllowedFileTypes != NULL )
dialog.allowedFileTypes = nsAllowedFileTypes;
FileChooserDelegate* delegate = [FileChooserDelegate new];
delegate.dialog = dialog;
if( [dialog runModal] != NSModalResponseOK )
// initialize filter accessory view
if( filters != NULL ) {
[delegate initFilterAccessoryView:filters :fileTypeIndex :nsFilterFieldLabel :isOptionSet( FC_showSingleFilterField )];
if( open && isOptionSetOrClear( FC_accessoryViewDisclosed ) )
((NSOpenPanel*)dialog).accessoryViewDisclosed = isOptionSet( FC_accessoryViewDisclosed );
}
// show dialog
NSModalResponse response = [dialog runModal];
[delegate release];
if( response != NSModalResponseOK )
return;
if( open )