System File Chooser: Linux: show file dialog in dark if current FlatLaf theme is dark (PR #988)
Some checks failed
CI / build (push) Has been cancelled
CI / release (push) Has been cancelled
Error Prone / error-prone (push) Has been cancelled
Native Libraries / Natives (macos-latest) (push) Has been cancelled
Native Libraries / Natives (ubuntu-24.04-arm) (push) Has been cancelled
Native Libraries / Natives (ubuntu-latest) (push) Has been cancelled
Native Libraries / Natives (windows-latest) (push) Has been cancelled

This commit is contained in:
Karl Tauber
2025-12-02 14:11:10 +01:00
parent 58e073a05b
commit 9e8b8697d1
6 changed files with 60 additions and 10 deletions

View File

@@ -37,7 +37,7 @@ import com.formdev.flatlaf.util.SystemInfo;
*/ */
public class FlatNativeLinuxLibrary public class FlatNativeLinuxLibrary
{ {
private static int API_VERSION_LINUX = 3002; private static int API_VERSION_LINUX = 3003;
/** /**
* Checks whether native library is loaded/available. * Checks whether native library is loaded/available.
@@ -178,6 +178,7 @@ public class FlatNativeLinuxLibrary
* to avoid blocking the AWT event dispatching thread. * to avoid blocking the AWT event dispatching thread.
* *
* @param owner the owner of the file dialog; or {@code null} * @param owner the owner of the file dialog; or {@code null}
* @param dark preferred appearance of the file dialog: {@code 1} = prefer dark, {@code 0} = prefer light, {@code -1} = default
* @param open if {@code true}, shows the open dialog; if {@code false}, shows the save dialog * @param open if {@code true}, shows the open dialog; if {@code false}, shows the save dialog
* @param title text displayed in dialog title; or {@code null} * @param title text displayed in dialog title; or {@code null}
* @param okButtonLabel text displayed in default button; or {@code null}. * @param okButtonLabel text displayed in default button; or {@code null}.
@@ -199,7 +200,7 @@ public class FlatNativeLinuxLibrary
* *
* @since 3.7 * @since 3.7
*/ */
public native static String[] showFileChooser( Window owner, boolean open, public native static String[] showFileChooser( Window owner, int dark, boolean open,
String title, String okButtonLabel, String currentName, String currentFolder, String title, String okButtonLabel, String currentName, String currentFolder,
int optionsSet, int optionsClear, FileChooserCallback callback, int optionsSet, int optionsClear, FileChooserCallback callback,
int fileTypeIndex, String... fileTypes ); int fileTypeIndex, String... fileTypes );

View File

@@ -1085,6 +1085,7 @@ public class SystemFileChooser
@Override @Override
String[] showSystemDialog( Window owner, SystemFileChooser fc ) { String[] showSystemDialog( Window owner, SystemFileChooser fc ) {
int dark = FlatLaf.isLafDark() ? 1 : 0;
boolean open = (fc.getDialogType() == OPEN_DIALOG); boolean open = (fc.getDialogType() == OPEN_DIALOG);
String approveButtonText = fc.getApproveButtonText(); String approveButtonText = fc.getApproveButtonText();
int approveButtonMnemonic = fc.getApproveButtonMnemonic(); int approveButtonMnemonic = fc.getApproveButtonMnemonic();
@@ -1162,7 +1163,7 @@ public class SystemFileChooser
} : null; } : null;
// show system file dialog // show system file dialog
return FlatNativeLinuxLibrary.showFileChooser( owner, open, return FlatNativeLinuxLibrary.showFileChooser( owner, dark, open,
fc.getDialogTitle(), approveButtonText, currentName, currentFolder, fc.getDialogTitle(), approveButtonText, currentName, currentFolder,
optionsSet, optionsClear, callback, optionsSet, optionsClear, callback,
fileTypeIndex, fileTypes.toArray( new String[fileTypes.size()] ) ); fileTypeIndex, fileTypes.toArray( new String[fileTypes.size()] ) );

View File

@@ -24,7 +24,7 @@
// increase this version if changing API or functionality of native library // increase this version if changing API or functionality of native library
// also update version in Java class com.formdev.flatlaf.ui.FlatNativeLinuxLibrary // also update version in Java class com.formdev.flatlaf.ui.FlatNativeLinuxLibrary
#define API_VERSION_LINUX 3002 #define API_VERSION_LINUX 3003
//---- JNI methods ------------------------------------------------------------ //---- JNI methods ------------------------------------------------------------

View File

@@ -168,7 +168,7 @@ static void handle_response( GtkWidget* dialog, gint responseId, gpointer data )
extern "C" extern "C"
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser
( JNIEnv* env, jclass cls, jobject owner, jboolean open, ( JNIEnv* env, jclass cls, jobject owner, jint dark, jboolean open,
jstring title, jstring okButtonLabel, jstring currentName, jstring currentFolder, jstring title, jstring okButtonLabel, jstring currentName, jstring currentFolder,
jint optionsSet, jint optionsClear, jobject callback, jint fileTypeIndex, jobjectArray fileTypes ) jint optionsSet, jint optionsClear, jobject callback, jint fileTypeIndex, jobjectArray fileTypes )
{ {
@@ -226,10 +226,40 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrar
gtk_window_set_modal( GTK_WINDOW( dialog ), true ); gtk_window_set_modal( GTK_WINDOW( dialog ), true );
// file dialog should use same screen as owner // file dialog should use same screen as owner
gtk_window_set_screen( GTK_WINDOW( dialog ), gdk_window_get_screen( gdkOwner ) ); GdkScreen* screen = gdk_window_get_screen( gdkOwner );
gtk_window_set_screen( GTK_WINDOW( dialog ), screen );
// set the transient when the file dialog is realized // set the transient when the file dialog is realized
g_signal_connect( dialog, "realize", G_CALLBACK( handle_realize ), gdkOwner ); g_signal_connect( dialog, "realize", G_CALLBACK( handle_realize ), gdkOwner );
// set light/dark appearance
if( dark >= 0 ) {
GtkSettings *settings = gtk_settings_get_for_screen( screen );
// get current GTK theme
gchar* currentGtkTheme;
g_object_get( settings, "gtk-theme-name", &currentGtkTheme, NULL );
const char* darkSuffix = "-dark";
bool isDarkGtkTheme = g_str_has_suffix( currentGtkTheme, darkSuffix );
if( isDarkGtkTheme && dark == 0 ) {
// current GTK theme is dark, but FlatLaf theme is light
// in this case, "gtk-application-prefer-dark-theme" does not work
// and there is no "gtk-application-prefer-light-theme" setting
// --> try to switch to light GTK theme (if available)
gchar* lightGtkTheme = g_strndup( currentGtkTheme, strlen( currentGtkTheme ) - strlen( darkSuffix ) );
gchar* themeDir = g_strdup_printf( "/usr/share/themes/%s", lightGtkTheme );
if( g_file_test( themeDir, G_FILE_TEST_IS_DIR ) )
g_object_set( settings, "gtk-theme-name", lightGtkTheme, NULL );
g_free( themeDir );
g_free( lightGtkTheme );
}
g_free( currentGtkTheme );
// let GTK know whether we prefer a dark theme
g_object_set( settings, "gtk-application-prefer-dark-theme", (dark == 1), NULL );
}
} }
// show dialog // show dialog

View File

@@ -7,6 +7,22 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_TOPLEFT
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_TOPLEFT 0L
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_TOP
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_TOP 1L
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_TOPRIGHT
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_TOPRIGHT 2L
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_RIGHT
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_RIGHT 3L
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_BOTTOMRIGHT
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_BOTTOMRIGHT 4L
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_BOTTOM
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_BOTTOM 5L
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_BOTTOMLEFT
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_BOTTOMLEFT 6L
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_LEFT
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_SIZE_LEFT 7L
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_MOVE #undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_MOVE
#define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_MOVE 8L #define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_MOVE 8L
#undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_select_folder #undef com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_FC_select_folder
@@ -48,10 +64,10 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_is
/* /*
* Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary * Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary
* Method: showFileChooser * Method: showFileChooser
* Signature: (Ljava/awt/Window;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILcom/formdev/flatlaf/ui/FlatNativeLinuxLibrary/FileChooserCallback;I[Ljava/lang/String;)[Ljava/lang/String; * Signature: (Ljava/awt/Window;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILcom/formdev/flatlaf/ui/FlatNativeLinuxLibrary/FileChooserCallback;I[Ljava/lang/String;)[Ljava/lang/String;
*/ */
JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_showFileChooser
(JNIEnv *, jclass, jobject, jboolean, jstring, jstring, jstring, jstring, jint, jint, jobject, jint, jobjectArray); (JNIEnv *, jclass, jobject, jint, jboolean, jstring, jstring, jstring, jstring, jint, jint, jobject, jint, jobjectArray);
/* /*
* Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary * Class: com_formdev_flatlaf_ui_FlatNativeLinuxLibrary

View File

@@ -24,6 +24,7 @@ import java.awt.Window;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.*; import javax.swing.*;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.extras.components.*; import com.formdev.flatlaf.extras.components.*;
import com.formdev.flatlaf.extras.components.FlatTriStateCheckBox.State; import com.formdev.flatlaf.extras.components.FlatTriStateCheckBox.State;
import com.formdev.flatlaf.testing.FlatSystemFileChooserTest.DummyModalDialog; import com.formdev.flatlaf.testing.FlatSystemFileChooserTest.DummyModalDialog;
@@ -121,8 +122,9 @@ public class FlatSystemFileChooserLinuxTest
System.out.println( FlatNativeLinuxLibrary.isGtk3Available() ); System.out.println( FlatNativeLinuxLibrary.isGtk3Available() );
int dark = FlatLaf.isLafDark() ? 1 : 0;
if( direct ) { if( direct ) {
String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, open, String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, dark, open,
title, okButtonLabel, currentName, currentFolder, title, okButtonLabel, currentName, currentFolder,
optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes ); optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes );
@@ -132,7 +134,7 @@ public class FlatSystemFileChooserLinuxTest
String[] fileTypes2 = fileTypes; String[] fileTypes2 = fileTypes;
new Thread( () -> { new Thread( () -> {
String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, open, String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, dark, open,
title, okButtonLabel, currentName, currentFolder, title, okButtonLabel, currentName, currentFolder,
optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes2 ); optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes2 );