1 /*
   2  * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.slider;
  33 
  34 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.gradientpicker.GradientPicker;
  35 
  36 import java.io.IOException;
  37 import java.util.logging.Level;
  38 import java.util.logging.Logger;
  39 
  40 import javafx.beans.value.ChangeListener;
  41 import javafx.event.ActionEvent;
  42 import javafx.fxml.FXML;
  43 import javafx.fxml.FXMLLoader;
  44 import javafx.scene.control.Slider;
  45 import javafx.scene.control.Label;
  46 import javafx.scene.control.TextField;
  47 import javafx.scene.input.KeyEvent;
  48 import javafx.scene.layout.GridPane;
  49 
  50 public class SliderControl extends GridPane {
  51 
  52     @FXML
  53     private Slider slider_slider;
  54     @FXML
  55     private Label slider_label;
  56     @FXML
  57     private TextField slider_textfield;
  58     private final int roundingFactor = 100; // 2 decimals rounding
  59 
  60     public SliderControl(String text, double min, double max, double initVal) {
  61         initialize(text, min, max, initVal);
  62     }
  63 
  64     public final Slider getSlider() {
  65         return slider_slider;
  66     }
  67 
  68     /**
  69      * Private
  70      */
  71     private void initialize(String text, double min, double max, double initVal) {
  72 
  73         final FXMLLoader loader = new FXMLLoader();
  74         loader.setLocation(SliderControl.class.getResource("SliderControl.fxml")); //NOI18N
  75         loader.setController(this);
  76         loader.setRoot(this);
  77         try {
  78             loader.load();
  79         } catch (IOException ex) {
  80             Logger.getLogger(GradientPicker.class.getName()).log(Level.SEVERE, null, ex);
  81         }
  82 
  83         assert slider_label != null;
  84         assert slider_slider != null;
  85         assert slider_textfield != null;
  86 
  87         slider_label.setText(text);
  88         slider_slider.setMin(min);
  89         slider_slider.setMax(max);
  90         slider_slider.setValue(initVal);
  91         slider_textfield.setText(Double.toString(initVal));
  92 
  93         slider_slider.valueProperty().addListener((ChangeListener<Number>) (ov, oldValue, newValue) -> {
  94             double rounded = round(newValue.doubleValue(), roundingFactor);
  95             slider_textfield.setText(Double.toString(rounded));
  96         });
  97     }
  98 
  99     @FXML
 100     void sliderAction(ActionEvent event) {
 101         double value = Double.valueOf(slider_textfield.getText());
 102         double rounded = round(value, roundingFactor);
 103         slider_slider.setValue(rounded);
 104         if (rounded > slider_slider.getMax()) {
 105             rounded = slider_slider.getMax();
 106             slider_textfield.setText(Double.toString(rounded));
 107         }
 108         if (rounded < slider_slider.getMin()) {
 109             rounded = slider_slider.getMin();
 110             slider_textfield.setText(Double.toString(rounded));
 111         }
 112         slider_textfield.selectAll();
 113         event.consume();
 114     }
 115 
 116     @FXML
 117     void sliderKeyPressed(KeyEvent e) {
 118         switch (e.getCode()) {
 119             case UP:
 120                 incOrDecFieldValue(e, 0.1);
 121                 break;
 122             case DOWN:
 123                 incOrDecFieldValue(e, -0.1);
 124                 break;
 125             default:
 126                 break;
 127         }
 128     }
 129 
 130     private void incOrDecFieldValue(KeyEvent e, double x) {
 131 
 132         if (!(e.getSource() instanceof TextField)) {
 133             return; // check it's a textField
 134         }        // increment or decrement the value
 135         final TextField tf = (TextField) e.getSource();
 136         final Double newValue = Double.valueOf(tf.getText()) + x;
 137         double rounded = round(newValue, roundingFactor);
 138         slider_slider.setValue(rounded);
 139         tf.setText(Double.toString(newValue));
 140         // Avoid using runLater
 141         // This should be done somewhere else (need to investigate)
 142 //        Platform.runLater(new Runnable() {
 143 //            @Override
 144 //            public void run() {
 145 //                // position caret after new value for easy editing
 146 //                tf.positionCaret(tf.getText().length());
 147 //            }
 148 //        });
 149     }
 150 
 151     private double round(double value, int roundingFactor) {
 152         double doubleRounded = Math.round(value * roundingFactor);
 153         return doubleRounded / roundingFactor;
 154     }
 155 }