/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.gui.components;
import io.bitsquare.gui.util.validation.InputValidator;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Effect;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.stage.Window;
import org.controlsfx.control.PopOver;
public class PasswordTextField extends PasswordField {
private final Effect invalidEffect = new DropShadow(BlurType.THREE_PASS_BOX, Color.RED, 4, 0.0, 0, 0);
private final ObjectProperty<InputValidator.ValidationResult> validationResult = new SimpleObjectProperty<>
(new InputValidator.ValidationResult(true));
private static PopOver errorMessageDisplay;
private Region layoutReference = this;
public InputValidator getValidator() {
return validator;
}
public void setValidator(InputValidator validator) {
this.validator = validator;
}
private InputValidator validator;
///////////////////////////////////////////////////////////////////////////////////////////
// Static
///////////////////////////////////////////////////////////////////////////////////////////
private static void hideErrorMessageDisplay() {
if (errorMessageDisplay != null)
errorMessageDisplay.hide();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public PasswordTextField() {
super();
validationResult.addListener((ov, oldValue, newValue) -> {
if (newValue != null) {
setEffect(newValue.isValid ? null : invalidEffect);
if (newValue.isValid)
hideErrorMessageDisplay();
else
applyErrorMessage(newValue);
}
});
sceneProperty().addListener((ov, oldValue, newValue) -> {
// we got removed from the scene so hide the popup (if open)
if (newValue == null)
hideErrorMessageDisplay();
});
focusedProperty().addListener((o, oldValue, newValue) -> {
if (oldValue && !newValue && validator != null)
validationResult.set(validator.validate(getText()));
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public void resetValidation() {
setEffect(null);
hideErrorMessageDisplay();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Setters
///////////////////////////////////////////////////////////////////////////////////////////
/**
* @param layoutReference The node used as reference for positioning. If not set explicitly the
* ValidatingTextField instance is used.
*/
public void setLayoutReference(Region layoutReference) {
this.layoutReference = layoutReference;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public ObjectProperty<InputValidator.ValidationResult> validationResultProperty() {
return validationResult;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
private void applyErrorMessage(InputValidator.ValidationResult validationResult) {
if (errorMessageDisplay != null)
errorMessageDisplay.hide();
if (!validationResult.isValid) {
createErrorPopOver(validationResult.errorMessage);
if (getScene() != null)
errorMessageDisplay.show(getScene().getWindow(), getErrorPopupPosition().getX(),
getErrorPopupPosition().getY());
if (errorMessageDisplay != null)
errorMessageDisplay.setDetached(false);
}
}
private Point2D getErrorPopupPosition() {
Window window = getScene().getWindow();
Point2D point;
point = layoutReference.localToScene(0, 0);
double x = Math.floor(point.getX() + window.getX() + layoutReference.getWidth() + 20 - getPadding().getLeft() -
getPadding().getRight());
double y = Math.floor(point.getY() + window.getY() + getHeight() / 2 - getPadding().getTop() - getPadding()
.getBottom());
return new Point2D(x, y);
}
private static void createErrorPopOver(String errorMessage) {
Label errorLabel = new Label(errorMessage);
errorLabel.setId("validation-error");
errorLabel.setPadding(new Insets(0, 10, 0, 10));
errorLabel.setOnMouseClicked(e -> hideErrorMessageDisplay());
errorMessageDisplay = new PopOver(errorLabel);
errorMessageDisplay.setDetachable(true);
errorMessageDisplay.setDetachedTitle("Close");
errorMessageDisplay.setArrowIndent(5);
}
}