mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-10-07 10:22:19 -04:00
Add validators, rework machine stores
This commit is contained in:
1 parent
f0f1417980
commit
4eb0c80d60
23 files changed
+608
-33
No files matched your search
@@ -0,0 +1,103 @@
|
||||
package io.xpipe.extension;
|
||||
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import net.synedra.validatorfx.Check;
|
||||
import net.synedra.validatorfx.ValidationMessage;
|
||||
import net.synedra.validatorfx.ValidationResult;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ChainedValidator implements io.xpipe.extension.Validator {
|
||||
|
||||
private final List<Validator> validators;
|
||||
private final ReadOnlyObjectWrapper<ValidationResult> validationResultProperty = new ReadOnlyObjectWrapper<>(new ValidationResult());
|
||||
private final ReadOnlyBooleanWrapper containsErrorsProperty = new ReadOnlyBooleanWrapper();
|
||||
|
||||
public ChainedValidator(List<Validator> validators) {
|
||||
this.validators = validators;
|
||||
validators.forEach(v -> {
|
||||
v.containsErrorsProperty().addListener((c,o,n) -> {
|
||||
containsErrorsProperty.set(containsErrors());
|
||||
});
|
||||
|
||||
v.validationResultProperty().addListener((c,o,n) -> {
|
||||
validationResultProperty.set(getValidationResult());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Check createCheck() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Check check) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Check check) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult getValidationResult() {
|
||||
var list = new ArrayList<ValidationMessage>();
|
||||
for (var val : validators) {
|
||||
list.addAll(val.getValidationResult().getMessages());
|
||||
}
|
||||
|
||||
var r = new ValidationResult();
|
||||
r.addAll(list);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadOnlyObjectProperty<ValidationResult> validationResultProperty() {
|
||||
return validationResultProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadOnlyBooleanProperty containsErrorsProperty() {
|
||||
return containsErrorsProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsErrors() {
|
||||
return validators.stream().anyMatch(Validator::containsErrors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate() {
|
||||
for (var val : validators) {
|
||||
if (!val.validate()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBinding createStringBinding() {
|
||||
return createStringBinding("- ", "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBinding createStringBinding(String prefix, String separator) {
|
||||
var list = new ArrayList<Observable>(validators.stream().map(Validator::createStringBinding).toList());
|
||||
Observable[] observables = list.toArray(Observable[]::new);
|
||||
return Bindings.createStringBinding(() -> {
|
||||
return validators.stream().map(v -> v.createStringBinding(prefix, separator).get()).collect(Collectors.joining("\n"));
|
||||
}, observables);
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,10 @@ package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.core.dialog.Dialog;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.MachineFileStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
@@ -13,19 +14,25 @@ public interface DataStoreProvider {
|
||||
|
||||
enum Category {
|
||||
STREAM,
|
||||
MACHINE,
|
||||
DATABASE;
|
||||
}
|
||||
|
||||
default Category getCategory() {
|
||||
if (StreamDataStore.class.isAssignableFrom(getStoreClasses().get(0))) {
|
||||
var c = getStoreClasses().get(0);
|
||||
if (StreamDataStore.class.isAssignableFrom(c)) {
|
||||
return Category.STREAM;
|
||||
}
|
||||
|
||||
if (MachineFileStore.class.isAssignableFrom(c) || ShellStore.class.isAssignableFrom(c)) {
|
||||
return Category.MACHINE;
|
||||
}
|
||||
|
||||
throw new ExtensionException("Provider " + getId() + " has no set category");
|
||||
}
|
||||
|
||||
default Region createConfigGui(Property<DataStore> store) {
|
||||
return null;
|
||||
default GuiDialog guiDialog(Property<DataStore> store) {
|
||||
throw new ExtensionException("Gui Dialog is not implemented by provider " + getId());
|
||||
}
|
||||
|
||||
default void init() throws Exception {
|
||||
@@ -65,7 +72,9 @@ public interface DataStoreProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
Dialog defaultDialog();
|
||||
default Dialog defaultDialog() {
|
||||
throw new ExtensionException("CLI Dialog not implemented by provider");
|
||||
}
|
||||
|
||||
default String display(DataStore store) {
|
||||
return store.toDisplay();
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package io.xpipe.extension;
|
||||
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import net.synedra.validatorfx.Check;
|
||||
import net.synedra.validatorfx.ValidationResult;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ExclusiveValidator<T> implements io.xpipe.extension.Validator {
|
||||
|
||||
private final Map<T, ? extends Validator> validators;
|
||||
private final ObservableValue<T> obs;
|
||||
|
||||
public ExclusiveValidator(Map<T, ? extends Validator> validators, ObservableValue<T> obs) {
|
||||
this.validators = validators;
|
||||
this.obs = obs;
|
||||
}
|
||||
|
||||
private Validator get() {
|
||||
return validators.get(obs.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Check createCheck() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Check check) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Check check) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult getValidationResult() {
|
||||
return get().getValidationResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadOnlyObjectProperty<ValidationResult> validationResultProperty() {
|
||||
return get().validationResultProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadOnlyBooleanProperty containsErrorsProperty() {
|
||||
return get().containsErrorsProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsErrors() {
|
||||
return get().containsErrors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate() {
|
||||
return get().validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBinding createStringBinding() {
|
||||
return createStringBinding("- ", "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBinding createStringBinding(String prefix, String separator) {
|
||||
var list = new ArrayList<Observable>(validators.values().stream().map(Validator::createStringBinding).toList());
|
||||
list.add(obs);
|
||||
Observable[] observables = list.toArray(Observable[]::new);
|
||||
return Bindings.createStringBinding(() -> {
|
||||
return get().createStringBinding(prefix, separator).get();
|
||||
}, observables);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.fxcomps.Comp;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class GuiDialog {
|
||||
|
||||
Comp<?> comp;
|
||||
Validator validator;
|
||||
|
||||
public GuiDialog(Comp<?> comp) {
|
||||
this.comp = comp;
|
||||
this.validator = new SimpleValidator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.xpipe.extension;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class Properties {
|
||||
|
||||
public static <T, V> void bindExclusive(Property<V> selected, Map<V, ? extends Property<T>> map, Property<T> toBind) {
|
||||
selected.addListener((c,o,n) -> {
|
||||
toBind.unbind();
|
||||
toBind.bind(map.get(n));
|
||||
});
|
||||
|
||||
toBind.bind(map.get(selected.getValue()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package io.xpipe.extension;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import net.synedra.validatorfx.Check;
|
||||
import net.synedra.validatorfx.Severity;
|
||||
import net.synedra.validatorfx.ValidationMessage;
|
||||
import net.synedra.validatorfx.ValidationResult;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SimpleValidator implements Validator {
|
||||
|
||||
private final Map<Check, ChangeListener<ValidationResult>> checks = new LinkedHashMap<>();
|
||||
private final ReadOnlyObjectWrapper<ValidationResult> validationResultProperty = new ReadOnlyObjectWrapper<>(new ValidationResult());
|
||||
private final ReadOnlyBooleanWrapper containsErrorsProperty = new ReadOnlyBooleanWrapper();
|
||||
|
||||
/** Create a check that lives within this checker's domain.
|
||||
* @return A check object whose dependsOn, decorates, etc. methods can be called
|
||||
*/
|
||||
public Check createCheck() {
|
||||
Check check = new Check();
|
||||
add(check);
|
||||
return check;
|
||||
}
|
||||
|
||||
/** Add another check to the checker. Changes in the check's validationResultProperty will be reflected in the checker.
|
||||
* @param check The check to add.
|
||||
*/
|
||||
public void add(Check check) {
|
||||
ChangeListener<ValidationResult> listener = (obs, oldv, newv) -> refreshProperties();
|
||||
checks.put(check, listener);
|
||||
check.validationResultProperty().addListener(listener);
|
||||
}
|
||||
|
||||
/** Removes a check from this validator.
|
||||
* @param check The check to remove from this validator.
|
||||
*/
|
||||
public void remove(Check check) {
|
||||
ChangeListener<ValidationResult> listener = checks.remove(check);
|
||||
if (listener != null) {
|
||||
check.validationResultProperty().removeListener(listener);
|
||||
}
|
||||
refreshProperties();
|
||||
}
|
||||
|
||||
/** Retrieves current validation result
|
||||
* @return validation result
|
||||
*/
|
||||
public ValidationResult getValidationResult() {
|
||||
return validationResultProperty.get();
|
||||
}
|
||||
|
||||
/** Can be used to track validation result changes
|
||||
* @return The Validation result property.
|
||||
*/
|
||||
public ReadOnlyObjectProperty<ValidationResult> validationResultProperty() {
|
||||
return validationResultProperty.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
/** A read-only boolean property indicating whether any of the checks of this validator emitted an error. */
|
||||
public ReadOnlyBooleanProperty containsErrorsProperty() {
|
||||
return containsErrorsProperty.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
public boolean containsErrors() {
|
||||
return containsErrorsProperty().get();
|
||||
}
|
||||
|
||||
/** Run all checks (decorating nodes if appropriate)
|
||||
* @return true if no errors were found, false otherwise
|
||||
*/
|
||||
public boolean validate() {
|
||||
for (Check check : checks.keySet()) {
|
||||
check.recheck();
|
||||
}
|
||||
return ! containsErrors();
|
||||
}
|
||||
|
||||
private void refreshProperties() {
|
||||
ValidationResult nextResult = new ValidationResult();
|
||||
for (Check check : checks.keySet()) {
|
||||
nextResult.addAll(check.getValidationResult().getMessages());
|
||||
}
|
||||
validationResultProperty.set(nextResult);
|
||||
boolean hasErrors = false;
|
||||
for (ValidationMessage msg : nextResult.getMessages()) {
|
||||
hasErrors = hasErrors || msg.getSeverity() == Severity.ERROR;
|
||||
}
|
||||
containsErrorsProperty.set(hasErrors);
|
||||
}
|
||||
|
||||
/** Create a string property that depends on the validation result.
|
||||
* Each error message will be displayed on a separate line prefixed with a bullet.
|
||||
*/
|
||||
public StringBinding createStringBinding() {
|
||||
return createStringBinding("- ", "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBinding createStringBinding(String prefix, String separator) {
|
||||
return Bindings.createStringBinding( () -> {
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (ValidationMessage msg : validationResultProperty.get().getMessages()) {
|
||||
if (str.length() > 0) {
|
||||
str.append(separator);
|
||||
}
|
||||
str.append(prefix).append(msg.getText());
|
||||
}
|
||||
return str.toString();
|
||||
}, validationResultProperty);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.xpipe.extension;
|
||||
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import net.synedra.validatorfx.Check;
|
||||
import net.synedra.validatorfx.ValidationResult;
|
||||
|
||||
public interface Validator {
|
||||
|
||||
public Check createCheck();
|
||||
|
||||
/** Add another check to the checker. Changes in the check's validationResultProperty will be reflected in the checker.
|
||||
* @param check The check to add.
|
||||
*/
|
||||
public void add(Check check);
|
||||
|
||||
/** Removes a check from this validator.
|
||||
* @param check The check to remove from this validator.
|
||||
*/
|
||||
public void remove(Check check);
|
||||
|
||||
/** Retrieves current validation result
|
||||
* @return validation result
|
||||
*/
|
||||
public ValidationResult getValidationResult();
|
||||
|
||||
/** Can be used to track validation result changes
|
||||
* @return The Validation result property.
|
||||
*/
|
||||
public ReadOnlyObjectProperty<ValidationResult> validationResultProperty();
|
||||
|
||||
/** A read-only boolean property indicating whether any of the checks of this validator emitted an error. */
|
||||
public ReadOnlyBooleanProperty containsErrorsProperty();
|
||||
|
||||
public boolean containsErrors();
|
||||
|
||||
/** Run all checks (decorating nodes if appropriate)
|
||||
* @return true if no errors were found, false otherwise
|
||||
*/
|
||||
public boolean validate();
|
||||
|
||||
/** Create a string property that depends on the validation result.
|
||||
* Each error message will be displayed on a separate line prefixed with a bullet.
|
||||
* @return
|
||||
*/
|
||||
public StringBinding createStringBinding();
|
||||
|
||||
/** Create a string property that depends on the validation result.
|
||||
* @param prefix The string to prefix each validation message with
|
||||
* @param separator The string to separate consecutive validation messages with
|
||||
* @param severities The severities to consider; If none is given, only Severity.ERROR will be considered
|
||||
* @return
|
||||
*/
|
||||
public StringBinding createStringBinding(String prefix, String separator);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.xpipe.extension;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import net.synedra.validatorfx.Check;
|
||||
|
||||
public class Validators {
|
||||
|
||||
public static Check nonNull(Validator v, ObservableValue<String> name, ObservableValue<?> s) {
|
||||
return v.createCheck().dependsOn("val", s).withMethod(c -> {
|
||||
if (c.get("val") == null ) {
|
||||
c.error(I18n.get("extension.mustNotBeEmpty", name.getValue()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,14 @@ package io.xpipe.extension.comp;
|
||||
import io.xpipe.core.charsetter.NewLine;
|
||||
import io.xpipe.core.util.Secret;
|
||||
import io.xpipe.extension.I18n;
|
||||
import io.xpipe.extension.Validator;
|
||||
import io.xpipe.extension.Validators;
|
||||
import io.xpipe.fxcomps.Comp;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
import net.synedra.validatorfx.Check;
|
||||
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
@@ -15,7 +18,7 @@ import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DynamicOptionsBuilder<T> {
|
||||
|
||||
@@ -40,6 +43,17 @@ public class DynamicOptionsBuilder<T> {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder<T> decorate(Check c) {
|
||||
entries.get(entries.size() - 1).comp().apply(s -> c.decorates(s.get()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder<T> nonNull(Validator v) {
|
||||
var e = entries.get(entries.size() - 1);
|
||||
var p = props.get(props.size() - 1);
|
||||
return decorate(Validators.nonNull(v, e.name(), p));
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder<T> addNewLine(Property<NewLine> prop) {
|
||||
var map = new LinkedHashMap<NewLine, ObservableValue<String>>();
|
||||
for (var e : NewLine.values()) {
|
||||
@@ -79,6 +93,13 @@ public class DynamicOptionsBuilder<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder<T> addString(String nameKey, Property<String> prop) {
|
||||
var comp = new TextFieldComp(prop);
|
||||
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder<T> addString(ObservableValue<String> name, Property<String> prop) {
|
||||
var comp = new TextFieldComp(prop);
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
@@ -86,8 +107,8 @@ public class DynamicOptionsBuilder<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder<T> addComp(Comp<?> comp) {
|
||||
entries.add(new DynamicOptionsComp.Entry(null, comp));
|
||||
public DynamicOptionsBuilder<T> addComp(ObservableValue<String> name, Comp<?> comp) {
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -105,19 +126,20 @@ public class DynamicOptionsBuilder<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public <V extends T> Comp<?> buildComp(Function<T, V> creator, Property<T> toSet) {
|
||||
public <V extends T> Comp<?> buildComp(Supplier<V> creator, Property<T> toSet) {
|
||||
props.forEach(prop -> {
|
||||
prop.addListener((c,o,n) -> {
|
||||
toSet.setValue(creator.apply(toSet.getValue()));
|
||||
toSet.setValue(creator.get());
|
||||
});
|
||||
});
|
||||
toSet.setValue(creator.get());
|
||||
if (title != null) {
|
||||
entries.add(0, new DynamicOptionsComp.Entry(null, Comp.of(() -> new Label(title.getValue())).styleClass("title")));
|
||||
}
|
||||
return new DynamicOptionsComp(entries, wrap);
|
||||
}
|
||||
|
||||
public <V extends T> Region build(Function<T, V> creator, Property<T> toSet) {
|
||||
public <V extends T> Region build(Supplier<V> creator, Property<T> toSet) {
|
||||
return buildComp(creator, toSet).createRegion();
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,18 @@ import com.jfoenix.controls.JFXTabPane;
|
||||
import io.xpipe.fxcomps.Comp;
|
||||
import io.xpipe.fxcomps.CompStructure;
|
||||
import io.xpipe.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.fxcomps.util.PlatformThread;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tab;
|
||||
import lombok.Getter;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class TabPaneComp extends Comp<CompStructure<JFXTabPane>> {
|
||||
|
||||
@Override
|
||||
@@ -32,12 +36,24 @@ public class TabPaneComp extends Comp<CompStructure<JFXTabPane>> {
|
||||
content.prefWidthProperty().bind(tabPane.widthProperty());
|
||||
}
|
||||
|
||||
tabPane.getSelectionModel().select(entries.indexOf(selected.getValue()));
|
||||
tabPane.getSelectionModel().selectedIndexProperty().addListener((c,o,n) -> {
|
||||
selected.setValue(entries.get(n.intValue()));
|
||||
});
|
||||
selected.addListener((c,o,n) -> {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
tabPane.getSelectionModel().select(entries.indexOf(n));
|
||||
});
|
||||
});
|
||||
|
||||
return new SimpleCompStructure<>(tabPane);
|
||||
}
|
||||
|
||||
private final Property<Entry> selected;
|
||||
private final List<Entry> entries;
|
||||
|
||||
public TabPaneComp(List<Entry> entries) {
|
||||
public TabPaneComp(Property<Entry> selected, List<Entry> entries) {
|
||||
this.selected = selected;
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ module io.xpipe.extension {
|
||||
requires static org.controlsfx.controls;
|
||||
requires java.desktop;
|
||||
requires org.fxmisc.richtext;
|
||||
requires static net.synedra.validatorfx;
|
||||
requires org.fxmisc.flowless;
|
||||
requires org.fxmisc.undofx;
|
||||
requires org.fxmisc.wellbehavedfx;
|
||||
|
||||
Reference in new issue
Block a user