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 }