Commit 4db94d92 authored by Nico Mack's avatar Nico Mack

Added multiTurn property to ValueWidget

parent cd7fb3bf
......@@ -12,6 +12,7 @@ LOWER_STOP_ANGLE_NODE=lowerStopAngle
MAXIMUM_VALUE_NODE=maximumValue
MINIMUM_VALUE_NODE=minimumValue
MODIFY_VALUE_ON_ROTATION_NODE=modifyValueOnRotation
MULTITURN_NODE=multiTurn
RADIAL_LAYOUT_NODE=radialLayout
REFERENCE_NODE=reference
RELATIVE_NODE=relative
......
......@@ -46,6 +46,7 @@ public class CpsNamespace extends NLS {
public static String MAXIMUM_VALUE_NODE;
public static String MINIMUM_VALUE_NODE;
public static String MODIFY_VALUE_ON_ROTATION_NODE;
public static String MULTITURN_NODE;
public static String RADIAL_LAYOUT_NODE;
public static String REFERENCE_NODE;
......
......@@ -7,6 +7,7 @@ import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.widget.builder.SelectorWidgetBuilder;
import lu.list.itis.dkd.tui.widget.corona.SelectableCorona;
import lu.list.itis.dkd.tui.widget.state.StateManager;
import lu.list.itis.dkd.tui.widget.state.StateManager.Direction;
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
......@@ -115,24 +116,15 @@ public class SelectorWidget extends ValueWidget {
// ---------------------------------------------------------------------------
@Override
protected void updateFromRotation(double angle) {
protected void updateFromRotation(double angle, Direction direction) {
// Empty to disable features implemented in inherited class
}
// ---------------------------------------------------------------------------
// protected float moduloTwoPi(float rawAngle) {
// double angle = rawAngle % TWO_PI;
// if (angle < 0)
// angle += TWO_PI;
// return (float) angle;
//
// }
// ---------------------------------------------------------------------------
protected int getCurrentPosition(float newAngle) {
double range = (constrainted) ? Math.toRadians(upperStopAngle - lowerStopAngle) : TWO_PI;
double range = (constrainted) ? Math.toRadians(upperStopAngle - lowerStopAngle) : AngleUtils.TWO_PI;
double segment = range / numberOfPositions;
int current = 0;
......
......@@ -24,24 +24,22 @@ import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.content.InformationProvider;
import lu.list.itis.dkd.tui.content.InformationReceiver;
import lu.list.itis.dkd.tui.cps.system.VariableBased;
import lu.list.itis.dkd.tui.cps.utility.EquationSystemBundle;
import lu.list.itis.dkd.tui.cps.variable.NumericalVariable;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import lu.list.itis.dkd.tui.utility.AngleUtils;
import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.widget.builder.BaseValueWidgetBuilder;
import lu.list.itis.dkd.tui.widget.corona.ConditionalCorona;
import lu.list.itis.dkd.tui.widget.corona.Corona;
import lu.list.itis.dkd.tui.widget.corona.ValueCorona;
import lu.list.itis.dkd.tui.widget.state.StateManager;
import lu.list.itis.dkd.tui.widget.state.StateManager.Direction;
import lu.list.itis.dkd.tui.widget.tether.InformationFeed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Graphics2D;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
......@@ -76,6 +74,8 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
protected double initialValue;
/** Whether the value held by the widget is to be modified on a rotation of a handle. */
protected boolean modifyValueOnRotation = false;
/** Whether the handle allows multiple revolutions of its handle. */
protected boolean multiTurn = false;
/** Internal flag specifying whether this widgets rotation is constrainted or not! */
protected boolean constrainted;
......@@ -85,8 +85,11 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
*/
protected double dialAngle;
private double stepAngle;
private double lastAngle;
private double steps;
private List<ValueCorona> dispatcher;
// ***************************************************************************
// * Constants *
// ***************************************************************************
......@@ -116,6 +119,7 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
this.upperStopAngle = builder.upperStopAngle;
this.stepSize = builder.stepSize;
this.modifyValueOnRotation = builder.modifyValueOnRotation;
this.multiTurn = builder.multiTurn;
this.variable = builder.variable;
this.buildFromProperties();
......@@ -138,6 +142,7 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
this.upperStopAngle = original.upperStopAngle;
this.stepSize = original.stepSize;
this.modifyValueOnRotation = original.modifyValueOnRotation;
this.multiTurn = original.multiTurn;
this.variable = original.variable;
this.buildFromProperties();
......@@ -155,6 +160,9 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
this.variable = new NumericalVariable(variableName, EquationSystemBundle.EMPTY_STRING, 0d);
}
lastAngle = Double.NaN;
if (!modifyValueOnRotation) {
this.initialValue = getVariable().getValue();
getVariable().setValue(0d);
......@@ -163,11 +171,48 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
}
constrainted = !(Double.isNaN(lowerStopAngle) || Double.isNaN(upperStopAngle));
if (constrainted) {
if (!multiTurn && constrainted) {
dialAngle = lowerStopAngle;
steps = (upperBound - lowerBound + 1) / stepSize;
stepAngle = (upperStopAngle - lowerStopAngle) / steps;
}
this.setupDispatcher();
}
// ---------------------------------------------------------------------------
/**
* iterates over all value coronas assigned to this widget and populates the dispatcher table, using
* variable names as keys and associating depending coronas as values
*/
// ---------------------------------------------------------------------------
private void setupDispatcher() {
this.dispatcher = new ArrayList<>();
List<ValueCorona> displays = this.getCoronas(ValueCorona.class);
for (ValueCorona display : displays) {
Variable<?> displayVariable = display.getVariable();
if ((displayVariable != null) && (displayVariable.equals(this.variable))) {
dispatcher.add(display);
}
}
}
// ---------------------------------------------------------------------------
/**
* Updates the display for the corona(s) associated with the specified variable.
*
* @param variableName
* specifies the name of the variable to update the corresponding displays of.
*/
// ---------------------------------------------------------------------------
private void updateDisplays() {
for (ValueCorona display : dispatcher) {
display.setInformation(this.variable.getValue());
}
}
// ---------------------------------------------------------------------------
......@@ -183,8 +228,8 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
// ---------------------------------------------------------------------------
protected void updateFromRotation(double angle) {
double value;
protected void updateFromRotation(double angle, Direction direction) {
double value = 0;
if (variable == null) {
return;
......@@ -192,47 +237,37 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
if (constrainted) {
value = lowerBound + (stepSize * Math.floor(((angle - lowerStopAngle) / stepAngle)));
if (value < this.lowerBound) {
value = this.lowerBound;
}
if (value > this.upperBound) {
value = this.upperBound;
value = Math.max(value, this.lowerBound);
value = Math.min(value, this.upperBound);
} else if (multiTurn) {
if (!Double.isNaN(lastAngle)) {
value = variable.getValue();
double wrapped = AngleUtils.wrapTwoPi(angle) - AngleUtils.wrapTwoPi(lastAngle);
double degrees = Math.toDegrees(wrapped);
double delta = Math.abs(degrees * stepSize);
switch (direction) {
case CLOCKWISE:
value += delta;
break;
case ANTICLOCKWISE:
value -= delta;
break;
default:
}
}
lastAngle = angle;
} else {
value = lowerBound + ((angle % (TWO_PI)) * (upperBound - lowerBound));
value = lowerBound + (AngleUtils.moduloTwoPi(angle) * (upperBound - lowerBound));
}
variable.setValue(value);
this.updateTethers(value);
this.updateDisplays();
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("{} = {}", variable.getName(), value); //$NON-NLS-1$
}
}
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
private void updateInformationReceivers() {
for (Corona corona : this.getAllCoronas()) {
if (corona instanceof InformationReceiver<?>) {
Type[] types = corona.getClass().getGenericInterfaces();
for (Type type : types) {
if (type instanceof ParameterizedType) {
String typeName = ((ParameterizedType) type).getActualTypeArguments()[0].getTypeName();
if (typeName.equals(String.class.getTypeName())) {
String information = (variable.getValue() != null) ? variable.getValue().toString() : null;
((InformationReceiver<String>) corona).setInformation(information);
} else if (typeName.equals(Double.class.getTypeName())) {
((InformationReceiver<Double>) corona).setInformation(variable.getValue());
}
}
}
}
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
......@@ -272,16 +307,11 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
StateManager manager = objectStates.get(tangibleObject.getObjectId());
// if (!modifyValueOnRotation || !manager.isRotating()) {
// return;
// }
if (constrainted) {
double angle = (lowerStopAngle + Math.PI + tangibleObject.getAngle()) % (TWO_PI);
double angle = AngleUtils.moduloTwoPi(lowerStopAngle + Math.PI + tangibleObject.getAngle());
if ((angle >= lowerStopAngle) && (angle <= upperStopAngle)) {
dialAngle = angle;
}
dialAngle = Math.max(angle, lowerStopAngle);
dialAngle = Math.min(angle, upperStopAngle);
Point position = getPosition(tangibleObject.getObjectId());
......@@ -291,15 +321,17 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
super.actionMove(tangibleObject.constrainedClone(new Point(position.x, position.y, (float) dialAngle, position.getState().getClass())));
}
} else {
dialAngle = (Math.PI + tangibleObject.getAngle()) % (TWO_PI);
dialAngle = AngleUtils.moduloTwoPi(Math.PI + tangibleObject.getAngle());
}
if (!modifyValueOnRotation) {
if (manager.wasRotating()) {
this.updateFromRotation(dialAngle);
Direction direction = manager.getDirectionOfRotation();
this.updateFromRotation(dialAngle, direction);
}
} else if (manager.isRotating()) {
this.updateFromRotation(dialAngle);
Direction direction = manager.getDirectionOfRotation();
this.updateFromRotation(dialAngle, direction);
}
}
......@@ -386,13 +418,4 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
return declared;
}
// ---------------------------------------------------------------------------
@Override
public void paint(Graphics2D canvas) {
this.updateInformationReceivers();
super.paint(canvas);
}
}
\ No newline at end of file
......@@ -37,6 +37,7 @@ import org.jdom2.Element;
/**
* Builder serving as abstract super class for all value widget builders.
*
* @author Nico Mack [nico.mack@list.lu]
* @author Eric Tobias [eric.tobias@list.lu]
* @since 1.3
* @version 1.3.0
......@@ -60,10 +61,23 @@ public abstract class BaseValueWidgetBuilder<B extends BaseValueWidgetBuilder<B>
public NumericalVariable variable;
/** Whether the value held by the widget is to be modified on a rotation of a handle. */
public boolean modifyValueOnRotation = false;
/**
* Whether the handle allows multiple revolutions of its handle. When multiTurn is false the handles
* angle directly controls the value held by the widget. With multiTurn set to true, the value is
* incremented or decremented by stepSize depending on the rotation direction of the handle.
*/
public boolean multiTurn = false;
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Simple no-arg constructor for the {@link BaseValueWidgetBuilder}.
*/
// ---------------------------------------------------------------------------
public BaseValueWidgetBuilder() {
super();
......@@ -74,38 +88,49 @@ public abstract class BaseValueWidgetBuilder<B extends BaseValueWidgetBuilder<B>
stepSize = 1;
}
// ---------------------------------------------------------------------------
/**
* Constructor initializing all fields from an {@link Element} containing as child elements all
* the information on fields to initialize.
* Constructor initializing all fields from an {@link Element} containing as child elements all the
* information on fields to initialize.
*
* @param rootElement
* The element harbouring, on child nodes, the necessary information to initialize all
* fields of the builder.
* The element harbouring, on child nodes, the necessary information to initialize all fields
* of the builder.
* @throws BuildException
* Thrown when any of the fields fail to populate due to an error in reading information
* from the XML file.
*/
// ---------------------------------------------------------------------------
public BaseValueWidgetBuilder(Element rootElement) throws BuildException {
super(rootElement);
this.buildFromBootstrap(rootElement, null, null);
}
// ---------------------------------------------------------------------------
/**
* Constructor initializing all fields from an {@link Element} containing as child elements all
* the information on fields to initialize.
* Constructor initializing all fields from an {@link Element} containing as child elements all the
* information on fields to initialize.
*
* @param rootElement
* The element harbouring, on child nodes, the necessary information to initialize all
* fields of the builder.
* The element harbouring, on child nodes, the necessary information to initialize all fields
* of the builder.
* @throws BuildException
* Thrown when any of the fields fail to populate due to an error in reading information
* from the XML file.
*/
// ---------------------------------------------------------------------------
public BaseValueWidgetBuilder(Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
super(rootElement, context, callback);
this.buildFromBootstrap(rootElement, context, callback);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitives *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
*
* @param rootElement
......@@ -113,9 +138,12 @@ public abstract class BaseValueWidgetBuilder<B extends BaseValueWidgetBuilder<B>
* @param callback
* @throws BuildException
*/
// ---------------------------------------------------------------------------
private void buildFromBootstrap(@Nullable Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
variable = (NumericalVariable) VariableBootstrapper.buildVariableFromElement(rootElement.getChild(CpsNamespace.VARIABLE_NODE), context, callback);
modifyValueOnRotation = BootstrappingUtils.getContentAsBoolean(rootElement, CpsNamespace.MODIFY_VALUE_ON_ROTATION_NODE, BootstrappingUtils.OPTIONAL, true, context);
multiTurn = BootstrappingUtils.getContentAsBoolean(rootElement, CpsNamespace.MULTITURN_NODE, BootstrappingUtils.OPTIONAL, false, context);
if (modifyValueOnRotation) {
lowerBound = BootstrappingUtils.getContentAsDouble(rootElement, Externalization.LOWER_BOUND_NODE, BootstrappingUtils.MANDATORY, null, context);
......@@ -126,74 +154,110 @@ public abstract class BaseValueWidgetBuilder<B extends BaseValueWidgetBuilder<B>
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Method for setting whether the built widget will increase and decrease its stored value with
* rotations.
*
* @param modify
* @param enabled
* Whether the value is to be modified by a rotation of the widget or not.
* @return An instance of this builder for chain-calling.
*/
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public B modifyValueOnRotation(boolean modify) {
this.modifyValueOnRotation = modify;
public B modifyValueOnRotation(boolean enabled) {
this.modifyValueOnRotation = enabled;
return (B) this;
}
// ---------------------------------------------------------------------------
/**
* Method for setting whether the built widget allows multiple revolutions or not.
*
* @param enabled
* When enabled is false the handles' angle directly controls the value held by the widget.
* With multiTurn set to true, the value is incremented or decremented by stepSize depending
* on the rotation direction of the handle.
* @return An instance of this builder for chain-calling.
*/
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public B multiTurn(boolean enabled) {
this.multiTurn = enabled;
return (B) this;
}
// ---------------------------------------------------------------------------
/**
* Method used to set the upper and lower bound of the angle used for modifying the value.
*
* @param _lowerBound
* @param lower
* The lower bound of the angle used to modify the value.
* @param _upperBound
* @param upper
* The upper bound of the angle used to modify the value.
* @return An instance of the builder for chain-calling.
*/
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public B withBounds(double _lowerBound, double _upperBound) {
this.lowerBound = _lowerBound;
this.upperBound = _upperBound;
public B withBounds(double lower, double upper) {
this.lowerBound = Math.min(lower, upper);
this.upperBound = Math.max(lower, upper);
return (B) this;
}
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public B withStops(double _lowerStopAngle, double _upperStopAngle) {
this.lowerStopAngle = _lowerStopAngle;
this.upperStopAngle = _upperStopAngle;
public B withStopAngles(double lower, double upper) {
this.lowerStopAngle = Math.min(lower, upper);
this.upperStopAngle = Math.max(lower, upper);
return (B) this;
}
@SuppressWarnings("unchecked")
public B withStopsInDegrees(double _lowerStopAngle, double _upperStopAngle) {
this.lowerStopAngle = Math.toRadians(_lowerStopAngle);
this.upperStopAngle = Math.toRadians(_upperStopAngle);
// ---------------------------------------------------------------------------
return (B) this;
public B withStopsInDegrees(double lower, double upper) {
return this.withStopAngles(Math.toRadians(lower), Math.toRadians(upper));
}
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public B withStepSize(double _stepSize) {
this.stepSize = _stepSize;
public B withStepSize(double size) {
this.stepSize = size;
return (B) this;
}
// ---------------------------------------------------------------------------
/**
* Method used to set the variable assigned to the widget.
*
* @param _variable
* @param variable
* The variable whose value will be governed by the widget.
* @return An instance of the builder for chain-calling.
*/
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public B withVariable(NumericalVariable _variable) {
this.variable = _variable;
public B withVariable(NumericalVariable variable) {
this.variable = variable;
return (B) this;
}
// ---------------------------------------------------------------------------
/** {@inheritDoc} */
// ---------------------------------------------------------------------------
@Override
public abstract ValueWidget build();
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment