Skip to content

Instantly share code, notes, and snippets.

@mmdemirbas
Last active May 31, 2022 22:27
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save mmdemirbas/07f7cb0840d68ef00e75cd60e9b97ce1 to your computer and use it in GitHub Desktop.
JavaFX Multiple Dates Picker
package com.mmdemirbas.lab;
/**
* Created by md on 5.04.2016.
*/
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.util.Callback;
import javafx.util.StringConverter;
import javax.swing.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class MultiDatePicker extends Application {
public static void main(String[] args) {
// launch(args);
launchSwing();
}
private static void launchSwing() {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Swing and JavaFX");
final JFXPanel fxPanel = new JFXPanel();
frame.add(fxPanel);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Platform.runLater(() -> fxPanel.setScene(createScene()));
});
}
private static Scene createScene() {
Button addButton = new Button("+");
Button removeButton = new Button("-");
ObservableList<LocalDate> selectedDates = FXCollections.observableArrayList();
ListView<LocalDate> dateList = new ListView<>(selectedDates);
String pattern = "yyyy-MM-dd";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(pattern);
DatePicker datePicker = new DatePicker();
datePicker.setShowWeekNumbers(true);
datePicker.setOnAction(event -> System.out.println("Selected date: " + datePicker.getValue()));
datePicker.setPromptText(pattern);
datePicker.setConverter(new StringConverter<LocalDate>() {
@Override
public String toString(LocalDate date) {
return (date == null) ? "" : dateFormatter.format(date);
}
@Override
public LocalDate fromString(String string) {
return ((string == null) || string.isEmpty()) ? null : LocalDate.parse(string, dateFormatter);
}
});
datePicker.setOnAction(event -> selectedDates.add(datePicker.getValue()));
datePicker.setDayCellFactory(new Callback<DatePicker, DateCell>() {
@Override
public DateCell call(DatePicker param) {
return new DateCell() {
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
boolean alreadySelected = selectedDates.contains(item);
setDisable(alreadySelected);
setStyle(alreadySelected ? "-fx-background-color: #09a30f;" : "");
}
};
}
});
// TODO: Hide text field of the date picker combo. Show dropdown directly on clicking "+" button.
// TODO: Keep dropdown of the date picker combo open until intentionally clicking some other where.
dateList.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.DELETE) {
removeSelectedDates(selectedDates, dateList);
}
});
dateList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
removeButton.disableProperty().bind(dateList.getSelectionModel().selectedItemProperty().isNull());
addButton.setOnAction(event -> {
Popup popup = new Popup();
popup.getContent().add(datePicker);
popup.setAutoHide(true);
Window window = addButton.getScene().getWindow();
Bounds bounds = addButton.localToScene(addButton.getBoundsInLocal());
double x = window.getX() + bounds.getMinX();
double y = window.getY() + bounds.getMinY() + bounds.getHeight() + 5;
popup.show(addButton, x, y);
datePicker.show();
});
removeButton.setOnAction(event -> removeSelectedDates(selectedDates, dateList));
HBox buttons = new HBox(addButton, removeButton);
return new Scene(new VBox(buttons, dateList));
}
private static boolean removeSelectedDates(ObservableList<LocalDate> selectedDates, ListView<LocalDate> dateList) {
return selectedDates.removeAll(dateList.getSelectionModel().getSelectedItems());
}
@Override
public void start(Stage primaryStage) {
primaryStage.setScene(createScene());
primaryStage.show();
}
}
@mmdemirbas
Copy link
Author

dropdown
list

@TedLarkenso
Copy link

This looks ideal for my JavaFX app. I changed the package and the class names. Unfortunately when I tried to use it, I hit a problem with JFXPanel.
Please can you help?

"C:\Program Files\Java\jdk-15.0.2\bin\java.exe" --module-path "C:\Program Files\Java\javafx-sdk-16\lib" --add-modules javafx.controls,javafx.fxml --add-exports javafx.graphics/com.sun.javafx.sg.prism=ALL-UNNAMED "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=50564:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Users\Ted\IdeaProjects\PerfectWindows\out\production\Fixtures;C:\Program Files\Java\javafx-sdk-16\lib\src.zip;C:\Program Files\Java\javafx-sdk-16\lib\javafx-swt.jar;C:\Program Files\Java\javafx-sdk-16\lib\javafx.web.jar;C:\Program Files\Java\javafx-sdk-16\lib\javafx.base.jar;C:\Program Files\Java\javafx-sdk-16\lib\javafx.fxml.jar;C:\Program Files\Java\javafx-sdk-16\lib\javafx.media.jar;C:\Program Files\Java\javafx-sdk-16\lib\javafx.swing.jar;C:\Program Files\Java\javafx-sdk-16\lib\javafx.controls.jar;C:\Program Files\Java\javafx-sdk-16\lib\javafx.graphics.jar" fixturesPkg.Main
Exception in thread "AWT-EventQueue-0" java.lang.IllegalAccessError: class javafx.embed.swing.JFXPanel (in unnamed module @0x676e7187) cannot access class com.sun.javafx.logging.PlatformLogger (in module javafx.base) because module javafx.base does not export com.sun.javafx.logging to unnamed module @0x676e7187
at javafx.embed.swing.JFXPanel.(JFXPanel.java:136)
at fixturesPkg.Main.lambda$launchSwing$1(Main.java:38)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:316)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

@mmdemirbas
Copy link
Author

I was using JDK 8 when I wrote this piece of code. I can see from the output that you are using JDK 15. The error seems related to the Java module system which is introduced on JDK 9 as I remember. I'm afraid I don't know much about the module system. But I can say that your problem is unrelated to the MultiDatePicker code itself. I believe you can find some related material when you search for the error on the Internet. For example, I found the following quick results when I searched this phrase:

IllegalAccessError: class javafx.embed.swing.JFXPanel (in unnamed module @0x676e7187) cannot access class com.sun.javafx.logging.PlatformLogger (in module javafx.base) because module javafx.base does not export com.sun.javafx.logging to unnamed module

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment