Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
516bd80
System File Chooser: implemented native bindings for NSOpenPanel and …
DevCharly Dec 30, 2024
49a0a83
System File Chooser: implemented native bindings for IFileOpenDialog …
DevCharly Dec 31, 2024
63272a0
System File Chooser: macOS:
DevCharly Dec 31, 2024
2b810ad
System File Chooser: implemented native bindings for GtkFileChooserDi…
DevCharly Jan 3, 2025
a303cd2
System File Chooser: renamed Windows and macOS test apps
DevCharly Jan 3, 2025
641fada
System File Chooser: implemented modality for GtkFileChooserDialog on…
DevCharly Jan 4, 2025
9453d55
System File Chooser: fixes for Windows
DevCharly Jan 4, 2025
91e8d04
System File Chooser: introduced class `SystemFileChooser` as replacem…
DevCharly Jan 6, 2025
2e16ded
System File Chooser: support macOS in class `SystemFileChooser`
DevCharly Jan 6, 2025
9af7f95
System File Chooser: added "Format" combobox on macOS (if using more …
DevCharly Jan 7, 2025
d7462bd
System File Chooser: use Cocoa autolayout for "Format" label and comb…
DevCharly Jan 7, 2025
251198c
Native Libraries:
DevCharly Jan 7, 2025
c73fd51
System File Chooser: support filename extension filters
DevCharly Jan 8, 2025
d49282d
System File Chooser: support "approve" callback and system message di…
DevCharly Jan 11, 2025
078e59a
System File Chooser: support "approve" callback and system message di…
DevCharly Jan 12, 2025
07fc190
Native Libraries: moved code to JNIUtils.cpp and *MessageDialog.cpp
DevCharly Jan 14, 2025
d513ec4
System File Chooser: support system message dialog with custom button…
DevCharly Jan 15, 2025
0a4c01c
Merge main into system-file-chooser
DevCharly Jan 18, 2025
d524536
System File Chooser: Linux: cross-compile native library for ARM64 on…
DevCharly Jan 19, 2025
f3ca3a0
System File Chooser: added "approve" callback to `SystemFileChooser`
DevCharly Jan 20, 2025
b808f6e
System File Chooser: support platform specific features
DevCharly Jan 20, 2025
1e3e4d7
System File Chooser: fixed (cross-)compile native library for ARM64 L…
DevCharly Jan 21, 2025
aecb496
System File Chooser: macOS: show file dialog in dark if current FlatL…
DevCharly Jan 21, 2025
3283cfe
System File Chooser: macOS: disable screen menu bar when file dialog …
DevCharly Jan 22, 2025
1121165
Merge main into system-file-chooser
DevCharly Jan 23, 2025
54d6959
System File Chooser:
DevCharly Jan 23, 2025
03e5f86
update to Gradle 8.12.1
DevCharly Jan 27, 2025
d81bcd5
Merge main into system-file-chooser
DevCharly Mar 9, 2025
5d247f6
GitHub Actions: natives.yml: include only the core natives that have …
DevCharly Mar 17, 2025
202a0d1
GitHub Actions: natives.yml: sign Windows and macOS native libraries
DevCharly Mar 18, 2025
3e8b213
System File Chooser: fixed font in message dialog on Windows
DevCharly Mar 20, 2025
dade1cb
System File Chooser:
DevCharly Mar 22, 2025
35e86ba
System File Chooser: updated all native libraries
DevCharly Mar 22, 2025
6715886
Merge main into system-file-chooser
DevCharly Jun 12, 2025
f288237
GitHub Actions: natives.yml:
DevCharly Jun 13, 2025
0f27125
GitHub Actions: natives.yml:
DevCharly Jun 17, 2025
5c2d8ba
System File Chooser: fix crash on macOS 15.x
DevCharly Jun 17, 2025
1eee350
Merge main into system-file-chooser
DevCharly Jun 23, 2025
299250a
System File Chooser: change `@since 3.6` to `@since 3.7`
DevCharly Oct 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 73 additions & 6 deletions .github/workflows/natives.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,30 @@ jobs:

- uses: gradle/actions/wrapper-validation@v4

- name: install libxt-dev
- name: apt update (Linux)
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
run: sudo apt install libxt-dev
run: sudo apt-get update

- name: install g++-aarch64-linux-gnu
if: matrix.os == 'ubuntu-latest'
run: sudo apt install g++-aarch64-linux-gnu
- name: install libxt-dev and libgtk-3-dev (Linux)
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
run: sudo apt-get install libxt-dev libgtk-3-dev

# - name: Download libgtk-3.so for arm64 (Linux)
# if: matrix.os == 'ubuntu-latest'
# working-directory: flatlaf-natives/flatlaf-natives-linux/lib/aarch64
# run: |
# pwd
# ls -l /usr/lib/x86_64-linux-gnu/libgtk*
# wget --no-verbose https://ports.ubuntu.com/pool/main/g/gtk%2b3.0/libgtk-3-0_3.24.18-1ubuntu1_arm64.deb
# ls -l
# ar -x libgtk-3-0_3.24.18-1ubuntu1_arm64.deb data.tar.xz
# tar -xvf data.tar.xz --wildcards --to-stdout "./usr/lib/aarch64-linux-gnu/libgtk-3.so.0.*" > libgtk-3.so
# rm libgtk-3-0_3.24.18-1ubuntu1_arm64.deb data.tar.xz
# ls -l

# - name: install g++-aarch64-linux-gnu (Linux)
# if: matrix.os == 'ubuntu-latest'
# run: sudo apt-get install g++-aarch64-linux-gnu

- name: Setup Java 11
uses: actions/setup-java@v4
Expand All @@ -53,10 +70,60 @@ jobs:
# tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied
run: ./gradlew build-natives --no-daemon

- name: Sign Windows DLLs
if: matrix.os == 'windows-latest'
uses: skymatic/code-sign-action@v3
with:
certificate: '${{ secrets.CODE_SIGN_CERT_BASE64 }}'
password: '${{ secrets.CODE_SIGN_CERT_PASSWORD }}'
certificatesha1: '${{ secrets.CODE_SIGN_CERT_SHA1 }}'
folder: 'flatlaf-core/src/main/resources/com/formdev/flatlaf/natives'

- name: Sign macOS natives
if: matrix.os == 'DISABLED--macos-latest'
env:
CERT_BASE64: ${{ secrets.CODE_SIGN_CERT_BASE64 }}
CERT_PASSWORD: ${{ secrets.CODE_SIGN_CERT_PASSWORD }}
CERT_IDENTITY: ${{ secrets.CODE_SIGN_CERT_IDENTITY }}
run: |
# https://docs.github.com/en/actions/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/cert.p12
KEYCHAIN_PATH=$RUNNER_TEMP/signing.keychain-db
KEYCHAIN_PASSWORD=$CERT_PASSWORD
# decode certificate
printenv CERT_BASE64 | base64 --decode > $CERTIFICATE_PATH
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P "$CERT_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
# set partition list (required for codesign)
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# add keychain to keychain search list
security list-keychains -d user -s $KEYCHAIN_PATH
# sign code
codesign --sign "$CERT_IDENTITY" --force --verbose=4 --timestamp \
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/libflatlaf-macos-*.dylib
codesign --display --verbose=4 flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/libflatlaf-macos-*.dylib
# cleanup
security delete-keychain $KEYCHAIN_PATH

- name: Set artifacts pattern
shell: bash
run: |
case ${{ matrix.os }} in
windows-latest) echo "artifactPattern=flatlaf-windows-*.dll" >> $GITHUB_ENV ;;
macos-latest) echo "artifactPattern=libflatlaf-macos-*.dylib" >> $GITHUB_ENV ;;
ubuntu-latest) echo "artifactPattern=libflatlaf-linux-x86_64.so" >> $GITHUB_ENV ;;
ubuntu-24.04-arm) echo "artifactPattern=libflatlaf-linux-arm64.so" >> $GITHUB_ENV ;;
esac

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
path: |
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/${{ env.artifactPattern }}
flatlaf-natives/flatlaf-natives-*/build
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package com.formdev.flatlaf;

import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import com.formdev.flatlaf.util.SystemFileChooser;
import com.formdev.flatlaf.util.UIScale;

/**
Expand Down Expand Up @@ -226,6 +228,17 @@ public interface FlatSystemProperties
*/
String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle";

/**
* Specifies whether {@link SystemFileChooser} uses operating system file dialogs.
* If set to {@code false}, the {@link JFileChooser} is used instead.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*
* @since 3.7
*/
String USE_SYSTEM_FILE_CHOOSER = "flatlaf.useSystemFileChooser";

/**
* Checks whether a system property is set and returns {@code true} if its value
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.awt.geom.AffineTransform;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import com.formdev.flatlaf.util.SystemInfo;

/**
Expand All @@ -34,20 +35,23 @@
* @author Karl Tauber
* @since 2.5
*/
class FlatNativeLinuxLibrary
public class FlatNativeLinuxLibrary
{
private static int API_VERSION_LINUX = 3001;
private static int API_VERSION_LINUX = 3002;

/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
static boolean isLoaded() {
public static boolean isLoaded() {
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded( API_VERSION_LINUX );
}


//---- X Window System ----------------------------------------------------

// direction for _NET_WM_MOVERESIZE message
// see https://specifications.freedesktop.org/wm-spec/latest/ar01s04.html
static final int
Expand Down Expand Up @@ -124,4 +128,109 @@ private static boolean hasCustomDecoration( Window window ) {
return (window instanceof JFrame && JFrame.isDefaultLookAndFeelDecorated() && ((JFrame)window).isUndecorated()) ||
(window instanceof JDialog && JDialog.isDefaultLookAndFeelDecorated() && ((JDialog)window).isUndecorated());
}


//---- GTK ----------------------------------------------------------------

private static Boolean isGtk3Available;

/**
* Checks whether GTK 3 is available.
* Use this before invoking any native method that uses GTK.
* Otherwise the app may terminate immediately if GTK is not installed.
* <p>
* This works because Java uses {@code dlopen(RTLD_LAZY)} to load JNI libraries,
* which only resolves symbols as the code that references them is executed.
*
* @since 3.7
*/
public static boolean isGtk3Available() {
if( isGtk3Available == null )
isGtk3Available = isLibAvailable( "libgtk-3.so.0" ) || isLibAvailable( "libgtk-3.so" );
return isGtk3Available;
}

private native static boolean isLibAvailable( String libname );

/**
* https://docs.gtk.org/gtk3/iface.FileChooser.html#properties
*
* @since 3.7
*/
public static final int
FC_select_folder = 1 << 0,
FC_select_multiple = 1 << 1,
FC_show_hidden = 1 << 2,
FC_local_only = 1 << 3, // default
FC_do_overwrite_confirmation = 1 << 4, // GTK 3 only; removed and always-on in GTK 4
FC_create_folders = 1 << 5; // default for Save

/**
* Shows the Linux/GTK system file dialog
* <a href="https://docs.gtk.org/gtk3/class.FileChooserDialog.html">GtkFileChooserDialog</a>.
* <p>
* Uses {@code GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER} if {@link #FC_select_folder} is set in parameter {@code optionsSet}.
* Otherwise uses {@code GTK_FILE_CHOOSER_ACTION_OPEN} if parameter {@code open} is {@code true},
* or {@code GTK_FILE_CHOOSER_ACTION_SAVE} if {@code false}.
* <p>
* <b>Note:</b> This method blocks the current thread until the user closes
* the file dialog. It is highly recommended to invoke it from a new thread
* to avoid blocking the AWT event dispatching thread.
*
* @param owner the owner of the file dialog; or {@code null}
* @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}.
* Use '_' for mnemonics (e.g. "_Choose")
* Use '__' for '_' character (e.g. "Choose__and__Quit").
* @param currentName user-editable filename currently shown in the filename field in save dialog; or {@code null}
* @param currentFolder current directory shown in the dialog; or {@code null}
* @param optionsSet options to set; see {@code FOS_*} constants
* @param optionsClear options to clear; see {@code FOS_*} constants
* @param callback approve callback; or {@code null}
* @param fileTypeIndex the file type that appears as selected (zero-based)
* @param fileTypes file types that the dialog can open or save.
* Two or more strings and {@code null} are required for each filter.
* First string is the display name of the filter shown in the combobox (e.g. "Text Files").
* Subsequent strings are the filter patterns (e.g. "*.txt" or "*").
* {@code null} is required to mark end of filter.
* @return file path(s) that the user selected; an empty array if canceled;
* or {@code null} on failures (no dialog shown)
*
* @since 3.7
*/
public native static String[] showFileChooser( Window owner, boolean open,
String title, String okButtonLabel, String currentName, String currentFolder,
int optionsSet, int optionsClear, FileChooserCallback callback,
int fileTypeIndex, String... fileTypes );

/** @since 3.7 */
public interface FileChooserCallback {
boolean approve( String[] files, long hwndFileDialog );
}

/**
* Shows a GTK message box
* <a href="https://docs.gtk.org/gtk3/class.MessageDialog.html">GtkMessageDialog</a>.
* <p>
* For use in {@link FileChooserCallback} only.
*
* @param hwndParent the parent of the message box
* @param messageType type of message being displayed:
* {@link JOptionPane#ERROR_MESSAGE}, {@link JOptionPane#INFORMATION_MESSAGE},
* {@link JOptionPane#WARNING_MESSAGE}, {@link JOptionPane#QUESTION_MESSAGE} or
* {@link JOptionPane#PLAIN_MESSAGE}
* @param primaryText primary text; if the dialog has a secondary text,
* this will appear as title in a larger bold font
* @param secondaryText secondary text; shown below of primary text; or {@code null}
* @param defaultButton index of the default button, which can be pressed using ENTER key
* @param buttons texts of the buttons; if no buttons given the a default "OK" button is shown.
* Use '_' for mnemonics (e.g. "_Choose")
* Use '__' for '_' character (e.g. "Choose__and__Quit").
* @return index of pressed button; or -1 for ESC key
*
* @since 3.7
*/
public native static int showMessageDialog( long hwndParent, int messageType,
String primaryText, String secondaryText, int defaultButton, String... buttons );
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.awt.Rectangle;
import java.awt.Window;
import javax.swing.JOptionPane;
import com.formdev.flatlaf.util.SystemInfo;

/**
Expand All @@ -44,7 +45,7 @@
*/
public class FlatNativeMacLibrary
{
private static int API_VERSION_MACOS = 2001;
private static int API_VERSION_MACOS = 2002;

/**
* Checks whether native library is loaded/available.
Expand All @@ -68,4 +69,88 @@ public static boolean isLoaded() {
/** @since 3.4 */ public native static Rectangle getWindowButtonsBounds( Window window );
/** @since 3.4 */ public native static boolean isWindowFullScreen( Window window );
/** @since 3.4 */ public native static boolean toggleWindowFullScreen( Window window );


/** @since 3.7 */
public static final int
// NSOpenPanel (extends NSSavePanel)
FC_canChooseFiles = 1 << 0, // default
FC_canChooseDirectories = 1 << 1,
FC_resolvesAliases = 1 << 2, // default
FC_allowsMultipleSelection = 1 << 3,
FC_accessoryViewDisclosed = 1 << 4,
// NSSavePanel
FC_showsTagField = 1 << 8, // default for Save
FC_canCreateDirectories = 1 << 9, // default for Save
FC_canSelectHiddenExtension = 1 << 10,
FC_showsHiddenFiles = 1 << 11,
FC_extensionHidden = 1 << 12,
FC_allowsOtherFileTypes = 1 << 13,
FC_treatsFilePackagesAsDirectories = 1 << 14,
// custom
FC_showSingleFilterField = 1 << 24;

/**
* Shows the macOS system file dialogs
* <a href="https://developer.apple.com/documentation/appkit/nsopenpanel?language=objc">NSOpenPanel</a> or
* <a href="https://developer.apple.com/documentation/appkit/nssavepanel?language=objc">NSSavePanel</a>.
* <p>
* <b>Note:</b> This method blocks the current thread until the user closes
* the file dialog. It is highly recommended to invoke it from a new thread
* to avoid blocking the AWT event dispatching thread.
*
* @param owner the owner of the file dialog; or {@code null}
* @param dark appearance of the file dialog: {@code 1} = dark, {@code 0} = light, {@code -1} = default
* @param open if {@code true}, shows the open dialog; if {@code false}, shows the save dialog
* @param title text displayed at top of save dialog (not used in open dialog); or {@code null}
* @param prompt text displayed in default button; or {@code null}
* @param message text displayed at top of open/save dialogs; or {@code null}
* @param filterFieldLabel text displayed in front of the filter combobox; or {@code null}
* @param nameFieldLabel text displayed in front of the filename text field in save dialog (not used in open dialog); or {@code null}
* @param nameFieldStringValue user-editable filename currently shown in the name field in save dialog (not used in open dialog); or {@code null}
* @param directoryURL current directory shown in the dialog; or {@code null}
* @param optionsSet options to set; see {@code FC_*} constants
* @param optionsClear options to clear; see {@code FC_*} constants
* @param fileTypeIndex the file type that appears as selected (zero-based)
* @param fileTypes file types that the dialog can open or save.
* Two or more strings and {@code null} are required for each filter.
* First string is the display name of the filter shown in the combobox (e.g. "Text Files").
* Subsequent strings are the filter patterns (e.g. "txt" or "*").
* {@code null} is required to mark end of filter.
* @return file path(s) that the user selected; an empty array if canceled;
* or {@code null} on failures (no dialog shown)
*
* @since 3.7
*/
public native static String[] showFileChooser( Window owner, int dark, boolean open,
String title, String prompt, String message, String filterFieldLabel,
String nameFieldLabel, String nameFieldStringValue, String directoryURL,
int optionsSet, int optionsClear, FileChooserCallback callback,
int fileTypeIndex, String... fileTypes );

/** @since 3.7 */
public interface FileChooserCallback {
boolean approve( String[] files, long hwndFileDialog );
}

/**
* Shows a macOS alert
* <a href="https://developer.apple.com/documentation/appkit/nsalert?language=objc">NSAlert</a>.
* <p>
* For use in {@link FileChooserCallback} only.
*
* @param hwndParent the parent of the message box
* @param alertStyle type of alert being displayed:
* {@link JOptionPane#ERROR_MESSAGE}, {@link JOptionPane#INFORMATION_MESSAGE} or
* {@link JOptionPane#WARNING_MESSAGE}
* @param messageText main message of the alert
* @param informativeText additional information about the alert; shown below of main message; or {@code null}
* @param defaultButton index of the default button, which can be pressed using ENTER key
* @param buttons texts of the buttons; if no buttons given the a default "OK" button is shown
* @return index of pressed button
*
* @since 3.7
*/
public native static int showMessageDialog( long hwndParent, int alertStyle,
String messageText, String informativeText, int defaultButton, String... buttons );
}
Loading