Package overrungl.nfd

Class NFD

java.lang.Object
overrungl.nfd.NFD

public final class NFD extends Object
NFD relies on preview features of the Java platform:
Programs can only use NFD when preview features are enabled.
Preview features may be removed in a future release, or upgraded to permanent features of the Java platform.

Native File Dialog Extended

A small C library with that portably invokes native file open, folder select and file save dialogs. Write dialog code once and have it pop up native dialogs on all supported platforms. Avoid linking large dependencies like wxWidgets and Qt.

Features:

  • Supports Windows, MacOS, and Linux
  • Friendly names for filters (e.g. Java Source files (*.java;*.kt) instead of (*.java;*.kt)) on platforms that support it
  • Automatically append file extension on platforms where users expect it
  • Support for setting a default folder path
  • Support for setting a default file name (e.g. Untitled.java)
  • Consistent UTF-8 support on all platforms
  • Native character set (UTF-16LE) support on Windows
  • Initialization and de-initialization of platform library (e.g. COM (Windows) / GTK (Linux GTK) / D-Bus (Linux portal)) decoupled from dialog functions, so applications can choose when to initialize/de-initialize
  • Multiple file selection support (for file open dialog)
  • No third party dependencies

Basic Usages

import overrungl.util.value.Pair;
void main() {
    NFD.init();

    try (MemoryStack stack = MemoryStack.stackPush()) {
        String[] outPath = new String[1];
        var filterItem = NFDNFilterItem.create(stack,
            new Pair<>("Source code", "java"),
            new Pair<>("Image file", "png,jpg"));
        var result = NFD.openDialogN(outPath, filterItem, null);
        switch (result) {
            case ERROR -> System.err.println("Error: " + NFD.getError());
            case OKAY -> System.out.println("Success! " + outPath[0]);
            case CANCEL -> System.out.println("User pressed cancel.");
        }
    }

    NFD.quit();
}

File Filter Syntax

Files can be filtered by file extension groups:
var filterItem = NFDNFilterItem.create(allocator,
    new Pair<>("Source code", "java"),
    new Pair<>("Image file", "png,jpg"));

A file filter is a pair of strings comprising the friendly name and the specification (multiple file extensions are comma-separated).

A list of file filters can be passed as an argument when invoking the library.

A wildcard filter is always added to every dialog.

Note: On MacOS, the file dialogs do not have friendly names and there is no way to switch between filters, so the filter specifications are combined (e.g. "java,kt,png,jpg"). The filter specification is also never explicitly shown to the user. This is usual MacOS behaviour and users expect it.

Note 2: You must ensure that the specification string is non-empty and that every file extension has at least one character. Otherwise, bad things might ensue (i.e. undefined behaviour).

Note 3: On Linux, the file extension is appended (if missing) when the user presses down the "Save" button. The appended file extension will remain visible to the user, even if an overwrite prompt is shown and the user then presses "Cancel".

Note 4: On Windows, the default folder parameter is only used if there is no recently used folder available. Otherwise, the default folder will be the folder that was last used. Internally, the Windows implementation calls IFileDialog::SetDefaultFolder(IShellItem). This is usual Windows behaviour and users expect it.

Iterating Over PathSets

A file open dialog that supports multiple selection produces a PathSet, which is a thin abstraction over the platform-specific collection. There are two ways to iterate over a PathSet:

Accessing by index

This method does array-like access on the PathSet, and is the easiest to use. However, on certain platforms (Linux, and possibly Windows), it takes O(N²) time in total to iterate the entire PathSet, because the underlying platform-specific implementation uses a linked list.

Using an enumerator (experimental)

This method uses an enumerator object to iterate the paths in the PathSet. It is guaranteed to take O(N) time in total to iterate the entire PathSet.

See NFDEnumerator.

This API is experimental, and subject to change.

Since:
0.1.0
Author:
squid233