{"id":13798290,"url":"https://github.com/Oliver-Loeffler/FXFileChooser","last_synced_at":"2025-05-13T05:32:01.881Z","repository":{"id":27593555,"uuid":"114529931","full_name":"Oliver-Loeffler/FXFileChooser","owner":"Oliver-Loeffler","description":"Custom JavaFX file chooser which allows quick manual filtering, which allows to add Path predicates as filter and which is testable using TestFX.","archived":false,"fork":false,"pushed_at":"2024-06-03T21:01:06.000Z","size":3628,"stargazers_count":49,"open_issues_count":16,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-11T11:37:31.543Z","etag":null,"topics":["chooser","custom-javafx","file","filechooser","javafx","javafx-component","listview","swing-component"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Oliver-Loeffler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-17T11:11:52.000Z","updated_at":"2025-04-15T17:28:08.000Z","dependencies_parsed_at":"2024-05-28T18:39:00.861Z","dependency_job_id":"371c7afa-266b-4538-8ff1-29d84601f02b","html_url":"https://github.com/Oliver-Loeffler/FXFileChooser","commit_stats":{"total_commits":502,"total_committers":5,"mean_commits":100.4,"dds":"0.11354581673306774","last_synced_commit":"8be32a135bf149ee70204b86237f09dadd46cda3"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oliver-Loeffler%2FFXFileChooser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oliver-Loeffler%2FFXFileChooser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oliver-Loeffler%2FFXFileChooser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oliver-Loeffler%2FFXFileChooser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Oliver-Loeffler","download_url":"https://codeload.github.com/Oliver-Loeffler/FXFileChooser/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253883121,"owners_count":21978611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chooser","custom-javafx","file","filechooser","javafx","javafx-component","listview","swing-component"],"created_at":"2024-08-04T00:00:41.397Z","updated_at":"2025-05-13T05:31:57.539Z","avatar_url":"https://github.com/Oliver-Loeffler.png","language":"Java","funding_links":["https://buymeacoffee.com/?via=raumzeitfalle"],"categories":["Community"],"sub_categories":["Libraries"],"readme":"# FXFileChooser\n\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![javadoc](https://javadoc.io/badge2/net.raumzeitfalle.fx/filechooser/0.0.11/javadoc.svg)](https://javadoc.io/doc/net.raumzeitfalle.fx/filechooser/0.0.11) [![codecov](https://codecov.io/gh/Oliver-Loeffler/FXFileChooser/branch/master/graph/badge.svg)](https://codecov.io/gh/Oliver-Loeffler/FXFileChooser) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.raumzeitfalle.fx/filechooser/badge.svg)](https://maven-badges.herokuapp.com/maven-central/net.raumzeitfalle.fx/filechooser)\n\n[![Verify on Windows](https://github.com/Oliver-Loeffler/FXFileChooser/actions/workflows/verify-windows.yml/badge.svg)](https://github.com/Oliver-Loeffler/FXFileChooser/actions/workflows/verify-windows.yml)\n\n[![Verify on Linux](https://github.com/Oliver-Loeffler/FXFileChooser/actions/workflows/verify-linux.yml/badge.svg)](https://github.com/Oliver-Loeffler/FXFileChooser/actions/workflows/verify-linux.yml)\n\nCustom JavaFX file chooser which allows quick manual filtering, which allows to add Path predicates as filter and which is testable using TestFX.\n\nAs the standard JavaFX file chooser uses system dialogs, so it is hard to test and hard to modify (e.g. new Skin). In some cases the system controls even show poor performance opening folders with many files (depends on operating system and JRE version).\n\nOn Microsoft Windows platforms running with Java 8, I've encountered cases where it was impossible to use the Java Swing JFileChooser, simply due to the high number of files in a directory. Using the JavaFX FileChooser was also not an option as I required a simplistic way to filter the files by name.\n\n## Licensing and attributions\n\nSee `LICENSE` and `NOTICE` for details. The project is licensed using the Apache License, Version 2.0 \nand attributes to FontAwesome Free 5.01 (Font Awesome Free License and CC BY 4.0 license).\n\n## Adding FXFileChooser to your Maven or Gradle project\n\n### Dependency for Maven `POM.xml` for JDK17+\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003enet.raumzeitfalle.fx\u003c/groupId\u003e\n  \u003cartifactId\u003efilechooser\u003c/artifactId\u003e\n  \u003cversion\u003e0.0.11\u003c/version\u003e\n\u003c/dependency\u003e\n```\n### Dependency for Gradle `build.gradle`\n\n```Groovy\nimplementation 'net.raumzeitfalle.fx:filechooser:0.0.11'\n```\n\nWhen using FXFileChooser in a modular Java project, then add following statement to your module-info:\n\n```java\nmodule your.module {\n    requires net.raumzeitfalle.fxfilechooser;\n}\n```\n\nBesides version `0.0.9` which is primarily intended for Java 8, there is version `0.0.10` which exists for a variety of Java/OpenJFX combinations.\nThose are not fully tested so please feel free to provide some feedback.\n\n| Java | JavaFX | artifact version |\n|-|--------|------------------|\n| 8      | 8      | `0.0.9`          |\n| 9      | 9      | `0.0.10-jdk9`    |\n| 11     | 11.0.1 | `0.0.10-jdk11`   |\n| 11     | 16     | `0.0.10-jdk16`   |\n| 17     | 17.0.2 | `0.0.10-jdk17`   |\n| 17     | 18.0.2 | `0.0.10-jfx18`   |\n| 17     | 19     | `0.0.10-jfx19`   |\n| 17     | 19     | `0.0.11`         |\n| 17     | 20     | `0.0.12`         |\n\nAll packages are available on https://search.maven.org/artifact/net.raumzeitfalle.fx/filechooser.\nJava 17 will be baseline for development from now on. However, backport of working features to Java 11 is thinkable.\n\nJava-11 (LTS) with OpenJFX11:\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003enet.raumzeitfalle.fx\u003c/groupId\u003e\n  \u003cartifactId\u003efilechooser\u003c/artifactId\u003e\n  \u003cversion\u003e0.0.10-jdk11\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nJava-17 (LTS) with OpenJFX17:\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003enet.raumzeitfalle.fx\u003c/groupId\u003e\n  \u003cartifactId\u003efilechooser\u003c/artifactId\u003e\n  \u003cversion\u003e0.0.12\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## How to build it\n\nThis project requires Java-17 or later and utilizes JavaFX 17 or later.\nThe build is setup using Maven. As publishing to http://search.maven.org requires signing, the Maven build is configured to sign the artifacts durin verify phase. This may cause problems when the GPG signing is no properly setup. Hence Maven profile exists, which skips the signing step.\n\nFollowing steps are needed to get the code and run the tests:\n\n```shell\ngit clone https://github.com/Oliver-Loeffler/FXFileChooser.git\ncd FXFileChooser\n\n# Run all tests\nmvn verify -P github\n\n# Install the artifact locally (tests ran already during verify)\nmvn install -P github -DskipTests=true\n```\n\nThe deployment is controlled using `maven-release-plugin`.\n\n## How it works\n\nFXFileChooser provides access to a DirectoryChooser and a ListView populated with files in the selected directory. The process starts in the users home directory. The ListView is populated by a background service running an update task upon request. \n\nOnce the ListView is populated with Path items, those are filtered by the String entered in the filter TextField. The filter condition is \"contains\" whereas special characters such as ` '\"','?','\u003c','\u003e','|',':','*'` are removed.\n\nIt turned out that with slow network connections the experience is great when using a single stream and updating the ListView in the streams forEach method. However, this can still take some seconds.\n\n**Ideas**\n 1. Provide some kind of directory content pre fetching for large network shares. \n 2. Indicate update progress and update ListView with one operation (one single update works fine, in case of using pre fetching that would be okay). Otherwise one would see an empty List and would have to wait.\n 3. In the above case, ensure that files which are selected but do not exist are removed from view on selection or hover OR dont accept the OK action in case the file does no longer exist and trigger update then.  \n 4. Update the view in one step (one single update works fine, in case of using pre fetching that would be okay). But then also update only what has changed. Never clear the list, only remove items which do no longer exist and add items which are not in the view.\n 5. Keep the selection (if file still exists after update)\n\n\n## Available versions\n\n * FileChooser placed in a customized JavaFX stage\n * One placed in a JavaFX dialog\n * One placed in a JFXPanel, so it can be used in Java Swing applications.\n\n\n## Features \u0026 Ideas\n \n * FXFileChooser is based on FXML and CSS and so fully customizable (the ListView might be replaced by a TableView to have more options in terms of sorting - or it will be completely exchangeable - I'll see)\n * Icons are realized as SVGPaths based upon FontAwesome Free 5 (no glyphs, no extra dependencies, the SVGPaths are part of the FXML)\n * The choose directory button provides a menu, where default locations (or a history of locations) can be provided. **(tbd.)**\n *  File types can be selected from filters **(tbd.)**\n \n ![default locations](pages/DefaultLocationsExample.png) ![path filter](pages/PathFilterExample.png) ![search option](pages/SortingMenuExample.png)\n * add sorting by name or time\n * consider using a TableView instead a plain list\n * consider regular expression support for filtering\n\n**Ideas**\n * A nice build script.\n * A great skin (CSS) is desirable (well I just got inspired by https://github.com/angelicalleite/museuid and will see :-).\n\n\nTests are missing and currently I'm playing with TestFX - but it's not yet working as I like it.\n\n## Structure\n\n### FXML Nodes and FXML IDs\n\n![FXML Nodes and IDs](pages/filechooser/FXML_and_ids.png)\n\n### CSS Classes\n\ntbd.\n\n## Using the FileChooser with Swing\n\n```java\nimport java.awt.FlowLayout;\n\nimport javax.swing.JButton;\nimport javax.swing.JFrame;\nimport javax.swing.JOptionPane;\n\nimport net.raumzeitfalle.fx.filechooser.Skin;\nimport net.raumzeitfalle.fx.filechooser.SwingFileChooser;\n\npublic class DemoSwing {\n    public static void main(String... args) {\n\n        JFrame frame = new JFrame(\"Window\");\n        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n        frame.setLayout(new FlowLayout(FlowLayout.LEFT));\n        JButton showDialogButton = new JButton(\"show SwingFileChooser\");\n        frame.getContentPane().add(showDialogButton);\n\n        // SwingFileChooser.setUseJavaFxDirectoryChooser(true);\n        SwingFileChooser fileChooser = SwingFileChooser.create(Skin.DARK);\n        showDialogButton.addActionListener(l -\u003e {\n            int option = fileChooser.showOpenDialog(frame);\n            if (option == SwingFileChooser.APPROVE_OPTION) {\n                JOptionPane.showMessageDialog(frame, fileChooser.getSelectedFile().toString());\n            }\n        });\n\n        frame.pack();\n        frame.setVisible(true);\n    }\n}\n```\n\n![Swing version with Filter](pages/OSX_Swing_JFXPanel.png)\n\n\n## Using the JavaFX Dialog version\n\n```java\nimport java.nio.file.Path;\n\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.Button;\nimport javafx.stage.Stage;\nimport net.raumzeitfalle.fx.filechooser.FXFileChooserDialog;\nimport net.raumzeitfalle.fx.filechooser.Skin;\n\npublic class DemoFxDialog extends Application {\n    public static void main(String[] args) {\n        Application.launch();\n    }\n\n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        Button button = new Button(\"Show File Chooser\");\n        FXFileChooserDialog dialog = FXFileChooserDialog.create(Skin.DARK);\n        button.setOnAction(evt -\u003e dialog.showOpenDialog(primaryStage).ifPresent(this::showSelection));\n\n        Scene scene = new Scene(button);\n        primaryStage.setScene(scene);\n        primaryStage.setTitle(\"Demo\");\n        primaryStage.show();\n    }\n\n    private void showSelection(Path selectedPath) {\n        Alert alert = new Alert(Alert.AlertType.INFORMATION);\n        alert.setHeaderText(\"File Selection\");\n        alert.setContentText(selectedPath.toString());\n        alert.show();\n    }\n}\n```\n\n\n![Swing version with Filter](pages/OSX_JavaFX_Dialog.png)\n\n## A customizable version with its own stage\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.Button;\nimport javafx.stage.Stage;\nimport net.raumzeitfalle.fx.filechooser.FXFileChooserStage;\nimport net.raumzeitfalle.fx.filechooser.Skin;\n\nimport java.nio.file.Path;\n\npublic class DemoFxStage extends Application {\n    public static void main(String[] args) {\n        Application.launch();\n    }\n\n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        Button button = new Button(\"Show File Chooser\");\n        FXFileChooserStage fc = FXFileChooserStage.create(Skin.DARK);\n        button.setOnAction(evt -\u003e fc.showOpenDialog(primaryStage)\n                                    .ifPresent(this::showSelection));\n\n        Scene scene = new Scene(button);\n        primaryStage.setScene(scene);\n        primaryStage.setTitle(\"Demo\");\n        primaryStage.show();\n    }\n\n    private void showSelection(Path selectedPath) {\n        Alert alert = new Alert(Alert.AlertType.INFORMATION);\n        alert.setHeaderText(\"File Selection\");\n        alert.setContentText(selectedPath.toString());\n        alert.show();\n    }\n}\n```\n\n\n## Using FileChooser as a control\n\nAs with version `0.0.9` the FileChooser can be used as a control within any scene. There is no need to have a separate window anymore. However, functionality is not yet finalized. Feel free to experiment. I'm looking forward to your feedback.\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.stage.Stage;\nimport net.raumzeitfalle.fx.filechooser.DirectoryChooserOption;\nimport net.raumzeitfalle.fx.filechooser.FileChooser;\nimport net.raumzeitfalle.fx.filechooser.Skin;\n\npublic class DemoFileChooser extends Application {\n\n    public static void main(String[] args) {\n        Application.launch();\n    }\n\n    private FileChooser fileChooser; \n    \n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        fileChooser = new FileChooser(Skin.DARK, DirectoryChooserOption.CUSTOM);\n        Scene scene = new Scene(fileChooser);\n        primaryStage.setScene(scene);\n        primaryStage.setTitle(\"Demo\");\n        primaryStage.show();\n    }\n\n    @Override\n    public void stop() {\n        fileChooser.shutdown();\n    }\n}\n```\n\n\n## The new DirectoryChooser control\n\nThere is also a new control named directory chooser. This is especially intended to be used on large volumes with many files and many folders. Folder scanning is executed on demand and progress is indicated per folder.\n\nOne can abort folder scanning by double-click on the progress indicator. If paths have been scanned, one can enter a path into the search field and the directory chooser will attempt to select the directory. The selection needs to be confirmed and accepted manually with click on okay.\n\nAlready explored folders will be marked with a `+` sign and presumably large folders will receive a `XL` tag. In case of large folders (\u003e 1000 items) the directory chooser will ask the user before running indexing.\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Alert;\nimport javafx.stage.Stage;\nimport net.raumzeitfalle.fx.dirchooser.DirectoryChooser;\nimport net.raumzeitfalle.fx.filechooser.Skin;\n\npublic class DemoDirectoryChooser extends Application {\n    public static void main(String[] args) {\n        Application.launch();\n    }\n\n    private DirectoryChooser dirChooser;\n    \n    private Scene scene;\n    \n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        dirChooser = new DirectoryChooser(Skin.DARK);\n        dirChooser.useCancelButtonProperty().setValue(true);\n        dirChooser.onSelect(()-\u003e{\n            Path selectedDir = dirChooser.selectedDirectoryProperty().get();\n            showMessage(\"Selected:\", selectedDir.normalize().toAbsolutePath().toString());\n        });\n        dirChooser.onCancel(\n                () -\u003e showMessage(\"Cancelled:\", \"One can hide the cancel button if not needed.\")\n        );\n\n        scene = new Scene(dirChooser);\n        primaryStage.setScene(scene);\n        primaryStage.setTitle(\"Demo\");\n        primaryStage.show();\n    }\n\n    @Override\n    public void stop() {\n        dirChooser.shutdown();\n    }\n\n    private void showMessage(String action, String message) {\n        Alert alert = new Alert(Alert.AlertType.INFORMATION);\n        alert.initOwner(scene.getWindow());\n        alert.setTitle(\"DirectoryChooser\");\n        alert.setHeaderText(action);\n        alert.setContentText(message);\n        alert.show();\n    }\n}\n```\n\n## Configuration Options\n\nIn some cases a non-programmatic configuration can be quite helpful. The configuration must be provided in the final packaged JAR as a resource in the default package (JAR root). As with version `0.0.9` following configuration options exist:\n\n| Properties file |  Property option | Default and Description |\n|-|-|-|\n| `directorychooser.properties` | `directory.chooser.icon.size` | Configures the size of directory and drive icons (24px by default) |\n| `filechooser.properties`     | `directory.chooser.icon.size` | Configures the size of icons in file chooser (usually 32px) |\n| `swingfilechooser.properties` | `use.javafx.platform.directory.chooser` | Configures if the JavaFX platform directory chooser is used or not. Defaults to false |\n\n## Support\n\nIf you like this work and if it is kind of useful four your purpose, I'd be happy if you could [by me a coffee](https://buymeacoffee.com/?via=raumzeitfalle), thanks!\nI also appreciate comments and feedback on how it works OR how it unfortunately not works so well.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOliver-Loeffler%2FFXFileChooser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FOliver-Loeffler%2FFXFileChooser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOliver-Loeffler%2FFXFileChooser/lists"}