As I mentioned in My First Experience with JavaFX blog, there is no password field in JavaFX, so I had to google for some workarounds. Although I found a few, none of them worked flawlessly, so last night I decided to spend some time trying to come up with a password field that would really work as expected. And, I think I managed to come up with an elegant and simple solution. No hacking in form of covering the text area with additional components or adding effects that blur the text box (including the caret and component borders). It looks and behaves exactly as you would expect of a password field. Click on the following picture to try it out:
Or click the following button for standalone mode:

And here is how it is implemented:
import javafx.scene.control.TextBox; import javafx.util.Math; /** * @author Martin Matula */ public class PasswordBox extends TextBox { public-read var password = ""; override function replaceSelection(arg) { var pos1 = Math.min(dot, mark); var pos2 = Math.max(dot, mark); password = "{password.substring(0, pos1)}{arg}{password.substring(pos2)}"; super.replaceSelection(getStars(arg.length())); } override function deleteNextChar() { if ((mark == dot) and (dot < password.length())) { password = "{password.substring(0, dot)}{password.substring(dot + 1)}"; } super.deleteNextChar(); } override function deletePreviousChar() { if ((mark == dot) and (dot > 0)) { password = "{password.substring(0, dot - 1)}{password.substring(dot)}"; } super.deletePreviousChar(); } function getStars(len: Integer): String { var result: String = ""; for (i in [1..len]) { result = "{result}*"; } result; } }
Quite simple, isn’t it? Here is how it is used in the Main class:
import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.text.Text; var password: PasswordBox; var stage: Stage = Stage { title: "PasswordBox Demo" width: 200 height: 100 scene: Scene { content: [ password = PasswordBox { translateX: 10 translateY: 10 columns: 20 }, Text { x: 10 y: 50 content: bind password.password } ] } }
I guess there is still a room for improving the API – the real password is stored in the password property, while the text property became useless and should never be set by a client. If you want to populate the field with a remembered password, you need to do it by calling replaceSelection("password") on the password field after it’s initialization (rather than setting the text or the password properties). Anyway, I wanted to keep the code simple so that you can easily see the basic idea behind it.





Posts
Aah, a nice, simple implementation, with a smart usage of function overriding. Well done!
Note, I have an alternative implementation of getStars, using the fact that a sequence of strings in an interpolated variable is made of concatenation of the strings.
super.replaceSelection(“{for (i in [ 1 .. arg.length() ]) ‘*’}”);
| September 12, 2009 @ 6:53 pm
Oh, nice – I did not know that. Thanks!
| September 12, 2009 @ 7:00 pm
Hey Martin, very cool. Only thing I’d do differently is to have PasswordBox extend TextInputControl and use a TextBox in it’s skin implementation (delegation vs inheritance). One reason is that TextBox gains the ability to be multiline in the next realease which would be odd for a password box I guess.
One other thing, does this implementation work with promptText?
| September 16, 2009 @ 5:18 pm
Hi Richard. I am glad you like it!
Yes, it does work with promptText. And thanks for the hints. Will look at it.
| September 16, 2009 @ 6:06 pm
I also believe that it should be possible to preset a password textfield with a value (“remember password”)
| September 21, 2009 @ 12:02 pm
Yes, I said in the blog, you can do this by calling replaceSelection(“remembered password”). So it works, although could be made nicer.
| September 21, 2009 @ 12:59 pm
Thank for share the code.
| October 15, 2009 @ 6:33 pm
Thanks a lot for the code. Awesome implementation. Really useful
| October 18, 2009 @ 7:19 am
perfecto… muy bueno y bonito , me gusto mucho
gracias… yo are el mio basándome en el tuyo gracias…
| January 26, 2010 @ 7:33 pm