Plugin-Based JME DevKit

I see 2 solutions:

  • The first one is to use Uses of Class java.text.NumberFormat (Java Platform SE 8 ) as stated in the Float valueOf javadoc to use the local setting to parse string number.

  • The second is to set the Local setting for everyone to US or English via Locale.setDefault(Locale.ENGLISH); somewhere in the initilization.

I tried the second which work and represent my number with dot. But it is les flexible than the first solution.

So if I understand you correct, it should be possible to set this line:

https://github.com/jayfella/jme-swing-devkit/blob/master/src/main/java/com/jayfella/devkit/forms/Configuration.java#L233

to

NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());

Does that solve your issue?

Edit: Hang on. I don’t even use that formatter. This is probably why the problem is there in the first place.

It’s this line here:

https://github.com/jayfella/jme-swing-devkit/blob/master/src/main/java/com/jayfella/devkit/swing/NumberFormatters.java#L10

Hi guys, maybe this example can help you:
from How to Use Formatted Text Fields (The Java™ Tutorials > Creating a GUI With JFC/Swing > Using Swing Components)

"…
Note that although the JFormattedTextField class inherits the setText method from the JTextField class, you do not usually call the setText method on a formatted text field. If you do, the field’s display changes accordingly but the value is not updated (unless the field’s formatter updates it constantly).

To obtain a formatted text field’s current value, use the getValue method. If necessary, you can ensure that the value reflects the text by calling the commitEdit method before getValue . Because the getValue method returns an Object , you need to cast it to the type used for your field’s value. …"

	private JComponent addVector3fProperty(Object bean, String property) {
		PropertyAccess<Vector3f> beanPropAccess = new PropertyAccess<>(bean, property);
		Vector3f vec = beanPropAccess.getValue();
		
		FloatFormatFactory floatFormatFactory = new FloatFormatFactory(-1000, 1000);
		JFormattedTextField xTextField = new JFormattedTextField(floatFormatFactory);
	    JFormattedTextField yTextField = new JFormattedTextField(floatFormatFactory);
	    JFormattedTextField zTextField = new JFormattedTextField(floatFormatFactory);
	    
        xTextField.setColumns(5);
        yTextField.setColumns(5);
		zTextField.setColumns(5);
        
        xTextField.setValue(vec != null ? vec.x : 0f);
        yTextField.setValue(vec != null ? vec.y : 0f);
        zTextField.setValue(vec != null ? vec.z : 0f);
        
        PropertyChangeListener listener = new PropertyChangeListener() {
			@Override
			public void propertyChange(PropertyChangeEvent e) {
				// TODO Auto-generated method stub
	        	float x = ((Number) xTextField.getValue()).floatValue();
	            float y = ((Number) yTextField.getValue()).floatValue();
	            float z = ((Number) zTextField.getValue()).floatValue();
	            
	            simpleApp.enqueue(() -> {
	            	beanPropAccess.setValue(new Vector3f(x, y, z));
	            	System.out.println("new Vector3f applied: " + new Vector3f(x, y, z));
	            });
			};
        };
        
        xTextField.addPropertyChangeListener("value", listener);
        yTextField.addPropertyChangeListener("value", listener);
        zTextField.addPropertyChangeListener("value", listener);
        
        JPanel panel = new JPanel();
        panel.add(new JLabel("x"));
        panel.add(xTextField);
        panel.add(new JLabel("y"));
        panel.add(yTextField);
        panel.add(new JLabel("z"));
        panel.add(zTextField);
        return panel;
	}
1 Like

Yes, I use that tool and it work like a charm. I submit a pull request on github to propose the fix.

Probably there is also another bug at file [src/main/java/com/jayfella/devkit/forms/Configuration.java]
at this lines 182… setFrustumFar is repetead twice on frustumNearTextField.getValue() and frustumFarTextField.getValue()

devKitConfig.getCameraConfig().setFieldOfView(((Number)fieldOfViewTextField.getValue()).floatValue()); devKitConfig.getCameraConfig().setFrustumFar(((Number)frustumNearTextField.getValue()).floatValue()); devKitConfig.getCameraConfig().setFrustumFar(((Number)frustumFarTextField.getValue()).floatValue());
1 Like

