com.pesegato.mermaid.PPropertyPanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Mermaid Show documentation
Show all versions of Mermaid Show documentation
3D GUI widgets for JMonkeyEngine 3
The newest version!
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.pesegato.mermaid;
/*
* $Id$
*
* Copyright (c) 2014 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.
*/
import com.jme3.font.BitmapText;
import com.jme3.scene.Node;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Checkbox;
import com.simsilica.lemur.CheckboxModel;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.DefaultCheckboxModel;
import com.simsilica.lemur.DefaultRangedValueModel;
import com.simsilica.lemur.FillMode;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.RangedValueModel;
import com.simsilica.lemur.Slider;
import com.simsilica.lemur.component.BorderLayout;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.core.GuiControl;
import com.simsilica.lemur.core.VersionedObject;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.style.ElementId;
import com.simsilica.lemur.style.Styles;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
/**
* A panel that supports adding reflected property and
* field access directly to it, presenting a table of
* value editors to the user.
*
* @author Paul Speed
*/
public class PPropertyPanel extends Node
implements VersionedObject {
public static final ElementId ELEMENT_ID = new ElementId("properties");
private BorderLayout layout;
private Container container;
private List properties = new ArrayList();
private AbstractProperty[] propertyArray;
private Object enabledBean;
private Checkbox enabledCheckbox;
//private CheckboxModel enabledModel = new EnabledCheckboxModel();
private PropertyDescriptor enabledProperty;
private long version;
public PPropertyPanel( String style ) {
this(true, ELEMENT_ID, style);
}
public PPropertyPanel( ElementId elementId, String style ) {
this(true, elementId, style);
}
protected PPropertyPanel( boolean applyStyles, ElementId elementId, String style ) {
super();
this.layout = new BorderLayout();
//getControl(GuiControl.class).setLayout(layout);
//this.container = new Container(new SpringGridLayout(Axis.Y, Axis.X, FillMode.Even, FillMode.Last),
// elementId.child("container"), style);
//layout.addChild(container, BorderLayout.Position.Center);
if( applyStyles ) {
Styles styles = GuiGlobals.getInstance().getStyles();
styles.applyStyles(this, elementId, style);
}
}
protected AbstractProperty[] getArray() {
if( propertyArray == null ) {
propertyArray = new AbstractProperty[properties.size()];
propertyArray = properties.toArray(propertyArray);
}
return propertyArray;
}
/**
* Call to force all property editors to refresh their values
* from the source object. This is the only way to get the
* UI to update if the values have been changed outside of the
* UI.
*/
public void refresh() {
for( AbstractProperty p : getArray() ) {
p.refresh();
}
}
/**
* Returns the raw container within which the property
* editors are added.
*/
public Container getContainer() {
return container;
}
protected PropertyDescriptor findProperty( Object bean, String propertyName ) {
try {
BeanInfo info = Introspector.getBeanInfo(bean.getClass());
for( PropertyDescriptor pd : info.getPropertyDescriptors() ) {
if( pd.getName().equals(propertyName) ) {
if( pd.getReadMethod() == null ) {
throw new RuntimeException("Property has no read method:" + propertyName + " on:" + bean.getClass());
}
if( pd.getWriteMethod() == null ) {
throw new RuntimeException("Property has no write method:" + propertyName + " on:" + bean.getClass());
}
return pd;
}
}
throw new RuntimeException("No suche property:" + propertyName + " on:" + bean.getClass());
} catch( IntrospectionException e ) {
throw new RuntimeException("Error introspecting object", e);
}
//return null;
}
protected Field findField( Object bean, String fieldName ) {
try {
return bean.getClass().getField(fieldName);
} catch( NoSuchFieldException ex ) {
throw new RuntimeException("Error inspecting object", ex);
} catch( SecurityException ex ) {
throw new RuntimeException("Error inspecting object", ex);
}
}
protected void addProperty( AbstractProperty p ) {
p.initialize(container);
properties.add(p);
propertyArray = null;
}
/*
protected void resetEnabled() {
if( enabledCheckbox == null ) {
// Perform setup
enabledCheckbox = new Checkbox("", enabledModel, getElementId().child("enabled.checkbox"), getStyle());
enabledModel.setChecked((Boolean)getPropertyValue(enabledProperty, enabledBean));
} else {
setPropertyValue(enabledProperty, enabledBean, enabledModel.isChecked());
}
}
/**
* Sets a boolean property that is presented as a panel-global
* "enabled" state. This is a way of exposing a boolean property
* as a checkbox model that can be used in an outer UI. Otherwise,
* for direct boolean property editing just use the normal addBoolean**()
* methods.
*
public CheckboxModel setEnabledProperty( Object bean, String property ) {
enabledProperty = findProperty(bean, property);
enabledBean = bean;
resetEnabled();
return enabledModel;
}
/**
* Returns the model that is used for enabled/disabled state. Note:
* this state currently has no effect on this component itself but is
* simply a way to expose a single boolean property to the caller as
* a checkbox model.
*
public CheckboxModel getEnabledModel() {
return enabledModel;
}*/
public Property addBooleanProperty( String name, Object bean, String property ) {
BooleanProperty p = new BooleanProperty(name, new PropertyAccess(bean, property));
addProperty(p);
return p;
}
public Property addFloatProperty( String name, Object bean, String property, float min, float max, float step ) {
FloatProperty p = new FloatProperty(name, new PropertyAccess(bean, property), min, max, step);
addProperty(p);
return p;
}
public Property addIntProperty( String name, Object bean, String proprety, int min, int max, int step ) {
IntProperty p = new IntProperty(name, new PropertyAccess(bean, proprety), min, max, step);
addProperty(p);
return p;
}
public Property addEnumProperty( String name, Object bean, String property ) {
EnumProperty p = new EnumProperty(name, new PropertyAccess(bean, property));
addProperty(p);
return p;
}
/*public Property addEnumProperty2( String name, Object bean, String property, float y ) {
posY=y;
EnumProperty2 p = new EnumProperty2(name, new PropertyAccess(bean, property));
addProperty(p);
return p;
}*/
public Property addBooleanField( String name, Object bean, String field ) {
BooleanProperty p = new BooleanProperty(name, new FieldAccess(bean, field));
addProperty(p);
return p;
}
public Property addFloatField( String name, Object bean, String field, float min, float max, float step ) {
FloatProperty p = new FloatProperty(name, new FieldAccess(bean, field), min, max, step);
addProperty(p);
return p;
}
public Property addIntField( String name, Object bean, String field, int min, int max, int step ) {
IntProperty p = new IntProperty(name, new FieldAccess(bean, field), min, max, step);
addProperty(p);
return p;
}
public Property addEnumField( String name, Object bean, String field ) {
EnumProperty p = new EnumProperty(name, new FieldAccess(bean, field));
addProperty(p);
return p;
}
@Override
public void updateLogicalState( float tpf ) {
super.updateLogicalState(tpf);
for( AbstractProperty p : getArray() ) {
p.update();
}
}
protected T getPropertyValue( PropertyDescriptor pd, Object bean ) {
try {
return (T)pd.getReadMethod().invoke(bean);
} catch( IllegalAccessException e ) {
throw new RuntimeException("Error getting value", e);
} catch( InvocationTargetException e ) {
throw new RuntimeException("Error getting value", e);
}
}
protected void setPropertyValue( PropertyDescriptor pd, Object bean, Object value ) {
try {
pd.getWriteMethod().invoke(bean, new Object[] { value });
version++;
} catch( IllegalAccessException e ) {
throw new RuntimeException("Error setting value", e);
} catch( InvocationTargetException e ) {
throw new RuntimeException("Error setting value", e);
}
}
protected T getFieldValue( Field field, Object bean ) {
try {
return (T)field.get(bean);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Error getting value", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Error getting value", e);
}
}
protected void setFieldValue( Field field, Object bean, Object value ) {
try {
field.set(bean, value);
version++;
} catch (IllegalArgumentException e) {
throw new RuntimeException("Error setting value", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Error setting value", e);
}
}
@Override
public long getVersion() {
return version;
}
@Override
public PPropertyPanel getObject() {
return this;
}
@Override
public VersionedReference createReference() {
return new VersionedReference(this);
}
/*
protected class EnabledCheckboxModel extends DefaultCheckboxModel {
@Override
public void setChecked( boolean checked ) {
if( isChecked() == checked ) {
return;
}
super.setChecked(checked);
resetEnabled();
}
}*/
public interface Property {
public void setValue( T value );
public T getValue();
}
protected interface Access {
public void setValue( T value );
public T getValue();
public Class getType();
}
protected class PropertyAccess implements Access {
private Object bean;
private PropertyDescriptor pd;
public PropertyAccess( Object bean, String propertyName ) {
this.bean = bean;
this.pd = findProperty(bean, propertyName);
if( this.pd == null ) {
throw new IllegalArgumentException("Property not found:" + propertyName + " on:" + bean);
}
}
@Override
public void setValue( T value ) {
setPropertyValue(pd, bean, value);
}
@Override
public T getValue() {
return getPropertyValue(pd, bean);
}
@Override
public Class getType() {
return pd.getPropertyType();
}
}
protected class FieldAccess implements Access {
private Object bean;
private Field fd;
public FieldAccess( Object bean, String fieldName ) {
this.bean = bean;
this.fd = findField(bean, fieldName);
if( this.fd == null ) {
throw new IllegalArgumentException("Field not found:" + fieldName + " on:" + bean);
}
}
@Override
public void setValue( T value ) {
setFieldValue(fd, bean, value);
}
@Override
public T getValue() {
return getFieldValue(fd, bean);
}
@Override
public Class getType() {
return fd.getType();
}
}
protected abstract class AbstractProperty implements Property {
private String name;
private Access access;
protected AbstractProperty( String name, Access access ) {
this.name = name;
this.access = access;
}
protected String getDisplayName() {
return name;
}
@Override
public void setValue( T value ) {
access.setValue(value);
}
@Override
public T getValue() {
return access.getValue();
}
public abstract void initialize( Container container );
public abstract void update();
public abstract void refresh();
}
float posY=0;
protected class BooleanProperty extends AbstractProperty {
private PCheckbox check;
private VersionedReference value;
public BooleanProperty( String name, Access access ) {
super(name, access);
}
@Override
public void initialize( Container container ) {
check = new PCheckbox(getDisplayName(),0,posY);
check.setChecked(getValue());
value = check.getModel().createReference();
attachChild(check);
posY-=.35f;
}
@Override
public void update() {
if( value.update() ) {
super.setValue(check.isChecked());
}
}
@Override
public void refresh() {
check.setChecked(getValue());
}
}
protected class FloatProperty extends AbstractProperty {
//private Label label;
private BitmapText valueText;
private PSlider2 slider;
private RangedValueModel model;
private float step;
private VersionedReference value;
private String format = "%14.3f";
public FloatProperty( String name, Access access, float min, float max, float step ) {
super(name, access);
this.model = new DefaultRangedValueModel( min, max, 0 );
this.step = step;
}
@Override
public void initialize( Container container ) {
//label = new Label(getDisplayName() + ":", getElementId().child("float.label"), getStyle());
//label.setTextHAlignment(HAlignment.Right);
slider = new PSlider2( getDisplayName(), model,false);
slider.setDelta(step);
//Float current = getValue();
//model.setValue(current);
refresh();
valueText=slider.getText();
//valueText = new Label("", getElementId().child("value.label"), getStyle());
updateText();
value = slider.getModel().createReference();
//container.addChild(label);
//container.addChild(valueText, 1);
attachChild(slider);
posY-=.35f;
}
protected void updateText() {
valueText.setText(String.format(format, model.getValue()));
}
@Override
public void update() {
if( value.update() ) {
super.setValue((float)model.getValue());
updateText();
}
}
@Override
public void refresh() {
Float current = getValue();
model.setValue(current);
}
}
protected class IntProperty extends AbstractProperty {
//private Label label;
private BitmapText valueText;
private PSlider2 slider;
private RangedValueModel model;
private int step;
private VersionedReference value;
private String format = "%14d";
public IntProperty( String name, Access access, int min, int max, int step ) {
super(name, access);
this.model = new DefaultRangedValueModel( min, max, 0 );
this.step = step;
}
@Override
public void initialize( Container container ) {
//label = new Label(getDisplayName() + ":", getElementId().child("int.label"), getStyle());
//label.setTextHAlignment(HAlignment.Right);
slider = new PSlider2(getDisplayName(),model,false);
slider.setDelta(step);
//Integer current = getValue();
//model.setValue(current);
refresh();
valueText = slider.getText();
updateText();
value = slider.getModel().createReference();
//container.addChild(label);
//container.addChild(valueText, 1);
attachChild(slider);
posY=-.35f;
}
protected void updateText() {
valueText.setText(String.format(format, Math.round(model.getValue())));
}
@Override
public void update() {
if( value.update() ) {
super.setValue((int)model.getValue());
updateText();
}
}
@Override
public void refresh() {
Integer current = getValue();
model.setValue(current);
}
}
protected class EnumProperty extends AbstractProperty {
//private Label label;
private BitmapText valueText;
private PSlider2 slider;
private RangedValueModel model;
private VersionedReference value;
private Class type;
private Enum[] values;
public EnumProperty( String name, Access access ) {
super(name, access);
this.type = access.getType();
this.values = (Enum[])type.getEnumConstants();
this.model = new DefaultRangedValueModel( 0, values.length - 1, 0 );
}
@Override
public void initialize( Container container ) {
slider = new PSlider2(getDisplayName(), model,true);
refresh();
valueText = slider.getText();
updateText();
value = slider.getModel().createReference();
attachChild(slider);
posY-=.35f;
}
protected void updateText() {
int index = (int)model.getValue();
valueText.setText(String.valueOf(values[index]));
}
@Override
public void update() {
if( value.update() ) {
int i = (int)model.getValue();
super.setValue(values[i]);
updateText();
}
}
@Override
public void refresh() {
Enum current = getValue();
int index = current.ordinal();
model.setValue(index);
}
}
/*public class EnumProperty2 extends AbstractProperty {
//private Label label;
private BitmapText valueText;
private PSlider2 slider;
private RangedValueModel model;
private VersionedReference value;
private Class type;
private Enum[] values;
public EnumProperty2( String name, Access access ) {
super(name, access);
this.type = access.getType();
this.values = (Enum[])type.getEnumConstants();
this.model = new DefaultRangedValueModel( 0, values.length - 1, 0 );
}
@Override
public void initialize( Container container ) {
slider = new PSlider2(getDisplayName(), model,true);
refresh();
valueText = slider.getText();
updateText();
value = slider.getModel().createReference();
PDropDown dropDown=new PDropDown(getDisplayName(), model, values);
attachChild(dropDown);
posY-=.7f;
}
protected void updateText() {
int index = (int)model.getValue();
valueText.setText(String.valueOf(values[index]));
}
@Override
public void update() {
if( value.update() ) {
int i = (int)model.getValue();
super.setValue(values[i]);
updateText();
}
}
@Override
public void refresh() {
Enum current = getValue();
int index = current.ordinal();
model.setValue(index);
}
}*/
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy