/* * 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.controls.behavior.JFXColorPickerBehavior; import com.jfoenix.effects.JFXDepthManager; import com.sun.javafx.css.converters.BooleanConverter; import com.sun.javafx.scene.control.skin.ComboBoxBaseSkin; import com.sun.javafx.scene.control.skin.ComboBoxPopupControl; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.beans.binding.Bindings; import javafx.beans.value.WritableValue; import javafx.css.*; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.control.ColorPicker; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.input.MouseEvent; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.util.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; /** * @author Shadi Shaheen */ public class JFXColorPickerSkin extends ComboBoxPopupControl<Color> { private Label displayNode; private Pane pickerColorBox; private StackPane pickerColorClip; private JFXColorPalette popupContent; StyleableBooleanProperty colorLabelVisible = new SimpleStyleableBooleanProperty(StyleableProperties.COLOR_LABEL_VISIBLE, JFXColorPickerSkin.this, "colorLabelVisible", true); public JFXColorPickerSkin(final ColorPicker colorPicker) { super(colorPicker, new JFXColorPickerBehavior(colorPicker)); // create displayNode displayNode = new Label(); displayNode.getStyleClass().add("color-picker-label"); displayNode.setManaged(false); displayNode.setMouseTransparent(true); // label graphic pickerColorBox = new Pane(); pickerColorBox.getStyleClass().add("picker-color"); pickerColorBox.setBackground(new Background(new BackgroundFill(Color.valueOf("#fafafa"), new CornerRadii(3), Insets.EMPTY))); pickerColorClip = new StackPane(); pickerColorClip.backgroundProperty().bind(Bindings.createObjectBinding(() -> { return new Background(new BackgroundFill(Color.WHITE, pickerColorBox.backgroundProperty() .get() != null ? pickerColorBox.getBackground() .getFills() .get(0) .getRadii() : new CornerRadii( 3), pickerColorBox.backgroundProperty() .get() != null ? pickerColorBox.getBackground() .getFills() .get(0) .getInsets() : Insets.EMPTY)); }, pickerColorBox.backgroundProperty())); pickerColorBox.setClip(pickerColorClip); JFXButton button = new JFXButton(""); button.ripplerFillProperty().bind(displayNode.textFillProperty()); button.minWidthProperty().bind(pickerColorBox.widthProperty()); button.minHeightProperty().bind(pickerColorBox.heightProperty()); button.addEventHandler(MouseEvent.ANY, (event) -> { if (!event.isConsumed()) { event.consume(); getSkinnable().fireEvent(event); } }); pickerColorBox.getChildren().add(button); updateColor(); getChildren().add(pickerColorBox); getChildren().remove(arrowButton); JFXDepthManager.setDepth(getSkinnable(), 1); // to improve the performance on 1st click getPopupContent(); // add listeners registerChangeListener(colorPicker.valueProperty(), "VALUE"); colorLabelVisible.addListener(invalidate -> { if (displayNode != null) { if (colorLabelVisible.get()) { displayNode.setText(colorDisplayName(getSkinnable().getValue())); } else { displayNode.setText(""); } } }); } @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { if (!colorLabelVisible.get()) { return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset); } String displayNodeText = displayNode.getText(); double width = 0; displayNode.setText("#00000000"); width = Math.max(width, super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset)); displayNode.setText(displayNodeText); return width; } static String colorDisplayName(Color c) { if (c != null) { return formatHexString(c); } return null; } static String tooltipString(Color c) { if (c != null) { return formatHexString(c); } return null; } static String formatHexString(Color c) { if (c != null) { return String.format((Locale) null, "#%02x%02x%02x", Math.round(c.getRed() * 255), Math.round(c.getGreen() * 255), Math.round(c.getBlue() * 255)).toUpperCase(); } else { return null; } } @Override protected Node getPopupContent() { if (popupContent == null) { popupContent = new JFXColorPalette((ColorPicker) getSkinnable()); popupContent.setPopupControl(getPopup()); } return popupContent; } @Override protected void focusLost() { } @Override public void show() { super.show(); final ColorPicker colorPicker = (ColorPicker) getSkinnable(); popupContent.updateSelection(colorPicker.getValue()); } @Override protected void handleControlPropertyChanged(String p) { super.handleControlPropertyChanged(p); if ("SHOWING".equals(p)) { if (getSkinnable().isShowing()) { show(); } else if (!popupContent.isCustomColorDialogShowing()) { hide(); } } else if ("VALUE".equals(p)) { // change the selected color updateColor(); } } @Override public Node getDisplayNode() { return displayNode; } private void updateColor() { final ColorPicker colorPicker = (ColorPicker) getSkinnable(); // update picker box color Circle ColorCircle = new Circle(); ColorCircle.setFill(colorPicker.getValue()); ColorCircle.setLayoutX(pickerColorBox.getWidth() / 4); ColorCircle.setLayoutY(pickerColorBox.getHeight() / 2); pickerColorBox.getChildren().add(ColorCircle); Timeline animateColor = new Timeline(new KeyFrame(Duration.millis(240), new KeyValue(ColorCircle.radiusProperty(), 200, Interpolator.EASE_BOTH))); animateColor.setOnFinished((finish) -> { pickerColorBox.setBackground(new Background(new BackgroundFill(ColorCircle.getFill(), pickerColorBox.getBackground() .getFills() .get(0) .getRadii(), pickerColorBox.getBackground() .getFills() .get(0) .getInsets()))); pickerColorBox.getChildren().remove(ColorCircle); }); animateColor.play(); // update label color displayNode.setTextFill(colorPicker.getValue().grayscale().getRed() < 0.5 ? Color.valueOf( "rgba(255, 255, 255, 0.87)") : Color.valueOf("rgba(0, 0, 0, 0.87)")); if (colorLabelVisible.get()) { displayNode.setText(colorDisplayName(colorPicker.getValue())); } else { displayNode.setText(""); } } public void syncWithAutoUpdate() { if (!getPopup().isShowing() && getSkinnable().isShowing()) { // Popup was dismissed. Maybe user clicked outside or typed ESCAPE. // Make sure JFXColorPickerUI button is in sync. getSkinnable().hide(); } } @Override protected void layoutChildren(final double x, final double y, final double w, final double h) { pickerColorBox.resizeRelocate(x - 1, y - 1, w + 2, h + 2); pickerColorClip.resize(w + 2, h + 2); super.layoutChildren(x, y, w, h); } /*************************************************************************** * * * Stylesheet Handling * * * **************************************************************************/ private static class StyleableProperties { private static final CssMetaData<ColorPicker, Boolean> COLOR_LABEL_VISIBLE = new CssMetaData<ColorPicker, Boolean>("-fx-color-label-visible", BooleanConverter.getInstance(), Boolean.TRUE) { @Override public boolean isSettable(ColorPicker n) { final JFXColorPickerSkin skin = (JFXColorPickerSkin) n.getSkin(); return skin.colorLabelVisible == null || !skin.colorLabelVisible.isBound(); } @Override public StyleableProperty<Boolean> getStyleableProperty(ColorPicker n) { final JFXColorPickerSkin skin = (JFXColorPickerSkin) n.getSkin(); return skin.colorLabelVisible; } }; private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; static { final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(ComboBoxBaseSkin.getClassCssMetaData()); styleables.add(COLOR_LABEL_VISIBLE); STYLEABLES = Collections.unmodifiableList(styleables); } } public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { return StyleableProperties.STYLEABLES; } @Override public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { return getClassCssMetaData(); } protected TextField getEditor() { return null; } protected javafx.util.StringConverter<Color> getConverter() { return null; } }