Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New feature for SDK: AbstractControl + SerializableClass #520

Open
capdevon opened this issue Aug 9, 2023 · 0 comments
Open

New feature for SDK: AbstractControl + SerializableClass #520

capdevon opened this issue Aug 9, 2023 · 0 comments
Labels

Comments

@capdevon
Copy link
Contributor

capdevon commented Aug 9, 2023

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.

  1. 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);
        }
    }

}
  1. 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;
    }

}
  1. TreeBuilder.java
public class TreeBuilder {
    
    public void buildTrees(TreeSettings settings) {
        System.out.println("Generate trees with settings: " + settings);
    }

}
  1. 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

}
  1. 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
@neph1 neph1 added the feature label Aug 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants