diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0caf318448..e9b314064c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ on: pull_request: env: - JAVA_VERSION: '22' + JAVA_VERSION: '23' jobs: verify: diff --git a/.github/workflows/bundles-kit.yml b/.github/workflows/bundles-kit.yml index ced47d5d21..a0ba1638ef 100644 --- a/.github/workflows/bundles-kit.yml +++ b/.github/workflows/bundles-kit.yml @@ -7,7 +7,7 @@ on: required: true type: string java-version: - default: '22' + default: '23' required: false type: string diff --git a/.github/workflows/bundles-linux.yml b/.github/workflows/bundles-linux.yml index 09bdc6ac54..837b4aa32d 100644 --- a/.github/workflows/bundles-linux.yml +++ b/.github/workflows/bundles-linux.yml @@ -10,11 +10,11 @@ on: required: true type: string java-version: - default: '22' + default: '23' required: false type: string javafx-version: - default: '22' + default: '23' required: false type: string test: @@ -38,8 +38,11 @@ jobs: release: ${{ inputs.java-version }} - name: Setup JavaFX + id: javafx run: | - wget -P /tmp https://download2.gluonhq.com/openjfx/${{ inputs.javafx-version }}/openjfx-${{ inputs.javafx-version }}_linux-x64_bin-jmods.zip + JAVAFX_MAJOR_VERSION=$(echo ${{ inputs.javafx-version }} | cut -d- -f1) + echo JAVAFX_MAJOR_VERSION=$JAVAFX_MAJOR_VERSION >> $GITHUB_OUTPUT + wget -P /tmp https://download2.gluonhq.com/openjfx/$JAVAFX_MAJOR_VERSION/openjfx-${{ inputs.javafx-version }}_linux-x64_bin-jmods.zip unzip /tmp/openjfx-${{ inputs.javafx-version }}_linux-x64_bin-jmods.zip -d /tmp - name: Cache Maven packages @@ -89,7 +92,7 @@ jobs: ls ${{ env.INSTALL_DIR }} env: MAIN_CLASS: com.oracle.javafx.scenebuilder.app.SceneBuilderApp - JAVAFX_HOME: /tmp/javafx-jmods-${{ inputs.javafx-version }}/ + JAVAFX_HOME: /tmp/javafx-jmods-${{ steps.javafx.outputs.JAVAFX_MAJOR_VERSION }}/ JPACKAGE_HOME: ${{ env.JAVA_HOME }} PROJECT_VERSION: ${{ inputs.project-version }} APP_VERSION: ${{ inputs.app-version }} diff --git a/.github/workflows/bundles-mac.yml b/.github/workflows/bundles-mac.yml index 53631d237e..feade70645 100644 --- a/.github/workflows/bundles-mac.yml +++ b/.github/workflows/bundles-mac.yml @@ -10,11 +10,11 @@ on: required: true type: string java-version: - default: '22' + default: '23' required: false type: string javafx-version: - default: '22' + default: '23' required: false type: string test: @@ -52,8 +52,11 @@ jobs: p12-password: ${{ secrets.CERTIFICATES_PASSWORD }} - name: Setup JavaFX + id: javafx run: | - wget -P /tmp https://download2.gluonhq.com/openjfx/${{ inputs.javafx-version }}/openjfx-${{ inputs.javafx-version }}_osx-x64_bin-jmods.zip + JAVAFX_MAJOR_VERSION=$(echo ${{ inputs.javafx-version }} | cut -d- -f1) + echo JAVAFX_MAJOR_VERSION=$JAVAFX_MAJOR_VERSION >> $GITHUB_OUTPUT + wget -P /tmp https://download2.gluonhq.com/openjfx/$JAVAFX_MAJOR_VERSION/openjfx-${{ inputs.javafx-version }}_osx-x64_bin-jmods.zip unzip /tmp/openjfx-${{ inputs.javafx-version }}_osx-x64_bin-jmods.zip -d /tmp - name: Cache Maven packages @@ -89,10 +92,10 @@ jobs: --mac-sign mv ${{ env.INSTALL_DIR }}/SceneBuilder-${{ env.APP_VERSION }}.dmg ${{ env.INSTALL_DIR }}/SceneBuilder-${{ env.PROJECT_VERSION }}-amd64.dmg ls ${{ env.INSTALL_DIR }} - echo ::set-output name=path::${{ env.INSTALL_DIR }}/SceneBuilder-${{ env.PROJECT_VERSION }}-amd64.dmg + echo path=${{ env.INSTALL_DIR }}/SceneBuilder-${{ env.PROJECT_VERSION }}-amd64.dmg >> $GITHUB_OUTPUT env: MAIN_CLASS: com.oracle.javafx.scenebuilder.app.SceneBuilderApp - JAVAFX_HOME: /tmp/javafx-jmods-${{ inputs.javafx-version }}/ + JAVAFX_HOME: /tmp/javafx-jmods-${{ steps.javafx.outputs.JAVAFX_MAJOR_VERSION }}/ JPACKAGE_HOME: ${{ env.JAVA_HOME }} MACSIGN_PREFIX: ${{ secrets.MACSIGN_PREFIX }} MACSIGN_USER: ${{ secrets.MACSIGN_USER }} diff --git a/.github/workflows/bundles-mac_aarch64.yml b/.github/workflows/bundles-mac_aarch64.yml index 1918ac8abe..cf319b69bf 100644 --- a/.github/workflows/bundles-mac_aarch64.yml +++ b/.github/workflows/bundles-mac_aarch64.yml @@ -10,11 +10,11 @@ on: required: true type: string java-version: - default: '22' + default: '23' required: false type: string javafx-version: - default: '22' + default: '23' required: false type: string test: @@ -53,8 +53,11 @@ jobs: p12-password: ${{ secrets.CERTIFICATES_PASSWORD }} - name: Setup JavaFX + id: javafx run: | - wget -P /tmp https://download2.gluonhq.com/openjfx/${{ inputs.javafx-version }}/openjfx-${{ inputs.javafx-version }}_osx-aarch64_bin-jmods.zip + JAVAFX_MAJOR_VERSION=$(echo ${{ inputs.javafx-version }} | cut -d- -f1) + echo JAVAFX_MAJOR_VERSION=$JAVAFX_MAJOR_VERSION >> $GITHUB_OUTPUT + wget -P /tmp https://download2.gluonhq.com/openjfx/$JAVAFX_MAJOR_VERSION/openjfx-${{ inputs.javafx-version }}_osx-aarch64_bin-jmods.zip unzip /tmp/openjfx-${{ inputs.javafx-version }}_osx-aarch64_bin-jmods.zip -d /tmp - name: Cache Maven packages @@ -90,10 +93,10 @@ jobs: --mac-sign mv ${{ env.INSTALL_DIR }}/SceneBuilder-${{ env.APP_VERSION }}.dmg ${{ env.INSTALL_DIR }}/SceneBuilder-${{ env.PROJECT_VERSION }}-aarch64.dmg ls ${{ env.INSTALL_DIR }} - echo ::set-output name=path::${{ env.INSTALL_DIR }}/SceneBuilder-${{ env.PROJECT_VERSION }}-aarch64.dmg + echo path=${{ env.INSTALL_DIR }}/SceneBuilder-${{ env.PROJECT_VERSION }}-aarch64.dmg >> $GITHUB_OUTPUT env: MAIN_CLASS: com.oracle.javafx.scenebuilder.app.SceneBuilderApp - JAVAFX_HOME: /tmp/javafx-jmods-${{ inputs.javafx-version }}/ + JAVAFX_HOME: /tmp/javafx-jmods-${{ steps.javafx.outputs.JAVAFX_MAJOR_VERSION }}/ JPACKAGE_HOME: ${{ env.JAVA_HOME }} MACSIGN_PREFIX: ${{ secrets.MACSIGN_PREFIX }} MACSIGN_USER: ${{ secrets.MACSIGN_USER }} diff --git a/.github/workflows/bundles-windows.yml b/.github/workflows/bundles-windows.yml index cc1c72bbef..75b5371196 100644 --- a/.github/workflows/bundles-windows.yml +++ b/.github/workflows/bundles-windows.yml @@ -10,11 +10,11 @@ on: required: true type: string java-version: - default: '22' + default: '23' required: false type: string javafx-version: - default: '22' + default: '23' required: false type: string test: @@ -42,9 +42,12 @@ jobs: release: ${{ inputs.java-version }} - name: Setup JavaFX + id: javafx shell: pwsh run: | - Invoke-WebRequest -Uri https://download2.gluonhq.com/openjfx/${{ inputs.javafx-version }}/openjfx-${{ inputs.javafx-version }}_windows-x64_bin-jmods.zip -OutFile D:\openjfx-${{ inputs.javafx-version }}_windows-x64_bin-jmods.zip + $JAVAFX_MAJOR_VERSION = '${{ inputs.javafx-version }}' -split '-' | Select-Object -Index 0 + echo "JAVAFX_MAJOR_VERSION=$JAVAFX_MAJOR_VERSION" >> $env:GITHUB_OUTPUT + Invoke-WebRequest -Uri https://download2.gluonhq.com/openjfx/$JAVAFX_MAJOR_VERSION/openjfx-${{ inputs.javafx-version }}_windows-x64_bin-jmods.zip -OutFile D:\openjfx-${{ inputs.javafx-version }}_windows-x64_bin-jmods.zip Expand-Archive -Force D:\openjfx-${{ inputs.javafx-version }}_windows-x64_bin-jmods.zip D:\ - name: Cache Maven packages @@ -75,7 +78,7 @@ jobs: call dir ${{ env.INSTALL_DIR }} env: MAIN_CLASS: com.oracle.javafx.scenebuilder.app.SceneBuilderApp - JAVAFX_HOME: D:\javafx-jmods-${{ inputs.javafx-version }} + JAVAFX_HOME: D:\javafx-jmods-${{ steps.javafx.outputs.JAVAFX_MAJOR_VERSION }} JPACKAGE_HOME: ${{ env.JAVA_HOME }} PROJECT_VERSION: ${{ inputs.project-version }} APP_VERSION: ${{ inputs.app-version }} diff --git a/.github/workflows/early-access.yml b/.github/workflows/early-access.yml index eb413e9bf7..3b0c59b3b4 100644 --- a/.github/workflows/early-access.yml +++ b/.github/workflows/early-access.yml @@ -5,8 +5,8 @@ on: branches: [ master ] env: - JAVAFX_VERSION: '22' - JAVA_VERSION: '22' + JAVA_VERSION: '23' + JAVAFX_VERSION: '23-ea+27' jobs: precheck: @@ -41,12 +41,12 @@ jobs: PROJECT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) APP_VERSION=$PROJECT_VERSION APP_VERSION=${APP_VERSION%-*} - echo ::set-output name=APP_VERSION::$APP_VERSION - echo ::set-output name=PROJECT_VERSION::$PROJECT_VERSION + echo APP_VERSION=$APP_VERSION >> $GITHUB_OUTPUT + echo PROJECT_VERSION=$PROJECT_VERSION >> $GITHUB_OUTPUT linux-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-linux.yml@master + uses: ./.github/workflows/bundles-linux.yml with: javafx-version: ${{ needs.precheck.outputs.JAVAFX_VERSION }} java-version: ${{ needs.precheck.outputs.JAVA_VERSION }} @@ -56,7 +56,7 @@ jobs: windows-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-windows.yml@master + uses: ./.github/workflows/bundles-windows.yml secrets: WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} WINDOWS_PASSWORD: ${{ secrets.WINDOWS_PASSWORD }} @@ -70,7 +70,7 @@ jobs: mac-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-mac.yml@master + uses: ./.github/workflows/bundles-mac.yml secrets: CERTIFICATES_FILE_BASE64: ${{ secrets.CERTIFICATES_FILE_BASE64 }} CERTIFICATES_PASSWORD: ${{ secrets.CERTIFICATES_PASSWORD }} @@ -87,7 +87,7 @@ jobs: mac_aarch64-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-mac_aarch64.yml@master + uses: ./.github/workflows/bundles-mac_aarch64.yml secrets: CERTIFICATES_FILE_BASE64: ${{ secrets.CERTIFICATES_FILE_BASE64 }} CERTIFICATES_PASSWORD: ${{ secrets.CERTIFICATES_PASSWORD }} @@ -104,7 +104,7 @@ jobs: kit-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-kit.yml@master + uses: ./.github/workflows/bundles-kit.yml with: java-version: ${{ needs.precheck.outputs.JAVA_VERSION }} project-version: ${{ needs.precheck.outputs.PROJECT_VERSION }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca95d84fb2..fad68a505f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,8 +11,8 @@ on: - GA env: - JAVAFX_VERSION: '22' - JAVA_VERSION: '22' + JAVAFX_VERSION: '23' + JAVA_VERSION: '23' jobs: precheck: @@ -54,13 +54,13 @@ jobs: S3_PATH=RC/$PROJECT_VERSION fi echo "Releasing.. "$PROJECT_VERSION - echo ::set-output name=APP_VERSION::$APP_VERSION - echo ::set-output name=PROJECT_VERSION::$PROJECT_VERSION - echo ::set-output name=S3_PATH::$S3_PATH + echo APP_VERSION=$APP_VERSION >> $GITHUB_OUTPUT + echo PROJECT_VERSION=$PROJECT_VERSION >> $GITHUB_OUTPUT + echo S3_PATH=$S3_PATH >> $GITHUB_OUTPUT linux-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-linux.yml@master + uses: ./.github/workflows/bundles-linux.yml with: javafx-version: ${{ needs.precheck.outputs.JAVAFX_VERSION }} java-version: ${{ needs.precheck.outputs.JAVA_VERSION }} @@ -70,7 +70,7 @@ jobs: windows-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-windows.yml@master + uses: ./.github/workflows/bundles-windows.yml secrets: WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} WINDOWS_PASSWORD: ${{ secrets.WINDOWS_PASSWORD }} @@ -84,7 +84,7 @@ jobs: mac-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-mac.yml@master + uses: ./.github/workflows/bundles-mac.yml secrets: CERTIFICATES_FILE_BASE64: ${{ secrets.CERTIFICATES_FILE_BASE64 }} CERTIFICATES_PASSWORD: ${{ secrets.CERTIFICATES_PASSWORD }} @@ -101,7 +101,7 @@ jobs: mac_aarch64-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-mac_aarch64.yml@master + uses: ./.github/workflows/bundles-mac_aarch64.yml secrets: CERTIFICATES_FILE_BASE64: ${{ secrets.CERTIFICATES_FILE_BASE64 }} CERTIFICATES_PASSWORD: ${{ secrets.CERTIFICATES_PASSWORD }} @@ -118,7 +118,7 @@ jobs: kit-bundles: needs: [precheck] - uses: gluonhq/scenebuilder/.github/workflows/bundles-kit.yml@master + uses: ./.github/workflows/bundles-kit.yml with: java-version: ${{ needs.precheck.outputs.JAVA_VERSION }} project-version: ${{ needs.precheck.outputs.PROJECT_VERSION }} diff --git a/app/pom.xml b/app/pom.xml index daec5e6bd3..22288a8722 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -7,7 +7,7 @@ com.gluonhq.scenebuilder parent - 22.0.1-SNAPSHOT + 23.0.0-SNAPSHOT diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index f9630b1ce6..351a99732c 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Gluon and/or its affiliates. + * Copyright (c) 2016, 2024, Gluon and/or its affiliates. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * @@ -82,9 +82,16 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDate; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; @@ -94,6 +101,8 @@ */ public class SceneBuilderApp extends Application implements AppPlatform.AppNotificationHandler { + private static final Logger LOGGER = Logger.getLogger(SceneBuilderApp.class.getName()); + public enum ApplicationControlAction { ABOUT, CHECK_UPDATES, @@ -496,8 +505,25 @@ private void createEmptyDocumentWindow() { newWindow.updateWithDefaultContent(); } + /** + * By default all necessary actions to open a single file or a group of files take place in this method. + * If it is required to perform certain actions after successfully loading all files, please use {@code handleOpenFilesAction(List files, Runnable onSuccess)} instead. + * + * All error handling takes place here within, there is no way yet to access exceptional results and to work with them. + */ @Override public void handleOpenFilesAction(List files) { + handleOpenFilesAction(files, () -> { /* no operation in this case */ }); + } + + /** + * As file loading errors are handled within this method (all exceptions are handled within), it can be helpful to be able to run a certain action after successful file loading (e.g. closing a certain stage). + * For this case this method offers the argument {@code Runnable onSuccess} which will be executed after successful file open activity. The {@code Runnable onSuccess} is only ran once, despite how many files have been loaded. + * + * @param files List of Strings denoting file paths to be opened + * @param onSuccess {@link Runnable} to be executed after all files have been opened successfully + */ + public void handleOpenFilesAction(List files, Runnable onSuccess) { assert files != null; assert files.isEmpty() == false; @@ -507,24 +533,60 @@ public void handleOpenFilesAction(List files) { } EditorController.updateNextInitialDirectory(fileObjs.get(0)); - + + Consumer> onError = errors -> showFileOpenErrors(errors, + () -> WelcomeDialogWindowController.getInstance().getStage()); + // Fix for #45 if (userLibrary.isFirstExplorationCompleted()) { - performOpenFiles(fileObjs); + performOpenFiles(fileObjs, onError, onSuccess); } else { // open files only after the first exploration has finished userLibrary.firstExplorationCompletedProperty().addListener(new InvalidationListener() { @Override public void invalidated(Observable observable) { if (userLibrary.isFirstExplorationCompleted()) { - performOpenFiles(fileObjs); userLibrary.firstExplorationCompletedProperty().removeListener(this); + performOpenFiles(fileObjs, onError, onSuccess); } } }); } } + /** + * For each file open error (when opened through the welcome dialog), the file + * name and the related exception text are presented to the user to confirm. + * For an empty collection, no dialog is displayed. + * + * @param errors A {@link Map} with having the file to be opened as the key and + * the occurred {@link Exception} as value. exceptions. + * @param owner Owner Supplier function to obtain the owners {@link Stage} + */ + private void showFileOpenErrors(Map errors, Supplier owner) { + if (errors.isEmpty()) { + return; + } + + for (Entry error : errors.entrySet()) { + final File fxmlFile = error.getKey(); + final Exception x = error.getValue(); + final ErrorDialog errorDialog = new ErrorDialog(owner.get()); + errorDialog.setMessage(I18N.getString("alert.open.failure1.message", displayName(fxmlFile.getPath()))); + errorDialog.setDetails(I18N.getString("alert.open.failure1.details")); + errorDialog.setDebugInfoWithThrowable(x); + errorDialog.setTitle(I18N.getString("alert.open.failure.title")); + errorDialog.setDetailsTitle(I18N.getString("alert.open.failure.title") + ": " + fxmlFile.getName()); + errorDialog.showAndWait(); + } + } + + private Supplier getOwnerWindow() { + return () -> findFirstUnusedDocumentWindowController() + .map(DocumentWindowController::getStage) + .orElse(WelcomeDialogWindowController.getInstance().getStage()); + } + @Override public void handleMessageBoxFailure(Exception x) { final ErrorDialog errorDialog = new ErrorDialog(null); @@ -657,11 +719,18 @@ public DocumentWindowController getFrontDocumentWindow() { } private void performOpenFiles(List fxmlFiles) { + performOpenFiles(fxmlFiles, r -> showFileOpenErrors(r, getOwnerWindow()), () -> { /* no action here */ } ); + } + + private void performOpenFiles(List fxmlFiles, Consumer> onError, Runnable onSuccess) { assert fxmlFiles != null; assert fxmlFiles.isEmpty() == false; - final Map exceptions = new HashMap<>(); + LOGGER.log(Level.FINE, "Opening {0} files...", fxmlFiles.size()); + final Map exceptionsPerFile = new HashMap<>(); + final List openedFiles = new ArrayList<>(); for (File fxmlFile : fxmlFiles) { + LOGGER.log(Level.FINE, "Attempting to open file {0}", fxmlFile); try { final DocumentWindowController dwc = lookupDocumentWindowControllers(fxmlFile.toURI().toURL()); @@ -673,46 +742,27 @@ private void performOpenFiles(List fxmlFiles) { var hostWindow = findFirstUnusedDocumentWindowController().orElse(makeNewWindow()); hostWindow.loadFromFile(fxmlFile); hostWindow.openWindow(); + openedFiles.add(fxmlFile); + LOGGER.log(Level.INFO, "Successfully opened file {0}", fxmlFile); } } catch (Exception xx) { - exceptions.put(fxmlFile, xx); + LOGGER.log(Level.WARNING, "Failed to open file: %s".formatted(fxmlFile), xx); + exceptionsPerFile.put(fxmlFile, xx); } } + + // Update recent items with opened files + if (!openedFiles.isEmpty()) { + final PreferencesController pc = PreferencesController.getSingleton(); + pc.getRecordGlobal().addRecentItems(openedFiles); + } - switch (exceptions.size()) { - case 0: { // Good - // Update recent items with opened files - final PreferencesController pc = PreferencesController.getSingleton(); - pc.getRecordGlobal().addRecentItems(fxmlFiles); - break; - } - case 1: { - final File fxmlFile = exceptions.keySet().iterator().next(); - final Exception x = exceptions.get(fxmlFile); - final ErrorDialog errorDialog = new ErrorDialog(null); - errorDialog.setMessage(I18N.getString("alert.open.failure1.message", displayName(fxmlFile.getPath()))); - errorDialog.setDetails(I18N.getString("alert.open.failure1.details")); - errorDialog.setDebugInfoWithThrowable(x); - errorDialog.setTitle(I18N.getString("alert.title.open")); - errorDialog.showAndWait(); - break; - } - default: { - final ErrorDialog errorDialog = new ErrorDialog(null); - if (exceptions.size() == fxmlFiles.size()) { - // Open operation has failed for all the files - errorDialog.setMessage(I18N.getString("alert.open.failureN.message")); - errorDialog.setDetails(I18N.getString("alert.open.failureN.details")); - } else { - // Open operation has failed for some files - errorDialog.setMessage(I18N.getString("alert.open.failureMofN.message", - exceptions.size(), fxmlFiles.size())); - errorDialog.setDetails(I18N.getString("alert.open.failureMofN.details")); - } - errorDialog.setTitle(I18N.getString("alert.title.open")); - errorDialog.showAndWait(); - break; - } + if (exceptionsPerFile.isEmpty()) { + LOGGER.log(Level.FINE, "Successfully opened all files."); + onSuccess.run(); + } else { + LOGGER.log(Level.WARNING, "Failed to open {0} of {1} files!", new Object[] {exceptionsPerFile.size(), fxmlFiles.size()}); + onError.accept(exceptionsPerFile); } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandler.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandler.java new file mode 100644 index 0000000000..7158bdc3f9 --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandler.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.oracle.javafx.scenebuilder.app.welcomedialog; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; + +final class WelcomeDialogFilesDropHandler { + + private static final Logger LOGGER = Logger.getLogger(WelcomeDialogFilesDropHandler.class.getName()); + + private final List droppedFiles; + private final List toOpen; + private final List unsupportedItems; + private Consumer> openFiles; + private Consumer> handleUnsupported; + + WelcomeDialogFilesDropHandler(List droppedFiles) { + this.droppedFiles = Objects.requireNonNull(droppedFiles); + this.toOpen = new ArrayList<>(droppedFiles.size()); + this.unsupportedItems = new ArrayList<>(droppedFiles.size()); + } + + final WelcomeDialogFilesDropHandler withSupportedFiles(Consumer> handleOpen) { + this.openFiles = handleOpen; + return this; + } + + final WelcomeDialogFilesDropHandler withUnsupportedFiles(Consumer> unsupportedHandler) { + this.handleUnsupported = unsupportedHandler; + return this; + } + + final void run() { + analyzeDroppedItems(); + handleDropResult(); + } + + final void handleDropResult() { + if (this.openFiles == null) { + throw new IllegalStateException("Please configure a dropped file handling action using the withSupportedFiles(...) method."); + } + if (this.handleUnsupported == null) { + throw new IllegalStateException("Please configure an action for handling of unsupported files using withUnsupportedFiles(...) method."); + } + + if (!toOpen.isEmpty()) { + LOGGER.log(Level.INFO, "Received drop event to open files..."); + openFiles.accept(toOpen); + } else { + LOGGER.log(Level.INFO, "Dropped object does not contain any loadable FXML files."); + handleUnsupported.accept(unsupportedItems); + } + } + + final void analyzeDroppedItems() { + if (droppedFiles.isEmpty()) { + return; + } + + for (var file : droppedFiles) { + if (file.isDirectory()) { + File[] children = file.listFiles(); + List inDir = new ArrayList<>(children.length); + for (var child : children) { + if (isFxml(child)) { + inDir.add(child.getAbsolutePath()); + } + } + if (inDir.isEmpty()) { + unsupportedItems.add(file.getAbsolutePath()); + } else { + toOpen.addAll(inDir); + } + } else { + if (isFxml(file)) { + toOpen.add(file.getAbsolutePath()); + } else { + unsupportedItems.add(file.getAbsolutePath()); + } + } + } + } + + final boolean isFxml(File file) { + if (file.isDirectory()) { + return false; + } + return file.toString() + .toLowerCase() + .endsWith(".fxml"); + } +} diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogWindowController.java index aaac1d51fe..dd259c9791 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogWindowController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Gluon and/or its affiliates. + * Copyright (c) 2017, 2024, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -32,14 +32,28 @@ package com.oracle.javafx.scenebuilder.app.welcomedialog; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + import com.oracle.javafx.scenebuilder.app.SceneBuilderApp; import com.oracle.javafx.scenebuilder.app.i18n.I18N; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesController; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal; import com.oracle.javafx.scenebuilder.app.util.AppSettings; import com.oracle.javafx.scenebuilder.kit.editor.EditorController; +import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.AlertDialog; +import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.AbstractModalDialog.ButtonID; +import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.ErrorDialog; import com.oracle.javafx.scenebuilder.kit.template.Template; import com.oracle.javafx.scenebuilder.kit.template.TemplatesBaseWindowController; + import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.fxml.FXML; @@ -48,19 +62,19 @@ import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.Tooltip; +import javafx.scene.input.DragEvent; +import javafx.scene.input.TransferMode; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import javafx.stage.Modality; +import javafx.stage.Stage; import javafx.stage.WindowEvent; -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - public class WelcomeDialogWindowController extends TemplatesBaseWindowController { + private static final Logger LOGGER = Logger.getLogger(WelcomeDialogWindowController.class.getName()); + @FXML private BorderPane contentPane; @@ -80,7 +94,7 @@ public class WelcomeDialogWindowController extends TemplatesBaseWindowController private final SceneBuilderApp sceneBuilderApp; - private WelcomeDialogWindowController() { + WelcomeDialogWindowController() { super(WelcomeDialogWindowController.class.getResource("WelcomeWindow.fxml"), //NOI18N I18N.getBundle(), null); // We want it to be a top level window so we're setting the owner to null. @@ -105,6 +119,33 @@ protected void controllerDidCreateStage() { getStage().setTitle(I18N.getString("welcome.title")); getStage().initModality(Modality.APPLICATION_MODAL); } + + @FXML + void handleFileDraggedOver(DragEvent event) { + if (event.getDragboard().hasFiles()) { + event.acceptTransferModes(TransferMode.ANY); + } + } + + @FXML + void handleDroppedFiles(DragEvent event) { + if (event.getDragboard().hasFiles()) { + new WelcomeDialogFilesDropHandler(event.getDragboard().getFiles()) + .withSupportedFiles(fileNames->Platform.runLater(()->handleOpen(fileNames))) + .withUnsupportedFiles(unsupported->notifyUserWhenDroppedUnsupportedFiles(unsupported)) + .run(); + } + } + + private void notifyUserWhenDroppedUnsupportedFiles(List unsupported) { + ErrorDialog dialog = new ErrorDialog(getStage()); + dialog.setTitle(I18N.getString("welcome.loading.when.dropped.error.title")); + dialog.setMessage(I18N.getString("welcome.loading.when.dropped.error.message")); + String detail = unsupported.stream() + .collect(Collectors.joining(System.lineSeparator())); + dialog.setDetails(detail); + Platform.runLater(()->dialog.showAndWait()); + } @Override protected void controllerDidLoadFxml() { @@ -168,7 +209,7 @@ private void loadAndPopulateRecentItemsInBackground() { } public static WelcomeDialogWindowController getInstance() { - if (instance == null){ + if (instance == null) { instance = new WelcomeDialogWindowController(); var stage = instance.getStage(); stage.setMinWidth(800); @@ -201,7 +242,6 @@ private void openDocument() { new FileChooser.ExtensionFilter(I18N.getString("file.filter.label.fxml"), "*.fxml") ); fileChooser.setInitialDirectory(EditorController.getNextInitialDirectory()); - List fxmlFiles = fileChooser.showOpenMultipleDialog(getStage()); // no file was selected, so nothing to do @@ -215,17 +255,101 @@ private void openDocument() { handleOpen(paths); } + + protected static AlertDialog questionMissingFilesCleanup(Stage stage, List missingFiles) { + String withPath = missingFiles.stream() + .collect(Collectors.joining(System.lineSeparator())); + + AlertDialog question = new AlertDialog(stage); + question.setDefaultButtonID(ButtonID.CANCEL); + question.setShowDefaultButton(true); + question.setOKButtonTitle(I18N.getString("alert.welcome.file.not.found.okay")); + question.setTitle(I18N.getString("alert.welcome.file.not.found.title")); + question.setMessage(I18N.getString("alert.welcome.file.not.found.question")); + question.setCancelButtonTitle(I18N.getString("alert.welcome.file.not.found.no")); + question.setDetails(I18N.getString("alert.welcome.file.not.found.message") + withPath); + return question; + } + + boolean filePathExists(String filePath) { + return Files.exists(Path.of(filePath)); + } + + /** + * Attempts to open files in filePaths. Scene Builder will only attempt to load + * files which exist. If a file does not exist, Scene Builder will ask the user + * to remove this file from recent files. + * + * @param filePaths List of file paths to project files to be opened by Scene + * Builder. + */ + private void handleOpen(List filePaths) { + handleOpen(filePaths, + this::askUserToRemoveMissingRecentFiles, + this::attemptOpenExistingFiles); + } - private void handleOpen(List paths) { + private void askUserToRemoveMissingRecentFiles(List missingFiles) { + if (!missingFiles.isEmpty()) { + var questionDialog = questionMissingFilesCleanup(getStage(), missingFiles); + if (questionDialog.showAndWait() == AlertDialog.ButtonID.OK) { + removeMissingFilesFromPrefs(missingFiles); + loadAndPopulateRecentItemsInBackground(); + } + } + } + + private void attemptOpenExistingFiles(List paths) { if (sceneBuilderApp.startupTasksFinishedBinding().get()) { - sceneBuilderApp.handleOpenFilesAction(paths); - getStage().hide(); + openFilesAndHideStage(paths); } else { - showMasker(() -> { - sceneBuilderApp.handleOpenFilesAction(paths); - getStage().hide(); - }); + showMasker(() -> openFilesAndHideStage(paths)); + } + } + + private void openFilesAndHideStage(List files) { + sceneBuilderApp.handleOpenFilesAction(files, () -> getStage().hide()); + } + + /** + * Attempts to open files in filePaths. + * In case of files are missing, a special procedure is applied to handle missing files. + * + * @param filePaths List of file paths to project files to be opened by Scene Builder. + * @param missingFilesHandler Determines how missing files are handled. + * @param fileLoader Determines how files are loaded. + */ + void handleOpen(List filePaths, + Consumer> missingFilesHandler, + Consumer> fileLoader) { + LOGGER.log(Level.INFO, "Attempting to open files: {0}", filePaths); + if (filePaths.isEmpty()) { + return; } + + List existingFiles = new ArrayList<>(); + List missingFiles = new ArrayList<>(); + filePaths.forEach(file -> { + if (filePathExists(file)) { + existingFiles.add(file); + } else { + missingFiles.add(file); + } + }); + + missingFilesHandler.accept(missingFiles); + + if (existingFiles.isEmpty()) { + return; + } + + fileLoader.accept(existingFiles); + } + + private void removeMissingFilesFromPrefs(List missingFiles) { + missingFiles.forEach(fxmlFileName -> LOGGER.log(Level.INFO, "Removing missing file from recent items: {0}", fxmlFileName)); + PreferencesRecordGlobal preferencesRecordGlobal = PreferencesController.getSingleton().getRecordGlobal(); + preferencesRecordGlobal.removeRecentItems(missingFiles); } private void showMasker(Runnable onEndAction) { @@ -239,7 +363,6 @@ private void showMasker(Runnable onEndAction) { if (isFinished) { Platform.runLater(() -> { onEndAction.run(); - // restore state in case welcome dialog is opened again contentPane.setDisable(false); masker.setVisible(false); diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties index 98c1360641..543a2cd435 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties @@ -1,4 +1,4 @@ -# Copyright (c) 2016, 2023, Gluon and/or its affiliates. +# Copyright (c) 2016, 2024, Gluon and/or its affiliates. # Copyright (c) 2012, 2014, Oracle and/or its affiliates. # All rights reserved. Use is subject to license terms. # @@ -397,12 +397,9 @@ alert.title.copy = Copy alert.title.start = Startup alert.title.messagebox = External Open +alert.open.failure.title = File Open Error alert.open.failure1.message = Could not open ''{0}'' alert.open.failure1.details = Open operation has failed. Make sure that the chosen file is a valid FXML document. -alert.open.failureN.message = Could not open the specified files -alert.open.failureN.details = Open operation has failed. Make sure that those files are valid FXML documents. -alert.open.failureMofN.message = Could not open {0} files -alert.open.failureMofN.details = Open operation has failed for some files. Make sure that those files are valid FXML documents. alert.review.question.message = {0} of your documents contain unsaved changes. Do you want to review them before exiting ? alert.review.question.details = Your changes will be lost if you don't review them. alert.overwrite.message = The file ''{0}'' was modified externally. Do you want to overwrite it ? @@ -436,7 +433,12 @@ alert.save.noextension.savewith = Save with '.fxml' alert.save.noextension.savewithout = Save without '.fxml' alert.open.failure.charset.not.found = The given charset could not be set. alert.open.failure.charset.not.found.details = It may be due to the encoding in your document. - +alert.welcome.file.not.found.question = One or more project files were not found. +alert.welcome.file.not.found.message = If those are located on a removable or network drive, please make sure the drive is connected.\n\n +alert.welcome.file.not.found.title = Project file(s) not found +alert.welcome.file.not.found.okay = Remove From List +alert.welcome.file.not.found.no = OK + # ----------------------------------------------------------------------------- # Log Messages # ----------------------------------------------------------------------------- @@ -509,6 +511,9 @@ welcome.recent.items.loading = Loading recent projects... welcome.recent.items.no.recent.items = no recent projects welcome.open.project.label = Open Project welcome.loading.label = Loading Components... +welcome.loading.when.dropped.error.title=Unsupported file format or empty directory +welcome.loading.when.dropped.error.message=The dropped object is either not a JavaFX FXML file or does not contain any FXML files to be loaded. + # -- Template (this keys are replicated from SceneBuilderKit.properties) template.new.project.label = New Project from Template template.title.header.desktop = Desktop diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp_ja.properties b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp_ja.properties index 280754a225..131863bdc4 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp_ja.properties +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp_ja.properties @@ -394,10 +394,6 @@ alert.title.messagebox = 外部プログラムで開く alert.open.failure1.message = ''{0}''を開けませんでした alert.open.failure1.details = 開くのに失敗しました。選択されたファイルが有効なFXMLドキュメントであることを確認してください。 -alert.open.failureN.message = 指定されたファイルを開けませんでした -alert.open.failureN.details = 開くのに失敗しました。それらのファイルが有効なFXMLドキュメントであることを確認してください。 -alert.open.failureMofN.message = {0}個のファイルを開けませんでした -alert.open.failureMofN.details = いくつかのファイルを開くのに失敗しました。それらのファイルが有効なFXMLドキュメントであることを確認してください。 alert.review.question.message = ドキュメントのうち{0}個に未保存の変更があります。終了する前にそれらをレビューしますか。 alert.review.question.details = レビューしないと、変更は失われます。 alert.overwrite.message = ファイル''{0}''は外部で変更されました。上書きしますか。 diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp_zh_CN.properties b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp_zh_CN.properties index 39a31e900c..0764eba7b9 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp_zh_CN.properties +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp_zh_CN.properties @@ -399,10 +399,6 @@ alert.title.messagebox = 外部打开 alert.open.failure1.message = 无法打开 ''{0}'' alert.open.failure1.details = 打开操作失败。确保所选文件是有效的 FXML 文档。 -alert.open.failureN.message = 无法打开指定的文件 -alert.open.failureN.details = 打开操作失败。请确保这些文件是有效的 FXML 文档。 -alert.open.failureMofN.message = 无法打开 {0} 文件 -alert.open.failureMofN.details = 某些文件的打开操作失败。请确保这些文件是有效的 FXML 文档。 alert.review.question.message = {0} 文档包含未保存的更改。您想在退出之前查看它们吗? alert.review.question.details = 如果您不查看更改,则更改将丢失。 alert.overwrite.message = 文件 ''{0}'' 已在外部修改。你想覆盖它吗? diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeWindow.fxml b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeWindow.fxml index 76c629d24e..d77276f797 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeWindow.fxml +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeWindow.fxml @@ -1,7 +1,7 @@ - - org.assertj - assertj-core - 3.19.0 - test - @@ -117,15 +109,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - 3.0.0-M5 - - false - 1 - - diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/util/dialog/ErrorDialog.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/util/dialog/ErrorDialog.java index 2696e85183..8ee7f50804 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/util/dialog/ErrorDialog.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/util/dialog/ErrorDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -43,6 +43,7 @@ public class ErrorDialog extends AlertDialog { private String debugInfo; + private String detailsTitle; public ErrorDialog(Window owner) { super(owner); @@ -80,7 +81,10 @@ public void setDebugInfoWithThrowable(Throwable t) { setDebugInfo(info); } - + public void setDetailsTitle(String detailsTitle) { + this.detailsTitle = detailsTitle; + } + /* * Private */ @@ -90,8 +94,11 @@ private void updateActionButtonVisibility() { } private void showDetailsDialog() { - final TextViewDialog detailDialog = new TextViewDialog(null); + final TextViewDialog detailDialog = new TextViewDialog(this.getStage()); detailDialog.setText(debugInfo); + if (detailsTitle != null) { + detailDialog.setTitle(detailsTitle); + } detailDialog.showAndWait(); } } diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/util/ExplorerBase.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/util/ExplorerBase.java index 6b711d4a49..1ddf56ebd0 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/util/ExplorerBase.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/util/ExplorerBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Gluon and/or its affiliates. + * Copyright (c) 2020, 2024, Gluon and/or its affiliates. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * @@ -102,8 +102,10 @@ JarReportEntry exploreEntry(String entryName, ClassLoader classLoader, String cl // http://stackoverflow.com/questions/8100376/class-forname-vs-classloader-loadclass-which-to-use-for-dynamic-loading entryClass = classLoader.loadClass(className); // Note: static intializers of entryClass are not run, this doesn't seem to be an issue - if (Modifier.isAbstract(entryClass.getModifiers()) - || !Node.class.isAssignableFrom(entryClass)) { + final int modifiers = entryClass.getModifiers(); + if (Modifier.isAbstract(modifiers) + || !Node.class.isAssignableFrom(entryClass) + || !(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers))) { status = JarReportEntry.Status.IGNORED; entryClass = null; entryException = null; diff --git a/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/editor/panel/util/dialog/AbstractModalDialogW.fxml b/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/editor/panel/util/dialog/AbstractModalDialogW.fxml index c847ccc9f4..84284225ae 100644 --- a/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/editor/panel/util/dialog/AbstractModalDialogW.fxml +++ b/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/editor/panel/util/dialog/AbstractModalDialogW.fxml @@ -1,4 +1,5 @@ + - + + @@ -55,7 +57,7 @@ - + diff --git a/pom.xml b/pom.xml index 99dba32f59..1dbbbc06fa 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.gluonhq.scenebuilder parent pom - 22.0.1-SNAPSHOT + 23.0.0-SNAPSHOT Scene Builder Scene Builder is a visual, drag n drop, layout tool for designing JavaFX application user interfaces 2012 @@ -16,11 +16,11 @@ - 22 + 23 ${java.version} ${java.version} ${java.version} - 22 + 23-ea+27 1.1.0 6.2.2 4.0.19 @@ -70,6 +70,12 @@ junit-jupiter-engine test + + org.assertj + assertj-core + 3.19.0 + test + @@ -134,6 +140,17 @@ maven-resources-plugin 3.2.0 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + false + 1 + +