-
Notifications
You must be signed in to change notification settings - Fork 103
Open
Labels
Description
I opened this Issue to keep track of forum discussions
https://hub.jmonkeyengine.org/t/new-feature-for-sdk-abstractcontrol-serializableclass/47005
It requires the creation of a dedicated library, for example jme-sdk-devtools, to be published on the MAVEN repository. In this way, the library can be used by users in their Maven or Gradle projects to add graphical functions to AbstractControls and write their own editors with the SDK.
- Main.java
package com.test.ui.model;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import com.jme3.app.SimpleApplication;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.control.Control;
import com.jme3.system.AppSettings;
import jme.capdevon.ui.annotations.ButtonProperty;
import jme.capdevon.ui.annotations.SerializableClass;
public class Test_SerializableClass extends SimpleApplication {
/**
* @param args
*/
public static void main(String[] args) {
AppSettings settings = new AppSettings(true);
settings.setResolution(640, 480);
Test_SerializableClass app = new Test_SerializableClass();
app.setShowSettings(false);
app.setPauseOnLostFocus(false);
app.setSettings(settings);
app.start();
}
@Override
public void simpleInitApp() {
viewPort.setBackgroundColor(new ColorRGBA(0.5f, 0.6f, 0.7f, 1.0f));
rootNode.addControl(new TreeEditorComponent());
for (int i = 0; i < rootNode.getNumControls(); i++) {
Control control = rootNode.getControl(i);
buildUIPanel(control);
}
}
private JPanel buildUIPanel(Control control) {
JPanel container = new JPanel();
System.out.println(control.getClass());
Field[] fields = FieldUtils.getAllFields(control.getClass());
for (Field field : fields) {
System.out.println("\t" + field);
addUIComponent(control, field, container);
}
List<Method> methods = MethodUtils.getMethodsListWithAnnotation(control.getClass(), ButtonProperty.class);
for (Method method : methods) {
ButtonProperty bp = method.getAnnotation(ButtonProperty.class);
JButton button = new JButton(bp.name());
button.setToolTipText(bp.tooltip());
button.addActionListener(e -> this.enqueue(() -> {
try {
method.invoke(control);
} catch (ReflectiveOperationException ex) {
ex.printStackTrace();
}
}));
container.add(new JLabel(""), "align righ");
container.add(button, "wrap, pushx, growx");
}
return container;
}
private void addUIComponent(Object bean, Field field, JPanel panel) {
String propertyName = field.getName();
Class<?> fieldType = field.getType();
if (fieldType.getAnnotation(SerializableClass.class) != null) {
System.out.println("\t--SerializableClass: " + fieldType);
Object value = getValueOf(propertyName, bean);
Field[] fields = FieldUtils.getAllFields(value.getClass());
for (Field fd : fields) {
System.out.println("\t\t--" + fd);
addUIComponent(value, fd, panel);
}
} else {
JComponent aComponent = ...;
panel.add(new JLabel(propertyName), "align righ");
panel.add(aComponent, "wrap, pushx, growx");
}
}
private static Object getValueOf(String propertyName, Object bean) {
try {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, bean.getClass());
return pd.getReadMethod().invoke(bean);
} catch (ReflectiveOperationException | IntrospectionException e) {
throw new RuntimeException(e);
}
}
}- TreeEditorComponent.java
public class TreeEditorComponent extends AbstractControl {
private TreeSettings buildSettings = new TreeSettings();
@ButtonProperty(name="Generate", tooltip="Procedural Vegetation Placement")
public void generateTrees() {
TreeBuilder builder = new TreeBuilder();
builder.buildTrees(buildSettings);
}
@Override
protected void controlUpdate(float tpf) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
public TreeSettings getBuildSettings() {
return buildSettings;
}
public void setBuildSettings(TreeSettings buildSettings) {
this.buildSettings = buildSettings;
}
}- TreeBuilder.java
public class TreeBuilder {
public void buildTrees(TreeSettings settings) {
System.out.println("Generate trees with settings: " + settings);
}
}- TreeSettings.java
@SerializableClass
public class TreeSettings {
private float cellSize = 1f;
private float cellHeight = 1.5f;
private float minTraversableHeight = 7.5f;
private float maxTraversableStep = 1f;
private float maxTraversableSlope = 48.0f;
// getters & setters
}- SerializableClass.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SerializableClass {
}Here is the output:
class com.test.ui.model.TreeEditorComponent
private com.test.ui.model.TreeSettings com.test.ui.model.TreeEditorComponent.buildSettings
--SerializableClass: class com.test.ui.model.TreeSettings
--private float com.test.ui.model.TreeSettings.cellSize
--private float com.test.ui.model.TreeSettings.cellHeight
--private float com.test.ui.model.TreeSettings.minTraversableHeight
--private float com.test.ui.model.TreeSettings.maxTraversableStep
--private float com.test.ui.model.TreeSettings.maxTraversableSlope
protected boolean com.jme3.scene.control.AbstractControl.enabled
protected com.jme3.scene.Spatial com.jme3.scene.control.AbstractControl.spatial