Good catch. Much appreciated.

1 Like

Another suggestion, I like your works on jme-swing-devkit. Instead of JSlider for FloatPoperty, what do you think about JSpinner ?

public static JComponent addFloatProperty(Object bean, String property, float min, float max, float step) {
	PropertyAccess<Float> beanPropAccess = new PropertyAccess<>(bean, property);
	float value = beanPropAccess.getValue();
	
	SpinnerNumberModel numberModel = new SpinnerNumberModel(value, min, max, step);
    JSpinner spinner = new JSpinner(numberModel);
	
    ChangeListener transformChangeListener = (ChangeEvent e) -> {
    	float newVal = numberModel.getNumber().floatValue();
        // invoke the method on the JME thread
        simpleApp.enqueue(() -> {
        	beanPropAccess.setValue(newVal);
        	System.out.println("addFloatProperty(spinner) applied: " + newVal);
        });
    };
    
    spinner.addChangeListener(transformChangeListener);
	
	return spinner;
}

Right, I added a fix in the pull request.

A JTextArea for mapping String.class if it is necessary a long description can be util ? :slight_smile:

here’s the code

public static JComponent addTextAreaProperty(Object bean, Field field, int row, int column) {

	boolean isPublic = Modifier.isPublic(field.getModifiers());
	String property = field.getName();
	
	Access<String> access = (isPublic) ? new FieldAccess<>(bean, property) : new PropertyAccess<>(bean, property);
	String text = access.getValue();

	JTextArea textArea = new JTextArea(row, column);
	textArea.setText(text);
	textArea.setLineWrap(true);
	textArea.setWrapStyleWord(true);
	
	Action wrapper = new AbstractAction() {
		@Override
		public void actionPerformed(ActionEvent event) {
			String newText = textArea.getText();
			// invoke the method on the JME thread
			simpleApp.enqueue(() -> {
				access.setValue(newText);
				System.out.println("addTextAreaProperty applied: " + newText);
			});
		}
	};
    
	final String COMMIT_ACTION = "commit";
	final KeyStroke keyStroke = KeyStroke.getKeyStroke("ENTER");
	
    textArea.getInputMap().put(keyStroke, COMMIT_ACTION);
    textArea.getActionMap().put(COMMIT_ACTION, wrapper);
	
	JScrollPane scrollPane = new JScrollPane(textArea);
	scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
	scrollPane.setPreferredSize(new Dimension(250, 250));

	return scrollPane;
}

For floats - actually what I want to make is a JSlider with a text field so you can manually input the value and see the value. I think it makes a lot more sense that way.

For the textArea - I haven’t needed one yet but the user can add their own components if they make plugins / appstates. This one may be useful.

Update pushed. 1.0.16 is the latest. Again - the plugin should pick up the new version automatically.

Thank you both @capdevon and @NyouB for your help and contribution :heart:

3 Likes

Hi guys,
I notice that in the class
[https://github.com/jayfella/jme-swing-devkit/blob/master/src/main/java/com/jayfella/devkit/forms/RunAppStateWindow.java]
line 256, this instruction

Reflections reflections = new Reflections(appState.getClass().getName(), new MethodAnnotationsScanner());
reflections.getMethodsAnnotatedWith(annotationCls);

goes in error if the the appState class have @DevKitAppState annotation, but don’t have any method annotated with one of the properties supported by the devkit (@FloatProperty, …).

org.reflections.ReflectionsException: Scanner MethodAnnotationsScanner was not configured
	at org.reflections.Store.get(Store.java:39)
	at org.reflections.Store.get(Store.java:61)
	at org.reflections.Store.get(Store.java:46)
	at org.reflections.Reflections.getMethodsAnnotatedWith(Reflections.java:478)

Maybe it can be better using this code, which do the same things but don’t goes on error:

for (Class<? extends Annotation> annotation : annotations) {
	annotatedMethods = MethodUtils.getMethodsListWithAnnotation(appState.getClass(), annotation);
	getterValues = getMethodValues(annotatedMethods, appState);
	allAnnotatedMethods.put(annotation, getterValues);
}