All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pesegato.mermaid.PPropertyPanel Maven / Gradle / Ivy

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