1 /*
   2  * Copyright (c) 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.effectpicker.editors;
  33 
  34 import com.oracle.javafx.scenebuilder.kit.util.control.effectpicker.EffectPickerController;
  35 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker;
  36 
  37 import java.io.IOException;
  38 import java.io.InputStream;
  39 import java.net.URL;
  40 
  41 import javafx.beans.property.BooleanProperty;
  42 import javafx.beans.property.ObjectProperty;
  43 import javafx.beans.property.SimpleBooleanProperty;
  44 import javafx.beans.property.SimpleObjectProperty;
  45 import javafx.beans.value.ChangeListener;
  46 import javafx.event.ActionEvent;
  47 import javafx.event.Event;
  48 import javafx.fxml.FXML;
  49 import javafx.fxml.FXMLLoader;
  50 import javafx.scene.Parent;
  51 import javafx.scene.control.ChoiceBox;
  52 import javafx.scene.control.Label;
  53 import javafx.scene.effect.Light;
  54 import javafx.scene.effect.Light.Distant;
  55 import javafx.scene.effect.Light.Point;
  56 import javafx.scene.effect.Light.Spot;
  57 import javafx.scene.layout.VBox;
  58 import javafx.scene.paint.Color;
  59 import javafx.scene.paint.Paint;
  60 
  61 public class LightControl extends VBox {
  62 
  63     @FXML
  64     private Label lightLabel;
  65     @FXML
  66     private ChoiceBox<LightsEnum> lightChoiceBox;
  67     @FXML
  68     private VBox lightProperties;
  69 
  70     private final ObjectProperty<Light> value = new SimpleObjectProperty<>();
  71     private final EffectPickerController effectPickerController;
  72 
  73     // Common color property
  74     private PaintPicker colorPicker;
  75 
  76     private final Light defaultDistant;
  77     private final Light defaultPoint;
  78     private final Light defaultSpot;
  79 
  80     private final BooleanProperty liveUpdate = new SimpleBooleanProperty();
  81 
  82     private enum LightsEnum {
  83 
  84         DISTANT,
  85         POINT,
  86         SPOT,
  87         NONE
  88     }
  89 
  90     public LightControl(EffectPickerController effectPickerController,
  91             String label, Light initValue) {
  92         this.effectPickerController = effectPickerController;
  93         this.defaultDistant = new Distant();
  94         this.defaultPoint = new Point();
  95         this.defaultSpot = new Spot();
  96         initialize(label, initValue);
  97     }
  98 
  99     public ObjectProperty<Light> valueProperty() {
 100         return value;
 101     }
 102 
 103     public Light getValue() {
 104         return value.get();
 105     }
 106 
 107     public void setValue(Light v) {
 108         value.set(v);
 109     }
 110 
 111     public final BooleanProperty liveUpdateProperty() {
 112         return liveUpdate;
 113     }
 114 
 115     public boolean isLiveUpdate() {
 116         return liveUpdate.get();
 117     }
 118 
 119     public void setLiveUpdate(boolean value) {
 120         liveUpdate.setValue(value);
 121     }
 122 
 123     private void initialize(String label, Light initValue) {
 124 
 125         final URL layoutURL = EnumControl.class.getResource("LightControl.fxml"); //NOI18N
 126         try (InputStream is = layoutURL.openStream()) {
 127             final FXMLLoader loader = new FXMLLoader();
 128             loader.setController(this);
 129             loader.setRoot(this);
 130             loader.setLocation(layoutURL);
 131             final Parent p = (Parent) loader.load(is);
 132             assert p == this;
 133         } catch (IOException x) {
 134             throw new RuntimeException(x);
 135         }
 136 
 137         lightLabel.setText(label);
 138         lightChoiceBox.getItems().addAll(LightsEnum.values());
 139 
 140         setValue(initValue);
 141         if (initValue == null) {
 142             lightChoiceBox.setValue(LightsEnum.NONE);
 143         } else if (initValue instanceof Distant) {
 144             lightChoiceBox.setValue(LightsEnum.DISTANT);
 145         } else if (initValue instanceof Point) {
 146             lightChoiceBox.setValue(LightsEnum.POINT);
 147         } else {
 148             assert initValue instanceof Spot;
 149             lightChoiceBox.setValue(LightsEnum.SPOT);
 150         }
 151 
 152         lightChoiceBox.getSelectionModel().selectedItemProperty().addListener((ChangeListener<LightsEnum>) (ov, oldValue, newValue) -> {
 153             final Light light;
 154             switch (newValue) {
 155                 case DISTANT:
 156                     light = defaultDistant;
 157                     break;
 158                 case POINT:
 159                     light = defaultPoint;
 160                     break;
 161                 case SPOT:
 162                     light = defaultSpot;
 163                     break;
 164                 case NONE:
 165                     light = null;
 166                     break;
 167                 default:
 168                     light = null;
 169                     assert false;
 170             }
 171             // First update the model with new light value
 172             setValue(light);
 173             // Then update the UI
 174             updateLightPropertiesUI();
 175             // Then notify the controller a change occured
 176             effectPickerController.incrementRevision();
 177         });
 178 
 179         lightChoiceBox.addEventHandler(ActionEvent.ACTION, (Event event) -> {
 180             event.consume();
 181         });
 182 
 183         updateLightPropertiesUI();
 184     }
 185 
 186     private void updateLightPropertiesUI() {
 187         lightProperties.getChildren().clear();
 188 
 189         // Add specific properties
 190         if (getValue() == null) {
 191             // No property to add
 192         } else {
 193             // Add common color property
 194             lightProperties.getChildren().add(getColorPicker());
 195             colorPicker.setPaintProperty(getValue().getColor());
 196 
 197             if (getValue() instanceof Distant) {
 198                 final Distant distant = (Distant) getValue();
 199 
 200                 final SliderControl azimuthEditor = new SliderControl(
 201                         effectPickerController, "azimuth", 0, 360.0, distant.getAzimuth(), 1.0, false); //NOI18N
 202                 distant.azimuthProperty().bind(azimuthEditor.valueProperty());
 203                 lightProperties.getChildren().add(azimuthEditor);
 204 
 205                 final SliderControl elevationEditor = new SliderControl(
 206                         effectPickerController, "elevation", 0, 360.0, distant.getElevation(), 1.0, false); //NOI18N
 207                 distant.elevationProperty().bind(elevationEditor.valueProperty());
 208                 lightProperties.getChildren().add(elevationEditor);
 209 
 210             } else {
 211                 assert getValue() instanceof Point;
 212                 final Point point = (Point) getValue();
 213 
 214                 final DoubleTextFieldControl xEditor = new DoubleTextFieldControl(
 215                         effectPickerController, "x", -10.0, 10.0, point.getX(), 1.0); //NOI18N
 216                 point.xProperty().bind(xEditor.valueProperty());
 217                 lightProperties.getChildren().add(xEditor);
 218 
 219                 final DoubleTextFieldControl yEditor = new DoubleTextFieldControl(
 220                         effectPickerController, "y", -10.0, 10.0, point.getY(), 1.0); //NOI18N
 221                 point.yProperty().bind(yEditor.valueProperty());
 222                 lightProperties.getChildren().add(yEditor);
 223 
 224                 final DoubleTextFieldControl zEditor = new DoubleTextFieldControl(
 225                         effectPickerController, "z", -10.0, 10.0, point.getY(), 1.0); //NOI18N
 226                 point.zProperty().bind(zEditor.valueProperty());
 227                 lightProperties.getChildren().add(zEditor);
 228 
 229                 if (point instanceof Spot) {
 230                     final Spot spot = (Spot) getValue();
 231 
 232                     final DoubleTextFieldControl pointsAtXEditor = new DoubleTextFieldControl(
 233                             effectPickerController, "pointsAtX", -10.0, 10.0, spot.getPointsAtX(), 1.0); //NOI18N
 234                     spot.pointsAtXProperty().bind(pointsAtXEditor.valueProperty());
 235                     lightProperties.getChildren().add(pointsAtXEditor);
 236 
 237                     final DoubleTextFieldControl pointsAtYEditor = new DoubleTextFieldControl(
 238                             effectPickerController, "pointsAtY", -10.0, 10.0, spot.getPointsAtY(), 1.0); //NOI18N
 239                     spot.pointsAtYProperty().bind(pointsAtYEditor.valueProperty());
 240                     lightProperties.getChildren().add(pointsAtYEditor);
 241 
 242                     final DoubleTextFieldControl pointsAtZEditor = new DoubleTextFieldControl(
 243                             effectPickerController, "pointsAtZ", -10.0, 10.0, spot.getPointsAtZ(), 1.0); //NOI18N
 244                     spot.pointsAtZProperty().bind(pointsAtZEditor.valueProperty());
 245                     lightProperties.getChildren().add(pointsAtZEditor);
 246 
 247                     final SliderControl specularExponentEditor = new SliderControl(
 248                             effectPickerController, "specularExponent", 0, 4.0, spot.getSpecularExponent(), 1.0, false); //NOI18N
 249                     spot.specularExponentProperty().bind(specularExponentEditor.valueProperty());
 250                     lightProperties.getChildren().add(specularExponentEditor);
 251 
 252                 }
 253             }
 254         }
 255     }
 256 
 257     private PaintPicker getColorPicker() {
 258         if (colorPicker == null) {
 259             colorPicker = new PaintPicker(effectPickerController.getPaintPickerDelegate(), PaintPicker.Mode.COLOR);
 260             colorPicker.paintProperty().addListener((ChangeListener<Paint>) (ov, oldValue, newValue) -> {
 261                 assert newValue instanceof Color;
 262                 final Color color = (Color) newValue;
 263                 getValue().setColor(color);
 264                 // Then notify the controller a change occured
 265                 effectPickerController.incrementRevision();
 266             });
 267             colorPicker.liveUpdateProperty().addListener((ChangeListener<Boolean>) (ov, oldValue, newValue) -> setLiveUpdate(newValue));
 268         }
 269         return colorPicker;
 270     }
 271 }