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

com.dua3.fx.controls.OptionsPane Maven / Gradle / Ivy

The newest version!
package com.dua3.fx.controls;

import com.dua3.cabe.annotations.Nullable;
import com.dua3.utility.options.Arguments;
import com.dua3.utility.options.ChoiceOption;
import com.dua3.utility.options.Flag;
import com.dua3.utility.options.Option;
import com.dua3.utility.options.SimpleOption;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.util.StringConverter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class OptionsPane extends GridPane implements InputControl {

    /**
     * Logger
     */
    protected static final Logger LOG = LogManager.getLogger(OptionsPane.class);
    private static final Insets INSETS = new Insets(2);
    private final InputControl.State state;
    private final Supplier>> options;
    private final Supplier dflt;
    private final Property value = new SimpleObjectProperty<>();
    private final Map, InputControl> items = new LinkedHashMap<>();

    /**
     * Create new OptionsPane.
     *
     * @param optionSet     the available options
     * @param currentValues the current values
     */
    public OptionsPane(Collection> optionSet, Arguments currentValues) {
        this(() -> optionSet, () -> currentValues);
    }

    public OptionsPane(Supplier>> options, Supplier dflt) {
        this.options = options;
        this.dflt = dflt;
        this.state = new State<>(value, dflt);
    }

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

    @SuppressWarnings({"unchecked", "rawtypes"})
    @Override
    public Arguments get() {
        Deque> entries = new ArrayDeque<>();
        for (var entry : items.entrySet()) {
            Option option = entry.getKey();
            Object value = entry.getValue().valueProperty().getValue();
            if (value != null) {
                entries.add(Arguments.createEntry(option, value));
            }
        }
        return Arguments.of(entries.toArray(Arguments.Entry[]::new));
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    public void set(Arguments arg) {
        for (var item : items.entrySet()) {
            Option option = item.getKey();
            InputControl control = item.getValue();
            Stream> stream = arg.stream(option);
            Optional value = stream.filter(list -> !list.isEmpty())
                    .reduce((first, second) -> second)
                    .map(List::getLast);
            control.set(value.orElse(null));
        }
    }

    @Override
    public void init() {
        getChildren().clear();

        Collection> optionSet = options.get();
        Arguments values = dflt.get();

        int row = 0;
        for (Option option : optionSet) {
            Label label = new Label(option.displayName());

            var control = createControl(values, option);
            items.put(option, control);

            addToGrid(label, 0, row);
            addToGrid(control.node(), 1, row);

            row++;
        }
    }

    @SuppressWarnings("unchecked")
    private  InputControl createControl(Arguments values, Option option) {
        if (option instanceof ChoiceOption co) {
            return new ChoiceInputControl<>(co, supplyDefault(co, values));
        } else if (option instanceof Flag f) {
            CheckBox checkBox = new CheckBox(f.displayName());
            return (InputControl) new SimpleInputControl<>(checkBox, checkBox.selectedProperty(), supplyDefault(f, values), nopValidator());
        } else if (option instanceof SimpleOption so) {
            StringConverter converter = new StringConverter<>() {
                @Override
                public String toString(T v) {
                    return option.format(v);
                }

                @Override
                public T fromString(String s) {
                    return option.map(s);
                }
            };
            return InputControl.stringInput(supplyDefault(so, values), nopValidator(), converter);
        }

        throw new UnsupportedOperationException("unsupported input type: " + option.getClass().getName());
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private static  T getValue(Option option, Arguments values) {
        if (option instanceof Flag flag) {
            return (T) (Object) values.isSet(flag);
        }
        if (option instanceof SimpleOption so) {
            return (T) values.get(so).orElse(so.getDefault());
        }
        if (option instanceof ChoiceOption co) {
            return (T) values.get(co).orElse(co.getDefault());
        }
        throw new IllegalArgumentException("Unknown option type: " + option);
    }

    private static  Function> nopValidator() {
        return s -> Optional.empty();
    }

    private static  Supplier supplyDefault(Option option, Arguments values) {
        return () -> getValue(option, values);
    }

    private void addToGrid(@Nullable Node node, int c, int r) {
        if (node != null) {
            add(node, c, r);
            setMargin(node, INSETS);
        }
    }

    @Override
    public void reset() {
        items.forEach((item, control) -> control.reset());
    }

    @Override
    public Property valueProperty() {
        return state.valueProperty();
    }

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

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

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy