Make controls more FXML-friendly
This commit is contained in:
parent
2956ef7558
commit
998bd69334
@ -30,28 +30,75 @@ package atlantafx.base.controls;
|
||||
import atlantafx.base.util.MaskChar;
|
||||
import atlantafx.base.util.MaskTextFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* This is a convenience wrapper for instantiating a {@link CustomTextField}
|
||||
* with {@code MaskTextFormatter}. For additional info refer to the {@link MaskTextFormatter}
|
||||
* This is a convenience wrapper for instantiating a {@link CustomTextField} with a
|
||||
* {@code MaskTextFormatter}. For additional info refer to the {@link MaskTextFormatter}
|
||||
* docs.
|
||||
*/
|
||||
public class MaskTextField extends CustomTextField {
|
||||
|
||||
protected final MaskTextFormatter formatter;
|
||||
/**
|
||||
* The whole dancing around the editable mask property is solely due to SceneBuilder
|
||||
* not works without no-arg constructor. It requires to make formatter value mutable
|
||||
* as well, which is not really tested and never intended to be supported. Also, since
|
||||
* the formatter property is not bound to the text field formatter property, setting the
|
||||
* latter manually can lead to memory leak.
|
||||
*/
|
||||
protected final StringProperty mask = new SimpleStringProperty(this, "mask");
|
||||
|
||||
public MaskTextField(@NamedArg("text") String mask) {
|
||||
protected final ReadOnlyObjectWrapper<MaskTextFormatter> formatter =
|
||||
new ReadOnlyObjectWrapper<>(this, "formatter");
|
||||
|
||||
public MaskTextField() {
|
||||
super("");
|
||||
init();
|
||||
}
|
||||
|
||||
public MaskTextField(@NamedArg("mask") String mask) {
|
||||
this("", mask);
|
||||
}
|
||||
|
||||
public MaskTextField(@NamedArg("text") String text, @NamedArg("mask") String mask) {
|
||||
super(text);
|
||||
this.formatter = MaskTextFormatter.create(this, mask);
|
||||
private MaskTextField(@NamedArg("text") String text,
|
||||
@NamedArg("mask") String mask) {
|
||||
super(Objects.requireNonNullElse(text, ""));
|
||||
|
||||
formatter.set(MaskTextFormatter.create(this, mask));
|
||||
setMask(mask); // set mask only after creating a formatter, for validation
|
||||
init();
|
||||
}
|
||||
|
||||
public MaskTextField(String text, List<MaskChar> mask) {
|
||||
super(text);
|
||||
this.formatter = MaskTextFormatter.create(this, mask);
|
||||
super(Objects.requireNonNullElse(text, ""));
|
||||
|
||||
formatter.set(MaskTextFormatter.create(this, mask));
|
||||
setMask(null);
|
||||
init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
mask.addListener((obs, old, val) -> {
|
||||
// this will replace the current text value with placeholder mask,
|
||||
// so, neither text no prompt won't be shown in the SceneBuilder
|
||||
formatter.set(val != null ? MaskTextFormatter.create(this, val) : null);
|
||||
});
|
||||
}
|
||||
|
||||
public StringProperty maskProperty() {
|
||||
return mask;
|
||||
}
|
||||
|
||||
public @Nullable String getMask() {
|
||||
return mask.get();
|
||||
}
|
||||
|
||||
public void setMask(@Nullable String mask) {
|
||||
this.mask.set(mask);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import atlantafx.base.util.Animations;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
@ -52,7 +53,7 @@ public class ModalPane extends Control {
|
||||
* @param topViewOrder the {@link #viewOrderProperty()} value to be set
|
||||
* to display the modal pane on top of the parent container.
|
||||
*/
|
||||
public ModalPane(int topViewOrder) {
|
||||
public ModalPane(@NamedArg("topViewOrder") int topViewOrder) {
|
||||
super();
|
||||
this.topViewOrder = topViewOrder;
|
||||
}
|
||||
|
@ -30,58 +30,65 @@ package atlantafx.base.controls;
|
||||
import atlantafx.base.util.PasswordTextFormatter;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
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.
|
||||
* with {@code PasswordTextFormatter}. For additional info refer to the
|
||||
* {@link PasswordTextFormatter} docs.
|
||||
*/
|
||||
public class PasswordTextField extends CustomTextField {
|
||||
|
||||
protected final PasswordTextFormatter formatter;
|
||||
protected final ReadOnlyObjectWrapper<PasswordTextFormatter> formatter
|
||||
= new ReadOnlyObjectWrapper<>(this, "formatter");
|
||||
|
||||
public PasswordTextField() {
|
||||
this("", PasswordTextFormatter.BULLET);
|
||||
}
|
||||
|
||||
public PasswordTextField(@NamedArg("text") String text) {
|
||||
this(text, PasswordTextFormatter.BULLET);
|
||||
}
|
||||
|
||||
public PasswordTextField(@NamedArg("text") String text, @NamedArg("bullet") char bullet) {
|
||||
protected PasswordTextField(@NamedArg("text") String text,
|
||||
@NamedArg("bullet") char bullet) {
|
||||
super(text);
|
||||
this.formatter = PasswordTextFormatter.create(this, bullet);
|
||||
formatter.set(PasswordTextFormatter.create(this, bullet));
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link PasswordTextFormatter#passwordProperty()}.
|
||||
*/
|
||||
public ReadOnlyStringProperty passwordProperty() {
|
||||
return formatter.passwordProperty();
|
||||
return formatter.get().passwordProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link PasswordTextFormatter#getPassword()}.
|
||||
*/
|
||||
public String getPassword() {
|
||||
return formatter.getPassword();
|
||||
return formatter.get().getPassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link PasswordTextFormatter#revealPasswordProperty()}.
|
||||
*/
|
||||
public BooleanProperty revealPasswordProperty() {
|
||||
return formatter.revealPasswordProperty();
|
||||
return formatter.get().revealPasswordProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link PasswordTextFormatter#isRevealPassword()}.
|
||||
* See {@link PasswordTextFormatter#getRevealPassword()}.
|
||||
*/
|
||||
public boolean isRevealPassword() {
|
||||
return formatter.isRevealPassword();
|
||||
public boolean getRevealPassword() {
|
||||
return formatter.get().getRevealPassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link PasswordTextFormatter#setRevealPassword(boolean)}.
|
||||
*/
|
||||
public void setRevealPassword(boolean reveal) {
|
||||
formatter.setRevealPassword(reveal);
|
||||
formatter.get().setRevealPassword(reveal);
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import javafx.util.StringConverter;
|
||||
public class RingProgressIndicator extends ProgressIndicator {
|
||||
|
||||
public RingProgressIndicator() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RingProgressIndicator(double progress) {
|
||||
|
@ -9,6 +9,8 @@ import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.event.Event;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
@ -131,7 +133,7 @@ public class ModalBox extends AnchorPane {
|
||||
|
||||
// call user specified close handler
|
||||
if (onClose.get() != null) {
|
||||
onClose.get().run();
|
||||
onClose.get().handle(new Event(Event.ANY));
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,18 +147,18 @@ public class ModalBox extends AnchorPane {
|
||||
* handler will be executed after the default close handler. Therefore, you
|
||||
* can use it to perform arbitrary actions on dialog close.
|
||||
*/
|
||||
protected final ObjectProperty<Runnable> onClose =
|
||||
protected final ObjectProperty<EventHandler<? super Event>> onClose =
|
||||
new SimpleObjectProperty<>(this, "onClose");
|
||||
|
||||
public Runnable getOnClose() {
|
||||
public EventHandler<? super Event> getOnClose() {
|
||||
return onClose.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<Runnable> onCloseProperty() {
|
||||
public ObjectProperty<EventHandler<? super Event>> onCloseProperty() {
|
||||
return onClose;
|
||||
}
|
||||
|
||||
public void setOnClose(Runnable onClose) {
|
||||
public void setOnClose(EventHandler<? super Event> onClose) {
|
||||
this.onClose.set(onClose);
|
||||
}
|
||||
|
||||
|
@ -62,11 +62,6 @@ public class MaskTextFormatter extends TextFormatter<String> {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public MaskTextFormatter(MaskTextFilter filter, TextField field) {
|
||||
super(filter);
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Factory Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -81,7 +81,7 @@ public class PasswordTextFormatter extends TextFormatter<String> {
|
||||
/**
|
||||
* See {@link #revealPasswordProperty}.
|
||||
*/
|
||||
public boolean isRevealPassword() {
|
||||
public boolean getRevealPassword() {
|
||||
return revealPasswordProperty().get();
|
||||
}
|
||||
|
||||
|
@ -111,10 +111,10 @@ public final class CustomTextFieldPage extends OutlinePage {
|
||||
var icon = new FontIcon(Feather.EYE_OFF);
|
||||
icon.setCursor(Cursor.HAND);
|
||||
icon.setOnMouseClicked(e -> {
|
||||
icon.setIconCode(tf.isRevealPassword()
|
||||
icon.setIconCode(tf.getRevealPassword()
|
||||
? Feather.EYE_OFF : Feather.EYE
|
||||
);
|
||||
tf.setRevealPassword(!tf.isRevealPassword());
|
||||
tf.setRevealPassword(!tf.getRevealPassword());
|
||||
});
|
||||
tf.setRight(icon);
|
||||
//snippet_3:end
|
||||
|
@ -94,7 +94,7 @@ public final class ThemePage extends OutlinePage {
|
||||
sceneBuilderDialog = new Lazy<>(() -> {
|
||||
var dialog = new SceneBuilderDialog();
|
||||
dialog.setClearOnClose(true);
|
||||
dialog.setOnClose(dialog::reset);
|
||||
dialog.setOnClose(e -> dialog.reset());
|
||||
return dialog;
|
||||
});
|
||||
|
||||
|
@ -4,13 +4,18 @@ package atlantafx.sampler.page.showcase;
|
||||
|
||||
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.AS_NEEDED;
|
||||
|
||||
import atlantafx.base.controls.MaskTextField;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import atlantafx.sampler.Resources;
|
||||
import atlantafx.sampler.page.Page;
|
||||
import atlantafx.sampler.util.NodeUtils;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
@ -79,4 +84,15 @@ public final class OverviewPage extends ScrollPane implements Page {
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static class Controller implements Initializable {
|
||||
public @FXML MaskTextField phoneTf;
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
phoneTf.setText("(415) 273-91-64");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import atlantafx.base.controls.Breadcrumbs?>
|
||||
<?import atlantafx.base.controls.Calendar?>
|
||||
<?import atlantafx.base.controls.Card?>
|
||||
<?import atlantafx.base.controls.CustomTextField?>
|
||||
<?import atlantafx.base.controls.PasswordTextField?>
|
||||
<?import atlantafx.base.controls.RingProgressIndicator?>
|
||||
<?import atlantafx.base.controls.Spacer?>
|
||||
<?import atlantafx.base.controls.Tile?>
|
||||
<?import atlantafx.base.controls.ToggleSwitch?>
|
||||
<?import atlantafx.base.layout.DeckPane?>
|
||||
<?import atlantafx.base.layout.InputGroup?>
|
||||
<?import atlantafx.base.layout.ModalBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.Separator?>
|
||||
<?import javafx.scene.control.ToggleButton?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
|
||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="897.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<Label layoutX="14.0" layoutY="14.0" style="-fx-text-fill: green;" text="Breadcrumbs" />
|
||||
<Breadcrumbs layoutX="14.0" layoutY="45.0" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="45.0" prefWidth="280.0" style="-fx-border-color: red;" />
|
||||
<Calendar layoutX="14.0" layoutY="107.0" />
|
||||
<CustomTextField layoutX="14.0" layoutY="470.0" prefHeight="36.0" prefWidth="264.0" text="Text" />
|
||||
<Label layoutX="14.0" layoutY="442.0" style="-fx-text-fill: green;" text="CustomTextField" />
|
||||
<ToggleSwitch layoutX="411.0" layoutY="33.0" />
|
||||
<Separator layoutX="295.0" layoutY="15.0" orientation="VERTICAL" prefHeight="759.0" prefWidth="25.0" />
|
||||
<PasswordTextField layoutX="14.0" layoutY="546.0" prefHeight="36.0" prefWidth="264.0" text="password" />
|
||||
<Label layoutX="14.0" layoutY="517.0" style="-fx-text-fill: green;" text="PasswordTextField" />
|
||||
<RingProgressIndicator layoutX="326.0" layoutY="25.0" progress="0.35" />
|
||||
<Tile layoutX="320.0" layoutY="209.0" prefHeight="73.0" prefWidth="483.0" subTitle="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla fermentum, quam eu pretium euismod, ipsum mauris interdum massa, at scelerisque nulla augue a nunc. Vivamus vehicula rhoncus est, ut placerat nulla pellentesque vel. Duis ac mattis sapien. " title="Title" />
|
||||
<InputGroup layoutX="526.0" layoutY="57.0">
|
||||
<children>
|
||||
<ToggleButton mnemonicParsing="false" text="Toggle1" />
|
||||
<ToggleButton mnemonicParsing="false" text="Toggle2" />
|
||||
<ToggleButton layoutX="124.0" layoutY="10.0" mnemonicParsing="false" text="Toggle3" />
|
||||
</children>
|
||||
</InputGroup>
|
||||
<Label layoutX="527.0" layoutY="23.0" style="-fx-text-fill: green;" text="InputGroup" />
|
||||
<HBox alignment="CENTER_LEFT" layoutX="527.0" layoutY="137.0" prefHeight="56.0" prefWidth="280.0" style="-fx-border-color: red;">
|
||||
<children>
|
||||
<Label text="Left" />
|
||||
<Spacer prefHeight="73.0" prefWidth="219.0" />
|
||||
<Label layoutX="10.0" layoutY="10.0" text="Right" />
|
||||
</children>
|
||||
</HBox>
|
||||
<Label layoutX="526.0" layoutY="108.0" style="-fx-text-fill: green;" text="Spacer" />
|
||||
<VBox layoutX="863.0" layoutY="128.0" prefHeight="200.0" prefWidth="100.0" style="-fx-border-color: red;">
|
||||
<children>
|
||||
<Label text="Top" />
|
||||
<Spacer />
|
||||
<Label layoutX="10.0" layoutY="10.0" text="Bottom" />
|
||||
</children>
|
||||
</VBox>
|
||||
<Label layoutX="336.0" layoutY="183.0" style="-fx-text-fill: green;" text="Tile" />
|
||||
<ModalBox layoutX="326.0" layoutY="459.0" prefHeight="175.0" prefWidth="264.0" style="-fx-border-color: red;" />
|
||||
<Label layoutX="325.0" layoutY="432.0" style="-fx-text-fill: green;" text="ModalBox" />
|
||||
<Card layoutX="641.0" layoutY="457.0" prefHeight="229.0" prefWidth="309.0" styleClass="elevated-2" />
|
||||
<Label layoutX="642.0" layoutY="432.0" style="-fx-text-fill: green;" text="Card" />
|
||||
<DeckPane layoutX="326.0" layoutY="695.0" prefHeight="127.0" prefWidth="264.0" style="-fx-border-color: red;" />
|
||||
<Label layoutX="325.0" layoutY="665.0" style="-fx-text-fill: green;" text="DeckPane" />
|
||||
</children>
|
||||
</AnchorPane>
|
@ -2,6 +2,7 @@
|
||||
|
||||
<?import atlantafx.base.controls.Calendar?>
|
||||
<?import atlantafx.base.controls.CustomTextField?>
|
||||
<?import atlantafx.base.controls.MaskTextField?>
|
||||
<?import atlantafx.base.controls.PasswordTextField?>
|
||||
<?import atlantafx.base.controls.RingProgressIndicator?>
|
||||
<?import atlantafx.base.controls.Tile?>
|
||||
@ -52,7 +53,7 @@
|
||||
<?import javafx.scene.paint.Color?>
|
||||
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||
|
||||
<GridPane hgap="40.0" vgap="20.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<GridPane hgap="40.0" vgap="20.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="atlantafx.sampler.page.showcase.OverviewPage$Controller">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="NEVER" />
|
||||
<ColumnConstraints hgrow="NEVER" />
|
||||
@ -208,6 +209,7 @@
|
||||
<RowConstraints vgrow="NEVER" />
|
||||
<RowConstraints vgrow="NEVER" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<TextField promptText="Prompt" GridPane.columnIndex="1" />
|
||||
@ -223,6 +225,7 @@
|
||||
<PasswordTextField text="password" GridPane.rowIndex="2" />
|
||||
<TextField editable="false" promptText="Readonly" GridPane.rowIndex="1" />
|
||||
<TextField disable="true" promptText="Disabled" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||
<MaskTextField fx:id="phoneTf" mask="(NNN) NNN-NN-NN" GridPane.rowIndex="3" />
|
||||
</children>
|
||||
</GridPane>
|
||||
<GridPane hgap="10.0" layoutX="30.0" layoutY="30.0" vgap="10.0" GridPane.rowIndex="2">
|
||||
|
Loading…
Reference in New Issue
Block a user