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

com.dua3.utility.fx.controls.RadioPane 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.property.Property;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.VBox;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Optional;
import java.util.function.Function;


/**
 * A custom control pane that arranges radio buttons vertically.
 * This class extends VBox and implements InputControl to provide
 * selection and validation capabilities.
 *
 * @param  The type of items that will be represented as radio buttons.
 */
public class RadioPane extends VBox implements InputControl {

    protected static final Logger LOG = LogManager.getLogger(RadioPane.class);
    private static final double SPACING = 4;
    private final LinkedHashMap items = new LinkedHashMap<>();
    private final ToggleGroup group;
    private final InputControl.State state;

    /**
     * Constructs a RadioPane with a given set of items, current value, and validation function.
     *
     * @param items        the collection of items to be represented as radio buttons
     * @param currentValue the item to be selected initially, nullable
     * @param validate     the validation function to validate the selected item, returning an optional error message
     */
    @SuppressWarnings("unchecked")
    public RadioPane(Collection items, @Nullable T currentValue, Function> validate) {
        this.group = new ToggleGroup();

        setSpacing(SPACING);
        ObservableList children = getChildren();
        for (var item : items) {
            RadioButton control = new RadioButton(String.valueOf(item));
            control.setUserData(item);
            control.setToggleGroup(group);
            children.add(control);
            this.items.put(item, control);
        }

        // update state when selected toggle changes
        Property<@Nullable T> property = new SimpleObjectProperty<>();
        group.selectedToggleProperty().addListener((v, o, n) -> {
            Toggle toggle = group.getSelectedToggle();
            property.setValue(toggle != null ? (T) toggle.getUserData() : null);
        });

        this.state = new State<>(property);
        state.setValidate(validate);

        // update toggle, when state changes
        state.valueProperty().addListener((v, o, n) -> group.selectToggle(this.items.get(n)));

        // set initial toggle
        group.selectToggle(this.items.get(currentValue));
    }

    @Override
    public Node node() {
        return this;
    }

    @Override
    public void reset() {
        state.reset();
    }

    @Override
    public Property<@Nullable T> valueProperty() {
        return state.valueProperty();
    }

    @Override
    public ReadOnlyBooleanProperty validProperty() {
        return state.validProperty();
    }

    @Override
    public ReadOnlyStringProperty errorProperty() {
        return state.errorProperty();
    }

    @Override
    public void requestFocus() {
        if (group.getToggles().isEmpty()) {
            super.requestFocus();
        }

        Toggle t = group.getSelectedToggle();
        if (t == null) {
            t = group.getToggles().get(0);

        }

        if (t instanceof Control) {
            ((Control) t).requestFocus();
        } else {
            super.requestFocus();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy