/* * Copyright (c) 2013 by Gerrit Grunwald * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package eu.hansolo.enzo.heatcontrol; import com.sun.javafx.css.converters.PaintConverter; import eu.hansolo.enzo.heatcontrol.skin.HeatControlSkin; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.DoublePropertyBase; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.css.CssMetaData; import javafx.css.Styleable; import javafx.css.StyleableObjectProperty; import javafx.css.StyleableProperty; import javafx.scene.control.Control; import javafx.scene.control.Skin; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import javafx.util.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * User: hansolo * Date: 08.11.13 * Time: 16:35 */ public class HeatControl extends Control { private DoubleProperty value; private double oldValue; private DoubleProperty minValue; private DoubleProperty maxValue; private DoubleProperty target; private double _minMeasuredValue; private DoubleProperty minMeasuredValue; private double _maxMeasuredValue; private DoubleProperty maxMeasuredValue; private int _decimals; private IntegerProperty decimals; private String _infoText; private StringProperty infoText; private BooleanProperty targetEnabled; private double _startAngle; private DoubleProperty startAngle; private double _angleRange; private DoubleProperty angleRange; // CSS styleable properties private ObjectProperty<Paint> tickMarkFill; // ******************** Constructors ************************************** public HeatControl() { getStyleClass().add("heat-control"); value = new DoublePropertyBase(0) { @Override protected void invalidated() { set(clamp(getMinValue(), getMaxValue(), get())); } @Override public Object getBean() { return this; } @Override public String getName() { return "value"; } }; minValue = new DoublePropertyBase(0) { @Override protected void invalidated() { if (getValue() < get()) setValue(get()); } @Override public Object getBean() { return this; } @Override public String getName() { return "minValue"; } }; maxValue = new DoublePropertyBase(40) { @Override protected void invalidated() { if (getValue() > get()) setValue(get()); } @Override public Object getBean() { return this; } @Override public String getName() { return "maxValue"; } }; oldValue = 0; target = new DoublePropertyBase(20) { @Override protected void invalidated() { set(clamp(getMinValue(), getMaxValue(), get())); } @Override public Object getBean() { return this; } @Override public String getName() { return "target"; } }; _minMeasuredValue = maxValue.getValue(); _maxMeasuredValue = 0; _decimals = 0; _infoText = ""; targetEnabled = new SimpleBooleanProperty(this, "targetEnabled", false); _startAngle = 325; _angleRange = 290; } // ******************** Methods ******************************************* public final double getValue() { return value.get(); } public final void setValue(final double VALUE) { oldValue = value.get(); value.set(clamp(getMinValue(), getMaxValue(), VALUE)); if (Math.abs(value.get() - target.get()) < 0.5) { setInfoText(""); } } public final DoubleProperty valueProperty() { return value; } public final double getOldValue() { return oldValue; } public final double getMinValue() { return minValue.get(); } public final void setMinValue(final double MIN_VALUE) { minValue.set(clamp(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, MIN_VALUE)); validate(); } public final DoubleProperty minValueProperty() { return minValue; } public final double getMaxValue() { return maxValue.get(); } public final void setMaxValue(final double MAX_VALUE) { maxValue.set(clamp(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, MAX_VALUE)); validate(); } public final DoubleProperty maxValueProperty() { return maxValue; } public final double getTarget() { return target.get(); } public final void setTarget(final double TARGET) { target.set(clamp(getMinValue(), getMaxValue(), TARGET)); } public final DoubleProperty thresholdProperty() { return target; } public final double getMinMeasuredValue() { return null == minMeasuredValue ? _minMeasuredValue : minMeasuredValue.get(); } public final void setMinMeasuredValue(final double MIN_MEASURED_VALUE) { if (null == minMeasuredValue) { _minMeasuredValue = MIN_MEASURED_VALUE; } else { minMeasuredValue.set(MIN_MEASURED_VALUE); } } public final ReadOnlyDoubleProperty minMeasuredValueProperty() { if (null == minMeasuredValue) { minMeasuredValue = new SimpleDoubleProperty(this, "minMeasuredValue", _minMeasuredValue); } return minMeasuredValue; } public final double getMaxMeasuredValue() { return null == maxMeasuredValue ? _maxMeasuredValue : maxMeasuredValue.get(); } public final void setMaxMeasuredValue(final double MAX_MEASURED_VALUE) { if (null == maxMeasuredValue) { _maxMeasuredValue = MAX_MEASURED_VALUE; } else { maxMeasuredValue.set(MAX_MEASURED_VALUE); } } public final ReadOnlyDoubleProperty maxMeasuredValueProperty() { if (null == maxMeasuredValue) { maxMeasuredValue = new SimpleDoubleProperty(this, "maxMeasuredValue", _maxMeasuredValue); } return maxMeasuredValue; } public void resetMinMeasuredValue() { setMinMeasuredValue(getValue()); } public void resetMaxMeasuredValue() { setMaxMeasuredValue(getValue()); } public void resetMinAndMaxMeasuredValue() { setMinMeasuredValue(getValue()); setMaxMeasuredValue(getValue()); } public final int getDecimals() { return null == decimals ? _decimals : decimals.get(); } public final void setDecimals(final int DECIMALS) { if (null == decimals) { _decimals = clamp(0, 3, DECIMALS); } else { decimals.set(clamp(0, 3, DECIMALS)); } } public final IntegerProperty decimalsProperty() { if (null == decimals) { decimals = new SimpleIntegerProperty(this, "decimals", _decimals); } return decimals; } public final String getInfoText() { return null == infoText ? _infoText : infoText.get(); } public final void setInfoText(final String INFO_TEXT) { if (null == infoText) { _infoText = INFO_TEXT; } else { infoText.set(INFO_TEXT); } } public final StringProperty infoTextProperty() { if (null == infoText) { infoText = new SimpleStringProperty(this, "infoText", _infoText); } return infoText; } public final boolean isTargetEnabled() { return targetEnabled.get(); } public final void setTargetEnabled(final boolean TARGET_ENABLED) { targetEnabled.set(TARGET_ENABLED); } public final BooleanProperty targetEnabledProperty() { return targetEnabled; } public double getStartAngle() { return null == startAngle ? _startAngle : startAngle.get(); } public final void setStartAngle(final double START_ANGLE) { if (null == startAngle) { _startAngle = clamp(0, 360, START_ANGLE); } else { startAngle.set(clamp(0, 360, START_ANGLE)); } } public final DoubleProperty startAngleProperty() { if (null == startAngle) { startAngle = new SimpleDoubleProperty(this, "startAngle", _startAngle); } return startAngle; } public final double getAngleRange() { return null == angleRange ? _angleRange : angleRange.get(); } public final void setAngleRange(final double ANGLE_RANGE) { if (null == angleRange) { _angleRange = clamp(0.0, 360.0, ANGLE_RANGE); } else { angleRange.set(clamp(0.0, 360.0, ANGLE_RANGE)); } } public final DoubleProperty angleRangeProperty() { if (null == angleRange) { angleRange = new SimpleDoubleProperty(this, "angleRange", _angleRange); } return angleRange; } private double clamp(final double MIN_VALUE, final double MAX_VALUE, final double VALUE) { if (VALUE < MIN_VALUE) return MIN_VALUE; if (VALUE > MAX_VALUE) return MAX_VALUE; return VALUE; } private int clamp(final int MIN_VALUE, final int MAX_VALUE, final int VALUE) { if (VALUE < MIN_VALUE) return MIN_VALUE; if (VALUE > MAX_VALUE) return MAX_VALUE; return VALUE; } private Duration clamp(final Duration MIN_VALUE, final Duration MAX_VALUE, final Duration VALUE) { if (VALUE.lessThan(MIN_VALUE)) return MIN_VALUE; if (VALUE.greaterThan(MAX_VALUE)) return MAX_VALUE; return VALUE; } private void validate() { if (getTarget() < getMinValue()) setTarget(getMinValue()); if (getTarget() > getMaxValue()) setTarget(getMaxValue()); if (getValue() < getMinValue()) setValue(getMinValue()); if (getValue() > getMaxValue()) setValue(getMaxValue()); } // ******************** CSS Stylable Properties *************************** public final Paint getTickMarkFill() { return null == tickMarkFill ? Color.BLACK : tickMarkFill.get(); } public final void setTickMarkFill(Paint value) { tickMarkFillProperty().set(value); } public final ObjectProperty<Paint> tickMarkFillProperty() { if (null == tickMarkFill) { tickMarkFill = new StyleableObjectProperty<Paint>(Color.BLACK) { @Override public CssMetaData getCssMetaData() { return StyleableProperties.TICK_MARK_FILL; } @Override public Object getBean() { return HeatControl.this; } @Override public String getName() { return "tickMarkFill"; } }; } return tickMarkFill; } // ******************** Style related ************************************* @Override protected Skin createDefaultSkin() { return new HeatControlSkin(this); } @Override protected String getUserAgentStylesheet() { return getClass().getResource("heatcontrol.css").toExternalForm(); } private static class StyleableProperties { private static final CssMetaData<HeatControl, Paint> TICK_MARK_FILL = new CssMetaData<HeatControl, Paint>("-tick-mark-fill", PaintConverter.getInstance(), Color.WHITE) { @Override public boolean isSettable(HeatControl heatControl) { return null == heatControl.tickMarkFill || !heatControl.tickMarkFill.isBound(); } @Override public StyleableProperty<Paint> getStyleableProperty(HeatControl heatControl) { return (StyleableProperty) heatControl.tickMarkFillProperty(); } }; private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; static { final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Control.getClassCssMetaData()); Collections.addAll(styleables, TICK_MARK_FILL); STYLEABLES = Collections.unmodifiableList(styleables); } } public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { return StyleableProperties.STYLEABLES; } @Override public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() { return getClassCssMetaData(); } }