diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/PaintConvertUtil.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/PaintConvertUtil.java new file mode 100644 index 000000000..bc7be4781 --- /dev/null +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/PaintConvertUtil.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.kit.util; + +import javafx.scene.paint.Color; +import javafx.scene.paint.CycleMethod; +import javafx.scene.paint.LinearGradient; +import javafx.scene.paint.Paint; +import javafx.scene.paint.RadialGradient; +import javafx.scene.paint.Stop; + +import java.util.List; + +/** + * Convert Paint to java code or css code; + */ +public class PaintConvertUtil { + + private static final int ROUNDING_FACTOR = 10000;//Use for round to 4 decimal places + + public static String convertPaintToCss(Paint fxPaint) { + if (fxPaint instanceof LinearGradient) { + LinearGradient paint = (LinearGradient) fxPaint; + StringBuilder strBuilder = new StringBuilder("linear-gradient(from ") + .append(lenToStr(paint.getStartX(), paint.isProportional())) + .append(" ").append(lenToStr(paint.getStartY(), paint.isProportional())) + .append(" to ").append(lenToStr(paint.getEndX(), paint.isProportional())) + .append(" ").append(lenToStr(paint.getEndY(), paint.isProportional())) + .append(", "); + connectCycleMethodAndStops(strBuilder, paint.getCycleMethod(), paint.getStops()); + return strBuilder.toString(); + } else if (fxPaint instanceof RadialGradient) { + RadialGradient paint = (RadialGradient) fxPaint; + StringBuilder strBuilder = new StringBuilder("radial-gradient(focus-angle ").append(round(paint.getFocusAngle())) + .append("deg, focus-distance ").append(round(paint.getFocusDistance() * 100)) + .append("% , center ").append(lenToStr(paint.getCenterX(), paint.isProportional())) + .append(" ").append(lenToStr(paint.getCenterY(), paint.isProportional())) + .append(", radius ").append(lenToStr(paint.getRadius(), paint.isProportional())) + .append(", "); + connectCycleMethodAndStops(strBuilder, paint.getCycleMethod(), paint.getStops()); + return strBuilder.toString(); + } else if (fxPaint instanceof Color) { + return toHex((Color) fxPaint); + } + return ""; + } + + public static String convertPaintToJavaCode(Paint fxPaint) { + if (fxPaint instanceof LinearGradient) { + LinearGradient paint = (LinearGradient) fxPaint; + return "LinearGradient paint = new LinearGradient(" + System.lineSeparator() + + round(paint.getStartX()) + ", " + round(paint.getStartY()) + ", " + + round(paint.getEndX()) + ", " + round(paint.getEndY()) + ", " + + paint.isProportional() + ", " + + cycleMethodToStr(paint.getCycleMethod()) + "," + System.lineSeparator() + + stopsToString(paint.getStops()) + + ");"; + } else if (fxPaint instanceof RadialGradient) { + RadialGradient paint = (RadialGradient) fxPaint; + return "RadialGradient paint = new RadialGradient(" + System.lineSeparator() + + round(paint.getFocusAngle()) + ", " + round(paint.getFocusDistance()) + ", " + round(paint.getCenterX()) + ", " + + round(paint.getCenterY()) + ", " + round(paint.getRadius()) + ", " + paint.isProportional() + ", " + + cycleMethodToStr(paint.getCycleMethod())+ "," + System.lineSeparator() + + stopsToString(paint.getStops()) + ");"; + } else if (fxPaint instanceof Color) { + return "Color paint = " + colorToJavaStr((Color) fxPaint) + ";"; + } + return ""; + } + + private static void connectCycleMethodAndStops(StringBuilder strBuilder, CycleMethod cycleMethod, List stops) { + switch (cycleMethod) { + case REFLECT: + strBuilder.append("reflect").append(", "); + break; + case REPEAT: + strBuilder.append("repeat").append(", "); + break; + default: + break; + } + int len = stops.size(); + for (int i = 0; i < len; i++) { + Stop stop = stops.get(i); + strBuilder.append(toHex(stop.getColor())).append(" ").append(round(stop.getOffset() * 100.0D)).append("%"); + if (i < len - 1) { + strBuilder.append(", "); + } + } + strBuilder.append(")"); + } + + private static String cycleMethodToStr(CycleMethod cycleMethod) { + String cycleMethodStr; + if (CycleMethod.REFLECT.equals(cycleMethod)) { + cycleMethodStr = "CycleMethod.REFLECT"; + } else if (CycleMethod.REPEAT.equals(cycleMethod)) { + cycleMethodStr = "CycleMethod.REPEAT"; + } else { + cycleMethodStr = "CycleMethod.NO_CYCLE"; + } + return cycleMethodStr; + } + + private static String stopsToString(List stops) { + StringBuilder stopsBuilder = new StringBuilder(32); + int len = stops.size(); + for (int i = 0; i < len; i++) { + Stop stop = stops.get(i); + Color color = stop.getColor(); + double offset = round(stop.getOffset()); + String strColor = colorToJavaStr(color); + stopsBuilder.append("new Stop(").append(offset).append(", ").append(strColor).append(")"); + if (i < len - 1) { + stopsBuilder.append(",").append(System.lineSeparator()); + } + } + return stopsBuilder.toString(); + } + + private static String colorToJavaStr(Color color) { + return String.format("new Color(%s, %s, %s, %s)", round(color.getRed()), round(color.getGreen()), round(color.getBlue()), round(color.getOpacity())); + } + + private static String lenToStr(double num, boolean isProportional) { + return isProportional ? round(num * 100.0D) + "%" : num + "px"; + } + + private static double round(double num) { + double doubleRounded = Math.round(num * ROUNDING_FACTOR); + return doubleRounded / ROUNDING_FACTOR; + } + + private static String toHex(Color color) { + int red = (int) Math.round(color.getRed() * 255.0D); + int green = (int) Math.round(color.getGreen() * 255.0D); + int blue = (int) Math.round(color.getBlue() * 255.0D); + int alpha = (int) Math.round(color.getOpacity() * 255.0D); + if (alpha == 255) { + return String.format("#%02x%02x%02x", red, green, blue); + } else { + return String.format("#%02x%02x%02x%02x", red, green, blue, alpha); + } + } +} diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.java index fc22610dd..9852abf01 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. + * Copyright (c) 2022, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -13,7 +13,7 @@ * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. - * - Neither the name of Oracle Corporation nor the names of its + * - Neither the name of Oracle Corporation and Gluon nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -31,23 +31,23 @@ */ package com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.colorpicker; +import com.oracle.javafx.scenebuilder.kit.util.PaintConvertUtil; import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker.Mode; import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPickerController; import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.gradientpicker.GradientPicker; import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.gradientpicker.GradientPickerStop; - -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - import javafx.beans.value.ChangeListener; import javafx.event.ActionEvent; -import javafx.fxml.FXMLLoader; -import javafx.scene.control.ScrollPane; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; import javafx.geometry.Bounds; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.ScrollPane; import javafx.scene.control.Slider; import javafx.scene.control.TextField; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; @@ -58,6 +58,10 @@ import javafx.scene.paint.RadialGradient; import javafx.scene.shape.Circle; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * Controller class for the color part of the paint editor. */ @@ -95,6 +99,12 @@ public class ColorPicker extends VBox { private TextField alpha_textfield; @FXML private TextField hexa_textfield; + @FXML + private ComboBox paintCombobox; + @FXML + private Button copyPaintButton; + private static final String JAVA_CODE = "Java Code"; + private static final String CSS_CODE = "CSS Code"; private final PaintPickerController paintPickerController; private boolean updating = false; @@ -242,6 +252,9 @@ private void initialize() { picker_region.pressedProperty().addListener(liveUpdateListener); hue_slider.pressedProperty().addListener(liveUpdateListener); alpha_slider.pressedProperty().addListener(liveUpdateListener); + // paint combobox add values + paintCombobox.getItems().setAll(CSS_CODE, JAVA_CODE); + paintCombobox.getSelectionModel().select(0); } /** @@ -434,9 +447,9 @@ private Color updateUI(double hue, double saturation, double brightness, double alpha = PaintPickerController.clamp(0, alpha, 1); // make an rgb color from the hsb final Color color = Color.hsb(hue, saturation, brightness, alpha); - int red = (int) Math.round(color.getRed() * 255); + int red = (int) Math.round(color.getRed() * 255); int green = (int) Math.round(color.getGreen() * 255); - int blue = (int) Math.round(color.getBlue() * 255); + int blue = (int) Math.round(color.getBlue() * 255); final String hexa = String.format("#%02x%02x%02x", red, green, blue); //NOI18N // Set TextFields value @@ -459,7 +472,7 @@ private Color updateUI(double hue, double saturation, double brightness, double sb.append("%, "); //NOI18N sb.append(brightness * 100); sb.append("%, "); //NOI18N - sb.append(alpha); + sb.append(alpha_rounded);//Fix #503 sb.append(")"); //NOI18N final String hsbCssValue = sb.toString(); final String chipStyle = "-fx-background-color: " + hsbCssValue; //NOI18N @@ -538,4 +551,21 @@ private void handleHexaException() { updateUI(color); hexa_textfield.selectAll(); } + + @FXML + void onActionCopyPaint(ActionEvent event) { + String item = paintCombobox.getSelectionModel().getSelectedItem(); + String paintStr; + Paint paint = paintPickerController.getPaintProperty(); + if (JAVA_CODE.equals(item)) { + paintStr = PaintConvertUtil.convertPaintToJavaCode(paint); + } else { //CSS_CODE.equals(item); + paintStr = PaintConvertUtil.convertPaintToCss(paint); + } + Clipboard clipboard = Clipboard.getSystemClipboard(); + ClipboardContent content = new ClipboardContent(); + content.putString(paintStr); + clipboard.setContent(content); + event.consume(); + } } diff --git a/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.css b/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.css index 488663323..ac8bcaab5 100644 --- a/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.css +++ b/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.css @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022, Gluon and/or its affiliates. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * @@ -59,3 +60,10 @@ .saturationRect { -fx-background-color: linear-gradient(to bottom right, white, transparent); } +.copy-region { + -fx-shape: "M44.3,19.7v39.4H4.9V19.7H44.3 M49.2,14.8H0V64h49.2V14.8zM64,0H14.8v9.8h4.9V4.9h39.4v44.3h-4.9v4.9H64V0zM34.6,34.5h-20v-5h20V34.5zM34.6,49.2h-20v-5h20V49.2z"; + -fx-background-color: #6E6E6E; +} +.copy-paint-button:hover > .copy-region { + -fx-background-color: #5E5E5E; +} \ No newline at end of file diff --git a/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.fxml b/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.fxml index 0b0b994cc..c3a542e7f 100644 --- a/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.fxml +++ b/kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.fxml @@ -1,6 +1,7 @@ + + + + + @@ -48,7 +53,7 @@ - + @@ -135,6 +140,35 @@ + + + +
+ +
+ + + + + + + + + +
+
+
diff --git a/kit/src/test/java/com/oracle/javafx/scenebuilder/kit/util/PaintConvertUtilTest.java b/kit/src/test/java/com/oracle/javafx/scenebuilder/kit/util/PaintConvertUtilTest.java new file mode 100644 index 000000000..0b899e335 --- /dev/null +++ b/kit/src/test/java/com/oracle/javafx/scenebuilder/kit/util/PaintConvertUtilTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.kit.util; + +import javafx.scene.paint.Color; +import javafx.scene.paint.CycleMethod; +import javafx.scene.paint.LinearGradient; +import javafx.scene.paint.RadialGradient; +import javafx.scene.paint.Stop; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PaintConvertUtilTest { + + @Test + public void testConvertPaintToCss() { + //1. convert color to css + Color c1 = Color.rgb(255, 255, 255); + String css1 = PaintConvertUtil.convertPaintToCss(c1); + assertEquals("#ffffff", css1); + Color c2 = Color.rgb(108, 178, 225, 0.62); + String css2 = PaintConvertUtil.convertPaintToCss(c2); + assertEquals("#6cb2e19e", css2); + //2. convert LinearGradient to css + LinearGradient lg1 = new LinearGradient( + 0.0, 0.0, 1.0, 0.0, true, CycleMethod.NO_CYCLE, + new Stop(0.0, new Color(0.14, 0.82, 0.95, 1.0)), + new Stop(0.5, new Color(0.84, 0.57, 0.98, 1.0)), + new Stop(1.0, new Color(1.0, 0.48, 0.52, 1.0))); + String cssLg1 = PaintConvertUtil.convertPaintToCss(lg1); + assertEquals("linear-gradient(from 0.0% 0.0% to 100.0% 0.0%, #24d1f2 0.0%, #d691fa 50.0%, #ff7a85 100.0%)", cssLg1); + //3. convert RadialGradient to css + RadialGradient rg1 = new RadialGradient( + 0.0, 0.0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE + , new Stop(0.0, new Color(0.11, 0.52, 0.93, 1.0)), + new Stop(1.0, new Color(0.68, 0.05, 0.93, 1.0))); + String cssRg1 = PaintConvertUtil.convertPaintToCss(rg1); + assertEquals("radial-gradient(focus-angle 0.0deg, focus-distance 0.0% , center 50.0% 50.0%, radius 50.0%, #1c85ed 0.0%, #ad0ded 100.0%)", cssRg1); + } + + @Test + public void testConvertPaintToJavaCode() { + //1. convert color to java code + Color c1 = Color.rgb(255, 255, 255, 1.0); + String code1 = PaintConvertUtil.convertPaintToJavaCode(c1); + assertEquals("Color paint = new Color(1.0, 1.0, 1.0, 1.0);", code1); + Color c2 = new Color(0.4235, 0.698, 0.8824, 0.62); + String code2 = PaintConvertUtil.convertPaintToJavaCode(c2); + assertEquals("Color paint = new Color(0.4235, 0.698, 0.8824, 0.62);", code2); + String newLine = System.lineSeparator(); + //2. convert LinearGradient to java code + LinearGradient lg1 = new LinearGradient( + 0.0, 0.0, 1.0, 0.0, true, CycleMethod.NO_CYCLE, + new Stop(0.0, new Color(0.14, 0.82, 0.95, 1.0)), + new Stop(0.5, new Color(0.84, 0.57, 0.98, 1.0)), + new Stop(1.0, new Color(1.0, 0.48, 0.52, 1.0))); + String codeLg1 = PaintConvertUtil.convertPaintToJavaCode(lg1); + assertEquals("LinearGradient paint = new LinearGradient(" + newLine + + "0.0, 0.0, 1.0, 0.0, true, CycleMethod.NO_CYCLE," + newLine + + "new Stop(0.0, new Color(0.14, 0.82, 0.95, 1.0))," + newLine + + "new Stop(0.5, new Color(0.84, 0.57, 0.98, 1.0))," + newLine + + "new Stop(1.0, new Color(1.0, 0.48, 0.52, 1.0)));", codeLg1); + //3. convert RadialGradient to java code + RadialGradient rg1 = new RadialGradient( + 0.0, 0.0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE, + new Stop(0.0, new Color(0.11, 0.52, 0.93, 1.0)), + new Stop(1.0, new Color(0.68, 0.05, 0.93, 1.0))); + String codeRg1 = PaintConvertUtil.convertPaintToJavaCode(rg1); + assertEquals("RadialGradient paint = new RadialGradient(" + newLine + + "0.0, 0.0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE," + newLine + + "new Stop(0.0, new Color(0.11, 0.52, 0.93, 1.0))," + newLine + + "new Stop(1.0, new Color(0.68, 0.05, 0.93, 1.0)));", codeRg1); + } +}