1 /* 2 * Copyright (c) 2012, 2015, 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.colorpicker.ColorPicker; 37 38 import javafx.fxml.FXML; 39 import javafx.fxml.FXMLLoader; 40 import javafx.geometry.Side; 41 import javafx.scene.image.ImageView; 42 import javafx.scene.input.KeyCode; 43 import javafx.scene.input.KeyEvent; 44 import javafx.scene.input.MouseEvent; 45 import javafx.scene.layout.VBox; 46 import javafx.scene.paint.Color; 47 import javafx.scene.shape.Rectangle; 48 49 import java.io.IOException; 50 import java.util.logging.Level; 51 import java.util.logging.Logger; 52 53 import javafx.beans.value.ChangeListener; 54 import javafx.event.ActionEvent; 55 import javafx.scene.control.Button; 56 import javafx.scene.control.ContextMenu; 57 import javafx.scene.control.CustomMenuItem; 58 import javafx.scene.control.TextField; 59 import javafx.scene.paint.Paint; 60 61 /** 62 * Controller class for the gradient editor stop. 63 */ 64 public class GradientPickerStop extends VBox { 65 66 @FXML 67 private Rectangle chip_rect; 68 @FXML 69 private ImageView indicator_image; 70 @FXML 71 private TextField offset_textfield; 72 @FXML 73 private ContextMenu context_menu; 74 @FXML 75 private CustomMenuItem custom_menu_item; 76 @FXML 77 private Button stop_button; 78 79 private final double min; 80 private final double max; 81 private double offset; 82 private Color color; 83 private boolean isSelected; 84 private double origX; 85 private double startDragX; 86 private double thumbWidth; 87 private final double edgeMargin = 2.0; 88 private final GradientPicker gradientPicker; 89 90 /* 91 * Clamp value to be between min and max. 92 */ 93 private static double clamp(double min, double value, double max) { 94 if (value < min) return min; 95 if (value > max) return max; 96 return value; 97 } 98 99 public GradientPickerStop(GradientPicker ge, double mini, double maxi, double val, Color c) { 100 gradientPicker = ge; 101 min = mini; 102 max = maxi; 103 offset = val; 104 color = c; 105 initialize(); 106 } 107 108 public void setOffset(double val) { 109 offset = clamp(min, val, max); 110 valueToPixels(); 111 } 112 113 public double getOffset() { 114 return offset; 115 } 116 117 public void setColor(Color c) { 118 color = c; 119 chip_rect.setFill(c); 120 } 121 122 public Color getColor() { 123 return color; 124 } 125 126 public void setSelected(boolean selected) { 127 isSelected = selected; 128 if (selected) { 129 indicator_image.setVisible(true); 130 } else { 131 indicator_image.setVisible(false); 132 } 133 } 134 135 public boolean isSelected() { 136 return isSelected; 137 } 138 139 private void initialize() { 140 141 final FXMLLoader loader = new FXMLLoader(); 142 loader.setLocation(GradientPickerStop.class.getResource("GradientPickerStop.fxml")); //NOI18N 143 loader.setController(this); 144 loader.setRoot(this); 145 try { 146 loader.load(); 147 } catch (IOException ex) { 148 Logger.getLogger(GradientPicker.class.getName()).log(Level.SEVERE, null, ex); 149 } 150 151 assert offset_textfield != null; 152 assert chip_rect != null; 153 154 offset_textfield.setText("" + offset); //NOI18N 155 156 chip_rect.setFill(color); 157 gradientPicker.setSelectedStop(this); 158 159 stop_button.setOnAction((ActionEvent event) -> { 160 event.consume(); 161 }); 162 163 // when we detect a width change, we know node layout is resolved so we position stop in track 164 widthProperty().addListener((ChangeListener<Number>) (ov, oldValue, newValue) -> { 165 if (newValue.doubleValue() > 0) { 166 thumbWidth = newValue.doubleValue(); 167 valueToPixels(); 168 } 169 }); 170 } 171 172 @FXML 173 void stopAction(ActionEvent event) { 174 double val = Double.valueOf(offset_textfield.getText()); 175 setOffset(val); 176 showHUD(); 177 // Called when moving a gradient stop : 178 // - update gradient preview accordingly 179 // - update model 180 final PaintPickerController paintPicker 181 = gradientPicker.getPaintPickerController(); 182 final Mode mode = paintPicker.getMode(); 183 final Paint value = gradientPicker.getValue(mode); 184 gradientPicker.updatePreview(value); 185 // Update model 186 paintPicker.setPaintProperty(value); 187 } 188 189 @FXML 190 void thumbKeyPressed(KeyEvent e) { 191 if (e.getCode() == KeyCode.BACK_SPACE || e.getCode() == KeyCode.DELETE) { 192 gradientPicker.removeStop(this); 193 // Called when removing a gradient stop : 194 // - update gradient preview accordingly 195 // - update model 196 final PaintPickerController paintPicker 197 = gradientPicker.getPaintPickerController(); 198 final Mode mode = paintPicker.getMode(); 199 final Paint value = gradientPicker.getValue(mode); 200 gradientPicker.updatePreview(value); 201 // Update model 202 paintPicker.setPaintProperty(value); 203 e.consume(); 204 } 205 } 206 207 @FXML 208 void thumbMousePressed(MouseEvent event) { 209 gradientPicker.setSelectedStop(this); 210 startDragX = event.getSceneX(); 211 origX = getLayoutX(); 212 toFront(); // make sure this stop is in highest z-order 213 // showHUD(); 214 pixelsToValue(); 215 // Called when selecting a gradient stop : 216 // - update color preview accordingly 217 // - do not update the model 218 final PaintPickerController paintPicker 219 = gradientPicker.getPaintPickerController(); 220 final ColorPicker colorPicker = paintPicker.getColorPicker(); 221 colorPicker.updateUI(color); 222 stop_button.requestFocus(); 223 } 224 225 @FXML 226 void thumbMouseReleased() { 227 pixelsToValue(); 228 } 229 230 @FXML 231 void thumbMouseDragged(MouseEvent event) { 232 double dragValue = event.getSceneX() - startDragX; 233 double deltaX = origX + dragValue; 234 double trackWidth = getParent().getBoundsInLocal().getWidth(); 235 final Double newX = clamp(edgeMargin, deltaX, (trackWidth - (getWidth() + edgeMargin))); 236 setLayoutX(newX); 237 // showHUD(); 238 pixelsToValue(); 239 // Called when moving a gradient stop : 240 // - update gradient preview accordingly 241 // - update model 242 final PaintPickerController paintPicker 243 = gradientPicker.getPaintPickerController(); 244 final Mode mode = paintPicker.getMode(); 245 final Paint value = gradientPicker.getValue(mode); 246 gradientPicker.updatePreview(value); 247 // Update model 248 paintPicker.setPaintProperty(value); 249 } 250 251 private void showHUD() { 252 offset_textfield.setText(Double.toString(offset)); 253 context_menu.show(this, Side.BOTTOM, 0, 5); // better way to center? 254 } 255 256 private void valueToPixels() { 257 double stopValue = clamp(min, offset, max); 258 double availablePixels = getParent().getLayoutBounds().getWidth() - (thumbWidth + edgeMargin); 259 double range = max - min; 260 double pixelPosition = ((availablePixels / range) * stopValue); 261 setLayoutX(pixelPosition); 262 } 263 264 private void pixelsToValue() { 265 double range = max - min; 266 double availablePixels = getParent().getLayoutBounds().getWidth() - (thumbWidth + edgeMargin); 267 setOffset(min + (getLayoutX() * (range / availablePixels))); 268 } 269 }