com.jfoenix.skins.JFXColorPalette Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.jfoenix.skins;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.utils.JFXNodeUtils;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.NodeOrientation;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Label;
import javafx.scene.control.PopupControl;
import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import java.util.List;
/**
* @author Shadi Shaheen FUTURE WORK: this UI will get re-designed to match material design guidlines
*/
class JFXColorPalette extends Region {
private static final int SQUARE_SIZE = 15;
// package protected for testing purposes
JFXColorGrid colorPickerGrid;
final JFXButton customColorLink = new JFXButton("Custom Color");
JFXCustomColorPickerDialog customColorDialog = null;
private ColorPicker colorPicker;
private final GridPane customColorGrid = new GridPane();
private final Label customColorLabel = new Label("Recent Colors");
private PopupControl popupControl;
private ColorSquare focusedSquare;
private Color mouseDragColor = null;
private boolean dragDetected = false;
private final ColorSquare hoverSquare = new ColorSquare();
public JFXColorPalette(final ColorPicker colorPicker) {
getStyleClass().add("color-palette-region");
this.colorPicker = colorPicker;
colorPickerGrid = new JFXColorGrid();
colorPickerGrid.getChildren().get(0).requestFocus();
customColorLabel.setAlignment(Pos.CENTER_LEFT);
customColorLink.setPrefWidth(colorPickerGrid.prefWidth(-1));
customColorLink.setAlignment(Pos.CENTER);
customColorLink.setFocusTraversable(true);
customColorLink.setOnAction(ev -> {
if (customColorDialog == null) {
customColorDialog = new JFXCustomColorPickerDialog(popupControl);
customColorDialog.customColorProperty().addListener((ov, t1, t2) -> {
colorPicker.setValue(customColorDialog.customColorProperty().get());
});
customColorDialog.setOnSave(() -> {
Color customColor = customColorDialog.customColorProperty().get();
buildCustomColors();
colorPicker.getCustomColors().add(customColor);
updateSelection(customColor);
Event.fireEvent(colorPicker, new ActionEvent());
colorPicker.hide();
});
}
customColorDialog.setCurrentColor(colorPicker.valueProperty().get());
if (popupControl != null) {
popupControl.setAutoHide(false);
}
customColorDialog.show();
customColorDialog.setOnHidden(event -> {
if (popupControl != null) {
popupControl.setAutoHide(true);
}
});
});
initNavigation();
customColorGrid.getStyleClass().add("color-picker-grid");
customColorGrid.setVisible(false);
buildCustomColors();
colorPicker.getCustomColors().addListener((Change extends Color> change) -> buildCustomColors());
VBox paletteBox = new VBox();
paletteBox.getStyleClass().add("color-palette");
paletteBox.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
paletteBox.setBorder(new Border(new BorderStroke(Color.valueOf("#9E9E9E"),
BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,
BorderWidths.DEFAULT)));
paletteBox.getChildren().addAll(colorPickerGrid, customColorLabel, customColorGrid, customColorLink);
hoverSquare.setMouseTransparent(true);
hoverSquare.getStyleClass().addAll("hover-square");
setFocusedSquare(null);
getChildren().addAll(paletteBox, hoverSquare);
Platform.runLater(() -> {
customColorDialog = new JFXCustomColorPickerDialog(popupControl);
customColorDialog.customColorProperty().addListener((ov, t1, t2) -> {
colorPicker.setValue(customColorDialog.customColorProperty().get());
});
customColorDialog.setOnSave(() -> {
Color customColor = customColorDialog.customColorProperty().get();
buildCustomColors();
colorPicker.getCustomColors().add(customColor);
updateSelection(customColor);
Event.fireEvent(colorPicker, new ActionEvent());
colorPicker.hide();
});
});
}
private void setFocusedSquare(ColorSquare square) {
hoverSquare.setVisible(square != null);
if (square == focusedSquare) {
return;
}
focusedSquare = square;
hoverSquare.setVisible(focusedSquare != null);
if (focusedSquare == null) {
return;
}
if (!focusedSquare.isFocused()) {
focusedSquare.requestFocus();
}
hoverSquare.rectangle.setFill(focusedSquare.rectangle.getFill());
Bounds b = square.localToScene(square.getLayoutBounds());
double x = b.getMinX();
double y = b.getMinY();
double xAdjust;
double scaleAdjust = hoverSquare.getScaleX() == 1.0 ? 0 : hoverSquare.getWidth() / 4.0;
if (colorPicker.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT) {
x = focusedSquare.getLayoutX();
xAdjust = -focusedSquare.getWidth() + scaleAdjust;
} else {
xAdjust = focusedSquare.getWidth() / 2.0 + scaleAdjust;
}
hoverSquare.setLayoutX(snapPosition(x) - xAdjust);
hoverSquare.setLayoutY(snapPosition(y) - focusedSquare.getHeight() / 2.0 + (hoverSquare.getScaleY() == 1.0 ? 0 : focusedSquare
.getHeight() / 4.0));
}
private void buildCustomColors() {
final ObservableList customColors = colorPicker.getCustomColors();
customColorGrid.getChildren().clear();
if (customColors.isEmpty()) {
customColorLabel.setVisible(false);
customColorLabel.setManaged(false);
customColorGrid.setVisible(false);
customColorGrid.setManaged(false);
return;
} else {
customColorLabel.setVisible(true);
customColorLabel.setManaged(true);
customColorGrid.setVisible(true);
customColorGrid.setManaged(true);
}
int customColumnIndex = 0;
int customRowIndex = 0;
int remainingSquares = customColors.size() % NUM_OF_COLUMNS;
int numEmpty = (remainingSquares == 0) ? 0 : NUM_OF_COLUMNS - remainingSquares;
for (int i = 0; i < customColors.size(); i++) {
Color c = customColors.get(i);
ColorSquare square = new ColorSquare(c, i, true);
customColorGrid.add(square, customColumnIndex, customRowIndex);
customColumnIndex++;
if (customColumnIndex == NUM_OF_COLUMNS) {
customColumnIndex = 0;
customRowIndex++;
}
}
for (int i = 0; i < numEmpty; i++) {
ColorSquare emptySquare = new ColorSquare();
customColorGrid.add(emptySquare, customColumnIndex, customRowIndex);
customColumnIndex++;
}
requestLayout();
}
private void initNavigation() {
setOnKeyPressed(ke -> {
switch (ke.getCode()) {
case SPACE:
case ENTER:
// select the focused color
if (focusedSquare != null) {
focusedSquare.selectColor(ke);
}
ke.consume();
break;
default: // no-op
}
});
}
public void setPopupControl(PopupControl pc) {
this.popupControl = pc;
}
public JFXColorGrid getColorGrid() {
return colorPickerGrid;
}
public boolean isCustomColorDialogShowing() {
return customColorDialog != null && customColorDialog.isVisible();
}
class ColorSquare extends StackPane {
Rectangle rectangle;
boolean isEmpty;
public ColorSquare() {
this(null, -1, false);
}
public ColorSquare(Color color, int index) {
this(color, index, false);
}
public ColorSquare(Color color, int index, boolean isCustom) {
// Add style class to handle selected color square
getStyleClass().add("color-square");
if (color != null) {
setFocusTraversable(true);
focusedProperty().addListener((s, ov, nv) -> setFocusedSquare(nv ? this : null));
addEventHandler(MouseEvent.MOUSE_ENTERED, event -> setFocusedSquare(ColorSquare.this));
addEventHandler(MouseEvent.MOUSE_EXITED, event -> setFocusedSquare(null));
addEventHandler(MouseEvent.MOUSE_RELEASED, event -> {
if (!dragDetected && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1) {
if (!isEmpty) {
Color fill = (Color) rectangle.getFill();
colorPicker.setValue(fill);
colorPicker.fireEvent(new ActionEvent());
updateSelection(fill);
event.consume();
}
colorPicker.hide();
}
});
}
rectangle = new Rectangle(SQUARE_SIZE, SQUARE_SIZE);
if (color == null) {
rectangle.setFill(Color.WHITE);
isEmpty = true;
} else {
rectangle.setFill(color);
}
rectangle.setStrokeType(StrokeType.INSIDE);
String tooltipStr = JFXNodeUtils.colorToHex(color);
Tooltip.install(this, new Tooltip((tooltipStr == null) ? "" : tooltipStr.toUpperCase()));
rectangle.getStyleClass().add("color-rect");
getChildren().add(rectangle);
}
public void selectColor(KeyEvent event) {
if (rectangle.getFill() != null) {
if (rectangle.getFill() instanceof Color) {
colorPicker.setValue((Color) rectangle.getFill());
colorPicker.fireEvent(new ActionEvent());
}
event.consume();
}
colorPicker.hide();
}
}
// The skin can update selection if colorpicker value changes..
public void updateSelection(Color color) {
setFocusedSquare(null);
for (ColorSquare c : colorPickerGrid.getSquares()) {
if (c.rectangle.getFill().equals(color)) {
setFocusedSquare(c);
return;
}
}
// check custom colors
for (Node n : customColorGrid.getChildren()) {
ColorSquare c = (ColorSquare) n;
if (c.rectangle.getFill().equals(color)) {
setFocusedSquare(c);
return;
}
}
}
class JFXColorGrid extends GridPane {
private final List squares;
public JFXColorGrid() {
getStyleClass().add("color-picker-grid");
setId("ColorCustomizerColorGrid");
int columnIndex = 0;
int rowIndex = 0;
squares = FXCollections.observableArrayList();
final int numColors = RAW_VALUES.length / 3;
Color[] colors = new Color[numColors];
for (int i = 0; i < numColors; i++) {
colors[i] = new Color(RAW_VALUES[i * 3] / 255,
RAW_VALUES[(i * 3) + 1] / 255, RAW_VALUES[(i * 3) + 2] / 255,
1.0);
ColorSquare cs = new ColorSquare(colors[i], i);
squares.add(cs);
}
for (ColorSquare square : squares) {
add(square, columnIndex, rowIndex);
columnIndex++;
if (columnIndex == NUM_OF_COLUMNS) {
columnIndex = 0;
rowIndex++;
}
}
setOnMouseDragged(t -> {
if (!dragDetected) {
dragDetected = true;
mouseDragColor = colorPicker.getValue();
}
int xIndex = clamp(0,
(int) t.getX() / (SQUARE_SIZE + 1), NUM_OF_COLUMNS - 1);
int yIndex = clamp(0,
(int) t.getY() / (SQUARE_SIZE + 1), NUM_OF_ROWS - 1);
int index = xIndex + yIndex * NUM_OF_COLUMNS;
colorPicker.setValue((Color) squares.get(index).rectangle.getFill());
updateSelection(colorPicker.getValue());
});
addEventHandler(MouseEvent.MOUSE_RELEASED, t -> {
if (colorPickerGrid.getBoundsInLocal().contains(t.getX(), t.getY())) {
updateSelection(colorPicker.getValue());
colorPicker.fireEvent(new ActionEvent());
colorPicker.hide();
} else {
// restore color as mouse release happened outside the grid.
if (mouseDragColor != null) {
colorPicker.setValue(mouseDragColor);
updateSelection(mouseDragColor);
}
}
dragDetected = false;
});
}
public List getSquares() {
return squares;
}
@Override
protected double computePrefWidth(double height) {
return (SQUARE_SIZE + 1) * NUM_OF_COLUMNS;
}
@Override
protected double computePrefHeight(double width) {
return (SQUARE_SIZE + 1) * NUM_OF_ROWS;
}
}
private static final int NUM_OF_COLUMNS = 10;
private static double[] RAW_VALUES = {
// WARNING: always make sure the number of colors is a divisable by NUM_OF_COLUMNS
250, 250, 250, // first row
245, 245, 245,
238, 238, 238,
224, 224, 224,
189, 189, 189,
158, 158, 158,
117, 117, 117,
97, 97, 97,
66, 66, 66,
33, 33, 33,
// second row
236, 239, 241,
207, 216, 220,
176, 190, 197,
144, 164, 174,
120, 144, 156,
96, 125, 139,
84, 110, 122,
69, 90, 100,
55, 71, 79,
38, 50, 56,
// third row
255, 235, 238,
255, 205, 210,
239, 154, 154,
229, 115, 115,
239, 83, 80,
244, 67, 54,
229, 57, 53,
211, 47, 47,
198, 40, 40,
183, 28, 28,
// forth row
252, 228, 236,
248, 187, 208,
244, 143, 177,
240, 98, 146,
236, 64, 122,
233, 30, 99,
216, 27, 96,
194, 24, 91,
173, 20, 87,
136, 14, 79,
// fifth row
243, 229, 245,
225, 190, 231,
206, 147, 216,
186, 104, 200,
171, 71, 188,
156, 39, 176,
142, 36, 170,
123, 31, 162,
106, 27, 154,
74, 20, 140,
// sixth row
237, 231, 246,
209, 196, 233,
179, 157, 219,
149, 117, 205,
126, 87, 194,
103, 58, 183,
94, 53, 177,
81, 45, 168,
69, 39, 160,
49, 27, 146,
// seventh row
232, 234, 246,
197, 202, 233,
159, 168, 218,
121, 134, 203,
92, 107, 192,
63, 81, 181,
57, 73, 171,
48, 63, 159,
40, 53, 147,
26, 35, 126,
// eigth row
227, 242, 253,
187, 222, 251,
144, 202, 249,
100, 181, 246,
66, 165, 245,
33, 150, 243,
30, 136, 229,
25, 118, 210,
21, 101, 192,
13, 71, 161,
// ninth row
225, 245, 254,
179, 229, 252,
129, 212, 250,
79, 195, 247,
41, 182, 246,
3, 169, 244,
3, 155, 229,
2, 136, 209,
2, 119, 189,
1, 87, 155,
// tenth row
224, 247, 250,
178, 235, 242,
128, 222, 234,
77, 208, 225,
38, 198, 218,
0, 188, 212,
0, 172, 193,
0, 151, 167,
0, 131, 143,
0, 96, 100,
// eleventh row
224, 242, 241,
178, 223, 219,
128, 203, 196,
77, 182, 172,
38, 166, 154,
0, 150, 136,
0, 137, 123,
0, 121, 107,
0, 105, 92,
0, 77, 64,
// twelfth row
232, 245, 233,
200, 230, 201,
165, 214, 167,
129, 199, 132,
102, 187, 106,
76, 175, 80,
67, 160, 71,
56, 142, 60,
46, 125, 50,
27, 94, 32,
// thirteenth row
241, 248, 233,
220, 237, 200,
197, 225, 165,
174, 213, 129,
156, 204, 101,
139, 195, 74,
124, 179, 66,
104, 159, 56,
85, 139, 47,
51, 105, 30,
// fourteenth row
249, 251, 231,
240, 244, 195,
230, 238, 156,
220, 231, 117,
212, 225, 87,
205, 220, 57,
192, 202, 51,
175, 180, 43,
158, 157, 36,
130, 119, 23,
// fifteenth row
255, 253, 231,
255, 249, 196,
255, 245, 157,
255, 241, 118,
255, 238, 88,
255, 235, 59,
253, 216, 53,
251, 192, 45,
249, 168, 37,
245, 127, 23,
// sixteenth row
255, 248, 225,
255, 236, 179,
255, 224, 130,
255, 213, 79,
255, 202, 40,
255, 193, 7,
255, 179, 0,
255, 160, 0,
255, 143, 0,
255, 111, 0,
// seventeenth row
255, 243, 224,
255, 224, 178,
255, 204, 128,
255, 183, 77,
255, 167, 38,
255, 152, 0,
251, 140, 0,
245, 124, 0,
239, 108, 0,
230, 81, 0,
// eighteenth row
251, 233, 231,
255, 204, 188,
255, 171, 145,
255, 138, 101,
255, 112, 67,
255, 87, 34,
244, 81, 30,
230, 74, 25,
216, 67, 21,
191, 54, 12,
// nineteenth row
239, 235, 233,
215, 204, 200,
188, 170, 164,
161, 136, 127,
141, 110, 99,
121, 85, 72,
109, 76, 65,
93, 64, 55,
78, 52, 46,
62, 39, 35,
};
private static final int NUM_OF_COLORS = RAW_VALUES.length / 3;
private static final int NUM_OF_ROWS = NUM_OF_COLORS / NUM_OF_COLUMNS;
private static int clamp(int min, int value, int max) {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy