How to unmask a JavaFX PasswordField or properly mask a TextField?
Asked Answered
J

8

19

In a UI of mine, I have a PasswordField like so (urm the one at the bottom!):

Login dialog with JavaFX PasswordField

I want a user to be able to check the checkbox you see in the picture and have all "secret" password characters displayed. Not much different from the option we get from many modern password-asking UI:s floating around. However, I cannot find anything in the JavaFX API that let me do that?

If my worries hold true, then I would like to use a TextField that display the last key pressed for only half a second or until next key is pressed, and then he shall mask all previous user input. This would produce a cool animation effect that one can see sometimes in modern UI:s. However, is there a way for me to get hold of the OS dependent (I think it is OS dependent??) password echo character I should use?

If it is not possible to get that OS dependent character, then I'd be glad to use the character you see on the picture (JavaFX on a Windows 8 machine). What is the UTF-8 code point for this stranger?

Jefe answered 9/6, 2013 at 20:47 Comment(0)
S
48

> However, I cannot find anything in the JavaFX API that let me do that?

The PasswordField component does not display masked text by default. However you can use PasswordField with TextField and toggle masked/unmasked text using these components respectively. Where the unmasked text is shown by TextField, as in example demo below.

> I would like to use a TextField that display the last key pressed for only half a second or until next key is pressed, and then he shall mask all previous user input.

Since PasswordField, itself is a extended version of TextField. You can always build your own custom password textbox with properties you mentioned.

> is there a way for me to get hold of the OS dependent (I think it is OS dependent??) password echo character I should use?

Frankly did not grab what you are saying here. You can track text changes by adding change listener to PasswordField.textPrperty() and do animations, timers etc. You can override the default bullet mask by extending PasswordFieldSkin and using it through CSS -fx-skin. See the definition of bullet in its source here:

public class PasswordFieldSkin extends TextFieldSkin {
    public static final char BULLET = '\u2022';

    public PasswordFieldSkin(PasswordField passwordField) {
        super(passwordField, new PasswordFieldBehavior(passwordField));
    }

    @Override protected String maskText(String txt) {
        TextField textField = getSkinnable();

        int n = textField.getLength();
        StringBuilder passwordBuilder = new StringBuilder(n);
        for (int i=0; i<n; i++) {
            passwordBuilder.append(BULLET);
        }

        return passwordBuilder.toString();
    }
}

Finally, Here is kick off demo app of showing password characters using bindings:

@Override
public void start(Stage primaryStage) {

    // text field to show password as unmasked
    final TextField textField = new TextField();
    // Set initial state
    textField.setManaged(false);
    textField.setVisible(false);

    // Actual password field
    final PasswordField passwordField = new PasswordField();

    CheckBox checkBox = new CheckBox("Show/Hide password");

    // Bind properties. Toggle textField and passwordField
    // visibility and managability properties mutually when checkbox's state is changed.
    // Because we want to display only one component (textField or passwordField)
    // on the scene at a time.
    textField.managedProperty().bind(checkBox.selectedProperty());
    textField.visibleProperty().bind(checkBox.selectedProperty());

    passwordField.managedProperty().bind(checkBox.selectedProperty().not());
    passwordField.visibleProperty().bind(checkBox.selectedProperty().not());

    // Bind the textField and passwordField text values bidirectionally.
    textField.textProperty().bindBidirectional(passwordField.textProperty());

    VBox root = new VBox(10);
    root.getChildren().addAll(passwordField, textField, checkBox);
    Scene scene = new Scene(root, 300, 250);
    primaryStage.setTitle("Demo");
    primaryStage.setScene(scene);
    primaryStage.show();
}
Someone answered 9/6, 2013 at 21:50 Comment(4)
Just tried your ass kicking demo. It works flawlessly. Could you not please edit and update your answer with some comments on the questions I asked? If so I can check your answer as the accepted answer. Many thanx for your demo!!Jefe
@MartinAndersson. Sure. Unfortunately I am out of office for few days. I will update my answer with the comments and the reference links ASAP.Someone
wow, amazing! One of the questions you say you didn't understood is how one get the os dependent "bullet mask" the Password field uses. Well at least I think it is os dependent. Uluk, your answer is probably the best answer I have read on stackoverflow.com. Keep it up!Jefe
@UlukBiy Thank you very much for the binding example.Watereddown
D
7

You need create three elements:

  • TextField : the password visible field
  • PasswodField : the password not visible field
  • CheckBox : the toggle visibility field

You place the passwords fields in the same position(x, y):

<PasswordField fx:id="pass_hidden" layoutX="X" layoutY="Y" />
<TextField fx:id="pass_text" layoutX="X" layoutY="Y"/>
<CheckBox fx:id="pass_toggle" onAction="#togglevisiblePassword" .... />

Note: Replaces the value of X and Y.

Add in your controller:

@FXML
private PasswordField pass_hidden;
@FXML
private TextField pass_text;
@FXML
private CheckBox pass_toggle;

/**
 * Controls the visibility of the Password field
 * @param event
 */
@FXML
public void togglevisiblePassword(ActionEvent event) {
    if (pass_toggle.isSelected()) {
        pass_text.setText(pass_hidden.getText());
        pass_text.setVisible(true);
        pass_hidden.setVisible(false);
        return;
    }
    pass_hidden.setText(pass_text.getText());
    pass_hidden.setVisible(true);
    pass_text.setVisible(false);
}

// The controller class needs to implement Initializable to override initialize
@Override
public void initialize(URL location, ResourceBundle resources) {
    this.togglevisiblePassword(null);
}

If you want to know the value of the password you can create a method that returns it:

private String passwordValue() {
    return pass_toggle.isSelected()?
       pass_text.getText(): pass_hidden.getText();
}
Diction answered 21/7, 2017 at 22:4 Comment(2)
Thanks for the solution for fxml. The pass_hidden field needs to be declared. btn_start_stop is not used.Sair
The controller class needs to implement Initializable to override initialize.Sair
B
3

I know this is older, but i was searching for answer and this is my solution:

@FXML
private JFXButton showpassword;
private String password;

showpassword.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
            password = passwordField.getText();
            passwordField.clear();
            passwordField.setPromptText(password);
        });
        showpassword.addEventFilter(MouseEvent.MOUSE_RELEASED, e -> {
            passwordField.setText(password);
            passwordField.setPromptText("Password");
        });

Using button with graphic like "WIN10 Eye - unmask password"

Baldridge answered 20/11, 2018 at 15:3 Comment(0)
T
3

well, the password field has one property that can be set the text in bullets.. this method maskText(String txt) stays on skin.. you can replace this with a new Skin.. when you type the method maskText test if you can raplace in bullets.. use one boolean to inform.. you can reuse this code from another event. it's an example. Regards

public class Main extends Application {

@Override
public void start(Stage stage) throws Exception {
    StackPane root = new StackPane();
    root.setAlignment(Pos.CENTER);
    root.setPadding(new Insets(50));

    PasswordField passwordField = new PasswordField();
    passwordField.setSkin(new VisiblePasswordFieldSkin(passwordField));

    root.getChildren().add(passwordField);

    stage.setScene(new Scene(root, 400, 400));
    stage.show();
}


}

class VisiblePasswordFieldSkin extends TextFieldSkin {

private final Button actionButton = new Button("View");
private final SVGPath actionIcon = new SVGPath();

private boolean mask = true;

public VisiblePasswordFieldSkin(PasswordField textField) {

    super(textField);

    actionButton.setId("actionButton");
    actionButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    actionButton.setPrefSize(30,30);
    actionButton.setFocusTraversable(false);
    actionButton.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, new Insets(0))));

    getChildren().add(actionButton);
    actionButton.setCursor(Cursor.HAND);
    actionButton.toFront();

    actionIcon.setContent(Icons.VIEWER.getContent());
    actionButton.setGraphic(actionIcon);

    actionButton.setVisible(false);
    
    actionButton.setOnMouseClicked(event -> {

        if(mask) {
            actionIcon.setContent(Icons.VIEWER_OFF.getContent());
            mask = false;
        } else {
            actionIcon.setContent(Icons.VIEWER.getContent());
            mask = true;
        }
        textField.setText(textField.getText());

        textField.end();

    });

    textField.textProperty().addListener((observable, oldValue, newValue) -> actionButton.setVisible(!newValue.isEmpty()));

}

@Override
protected void layoutChildren(double x, double y, double w, double h) {
    super.layoutChildren(x, y, w, h);

    layoutInArea(actionButton, x, y, w, h,0, HPos.RIGHT, VPos.CENTER);
}

