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.gradientpicker; 33 34 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker.Mode; 35 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPickerController; 36 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.rotator.RotatorControl; 37 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.slider.SliderControl; 38 39 import javafx.fxml.FXML; 40 import javafx.fxml.FXMLLoader; 41 import javafx.scene.control.Label; 42 import javafx.scene.control.Slider; 43 import javafx.scene.input.MouseEvent; 44 import javafx.scene.layout.Pane; 45 import javafx.scene.layout.StackPane; 46 import javafx.scene.layout.VBox; 47 import javafx.scene.paint.*; 48 import javafx.scene.shape.Rectangle; 49 50 import java.io.IOException; 51 import java.util.ArrayList; 52 import java.util.List; 53 import java.util.logging.Level; 54 import java.util.logging.Logger; 55 56 import javafx.beans.value.ChangeListener; 57 import javafx.collections.FXCollections; 58 import javafx.event.ActionEvent; 59 import javafx.event.Event; 60 import javafx.scene.control.CheckBox; 61 import javafx.scene.control.ChoiceBox; 62 63 /** 64 * Controller class for the gradient part of the paint editor. 65 */ 66 public class GradientPicker extends VBox { 67 68 @FXML 69 private Pane track_pane; 70 @FXML 71 private Label stop_label; 72 @FXML 73 private Rectangle preview_rect; 74 @FXML 75 private StackPane slider_container; 76 @FXML 77 private VBox radial_container; 78 @FXML 79 private VBox shared_container; 80 @FXML 81 private Slider startX_slider; 82 @FXML 83 private Slider endX_slider; 84 @FXML 85 private Slider startY_slider; 86 @FXML 87 private Slider endY_slider; 88 @FXML 89 private Slider centerX_slider; 90 @FXML 91 private Slider centerY_slider; 92 @FXML 93 private CheckBox proportional_checkbox; 94 @FXML 95 private ChoiceBox<CycleMethod> cycleMethod_choicebox; 96 97 private final PaintPickerController paintPicker; 98 99 private final RotatorControl focusAngleRotator 100 = new RotatorControl("focusAngle"); //NOI18N 101 private final SliderControl focusDistanceSlider 102 = new SliderControl("focusDistance", -1.0, 1.0, 0.0); //NOI18N 103 private final SliderControl radiusSlider 104 = new SliderControl("radius", 0.0, 1.0, 0.5); //NOI18N 105 private final List<GradientPickerStop> gradientPickerStops = new ArrayList<>(); 106 private final int maxStops = 12; // the numbers of stops supported in platform 107 108 public GradientPicker(PaintPickerController pe) { 109 paintPicker = pe; 110 initialize(); 111 } 112 113 public final PaintPickerController getPaintPickerController() { 114 return paintPicker; 115 } 116 117 public Paint getValue(Mode mode) { 118 final Paint paint; 119 switch (mode) { 120 case LINEAR: 121 double startX = startX_slider.getValue(); 122 double startY = startY_slider.getValue(); 123 double endX = endX_slider.getValue(); 124 double endY = endY_slider.getValue(); 125 boolean linear_proportional = proportional_checkbox.isSelected(); 126 final CycleMethod linear_cycleMethod = cycleMethod_choicebox.getValue(); 127 paint = new LinearGradient(startX, startY, endX, endY, 128 linear_proportional, linear_cycleMethod, getStops()); 129 break; 130 case RADIAL: 131 double focusAngle = focusAngleRotator.getRotationProperty(); 132 double focusDistance = focusDistanceSlider.getSlider().getValue(); 133 double centerX = centerX_slider.getValue(); 134 double centerY = centerY_slider.getValue(); 135 double radius = radiusSlider.getSlider().getValue(); 136 boolean radial_proportional = proportional_checkbox.isSelected(); 137 final CycleMethod radial_cycleMethod = cycleMethod_choicebox.getValue(); 138 paint = new RadialGradient(focusAngle, focusDistance, centerX, centerY, radius, 139 radial_proportional, radial_cycleMethod, getStops()); 140 break; 141 default: 142 assert false; 143 paint = null; 144 break; 145 } 146 return paint; 147 } 148 149 public boolean isGradientStopsEmpty() { 150 return gradientPickerStops.isEmpty(); 151 } 152 153 public List<GradientPickerStop> getGradientStops() { 154 return gradientPickerStops; 155 } 156 157 public GradientPickerStop getSelectedStop() { 158 GradientPickerStop selectedThumb = null; 159 for (GradientPickerStop gradientStopThumb : gradientPickerStops) { 160 if (gradientStopThumb.isSelected()) { 161 selectedThumb = gradientStopThumb; 162 } 163 } 164 return selectedThumb; 165 } 166 167 public void updateUI(Paint value) { 168 assert value instanceof LinearGradient || value instanceof RadialGradient; 169 if (value instanceof LinearGradient) { 170 final LinearGradient linear = (LinearGradient) value; 171 startX_slider.setValue(linear.getStartX()); 172 startY_slider.setValue(linear.getStartY()); 173 endX_slider.setValue(linear.getEndX()); 174 endY_slider.setValue(linear.getEndY()); 175 proportional_checkbox.setSelected(linear.isProportional()); 176 cycleMethod_choicebox.setValue(linear.getCycleMethod()); 177 // clear first 178 removeAllStops(); 179 for (Stop stop : linear.getStops()) { 180 // Update stops 181 addStop(0.0, 1.0, stop.getOffset(), stop.getColor()); 182 } 183 184 } else { 185 assert value instanceof RadialGradient; 186 final RadialGradient radial = (RadialGradient) value; 187 centerX_slider.setValue(radial.getCenterX()); 188 centerY_slider.setValue(radial.getCenterY()); 189 focusAngleRotator.setRotationProperty(radial.getFocusAngle()); 190 focusDistanceSlider.getSlider().setValue(radial.getFocusDistance()); 191 radiusSlider.getSlider().setValue(radial.getRadius()); 192 proportional_checkbox.setSelected(radial.isProportional()); 193 cycleMethod_choicebox.setValue(radial.getCycleMethod()); 194 // clear first 195 removeAllStops(); 196 for (Stop stop : radial.getStops()) { 197 // Update stops 198 addStop(0.0, 1.0, stop.getOffset(), stop.getColor()); 199 } 200 } 201 setMode(value); 202 updatePreview(value); 203 } 204 205 public void updatePreview(Paint value) { 206 preview_rect.setFill(value); 207 } 208 209 public void setMode(Paint value) { 210 final Mode mode; 211 if (value instanceof LinearGradient) { 212 mode = Mode.LINEAR; 213 } else { 214 assert value instanceof RadialGradient; 215 mode = Mode.RADIAL; 216 } 217 startX_slider.setVisible(mode == Mode.LINEAR); 218 startY_slider.setVisible(mode == Mode.LINEAR); 219 endX_slider.setVisible(mode == Mode.LINEAR); 220 endY_slider.setVisible(mode == Mode.LINEAR); 221 centerX_slider.setVisible(mode == Mode.RADIAL); 222 centerY_slider.setVisible(mode == Mode.RADIAL); 223 radial_container.setVisible(mode == Mode.RADIAL); 224 radial_container.setManaged(mode == Mode.RADIAL); 225 } 226 227 /** 228 * Private 229 */ 230 private void initialize() { 231 232 final FXMLLoader loader = new FXMLLoader(); 233 loader.setLocation(GradientPicker.class.getResource("GradientPicker.fxml")); //NOI18N 234 loader.setController(this); 235 loader.setRoot(this); 236 try { 237 loader.load(); 238 } catch (IOException ex) { 239 Logger.getLogger(GradientPicker.class.getName()).log(Level.SEVERE, null, ex); 240 } 241 242 assert proportional_checkbox != null; 243 assert cycleMethod_choicebox != null; 244 assert startX_slider != null; 245 assert endX_slider != null; 246 assert startY_slider != null; 247 assert endY_slider != null; 248 assert centerX_slider != null; 249 assert centerY_slider != null; 250 assert radial_container != null; 251 252 // Add two default stops 253 final GradientPickerStop black = addStop(0.0, 1.0, 0.0, Color.BLACK); 254 addStop(0.0, 1.0, 1.0, Color.WHITE); 255 // Select first default stop 256 setSelectedStop(black); 257 proportional_checkbox.setSelected(true); 258 proportional_checkbox.selectedProperty().addListener((ChangeListener<Boolean>) (ov, oldValue, newValue) -> { 259 final Mode mode = paintPicker.getMode(); 260 final Paint value = getValue(mode); 261 // Update UI 262 preview_rect.setFill(value); 263 // Update model 264 paintPicker.setPaintProperty(value); 265 }); 266 proportional_checkbox.setOnAction((ActionEvent event) -> { 267 event.consume(); 268 }); 269 270 cycleMethod_choicebox.setItems(FXCollections.observableArrayList(CycleMethod.values())); 271 cycleMethod_choicebox.getSelectionModel().selectFirst(); 272 cycleMethod_choicebox.getSelectionModel().selectedItemProperty().addListener((ChangeListener<CycleMethod>) (ov, oldValue, newValue) -> { 273 final Mode mode = paintPicker.getMode(); 274 final Paint value = getValue(mode); 275 // Update UI 276 preview_rect.setFill(value); 277 // Update model 278 paintPicker.setPaintProperty(value); 279 }); 280 cycleMethod_choicebox.addEventHandler(ActionEvent.ACTION, (Event event) -> { 281 event.consume(); 282 }); 283 284 final ChangeListener<Number> onValueChange = (ov, oldValue, newValue) -> { 285 final Mode mode = paintPicker.getMode(); 286 final Paint value = getValue(mode); 287 // Update UI 288 preview_rect.setFill(value); 289 // Update model 290 paintPicker.setPaintProperty(value); 291 }; 292 startX_slider.valueProperty().addListener(onValueChange); 293 startY_slider.valueProperty().addListener(onValueChange); 294 endX_slider.valueProperty().addListener(onValueChange); 295 endY_slider.valueProperty().addListener(onValueChange); 296 297 centerX_slider.valueProperty().addListener(onValueChange); 298 centerY_slider.valueProperty().addListener(onValueChange); 299 focusAngleRotator.rotationProperty().addListener(onValueChange); 300 focusDistanceSlider.getSlider().valueProperty().addListener(onValueChange); 301 radiusSlider.getSlider().valueProperty().addListener(onValueChange); 302 303 radial_container.getChildren().addAll(radiusSlider, focusDistanceSlider, focusAngleRotator); 304 radial_container.setVisible(false); 305 radial_container.setManaged(false); 306 307 final ChangeListener<Boolean> liveUpdateListener = (ov, oldValue, newValue) -> paintPicker.setLiveUpdate(newValue); 308 startX_slider.pressedProperty().addListener(liveUpdateListener); 309 startY_slider.pressedProperty().addListener(liveUpdateListener); 310 endX_slider.pressedProperty().addListener(liveUpdateListener); 311 endY_slider.pressedProperty().addListener(liveUpdateListener); 312 centerX_slider.pressedProperty().addListener(liveUpdateListener); 313 centerY_slider.pressedProperty().addListener(liveUpdateListener); 314 radiusSlider.pressedProperty().addListener(liveUpdateListener); 315 focusDistanceSlider.pressedProperty().addListener(liveUpdateListener); 316 focusAngleRotator.pressedProperty().addListener(liveUpdateListener); 317 slider_container.pressedProperty().addListener(liveUpdateListener); 318 } 319 320 @FXML 321 void sliderPressed(MouseEvent event) { 322 double percentH = ((100.0 / track_pane.getWidth()) * event.getX()) / 100; 323 final Color color = paintPicker.getColorPicker().getValue(); 324 addStop(0.0, 1.0, percentH, color); 325 final Mode mode = paintPicker.getMode(); 326 final Paint value = getValue(mode); 327 // Update UI 328 preview_rect.setFill(value); 329 // Update model 330 paintPicker.setPaintProperty(value); 331 } 332 333 @FXML 334 void sliderDragged(MouseEvent event) { 335 final Mode mode = paintPicker.getMode(); 336 final Paint value = getValue(mode); 337 // Update UI 338 preview_rect.setFill(value); 339 // Update model 340 paintPicker.setPaintProperty(value); 341 } 342 343 GradientPickerStop addStop(double min, double max, double value, Color color) { 344 if (gradientPickerStops.size() < maxStops) { 345 final GradientPickerStop gradientStop 346 = new GradientPickerStop(this, min, max, value, color); 347 track_pane.getChildren().add(gradientStop); 348 gradientPickerStops.add(gradientStop); 349 return gradientStop; 350 } 351 return null; 352 } 353 354 void removeStop(GradientPickerStop gradientStop) { 355 track_pane.getChildren().remove(gradientStop); 356 gradientPickerStops.remove(gradientStop); 357 } 358 359 void removeAllStops() { 360 track_pane.getChildren().clear(); 361 gradientPickerStops.clear(); 362 } 363 364 public void setSelectedStop(GradientPickerStop gradientStop) { 365 for (GradientPickerStop stop : gradientPickerStops) { 366 stop.setSelected(false); // turn them all false 367 } 368 if (gradientStop != null) { 369 gradientStop.setSelected(true); 370 } 371 } 372 373 private List<Stop> getStops() { 374 final List<Stop> stops = new ArrayList<>(); 375 for (GradientPickerStop ges : getGradientStops()) { 376 final Stop stop = new Stop(ges.getOffset(), ges.getColor()); 377 stops.add(stop); 378 } 379 return stops; 380 } 381 }