package eu.hansolo.enzo.vumeter; import com.sun.javafx.css.converters.PaintConverter; import eu.hansolo.enzo.common.Section; import eu.hansolo.enzo.vumeter.skin.VuMeterSkin; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.css.CssMetaData; import javafx.css.Styleable; import javafx.css.StyleableObjectProperty; import javafx.css.StyleableProperty; import javafx.geometry.Orientation; import javafx.scene.control.Control; import javafx.scene.control.Skin; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * User: hansolo * Date: 04.11.13 * Time: 10:29 */ public class VuMeter extends Control { // CSS styleable properties private static final Color DEFAULT_SECTION_FILL_0 = Color.web("#f3622d"); private static final Color DEFAULT_SECTION_FILL_1 = Color.web("#fba71b"); private static final Color DEFAULT_SECTION_FILL_2 = Color.web("#57b757"); private static final Color DEFAULT_SECTION_FILL_3 = Color.web("#f5982b"); private static final Color DEFAULT_SECTION_FILL_4 = Color.web("#41a9c9"); private static final Color DEFAULT_SECTION_FILL_5 = Color.web("#4258c9"); private static final Color DEFAULT_SECTION_FILL_6 = Color.web("#9a42c8"); private static final Color DEFAULT_SECTION_FILL_7 = Color.web("#c84164"); private static final Color DEFAULT_SECTION_FILL_8 = Color.web("#888888"); private static final Color DEFAULT_SECTION_FILL_9 = Color.web("#aaaaaa"); private ObjectProperty<Paint> sectionFill0; private ObjectProperty<Paint> sectionFill1; private ObjectProperty<Paint> sectionFill2; private ObjectProperty<Paint> sectionFill3; private ObjectProperty<Paint> sectionFill4; // Properties private double _value; private DoubleProperty value; private double _minValue; private DoubleProperty minValue; private double _maxValue; private DoubleProperty maxValue; private Orientation _orientation; private ObjectProperty<Orientation> orientation; private int _noOfLeds; private IntegerProperty noOfLeds; private double _ledSpacing; private DoubleProperty ledSpacing; private boolean _peakValueVisible = false; private BooleanProperty peakValueVisible; private ObservableList<Section> sections; private boolean _interactive; private BooleanProperty interactive; // ******************** Constructors ************************************** public VuMeter() { getStyleClass().add("vu-meter"); _value = 0.0; _minValue = 0.0; _maxValue = 1.0; _orientation = Orientation.HORIZONTAL; _noOfLeds = 15; _ledSpacing = 3; sections = FXCollections.observableArrayList(); _interactive = false; } // ******************** Methods ******************************************* public final double getValue() { return null == value ? _value : value.get(); } public final void setValue(final double VALUE) { if (isInteractive()) return; if (null == value) { _value = clamp(getMinValue(), getMaxValue(), VALUE); } else { value.set(clamp(getMinValue(), getMaxValue(), VALUE)); } } public final ReadOnlyDoubleProperty valueProperty() { if (null == value) { value = new SimpleDoubleProperty(this, "value", _value); } return value; } public final double getMinValue() { return null == minValue ? _minValue : minValue.get(); } public final void setMinValue(final double MIN_VALUE) { if (null == minValue) { _minValue = clamp(-Double.MAX_VALUE, getMaxValue(), MIN_VALUE); } else { minValue.set(clamp(-Double.MAX_VALUE, getMaxValue(), MIN_VALUE)); } } public final ReadOnlyDoubleProperty minValueProperty() { if (null == minValue) { minValue = new SimpleDoubleProperty(this, "minValue", _minValue); } return minValue; } public final double getMaxValue() { return null == maxValue ? _maxValue : maxValue.get(); } public final void setMaxValue(final double MAX_VALUE) { if (null == maxValue) { _maxValue = clamp(getMinValue(), Double.MAX_VALUE, MAX_VALUE); } else { maxValue.set(clamp(getMinValue(), Double.MAX_VALUE, MAX_VALUE)); } } public final ReadOnlyDoubleProperty maxValueProperty() { if (null == maxValue) { maxValue = new SimpleDoubleProperty(this, "maxValue", _maxValue); } return maxValue; } public final Orientation getOrientation() { return null == orientation ? _orientation : orientation.get(); } public final void setOrientation(final Orientation ORIENTATION) { if (null == orientation) { _orientation = ORIENTATION; } else { orientation.set(ORIENTATION); } } public final ObjectProperty<Orientation> orientationProperty() { if (null == orientation) { orientation = new SimpleObjectProperty<>(this, "orientation", _orientation); } return orientation; } public final int getNoOfLeds() { return null == noOfLeds ? _noOfLeds : noOfLeds.get(); } public final void setNoOfLeds(final int NO_OF_LEDS) { if (null == noOfLeds) { _noOfLeds = clamp(2, Integer.MAX_VALUE, NO_OF_LEDS); } else { noOfLeds.set(clamp(2, Integer.MAX_VALUE, NO_OF_LEDS)); } } public final ReadOnlyIntegerProperty noOfLedsProperty() { if (null == noOfLeds) { noOfLeds = new SimpleIntegerProperty(this, "noOfLeds", _noOfLeds); } return noOfLeds; } public final double getLedSpacing() { return null == ledSpacing ? _ledSpacing : ledSpacing.get(); } public final void setLedSpacing(final double LED_SPACING) { if (null == ledSpacing) { _ledSpacing = clamp(0, Double.MAX_VALUE, LED_SPACING); } else { ledSpacing.set(clamp(0, Double.MAX_VALUE, LED_SPACING)); } } public final ReadOnlyDoubleProperty ledSpacingProperty() { if (null == ledSpacing) { ledSpacing = new SimpleDoubleProperty(this, "ledSpacing", _ledSpacing); } return ledSpacing; } public final boolean isPeakValueVisible() { return null == peakValueVisible ? _peakValueVisible : peakValueVisible.get(); } public final void setPeakValueVisible(final boolean PEAK_VALUE_VISIBLE) { if (null == peakValueVisible) { _peakValueVisible = PEAK_VALUE_VISIBLE; } else { peakValueVisible.set(PEAK_VALUE_VISIBLE); } } public final BooleanProperty peakValueVisibleProperty() { if (null == peakValueVisible) { peakValueVisible = new SimpleBooleanProperty(this, "peakValueVisible", _peakValueVisible); } return peakValueVisible; } public final ObservableList<Section> getSections() { return sections; } public final void setSections(final List<Section> SECTIONS) { sections.setAll(SECTIONS); } public final void setSections(final Section... SECTIONS) { setSections(Arrays.asList(SECTIONS)); } public final void addSection(final Section SECTION) { if (!sections.contains(SECTION)) sections.add(SECTION); } public final void removeSection(final Section SECTION) { if (sections.contains(SECTION)) sections.remove(SECTION); } public final boolean isInteractive() { return null == interactive ? _interactive : interactive.get(); } public final void setInteractive(final boolean INTERACTIVE) { if (null == interactive) { _interactive = INTERACTIVE; } else { interactive.set(INTERACTIVE); } } public final BooleanProperty interactiveProperty() { if (null == interactive) { interactive = new SimpleBooleanProperty(this, "interactive", _interactive); } return interactive; } // ******************** CSS Stylable Properties *************************** public final Paint getSectionFill0() { return null == sectionFill0 ? DEFAULT_SECTION_FILL_0 : sectionFill0.get(); } public final void setSectionFill0(Paint value) { sectionFill0Property().set(value); } public final ObjectProperty<Paint> sectionFill0Property() { if (null == sectionFill0) { sectionFill0 = new StyleableObjectProperty<Paint>(DEFAULT_SECTION_FILL_0) { @Override public CssMetaData getCssMetaData() { return StyleableProperties.SECTION_FILL_0; } @Override public Object getBean() { return this; } @Override public String getName() { return "sectionFill0"; } }; } return sectionFill0; } public final Paint getSectionFill1() { return null == sectionFill1 ? DEFAULT_SECTION_FILL_1 : sectionFill1.get(); } public final void setSectionFill1(Paint value) { sectionFill1Property().set(value); } public final ObjectProperty<Paint> sectionFill1Property() { if (null == sectionFill1) { sectionFill1 = new StyleableObjectProperty<Paint>(DEFAULT_SECTION_FILL_1) { @Override public CssMetaData getCssMetaData() { return StyleableProperties.SECTION_FILL_1; } @Override public Object getBean() { return this; } @Override public String getName() { return "sectionFill1"; } }; } return sectionFill1; } public final Paint getSectionFill2() { return null == sectionFill2 ? DEFAULT_SECTION_FILL_2 : sectionFill2.get(); } public final void setSectionFill2(Paint value) { sectionFill2Property().set(value); } public final ObjectProperty<Paint> sectionFill2Property() { if (null == sectionFill2) { sectionFill2 = new StyleableObjectProperty<Paint>(DEFAULT_SECTION_FILL_2) { @Override public CssMetaData getCssMetaData() { return StyleableProperties.SECTION_FILL_2; } @Override public Object getBean() { return this; } @Override public String getName() { return "sectionFill2"; } }; } return sectionFill2; } public final Paint getSectionFill3() { return null == sectionFill3 ? DEFAULT_SECTION_FILL_3 : sectionFill3.get(); } public final void setSectionFill3(Paint value) { sectionFill3Property().set(value); } public final ObjectProperty<Paint> sectionFill3Property() { if (null == sectionFill3) { sectionFill3 = new StyleableObjectProperty<Paint>(DEFAULT_SECTION_FILL_3) { @Override public CssMetaData getCssMetaData() { return StyleableProperties.SECTION_FILL_3; } @Override public Object getBean() { return this; } @Override public String getName() { return "sectionFill3"; } }; } return sectionFill3; } public final Paint getSectionFill4() { return null == sectionFill4 ? DEFAULT_SECTION_FILL_4 : sectionFill4.get(); } public final void setSectionFill4(Paint value) { sectionFill4Property().set(value); } public final ObjectProperty<Paint> sectionFill4Property() { if (null == sectionFill4) { sectionFill4 = new StyleableObjectProperty<Paint>(DEFAULT_SECTION_FILL_4) { @Override public CssMetaData getCssMetaData() { return StyleableProperties.SECTION_FILL_4; } @Override public Object getBean() { return this; } @Override public String getName() { return "sectionFill4"; } }; } return sectionFill4; } // ******************** Utility Methods *********************************** 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; } // ******************** Style related ************************************* @Override protected Skin createDefaultSkin() { return new VuMeterSkin(this); } @Override protected String getUserAgentStylesheet() { return getClass().getResource("vumeter.css").toExternalForm(); } private static class StyleableProperties { private static final CssMetaData<VuMeter, Paint> SECTION_FILL_0 = new CssMetaData<VuMeter, Paint>("-section-fill-0", PaintConverter.getInstance(), DEFAULT_SECTION_FILL_0) { @Override public boolean isSettable(VuMeter gauge) { return null == gauge.sectionFill0 || !gauge.sectionFill0.isBound(); } @Override public StyleableProperty<Paint> getStyleableProperty(VuMeter gauge) { return (StyleableProperty) gauge.sectionFill0Property(); } @Override public Paint getInitialValue(VuMeter gauge) { return gauge.getSectionFill0(); } }; private static final CssMetaData<VuMeter, Paint> SECTION_FILL_1 = new CssMetaData<VuMeter, Paint>("-section-fill-1", PaintConverter.getInstance(), DEFAULT_SECTION_FILL_1) { @Override public boolean isSettable(VuMeter gauge) { return null == gauge.sectionFill1 || !gauge.sectionFill1.isBound(); } @Override public StyleableProperty<Paint> getStyleableProperty(VuMeter gauge) { return (StyleableProperty) gauge.sectionFill1Property(); } @Override public Paint getInitialValue(VuMeter gauge) { return gauge.getSectionFill1(); } }; private static final CssMetaData<VuMeter, Paint> SECTION_FILL_2 = new CssMetaData<VuMeter, Paint>("-section-fill-2", PaintConverter.getInstance(), DEFAULT_SECTION_FILL_2) { @Override public boolean isSettable(VuMeter gauge) { return null == gauge.sectionFill2 || !gauge.sectionFill2.isBound(); } @Override public StyleableProperty<Paint> getStyleableProperty(VuMeter gauge) { return (StyleableProperty) gauge.sectionFill2Property(); } @Override public Paint getInitialValue(VuMeter gauge) { return gauge.getSectionFill2(); } }; private static final CssMetaData<VuMeter, Paint> SECTION_FILL_3 = new CssMetaData<VuMeter, Paint>("-section-fill-3", PaintConverter.getInstance(), DEFAULT_SECTION_FILL_3) { @Override public boolean isSettable(VuMeter gauge) { return null == gauge.sectionFill3 || !gauge.sectionFill3.isBound(); } @Override public StyleableProperty<Paint> getStyleableProperty(VuMeter gauge) { return (StyleableProperty) gauge.sectionFill3Property(); } @Override public Paint getInitialValue(VuMeter gauge) { return gauge.getSectionFill3(); } }; private static final CssMetaData<VuMeter, Paint> SECTION_FILL_4 = new CssMetaData<VuMeter, Paint>("-section-fill-4", PaintConverter.getInstance(), DEFAULT_SECTION_FILL_4) { @Override public boolean isSettable(VuMeter gauge) { return null == gauge.sectionFill4 || !gauge.sectionFill4.isBound(); } @Override public StyleableProperty<Paint> getStyleableProperty(VuMeter gauge) { return (StyleableProperty) gauge.sectionFill4Property(); } @Override public Paint getInitialValue(VuMeter gauge) { return gauge.getSectionFill4(); } }; private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; static { final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Control.getClassCssMetaData()); Collections.addAll(styleables, SECTION_FILL_0, SECTION_FILL_1, SECTION_FILL_2, SECTION_FILL_3, SECTION_FILL_4); STYLEABLES = Collections.unmodifiableList(styleables); } } public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { return StyleableProperties.STYLEABLES; } @Override public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() { return getClassCssMetaData(); } }