com.lyonesgamer.propertygrid.PGProperty Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of JPropertyGrid Show documentation
Show all versions of JPropertyGrid Show documentation
A property editor component for Swing.
The newest version!
package com.lyonesgamer.propertygrid;
import com.lyonesgamer.propertygrid.properties.PropertyCategory;
import net.jodah.typetools.TypeResolver;
import javax.swing.*;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import java.util.*;
import java.awt.Color;
import java.awt.Font;
import java.util.function.Function;
/**
* A single property. Represented as a single row in the grid, corresponds to one value.
*
* @param The type of value this property holds.
* @author Tristan Patch
* @since 1.0
*/
public abstract class PGProperty implements Comparable {
/**
* The grid this property will be shown in.
*/
protected JPropertyGrid parent = null;
/**
* The category this property is under.
*/
protected PropertyCategory category = null;
/**
* The displayed name of the property on the grid.
*/
protected String name = "";
/**
* An object that can be associated with any property. It isn't used by the property,
* but is only provided for the user.
*/
protected Optional clientObject = Optional.empty();
/**
* The cell that displays the name of the property. Shown on the left in e.g. English.
*/
protected PropertyCell nameCell;
/**
* The cell that displays the value of the property. Shown on the right in e.g. English.
*/
protected PropertyCell valueCell;
/**
* The string that appears in the tooltip for this property.
*/
protected String helpString = "";
/**
* Whether this property is disabled. Disabled properties are greyed out and read-only.
*/
protected boolean disabled = false;
/**
* Whether this property is visible. Hidden properties maintain state but don't appear to the user.
*/
protected boolean visible = true;
/**
* A custom to use to validate this property.
*/
protected Function validationFunction = null;
/**
* Creates a new property. Should be called by any subclasses.
* @param name The name of the property.
*/
public PGProperty(String name) {
setName(name);
}
/**
* Returns the type of value that this property holds -- i.e. the `E` it was constructed with.
* @return This property's type.
*/
public Class getType() {
return TypeResolver.resolveRawArgument(PGProperty.class, this.getClass());
}
/**
* Sets the custom data object.
* @param object The client data.
* @param The type of object the client data is.
*/
public void setClientObject(Optional object) {
this.clientObject = object;
}
/**
* Gets the custom data associated with this property.
* @return The client data.
*/
public Optional getClientObject() {
return clientObject;
}
/**
* Defines the function used to validate this property. If not set, uses default (i.e. property-defined) functionality
* to determine if the given value is a valid value for its type (e.g. non-negative in an unsigned integer property).
*
* @param validationFunction An arbitrary function to use to validate input.
*/
public void setValidationFunction(Function validationFunction) {
this.validationFunction = validationFunction;
}
/**
* Called from the property grid when it gets added.
* @param grid The grid this has been added to.
* @param parent The category this property belongs to.
*/
protected void afterAdded(JPropertyGrid grid, PropertyCategory parent) {
this.parent = grid;
this.category = parent;
doAfterAdded();
}
/**
* Called by {@link #afterAdded(JPropertyGrid, com.lyonesgamer.propertygrid.properties.PropertyCategory)}. Used to
* handle custom setup actions.
*/
protected abstract void doAfterAdded();
/**
* Sets the name of this property.
* @param name The name of the property.
*/
public void setName(String name) {
this.name = name;
}
/**
* If this property has been added to a category, returns the name of the category followed by the name of this,
* separated by a slash. If category is null, returns the name of this property.
*
* @return The fully-qualified name of this property.
*/
public String getName() {
if (category == null)
return name;
return category.getName() + "/" + name;
}
/**
* Returns the grid this property belongs to.
* @return This property's parent grid.
*/
public JPropertyGrid getParent() {
return parent;
}
/**
* Show the property if it was previously hidden.
*/
public void show() {
this.visible = true;
}
/**
* Hide the property. Does nothing if already hidden. Hidden properties maintain their state and can be expanded,
* collapsed, change property, etc. but don't appear to the user.
*/
public void hide() {
this.visible = false;
}
/**
* Determines if a value is "valid" for this property. If it returns false, values cannot be changed.
* Internally, this function either calls the custom `validation function`, or defaults to calling
* {@link #onValidateValue(Object)}.
*
* @param value The value to be validated.
* @return If the value is valid.
*/
public boolean validateValue(E value) {
if (validationFunction != null)
return validationFunction.apply(value);
else
return onValidateValue(value);
}
/**
* Determine if value is a "valid" value for this property. Values cannot be changed if this returns false.
*
* @param value The value to be validated.
* @return If the value is valid.
*/
protected abstract boolean onValidateValue(E value);
/**
* Custom handling in case the validation fails. Defaults to no behavior. Should only be overriden if additional handling
* is necessary on top of denying value change.
*
* @param value The value that failed.
*/
public void onValidationFailure(E value) {}
/**
* Sets the value of this property. Must be implemented to change the value. Called by setValue() after
* validating the value.
*
* @param value The value to set to.
*/
protected abstract void onSetValue(E value);
/**
* Sets the value of this property. Calls validateValue() first, then onSetValue() to let subclasses handle it.
*
* @param value The value to be set.
*/
public void setValue(E value) {
if (validateValue(value))
onSetValue(value);
else
onValidationFailure(value);
}
/**
* Sets the value of this property from an arbitrary string. The string is not guaranteed to be parseable to
* a valid value for this property; subclasses must be prepared to handle any string, as well as call
* {@link #validateValue(Object)}. If the value is not valid, it need not set.
*
* @param value The string to attempt to set from.
*/
public abstract void setValueFromString(String value);
/**
* Sets the value of this property from an arbitrary long. The value is not guaranteed to be associated with a valid
* value for this property. If the value is not valid, it need not set.
*
* @param value The long to set this value from.
*/
public abstract void setValueFromLong(long value);
/**
* Getter for the value.
*
* @return The value this property holds.
*/
public abstract E getValue();
/**
* Converts a String to a value, likely using the same (or similar) algorithm as {@link #setValueFromString(String)}.
* @param value The value to convert to a value type of this property.
* @return The value this property holds.
*/
public abstract E stringToValue(String value);
/**
* Converts an int to a value, likely using the same (or similar) algorithm as {@link #setValueFromLong(long)}.
* @param value The value to convert to a value type of this property.
* @return The value this property holds.
*/
public abstract E longToValue(long value);
/**
* Returns the value as a String in a way that makes sense for this property.
*
* @param value The value to convert.
* @return A String representation of this value.
*/
public abstract String valueToString(E value);
/**
* Determines if this property is disabled. Disabled properties are greyed out and are read-only.
* @return If this property is disabled.
*/
public boolean isDisabled() {
return disabled;
}
/**
* Sets this property's disabled status. Disabled properties are greyed out and are read-only.
* @param disabled Whether this property becomes disabled.
*/
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
/**
* Default implementation that can be overriden in children. Compares names alphabetically.
* @param o The object to compare to.
* @return True if the object is a PGProperty and the names are equal (case-sensitive), false otherwise.
*/
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof PGProperty))
return false;
PGProperty category = (PGProperty)o;
return name.equals(category.name);
}
/**
* Default implementation that can be overriden in children. Uses name to generate code.
* @return The hash code of this property.
*/
@Override
public int hashCode() {
return 18*name.hashCode();
}
/**
* Compares this property to the other alphabetically.
*
* The result of this function affects its ordering in the UI. However, it must be ensured that
* `this.compareTo(property)` is opposite of `property.compareTo(this)` or it will result in undefined
* behavior. Also note that the contract of {@link java.lang.Comparable} is that if `this.compareTo(prop) == 0`, then
* `this.equals(prop)` MUST be true, or weird things happen (see the Comparable javadocs for more info).
* It's recommended that custom subclasses don't override this, as the standard ones don't, and there is no guarantee
* that the properties will be of the same type, meaning custom implementations will conflict with existing ones.
*
* @param property The category to compare to.
* @return 0 if the categories have the same name, 1 if this property's name is greater than category's, and -1 if it is less than.
*/
@SuppressWarnings("NullableProblems") //For IntelliJ, seemingly no way to fix the "overrides @NotNull" warning.
@Override
public int compareTo(PGProperty property) {
if (property == null)
throw new NullPointerException("Cannot pass null to compareTo()");
return name.compareTo(property.name);
}
/**
* Gets the renderer used to display the value of the property when not editing.
* @return The property's unique renderer.
*/
public abstract PGCellRenderer getRenderer();
/**
* Gets the renderer used to display a control allowing the user to change the value.
* @return The property's unique editor.
*/
public abstract PGCellEditor getEditor();
/**
* Metadata about an individual cell in a property. Defines properties used by the renderer to influence appearance.
*/
public static class PropertyCell {
/**
* The String representation of either the property's label or value.
*/
public String text;
/**
* An icon used to represent the value graphically. If it is null, no icon is displayed.
*/
public ImageIcon icon;
/**
* The color of the cell.
*/
public Color bgColor;
/**
* The color of text within the cell.
*/
public Color textColor;
/**
* The font to use to render the cell's text.
*/
public Font font;
/**
* Creates a new cell. Font is assumed to be system default.
*
* @param text The text to display inside the cell.
* @param icon An icon to display inside the cell. If null, no icon is shown.
* @param bgColor The color of the cell.
* @param textColor The color of text within the cell.
*/
public PropertyCell(String text, ImageIcon icon, Color bgColor, Color textColor) {
this.text = text;
this.icon = icon;
this.bgColor = bgColor;
this.textColor = textColor;
this.font = new JLabel().getFont(); //Perhaps a bit hacky, but it gets the default font in this LaF.
}
}
/**
* A Swing control to allow for editing of the value of a given property.
*/
public interface PGCellEditor extends TableCellEditor {
}
/**
* A Swing control the render a given cell in the grid. Overriden by the editor if currently editing.
*/
public interface PGCellRenderer extends TableCellRenderer {
}
}