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;
  33 
  34 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker.Mode;
  35 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.colorpicker.ColorPicker;
  36 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.gradientpicker.GradientPicker;
  37 
  38 import javafx.beans.property.BooleanProperty;
  39 import javafx.beans.property.ObjectProperty;
  40 import javafx.beans.property.SimpleBooleanProperty;
  41 import javafx.beans.property.SimpleObjectProperty;
  42 import javafx.beans.value.ChangeListener;
  43 import javafx.event.ActionEvent;
  44 import javafx.fxml.FXML;
  45 import javafx.scene.control.ToggleButton;
  46 import javafx.scene.layout.VBox;
  47 import javafx.scene.paint.Color;
  48 import javafx.scene.paint.CycleMethod;
  49 import javafx.scene.paint.ImagePattern;
  50 import javafx.scene.paint.LinearGradient;
  51 import javafx.scene.paint.Paint;
  52 import javafx.scene.paint.RadialGradient;
  53 import javafx.stage.Window;
  54 
  55 /**
  56  * Controller class for the paint editor.
  57  */
  58 public class PaintPickerController {
  59 
  60     @FXML
  61     private VBox root_vbox;
  62     @FXML
  63     private ToggleButton colorToggleButton;
  64     @FXML
  65     private ToggleButton linearToggleButton;
  66     @FXML
  67     private ToggleButton radialToggleButton;
  68 
  69     private ColorPicker colorPicker;
  70     private GradientPicker gradientPicker;
  71     private PaintPicker.Delegate delegate;
  72 
  73     private final ObjectProperty<Paint> paint = new SimpleObjectProperty<>();
  74     private final BooleanProperty liveUpdate = new SimpleBooleanProperty();
  75 
  76     public final static Color DEFAULT_COLOR = Color.BLACK;
  77     public final static LinearGradient DEFAULT_LINEAR
  78             = new LinearGradient(0.0, 0.0, 1.0, 1.0, true, CycleMethod.NO_CYCLE);
  79     public final static RadialGradient DEFAULT_RADIAL
  80             = new RadialGradient(0.0, 0.0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE);
  81 
  82     public final ObjectProperty<Paint> paintProperty() {
  83         return paint;
  84     }
  85 
  86     public final Paint getPaintProperty() {
  87         return paint.get();
  88     }
  89 
  90     public final void setPaintProperty(Paint value) {
  91         paint.setValue(value);
  92     }
  93 
  94     public final BooleanProperty liveUpdateProperty() {
  95         return liveUpdate;
  96     }
  97 
  98     public boolean isLiveUpdate() {
  99         return liveUpdate.get();
 100     }
 101 
 102     public void setLiveUpdate(boolean value) {
 103         liveUpdate.setValue(value);
 104     }
 105 
 106     public ColorPicker getColorPicker() {
 107         return colorPicker;
 108     }
 109 
 110     public GradientPicker getGradientPicker() {
 111         return gradientPicker;
 112     }
 113 
 114     public PaintPicker.Delegate getDelegate() {
 115         return delegate;
 116     }
 117 
 118     public VBox getRoot() {
 119         return root_vbox;
 120     }
 121 
 122     /**
 123      * Simple utility function which clamps the given value to be strictly
 124      * between the min and max values.
 125      * @param min
 126      * @param value
 127      * @param max
 128      * @return
 129      * @treatAsPrivate
 130      */
 131     public static double clamp(double min, double value, double max) {
 132         if (value < min) return min;
 133         if (value > max) return max;
 134         return value;
 135     }
 136 
 137     void setDelegate(PaintPicker.Delegate delegate) {
 138         this.delegate = delegate;
 139     }
 140 
 141     public Mode getMode() {
 142         final Mode mode;
 143         final Paint value = getPaintProperty();
 144         if (value instanceof Color) {
 145             mode = Mode.COLOR;
 146         } else if (value instanceof LinearGradient) {
 147             mode = Mode.LINEAR;
 148         } else {
 149             assert value instanceof RadialGradient;
 150             mode = Mode.RADIAL;
 151         }
 152         return mode;
 153     }
 154 
 155     public void updateUI(Paint value) {
 156         if (value != null) {
 157             setMode(value);
 158             if (value instanceof Color) {
 159                 colorPicker.updateUI((Color) value);
 160             } else if (value instanceof LinearGradient) {
 161                 gradientPicker.updateUI((LinearGradient) value);
 162             } else if (value instanceof RadialGradient) {
 163                 gradientPicker.updateUI((RadialGradient) value);
 164             } else {
 165                 // Case not yet handled
 166                 assert value instanceof ImagePattern;
 167             }
 168         }
 169     }
 170 
 171     @FXML
 172     public void initialize() {
 173         assert root_vbox != null;
 174         assert colorToggleButton != null;
 175         assert linearToggleButton != null;
 176         assert radialToggleButton != null;
 177 
 178         colorPicker = new ColorPicker(this);
 179         gradientPicker = new GradientPicker(this);
 180 
 181         // Default value
 182         setPaintProperty(DEFAULT_COLOR);
 183 
 184         // Resize the window so it matches the selected editor size
 185         root_vbox.heightProperty().addListener((ChangeListener<Number>) (ov, t, t1) -> {
 186             final Window window = root_vbox.getScene().getWindow();
 187             window.sizeToScene();
 188         });
 189         root_vbox.getChildren().add(colorPicker);
 190     }
 191 
 192     void setSingleMode(Mode mode) {
 193         // First disable toggle buttons so we cannot switch from 1 mode to another
 194         colorToggleButton.setManaged(false);
 195         linearToggleButton.setManaged(false);
 196         radialToggleButton.setManaged(false);
 197 
 198         final Paint value;
 199         switch (mode) {
 200             case COLOR:
 201                 value = DEFAULT_COLOR;
 202                 break;
 203             case LINEAR:
 204                 value = DEFAULT_LINEAR;
 205                 break;
 206             case RADIAL:
 207                 value = DEFAULT_RADIAL;
 208                 break;
 209             default:
 210                 value = null;
 211                 assert false;
 212                 break;
 213         }
 214         // Update model
 215         setPaintProperty(value);
 216         // Update UI
 217         updateUI(value);
 218     }
 219 
 220     private void setMode(Paint value) {
 221         if (value instanceof Color) {
 222             // make sure that a second click doesn't deselect the button
 223             if (colorToggleButton.isSelected() == false) {
 224                 colorToggleButton.setSelected(true);
 225             }
 226             root_vbox.getChildren().remove(gradientPicker);
 227         } else if (value instanceof LinearGradient) {
 228             // make sure that a second click doesn't deselect the button
 229             if (linearToggleButton.isSelected() == false) {
 230                 linearToggleButton.setSelected(true);
 231             }
 232             if (!root_vbox.getChildren().contains(gradientPicker)) {
 233                 root_vbox.getChildren().add(gradientPicker);
 234             }
 235         } else if (value instanceof RadialGradient) {
 236             // make sure that a second click doesn't deselect the button
 237             if (radialToggleButton.isSelected() == false) {
 238                 radialToggleButton.setSelected(true);
 239             }
 240             if (!root_vbox.getChildren().contains(gradientPicker)) {
 241                 root_vbox.getChildren().add(gradientPicker);
 242             }
 243         } else {
 244             // Case not yet handled
 245             assert value instanceof ImagePattern;
 246         }
 247     }
 248 
 249     @FXML
 250     void onColorButtonAction(ActionEvent event) {
 251         final ToggleButton tb = (ToggleButton) event.getTarget();
 252         assert tb == colorToggleButton;
 253         final Color value = colorPicker.getValue();
 254         // Update UI
 255         setMode(value);
 256         // Update model
 257         setPaintProperty(value);
 258         event.consume();
 259     }
 260 
 261     @FXML
 262     void onLinearButtonAction(ActionEvent event) {
 263         final ToggleButton tb = (ToggleButton) event.getTarget();
 264         assert tb == linearToggleButton;
 265         final Paint value = gradientPicker.getValue(Mode.LINEAR);
 266         assert value instanceof LinearGradient;
 267         // Update UI
 268         setMode(value);
 269         gradientPicker.setMode(value);
 270         gradientPicker.updatePreview(value);
 271         // Update model
 272         setPaintProperty(value);
 273         event.consume();
 274     }
 275 
 276     @FXML
 277     void onRadialButtonAction(ActionEvent event) {
 278         final ToggleButton tb = (ToggleButton) event.getTarget();
 279         assert tb == radialToggleButton;
 280         final Paint value = gradientPicker.getValue(Mode.RADIAL);
 281         assert value instanceof RadialGradient;
 282         // Update UI
 283         setMode(value);
 284         gradientPicker.setMode(value);
 285         gradientPicker.updatePreview(value);
 286         // Update model
 287         setPaintProperty(value);
 288         event.consume();
 289     }
 290 }