Skip to content

Commit

Permalink
feat: Added copy code functionality to Color Picker (gluonhq#496)
Browse files Browse the repository at this point in the history
  • Loading branch information
leewyatt authored Feb 18, 2022
1 parent 5e2e68a commit a586380
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -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<Stop> 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<Stop> 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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.
*
Expand All @@ -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;
Expand All @@ -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.
*/
Expand Down Expand Up @@ -95,6 +99,12 @@ public class ColorPicker extends VBox {
private TextField alpha_textfield;
@FXML
private TextField hexa_textfield;
@FXML
private ComboBox<String> 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;
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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.
*
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
Copyright (c) 2019, Gluon and/or its affiliates.
Copyright (c) 2019, 2022, Gluon and/or its affiliates.
Copyright (c) 2012, 2014, Oracle and/or its affiliates.
All rights reserved. Use is subject to license terms.
Expand Down Expand Up @@ -31,14 +32,18 @@
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->

<?import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.DoubleTextField?>
<?import java.net.URL?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
Expand All @@ -48,7 +53,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>

<fx:root type="javafx.scene.layout.VBox" id="CONTENT" maxHeight="-1.0" prefHeight="-1.0" prefWidth="-1.0" spacing="2.0" style="" visible="true" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2">
<fx:root id="CONTENT" maxHeight="-1.0" prefHeight="-1.0" prefWidth="-1.0" spacing="2.0" style="" type="javafx.scene.layout.VBox" visible="true" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
<children>
<HBox alignment="CENTER_LEFT" maxWidth="1.7976931348623157E308" prefHeight="-1.0" prefWidth="-1.0" spacing="2.0">
<children>
Expand Down Expand Up @@ -135,6 +140,35 @@
<RowConstraints minHeight="5.0" prefHeight="-1.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
<HBox alignment="CENTER_LEFT" prefHeight="35.0" prefWidth="328.0">
<children>
<BorderPane HBox.hgrow="ALWAYS">
<center>
<ComboBox fx:id="paintCombobox" prefHeight="23.0" prefWidth="257.0" />
</center>
<right>
<Button fx:id="copyPaintButton" mnemonicParsing="false" onAction="#onActionCopyPaint" prefHeight="23.0" prefWidth="30.0" styleClass="copy-paint-button" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets left="1.0" />
</BorderPane.margin>
<graphic>
<Region prefHeight="17.0" prefWidth="17.0" styleClass="copy-region" />
</graphic>
</Button>
</right>
<left>
<Label text="Copy" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets />
</BorderPane.margin>
</Label>
</left>
<padding>
<Insets bottom="2.0" left="1.0" right="1.0" top="2.0" />
</padding>
</BorderPane>
</children>
</HBox>
</children>
<stylesheets>
<URL value="@ColorPicker.css" />
Expand Down
Loading

0 comments on commit a586380

Please sign in to comment.