From 9e8b8697d1781047793ecd27071afe3eeca521dd Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 2 Dec 2025 14:11:10 +0100 Subject: [PATCH] System File Chooser: Linux: show file dialog in dark if current FlatLaf theme is dark (PR #988) --- .../flatlaf/ui/FlatNativeLinuxLibrary.java | 5 +-- .../flatlaf/util/SystemFileChooser.java | 3 +- .../src/main/cpp/ApiVersion.cpp | 2 +- .../src/main/cpp/GtkFileChooser.cpp | 34 +++++++++++++++++-- ...ormdev_flatlaf_ui_FlatNativeLinuxLibrary.h | 20 +++++++++-- .../FlatSystemFileChooserLinuxTest.java | 6 ++-- 6 files changed, 60 insertions(+), 10 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLinuxLibrary.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLinuxLibrary.java index 1aceddf4..14e58039 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLinuxLibrary.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLinuxLibrary.java @@ -37,7 +37,7 @@ import com.formdev.flatlaf.util.SystemInfo; */ public class FlatNativeLinuxLibrary { - private static int API_VERSION_LINUX = 3002; + private static int API_VERSION_LINUX = 3003; /** * Checks whether native library is loaded/available. @@ -178,6 +178,7 @@ public class FlatNativeLinuxLibrary * to avoid blocking the AWT event dispatching thread. * * @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 title text displayed in dialog title; or {@code null} * @param okButtonLabel text displayed in default button; or {@code null}. @@ -199,7 +200,7 @@ public class FlatNativeLinuxLibrary * * @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, int optionsSet, int optionsClear, FileChooserCallback callback, int fileTypeIndex, String... fileTypes ); 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 105e9c5f..52bb5762 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 @@ -1085,6 +1085,7 @@ public class SystemFileChooser @Override String[] showSystemDialog( Window owner, SystemFileChooser fc ) { + int dark = FlatLaf.isLafDark() ? 1 : 0; boolean open = (fc.getDialogType() == OPEN_DIALOG); String approveButtonText = fc.getApproveButtonText(); int approveButtonMnemonic = fc.getApproveButtonMnemonic(); @@ -1162,7 +1163,7 @@ public class SystemFileChooser } : null; // show system file dialog - return FlatNativeLinuxLibrary.showFileChooser( owner, open, + return FlatNativeLinuxLibrary.showFileChooser( owner, dark, open, fc.getDialogTitle(), approveButtonText, currentName, currentFolder, optionsSet, optionsClear, callback, fileTypeIndex, fileTypes.toArray( new String[fileTypes.size()] ) ); diff --git a/flatlaf-natives/flatlaf-natives-linux/src/main/cpp/ApiVersion.cpp b/flatlaf-natives/flatlaf-natives-linux/src/main/cpp/ApiVersion.cpp index cdd701a9..ae0a7d73 100644 --- a/flatlaf-natives/flatlaf-natives-linux/src/main/cpp/ApiVersion.cpp +++ b/flatlaf-natives/flatlaf-natives-linux/src/main/cpp/ApiVersion.cpp @@ -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.FlatNativeLinuxLibrary -#define API_VERSION_LINUX 3002 +#define API_VERSION_LINUX 3003 //---- JNI methods ------------------------------------------------------------ diff --git a/flatlaf-natives/flatlaf-natives-linux/src/main/cpp/GtkFileChooser.cpp b/flatlaf-natives/flatlaf-natives-linux/src/main/cpp/GtkFileChooser.cpp index 5603e4ae..828ba66e 100644 --- a/flatlaf-natives/flatlaf-natives-linux/src/main/cpp/GtkFileChooser.cpp +++ b/flatlaf-natives/flatlaf-natives-linux/src/main/cpp/GtkFileChooser.cpp @@ -168,7 +168,7 @@ static void handle_response( GtkWidget* dialog, gint responseId, gpointer data ) extern "C" 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, 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 ); // 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 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", ¤tGtkTheme, 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 diff --git a/flatlaf-natives/flatlaf-natives-linux/src/main/headers/com_formdev_flatlaf_ui_FlatNativeLinuxLibrary.h b/flatlaf-natives/flatlaf-natives-linux/src/main/headers/com_formdev_flatlaf_ui_FlatNativeLinuxLibrary.h index 0c581640..2d855b67 100644 --- a/flatlaf-natives/flatlaf-natives-linux/src/main/headers/com_formdev_flatlaf_ui_FlatNativeLinuxLibrary.h +++ b/flatlaf-natives/flatlaf-natives-linux/src/main/headers/com_formdev_flatlaf_ui_FlatNativeLinuxLibrary.h @@ -7,6 +7,22 @@ #ifdef __cplusplus extern "C" { #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 #define com_formdev_flatlaf_ui_FlatNativeLinuxLibrary_MOVE 8L #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 * 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 - (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 diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserLinuxTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserLinuxTest.java index 65c68b49..52af4b9a 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserLinuxTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserLinuxTest.java @@ -24,6 +24,7 @@ import java.awt.Window; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; import javax.swing.*; +import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.extras.components.*; import com.formdev.flatlaf.extras.components.FlatTriStateCheckBox.State; import com.formdev.flatlaf.testing.FlatSystemFileChooserTest.DummyModalDialog; @@ -121,8 +122,9 @@ public class FlatSystemFileChooserLinuxTest System.out.println( FlatNativeLinuxLibrary.isGtk3Available() ); + int dark = FlatLaf.isLafDark() ? 1 : 0; if( direct ) { - String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, open, + String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, dark, open, title, okButtonLabel, currentName, currentFolder, optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes ); @@ -132,7 +134,7 @@ public class FlatSystemFileChooserLinuxTest String[] fileTypes2 = fileTypes; new Thread( () -> { - String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, open, + String[] files = FlatNativeLinuxLibrary.showFileChooser( owner, dark, open, title, okButtonLabel, currentName, currentFolder, optionsSet.get(), optionsClear.get(), callback, fileTypeIndex, fileTypes2 );