diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/AssetBrowser.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/AssetBrowser.java
new file mode 100644
index 000000000..7f4f06757
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/AssetBrowser.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser;
+
+import com.jme3.gde.assetbrowser.icons.Icons;
+import com.jme3.gde.assetbrowser.widgets.AssetPreviewWidget;
+import com.jme3.gde.assetbrowser.widgets.MatDefPreview;
+import com.jme3.gde.assetbrowser.widgets.MaterialPreview;
+import com.jme3.gde.assetbrowser.widgets.ModelPreview;
+import com.jme3.gde.assetbrowser.widgets.PreviewInteractionListener;
+import com.jme3.gde.assetbrowser.widgets.SoundPreview;
+import com.jme3.gde.assetbrowser.widgets.TexturePreview;
+import com.jme3.gde.core.assets.BinaryModelDataObject;
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.core.util.ProjectSelection;
+import com.jme3.gde.materials.JMEMaterialDataObject;
+import com.jme3.gde.materials.multiview.MaterialOpenSupport;
+import com.jme3.gde.scenecomposer.OpenSceneComposer;
+import com.jme3.gde.scenecomposer.SceneComposerTopComponent;
+import com.jme3.gde.textureeditor.JmeTextureDataObject;
+import com.jme3.gde.textureeditor.OpenTexture;
+import com.jme3.scene.Spatial;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.swing.JOptionPane;
+import org.netbeans.api.project.Project;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.filesystems.FileAttributeEvent;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileEvent;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileRenameEvent;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectNotFoundException;
+import org.openide.util.Exceptions;
+
+/**
+ * Top component for AssetBrowser
+ *
+ * @author rickard
+ */
+public class AssetBrowser extends javax.swing.JPanel implements PreviewInteractionListener {
+
+ private static final String MATERIALS = "Materials";
+ private static final String MAT_DEFS = "MatDefs";
+ private static final String MODELS = "Models";
+ private static final String TEXTURES = "Textures";
+ private static final String SOUNDS = "Sounds";
+ private ProjectAssetManager assetManager;
+ private PreviewHelper previewUtil;
+ private String projectName;
+
+ private int lastGridColumns = 0;
+ private int lastGridRows = 0;
+ private String lastFilter;
+
+ private int sizeX = Constants.sizeX;
+ private int sizeY = Constants.sizeY;
+ private int imageSize = Constants.imageSize;
+ private int oldSliderValue = 2;
+
+ private boolean componentListenerAdded = false;
+ private ComponentListener resizeListener = new ComponentListener() {
+ @Override
+ public void componentResized(ComponentEvent e) {
+ setSize(getParent().getSize());
+ setPreferredSize(getParent().getSize());
+ getLayout().layoutContainer(AssetBrowser.this);
+ java.awt.EventQueue.invokeLater(() -> {
+
+ loadAssets(lastFilter);
+
+ });
+ }
+
+ @Override
+ public void componentMoved(ComponentEvent e) {
+// setSize(new Dimension(0,0));
+ }
+
+ @Override
+ public void componentShown(ComponentEvent e) {
+ }
+
+ @Override
+ public void componentHidden(ComponentEvent e) {
+ }
+ };
+
+ /**
+ * Creates new form AssetBrowser
+ */
+ public AssetBrowser() {
+
+ initComponents();
+
+ addComponentListener(resizeListener);
+ }
+
+ /**
+ * Will recalculate grid, and remove all previews and regenerate if rows or
+ * columns or filter has changed
+ *
+ * @param filter only show previews containing filter
+ */
+ private void loadAssets(String filter) {
+ if (assetManager == null) {
+ return;
+ }
+
+ // this is required to make the panel resize
+ if (!componentListenerAdded && getParent() != null) {
+ getParent().addComponentListener(resizeListener);
+ componentListenerAdded = true;
+ removeComponentListener(resizeListener);
+ }
+ Dimension size = previewsPanel.getSize();
+
+ int rows = Math.min(size.height, getHeight() - 30) / sizeY;
+
+ final var textures = Arrays.stream(assetManager.getTextures()).filter(s -> filter.isEmpty() || s.toLowerCase().contains(filter)).collect(Collectors.toList());
+ final var materials = Arrays.stream(assetManager.getMaterials()).filter(s -> filter.isEmpty() || s.toLowerCase().contains(filter)).collect(Collectors.toList());
+ final var models = Arrays.stream(assetManager.getModels()).filter(s -> filter.isEmpty() || s.toLowerCase().contains(filter)).collect(Collectors.toList());
+ final var sounds = Arrays.stream(assetManager.getSounds()).filter(s -> filter.isEmpty() || s.toLowerCase().contains(filter)).collect(Collectors.toList());
+ final var matdefs = Arrays.stream(assetManager.getMatDefs()).filter(s -> filter.isEmpty() || s.toLowerCase().contains(filter)).collect(Collectors.toList());
+ int numAssets = textures.size() + materials.size() + models.size() + sounds.size() + matdefs.size();
+ int columns = Math.max(numAssets / rows, 1);
+
+ Dimension newSize = new Dimension(columns * sizeX, rows * sizeY);
+ if (columns != lastGridColumns || rows != lastGridRows || !lastFilter.equals(filter)) {
+ GridBagConstraints constraints = new GridBagConstraints();
+ previewsPanel.setLayout(new GridBagLayout());
+ constraints.fill = GridBagConstraints.BOTH;
+ constraints.gridx = sizeX;
+ constraints.gridy = sizeY;
+ previewsPanel.removeAll();
+ previewsPanel.setSize(newSize);
+ previewsPanel.setPreferredSize(newSize);
+
+ previewsPanel.setLayout(new GridBagLayout());
+
+ int index = addAssets(textures, TEXTURES, constraints, columns, rows, 0);
+ index = addAssets(materials, MATERIALS, constraints, columns, rows, index);
+ index = addAssets(models, MODELS, constraints, columns, rows, index);
+ index = addAssets(sounds, SOUNDS, constraints, columns, rows, index);
+ index = addAssets(matdefs, MAT_DEFS, constraints, columns, rows, index);
+ lastGridColumns = columns;
+ lastGridRows = rows;
+ lastFilter = filter;
+ }
+ }
+
+ /**
+ * Add assets of a specific type to the grid
+ *
+ * @param items the assets to preview
+ * @param type type of asset
+ * @param constraints
+ * @param columns columns in the grid
+ * @param rows rows in the grid
+ * @param startIndex last used index when adding previews
+ * @return
+ */
+ private int addAssets(List items, String type, GridBagConstraints constraints, int columns, int rows, int startIndex) {
+ Collections.sort(items);
+ int index = startIndex;
+ for (String item : items) {
+ AssetPreviewWidget preview = null;
+
+ constraints.gridx = index % columns;
+ constraints.gridy = (int) (((float) index-1) / (columns));
+ if (type.startsWith(TEXTURES)) {
+ preview = new TexturePreview(this, previewUtil.getOrCreateTexturePreview(item, imageSize));
+ } else if (type.startsWith(MATERIALS)) {
+ preview = new MaterialPreview(this);
+ preview.setPreviewImage(previewUtil.getOrCreateMaterialPreview(item, preview, imageSize));
+ } else if (type.startsWith(MODELS)) {
+ preview = new ModelPreview(this);
+ preview.setPreviewImage(previewUtil.getOrCreateModelPreview(item, preview, imageSize));
+ } else if (type.startsWith(SOUNDS)) {
+ preview = new SoundPreview(this, previewUtil.getSoundPreview(item, imageSize));
+ } else if (type.startsWith(MAT_DEFS)) {
+ preview = new MatDefPreview(this, previewUtil.getDefaultIcon(item, imageSize));
+ }
+ if (preview == null) {
+ continue;
+ }
+ preview.setMinimumSize(new Dimension(sizeX, sizeY));
+ preview.setPreferredSize(new Dimension(sizeX, sizeY));
+ if (assetManager.getAbsoluteAssetPath(item) != null) {
+ preview.setEditable(true);
+ }
+ preview.setPreviewName(item);
+ previewsPanel.add(preview, constraints);
+ index++;
+ }
+ return index;
+ }
+
+ /**
+ * Creates the base folder for previews in the project directory
+ *
+ * @param assetManager
+ */
+ private void createAssetBrowserFolder(ProjectAssetManager assetManager) {
+ final FileObject fileObject = assetManager.getProject().getProjectDirectory();
+
+ final String path = assetManager.isGradleProject() ?
+ fileObject.getParent().getPath() : fileObject.getPath();
+
+ final File file = new File(path, ".assetBrowser/");
+ if (!file.exists()) {
+ file.mkdirs();
+ }
+ }
+
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ projectLabel = new javax.swing.JLabel();
+ jScrollPane1 = new javax.swing.JScrollPane();
+ previewsPanel = new javax.swing.JPanel();
+ jPanel2 = new javax.swing.JPanel();
+ filterField = new javax.swing.JTextField();
+ clearFilterButton = new javax.swing.JButton();
+ filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
+ sizeSlider = new javax.swing.JSlider();
+
+ setAlignmentX(0.0F);
+ setAlignmentY(0.0F);
+ setPreferredSize(new java.awt.Dimension(2000, 2000));
+ setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.PAGE_AXIS));
+
+ projectLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
+ org.openide.awt.Mnemonics.setLocalizedText(projectLabel, org.openide.util.NbBundle.getMessage(AssetBrowser.class, "AssetBrowser.projectLabel.text")); // NOI18N
+ projectLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP);
+ projectLabel.setAlignmentX(0.5F);
+ projectLabel.setAlignmentY(0.0F);
+ projectLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
+ projectLabel.setMaximumSize(new java.awt.Dimension(32000, 32000));
+ projectLabel.addMouseListener(new java.awt.event.MouseAdapter() {
+ public void mouseClicked(java.awt.event.MouseEvent evt) {
+ projectLabelMouseClicked(evt);
+ }
+ });
+ add(projectLabel);
+
+ jScrollPane1.setMinimumSize(new java.awt.Dimension(150, 150));
+ jScrollPane1.setPreferredSize(new java.awt.Dimension(2000, 3000));
+
+ previewsPanel.setPreferredSize(new java.awt.Dimension(200, 300));
+ previewsPanel.setLayout(new java.awt.GridBagLayout());
+ jScrollPane1.setViewportView(previewsPanel);
+
+ add(jScrollPane1);
+
+ jPanel2.setMaximumSize(new java.awt.Dimension(2147483647, 23));
+ jPanel2.setMinimumSize(new java.awt.Dimension(104, 33));
+ jPanel2.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEADING));
+
+ filterField.setText(org.openide.util.NbBundle.getMessage(AssetBrowser.class, "AssetBrowser.filterField.text")); // NOI18N
+ filterField.setToolTipText(org.openide.util.NbBundle.getMessage(AssetBrowser.class, "AssetBrowser.filterField.toolTipText")); // NOI18N
+ filterField.setMinimumSize(new java.awt.Dimension(40, 23));
+ filterField.setPreferredSize(new java.awt.Dimension(250, 23));
+ filterField.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ filterFieldFocusLost(evt);
+ }
+ });
+ filterField.addMouseListener(new java.awt.event.MouseAdapter() {
+ public void mouseClicked(java.awt.event.MouseEvent evt) {
+ filterFieldMouseClicked(evt);
+ }
+ });
+ filterField.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ filterFieldActionPerformed(evt);
+ }
+ });
+ filterField.addKeyListener(new java.awt.event.KeyAdapter() {
+ public void keyPressed(java.awt.event.KeyEvent evt) {
+ filterFieldKeyPressed(evt);
+ }
+ });
+ jPanel2.add(filterField);
+
+ clearFilterButton.setIcon(Icons.clearFilter);
+ org.openide.awt.Mnemonics.setLocalizedText(clearFilterButton, org.openide.util.NbBundle.getMessage(AssetBrowser.class, "AssetBrowser.clearFilterButton.text")); // NOI18N
+ clearFilterButton.setMaximumSize(new java.awt.Dimension(23, 23));
+ clearFilterButton.setMinimumSize(new java.awt.Dimension(23, 23));
+ clearFilterButton.setPreferredSize(new java.awt.Dimension(23, 23));
+ clearFilterButton.addMouseListener(new java.awt.event.MouseAdapter() {
+ public void mouseClicked(java.awt.event.MouseEvent evt) {
+ clearFilterButtonMouseClicked(evt);
+ }
+ });
+ jPanel2.add(clearFilterButton);
+ jPanel2.add(filler1);
+
+ sizeSlider.setMaximum(2);
+ sizeSlider.setToolTipText(org.openide.util.NbBundle.getMessage(AssetBrowser.class, "AssetBrowser.sizeSlider.toolTipText")); // NOI18N
+ sizeSlider.setName("sizeSlider"); // NOI18N
+ sizeSlider.setPreferredSize(new java.awt.Dimension(100, 20));
+ sizeSlider.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ sizeSliderStateChanged(evt);
+ }
+ });
+ jPanel2.add(sizeSlider);
+
+ add(jPanel2);
+ }// //GEN-END:initComponents
+
+ /**
+ * Select project to view
+ *
+ * @param evt
+ */
+ private void projectLabelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_projectLabelMouseClicked
+ assetManager = ProjectSelection.getProjectAssetManager("Select project");
+ projectName = assetManager.getProject().getProjectDirectory().getName();
+ projectLabel.setText(projectName);
+ previewUtil = new PreviewHelper(assetManager);
+ createAssetBrowserFolder(assetManager);
+ // Check which assets was added/deleted/renamed/changed? Nah, just load
+ // everything!
+ assetManager.getAssetFolder().addRecursiveListener(new FileChangeListener() {
+ @Override
+ public void fileFolderCreated(FileEvent fe) {
+ loadAssets(lastFilter);
+ }
+
+ @Override
+ public void fileDataCreated(FileEvent fe) {
+ loadAssets(lastFilter);
+ }
+
+ @Override
+ public void fileChanged(FileEvent fe) {
+ loadAssets(lastFilter);
+ }
+
+ @Override
+ public void fileDeleted(FileEvent fe) {
+ loadAssets(lastFilter);
+ }
+
+ @Override
+ public void fileRenamed(FileRenameEvent fre) {
+ loadAssets(lastFilter);
+ }
+
+ @Override
+ public void fileAttributeChanged(FileAttributeEvent fae) {
+ loadAssets(lastFilter);
+ }
+ });
+ loadAssets("");
+ }//GEN-LAST:event_projectLabelMouseClicked
+
+ private void filterFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_filterFieldFocusLost
+
+ }//GEN-LAST:event_filterFieldFocusLost
+
+ private void filterFieldKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_filterFieldKeyPressed
+ if (evt.getKeyCode() == KeyEvent.VK_TAB || evt.getKeyCode() == KeyEvent.VK_ENTER) {
+ previewsPanel.requestFocusInWindow();
+ loadAssets(filterField.getText().toLowerCase());
+ }
+ }//GEN-LAST:event_filterFieldKeyPressed
+
+ private void filterFieldMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_filterFieldMouseClicked
+ //filterField.setSelectionStart(0);
+ //filterField.setSelectionEnd(filterField.getSelectedText().length());
+ }//GEN-LAST:event_filterFieldMouseClicked
+
+ private void filterFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_filterFieldActionPerformed
+ // TODO add your handling code here:
+ }//GEN-LAST:event_filterFieldActionPerformed
+
+ /**
+ * Change size of previews
+ *
+ * @param evt
+ */
+ private void sizeSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sizeSliderStateChanged
+ final var value = sizeSlider.getValue();
+ switch (value) {
+ case 0:
+ sizeX = (int) (Constants.sizeX * 0.5f);
+ sizeY = (int) (Constants.sizeY * 0.5f);
+ imageSize = (int) (Constants.imageSize * 0.5f);
+ break;
+ case 1:
+ sizeX = (int) (Constants.sizeY * 0.75f);
+ sizeY = (int) (Constants.sizeX * 0.75f);
+ imageSize = (int) (Constants.imageSize * 0.75f);
+ break;
+ case 2:
+ sizeX = Constants.sizeY;
+ sizeY = Constants.sizeX;
+ imageSize = Constants.imageSize;
+ break;
+ }
+ if (value != oldSliderValue) {
+ loadAssets(lastFilter);
+ oldSliderValue = value;
+ }
+ }//GEN-LAST:event_sizeSliderStateChanged
+
+ private void clearFilterButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_clearFilterButtonMouseClicked
+ filterField.setText("");
+ lastFilter = "";
+ loadAssets("");
+ }//GEN-LAST:event_clearFilterButtonMouseClicked
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton clearFilterButton;
+ private javax.swing.Box.Filler filler1;
+ private javax.swing.JTextField filterField;
+ private javax.swing.JPanel jPanel2;
+ private javax.swing.JScrollPane jScrollPane1;
+ private javax.swing.JPanel previewsPanel;
+ private javax.swing.JLabel projectLabel;
+ private javax.swing.JSlider sizeSlider;
+ // End of variables declaration//GEN-END:variables
+
+ /**
+ * Double click an asset to open it (if supported)
+ */
+ @Override
+ public void openAsset(AssetPreviewWidget widget) {
+ FileObject pf = assetManager.getAssetFileObject(widget.getPreviewName());
+ if (widget instanceof MaterialPreview) {
+ try {
+ JMEMaterialDataObject matObject = (JMEMaterialDataObject) DataObject.find(pf);
+ new MaterialOpenSupport(matObject.getPrimaryEntry()).open();
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ } else if (widget instanceof TexturePreview) {
+ try {
+ JmeTextureDataObject textureObject = (JmeTextureDataObject) DataObject.find(pf);
+ OpenTexture openTexture = new OpenTexture(textureObject);
+ openTexture.actionPerformed(null);
+ } catch (DataObjectNotFoundException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+
+ } else if (widget instanceof ModelPreview) {
+ try {
+ BinaryModelDataObject model = (BinaryModelDataObject) DataObject.find(pf);
+ Runnable call = () -> {
+ assetManager.clearCache();
+ final Spatial asset = model.loadAsset();
+ if (asset != null) {
+ java.awt.EventQueue.invokeLater(() -> {
+ SceneComposerTopComponent composer = SceneComposerTopComponent.findInstance();
+ composer.openScene(asset, model, assetManager);
+ });
+ } else {
+ NotifyDescriptor.Confirmation msg = new NotifyDescriptor.Confirmation(
+ "Error opening " + model.getPrimaryFile().getNameExt(),
+ NotifyDescriptor.OK_CANCEL_OPTION,
+ NotifyDescriptor.ERROR_MESSAGE);
+ DialogDisplayer.getDefault().notify(msg);
+ }
+
+ };
+ new Thread(call).start();
+ } catch (DataObjectNotFoundException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ } else {
+ JOptionPane.showMessageDialog(null, "Not yet supported");
+ }
+ }
+
+ @Override
+ public void refreshPreview(AssetPreviewWidget widget) {
+ // not yet implemented
+ }
+
+ /**
+ * Delete the asset
+ */
+ @Override
+ public void deleteAsset(AssetPreviewWidget widget) {
+ int result = JOptionPane.showConfirmDialog(null, "Delete asset? " + widget.getAssetName());
+ if (result == JOptionPane.OK_OPTION) {
+ FileObject pf = assetManager.getAssetFileObject(widget.getPreviewName());
+ try {
+ pf.delete();
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ }
+
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/AssetBrowserTopComponent.form b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/AssetBrowserTopComponent.form
new file mode 100644
index 000000000..77d24fcba
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/AssetBrowserTopComponent.form
@@ -0,0 +1,48 @@
+
+
+
+
+
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/AssetBrowserTopComponent.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/AssetBrowserTopComponent.java
new file mode 100644
index 000000000..670cf039c
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/AssetBrowserTopComponent.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2003-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser;
+
+import org.netbeans.api.settings.ConvertAsProperties;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.util.NbBundle;
+import org.openide.windows.TopComponent;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ * Top component which displays something.
+ */
+@ConvertAsProperties(
+ dtd = "-//com.jme3.gde.assetbrowser//AssetBrowser//EN",
+ autostore = false
+)
+@TopComponent.Description(
+ preferredID = "AssetBrowserTopComponent",
+ //iconBase="SET/PATH/TO/ICON/HERE",
+ persistenceType = TopComponent.PERSISTENCE_ALWAYS
+)
+@TopComponent.Registration(mode = "navigator", openAtStartup = true)
+@ActionID(category = "Window", id = "com.jme3.gde.assetbrowser.AssetBrowserTopComponent")
+@ActionReference(path = "Menu/Window" /*, position = 333 */)
+@TopComponent.OpenActionRegistration(
+ displayName = "#CTL_AssetBrowserAction",
+ preferredID = "AssetBrowserTopComponent"
+)
+//@Messages({
+// "CTL_AssetBrowserAction=AssetBrowser",
+// "CTL_AssetBrowserTopComponent=AssetBrowser Window",
+// "HINT_AssetBrowserTopComponent=This is a AssetBrowser window"
+//})
+public final class AssetBrowserTopComponent extends TopComponent {
+
+ public AssetBrowserTopComponent() {
+ initComponents();
+ setName(NbBundle.getMessage(AssetBrowserTopComponent.class, "CTL_AssetBrowserTopComponent"));
+// setName(Bundle.CTL_AssetBrowserTopComponent());
+ setToolTipText(NbBundle.getMessage(AssetBrowserTopComponent.class, "HINT_AssetBrowserTopComponent"));
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ assetBrowser1 = new com.jme3.gde.assetbrowser.AssetBrowser();
+
+ setLayout(new java.awt.BorderLayout());
+ add(assetBrowser1, java.awt.BorderLayout.SOUTH);
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private com.jme3.gde.assetbrowser.AssetBrowser assetBrowser1;
+ // End of variables declaration//GEN-END:variables
+ @Override
+ public void componentOpened() {
+ // TODO add custom code on component opening
+ }
+
+ @Override
+ public void componentClosed() {
+ // TODO add custom code on component closing
+ }
+
+ void writeProperties(java.util.Properties p) {
+ // better to version settings since initial version as advocated at
+ // http://wiki.apidesign.org/wiki/PropertyFiles
+ p.setProperty("version", "1.0");
+ // TODO store your settings
+ }
+
+ void readProperties(java.util.Properties p) {
+ String version = p.getProperty("version");
+ // TODO read your settings according to their version
+ }
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/Bundle.properties b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/Bundle.properties
new file mode 100644
index 000000000..49e153588
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/Bundle.properties
@@ -0,0 +1,10 @@
+OpenIDE-Module-Display-Category=jMonkeyEngine
+OpenIDE-Module-Name=AssetBrowser
+CTL_AssetBrowserAction=AssetBrowser
+CTL_AssetBrowserTopComponent=AssetBrowser
+HINT_AssetBrowserTopComponent=AssetBrowser
+AssetBrowser.projectLabel.text=No project selected
+AssetBrowser.filterField.text=
+AssetBrowser.sizeSlider.toolTipText=Size of previews
+AssetBrowser.clearFilterButton.text=
+AssetBrowser.filterField.toolTipText=Enter text to filter
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/Constants.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/Constants.java
new file mode 100644
index 000000000..5460f2ef6
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/Constants.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser;
+
+/**
+ *
+ * @author rickard
+ */
+public class Constants {
+
+ static final int sizeX = 170;
+ static final int sizeY = 180;
+ static final int imageSize = 150;
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/PreviewHelper.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/PreviewHelper.java
new file mode 100644
index 000000000..90dd453e1
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/PreviewHelper.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser;
+
+import com.jme3.gde.assetbrowser.icons.Icons;
+import com.jme3.gde.assetbrowser.widgets.AssetPreviewWidget;
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.core.icons.IconList;
+import com.jme3.gde.core.scene.PreviewRequest;
+import com.jme3.gde.core.scene.SceneApplication;
+import com.jme3.gde.core.scene.SceneListener;
+import com.jme3.gde.core.scene.SceneRequest;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import javax.imageio.ImageIO;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import jme3tools.converters.ImageToAwt;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+/**
+ * Helper class for generating preview images
+ *
+ * @author rickard
+ */
+public class PreviewHelper {
+
+ private static final int PREVIEW_SIZE = 150;
+ private final ProjectAssetManager assetManager;
+
+ private static final Vector3f previewLocation = new Vector3f(4, 4, 7);
+ private static final Vector3f previewLookAt = new Vector3f(0, 0, 0);
+
+ public PreviewHelper(ProjectAssetManager assetManager) {
+ this.assetManager = assetManager;
+ }
+
+ public Icon getOrCreateTexturePreview(String asset, int size) {
+ final var icon = tryGetPreview(asset, size);
+ if (icon != null) {
+ return icon;
+ }
+ System.out.println("creating preview ");
+ Texture texture = assetManager.loadTexture(asset);
+ Image image = texture.getImage();
+
+ BufferedImage buff = ImageToAwt.convert(image, false, false, 0);
+
+ BufferedImage scaled = scaleDown(buff, 150, 150);
+ BufferedImage noAlpha = convertImage(scaled);
+ savePreview(assetManager, asset.split("\\.")[0], noAlpha);
+ return new ImageIcon(noAlpha);
+ }
+
+ public Icon getSoundPreview(String asset, int size) {
+ return Icons.soundIcon;
+ }
+
+ public Icon getDefaultIcon(String asset, int size) {
+ return Icons.assetIcon;
+ }
+
+ public Icon getOrCreateMaterialPreview(String asset, AssetPreviewWidget widget, int size) {
+ final var icon = tryGetPreview(asset, size);
+ if (icon != null) {
+ return icon;
+ }
+
+ Material mat = assetManager.loadMaterial(asset);
+
+ Box boxMesh = new Box(1.75f, 1.75f, 1.75f);
+ Geometry box = new Geometry("previewBox", boxMesh);
+ box.setMaterial(mat);
+ PreviewListener listener = new PreviewListener(assetManager, mat.getAssetName().split("\\.")[0], widget);
+ SceneApplication.getApplication().addSceneListener(listener);
+ SceneApplication.getApplication().enqueue(() -> {
+ SceneApplication.getApplication().getRenderManager().preloadScene(box);
+ java.awt.EventQueue.invokeLater(() -> {
+ MikktspaceTangentGenerator.generate(box);
+ PreviewRequest request = new PreviewRequest(listener, box, PREVIEW_SIZE, PREVIEW_SIZE);
+ request.getCameraRequest().setLocation(previewLocation);
+ request.getCameraRequest().setLookAt(previewLookAt, Vector3f.UNIT_Y);
+ SceneApplication.getApplication().createPreview(request);
+ });
+ });
+ return IconList.asset;
+ }
+
+ private Icon tryGetPreview(String asset, int size) {
+ final var assetPath = assetManager.getAbsoluteAssetPath(asset);
+
+ final FileTime assetModificationTime = getAssetModificationTime(assetPath);
+
+ final File previewFile = loadPreviewFile(assetManager, asset.split("\\.")[0]);
+
+ if (previewFile != null && assetModificationTime != null) {
+ final Path previewPath = previewFile.toPath();
+ if(previewPath == null) {
+ return null;
+ }
+ try {
+ final BasicFileAttributes previewAttributes = Files.readAttributes(
+ previewPath, BasicFileAttributes.class);
+ final FileTime previewCreationTime = previewAttributes.creationTime();
+
+ if (previewCreationTime.compareTo(assetModificationTime) > 0) {
+ System.out.println("existing preview OK " + previewFile);
+ BufferedImage image = ImageIO.read(previewFile);
+ if (image != null) {
+ return new ImageIcon(size != PREVIEW_SIZE ? image.getScaledInstance(size, size, 0) : image);
+ }
+ }
+
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ return null;
+ }
+
+ public Icon getOrCreateModelPreview(String asset, AssetPreviewWidget widget, int size) {
+ final var icon = tryGetPreview(asset, size);
+ if (icon != null) {
+ return icon;
+ }
+
+ Material unshaded = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ unshaded.setColor("Color", ColorRGBA.Red);
+
+ Spatial spatial = assetManager.loadModel(asset);
+
+ recurseApplyDefaultMaterial(spatial, unshaded);
+
+ PreviewListener listener = new PreviewListener(assetManager, asset.split("\\.")[0], widget);
+ SceneApplication.getApplication().addSceneListener(listener);
+ SceneApplication.getApplication().enqueue(() -> {
+ SceneApplication.getApplication().getRenderManager().preloadScene(spatial);
+ java.awt.EventQueue.invokeLater(() -> {
+ PreviewRequest request = new PreviewRequest(listener, spatial, PREVIEW_SIZE, PREVIEW_SIZE);
+ request.getCameraRequest().setLocation(previewLocation);
+ request.getCameraRequest().setLookAt(previewLookAt, Vector3f.UNIT_Y);
+ SceneApplication.getApplication().createPreview(request);
+ });
+ });
+ return IconList.asset;
+ }
+
+ /**
+ * Applies unshaded MatDef if spatial has no material already
+ *
+ * @param spatial
+ * @param material
+ */
+ private void recurseApplyDefaultMaterial(Spatial spatial, Material material) {
+ if (spatial instanceof Node) {
+ ((Node) spatial).getChildren().forEach(child -> recurseApplyDefaultMaterial(child, material));
+ } else if (spatial instanceof Geometry) {
+ if (((Geometry) spatial).getMaterial() == null) {
+ spatial.setMaterial(material);
+ }
+ }
+ }
+
+ private FileTime getAssetModificationTime(String assetPath) {
+ if (assetPath == null) {
+ return null;
+ }
+ Path path = new File(assetPath).toPath();
+
+ try {
+ // creating BasicFileAttributes class object using
+ // readAttributes method
+ BasicFileAttributes file_att = Files.readAttributes(
+ path, BasicFileAttributes.class);
+ return file_att.lastModifiedTime();
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return null;
+ }
+
+ private File loadPreviewFile(ProjectAssetManager assetManager, String id) {
+ FileObject fileObject = assetManager.getProject().getProjectDirectory();
+ return new File(fileObject.getPath() + "/.assetBrowser/", id + ".jpg");
+ }
+
+ private void savePreview(ProjectAssetManager assetManager, String id, BufferedImage preview) {
+ FileObject fileObject = assetManager.getProject().getProjectDirectory();
+ String[] fileSections = id.split("/");
+ String fileName = fileSections[fileSections.length - 1];
+ File path = new File(fileObject.getPath() + "/.assetBrowser/" + id.substring(0, id.length() - fileName.length()));
+ File file = new File(path, fileName + ".jpg");
+ try {
+ path.mkdirs();
+ file.createNewFile();
+ ImageIO.write(preview, "jpg", file);
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
+ private BufferedImage scaleDown(BufferedImage sourceImage, int targetWidth, int targetHeight) {
+ int sourceWidth = sourceImage.getWidth();
+ int sourceHeight = sourceImage.getHeight();
+
+ BufferedImage targetImage = new BufferedImage(targetWidth, targetHeight, sourceImage.getType());
+
+ Graphics2D g = targetImage.createGraphics();
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ g.drawImage(sourceImage, 0, 0, targetWidth, targetHeight, 0, 0, sourceWidth, sourceHeight, null);
+ g.dispose();
+
+ return targetImage;
+ }
+
+ private class PreviewListener implements SceneListener {
+
+ final AssetPreviewWidget widget;
+ final ProjectAssetManager assetManager;
+ private final String assetName;
+
+ public PreviewListener(ProjectAssetManager assetManager, String assetName, AssetPreviewWidget widget) {
+ this.widget = widget;
+ this.assetManager = assetManager;
+ this.assetName = assetName;
+ }
+
+ @Override
+ public void sceneOpened(SceneRequest request) {
+ }
+
+ @Override
+ public void sceneClosed(SceneRequest request) {
+ }
+
+ @Override
+ public void previewCreated(PreviewRequest request) {
+ if (request.getRequester() == this) {
+ final var image = convertImage(request.getImage());
+ java.awt.EventQueue.invokeLater(() -> {
+ widget.setPreviewImage(new ImageIcon(image));
+ savePreview(assetManager, assetName, image);
+ widget.revalidate();
+ });
+ }
+ }
+ };
+
+ private static BufferedImage convertImage(BufferedImage preview) {
+ final int width = preview.getWidth();
+ final int height = preview.getHeight();
+ BufferedImage converted = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ Graphics2D g = converted.createGraphics();
+ g.setColor(Color.WHITE);
+ g.fillRect(0, 0, width, height);
+ int w = preview.getWidth();
+ int h = preview.getHeight();
+ g.drawImage(preview, 0, 0, w, h, 0, h, w, 0, null);
+ g.dispose();
+ return converted;
+ }
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/dnd/AssetPreviewPopupMenu.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/dnd/AssetPreviewPopupMenu.java
new file mode 100644
index 000000000..41939f75d
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/dnd/AssetPreviewPopupMenu.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser.dnd;
+
+import java.awt.event.ActionListener;
+import javax.swing.JPopupMenu;
+
+/**
+ * Pop up menu for actions on asset previews
+ *
+ * @author rickard
+ */
+public class AssetPreviewPopupMenu extends JPopupMenu {
+
+ public AssetPreviewPopupMenu(ActionListener listener) {
+ add("Refresh").addActionListener(listener);
+ add("Delete").addActionListener(listener);
+ }
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/dnd/AssetPreviewWidgetMouseListener.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/dnd/AssetPreviewWidgetMouseListener.java
new file mode 100644
index 000000000..4fd9c959c
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/dnd/AssetPreviewWidgetMouseListener.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser.dnd;
+
+import com.jme3.gde.assetbrowser.widgets.AssetPreviewWidget;
+import com.jme3.gde.assetbrowser.widgets.PreviewInteractionListener;
+import java.awt.event.MouseAdapter;
+import javax.swing.JOptionPane;
+import javax.swing.TransferHandler;
+
+/**
+ * For handling drag and drop of assets.
+ *
+ * @author rickard
+ */
+public final class AssetPreviewWidgetMouseListener extends MouseAdapter {
+
+ private final AssetPreviewWidget previewWidget;
+ private final PreviewInteractionListener listener;
+ private boolean pressed, moved;
+
+ public AssetPreviewWidgetMouseListener(AssetPreviewWidget previewWidget, PreviewInteractionListener listener) {
+ this.previewWidget = previewWidget;
+ this.listener = listener;
+ }
+
+ @Override
+ public void mouseClicked(final java.awt.event.MouseEvent evt) {
+ if (evt.getClickCount() == 2) {
+ evt.consume();
+ if (previewWidget.isEditable()) {
+ listener.openAsset(previewWidget);
+ } else {
+ JOptionPane.showMessageDialog(null, "Project dependencies can't be edited");
+ }
+ }
+ }
+
+ @Override
+ public void mousePressed(final java.awt.event.MouseEvent evt) {
+ pressed = true;
+
+ }
+
+ @Override
+ public void mouseReleased(final java.awt.event.MouseEvent evt) {
+ pressed = false;
+ moved = false;
+ }
+
+ @Override
+ public void mouseMoved(final java.awt.event.MouseEvent evt) {
+ }
+
+ @Override
+ public void mouseDragged(final java.awt.event.MouseEvent evt) {
+ if (pressed) {
+ moved = true;
+ TransferHandler handler = previewWidget.getTransferHandler();
+ if (handler != null) {
+ handler.exportAsDrag(previewWidget, evt, TransferHandler.COPY);
+ }
+ }
+ }
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/Icons.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/Icons.java
new file mode 100644
index 000000000..9f0eb7951
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/Icons.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser.icons;
+
+import javax.swing.ImageIcon;
+import org.openide.util.ImageUtilities;
+
+/**
+ * Lists all icons used by the AssetBrowser
+ * @author rickard
+ */
+public class Icons {
+
+ public static final String ICONS_PATH = "com/jme3/gde/assetbrowser/icons/";
+ public static final String TEXTURE_REMOVE = ICONS_PATH + "remove_texture.svg";
+ // use png for asset preview
+ public static final String SOUND_WAVES = ICONS_PATH + "sound_waves.png";
+ public static final String ASSET = ICONS_PATH + "asset.png";
+
+ public static final ImageIcon clearFilter =
+ ImageUtilities.loadImageIcon(TEXTURE_REMOVE, false);
+ public static final ImageIcon soundIcon =
+ ImageUtilities.loadImageIcon(SOUND_WAVES, false);
+ public static final ImageIcon assetIcon =
+ ImageUtilities.loadImageIcon(ASSET, false);
+}
\ No newline at end of file
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/asset.png b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/asset.png
new file mode 100644
index 000000000..f94a32406
Binary files /dev/null and b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/asset.png differ
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/remove_texture.svg b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/remove_texture.svg
new file mode 100644
index 000000000..969e234ef
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/remove_texture.svg
@@ -0,0 +1 @@
+
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/sound-waves.png b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/sound-waves.png
new file mode 100644
index 000000000..58c6322fc
Binary files /dev/null and b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/sound-waves.png differ
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/sound-waves.svg b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/sound-waves.svg
new file mode 100644
index 000000000..66dfed4f5
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/icons/sound-waves.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/AssetPreviewWidget.form b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/AssetPreviewWidget.form
new file mode 100644
index 000000000..e6b7a0420
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/AssetPreviewWidget.form
@@ -0,0 +1,68 @@
+
+
+
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/AssetPreviewWidget.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/AssetPreviewWidget.java
new file mode 100644
index 000000000..12a3d2150
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/AssetPreviewWidget.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser.widgets;
+
+import com.jme3.gde.assetbrowser.dnd.AssetPreviewPopupMenu;
+import com.jme3.gde.assetbrowser.dnd.AssetPreviewWidgetMouseListener;
+import com.jme3.gde.core.icons.IconList;
+import com.jme3.gde.core.scene.PreviewRequest;
+import com.jme3.gde.core.scene.SceneListener;
+import com.jme3.gde.core.scene.SceneRequest;
+import com.jme3.gde.core.dnd.AssetNameHolder;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.Icon;
+
+/**
+ * Displays an asset as an image in the AssetBrowser and handles open action and
+ * dragging (if supported)
+ *
+ * @author rickard
+ */
+public class AssetPreviewWidget extends javax.swing.JPanel implements SceneListener, AssetNameHolder, ActionListener {
+
+ private boolean editable;
+ private PreviewInteractionListener listener;
+
+ /**
+ * Creates new form AssetPreviewWidget
+ */
+ public AssetPreviewWidget() {
+ initComponents();
+ }
+
+ public AssetPreviewWidget(final PreviewInteractionListener listener, Icon icon) {
+ this(listener);
+ assetPreviewLabel.setIcon(icon);
+ }
+
+ public AssetPreviewWidget(final PreviewInteractionListener listener) {
+ this();
+ this.listener = listener;
+ final var mouseListener = new AssetPreviewWidgetMouseListener(this, listener);
+ addMouseListener(mouseListener);
+ addMouseMotionListener(mouseListener);
+ setComponentPopupMenu(new AssetPreviewPopupMenu(this));
+ }
+
+ public void setPreviewImage(Icon icon) {
+ assetPreviewLabel.setIcon(icon);
+ }
+
+ public void setPreviewName(String name) {
+ assetNameLabel.setText(name);
+ setToolTipText(name);
+ }
+
+ public String getPreviewName() {
+ return assetNameLabel.getText();
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ assetNameLabel = new javax.swing.JLabel();
+ assetPreviewLabel = new javax.swing.JLabel();
+
+ setMinimumSize(new java.awt.Dimension(170, 180));
+ setPreferredSize(new java.awt.Dimension(170, 180));
+ addMouseListener(new java.awt.event.MouseAdapter() {
+ public void mousePressed(java.awt.event.MouseEvent evt) {
+ formMousePressed(evt);
+ }
+ });
+ setLayout(new java.awt.BorderLayout());
+
+ org.openide.awt.Mnemonics.setLocalizedText(assetNameLabel, org.openide.util.NbBundle.getMessage(AssetPreviewWidget.class, "AssetPreviewWidget.assetNameLabel.text")); // NOI18N
+ add(assetNameLabel, java.awt.BorderLayout.SOUTH);
+ assetNameLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(AssetPreviewWidget.class, "AssetPreviewWidget.assetNameLabel.AccessibleContext.accessibleName")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(assetPreviewLabel, org.openide.util.NbBundle.getMessage(AssetPreviewWidget.class, "AssetPreviewWidget.assetPreviewLabel.text")); // NOI18N
+ assetPreviewLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP);
+ assetPreviewLabel.setPreferredSize(new java.awt.Dimension(150, 150));
+ add(assetPreviewLabel, java.awt.BorderLayout.CENTER);
+ assetPreviewLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(AssetPreviewWidget.class, "AssetPreviewWidget.assetPreviewLabel.AccessibleContext.accessibleName")); // NOI18N
+ }// //GEN-END:initComponents
+
+ private void formMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMousePressed
+
+ }//GEN-LAST:event_formMousePressed
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel assetNameLabel;
+ private javax.swing.JLabel assetPreviewLabel;
+ // End of variables declaration//GEN-END:variables
+
+ @Override
+ public void sceneOpened(SceneRequest request) {
+ }
+
+ @Override
+ public void sceneClosed(SceneRequest request) {
+ }
+
+ @Override
+ public void previewCreated(PreviewRequest request) {
+ if (request.getRequester() == this) {
+ java.awt.EventQueue.invokeLater(() -> {
+ assetPreviewLabel.setIcon(IconList.asset);
+// invalidate();
+ revalidate();
+ repaint();
+// updateUI();
+ });
+ }
+ }
+
+ @Override
+ public String getAssetName() {
+ return assetNameLabel.getText();
+ }
+
+ @Override
+ public void setAssetName(String name) {
+ assetNameLabel.setText(name);
+ }
+
+ public void setEditable(boolean editable) {
+ this.editable = editable;
+ }
+
+ public boolean isEditable() {
+ return editable;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ switch (e.getActionCommand()) {
+ case "Refresh":
+ listener.refreshPreview(this);
+ break;
+ case "Delete":
+ listener.deleteAsset(this);
+ break;
+ }
+ }
+
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/Bundle.properties b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/Bundle.properties
new file mode 100644
index 000000000..7be2afddf
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/Bundle.properties
@@ -0,0 +1,4 @@
+AssetPreviewWidget.assetNameLabel.text=assetName
+AssetPreviewWidget.assetNameLabel.AccessibleContext.accessibleName=assetNameLabel
+AssetPreviewWidget.assetPreviewLabel.AccessibleContext.accessibleName=assetPreviewLabel
+AssetPreviewWidget.assetPreviewLabel.text=
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/MatDefPreview.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/MatDefPreview.java
new file mode 100644
index 000000000..5faad99a9
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/MatDefPreview.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser.widgets;
+
+import javax.swing.Icon;
+
+/**
+ * A preview of a MatDef in the AssetBrowser
+ *
+ * @author rickard
+ */
+public class MatDefPreview extends AssetPreviewWidget {
+
+ public MatDefPreview(PreviewInteractionListener listener, Icon icon) {
+ super(listener, icon);
+ }
+
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/MaterialPreview.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/MaterialPreview.java
new file mode 100644
index 000000000..2d51fb22e
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/MaterialPreview.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser.widgets;
+
+import com.jme3.gde.core.dnd.MaterialDataFlavor;
+import com.jme3.gde.core.dnd.AssetGrabHandler;
+
+/**
+ *
+ * @author rickard
+ */
+public class MaterialPreview extends AssetPreviewWidget {
+
+ public MaterialPreview(PreviewInteractionListener listener) {
+ super(listener);
+ setTransferHandler(new AssetGrabHandler(this, new MaterialDataFlavor()));
+ }
+
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/ModelPreview.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/ModelPreview.java
new file mode 100644
index 000000000..363fe71a6
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/ModelPreview.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser.widgets;
+
+import com.jme3.gde.core.dnd.AssetGrabHandler;
+import com.jme3.gde.core.dnd.SpatialDataFlavor;
+
+/**
+ *
+ * @author rickard
+ */
+public class ModelPreview extends AssetPreviewWidget {
+
+ public ModelPreview(PreviewInteractionListener listener) {
+ super(listener);
+ setTransferHandler(new AssetGrabHandler(this, new SpatialDataFlavor()));
+ }
+
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/PreviewInteractionListener.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/PreviewInteractionListener.java
new file mode 100644
index 000000000..af69d3c03
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/PreviewInteractionListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser.widgets;
+
+/**
+ *
+ * @author rickard
+ */
+public interface PreviewInteractionListener {
+
+ void openAsset(AssetPreviewWidget widget);
+
+ void refreshPreview(AssetPreviewWidget widget);
+
+ void deleteAsset(AssetPreviewWidget widget);
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/SoundPreview.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/SoundPreview.java
new file mode 100644
index 000000000..148703750
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/SoundPreview.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.assetbrowser.widgets;
+
+import javax.swing.Icon;
+
+/**
+ * Displaying a preview of a sound in the AssetBrowser
+ *
+ * @author rickard
+ */
+public class SoundPreview extends AssetPreviewWidget {
+
+ public SoundPreview(PreviewInteractionListener listener, Icon icon) {
+ super(listener, icon);
+ }
+}
diff --git a/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/TexturePreview.java b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/TexturePreview.java
new file mode 100644
index 000000000..1fa401a28
--- /dev/null
+++ b/jme3-assetbrowser/src/com/jme3/gde/assetbrowser/widgets/TexturePreview.java
@@ -0,0 +1,22 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package com.jme3.gde.assetbrowser.widgets;
+
+import com.jme3.gde.core.dnd.AssetGrabHandler;
+import com.jme3.gde.core.dnd.TextureDataFlavor;
+import javax.swing.Icon;
+
+/**
+ *
+ * @author rickard
+ */
+public class TexturePreview extends AssetPreviewWidget {
+
+ public TexturePreview(PreviewInteractionListener listener, Icon icon) {
+ super(listener, icon);
+ setTransferHandler(new AssetGrabHandler(this, new TextureDataFlavor()));
+ }
+
+}
diff --git a/jme3-behaviortrees/nbproject/genfiles.properties b/jme3-behaviortrees/nbproject/genfiles.properties
index 6ac5d8385..5e64f64fd 100644
--- a/jme3-behaviortrees/nbproject/genfiles.properties
+++ b/jme3-behaviortrees/nbproject/genfiles.properties
@@ -5,4 +5,4 @@ build.xml.stylesheet.CRC32=a56c6a5b@2.72.1
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=8aca329b
nbproject/build-impl.xml.script.CRC32=35831a68
-nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.72.1
+nbproject/build-impl.xml.stylesheet.CRC32=49aa68b0@2.91
diff --git a/jme3-core/nbproject/project.xml b/jme3-core/nbproject/project.xml
index 5415578cd..505f969fe 100644
--- a/jme3-core/nbproject/project.xml
+++ b/jme3-core/nbproject/project.xml
@@ -12,7 +12,7 @@
1
- 3.3.0
+ 3.6.0
@@ -21,20 +21,20 @@
1
- 3.3.0
+ 3.6.0com.jme3.gde.core.updatecenters
- 3.3.0
+ 3.6.0com.jme3.gde.project.testdata1
- 3.3.0
+ 3.6.0
@@ -434,6 +434,7 @@
com.jme3.gde.core.assets.nodescom.jme3.gde.core.codelesscom.jme3.gde.core.completion
+ com.jme3.gde.core.dndcom.jme3.gde.core.editor.iconscom.jme3.gde.core.editor.nodescom.jme3.gde.core.errorreport
diff --git a/jme3-core/src/com/jme3/gde/core/assets/ProjectAssetManager.java b/jme3-core/src/com/jme3/gde/core/assets/ProjectAssetManager.java
index 260d39c33..ee6ec150e 100644
--- a/jme3-core/src/com/jme3/gde/core/assets/ProjectAssetManager.java
+++ b/jme3-core/src/com/jme3/gde/core/assets/ProjectAssetManager.java
@@ -455,64 +455,85 @@ private String stripFirstSlash(String input) {
return input;
}
- @Deprecated
- public AssetManager getManager() {
- return this;
- }
-
public String[] getModels() {
- return filesWithSuffix("j3o");
+ return getModels(true);
}
+ public String[] getModels(boolean includeDependencies) {
+ return filesWithSuffix("j3o", includeDependencies);
+ }
+
public String[] getMaterials() {
- return filesWithSuffix("j3m");
+ return getMaterials(true);
}
+ public String[] getMaterials(boolean includeDependencies) {
+ return filesWithSuffix("j3m", includeDependencies);
+ }
+
public String[] getSounds() {
- ArrayList list = new ArrayList();
- list.addAll(collectFilesWithSuffix("wav"));
- list.addAll(collectFilesWithSuffix("ogg"));
- return list.toArray(new String[list.size()]);
+ return getSounds(true);
}
+ public String[] getSounds(boolean includeDependencies) {
+ ArrayList list = new ArrayList<>();
+ list.addAll(collectFilesWithSuffix("wav", includeDependencies));
+ list.addAll(collectFilesWithSuffix("ogg", includeDependencies));
+ return list.toArray(String[]::new);
+ }
+
public String[] getTextures() {
- ArrayList list = new ArrayList();
- list.addAll(collectFilesWithSuffix("jpg"));
- list.addAll(collectFilesWithSuffix("jpeg"));
- list.addAll(collectFilesWithSuffix("gif"));
- list.addAll(collectFilesWithSuffix("png"));
- list.addAll(collectFilesWithSuffix("dds"));
- list.addAll(collectFilesWithSuffix("pfm"));
- list.addAll(collectFilesWithSuffix("hdr"));
- list.addAll(collectFilesWithSuffix("tga"));
- return list.toArray(new String[list.size()]);
+ return getTextures(true);
+ }
+
+ public String[] getTextures(boolean includeDependencies) {
+ ArrayList list = new ArrayList<>();
+ list.addAll(collectFilesWithSuffix("jpg", includeDependencies));
+ list.addAll(collectFilesWithSuffix("jpeg", includeDependencies));
+ list.addAll(collectFilesWithSuffix("gif", includeDependencies));
+ list.addAll(collectFilesWithSuffix("png", includeDependencies));
+ list.addAll(collectFilesWithSuffix("dds", includeDependencies));
+ list.addAll(collectFilesWithSuffix("pfm", includeDependencies));
+ list.addAll(collectFilesWithSuffix("hdr", includeDependencies));
+ list.addAll(collectFilesWithSuffix("tga", includeDependencies));
+ return list.toArray(String[]::new);
+ }
+
+ public String[] getMatDefs() {
+ return getMatDefs(true);
}
- public String[] getMatDefs() {
- return filesWithSuffix("j3md");
+ public String[] getMatDefs(boolean includeDependencies) {
+ return filesWithSuffix("j3md", includeDependencies);
}
public List getProjectShaderNodeDefs() {
- return collectProjectFilesWithSuffix("j3sn", new LinkedList());
+ return collectProjectFilesWithSuffix("j3sn", new LinkedList<>());
}
public List getDependenciesShaderNodeDefs() {
- return collectDependenciesFilesWithSuffix("j3sn", new LinkedList());
+ return collectDependenciesFilesWithSuffix("j3sn", new LinkedList<>());
}
-
+
public String[] getAssetsWithSuffix(String string) {
- return filesWithSuffix(string);
+ return getAssetsWithSuffix(string, true);
+ }
+
+ public String[] getAssetsWithSuffix(String string, boolean includeDependencies) {
+ return filesWithSuffix(string, includeDependencies);
}
- private String[] filesWithSuffix(String string) {
- List list = collectFilesWithSuffix(string);
- return list.toArray(new String[list.size()]);
+ private String[] filesWithSuffix(String string, boolean includeDependencies) {
+ List list = collectFilesWithSuffix(string, includeDependencies);
+ return list.toArray(String[]::new);
}
- private List collectFilesWithSuffix(String suffix) {
- List list = new LinkedList();
+ private List collectFilesWithSuffix(String suffix, boolean includeDependencies) {
+ List list = new LinkedList<>();
collectProjectFilesWithSuffix(suffix, list);
- collectDependenciesFilesWithSuffix(suffix, list);
+ if(includeDependencies) {
+ collectDependenciesFilesWithSuffix(suffix, list);
+ }
return list;
}
@@ -682,6 +703,10 @@ public void run() {
}
});
}
+
+ public boolean isGradleProject() {
+ return GradleBaseProject.get(project) != null;
+ }
public Mutex mutex() {
return mutex;
diff --git a/jme3-core/src/com/jme3/gde/core/dnd/AssetGrabHandler.java b/jme3-core/src/com/jme3/gde/core/dnd/AssetGrabHandler.java
new file mode 100644
index 000000000..ff20fac84
--- /dev/null
+++ b/jme3-core/src/com/jme3/gde/core/dnd/AssetGrabHandler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.core.dnd;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JComponent;
+import javax.swing.TransferHandler;
+import javax.swing.TransferHandler.TransferSupport;
+
+/**
+ * Based on:
+ * https://stackoverflow.com/questions/23225958/dragging-between-two-components-in-swing
+ *
+ * @author rickard
+ * @param
+ */
+public class AssetGrabHandler extends TransferHandler {
+
+ private static final long serialVersionUID = 1L;
+ private final DataFlavor flavor;
+ private final AssetNameHolder origin;
+
+ public AssetGrabHandler(AssetNameHolder origin, T flavor) {
+ this.origin = origin;
+ this.flavor = flavor;
+ }
+
+ @Override
+ public boolean canImport(TransferSupport info) {
+ return info.isDataFlavorSupported(flavor);
+ }
+
+ @Override
+ public boolean importData(TransferSupport transferSupport) {
+ final Transferable t = transferSupport.getTransferable();
+ try {
+ return t.getTransferData(flavor) != null;
+ } catch (UnsupportedFlavorException | IOException e) {
+ Logger.getLogger(AssetGrabHandler.class.getName()).log(Level.WARNING, "Non-supported flavor {0}", t);
+ }
+ return false;
+ }
+
+ @Override
+ public int getSourceActions(JComponent c) {
+ return TransferHandler.COPY;
+ }
+
+ @Override
+ public Transferable createTransferable(JComponent source) {
+ // We need the values from the list as an object array, otherwise the data flavor won't match in importData
+ return new AssetTransferable(origin, flavor);
+ }
+
+}
diff --git a/jme3-core/src/com/jme3/gde/core/dnd/AssetNameHolder.java b/jme3-core/src/com/jme3/gde/core/dnd/AssetNameHolder.java
new file mode 100644
index 000000000..57efb7345
--- /dev/null
+++ b/jme3-core/src/com/jme3/gde/core/dnd/AssetNameHolder.java
@@ -0,0 +1,16 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package com.jme3.gde.core.dnd;
+
+/**
+ *
+ * @author rickard
+ */
+public interface AssetNameHolder {
+
+ String getAssetName();
+
+ void setAssetName(String name);
+}
diff --git a/jme3-core/src/com/jme3/gde/core/dnd/AssetTransferable.java b/jme3-core/src/com/jme3/gde/core/dnd/AssetTransferable.java
new file mode 100644
index 000000000..ff025514a
--- /dev/null
+++ b/jme3-core/src/com/jme3/gde/core/dnd/AssetTransferable.java
@@ -0,0 +1,60 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package com.jme3.gde.core.dnd;
+
+import com.jme3.gde.core.dnd.AssetNameHolder;
+import com.jme3.gde.core.dnd.StringDataFlavor;
+import com.jme3.gde.core.dnd.TextureDataFlavor;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import javax.swing.JPanel;
+
+/**
+ *
+ * @author rickard
+ * @param
+ */
+public class AssetTransferable implements Transferable {
+
+ private DataFlavor[] flavors;
+ private AssetNameHolder string;
+
+ public AssetTransferable(AssetNameHolder name, T flavor) {
+ this.string = name;
+ flavors = new DataFlavor[]{flavor};
+ }
+
+ @Override
+ public DataFlavor[] getTransferDataFlavors() {
+ return flavors;
+ }
+
+ @Override
+ public boolean isDataFlavorSupported(DataFlavor flavor) {
+ for (DataFlavor mine : getTransferDataFlavors()) {
+ if (mine.equals(flavor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public AssetNameHolder getString() {
+ return string;
+ }
+
+ @Override
+ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
+ if (isDataFlavorSupported(flavor)) {
+ return getString();
+ } else {
+ throw new UnsupportedFlavorException(flavor);
+ }
+
+ }
+
+}
diff --git a/jme3-core/src/com/jme3/gde/core/dnd/MaterialDataFlavor.java b/jme3-core/src/com/jme3/gde/core/dnd/MaterialDataFlavor.java
new file mode 100644
index 000000000..271945499
--- /dev/null
+++ b/jme3-core/src/com/jme3/gde/core/dnd/MaterialDataFlavor.java
@@ -0,0 +1,10 @@
+package com.jme3.gde.core.dnd;
+
+/**
+ *
+ * @author rickard
+ */
+public class MaterialDataFlavor extends StringDataFlavor {
+
+ public final static MaterialDataFlavor instance = new MaterialDataFlavor();
+}
diff --git a/jme3-core/src/com/jme3/gde/core/dnd/MaterialDropTargetListener.java b/jme3-core/src/com/jme3/gde/core/dnd/MaterialDropTargetListener.java
new file mode 100644
index 000000000..58f72c4bc
--- /dev/null
+++ b/jme3-core/src/com/jme3/gde/core/dnd/MaterialDropTargetListener.java
@@ -0,0 +1,80 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package com.jme3.gde.core.dnd;
+
+import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent;
+import com.jme3.math.Vector2f;
+import java.awt.Cursor;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.dnd.DropTargetContext;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+
+/**
+ *
+ * @author rickard
+ */
+public class MaterialDropTargetListener implements DropTargetListener {
+
+ private static final Cursor droppableCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
+ private static final Cursor notDroppableCursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
+
+ private final SceneViewerTopComponent rootPanel;
+
+ public MaterialDropTargetListener(SceneViewerTopComponent rootPanel) {
+ this.rootPanel = rootPanel;
+ }
+
+ @Override
+ public void dragEnter(DropTargetDragEvent dtde) {
+ }
+
+ @Override
+ public void dragOver(DropTargetDragEvent dtde) {
+ if (!this.rootPanel.getCursor().equals(droppableCursor)) {
+ this.rootPanel.setCursor(droppableCursor);
+ }
+ }
+
+ @Override
+ public void dropActionChanged(DropTargetDragEvent dtde) {
+ }
+
+ @Override
+ public void dragExit(DropTargetEvent dte) {
+ this.rootPanel.setCursor(notDroppableCursor);
+ }
+
+ @Override
+ public void drop(DropTargetDropEvent dtde) {
+ this.rootPanel.setCursor(Cursor.getDefaultCursor());
+
+ Object transferableObj = null;
+ try {
+ final DataFlavor dragAndDropPanelFlavor = new MaterialDataFlavor();
+
+ final Transferable transferable = dtde.getTransferable();
+
+ if (transferable.isDataFlavorSupported(dragAndDropPanelFlavor)) {
+ transferableObj = dtde.getTransferable().getTransferData(dragAndDropPanelFlavor);
+ }
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ if (transferableObj == null) {
+ return;
+ }
+ final int dropYLoc = dtde.getLocation().y;
+ final int dropXLoc = dtde.getLocation().x;
+
+ rootPanel.applyMaterial(((AssetNameHolder) transferableObj).getAssetName(), new Vector2f(dropXLoc, dropYLoc));
+ }
+
+}
diff --git a/jme3-core/src/com/jme3/gde/core/dnd/SceneViewerDropTargetListener.java b/jme3-core/src/com/jme3/gde/core/dnd/SceneViewerDropTargetListener.java
new file mode 100644
index 000000000..81dedec2d
--- /dev/null
+++ b/jme3-core/src/com/jme3/gde/core/dnd/SceneViewerDropTargetListener.java
@@ -0,0 +1,94 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package com.jme3.gde.core.dnd;
+
+import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent;
+import com.jme3.math.Vector2f;
+import java.awt.Cursor;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Handles dropping Materials or Spatial from the AssetBrowser to the
+ * SceneViewer
+ * @author rickard
+ */
+public class SceneViewerDropTargetListener implements DropTargetListener {
+
+ private static final Cursor droppableCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
+ private static final Cursor notDroppableCursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
+
+ private final SceneViewerTopComponent rootPanel;
+
+ public SceneViewerDropTargetListener(final SceneViewerTopComponent rootPanel) {
+ this.rootPanel = rootPanel;
+ }
+
+ @Override
+ public void dragEnter(final DropTargetDragEvent dtde) {
+ }
+
+ @Override
+ public void dragOver(final DropTargetDragEvent dtde) {
+ if (!this.rootPanel.getCursor().equals(droppableCursor)) {
+ this.rootPanel.setCursor(droppableCursor);
+ }
+ }
+
+ @Override
+ public void dropActionChanged(DropTargetDragEvent dtde) {
+ }
+
+ @Override
+ public void dragExit(final DropTargetEvent dte) {
+ this.rootPanel.setCursor(notDroppableCursor);
+ }
+
+ @Override
+ public void drop(final DropTargetDropEvent dtde) {
+ this.rootPanel.setCursor(Cursor.getDefaultCursor());
+
+ AssetNameHolder transferableObj = null;
+ Transferable transferable = null;
+ DataFlavor flavor = null;
+
+ try {
+ transferable = dtde.getTransferable();
+ final DataFlavor[] flavors = transferable.getTransferDataFlavors();
+
+ flavor = flavors[0];
+ // What does the Transferable support
+ if (transferable.isDataFlavorSupported(flavor)) {
+ transferableObj = (AssetNameHolder) dtde.getTransferable().getTransferData(flavor);
+ }
+
+ } catch (UnsupportedFlavorException | IOException ex) {
+ Logger.getLogger(SceneViewerDropTargetListener.class.getName()).log(Level.WARNING, "Non-supported flavor {0}", transferable);
+ }
+
+ if (transferable == null || transferableObj == null) {
+ return;
+ }
+
+ final int dropYLoc = dtde.getLocation().y;
+ final int dropXLoc = dtde.getLocation().x;
+
+ if (flavor instanceof SpatialDataFlavor) {
+ rootPanel.addModel(transferableObj.getAssetName(), new Vector2f(dropXLoc, dropYLoc));
+ } else if (flavor instanceof MaterialDataFlavor) {
+ rootPanel.applyMaterial(transferableObj.getAssetName(), new Vector2f(dropXLoc, dropYLoc));
+ }
+
+ }
+
+}
diff --git a/jme3-core/src/com/jme3/gde/core/dnd/SpatialDataFlavor.java b/jme3-core/src/com/jme3/gde/core/dnd/SpatialDataFlavor.java
new file mode 100644
index 000000000..1bb8bf122
--- /dev/null
+++ b/jme3-core/src/com/jme3/gde/core/dnd/SpatialDataFlavor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.core.dnd;
+
+/**
+ *
+ * @author rickard
+ */
+public class SpatialDataFlavor extends StringDataFlavor {
+
+ public final static SpatialDataFlavor instance = new SpatialDataFlavor();
+
+}
diff --git a/jme3-core/src/com/jme3/gde/core/dnd/StringDataFlavor.java b/jme3-core/src/com/jme3/gde/core/dnd/StringDataFlavor.java
new file mode 100644
index 000000000..bd7e79301
--- /dev/null
+++ b/jme3-core/src/com/jme3/gde/core/dnd/StringDataFlavor.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.core.dnd;
+
+import java.awt.datatransfer.DataFlavor;
+
+/**
+ * Based on:
+ * https://stackoverflow.com/questions/23225958/dragging-between-two-components-in-swing
+ *
+ * @author rickard
+ */
+public class StringDataFlavor extends DataFlavor {
+
+ public StringDataFlavor() {
+
+ super("text/plain", null);
+
+ }
+
+}
diff --git a/jme3-core/src/com/jme3/gde/core/dnd/TextureDataFlavor.java b/jme3-core/src/com/jme3/gde/core/dnd/TextureDataFlavor.java
new file mode 100644
index 000000000..576db866f
--- /dev/null
+++ b/jme3-core/src/com/jme3/gde/core/dnd/TextureDataFlavor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.core.dnd;
+
+/**
+ *
+ * @author rickard
+ */
+public class TextureDataFlavor extends StringDataFlavor {
+
+ public final static TextureDataFlavor instance = new TextureDataFlavor();
+
+}
diff --git a/jme3-core/src/com/jme3/gde/core/scene/PreviewRequest.java b/jme3-core/src/com/jme3/gde/core/scene/PreviewRequest.java
index 425820361..c5f734a38 100644
--- a/jme3-core/src/com/jme3/gde/core/scene/PreviewRequest.java
+++ b/jme3-core/src/com/jme3/gde/core/scene/PreviewRequest.java
@@ -37,7 +37,9 @@
import java.awt.image.BufferedImage;
/**
- *
+ * Used to render an image of a scene using SceneApplication. Used by
+ * Material Editor and Shader Editor, for example.
+ *
* @author normenhansen
*/
public class PreviewRequest {
diff --git a/jme3-core/src/com/jme3/gde/core/sceneviewer/SceneViewerTopComponent.java b/jme3-core/src/com/jme3/gde/core/sceneviewer/SceneViewerTopComponent.java
index c5a8111d6..c91d3314a 100644
--- a/jme3-core/src/com/jme3/gde/core/sceneviewer/SceneViewerTopComponent.java
+++ b/jme3-core/src/com/jme3/gde/core/sceneviewer/SceneViewerTopComponent.java
@@ -24,13 +24,26 @@
*/
package com.jme3.gde.core.sceneviewer;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.MaterialKey;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.gde.core.dnd.SceneViewerDropTargetListener;
import com.jme3.gde.core.filters.FilterExplorerTopComponent;
import com.jme3.gde.core.icons.IconList;
import com.jme3.gde.core.scene.SceneApplication;
import com.jme3.gde.core.scene.SceneRequest;
import com.jme3.input.awt.AwtKeyInput;
import com.jme3.input.event.KeyInputEvent;
+import com.jme3.material.Material;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
import java.awt.Component;
+import java.awt.dnd.DropTarget;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseWheelEvent;
@@ -51,7 +64,7 @@
* It also contains the top bar.
*/
@ConvertAsProperties(dtd = "-//com.jme3.gde.core.sceneviewer//SceneViewer//EN",
-autostore = false)
+ autostore = false)
public final class SceneViewerTopComponent extends TopComponent {
private static SceneViewerTopComponent instance;
@@ -101,10 +114,9 @@ public Void call() throws Exception {
String action;
if (e.getWheelRotation() < 0) {
action = "MouseWheel";
- }else if (e.getWheelRotation() > 0) {
+ } else if (e.getWheelRotation() > 0) {
action = "MouseWheel-";
- }
- else {
+ } else {
return null;
}
if (app.getActiveCameraController() != null) {
@@ -154,6 +166,7 @@ public Void call() throws Exception {
});
//}
+ oGLPanel.setDropTarget(new DropTarget(this, new SceneViewerDropTargetListener(this)));
}
/**
@@ -352,7 +365,8 @@ private void enableNormalViewActionPerformed(java.awt.event.ActionEvent evt) {//
* only, i.e. deserialization routines; otherwise you could get a
* non-deserialized instance. To obtain the singleton instance, use
* {@link #findInstance}.
- * @return
+ *
+ * @return
*/
public static synchronized SceneViewerTopComponent getDefault() {
if (instance == null) {
@@ -364,7 +378,8 @@ public static synchronized SceneViewerTopComponent getDefault() {
/**
* Obtain the SceneViewerTopComponent instance. Never call
* {@link #getDefault} directly!
- * @return
+ *
+ * @return
*/
public static synchronized SceneViewerTopComponent findInstance() {
TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
@@ -454,4 +469,45 @@ protected String preferredID() {
public UndoRedo getUndoRedo() {
return Lookup.getDefault().lookup(UndoRedo.class);
}
+
+ public void applyMaterial(String assetName, Vector2f cursorPosition) {
+ AssetManager assetManager = app.getAssetManager();
+ Spatial spatial = pickWorldSpatial(app.getCamera(), new Vector2f(cursorPosition.x, app.getCamera().getHeight() - cursorPosition.y), app.getRootNode());
+ System.out.println("position " + new Vector2f(cursorPosition.x, app.getCamera().getHeight() - cursorPosition.y));
+ if (spatial != null) {
+ Material material = assetManager.loadAsset(new MaterialKey(assetName));
+ spatial.setMaterial(material);
+ }
+ }
+
+ public void addModel(String assetName, Vector2f cursorPosition) {
+ AssetManager assetManager = app.getAssetManager();
+ Spatial spatial = assetManager.loadModel(assetName);
+ CollisionResult cr = pick(app.getCamera(), cursorPosition, app.getRootNode());
+ spatial.setLocalTranslation(cr != null ?
+ cr.getContactPoint() : app.getCamera().getWorldCoordinates(cursorPosition, 100f));
+ app.getRootNode().attachChild(spatial);
+ }
+
+ public static Spatial pickWorldSpatial(Camera cam, Vector2f mouseLoc, Node jmeRootNode) {
+ CollisionResult cr = pick(cam, mouseLoc, jmeRootNode);
+ if (cr != null) {
+ return cr.getGeometry();
+ } else {
+ return null;
+ }
+ }
+
+ private static CollisionResult pick(Camera cam, Vector2f mouseLoc, Node node) {
+ CollisionResults results = new CollisionResults();
+ Ray ray = new Ray();
+ Vector3f pos = cam.getWorldCoordinates(mouseLoc, 0).clone();
+ Vector3f dir = cam.getWorldCoordinates(mouseLoc, 0.125f).clone();
+ dir.subtractLocal(pos).normalizeLocal();
+ ray.setOrigin(pos);
+ ray.setDirection(dir);
+ node.collideWith(ray, results);
+ CollisionResult result = results.getClosestCollision();
+ return result;
+ }
}
diff --git a/jme3-materialeditor/src/com/jme3/gde/materials/MaterialPreviewOpenSupport.java b/jme3-materialeditor/src/com/jme3/gde/materials/MaterialPreviewOpenSupport.java
new file mode 100644
index 000000000..004572f96
--- /dev/null
+++ b/jme3-materialeditor/src/com/jme3/gde/materials/MaterialPreviewOpenSupport.java
@@ -0,0 +1,30 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package com.jme3.gde.materials;
+
+import com.jme3.gde.materials.JMEMaterialDataObject;
+import com.jme3.gde.materials.multiview.MaterialEditorTopComponent;
+import org.openide.cookies.CloseCookie;
+import org.openide.cookies.OpenCookie;
+import org.openide.loaders.OpenSupport;
+import org.openide.windows.CloneableTopComponent;
+
+/**
+ *
+ * @author rickard
+ */
+public class MaterialPreviewOpenSupport extends OpenSupport implements OpenCookie, CloseCookie {
+
+ public MaterialPreviewOpenSupport(JMEMaterialDataObject.Entry entry) {
+ super(entry);
+ }
+
+ @Override
+ protected CloneableTopComponent createCloneableTopComponent() {
+ JMEMaterialDataObject dobj = (JMEMaterialDataObject) entry.getDataObject();
+ MaterialEditorTopComponent tc = new MaterialEditorTopComponent(dobj);
+ return tc;
+ }
+}
diff --git a/jme3-materialeditor/src/com/jme3/gde/materials/dnd/TextureDropTargetListener.java b/jme3-materialeditor/src/com/jme3/gde/materials/dnd/TextureDropTargetListener.java
new file mode 100644
index 000000000..fa6a4ae29
--- /dev/null
+++ b/jme3-materialeditor/src/com/jme3/gde/materials/dnd/TextureDropTargetListener.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * 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 'jMonkeyEngine' 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.jme3.gde.materials.dnd;
+
+import com.jme3.gde.core.dnd.AssetNameHolder;
+import com.jme3.gde.core.dnd.TextureDataFlavor;
+import java.awt.Cursor;
+import java.awt.datatransfer.Transferable;
+import java.awt.dnd.DropTargetContext;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+
+/**
+ *
+ * @author rickard
+ */
+public class TextureDropTargetListener implements DropTargetListener {
+
+ private static final Cursor droppableCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
+ private static final Cursor notDroppableCursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
+
+ private final TextureDropTarget rootPanel;
+
+ public TextureDropTargetListener(TextureDropTarget rootPanel) {
+ this.rootPanel = rootPanel;
+ }
+
+ @Override
+ public void dragEnter(DropTargetDragEvent dtde) {
+ }
+
+ @Override
+ public void dragOver(DropTargetDragEvent dtde) {
+ if (!this.rootPanel.getCursor().equals(droppableCursor)) {
+ this.rootPanel.setCursor(droppableCursor);
+ }
+ }
+
+ @Override
+ public void dropActionChanged(DropTargetDragEvent dtde) {
+ }
+
+ @Override
+ public void dragExit(DropTargetEvent dte) {
+ this.rootPanel.setCursor(notDroppableCursor);
+ }
+
+ @Override
+ public void drop(DropTargetDropEvent dtde) {
+ this.rootPanel.setCursor(Cursor.getDefaultCursor());
+
+ Object transferableObj = null;
+ try {
+
+ final Transferable transferable = dtde.getTransferable();
+
+ if (transferable.isDataFlavorSupported(TextureDataFlavor.instance)) {
+ transferableObj = dtde.getTransferable().getTransferData(TextureDataFlavor.instance);
+ }
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ if (transferableObj == null) {
+ return;
+ }
+
+ rootPanel.setTexture("\"" + ((AssetNameHolder) transferableObj).getAssetName() + "\"");
+ }
+
+ public interface TextureDropTarget {
+
+ void setTexture(String texture);
+
+ void setCursor(Cursor cursor);
+
+ Cursor getCursor();
+ }
+}
diff --git a/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanel.java b/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanel.java
index 5540833ff..d7ace1e31 100644
--- a/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanel.java
+++ b/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanel.java
@@ -15,10 +15,13 @@
import com.jme3.gde.core.properties.TexturePropertyEditor;
import com.jme3.gde.core.properties.preview.TexturePreview;
import com.jme3.gde.materials.MaterialProperty;
+import com.jme3.gde.materials.dnd.TextureDropTargetListener;
+import com.jme3.gde.materials.dnd.TextureDropTargetListener.TextureDropTarget;
import com.jme3.gde.materials.multiview.MaterialEditorTopComponent;
import com.jme3.gde.materials.multiview.widgets.icons.Icons;
import java.awt.Component;
import java.awt.Graphics2D;
+import java.awt.dnd.DropTarget;
import java.awt.image.BufferedImage;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.logging.Level;
@@ -30,7 +33,7 @@
*
* @author normenhansen
*/
-public class TexturePanel extends MaterialPropertyWidget {
+public class TexturePanel extends MaterialPropertyWidget implements TextureDropTarget{
private TexturePropertyEditor editor;
private ProjectAssetManager manager;
@@ -54,6 +57,8 @@ public TexturePanel(ProjectAssetManager manager) {
this.manager = manager;
editor = new TexturePropertyEditor(manager);
initComponents();
+
+ setDropTarget(new DropTarget(this, new TextureDropTargetListener(this)));
}
private void displayPreview() {
@@ -333,4 +338,23 @@ public void cleanUp() {
private javax.swing.JSeparator jSeparator1;
private javax.swing.JLabel texturePreview;
// End of variables declaration//GEN-END:variables
+
+ @Override
+ public void setTexture(String name) {
+ property.setValue("");
+ java.awt.EventQueue.invokeLater(() -> {
+ if(name.startsWith("\"")){
+ textureName = name;
+ } else {
+ textureName = "\"" + name + "\"";
+ }
+ property.setValue(textureName);
+ displayPreview();
+ updateFlipRepeat();
+ java.awt.EventQueue.invokeLater(() -> {
+ fireChanged();
+ });
+ });
+
+ }
}
diff --git a/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanelSquare.java b/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanelSquare.java
index 947d11238..b250d746a 100644
--- a/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanelSquare.java
+++ b/jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanelSquare.java
@@ -37,8 +37,12 @@
import com.jme3.gde.core.properties.TexturePropertyEditor;
import com.jme3.gde.core.properties.preview.TexturePreview;
import com.jme3.gde.materials.MaterialProperty;
+import com.jme3.gde.core.dnd.AssetNameHolder;
+import com.jme3.gde.materials.dnd.TextureDropTargetListener;
+import com.jme3.gde.materials.dnd.TextureDropTargetListener.TextureDropTarget;
import com.jme3.gde.materials.multiview.MaterialEditorTopComponent;
import java.awt.Component;
+import java.awt.dnd.DropTarget;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -49,7 +53,7 @@
* A more compact texture panel designed for the shader node editor.
* @author rickard
*/
-public class TexturePanelSquare extends MaterialPropertyWidget {
+public class TexturePanelSquare extends MaterialPropertyWidget implements TextureDropTarget {
private final TexturePropertyEditor editor;
private final ProjectAssetManager manager;
@@ -99,6 +103,7 @@ public void mouseEntered(MouseEvent e) {
public void mouseExited(MouseEvent e) {
}
});
+ setDropTarget(new DropTarget(this, new TextureDropTargetListener(this)));
}
private void displayPreview() {
@@ -305,4 +310,23 @@ public void cleanUp() {
private javax.swing.JPanel jPanel1;
private javax.swing.JLabel texturePreview;
// End of variables declaration//GEN-END:variables
+
+ @Override
+ public void setTexture(String name) {
+ property.setValue("");
+ java.awt.EventQueue.invokeLater(() -> {
+ if(name.startsWith("\"")){
+ textureName = name;
+ } else {
+ textureName = "\"" + name + "\"";
+ }
+ property.setValue(textureName);
+ displayPreview();
+ updateFlipRepeat();
+ java.awt.EventQueue.invokeLater(() -> {
+ fireChanged();
+ });
+ });
+
+ }
}
diff --git a/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/OpenSceneComposer.java b/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/OpenSceneComposer.java
index 4216a91e0..c361869da 100644
--- a/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/OpenSceneComposer.java
+++ b/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/OpenSceneComposer.java
@@ -27,32 +27,26 @@ public void actionPerformed(ActionEvent ev) {
if (manager == null) {
return;
}
- Runnable call = new Runnable() {
-
- public void run() {
- ProgressHandle progressHandle = ProgressHandle.createHandle("Opening in SceneComposer");
- progressHandle.start();
- try {
- manager.clearCache();
- final Spatial asset = context.loadAsset();
- if (asset != null) {
- java.awt.EventQueue.invokeLater(new Runnable() {
-
- public void run() {
- SceneComposerTopComponent composer = SceneComposerTopComponent.findInstance();
- composer.openScene(asset, context, manager);
- }
- });
- } else {
- Confirmation msg = new NotifyDescriptor.Confirmation(
- "Error opening " + context.getPrimaryFile().getNameExt(),
- NotifyDescriptor.OK_CANCEL_OPTION,
- NotifyDescriptor.ERROR_MESSAGE);
- DialogDisplayer.getDefault().notify(msg);
- }
- } finally {
- progressHandle.finish();
+ Runnable call = () -> {
+ ProgressHandle progressHandle = ProgressHandle.createHandle("Opening in SceneComposer");
+ progressHandle.start();
+ try {
+ manager.clearCache();
+ final Spatial asset = context.loadAsset();
+ if (asset != null) {
+ java.awt.EventQueue.invokeLater(() -> {
+ SceneComposerTopComponent composer = SceneComposerTopComponent.findInstance();
+ composer.openScene(asset, context, manager);
+ });
+ } else {
+ Confirmation msg = new NotifyDescriptor.Confirmation(
+ "Error opening " + context.getPrimaryFile().getNameExt(),
+ NotifyDescriptor.OK_CANCEL_OPTION,
+ NotifyDescriptor.ERROR_MESSAGE);
+ DialogDisplayer.getDefault().notify(msg);
}
+ } finally {
+ progressHandle.finish();
}
};
new Thread(call).start();
diff --git a/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/layer.xml b/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/layer.xml
index 07bba0f5e..6e623878b 100644
--- a/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/layer.xml
+++ b/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/layer.xml
@@ -66,6 +66,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+