@Override
protected String maskText(String txt) {
    if (getSkinnable() instanceof PasswordField && mask) {
        int n = txt.length();
        StringBuilder passwordBuilder = new StringBuilder(n);
        for (int i = 0; i < n; i++) {
            passwordBuilder.append(BULLET);
        }
        return passwordBuilder.toString();
    } else {

        return txt;
    }
}
}

enum Icons {

VIEWER_OFF("M12 6c3.79 0 7.17 2.13 8.82 5.5-.59 1.22-1.42 2.27-2." +
        "41 3.12l1.41 1.41c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 " +
        "0-2.49.2-3.64.57l1.65 1.65C10.66 6.09 11.32 6 12 6zm-1.07 1.14L13 9.21c.57.25 1.03.71 " +
        "1.28 1.28l2.07 2.07c.08-.34.14-.7.14-1.07C16.5 9.01 14.48 7 12 7c-.37 0-.72.05-1.07." +
        "14zM2.01 3.87l2.68 2.68C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.98-.29 " +
        "4.32-.82l3.42 3.42 1.41-1.41L3.42 2.45 2.01 3.87zm7.5 7.5l2.61 2.61c-.04.01-.08.02-.12.02-1.38 " +
        "0-2.5-1.12-2.5-2.5 0-.05.01-.08.01-.13zm-3.4-3.4l1.75 1.75c-.23.55-.36 1.15-.36 1.78 0 2.48 2.02 " +
        "4.5 4.5 4.5.63 0 1.23-.13 1.77-.36l.98.98c-.88.24-1.8.38-2.75.38-3.79 0-7.17-2.13-8.82-5.5.7-1.43 1.72-2.61 2.93-3.53z"),

VIEWER("M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7." +
        "5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z");

private String content;

Icons(String content) {
    this.content = content;
}

public String getContent() {
    return content;
}
}

enter image description here enter image description here

View in GitHub

Tameka answered 23/11, 2021 at 16:6 Comment(0)
S
2

You could use a custom Tooltip to show the password, and use the Checkbox to show / hide the Tooltip.

enter image description here

The code for this demo can be found here.

Superabound answered 4/2, 2020 at 16:16 Comment(0)
C
0
 void viewpass(ActionEvent event) {




  if (checkpass.isSelected()){
 pass.setPromptText(pass.getText());
 pass.setText(""); 
  pass.setDisable(true);

  }else {
 pass .setText(pass.getPromptText());
 pass.setPromptText("");
 pass.setDisable(false);
 }






}
Contrabandist answered 22/8, 2018 at 20:26 Comment(0)
F
0

You can also do it using textfield and password field with radio button As follows.

import javafx.fxml.Initializable;
import com.jfoenix.controls.*;
import com.jfoenix.controls.JFXPasswordField;
import com.jfoenix.controls.JFXRadioButton;
import javafx.fxml.FXML;
import java.net.URL;
import java.util.ResourceBundle;

public class Controller implements Initializable{
    @FXML
    private JFXPasswordField PasswordField;
    @FXML
    private JFXRadioButton passVisible;
    @FXML
    private JFXTextField textField1;
    @Override
    public void initialize(URL location, ResourceBundle resources)
    {
textField1.textProperty().bind(PasswordField.textProperty());
textField1.visibleProperty().bind(passVisible.selectedProperty());
PasswordField.visibleProperty().bind(passVisible.selectedProperty().not());
    }
}
Flavour answered 6/7, 2021 at 10:38 Comment(0)
O
0

For a third-party library solution: Review the MaterialFX library which includes a password control with show/hide functionality.

MaterialFX

Features are (from MFXPasswordField javadoc):

implementation of a password field, a TextField which masks the given input text. Extends MFXTextField, starts with a default trailing icon which allows to show/hide the password and it's defined by the defaultTrailingIcon() method so it can be changed after instantiation or by overriding the method. Specific features:

  • Allows to change the "mask" character, even at runtime
  • Allows to show/hide the password.
  • Allows to copy the selected text (Ctrl + C)
  • Allows to cut the selected text (Ctrl + X)
  • Allows to paste the text in the clipboard to the field (Ctrl + V)
  • Allows to enable/disable copy, cut and paste at any time
  • Introduces a new PseudoClass, ":masked" that activates when the text is masked
Osage answered 20/2 at 20:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.