diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9f682a0..4154dea 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-latest, macOS-latest] - java: [ '16' ] + java: [ '17' ] fail-fast: false steps: # ____ _ __ __ ___ ____ __ diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index 35f824a..8c2f750 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: os: [windows-latest, ubuntu-latest, macOS-latest] - java: [ '16' ] + java: [ '17' ] fail-fast: false steps: - name: Git checkout diff --git a/README.md b/README.md index d90f2b7..a06b271 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,4 @@ Rewritten in JavaFX to bring a modern and updated UI as well as a few new featur You can add background images, extract images from PDF files, and save the portrait alongside the token using the same background image and/or colors! Several bug fixes and requested enhancements have also been added since 2.0. -Newly built using the latest JDK (16+) and using GitHub actions for Continuous Integration & Deployment! +Newly built using the latest JDK 17 and using GitHub actions for Continuous Integration & Deployment! diff --git a/build.gradle.kts b/build.gradle.kts index 95ed2d7..d0fb5bc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,9 +3,9 @@ import java.time.LocalDateTime plugins { application java - id("org.openjfx.javafxplugin") version "0.0.10" - id("org.beryx.jlink") version "2.24.4" - id("com.diffplug.spotless") version "6.0.4" + id("org.openjfx.javafxplugin") version "0.0.13" + id("org.beryx.jlink") version "2.25.0" + id("com.diffplug.spotless") version "6.11.0" id("io.wusa.semver-git-plugin") version "2.3.7" } @@ -61,13 +61,13 @@ application { } java { - sourceCompatibility = JavaVersion.VERSION_16 - targetCompatibility = JavaVersion.VERSION_16 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 modularity.inferModulePath.set(true) } javafx { - version = "16" + version = "17.0.2" modules = listOf("javafx.base", "javafx.controls", "javafx.fxml", "javafx.swing", "javafx.graphics") } @@ -95,19 +95,19 @@ repositories { dependencies { // Logging - annotationProcessor(group = "org.apache.logging.log4j", name = "log4j-core", version = "2.15.0") - implementation(group = "org.apache.logging.log4j", name = "log4j-api", version = "2.15.0") - implementation(group = "org.apache.logging.log4j", name = "log4j-1.2-api", version = "2.15.0") + annotationProcessor(group = "org.apache.logging.log4j", name = "log4j-core", version = "2.19.0") + implementation(group = "org.apache.logging.log4j", name = "log4j-api", version = "2.19.0") + implementation(group = "org.apache.logging.log4j", name = "log4j-1.2-api", version = "2.19.0") // Bridges v1 to v2 for other code in other libs - implementation(group = "org.slf4j", name = "slf4j-simple", version = "1.7.32") + implementation(group = "org.slf4j", name = "slf4j-simple", version = "1.7.36") - implementation(group = "io.sentry", name = "sentry", version = "5.4.3") - implementation(group = "io.sentry", name = "sentry-log4j2", version = "4.1.0") + implementation(group = "io.sentry", name = "sentry", version = "5.7.4") + implementation(group = "io.sentry", name = "sentry-log4j2", version = "5.7.4") implementation(group = "javax.servlet", name = "javax.servlet-api", version = "4.0.1") // For PDF image extraction - implementation(group = "org.apache.pdfbox", name = "pdfbox", version = "2.0.24") + implementation(group = "org.apache.pdfbox", name = "pdfbox", version = "2.0.27") // To decrypt password/secured PDFs implementation(group = "org.bouncycastle", name = "bcmail-jdk15on", version = "1.70") @@ -118,13 +118,13 @@ dependencies { implementation(group = "org.sejda.imageio", name = "webp-imageio", version = "0.1.6") // Image processing lib https://github.com/haraldk/TwelveMonkeys - implementation(group = "com.twelvemonkeys.imageio", name = "imageio-core", version = "3.6.4") - implementation(group = "com.twelvemonkeys.imageio", name = "imageio-jpeg", version = "3.6.4") - implementation(group = "com.twelvemonkeys.imageio", name = "imageio-psd", version = "3.6.4") + implementation(group = "com.twelvemonkeys.imageio", name = "imageio-core", version = "3.9.3") + implementation(group = "com.twelvemonkeys.imageio", name = "imageio-jpeg", version = "3.9.3") + implementation(group = "com.twelvemonkeys.imageio", name = "imageio-psd", version = "3.9.3") // Other public libs implementation(group = "commons-io", name = "commons-io", version = "2.11.0") - implementation(group = "com.google.code.gson", name = "gson", version = "2.8.9") + implementation(group = "com.google.code.gson", name = "gson", version = "2.10") } val sharedManifest = the().manifest { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index af7be50..8fad3f5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 745fc55..f4ea031 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -6,8 +6,8 @@ requires javafx.swing; requires java.prefs; requires com.google.gson; - requires common.io; - requires imageio.psd; + requires com.twelvemonkeys.imageio.psd; + requires com.twelvemonkeys.common.io; requires org.apache.logging.log4j; requires jai.imageio.jpeg2000; requires org.apache.commons.io; diff --git a/src/main/java/net/rptools/tokentool/AppConstants.java b/src/main/java/net/rptools/tokentool/AppConstants.java index 0aa59f3..2647c58 100644 --- a/src/main/java/net/rptools/tokentool/AppConstants.java +++ b/src/main/java/net/rptools/tokentool/AppConstants.java @@ -45,7 +45,7 @@ public class AppConstants { public static final String RPTOOLS_URL = "http://www.rptools.net"; - public static final ObservableList VALID_IMAGE_EXTENSIONS = + public static final ObservableList VALID_IMAGE_EXTENSIONS = FXCollections.observableArrayList("webp", "png", "jpg", "gif", "tif"); public static final String DEFAULT_IMAGE_EXTENSION = "webp"; public static final String DEFAULT_IMAGE_EXTENSION_DESCRIPTION = "WebP Image"; diff --git a/src/main/java/net/rptools/tokentool/AppSetup.java b/src/main/java/net/rptools/tokentool/AppSetup.java index 118be9d..9fa1fcf 100644 --- a/src/main/java/net/rptools/tokentool/AppSetup.java +++ b/src/main/java/net/rptools/tokentool/AppSetup.java @@ -122,7 +122,7 @@ public static void installDefaultOverlays() throws IOException { } /* - * OK so here will will only install any overlays that are in a directory with a newer version + * OK so here we will only install any overlays that are in a directory with a newer version * than what is installed. So, if a user skips versions and goes from 2 to 2.3 any overlays * in 2.1, 2.2, and 2.3 will get installed. I'm doing it this way in case a user reorganizes his * directory structure or deletes overlays he doesn't want, we don't reinstall them and annoy diff --git a/src/main/java/net/rptools/tokentool/client/SplashScreenLoader.java b/src/main/java/net/rptools/tokentool/client/SplashScreenLoader.java index 9e32286..868e25f 100644 --- a/src/main/java/net/rptools/tokentool/client/SplashScreenLoader.java +++ b/src/main/java/net/rptools/tokentool/client/SplashScreenLoader.java @@ -37,7 +37,7 @@ public class SplashScreenLoader extends Preloader { public void start(Stage stage) throws Exception { setUserAgentStylesheet( - STYLESHEET_CASPIAN); // I like the look of the this progress bar better for this screen... + STYLESHEET_CASPIAN); // I like the look of this progress bar better for this screen... FXMLLoader fxmlLoader = new FXMLLoader( diff --git a/src/main/java/net/rptools/tokentool/controller/ManageOverlays_Controller.java b/src/main/java/net/rptools/tokentool/controller/ManageOverlays_Controller.java index e35dca1..ff6b8b1 100644 --- a/src/main/java/net/rptools/tokentool/controller/ManageOverlays_Controller.java +++ b/src/main/java/net/rptools/tokentool/controller/ManageOverlays_Controller.java @@ -140,8 +140,8 @@ void initialize() { executorService = Executors.newSingleThreadScheduledExecutor( - runable -> { - loadOverlaysThread = Executors.defaultThreadFactory().newThread(runable); + runnable -> { + loadOverlaysThread = Executors.defaultThreadFactory().newThread(runnable); loadOverlaysThread.setDaemon(true); return loadOverlaysThread; }); diff --git a/src/main/java/net/rptools/tokentool/controller/PdfViewer_Controller.java b/src/main/java/net/rptools/tokentool/controller/PdfViewer_Controller.java index bd1f45e..6d9ec56 100644 --- a/src/main/java/net/rptools/tokentool/controller/PdfViewer_Controller.java +++ b/src/main/java/net/rptools/tokentool/controller/PdfViewer_Controller.java @@ -343,8 +343,8 @@ void saveAllButton_onAction() { pdfSideStackPane.setOpacity(0.2); pdfSideStackPane.setDisable(true); - Task task = - new Task() { + Task task = + new Task<>() { @Override public Void call() { final int pageCount = pdfModel.getPdfPageCount(); @@ -359,8 +359,7 @@ public Void call() { extractAllImagesLabel.setVisible(true); for (int page = 0; page < pageCount; page++) { - pdfModel.extractAllImagesFromPage( - selectedDirectory.getPath(), imageFormat, page, imageMinDimension); + pdfModel.extractAllImagesFromPage(selectedDirectory.getPath(), imageFormat, page, imageMinDimension); updateProgress(page, pageCount); updateMessage("Extracting page " + page + " of " + pageCount); @@ -413,7 +412,7 @@ protected Void call() { pdfProgressIndicator.setVisible(true); pdfProgressIndicator.setOpacity(1); }); - Platform.runLater(() -> pause.play()); + Platform.runLater(pause::play); // Do the actual work Image image = pdfModel.getImage(pageIndex); @@ -423,7 +422,7 @@ protected Void call() { // Skip the animation for quick page turns long loadTime = System.currentTimeMillis() - startTime; if (loadTime < 500) { - Platform.runLater(() -> pause.stop()); + Platform.runLater(pause::stop); pdfProgressIndicator.setVisible(false); } else { pdfProgressIndicator.setVisible(true); @@ -516,7 +515,7 @@ protected void done() { try { imageTilePane.getChildren().clear(); - Collections.sort(imageButtons, new ToggleButtonComparator()); + imageButtons.sort(new ToggleButtonComparator()); Collections.reverse(imageButtons); double pdfMinDimension = 0; @@ -546,10 +545,10 @@ private static class ToggleButtonComparator implements Comparator @Override public int compare(ToggleButton tb1, ToggleButton tb2) { - Double d1 = + double d1 = ((ImageView) tb1.getGraphic()).getImage().getWidth() * ((ImageView) tb1.getGraphic()).getImage().getHeight(); - Double d2 = + double d2 = ((ImageView) tb2.getGraphic()).getImage().getWidth() * ((ImageView) tb2.getGraphic()).getImage().getHeight(); return Double.compare(d1, d2); diff --git a/src/main/java/net/rptools/tokentool/controller/TokenTool_Controller.java b/src/main/java/net/rptools/tokentool/controller/TokenTool_Controller.java index 3b881d9..34d2db4 100644 --- a/src/main/java/net/rptools/tokentool/controller/TokenTool_Controller.java +++ b/src/main/java/net/rptools/tokentool/controller/TokenTool_Controller.java @@ -37,6 +37,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.UnaryOperator; +import java.util.stream.Stream; + import javafx.animation.FadeTransition; import javafx.application.Platform; import javafx.beans.binding.Bindings; @@ -807,48 +809,28 @@ void compositeTokenPane_KeyPressed(KeyEvent key) { double y = getCurrentLayer().getTranslateY(); switch (key.getCode()) { - case LEFT: - case KP_LEFT: - case NUMPAD4: - x--; - break; - case RIGHT: - case KP_RIGHT: - case NUMPAD6: - x++; - break; - case UP: - case KP_UP: - case NUMPAD8: - y--; - break; - case DOWN: - case KP_DOWN: - case NUMPAD2: - y++; - break; - case HOME: - case NUMPAD7: + case LEFT, KP_LEFT, NUMPAD4 -> x--; + case RIGHT, KP_RIGHT, NUMPAD6 -> x++; + case UP, KP_UP, NUMPAD8 -> y--; + case DOWN, KP_DOWN, NUMPAD2 -> y++; + case HOME, NUMPAD7 -> { x--; y--; - break; - case END: - case NUMPAD1: + } + case END, NUMPAD1 -> { x--; y++; - break; - case PAGE_UP: - case NUMPAD9: + } + case PAGE_UP, NUMPAD9 -> { x++; y--; - break; - case PAGE_DOWN: - case NUMPAD3: + } + case PAGE_DOWN, NUMPAD3 -> { x++; y++; - break; - default: - break; + } + default -> { + } } getCurrentLayer().setTranslateX(x); @@ -1269,23 +1251,16 @@ private void saveToken() { FileChooser fileChooser = new FileChooser(); log.info("***** Saving Token as a {}", fileExtension); - try { - File tokenFile = - fileSaveUtil.getFileName( - false, - useFileNumberingCheckbox.isSelected(), - fileNameTextField.getText(), - getFileSaveFormatChoiceboxSelection(), - fileNameSuffixTextField, - true); - fileChooser.setInitialFileName(tokenFile.getName()); - if (tokenFile.getParentFile() != null) { - if (tokenFile.getParentFile().isDirectory()) { - fileChooser.setInitialDirectory(tokenFile.getParentFile()); - } - } - } catch (IOException e1) { - log.error("Error writing token!", e1); + File tokenFile = fileSaveUtil.getFileName( + false, + useFileNumberingCheckbox.isSelected(), + fileNameTextField.getText(), + getFileSaveFormatChoiceboxSelection(), + fileNameSuffixTextField, + true); + fileChooser.setInitialFileName(tokenFile.getName()); + if (tokenFile.getParentFile() != null && tokenFile.getParentFile().isDirectory()) { + fileChooser.setInitialDirectory(tokenFile.getParentFile()); } fileChooser.getExtensionFilters().addAll(AppConstants.IMAGE_EXTENSION_FILTER); @@ -1315,7 +1290,6 @@ private void saveToken() { private boolean writeTokenImage(File tokenFile, String extension) { try { - String imageType = extension; Image tokenImage; if (clipPortraitCheckbox.isSelected()) { @@ -1328,8 +1302,8 @@ private boolean writeTokenImage(File tokenFile, String extension) { BufferedImage imageRGB = SwingFXUtils.fromFXImage(tokenImage, null); - log.debug("Writing token image as: " + imageType); - boolean writeSuccessful = ImageIO.write(imageRGB, imageType, tokenFile); + log.debug("Writing token image as: " + extension); + boolean writeSuccessful = ImageIO.write(imageRGB, extension, tokenFile); if (!writeSuccessful) { // Remove alpha-channel from buffered image @@ -1339,16 +1313,15 @@ private boolean writeTokenImage(File tokenFile, String extension) { graphics.drawImage(image, 0, 0, null); graphics.dispose(); - log.debug("Writing token image as:" + imageType); - writeSuccessful = ImageIO.write(imageRGB, imageType, tokenFile); + log.debug("Writing token image as:" + extension); + writeSuccessful = ImageIO.write(imageRGB, extension, tokenFile); } return writeSuccessful; } catch (IOException e) { log.error("Unable to write token to file: " + tokenFile.getAbsolutePath(), e); } catch (IndexOutOfBoundsException e) { - log.error( - "Image width/height out of bounds: " + getOverlayWidth() + " x " + getOverlayHeight(), e); + log.error("Image width/height out of bounds: " + getOverlayWidth() + " x " + getOverlayHeight(), e); } return false; @@ -1434,7 +1407,7 @@ public void updateOverlayTreeViewRecentFolder(boolean selectMostRecent) { recentFolder.getChildren().add(iter.previous().getValue()); } - if (overlayTreeView.getRoot().getChildren().indexOf(recentFolder) != -1) { + if (overlayTreeView.getRoot().getChildren().contains(recentFolder)) { overlayTreeView.getRoot().getChildren().remove(recentFolder); } overlayTreeView.getRoot().getChildren().add(recentFolder); @@ -1582,10 +1555,9 @@ public void refreshCache() { progressBarLabel.setVisible(true); updateOverlayTreeview(null); - try { + try (Stream files = Files.walk(AppConstants.OVERLAY_DIR.toPath())) { loadCount.set(0); - overlayCount = - (int) Files.walk(AppConstants.OVERLAY_DIR.toPath()).filter(Files::isRegularFile).count(); + overlayCount = (int) files.filter(Files::isRegularFile).count(); log.info("overlayCount: " + overlayCount); treeItems = cacheOverlays(AppConstants.OVERLAY_DIR, null); @@ -1634,7 +1606,7 @@ private void treeViewFinish() { }); } - private TreeItem cacheOverlays(File dir, TreeItem parent) throws IOException { + private TreeItem cacheOverlays(File dir, TreeItem parent) { log.debug("Caching " + dir.getAbsolutePath()); TreeItem root = new TreeItem<>(dir.toPath()); diff --git a/src/main/java/net/rptools/tokentool/model/OverlayTreeItem.java b/src/main/java/net/rptools/tokentool/model/OverlayTreeItem.java index 16751bd..5a1a6ce 100644 --- a/src/main/java/net/rptools/tokentool/model/OverlayTreeItem.java +++ b/src/main/java/net/rptools/tokentool/model/OverlayTreeItem.java @@ -67,7 +67,7 @@ public boolean isLeaf() { // try-with-resources statement ensures that each resource is closed at the end of the // statement otherwise stream is left open and directory can not be deleted! try (Stream files = Files.list(getValue()).filter(Files::isDirectory)) { - isLeaf = files.count() == 0; + isLeaf = files.findAny().isEmpty(); } } catch (IOException e) { @@ -86,8 +86,8 @@ public boolean isLeaf() { */ private ObservableList> buildChildren() { if (Files.isDirectory(getValue())) { - try { - return Files.list(getValue()) + try (Stream files = Files.list(getValue())) { + return files .filter(Files::isDirectory) .map(OverlayTreeItem::new) .collect(Collectors.toCollection(FXCollections::observableArrayList)); diff --git a/src/main/java/net/rptools/tokentool/util/ExtractImagesFromPDF.java b/src/main/java/net/rptools/tokentool/util/ExtractImagesFromPDF.java index 5490a39..8c61f98 100644 --- a/src/main/java/net/rptools/tokentool/util/ExtractImagesFromPDF.java +++ b/src/main/java/net/rptools/tokentool/util/ExtractImagesFromPDF.java @@ -149,8 +149,7 @@ private void getImagesFromResources(PDResources resources, boolean extractOnly) if (!imageTracker.contains(xObject.getCOSObject())) { imageTracker.add(xObject.getCOSObject()); - String name = - pdfName + " - pg " + (currentPageNumber + 1) + " - img " + imageTracker.size(); + String name = pdfName + " - pg " + (currentPageNumber + 1) + " - img " + imageTracker.size(); log.debug("Extracting image... " + name); diff --git a/src/main/java/net/rptools/tokentool/util/FileSaveUtil.java b/src/main/java/net/rptools/tokentool/util/FileSaveUtil.java index 221d278..1b62780 100644 --- a/src/main/java/net/rptools/tokentool/util/FileSaveUtil.java +++ b/src/main/java/net/rptools/tokentool/util/FileSaveUtil.java @@ -117,8 +117,7 @@ public File getTempFileName( boolean useNumbering, String tempFileName, String imageExtension, - TextField fileNameSuffix) - throws IOException { + TextField fileNameSuffix) { return getTempFileName( asToken, useNumbering, tempFileName, imageExtension, fileNameSuffix, true); } @@ -129,8 +128,7 @@ public File getTempFileName( String tempFileName, String imageExtension, TextField fileNameSuffix, - boolean advanceFileNameSuffix) - throws IOException { + boolean advanceFileNameSuffix) { return new File( System.getProperty("java.io.tmpdir"), getFileName( @@ -143,7 +141,7 @@ public File getTempFileName( .getName()); } - public File getTempFileName(String tempFileName, String imageExtension) throws IOException { + public File getTempFileName(String tempFileName, String imageExtension) { final String _extension = "." + imageExtension; if (!tempFileName.endsWith(_extension)) { @@ -153,20 +151,10 @@ public File getTempFileName(String tempFileName, String imageExtension) throws I return new File(System.getProperty("java.io.tmpdir"), tempFileName); } - public File getFileName( - boolean asToken, - boolean useNumbering, - String tempFileName, - String imageExtension, - TextField fileNameSuffix, - boolean advanceFileNameSuffix) - throws IOException { + public File getFileName(boolean asToken, boolean useNumbering, String tempFileName, String imageExtension, + TextField fileNameSuffix, boolean advanceFileNameSuffix) { - if (!imageExtension.startsWith(".")) { - imageExtension = "." + imageExtension; - } else { - imageExtension = "." + imageExtension; - } + imageExtension = "." + imageExtension; if (useNumbering) { int dragCounter; diff --git a/src/main/java/net/rptools/tokentool/util/ImageUtil.java b/src/main/java/net/rptools/tokentool/util/ImageUtil.java index 8dadc6a..275a214 100644 --- a/src/main/java/net/rptools/tokentool/util/ImageUtil.java +++ b/src/main/java/net/rptools/tokentool/util/ImageUtil.java @@ -125,7 +125,6 @@ private static ImageView getImage( if (ImageUtil.SUPPORTED_IMAGE_FILE_FILTER.accept(null, fileURL)) { thumb = processMagenta(new Image(fileURL), COLOR_THRESHOLD, overlayWanted); } else if (ImageUtil.PSD_FILE_FILTER.accept(null, fileURL)) { - ImageInputStream is = null; PSDImageReader reader = null; int imageIndex = 1; @@ -137,8 +136,7 @@ private static ImageView getImage( File file = filePath.toFile(); - try { - is = ImageIO.createImageInputStream(file); + try (ImageInputStream is = ImageIO.createImageInputStream(file)) { if (is == null || is.length() == 0) { log.info("Image from file " + file.getAbsolutePath() + " is null"); } @@ -183,8 +181,9 @@ private static ImageView getImage( log.error("Processing: " + file.getAbsolutePath(), e); } finally { // Dispose reader in finally block to avoid memory leaks - reader.dispose(); - is.close(); + if (reader != null) { + reader.dispose(); + } } } @@ -452,15 +451,11 @@ private static boolean isMagenta(Color color, int fudge) { return false; } - if (g > r - fudge || g > b - fudge) { - return false; - } - - return true; + return !(g > r - fudge) && !(g > b - fudge); } public static String getFileType(File imageFile) { - if (FilenameUtils.getExtension(imageFile.getName()).toLowerCase().equals("psd")) { + if (FilenameUtils.getExtension(imageFile.getName()).equalsIgnoreCase("psd")) { return "Adobe Photoshop " + I18N.getString("imageUtil.filetype.label.image"); } else { return FilenameUtils.getExtension(imageFile.getName()).toUpperCase() diff --git a/src/main/java/net/rptools/tokentool/util/StageResizeMoveUtil.java b/src/main/java/net/rptools/tokentool/util/StageResizeMoveUtil.java index d8491e6..3b8f48d 100644 --- a/src/main/java/net/rptools/tokentool/util/StageResizeMoveUtil.java +++ b/src/main/java/net/rptools/tokentool/util/StageResizeMoveUtil.java @@ -65,8 +65,7 @@ private static void addListenerDeeply(Node node, EventHandler listen node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener); node.addEventHandler(MouseEvent.MOUSE_EXITED, listener); node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener); - if (node instanceof Parent) { - Parent parent = (Parent) node; + if (node instanceof Parent parent) { ObservableList children = parent.getChildrenUnmodifiable(); for (Node child : children) { addListenerDeeply(child, listener);