mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
System File Chooser: implemented native bindings for NSOpenPanel and NSSavePanel on macOS
This commit is contained in:
@@ -44,3 +44,7 @@
|
||||
jclass findClass( JNIEnv *env, const char* className, bool globalRef );
|
||||
jfieldID getFieldID( JNIEnv *env, jclass cls, const char* fieldName, const char* fieldSignature, bool staticField );
|
||||
jmethodID getMethodID( JNIEnv *env, jclass cls, const char* methodName, const char* methodSignature, bool staticMethod );
|
||||
|
||||
NSString* JavaToNSString( JNIEnv *env, jstring javaString );
|
||||
jstring NSToJavaString( JNIEnv *env, NSString *nsString );
|
||||
jstring NormalizedPathJavaFromNSString( JNIEnv* env, NSString *nsString );
|
||||
|
||||
@@ -13,6 +13,32 @@ extern "C" {
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_MEDIUM 1L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_LARGE
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTONS_SPACING_LARGE 2L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canChooseFiles
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canChooseFiles 1L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canChooseDirectories
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canChooseDirectories 2L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_resolvesAliases_NO
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_resolvesAliases_NO 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_showsTagField_YES
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsTagField_YES 256L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsTagField_NO
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsTagField_NO 512L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canCreateDirectories_YES
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canCreateDirectories_YES 1024L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canCreateDirectories_NO
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canCreateDirectories_NO 2048L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canSelectHiddenExtension
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_canSelectHiddenExtension 4096L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsHiddenFiles
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_showsHiddenFiles 16384L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_extensionHidden
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_extensionHidden 65536L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_allowsOtherFileTypes
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_allowsOtherFileTypes 262144L
|
||||
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_treatsFilePackagesAsDirectories
|
||||
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_FC_treatsFilePackagesAsDirectories 1048576L
|
||||
/*
|
||||
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
|
||||
* Method: setWindowRoundedBorder
|
||||
@@ -53,6 +79,14 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_isWi
|
||||
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_toggleWindowFullScreen
|
||||
(JNIEnv *, jclass, jobject);
|
||||
|
||||
/*
|
||||
* 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;I[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, jobjectArray);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
|
||||
#import <jni.h>
|
||||
#import "com_formdev_flatlaf_ui_FlatNativeLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
// increase this version if changing API or functionality of native library
|
||||
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeMacLibrary
|
||||
#define API_VERSION_MACOS 2001
|
||||
#define API_VERSION_MACOS 2002
|
||||
|
||||
|
||||
//---- JNI methods ------------------------------------------------------------
|
||||
|
||||
@@ -75,3 +75,38 @@ jmethodID getMethodID( JNIEnv *env, jclass cls, const char* methodName, const ch
|
||||
|
||||
return methodID;
|
||||
}
|
||||
|
||||
NSString* JavaToNSString( JNIEnv *env, jstring javaString ) {
|
||||
if( javaString == NULL )
|
||||
return NULL;
|
||||
|
||||
int len = env->GetStringLength( javaString );
|
||||
const jchar* chars = env->GetStringChars( javaString, NULL );
|
||||
if( chars == NULL )
|
||||
return NULL;
|
||||
|
||||
NSString* nsString = [NSString stringWithCharacters:(unichar*)chars length:len];
|
||||
env->ReleaseStringChars( javaString, chars );
|
||||
return nsString;
|
||||
}
|
||||
|
||||
jstring NSToJavaString( JNIEnv *env, NSString *nsString ) {
|
||||
if( nsString == NULL )
|
||||
return NULL;
|
||||
|
||||
jsize len = [nsString length];
|
||||
unichar* buffer = (unichar*) calloc( len, sizeof( unichar ) );
|
||||
if( buffer == NULL )
|
||||
return NULL;
|
||||
|
||||
[nsString getCharacters:buffer];
|
||||
jstring javaString = env->NewString( buffer, len );
|
||||
free( buffer );
|
||||
return javaString;
|
||||
}
|
||||
|
||||
jstring NormalizedPathJavaFromNSString( JNIEnv* env, NSString *nsString ) {
|
||||
return (nsString != NULL)
|
||||
? NSToJavaString( env, [nsString precomposedStringWithCanonicalMapping] )
|
||||
: NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2024 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <jni.h>
|
||||
#import "JNIUtils.h"
|
||||
#import "JNFRunLoop.h"
|
||||
#import "com_formdev_flatlaf_ui_FlatNativeMacLibrary.h"
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
#define isOptionSet( option ) ((options & com_formdev_flatlaf_ui_FlatNativeMacLibrary_ ## option) != 0)
|
||||
#define isYesOrNoOptionSet( option ) isOptionSet( option ## _YES ) || isOptionSet( option ## _NO )
|
||||
|
||||
// declare internal methods
|
||||
NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window );
|
||||
|
||||
//---- 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 options, jobjectArray allowedFileTypes )
|
||||
{
|
||||
JNI_COCOA_ENTER()
|
||||
|
||||
// convert Java strings to NSString (on Java thread)
|
||||
NSString* nsTitle = JavaToNSString( env, title );
|
||||
NSString* nsPrompt = JavaToNSString( env, prompt );
|
||||
NSString* nsMessage = JavaToNSString( env, message );
|
||||
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 );
|
||||
NSString* nsStr = JavaToNSString( env, str );
|
||||
nsArray[i] = nsStr;
|
||||
}
|
||||
nsAllowedFileTypes = nsArray;
|
||||
}
|
||||
|
||||
NSArray* urls = NULL;
|
||||
NSArray** purls = &urls;
|
||||
NSURL* url = NULL;
|
||||
NSURL** purl = &url;
|
||||
|
||||
// show file dialog on macOS thread
|
||||
[FlatJNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
|
||||
NSSavePanel* dialog = open ? [NSOpenPanel openPanel] : [NSSavePanel savePanel];
|
||||
|
||||
if( nsTitle != NULL )
|
||||
dialog.title = nsTitle;
|
||||
if( nsPrompt != NULL )
|
||||
dialog.prompt = nsPrompt;
|
||||
if( nsMessage != NULL )
|
||||
dialog.message = nsMessage;
|
||||
if( nsNameFieldLabel != NULL )
|
||||
dialog.nameFieldLabel = nsNameFieldLabel;
|
||||
if( nsNameFieldStringValue != NULL )
|
||||
dialog.nameFieldStringValue = nsNameFieldStringValue;
|
||||
if( nsDirectoryURL != NULL )
|
||||
dialog.directoryURL = [NSURL fileURLWithPath:nsDirectoryURL isDirectory:YES];
|
||||
|
||||
if( open ) {
|
||||
NSOpenPanel* openDialog = (NSOpenPanel*) dialog;
|
||||
|
||||
bool canChooseFiles = isOptionSet( FC_canChooseFiles );
|
||||
bool canChooseDirectories = isOptionSet( FC_canChooseDirectories );
|
||||
if( !canChooseFiles && !canChooseDirectories )
|
||||
canChooseFiles = true;
|
||||
openDialog.canChooseFiles = canChooseFiles;
|
||||
openDialog.canChooseDirectories = canChooseDirectories;
|
||||
if( isOptionSet( FC_resolvesAliases_NO ) )
|
||||
openDialog.resolvesAliases = NO;
|
||||
if( isOptionSet( FC_allowsMultipleSelection ) )
|
||||
openDialog.allowsMultipleSelection = YES;
|
||||
}
|
||||
|
||||
if( isYesOrNoOptionSet( FC_showsTagField ) )
|
||||
dialog.showsTagField = isOptionSet( FC_showsTagField_YES );
|
||||
if( isYesOrNoOptionSet( FC_canCreateDirectories ) )
|
||||
dialog.canCreateDirectories = isOptionSet( FC_canCreateDirectories_YES );
|
||||
if( isOptionSet( FC_canSelectHiddenExtension ) )
|
||||
dialog.canSelectHiddenExtension = YES;
|
||||
if( isOptionSet( FC_showsHiddenFiles) )
|
||||
dialog.showsHiddenFiles = YES;
|
||||
if( isOptionSet( FC_extensionHidden ) )
|
||||
dialog.extensionHidden = YES;
|
||||
if( isOptionSet( FC_allowsOtherFileTypes ) )
|
||||
dialog.allowsOtherFileTypes = YES;
|
||||
if( isOptionSet( FC_treatsFilePackagesAsDirectories ) )
|
||||
dialog.treatsFilePackagesAsDirectories = YES;
|
||||
|
||||
// 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;
|
||||
|
||||
if( [dialog runModal] != NSModalResponseOK )
|
||||
return;
|
||||
|
||||
if( open )
|
||||
*purls = ((NSOpenPanel*)dialog).URLs;
|
||||
else
|
||||
*purl = dialog.URL;
|
||||
}];
|
||||
|
||||
if( url != NULL )
|
||||
urls = @[url];
|
||||
|
||||
if( urls == NULL )
|
||||
return NULL;
|
||||
|
||||
// convert URLs to Java string array
|
||||
jsize count = urls.count;
|
||||
jclass stringClass = env->FindClass( "java/lang/String" );
|
||||
jobjectArray result = env->NewObjectArray( count, stringClass, NULL );
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
jstring filename = NormalizedPathJavaFromNSString( env, [urls[i] path] );
|
||||
env->SetObjectArrayElement( result, i, filename );
|
||||
env->DeleteLocalRef( filename );
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
JNI_COCOA_EXIT()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user