Add PasswordTextField
This commit is contained in:
parent
d521f8ac4a
commit
5e8a4d3547
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
- (Base) New `DeckPane` component with swipe and slide transition support.
|
- (Base) New `DeckPane` component with swipe and slide transition support.
|
||||||
- (Base) New `MaskTextField` (and `MaskTextFormatter`) component to support masked text input.
|
- (Base) New `MaskTextField` (and `MaskTextFormatter`) component to support masked text input.
|
||||||
|
- (Base) New `PasswordTextField` component to simplify `PasswordTextFormatter` usage.
|
||||||
- (CSS) 🚀 New MacOS-like Cupertino theme in light and dark variants.
|
- (CSS) 🚀 New MacOS-like Cupertino theme in light and dark variants.
|
||||||
- (CSS) 🚀 New [Dracula](https://ui.draculatheme.com/) theme.
|
- (CSS) 🚀 New [Dracula](https://ui.draculatheme.com/) theme.
|
||||||
- (CSS) New `TabPane` style. There are three styles supported: default, floating and classic (new one).
|
- (CSS) New `TabPane` style. There are three styles supported: default, floating and classic (new one).
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code 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 General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package atlantafx.base.controls;
|
||||||
|
|
||||||
|
import atlantafx.base.util.PasswordTextFormatter;
|
||||||
|
import javafx.beans.NamedArg;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyStringProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience wrapper for instantiating a {@link CustomTextField}
|
||||||
|
* with {@code PasswordTextFormatter}. For additional info refer to the {@link PasswordTextFormatter}
|
||||||
|
* docs.
|
||||||
|
*/
|
||||||
|
public class PasswordTextField extends CustomTextField {
|
||||||
|
|
||||||
|
protected final PasswordTextFormatter formatter;
|
||||||
|
|
||||||
|
public PasswordTextField(@NamedArg("text") String text) {
|
||||||
|
this(text, PasswordTextFormatter.BULLET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PasswordTextField(@NamedArg("text") String text, @NamedArg("bullet") char bullet) {
|
||||||
|
super(text);
|
||||||
|
this.formatter = PasswordTextFormatter.create(this, bullet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link PasswordTextFormatter#passwordProperty()}.
|
||||||
|
*/
|
||||||
|
public ReadOnlyStringProperty passwordProperty() {
|
||||||
|
return formatter.passwordProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link PasswordTextFormatter#getPassword()}.
|
||||||
|
*/
|
||||||
|
public String getPassword() {
|
||||||
|
return formatter.getPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link PasswordTextFormatter#revealPasswordProperty()}.
|
||||||
|
*/
|
||||||
|
public BooleanProperty revealPasswordProperty() {
|
||||||
|
return formatter.revealPasswordProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link PasswordTextFormatter#isRevealPassword()}.
|
||||||
|
*/
|
||||||
|
public boolean isRevealPassword() {
|
||||||
|
return formatter.isRevealPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link PasswordTextFormatter#setRevealPassword(boolean)}.
|
||||||
|
*/
|
||||||
|
public void setRevealPassword(boolean reveal) {
|
||||||
|
formatter.setRevealPassword(reveal);
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ public class PasswordTextFormatter extends TextFormatter<String> {
|
|||||||
|
|
||||||
protected PasswordTextFormatter(StringConverter<String> valueConverter,
|
protected PasswordTextFormatter(StringConverter<String> valueConverter,
|
||||||
UnaryOperator<Change> filter,
|
UnaryOperator<Change> filter,
|
||||||
TextField textField,
|
TextField field,
|
||||||
char bullet) {
|
char bullet) {
|
||||||
super(valueConverter, null, filter);
|
super(valueConverter, null, filter);
|
||||||
|
|
||||||
@ -32,13 +32,13 @@ public class PasswordTextFormatter extends TextFormatter<String> {
|
|||||||
if (filter == null) {
|
if (filter == null) {
|
||||||
throw new NullPointerException("UnaryOperator cannot be null!");
|
throw new NullPointerException("UnaryOperator cannot be null!");
|
||||||
}
|
}
|
||||||
if (textField == null) {
|
if (field == null) {
|
||||||
throw new NullPointerException("TextField cannot be null!");
|
throw new NullPointerException("TextField cannot be null!");
|
||||||
}
|
}
|
||||||
|
|
||||||
PasswordFilter passwordFilter = (PasswordFilter) getFilter();
|
PasswordFilter passwordFilter = (PasswordFilter) getFilter();
|
||||||
passwordFilter.setBullet(bullet);
|
passwordFilter.setBullet(bullet);
|
||||||
passwordFilter.setInitialText(textField.getText());
|
passwordFilter.setInitialText(field.getText());
|
||||||
|
|
||||||
revealPasswordProperty().addListener((obs, old, val) -> {
|
revealPasswordProperty().addListener((obs, old, val) -> {
|
||||||
if (val == null) {
|
if (val == null) {
|
||||||
@ -48,41 +48,68 @@ public class PasswordTextFormatter extends TextFormatter<String> {
|
|||||||
// Force text field update, because converter is only called on focus events by default.
|
// Force text field update, because converter is only called on focus events by default.
|
||||||
// Also, reset caret first, because otherwise its position won't be correct due to
|
// Also, reset caret first, because otherwise its position won't be correct due to
|
||||||
// #javafx-bug (https://bugs.openjdk.org/browse/JDK-8248914).
|
// #javafx-bug (https://bugs.openjdk.org/browse/JDK-8248914).
|
||||||
textField.positionCaret(0);
|
field.positionCaret(0);
|
||||||
textField.commitValue();
|
field.commitValue();
|
||||||
});
|
});
|
||||||
|
|
||||||
// force text field update on scene show
|
// force text field update on scene show
|
||||||
Platform.runLater(textField::commitValue);
|
Platform.runLater(field::commitValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always returns the unmasked password text regardless of
|
||||||
|
* the {@link #revealPasswordProperty} state.
|
||||||
|
*/
|
||||||
public ReadOnlyStringProperty passwordProperty() {
|
public ReadOnlyStringProperty passwordProperty() {
|
||||||
return ((PasswordFilter) getFilter()).password.getReadOnlyProperty();
|
return ((PasswordFilter) getFilter()).password.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link #passwordProperty()}.
|
||||||
|
*/
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return passwordProperty().get();
|
return passwordProperty().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the unmasked password text is revealed or not.
|
||||||
|
*/
|
||||||
public BooleanProperty revealPasswordProperty() {
|
public BooleanProperty revealPasswordProperty() {
|
||||||
return ((PasswordFilter) getFilter()).revealPassword;
|
return ((PasswordFilter) getFilter()).revealPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link #revealPasswordProperty}.
|
||||||
|
*/
|
||||||
public boolean isRevealPassword() {
|
public boolean isRevealPassword() {
|
||||||
return revealPasswordProperty().get();
|
return revealPasswordProperty().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link #revealPasswordProperty}.
|
||||||
|
*/
|
||||||
public void setRevealPassword(boolean reveal) {
|
public void setRevealPassword(boolean reveal) {
|
||||||
revealPasswordProperty().set(reveal);
|
revealPasswordProperty().set(reveal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Life would be easier if TextFormatter had the default constructor.
|
/**
|
||||||
public static PasswordTextFormatter create(TextField textField, char bullet) {
|
* Creates a new password text formatter with the provided mask character and
|
||||||
|
* applies itself to the specified text field.
|
||||||
|
*/
|
||||||
|
public static PasswordTextFormatter create(TextField field, char bullet) {
|
||||||
var filter = new PasswordFilter();
|
var filter = new PasswordFilter();
|
||||||
var converter = new PasswordStringConverter(filter);
|
var converter = new PasswordStringConverter(filter);
|
||||||
return new PasswordTextFormatter(converter, filter, textField, bullet);
|
|
||||||
|
var formatter = new PasswordTextFormatter(converter, filter, field, bullet);
|
||||||
|
field.setTextFormatter(formatter);
|
||||||
|
|
||||||
|
return formatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new password text formatter with the default mask character and
|
||||||
|
* applies itself to the specified text field.
|
||||||
|
*/
|
||||||
public static PasswordTextFormatter create(TextField textField) {
|
public static PasswordTextFormatter create(TextField textField) {
|
||||||
return create(textField, BULLET);
|
return create(textField, BULLET);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testTextIsMaskedByDefault() {
|
public void testTextIsMaskedByDefault() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
assertEquals("+".repeat(3), field.getText());
|
assertEquals("+".repeat(3), field.getText());
|
||||||
@ -26,7 +25,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testTextCanBeRevealed() {
|
public void testTextCanBeRevealed() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
fmt.setRevealPassword(true);
|
fmt.setRevealPassword(true);
|
||||||
@ -38,7 +36,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testPrependText() {
|
public void testPrependText() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
field.insertText(0, "456");
|
field.insertText(0, "456");
|
||||||
@ -50,7 +47,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testAppendText() {
|
public void testAppendText() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
field.appendText("456");
|
field.appendText("456");
|
||||||
@ -62,7 +58,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testInsertText() {
|
public void testInsertText() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
field.insertText(2, "456");
|
field.insertText(2, "456");
|
||||||
@ -74,7 +69,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testNoInitialText() {
|
public void testNoInitialText() {
|
||||||
var field = new TextField(null);
|
var field = new TextField(null);
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
|
|
||||||
field.appendText("456");
|
field.appendText("456");
|
||||||
assertEquals("+".repeat(3), field.getText());
|
assertEquals("+".repeat(3), field.getText());
|
||||||
@ -85,7 +79,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testDeleteSomeText() {
|
public void testDeleteSomeText() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
field.deleteText(0, 2);
|
field.deleteText(0, 2);
|
||||||
@ -97,7 +90,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testDeleteAllText() {
|
public void testDeleteAllText() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
field.deleteText(0, field.getText().length());
|
field.deleteText(0, field.getText().length());
|
||||||
@ -109,7 +101,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testSetTextToNull() {
|
public void testSetTextToNull() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
field.setText(null);
|
field.setText(null);
|
||||||
@ -121,7 +112,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testReplaceSelection() {
|
public void testReplaceSelection() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
field.selectRange(1, field.getText().length());
|
field.selectRange(1, field.getText().length());
|
||||||
@ -134,7 +124,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testReplaceAll() {
|
public void testReplaceAll() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123");
|
field.setText("123");
|
||||||
|
|
||||||
field.selectRange(0, field.getText().length());
|
field.selectRange(0, field.getText().length());
|
||||||
@ -147,7 +136,6 @@ public class PasswordTextFormatterTest {
|
|||||||
public void testCanContainBullets() {
|
public void testCanContainBullets() {
|
||||||
var field = new TextField();
|
var field = new TextField();
|
||||||
var fmt = PasswordTextFormatter.create(field, '+');
|
var fmt = PasswordTextFormatter.create(field, '+');
|
||||||
field.setTextFormatter(fmt);
|
|
||||||
field.setText("123++");
|
field.setText("123++");
|
||||||
|
|
||||||
assertEquals("+".repeat(5), field.getText());
|
assertEquals("+".repeat(5), field.getText());
|
||||||
|
@ -236,7 +236,7 @@ class Sidebar extends StackPane {
|
|||||||
navLink(CheckBoxPage.NAME, CheckBoxPage.class),
|
navLink(CheckBoxPage.NAME, CheckBoxPage.class),
|
||||||
navLink(ColorPickerPage.NAME, ColorPickerPage.class),
|
navLink(ColorPickerPage.NAME, ColorPickerPage.class),
|
||||||
navLink(ComboBoxPage.NAME, ComboBoxPage.class, "ChoiceBox"),
|
navLink(ComboBoxPage.NAME, ComboBoxPage.class, "ChoiceBox"),
|
||||||
navLink(CustomTextFieldPage.NAME, CustomTextFieldPage.class, "MaskTextField"),
|
navLink(CustomTextFieldPage.NAME, CustomTextFieldPage.class, "MaskTextField", "PasswordTextField"),
|
||||||
navLink(DatePickerPage.NAME, DatePickerPage.class),
|
navLink(DatePickerPage.NAME, DatePickerPage.class),
|
||||||
navLink(DialogPage.NAME, DialogPage.class),
|
navLink(DialogPage.NAME, DialogPage.class),
|
||||||
navLink(HtmlEditorPage.NAME, HtmlEditorPage.class),
|
navLink(HtmlEditorPage.NAME, HtmlEditorPage.class),
|
||||||
|
@ -8,7 +8,7 @@ import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|||||||
|
|
||||||
import atlantafx.base.controls.CustomTextField;
|
import atlantafx.base.controls.CustomTextField;
|
||||||
import atlantafx.base.controls.MaskTextField;
|
import atlantafx.base.controls.MaskTextField;
|
||||||
import atlantafx.base.util.PasswordTextFormatter;
|
import atlantafx.base.controls.PasswordTextField;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.AbstractPage;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.SampleBlock;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
@ -89,20 +89,17 @@ public class CustomTextFieldPage extends AbstractPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock passwordSample() {
|
private SampleBlock passwordSample() {
|
||||||
var tf = new CustomTextField("qwerty");
|
var tf = new PasswordTextField("qwerty");
|
||||||
tf.setPrefWidth(PREF_WIDTH);
|
tf.setPrefWidth(PREF_WIDTH);
|
||||||
|
|
||||||
var passwordFormatter = PasswordTextFormatter.create(tf);
|
|
||||||
tf.setTextFormatter(passwordFormatter);
|
|
||||||
|
|
||||||
var icon = new FontIcon(Feather.EYE_OFF);
|
var icon = new FontIcon(Feather.EYE_OFF);
|
||||||
icon.setCursor(Cursor.HAND);
|
icon.setCursor(Cursor.HAND);
|
||||||
icon.setOnMouseClicked(e -> {
|
icon.setOnMouseClicked(e -> {
|
||||||
if (passwordFormatter.revealPasswordProperty().get()) {
|
if (tf.revealPasswordProperty().get()) {
|
||||||
passwordFormatter.revealPasswordProperty().set(false);
|
tf.revealPasswordProperty().set(false);
|
||||||
icon.setIconCode(Feather.EYE_OFF);
|
icon.setIconCode(Feather.EYE_OFF);
|
||||||
} else {
|
} else {
|
||||||
passwordFormatter.revealPasswordProperty().set(true);
|
tf.revealPasswordProperty().set(true);
|
||||||
icon.setIconCode(Feather.EYE);
|
icon.setIconCode(Feather.EYE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user