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