All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dua3.utility.fx.controls.InputControl Maven / Gradle / Ivy

There is a newer version: 15.0.2
Show newest version
package com.dua3.utility.fx.controls;

import org.jspecify.annotations.Nullable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.stage.FileChooser;
import javafx.util.StringConverter;

import java.nio.file.Path;
import java.text.NumberFormat;
import java.util.Collection;
import java.util.Locale;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

/**
 * Interface for an input field.
 *
 * @param  the input result type
 */
public interface InputControl {
    /**
     * Creates a {@link SimpleInputControl} for a TextField with String input.
     *
     * @param dflt a {@link Supplier} providing the default value for the TextField
     * @param validate a {@link Function} that takes a String and returns an Optional containing a validation error message, if any
     * @return a {@link SimpleInputControl} containing the TextField and associated properties
     */
    static SimpleInputControl stringInput(Supplier dflt, Function> validate) {
        TextField control = new TextField();
        StringProperty value = control.textProperty();
        return new SimpleInputControl<>(control, value, dflt, validate);
    }

    /**
     * Creates a new {@link SimpleInputControl} for a {@link TextField} with bidirectional binding.
     *
     * @param dflt      The supplier providing the default value.
     * @param validate  A function to validate the value, returning an optional error message.
     * @param converter The StringConverter to convert between the value and its string representation.
     * @param        The type of the value.
     * @return A {@link SimpleInputControl} containing the {@link TextField} and associated properties.
     */
    static  SimpleInputControl stringInput(Supplier dflt, Function<@Nullable T, Optional> validate, StringConverter<@Nullable T> converter) {
        TextField control = new TextField();
        ObjectProperty<@Nullable T> value = new SimpleObjectProperty<>();
        Bindings.bindBidirectional(control.textProperty(), value, converter);
        return new SimpleInputControl<>(control, value, dflt, validate);
    }

    /**
     * Creates a {@link SimpleInputControl} for integer values.
     *
     * @param dflt the default value {@link Supplier}
     * @param validate the {@link Function} to validate the integer input
     * @return a {@link SimpleInputControl} for integer input
     */
    static SimpleInputControl integerInput(Supplier<@Nullable Integer> dflt, Function<@Nullable Integer, Optional> validate) {
        TextField control = new TextField();
        StringProperty textProperty = control.textProperty();
        IntegerProperty value = new SimpleIntegerProperty();
        textProperty.bindBidirectional(value, NumberFormat.getIntegerInstance(Locale.getDefault()));
        return new SimpleInputControl<>(control, value.asObject(), dflt, validate);
    }

    /**
     * Creates a {@link SimpleInputControl} for decimal input using a {@link TextField}.
     *
     * @param dflt the {@link Supplier} providing the default value for the input
     * @param validate the {@link Function} to validate the input value
     * @return a {@link SimpleInputControl} that manages a TextField for Decimal input
     */
    static SimpleInputControl decimalInput(Supplier<@Nullable Double> dflt, Function<@Nullable Double, Optional> validate) {
        TextField control = new TextField();
        StringProperty textProperty = control.textProperty();
        DoubleProperty value = new SimpleDoubleProperty();
        textProperty.bindBidirectional(value, NumberFormat.getInstance(Locale.getDefault()));
        return new SimpleInputControl<>(control, value.asObject(), dflt, validate);
    }

    /**
     * Creates a {@link SimpleInputControl} for a {@link CheckBox} with a default value, text, and validation function.
     *
     * @param dflt a {@link Supplier} providing the default Boolean value
     * @param text the text to be displayed with the {@link CheckBox}
     * @param validate a {@link Function} that takes a Boolean value and returns an {@link Optional} containing an error message if validation fails
     * @return a new instance of {@link SimpleInputControl} configured with a {@link CheckBox} and the provided parameters
     */
    static SimpleInputControl checkBoxInput(Supplier<@Nullable Boolean> dflt, String text, Function<@Nullable Boolean, Optional> validate) {
        CheckBox control = new CheckBox(text);
        BooleanProperty value = control.selectedProperty();
        return new SimpleInputControl<>(control, value.asObject(), dflt, validate);
    }

    /**
     * Creates a {@link ComboBox} input control with specified choices, default value {@link Supplier}, and validation {@link Function}.
     *
     * @param  the type of the items in the {@link ComboBox}
     * @param choices the collection of available choices for the {@link ComboBox}
     * @param dflt a {@link Supplier} providing the default value
     * @param validate a {@link Function} to validate the selected item which returns an optional error message
     * @return a {@link SimpleInputControl} containing the ComboBox and its value property
     */
    static  SimpleInputControl, T> comboBoxInput(Collection choices, Supplier dflt, Function> validate) {
        ComboBox control = new ComboBox<>(FXCollections.observableArrayList(choices));
        Property value = control.valueProperty();
        return new SimpleInputControl<>(control, value, dflt, validate);
    }

    /**
     * Creates a new {@code SimpleInputControl} for a {@link ComboBoxEx} with the specified parameters.
     *
     * @param       the type of items contained in the {@link ComboBoxEx}
     * @param choices  the collection of choices to populate the {@link ComboBoxEx}
     * @param dflt     a {@link Supplier} for the default value
     * @param edit     a {@link UnaryOperator} to perform editing on the selected item (nullable)
     * @param add      a {@link Supplier} to provide a new item to add (nullable)
     * @param remove   a {@link BiPredicate} to determine if an item should be removed (nullable)
     * @param format   a {@link Function} to format the items as strings
     * @param validate a {@link Function} to validate the current value
     * @return a new instance of {@code SimpleInputControl} configured with a {@link ComboBoxEx} and its value property
     */
    static  SimpleInputControl, T> comboBoxExInput(
            Collection choices,
            Supplier dflt,
            @Nullable UnaryOperator edit,
            @Nullable Supplier add,
            @Nullable BiPredicate, T> remove,
            Function format,
            Function> validate) {
        ComboBoxEx control = new ComboBoxEx<>(edit, add, remove, format, FXCollections.observableArrayList(choices));
        Property value = control.valueProperty();
        return new SimpleInputControl<>(control, value, dflt, validate);
    }

    /**
     * Provides a file chooser input control.
     *
     * @param dflt          a {@link Supplier} providing the default file path.
     * @param mode          the {@link FileDialogMode} of the dialog (e.g., OPEN, SAVE, DIRECTORY).
     * @param existingOnly  specifies if only existing files can be chosen.
     * @param filters       a {@link Collection} of {@link javafx.stage.FileChooser.ExtensionFilter} to apply.
     * @param validate      a {@link Function} that validates the selected file path.
     * @return An {@code InputControl} instance for file selection.
     */
    static InputControl chooseFile(Supplier dflt, FileDialogMode mode, boolean existingOnly, Collection filters,
                                         Function> validate) {
        return new FileInput(mode, existingOnly, dflt, filters, validate);
    }

    /**
     * Get the {@link Node} for this input element.
     *
     * @return the node
     */
    Node node();

    /**
     * Get value.
     *
     * @return the current value
     */
    default T get() {
        return valueProperty().getValue();
    }

    /**
     * Provides the property representing the value of this input control.
     *
     * @return the property containing the current value
     */
    Property<@Nullable T> valueProperty();

    /**
     * Set value.
     *
     * @param arg the value to set
     */
    default void set(@Nullable T arg) {
        valueProperty().setValue(arg);
    }

    /**
     * Test if content is valid.
     * @return true, if content is valid
     */
    default boolean isValid() {
        return validProperty().get();
    }

    /**
     * Set/update control state.
     */
    default void init() {
        // nop
    }

    /**
     * Reset value to default
     */
    void reset();

    /**
     * Provides a read-only property representing the validity of the input.
     *
     * @return a ReadOnlyBooleanProperty that is true if the input is valid and false otherwise
     */
    ReadOnlyBooleanProperty validProperty();

    /**
     * Provides a read-only property representing the error message for this input control.
     *
     * 

This property contains an error message if the input is invalid, otherwise it is empty. * * @return a ReadOnlyStringProperty containing the error message if there is a validation error, otherwise empty */ ReadOnlyStringProperty errorProperty(); /** * State class encapsulates a value, validation logic, error message, and validity state. * * @param the type of the value being managed */ class State { private final Property value; private final BooleanProperty valid = new SimpleBooleanProperty(true); private final StringProperty error = new SimpleStringProperty(""); private Supplier dflt; private Function> validate; /** * Constructs a State object with the given value. * * @param value the property representing the value managed by this State */ public State(Property value) { this(value, freeze(value)); } /** * Constructs a State object with the given value and default value supplier. * * @param value the property representing the value managed by this State * @param dflt a supplier that provides the default value for the property */ public State(Property value, Supplier dflt) { this(value, dflt, s -> Optional.empty()); } /** * Creates a supplier that always returns the current value of the given ObservableValue, * capturing its value at the moment this method is called. * * @param value the ObservableValue whose current value is to be captured * @return a Supplier that returns the captured value */ private static Supplier freeze(ObservableValue value) { final R frozen = value.getValue(); return () -> frozen; } /** * Constructs a State object with the given value, default value supplier, and validation function. * * @param value the property representing the value managed by this State * @param dflt a supplier that provides the default value for the property * @param validate a function that validates the value and returns an optional error message */ public State(Property value, Supplier dflt, Function> validate) { this.value = value; this.value.addListener((v, o, n) -> updateValidState(n)); this.dflt = dflt; this.validate = validate; this.value.addListener((v, o, n) -> updateValidState(n)); } private void updateValidState(@Nullable R r) { Optional result = validate.apply(r); valid.setValue(result.isEmpty()); error.setValue(result.orElse("")); } /** * Sets the validation function for the State. * * @param validate a function that validates the value and returns an optional error message */ public void setValidate(Function> validate) { this.validate = validate; updateValidState(valueProperty().getValue()); } /** * Returns the property representing the value managed by this State. * * @return the property representing the value */ public Property valueProperty() { return value; } /** * Provides a read-only boolean property indicating the validity state. * * @return a {@link ReadOnlyBooleanProperty} representing whether the current state is valid */ public ReadOnlyBooleanProperty validProperty() { return valid; } /** * Returns a read-only string property representing the current error message. * If the value is valid, the error message will be an empty string. * * @return ReadOnlyStringProperty representing the error message. */ public ReadOnlyStringProperty errorProperty() { return error; } /** * Sets the default value supplier for this State. * * @param dflt a supplier that provides the default value for the property */ public void setDefault(Supplier dflt) { this.dflt = dflt; } /** * Resets the state to its default value. * *

This method sets the current value of the property managed by this * state to the default value supplied during the creation of the state. */ public void reset() { value.setValue(dflt.get()); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy