All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jfoenix.validation.ValidationFacade Maven / Gradle / Ivy

There is a newer version: 9.0.10
Show newest version
/*
 * 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.validation;

import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.validation.base.ValidatorBase;
import javafx.animation.Animation.Status;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.util.Duration;

public class ValidationFacade extends VBox {

    /**
     * Initialize the style class to 'validation-facade'.
     * 

* This is the selector class from which CSS can be used to style * this control. */ private static final String DEFAULT_STYLE_CLASS = "validation-facade"; private Label errorLabel; private StackPane errorIcon; private HBox errorContainer; private double oldErrorLabelHeight = -1; private double initYlayout = -1; private double initHeight = -1; private boolean errorShown = false; private double currentFieldHeight = -1; private double errorLabelInitHeight = 0; private boolean heightChanged = false; private boolean disableAnimation = false; private Timeline hideErrorAnimation; public ValidationFacade() { getStyleClass().add(DEFAULT_STYLE_CLASS); setPadding(new Insets(0, 0, 0, 0)); setSpacing(0); errorLabel = new Label(); errorLabel.getStyleClass().add("error-label"); errorLabel.setWrapText(true); StackPane errorLabelContainer = new StackPane(); errorLabelContainer.getChildren().add(errorLabel); StackPane.setAlignment(errorLabel, Pos.CENTER_LEFT); errorIcon = new StackPane(); errorContainer = new HBox(); errorContainer.setAlignment(Pos.TOP_LEFT); errorContainer.getChildren().add(errorLabelContainer); errorContainer.getChildren().add(errorIcon); HBox.setHgrow(errorLabelContainer, Priority.ALWAYS); errorLabelContainer.setMaxWidth(Double.MAX_VALUE); errorIcon.setTranslateY(5); errorContainer.setSpacing(10); errorContainer.setVisible(false); errorContainer.setOpacity(0); // add listeners to show error label errorLabel.heightProperty().addListener((o, oldVal, newVal) -> { if (errorShown) { if (oldErrorLabelHeight == -1) { oldErrorLabelHeight = errorLabelInitHeight = oldVal.doubleValue(); } heightChanged = true; double newHeight = getHeight() - oldErrorLabelHeight + newVal.doubleValue(); // show the error Timeline errorAnimation = new Timeline(new KeyFrame(Duration.ZERO, new KeyValue(minHeightProperty(), currentFieldHeight, Interpolator.EASE_BOTH)), new KeyFrame(Duration.millis(160), // text pane animation new KeyValue(translateYProperty(), (initYlayout + getMaxHeight() / 2) - newHeight / 2, Interpolator.EASE_BOTH), // animate the height change effect new KeyValue(minHeightProperty(), newHeight, Interpolator.EASE_BOTH))); errorAnimation.play(); // show the error label when finished errorAnimation.setOnFinished(finish -> new Timeline(new KeyFrame(Duration.millis(160), new KeyValue(errorContainer.opacityProperty(), 1, Interpolator.EASE_BOTH))).play()); currentFieldHeight = newHeight; oldErrorLabelHeight = newVal.doubleValue(); } }); errorContainer.visibleProperty().addListener((o, oldVal, newVal) -> { // show the error label if it's not shown new Timeline(new KeyFrame(Duration.millis(160), new KeyValue(errorContainer.opacityProperty(), 1, Interpolator.EASE_BOTH))).play(); }); activeValidatorProperty().addListener((o, oldVal, newVal) -> { if (!isDisableAnimation()) { if (hideErrorAnimation != null && hideErrorAnimation.getStatus() == Status.RUNNING) { hideErrorAnimation.stop(); } if (newVal != null) { hideErrorAnimation = new Timeline(new KeyFrame(Duration.millis(160), new KeyValue(errorContainer.opacityProperty(), 0, Interpolator.EASE_BOTH))); hideErrorAnimation.setOnFinished(finish -> { JFXUtilities.runInFX(() -> showError(newVal)); }); hideErrorAnimation.play(); } else { JFXUtilities.runInFX(this::hideError); } } else { if (newVal != null) { JFXUtilities.runInFXAndWait(() -> showError(newVal)); } else { JFXUtilities.runInFXAndWait(this::hideError); } } }); } /*************************************************************************** * * Properties * * **************************************************************************/ /** * holds the current active validator on the text field in case of * validation error */ private ReadOnlyObjectWrapper activeValidator = new ReadOnlyObjectWrapper<>(); public ValidatorBase getActiveValidator() { return activeValidator == null ? null : activeValidator.get(); } public ReadOnlyObjectProperty activeValidatorProperty() { return this.activeValidator.getReadOnlyProperty(); } /** * list of validators that will validate the text value upon calling { * {@link #validate()} */ private ObservableList validators = FXCollections.observableArrayList(); public ObservableList getValidators() { return validators; } public void setValidators(ValidatorBase... validators) { this.validators.addAll(validators); } /** * validates the text value using the list of validators provided by the * user {{@link #setValidators(ValidatorBase...)} * * @return true if the value is valid else false */ public static boolean validate(Control control) { ValidationFacade facade = (ValidationFacade) control.getParent(); for (ValidatorBase validator : facade.validators) { if (validator.getSrcControl() == null) { validator.setSrcControl(facade.controlProperty.get()); } validator.validate(); if (validator.getHasErrors()) { facade.activeValidator.set(validator); control.pseudoClassStateChanged(PSEUDO_CLASS_ERROR, true); return false; } } control.pseudoClassStateChanged(PSEUDO_CLASS_ERROR, false); facade.activeValidator.set(null); return true; } public static void reset(Control control) { ValidationFacade facade = (ValidationFacade) control.getParent(); control.pseudoClassStateChanged(PSEUDO_CLASS_ERROR, false); facade.activeValidator.set(null); } private ObjectProperty controlProperty = new SimpleObjectProperty<>(); public Control getControl() { return controlProperty.get(); } public void setControl(Control control) { maxWidthProperty().bind(control.maxWidthProperty()); prefWidthProperty().bind(control.prefWidthProperty()); prefHeightProperty().bind(control.prefHeightProperty()); errorContainer.setMaxWidth(control.getMaxWidth() > -1 ? control.getMaxWidth() : control.getPrefWidth()); errorContainer.prefWidthProperty().bind(control.widthProperty()); errorContainer.prefHeightProperty().bind(control.heightProperty()); getChildren().clear(); getChildren().add(control); getChildren().add(errorContainer); this.controlProperty.set(control); } private void showError(ValidatorBase validator) { // set text in error label errorLabel.setText(validator.getMessage()); // show error icon Node awsomeIcon = validator.getIcon(); errorIcon.getChildren().clear(); if (awsomeIcon != null) { errorIcon.getChildren().add(awsomeIcon); StackPane.setAlignment(awsomeIcon, Pos.TOP_RIGHT); } // init only once, to fix the text pane from resizing if (initYlayout == -1) { initYlayout = getBoundsInParent().getMinY(); initHeight = getHeight(); currentFieldHeight = initHeight; } errorContainer.setVisible(true); errorShown = true; } private void hideError() { if (heightChanged) { new Timeline(new KeyFrame(Duration.millis(160), new KeyValue(translateYProperty(), 0, Interpolator.EASE_BOTH))).play(); // reset the height of text field new Timeline(new KeyFrame(Duration.millis(160), new KeyValue(minHeightProperty(), initHeight, Interpolator.EASE_BOTH))).play(); heightChanged = false; } // clear error label text errorLabel.setText(null); oldErrorLabelHeight = errorLabelInitHeight; // clear error icon errorIcon.getChildren().clear(); // reset the height of the text field currentFieldHeight = initHeight; // hide error container errorContainer.setVisible(false); errorShown = false; } public boolean isDisableAnimation() { return disableAnimation; } public void setDisableAnimation(boolean disableAnimation) { this.disableAnimation = disableAnimation; } /** * this style class will be activated when a validation error occurs */ private static final PseudoClass PSEUDO_CLASS_ERROR = PseudoClass.getPseudoClass("error"); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy