1 /* 2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.scene.control; 27 28 import com.sun.javafx.scene.control.IntegerField; 29 import com.sun.javafx.scene.control.WebColorField; 30 import com.sun.javafx.scene.control.skin.IntegerFieldSkin; 31 import com.sun.javafx.scene.control.skin.WebColorFieldSkin; 32 import javafx.beans.InvalidationListener; 33 import javafx.beans.Observable; 34 import javafx.beans.property.*; 35 import javafx.event.EventHandler; 36 import javafx.scene.Scene; 37 import javafx.scene.control.*; 38 import javafx.scene.input.MouseEvent; 39 import javafx.scene.layout.*; 40 import javafx.scene.paint.*; 41 import javafx.stage.Modality; 42 import javafx.stage.Stage; 43 import javafx.stage.StageStyle; 44 import javafx.stage.Window; 45 import javafx.beans.binding.Bindings; 46 import javafx.beans.binding.ObjectBinding; 47 import javafx.geometry.Insets; 48 import javafx.geometry.Orientation; 49 import javafx.geometry.Pos; 50 import javafx.geometry.Rectangle2D; 51 import javafx.scene.input.KeyEvent; 52 import javafx.stage.Screen; 53 import javafx.stage.WindowEvent; 54 55 /** 56 * 57 */ 58 public class CustomColorDialog extends HBox { 59 60 private final Stage dialog = new Stage(); 61 private ColorRectPane colorRectPane; 62 private ControlsPane controlsPane; 63 64 private ObjectProperty<Color> currentColorProperty = new SimpleObjectProperty<>(Color.WHITE); 65 private ObjectProperty<Color> customColorProperty = new SimpleObjectProperty<>(Color.TRANSPARENT); 66 private Runnable onSave; 67 private Runnable onUse; 68 private Runnable onCancel; 69 70 private WebColorField webField = null; 71 private Scene customScene; 72 73 public CustomColorDialog(Window owner) { 74 getStyleClass().add("custom-color-dialog"); 75 if (owner != null) dialog.initOwner(owner); 76 dialog.setTitle(Properties.getColorPickerString("customColorDialogTitle")); 77 dialog.initModality(Modality.APPLICATION_MODAL); 78 dialog.initStyle(StageStyle.UTILITY); 79 dialog.setResizable(false); 80 colorRectPane = new ColorRectPane(); 81 controlsPane = new ControlsPane(); 82 setHgrow(controlsPane, Priority.ALWAYS); 83 84 customScene = new Scene(this); 85 final Scene ownerScene = owner.getScene(); 86 if (ownerScene != null) { 87 if (ownerScene.getUserAgentStylesheet() != null) { 88 customScene.setUserAgentStylesheet(ownerScene.getUserAgentStylesheet()); 89 } 90 customScene.getStylesheets().addAll(ownerScene.getStylesheets()); 91 } 92 getChildren().addAll(colorRectPane, controlsPane); 93 94 dialog.setScene(customScene); 95 dialog.addEventHandler(KeyEvent.ANY, keyEventListener); 96 } 97 98 private final EventHandler<KeyEvent> keyEventListener = e -> { 99 switch (e.getCode()) { 100 case ESCAPE : 101 dialog.setScene(null); 102 dialog.close(); 103 default: 104 break; 105 } 106 }; 107 108 public void setCurrentColor(Color currentColor) { 109 this.currentColorProperty.set(currentColor); 110 } 111 112 public final Color getCurrentColor() { 113 return currentColorProperty.get(); 114 } 115 116 public final ObjectProperty<Color> customColorProperty() { 117 return customColorProperty; 118 } 119 120 public final void setCustomColor(Color color) { 121 customColorProperty.set(color); 122 } 123 124 public final Color getCustomColor() { 125 return customColorProperty.get(); 126 } 127 128 public Runnable getOnSave() { 129 return onSave; 130 } 131 132 public void setOnSave(Runnable onSave) { 133 this.onSave = onSave; 134 } 135 136 public Runnable getOnUse() { 137 return onUse; 138 } 139 140 public void setOnUse(Runnable onUse) { 141 this.onUse = onUse; 142 } 143 144 public Runnable getOnCancel() { 145 return onCancel; 146 } 147 148 public void setOnCancel(Runnable onCancel) { 149 this.onCancel = onCancel; 150 } 151 152 public void setOnHidden(EventHandler<WindowEvent> onHidden) { 153 dialog.setOnHidden(onHidden); 154 } 155 156 public Stage getDialog() { 157 return dialog; 158 } 159 160 public void show() { 161 if (dialog.getOwner() != null) { 162 // Workaround of RT-29871: Instead of just invoking fixPosition() 163 // here need to use listener that fixes dialog position once both 164 // width and height are determined 165 dialog.widthProperty().addListener(positionAdjuster); 166 dialog.heightProperty().addListener(positionAdjuster); 167 positionAdjuster.invalidated(null); 168 } 169 if (dialog.getScene() == null) dialog.setScene(customScene); 170 colorRectPane.updateValues(); 171 dialog.show(); 172 } 173 174 private InvalidationListener positionAdjuster = new InvalidationListener() { 175 176 @Override 177 public void invalidated(Observable ignored) { 178 if (Double.isNaN(dialog.getWidth()) || Double.isNaN(dialog.getHeight())) { 179 return; 180 } 181 dialog.widthProperty().removeListener(positionAdjuster); 182 dialog.heightProperty().removeListener(positionAdjuster); 183 fixPosition(); 184 } 185 186 }; 187 188 private void fixPosition() { 189 Window w = dialog.getOwner(); 190 Screen s = com.sun.javafx.util.Utils.getScreen(w); 191 Rectangle2D sb = s.getBounds(); 192 double xR = w.getX() + w.getWidth(); 193 double xL = w.getX() - dialog.getWidth(); 194 double x, y; 195 if (sb.getMaxX() >= xR + dialog.getWidth()) { 196 x = xR; 197 } else if (sb.getMinX() <= xL) { 198 x = xL; 199 } else { 200 x = Math.max(sb.getMinX(), sb.getMaxX() - dialog.getWidth()); 201 } 202 y = Math.max(sb.getMinY(), Math.min(sb.getMaxY() - dialog.getHeight(), w.getY())); 203 dialog.setX(x); 204 dialog.setY(y); 205 } 206 207 @Override public void layoutChildren() { 208 super.layoutChildren(); 209 if (dialog.getMinWidth() > 0 && dialog.getMinHeight() > 0) { 210 // don't recalculate min size once it's set 211 return; 212 } 213 214 // Math.max(0, ...) added for RT-34704 to ensure the dialog is at least 0 x 0 215 double minWidth = Math.max(0, computeMinWidth(getHeight()) + (dialog.getWidth() - customScene.getWidth())); 216 double minHeight = Math.max(0, computeMinHeight(getWidth()) + (dialog.getHeight() - customScene.getHeight())); 217 dialog.setMinWidth(minWidth); 218 dialog.setMinHeight(minHeight); 219 } 220 221 /* ------------------------------------------------------------------------*/ 222 223 private class ColorRectPane extends HBox { 224 225 private Pane colorRect; 226 private Pane colorBar; 227 private Pane colorRectOverlayOne; 228 private Pane colorRectOverlayTwo; 229 private Region colorRectIndicator; 230 private Region colorBarIndicator; 231 232 private boolean changeIsLocal = false; 233 private DoubleProperty hue = new SimpleDoubleProperty(-1) { 234 @Override protected void invalidated() { 235 if (!changeIsLocal) { 236 changeIsLocal = true; 237 updateHSBColor(); 238 changeIsLocal = false; 239 } 240 } 241 }; 242 private DoubleProperty sat = new SimpleDoubleProperty(-1) { 243 @Override protected void invalidated() { 244 if (!changeIsLocal) { 245 changeIsLocal = true; 246 updateHSBColor(); 247 changeIsLocal = false; 248 } 249 } 250 }; 251 private DoubleProperty bright = new SimpleDoubleProperty(-1) { 252 @Override protected void invalidated() { 253 if (!changeIsLocal) { 254 changeIsLocal = true; 255 updateHSBColor(); 256 changeIsLocal = false; 257 } 258 } 259 }; 260 private IntegerProperty red = new SimpleIntegerProperty(-1) { 261 @Override protected void invalidated() { 262 if (!changeIsLocal) { 263 changeIsLocal = true; 264 updateRGBColor(); 265 changeIsLocal = false; 266 } 267 } 268 }; 269 270 private IntegerProperty green = new SimpleIntegerProperty(-1) { 271 @Override protected void invalidated() { 272 if (!changeIsLocal) { 273 changeIsLocal = true; 274 updateRGBColor(); 275 changeIsLocal = false; 276 } 277 } 278 }; 279 280 private IntegerProperty blue = new SimpleIntegerProperty(-1) { 281 @Override protected void invalidated() { 282 if (!changeIsLocal) { 283 changeIsLocal = true; 284 updateRGBColor(); 285 changeIsLocal = false; 286 } 287 } 288 }; 289 290 private DoubleProperty alpha = new SimpleDoubleProperty(100) { 291 @Override protected void invalidated() { 292 if (!changeIsLocal) { 293 changeIsLocal = true; 294 setCustomColor(new Color( 295 getCustomColor().getRed(), 296 getCustomColor().getGreen(), 297 getCustomColor().getBlue(), 298 clamp(alpha.get() / 100))); 299 changeIsLocal = false; 300 } 301 } 302 }; 303 304 private void updateRGBColor() { 305 Color newColor = Color.rgb(red.get(), green.get(), blue.get(), clamp(alpha.get() / 100)); 306 hue.set(newColor.getHue()); 307 sat.set(newColor.getSaturation() * 100); 308 bright.set(newColor.getBrightness() * 100); 309 setCustomColor(newColor); 310 } 311 312 private void updateHSBColor() { 313 Color newColor = Color.hsb(hue.get(), clamp(sat.get() / 100), 314 clamp(bright.get() / 100), clamp(alpha.get() / 100)); 315 red.set(doubleToInt(newColor.getRed())); 316 green.set(doubleToInt(newColor.getGreen())); 317 blue.set(doubleToInt(newColor.getBlue())); 318 setCustomColor(newColor); 319 } 320 321 private void colorChanged() { 322 if (!changeIsLocal) { 323 changeIsLocal = true; 324 hue.set(getCustomColor().getHue()); 325 sat.set(getCustomColor().getSaturation() * 100); 326 bright.set(getCustomColor().getBrightness() * 100); 327 red.set(doubleToInt(getCustomColor().getRed())); 328 green.set(doubleToInt(getCustomColor().getGreen())); 329 blue.set(doubleToInt(getCustomColor().getBlue())); 330 changeIsLocal = false; 331 } 332 } 333 334 public ColorRectPane() { 335 336 getStyleClass().add("color-rect-pane"); 337 338 customColorProperty().addListener((ov, t, t1) -> { 339 colorChanged(); 340 }); 341 342 colorRectIndicator = new Region(); 343 colorRectIndicator.setId("color-rect-indicator"); 344 colorRectIndicator.setManaged(false); 345 colorRectIndicator.setMouseTransparent(true); 346 colorRectIndicator.setCache(true); 347 348 final Pane colorRectOpacityContainer = new StackPane(); 349 350 colorRect = new StackPane() { 351 // This is an implementation of square control that chooses its 352 // size to fill the available height 353 @Override 354 public Orientation getContentBias() { 355 return Orientation.VERTICAL; 356 } 357 @Override 358 protected double computePrefWidth(double height) { 359 return height; 360 } 361 @Override 362 protected double computeMaxWidth(double height) { 363 return height; 364 } 365 }; 366 colorRect.getStyleClass().addAll("color-rect", "transparent-pattern"); 367 368 Pane colorRectHue = new Pane(); 369 colorRectHue.backgroundProperty().bind(new ObjectBinding<Background>() { 370 371 { 372 bind(hue); 373 } 374 375 @Override protected Background computeValue() { 376 return new Background(new BackgroundFill( 377 Color.hsb(hue.getValue(), 1.0, 1.0), 378 CornerRadii.EMPTY, Insets.EMPTY)); 379 } 380 }); 381 382 colorRectOverlayOne = new Pane(); 383 colorRectOverlayOne.getStyleClass().add("color-rect"); 384 colorRectOverlayOne.setBackground(new Background(new BackgroundFill( 385 new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, 386 new Stop(0, Color.rgb(255, 255, 255, 1)), 387 new Stop(1, Color.rgb(255, 255, 255, 0))), 388 CornerRadii.EMPTY, Insets.EMPTY))); 389 390 EventHandler<MouseEvent> rectMouseHandler = event -> { 391 final double x = event.getX(); 392 final double y = event.getY(); 393 sat.set(clamp(x / colorRect.getWidth()) * 100); 394 bright.set(100 - (clamp(y / colorRect.getHeight()) * 100)); 395 }; 396 397 colorRectOverlayTwo = new Pane(); 398 colorRectOverlayTwo.getStyleClass().addAll("color-rect"); 399 colorRectOverlayTwo.setBackground(new Background(new BackgroundFill( 400 new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, 401 new Stop(0, Color.rgb(0, 0, 0, 0)), new Stop(1, Color.rgb(0, 0, 0, 1))), 402 CornerRadii.EMPTY, Insets.EMPTY))); 403 colorRectOverlayTwo.setOnMouseDragged(rectMouseHandler); 404 colorRectOverlayTwo.setOnMousePressed(rectMouseHandler); 405 406 Pane colorRectBlackBorder = new Pane(); 407 colorRectBlackBorder.setMouseTransparent(true); 408 colorRectBlackBorder.getStyleClass().addAll("color-rect", "color-rect-border"); 409 410 colorBar = new Pane(); 411 colorBar.getStyleClass().add("color-bar"); 412 colorBar.setBackground(new Background(new BackgroundFill(createHueGradient(), 413 CornerRadii.EMPTY, Insets.EMPTY))); 414 415 colorBarIndicator = new Region(); 416 colorBarIndicator.setId("color-bar-indicator"); 417 colorBarIndicator.setMouseTransparent(true); 418 colorBarIndicator.setCache(true); 419 420 colorRectIndicator.layoutXProperty().bind(sat.divide(100).multiply(colorRect.widthProperty())); 421 colorRectIndicator.layoutYProperty().bind(Bindings.subtract(1, bright.divide(100)).multiply(colorRect.heightProperty())); 422 colorBarIndicator.layoutYProperty().bind(hue.divide(360).multiply(colorBar.heightProperty())); 423 colorRectOpacityContainer.opacityProperty().bind(alpha.divide(100)); 424 425 EventHandler<MouseEvent> barMouseHandler = event -> { 426 final double y = event.getY(); 427 hue.set(clamp(y / colorRect.getHeight()) * 360); 428 }; 429 430 colorBar.setOnMouseDragged(barMouseHandler); 431 colorBar.setOnMousePressed(barMouseHandler); 432 433 colorBar.getChildren().setAll(colorBarIndicator); 434 colorRectOpacityContainer.getChildren().setAll(colorRectHue, colorRectOverlayOne, colorRectOverlayTwo); 435 colorRect.getChildren().setAll(colorRectOpacityContainer, colorRectBlackBorder, colorRectIndicator); 436 HBox.setHgrow(colorRect, Priority.SOMETIMES); 437 getChildren().addAll(colorRect, colorBar); 438 } 439 440 private void updateValues() { 441 if (getCurrentColor() == null) { 442 setCurrentColor(Color.TRANSPARENT); 443 } 444 changeIsLocal = true; 445 //Initialize hue, sat, bright, color, red, green and blue 446 hue.set(getCurrentColor().getHue()); 447 sat.set(getCurrentColor().getSaturation()*100); 448 bright.set(getCurrentColor().getBrightness()*100); 449 alpha.set(getCurrentColor().getOpacity()*100); 450 setCustomColor(Color.hsb(hue.get(), clamp(sat.get() / 100), clamp(bright.get() / 100), 451 clamp(alpha.get()/100))); 452 red.set(doubleToInt(getCustomColor().getRed())); 453 green.set(doubleToInt(getCustomColor().getGreen())); 454 blue.set(doubleToInt(getCustomColor().getBlue())); 455 changeIsLocal = false; 456 } 457 458 @Override protected void layoutChildren() { 459 super.layoutChildren(); 460 461 // to maintain default size 462 colorRectIndicator.autosize(); 463 // to maintain square size 464 double size = Math.min(colorRect.getWidth(), colorRect.getHeight()); 465 colorRect.resize(size, size); 466 colorBar.resize(colorBar.getWidth(), size); 467 } 468 } 469 470 /* ------------------------------------------------------------------------*/ 471 472 private class ControlsPane extends VBox { 473 474 private Label currentColorLabel; 475 private Label newColorLabel; 476 private Region currentColorRect; 477 private Region newColorRect; 478 private Region currentTransparent; // for opacity 479 private GridPane currentAndNewColor; 480 private Region currentNewColorBorder; 481 private ToggleButton hsbButton; 482 private ToggleButton rgbButton; 483 private ToggleButton webButton; 484 private HBox hBox; 485 486 private Label labels[] = new Label[4]; 487 private Slider sliders[] = new Slider[4]; 488 private IntegerField fields[] = new IntegerField[4]; 489 private Label units[] = new Label[4]; 490 private HBox buttonBox; 491 private Region whiteBox; 492 493 private GridPane settingsPane = new GridPane(); 494 495 public ControlsPane() { 496 getStyleClass().add("controls-pane"); 497 498 currentNewColorBorder = new Region(); 499 currentNewColorBorder.setId("current-new-color-border"); 500 501 currentTransparent = new Region(); 502 currentTransparent.getStyleClass().addAll("transparent-pattern"); 503 504 currentColorRect = new Region(); 505 currentColorRect.getStyleClass().add("color-rect"); 506 currentColorRect.setId("current-color"); 507 currentColorRect.backgroundProperty().bind(new ObjectBinding<Background>() { 508 { 509 bind(currentColorProperty); 510 } 511 @Override protected Background computeValue() { 512 return new Background(new BackgroundFill(currentColorProperty.get(), CornerRadii.EMPTY, Insets.EMPTY)); 513 } 514 }); 515 516 newColorRect = new Region(); 517 newColorRect.getStyleClass().add("color-rect"); 518 newColorRect.setId("new-color"); 519 newColorRect.backgroundProperty().bind(new ObjectBinding<Background>() { 520 { 521 bind(customColorProperty); 522 } 523 @Override protected Background computeValue() { 524 return new Background(new BackgroundFill(customColorProperty.get(), CornerRadii.EMPTY, Insets.EMPTY)); 525 } 526 }); 527 528 currentColorLabel = new Label(Properties.getColorPickerString("currentColor")); 529 newColorLabel = new Label(Properties.getColorPickerString("newColor")); 530 531 whiteBox = new Region(); 532 whiteBox.getStyleClass().add("customcolor-controls-background"); 533 534 hsbButton = new ToggleButton(Properties.getColorPickerString("colorType.hsb")); 535 hsbButton.getStyleClass().add("left-pill"); 536 rgbButton = new ToggleButton(Properties.getColorPickerString("colorType.rgb")); 537 rgbButton.getStyleClass().add("center-pill"); 538 webButton = new ToggleButton(Properties.getColorPickerString("colorType.web")); 539 webButton.getStyleClass().add("right-pill"); 540 final ToggleGroup group = new ToggleGroup(); 541 542 hBox = new HBox(); 543 hBox.setAlignment(Pos.CENTER); 544 hBox.getChildren().addAll(hsbButton, rgbButton, webButton); 545 546 Region spacer1 = new Region(); 547 spacer1.setId("spacer1"); 548 Region spacer2 = new Region(); 549 spacer2.setId("spacer2"); 550 Region leftSpacer = new Region(); 551 leftSpacer.setId("spacer-side"); 552 Region rightSpacer = new Region(); 553 rightSpacer.setId("spacer-side"); 554 Region bottomSpacer = new Region(); 555 bottomSpacer.setId("spacer-bottom"); 556 557 currentAndNewColor = new GridPane(); 558 currentAndNewColor.getColumnConstraints().addAll(new ColumnConstraints(), new ColumnConstraints()); 559 currentAndNewColor.getColumnConstraints().get(0).setHgrow(Priority.ALWAYS); 560 currentAndNewColor.getColumnConstraints().get(1).setHgrow(Priority.ALWAYS); 561 currentAndNewColor.getRowConstraints().addAll(new RowConstraints(), new RowConstraints(), new RowConstraints()); 562 currentAndNewColor.getRowConstraints().get(2).setVgrow(Priority.ALWAYS); 563 VBox.setVgrow(currentAndNewColor, Priority.ALWAYS); 564 565 currentAndNewColor.getStyleClass().add("current-new-color-grid"); 566 currentAndNewColor.add(currentColorLabel, 0, 0); 567 currentAndNewColor.add(newColorLabel, 1, 0); 568 currentAndNewColor.add(spacer1, 0, 1, 2, 1); 569 currentAndNewColor.add(currentTransparent, 0, 2, 2, 1); 570 currentAndNewColor.add(currentColorRect, 0, 2); 571 currentAndNewColor.add(newColorRect, 1, 2); 572 currentAndNewColor.add(currentNewColorBorder, 0, 2, 2, 1); 573 currentAndNewColor.add(spacer2, 0, 3, 2, 1); 574 575 settingsPane = new GridPane(); 576 settingsPane.setId("settings-pane"); 577 settingsPane.getColumnConstraints().addAll(new ColumnConstraints(), 578 new ColumnConstraints(), new ColumnConstraints(), 579 new ColumnConstraints(), new ColumnConstraints(), 580 new ColumnConstraints()); 581 settingsPane.getColumnConstraints().get(0).setHgrow(Priority.NEVER); 582 settingsPane.getColumnConstraints().get(2).setHgrow(Priority.ALWAYS); 583 settingsPane.getColumnConstraints().get(3).setHgrow(Priority.NEVER); 584 settingsPane.getColumnConstraints().get(4).setHgrow(Priority.NEVER); 585 settingsPane.getColumnConstraints().get(5).setHgrow(Priority.NEVER); 586 settingsPane.add(whiteBox, 0, 0, 6, 5); 587 settingsPane.add(hBox, 0, 0, 6, 1); 588 settingsPane.add(leftSpacer, 0, 0); 589 settingsPane.add(rightSpacer, 5, 0); 590 settingsPane.add(bottomSpacer, 0, 4); 591 592 webField = new WebColorField(); 593 webField.getStyleClass().add("web-field"); 594 webField.setSkin(new WebColorFieldSkin(webField)); 595 webField.valueProperty().bindBidirectional(customColorProperty); 596 webField.visibleProperty().bind(group.selectedToggleProperty().isEqualTo(webButton)); 597 settingsPane.add(webField, 2, 1); 598 599 // Color settings Grid Pane 600 for (int i = 0; i < 4; i++) { 601 labels[i] = new Label(); 602 labels[i].getStyleClass().add("settings-label"); 603 604 sliders[i] = new Slider(); 605 606 fields[i] = new IntegerField(); 607 fields[i].getStyleClass().add("color-input-field"); 608 fields[i].setSkin(new IntegerFieldSkin(fields[i])); 609 610 units[i] = new Label(i == 0 ? "\u00B0" : "%"); 611 units[i].getStyleClass().add("settings-unit"); 612 613 if (i > 0 && i < 3) { 614 // first row and opacity labels are always visible 615 // second and third row labels are not visible in Web page 616 labels[i].visibleProperty().bind(group.selectedToggleProperty().isNotEqualTo(webButton)); 617 } 618 if (i < 3) { 619 // sliders and fields shouldn't be visible in Web page 620 sliders[i].visibleProperty().bind(group.selectedToggleProperty().isNotEqualTo(webButton)); 621 fields[i].visibleProperty().bind(group.selectedToggleProperty().isNotEqualTo(webButton)); 622 units[i].visibleProperty().bind(group.selectedToggleProperty().isEqualTo(hsbButton)); 623 } 624 int row = 1 + i; 625 if (i == 3) { 626 // opacity row is shifted one gridPane row down 627 row++; 628 } 629 630 settingsPane.add(labels[i], 1, row); 631 settingsPane.add(sliders[i], 2, row); 632 settingsPane.add(fields[i], 3, row); 633 settingsPane.add(units[i], 4, row); 634 } 635 636 set(3, Properties.getColorPickerString("opacity_colon"), 100, colorRectPane.alpha); 637 638 hsbButton.setToggleGroup(group); 639 rgbButton.setToggleGroup(group); 640 webButton.setToggleGroup(group); 641 group.selectedToggleProperty().addListener((observable, oldValue, newValue) -> { 642 if (newValue == null) { 643 group.selectToggle(oldValue); 644 } else { 645 if (newValue == hsbButton) { 646 showHSBSettings(); 647 } else if (newValue == rgbButton) { 648 showRGBSettings(); 649 } else { 650 showWebSettings(); 651 } 652 } 653 }); 654 group.selectToggle(hsbButton); 655 656 buttonBox = new HBox(); 657 buttonBox.setId("buttons-hbox"); 658 659 Button saveButton = new Button(Properties.getColorPickerString("Save")); 660 saveButton.setDefaultButton(true); 661 saveButton.setOnAction(t -> { 662 if (onSave != null) { 663 onSave.run(); 664 } 665 dialog.hide(); 666 }); 667 668 Button useButton = new Button(Properties.getColorPickerString("Use")); 669 useButton.setOnAction(t -> { 670 if (onUse != null) { 671 onUse.run(); 672 } 673 dialog.hide(); 674 }); 675 676 Button cancelButton = new Button(Properties.getColorPickerString("Cancel")); 677 cancelButton.setCancelButton(true); 678 cancelButton.setOnAction(e -> { 679 customColorProperty.set(getCurrentColor()); 680 if (onCancel != null) { 681 onCancel.run(); 682 } 683 dialog.hide(); 684 }); 685 buttonBox.getChildren().addAll(saveButton, useButton, cancelButton); 686 687 getChildren().addAll(currentAndNewColor, settingsPane, buttonBox); 688 } 689 690 private void showHSBSettings() { 691 set(0, Properties.getColorPickerString("hue_colon"), 360, colorRectPane.hue); 692 set(1, Properties.getColorPickerString("saturation_colon"), 100, colorRectPane.sat); 693 set(2, Properties.getColorPickerString("brightness_colon"), 100, colorRectPane.bright); 694 } 695 696 private void showRGBSettings() { 697 set(0, Properties.getColorPickerString("red_colon"), 255, colorRectPane.red); 698 set(1, Properties.getColorPickerString("green_colon"), 255, colorRectPane.green); 699 set(2, Properties.getColorPickerString("blue_colon"), 255, colorRectPane.blue); 700 } 701 702 private void showWebSettings() { 703 labels[0].setText(Properties.getColorPickerString("web_colon")); 704 } 705 706 private Property<Number>[] bindedProperties = new Property[4]; 707 708 private void set(int row, String caption, int maxValue, Property<Number> prop) { 709 labels[row].setText(caption); 710 if (bindedProperties[row] != null) { 711 sliders[row].valueProperty().unbindBidirectional(bindedProperties[row]); 712 fields[row].valueProperty().unbindBidirectional(bindedProperties[row]); 713 } 714 sliders[row].setMax(maxValue); 715 sliders[row].valueProperty().bindBidirectional(prop); 716 labels[row].setLabelFor(sliders[row]); 717 fields[row].setMaxValue(maxValue); 718 fields[row].valueProperty().bindBidirectional(prop); 719 bindedProperties[row] = prop; 720 } 721 } 722 723 static double clamp(double value) { 724 return value < 0 ? 0 : value > 1 ? 1 : value; 725 } 726 727 private static LinearGradient createHueGradient() { 728 double offset; 729 Stop[] stops = new Stop[255]; 730 for (int y = 0; y < 255; y++) { 731 offset = (double)(1 - (1.0 / 255) * y); 732 int h = (int)((y / 255.0) * 360); 733 stops[y] = new Stop(offset, Color.hsb(h, 1.0, 1.0)); 734 } 735 return new LinearGradient(0f, 1f, 0f, 0f, true, CycleMethod.NO_CYCLE, stops); 736 } 737 738 private static int doubleToInt(double value) { 739 return (int) (value * 255 + 0.5); // Adding 0.5 for rounding only 740 } 741 }