Skip to content

Commit

Permalink
feat: Enabled loading of FXML files via drag&drop to WelcomeDialog. (g…
Browse files Browse the repository at this point in the history
  • Loading branch information
Oliver-Loeffler committed Aug 26, 2024
1 parent d36d9f6 commit 1bae4de
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -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<File> droppedFiles;
private final List<String> toOpen;
private final List<String> unsupportedItems;
private Consumer<List<String>> openFiles;
private Consumer<List<String>> handleUnsupported;

WelcomeDialogFilesDropHandler(List<File> droppedFiles) {
this.droppedFiles = Objects.requireNonNull(droppedFiles);
this.toOpen = new ArrayList<>(droppedFiles.size());
this.unsupportedItems = new ArrayList<>(droppedFiles.size());
}

final WelcomeDialogFilesDropHandler withSupportedFiles(Consumer<List<String>> handleOpen) {
this.openFiles = handleOpen;
return this;
}

final WelcomeDialogFilesDropHandler withUnsupportedFiles(Consumer<List<String>> 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<String> 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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,17 @@

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.AlertDialog;
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;
Expand All @@ -61,13 +51,25 @@
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.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;

public class WelcomeDialogWindowController extends TemplatesBaseWindowController {

private static final Logger LOGGER = Logger.getLogger(WelcomeDialogWindowController.class.getName());
Expand Down Expand Up @@ -116,6 +118,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<String> 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() {
Expand Down Expand Up @@ -262,7 +291,7 @@ private void handleOpen(List<String> filePaths) {
private void askUserToRemoveMissingRecentFiles(List<String> missingFiles) {
if (!missingFiles.isEmpty()) {
var questionDialog = questionMissingFilesCleanup(getStage(), missingFiles);
if (questionDialog.showAndWait() == AlertDialog.ButtonID.OK) {
if (questionDialog.showAndWait() == ButtonID.OK) {
removeMissingFilesFromPrefs(missingFiles);
loadAndPopulateRecentItemsInBackground();
}
Expand Down Expand Up @@ -292,7 +321,7 @@ private void openFilesAndHideStage(List<String> files) {
void handleOpen(List<String> filePaths,
Consumer<List<String>> missingFilesHandler,
Consumer<List<String>> fileLoader) {

LOGGER.log(Level.INFO, "Attempting to open files: {0}", filePaths);
if (filePaths.isEmpty()) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
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:
Expand Down Expand Up @@ -43,7 +43,7 @@
<?import javafx.scene.text.Font?>

<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="650.0" prefWidth="1024.0" stylesheets="@WelcomeWindow.css" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
<BorderPane fx:id="contentPane">
<BorderPane fx:id="contentPane" onDragDropped="#handleDroppedFiles" onDragOver="#handleFileDraggedOver">
<left>
<VBox styleClass="left-pane">
<children>
Expand Down
Loading

0 comments on commit 1bae4de

Please sign in to comment.