Various small fixes

This commit is contained in:
Christopher Schnick 2022-12-23 10:29:08 +01:00
parent 71b5d2d716
commit 9a2a1a8745
18 changed files with 415 additions and 137 deletions

View file

@ -42,10 +42,8 @@ public abstract class DataSourceImpl implements DataSource {
case RAW -> {
yield new DataRawImpl(res.getId(), config, res.getInternalSource());
}
case COLLECTION -> throw new UnsupportedOperationException(
"Unimplemented case: " + res.getType());
default -> throw new IllegalArgumentException(
"Unexpected value: " + res.getType());
case COLLECTION -> throw new UnsupportedOperationException("Unimplemented case: " + res.getType());
default -> throw new IllegalArgumentException("Unexpected value: " + res.getType());
};
});
}
@ -64,17 +62,18 @@ public abstract class DataSourceImpl implements DataSource {
public static DataSource create(DataSourceId id, String type, DataStore store) {
if (store instanceof StreamDataStore s && s.isContentExclusivelyAccessible()) {
var res = XPipeApiConnection.execute(con -> {
var req = StoreStreamExchange.Request.builder().build();
StoreStreamExchange.Response r = con.performOutputExchange(req, out -> {
store = XPipeApiConnection.execute(con -> {
var internal = con.createInternalStreamStore();
var req = WriteStreamExchange.Request.builder()
.name(internal.getUuid().toString())
.build();
con.performOutputExchange(req, out -> {
try (InputStream inputStream = s.openInput()) {
inputStream.transferTo(out);
}
});
return r;
return internal;
});
store = res.getStore();
}
var startReq = ReadExchange.Request.builder()
@ -96,14 +95,17 @@ public abstract class DataSourceImpl implements DataSource {
}
public static DataSource create(DataSourceId id, String type, InputStream in) {
var res = XPipeApiConnection.execute(con -> {
var req = StoreStreamExchange.Request.builder().build();
StoreStreamExchange.Response r = con.performOutputExchange(req, out -> in.transferTo(out));
return r;
var store = XPipeApiConnection.execute(con -> {
var internal = con.createInternalStreamStore();
var req = WriteStreamExchange.Request.builder()
.name(internal.getUuid().toString())
.build();
con.performOutputExchange(req, out -> {
in.transferTo(out);
});
return internal;
});
var store = res.getStore();
var startReq = ReadExchange.Request.builder()
.provider(type)
.store(store)

View file

@ -7,12 +7,15 @@ import io.xpipe.api.connector.XPipeApiConnection;
import io.xpipe.api.util.TypeDescriptor;
import io.xpipe.beacon.BeaconException;
import io.xpipe.beacon.exchange.ReadExchange;
import io.xpipe.beacon.exchange.StoreStreamExchange;
import io.xpipe.beacon.exchange.WriteStreamExchange;
import io.xpipe.beacon.exchange.cli.StoreAddExchange;
import io.xpipe.beacon.util.QuietDialogHandler;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNodeAcceptor;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.typed.TypedDataStreamWriter;
import io.xpipe.core.impl.InternalStreamStore;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
@ -25,13 +28,20 @@ public class DataTableAccumulatorImpl implements DataTableAccumulator {
private final XPipeApiConnection connection;
private final TupleType type;
private int rows;
private InternalStreamStore store;
private TupleType writtenDescriptor;
private OutputStream bodyOutput;
public DataTableAccumulatorImpl(TupleType type) {
this.type = type;
connection = XPipeApiConnection.open();
connection.sendRequest(StoreStreamExchange.Request.builder().build());
store = new InternalStreamStore();
var addReq = StoreAddExchange.Request.builder().storeInput(store).name(store.getUuid().toString()).build();
StoreAddExchange.Response addRes = connection.performSimpleExchange(addReq);
QuietDialogHandler.handle(addRes.getConfig(), connection);
connection.sendRequest(WriteStreamExchange.Request.builder().name(store.getUuid().toString()).build());
bodyOutput = connection.sendBody();
}
@ -43,12 +53,12 @@ public class DataTableAccumulatorImpl implements DataTableAccumulator {
throw new BeaconException(e);
}
StoreStreamExchange.Response res = connection.receiveResponse();
WriteStreamExchange.Response res = connection.receiveResponse();
connection.close();
var req = ReadExchange.Request.builder()
.target(id)
.store(res.getStore())
.store(store)
.provider("xpbt")
.configureAll(false)
.build();

View file

@ -1,5 +1,9 @@
package io.xpipe.beacon;
import io.xpipe.beacon.exchange.cli.StoreAddExchange;
import io.xpipe.beacon.util.QuietDialogHandler;
import io.xpipe.core.impl.InternalStreamStore;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -169,6 +173,14 @@ public abstract class BeaconConnection implements AutoCloseable {
}
}
public InternalStreamStore createInternalStreamStore() {
var store = new InternalStreamStore();
var addReq = StoreAddExchange.Request.builder().storeInput(store).name(store.getUuid().toString()).build();
StoreAddExchange.Response addRes = performSimpleExchange(addReq);
QuietDialogHandler.handle(addRes.getConfig(), this);
return store;
}
private BeaconException unwrapException(Exception exception) {
if (exception instanceof ServerException s) {
return new BeaconException("An internal server error occurred", s);

View file

@ -1,31 +0,0 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.core.impl.FileStore;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Stores a stream of data in a storage.
*/
public class StoreStreamExchange implements MessageExchange {
@Override
public String getId() {
return "storeStream";
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
FileStore store;
}
}

View file

@ -1,7 +1,7 @@
package io.xpipe.beacon.util;
import io.xpipe.beacon.BeaconConnection;
import io.xpipe.beacon.ClientException;
import io.xpipe.beacon.BeaconException;
import io.xpipe.beacon.exchange.cli.DialogExchange;
import io.xpipe.core.dialog.BaseQueryElement;
import io.xpipe.core.dialog.ChoiceElement;
@ -13,7 +13,7 @@ import java.util.UUID;
public class QuietDialogHandler {
public static void handle(DialogReference ref, BeaconConnection connection) throws ClientException {
public static void handle(DialogReference ref, BeaconConnection connection) {
new QuietDialogHandler(ref, connection, Map.of()).handle();
}
@ -29,7 +29,7 @@ public class QuietDialogHandler {
this.overrides = overrides;
}
public void handle() throws ClientException {
public void handle() {
String response = null;
if (element instanceof ChoiceElement c) {
@ -45,7 +45,7 @@ public class QuietDialogHandler {
.value(response)
.build());
if (res.getElement() != null && element.equals(res.getElement())) {
throw new ClientException(
throw new BeaconException(
"Invalid value for key " + res.getElement().toDisplayString());
}

View file

@ -62,7 +62,6 @@ module io.xpipe.beacon {
ListStoresExchange,
DialogExchange,
QueryDataSourceExchange,
StoreStreamExchange,
EditExchange,
RemoveEntryExchange,
RemoveCollectionExchange,

View file

@ -1,49 +1,111 @@
package io.xpipe.core.charsetter;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import io.xpipe.core.util.Identifiers;
import lombok.Value;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Stream;
@Value
public class StreamCharset {
public static final StreamCharset UTF8 = new StreamCharset(StandardCharsets.UTF_8, null);
public static final StreamCharset UTF8_BOM =
new StreamCharset(StandardCharsets.UTF_8, new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
public static final StreamCharset UTF16_BE = new StreamCharset(StandardCharsets.UTF_16BE, null);
public static final StreamCharset UTF16_BE_BOM =
new StreamCharset(StandardCharsets.UTF_16BE, new byte[] {(byte) 0xFE, (byte) 0xFF});
public static final StreamCharset UTF16_LE = new StreamCharset(StandardCharsets.UTF_16LE, null);
public static final StreamCharset UTF16_LE_BOM =
new StreamCharset(StandardCharsets.UTF_16LE, new byte[] {(byte) 0xFF, (byte) 0xFE});
public static final StreamCharset UTF8 =
new StreamCharset(StandardCharsets.UTF_8, null, Identifiers.get("utf", "8"));
public static final StreamCharset UTF32_LE = new StreamCharset(Charset.forName("utf-32le"), null);
public static final StreamCharset UTF32_LE_BOM =
new StreamCharset(Charset.forName("utf-32le"), new byte[] {0x00, 0x00, (byte) 0xFE, (byte) 0xFF});
public static final StreamCharset UTF32_BE = new StreamCharset(Charset.forName("utf-32be"), null);
public static final StreamCharset UTF32_BE_BOM =
new StreamCharset(Charset.forName("utf-32be"), new byte[] {(byte) 0xFF, (byte) 0xFE, 0x00, 0x00, });
public static final StreamCharset UTF8_BOM = new StreamCharset(
StandardCharsets.UTF_8,
new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF},
Identifiers.get("utf", "8", "bom"));
// ======
// UTF-16
// ======
public static final StreamCharset UTF16_BE =
new StreamCharset(StandardCharsets.UTF_16BE, null, Identifiers.get("utf", "16", "be"));
public static final StreamCharset UTF16_BE_BOM = new StreamCharset(
StandardCharsets.UTF_16BE,
new byte[] {(byte) 0xFE, (byte) 0xFF},
Identifiers.get("utf", "16", "be", "bom"));
public static final StreamCharset UTF16_LE =
new StreamCharset(StandardCharsets.UTF_16LE, null, Identifiers.get("utf", "16", "le"));
public static final StreamCharset UTF16_LE_BOM = new StreamCharset(
StandardCharsets.UTF_16LE,
new byte[] {(byte) 0xFF, (byte) 0xFE},
Identifiers.get("utf", "16", "le", "bom"));
public static final StreamCharset UTF16 =
new StreamCharset(StandardCharsets.UTF_16, null, Identifiers.get("utf", "16"));
public static final StreamCharset UTF16_BOM = new StreamCharset(
StandardCharsets.UTF_16,
ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)
? UTF16_BE_BOM.getByteOrderMark()
: UTF16_LE_BOM.getByteOrderMark(),
Identifiers.get("utf", "16", "bom"));
// ======
// UTF-32
// ======
public static final StreamCharset UTF32_LE =
new StreamCharset(Charset.forName("utf-32le"), null, Identifiers.get("utf", "32", "le"));
public static final StreamCharset UTF32_LE_BOM = new StreamCharset(
Charset.forName("utf-32le"),
new byte[] {0x00, 0x00, (byte) 0xFE, (byte) 0xFF},
Identifiers.get("utf", "32", "le", "bom"));
public static final StreamCharset UTF32_BE =
new StreamCharset(Charset.forName("utf-32be"), null, Identifiers.get("utf", "32", "be"));
public static final StreamCharset UTF32_BE_BOM = new StreamCharset(
Charset.forName("utf-32be"),
new byte[] {
(byte) 0xFF, (byte) 0xFE, 0x00, 0x00,
},
Identifiers.get("utf", "32", "be", "bom"));
public static final List<StreamCharset> COMMON = List.of(
UTF8,
UTF8_BOM,
UTF16_BE,
UTF16_BE_BOM,
UTF16_LE,
UTF16_LE_BOM,
new StreamCharset(StandardCharsets.US_ASCII, null),
new StreamCharset(StandardCharsets.ISO_8859_1, null),
new StreamCharset(Charset.forName("Windows-1251"), null),
new StreamCharset(Charset.forName("Windows-1252"), null));
private static final List<StreamCharset> RARE_KNOWN = List.of(UTF32_LE, UTF32_LE_BOM, UTF32_BE, UTF32_BE_BOM);
UTF16,
UTF16_BOM,
new StreamCharset(
StandardCharsets.US_ASCII,
null,
Identifiers.join(Identifiers.get("ascii"), Identifiers.get("us", "ascii"))),
new StreamCharset(
StandardCharsets.ISO_8859_1,
null,
Identifiers.join(
Identifiers.get("iso", "8859"),
Identifiers.get("iso", "8859", "1"),
Identifiers.get("8859"),
Identifiers.get("8859", "1"))),
new StreamCharset(
Charset.forName("Windows-1251"),
null,
Identifiers.join(Identifiers.get("windows", "1251"), Identifiers.get("1251"))),
new StreamCharset(
Charset.forName("Windows-1252"),
null,
Identifiers.join(Identifiers.get("windows", "1252"), Identifiers.get("1252"))));
private static final List<StreamCharset> RARE_NAMED =
List.of(UTF16_LE, UTF16_LE_BOM, UTF16_BE, UTF16_BE_BOM, UTF32_LE, UTF32_LE_BOM, UTF32_BE, UTF32_BE_BOM);
public static final List<StreamCharset> RARE = Stream.concat(
RARE_KNOWN.stream(),
RARE_NAMED.stream(),
Charset.availableCharsets().values().stream()
.filter(charset -> !charset.equals(StandardCharsets.UTF_16)
&& !charset.equals(Charset.forName("utf-32"))
@ -52,31 +114,59 @@ public class StreamCharset {
&& !charset.displayName().endsWith("-BOM")
&& COMMON.stream()
.noneMatch(c -> c.getCharset().equals(charset))
&& RARE_KNOWN.stream()
&& RARE_NAMED.stream()
.noneMatch(c -> c.getCharset().equals(charset)))
.map(charset -> new StreamCharset(charset, null)))
.map(charset -> new StreamCharset(
charset,
null,
Identifiers.get(charset.name().split("-")))))
.toList();
public static final List<StreamCharset> ALL = Stream.concat(COMMON.stream(), RARE.stream()).toList();
Charset charset;
byte[] byteOrderMark;
List<String> names;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof StreamCharset that)) {
return false;
}
return charset.equals(that.charset) && Arrays.equals(byteOrderMark, that.byteOrderMark);
}
@Override
public int hashCode() {
int result = Objects.hash(charset);
result = 31 * result + Arrays.hashCode(byteOrderMark);
return result;
}
public static StreamCharset get(Charset charset, boolean byteOrderMark) {
return Stream.concat(COMMON.stream(), RARE.stream())
return ALL.stream()
.filter(streamCharset ->
streamCharset.getCharset().equals(charset) && streamCharset.hasByteOrderMark() == byteOrderMark)
.findFirst()
.orElseThrow();
}
@JsonCreator
public static StreamCharset get(String s) {
var byteOrderMark = s.endsWith("-bom");
var charset = Charset.forName(s.substring(0, s.length() - (byteOrderMark ? 4 : 0)));
return StreamCharset.get(charset, byteOrderMark);
var found = ALL.stream().filter(streamCharset -> streamCharset.getNames().contains(s.toLowerCase(Locale.ROOT))).findFirst();
if (found.isEmpty()) {
throw new IllegalArgumentException("Unknown charset name: " + s);
}
return found.get();
}
@JsonValue
public String toString() {
return getCharset().name().toLowerCase(Locale.ROOT) + (hasByteOrderMark() ? "-bom" : "");
return getNames().get(0);
}
public boolean hasByteOrderMark() {

View file

@ -0,0 +1,61 @@
package io.xpipe.core.impl;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.store.DataFlow;
import io.xpipe.core.store.StreamDataStore;
import io.xpipe.core.util.DataStateProvider;
import io.xpipe.core.util.JacksonizedValue;
import lombok.Getter;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
@JsonTypeName("internalStream")
@SuperBuilder
@Jacksonized
@Getter
public class InternalStreamStore extends JacksonizedValue implements StreamDataStore {
private final UUID uuid;
public InternalStreamStore() {
this.uuid = UUID.randomUUID();
}
@Override
public DataFlow getFlow() {
return DataFlow.INPUT_OUTPUT;
}
@Override
public Optional<String> determineDefaultName() {
return Optional.of(uuid.toString());
}
private Path getFile() {
return DataStateProvider.get().getInternalStreamStore(uuid);
}
@Override
public Optional<Instant> determineLastModified() throws IOException {
return Optional.of(Files.getLastModifiedTime(getFile()).toInstant());
}
@Override
public InputStream openInput() throws Exception {
return Files.newInputStream(getFile());
}
@Override
public OutputStream openOutput() throws Exception {
return Files.newOutputStream(getFile());
}
}

View file

@ -5,6 +5,7 @@ import io.xpipe.core.impl.StdinDataStore;
import io.xpipe.core.impl.StdoutDataStore;
import io.xpipe.core.source.DataSource;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;
@ -101,7 +102,7 @@ public interface DataStore {
/**
* Determines the last modified of this data store if this data store supports it.
*/
default Optional<Instant> determineLastModified() {
default Optional<Instant> determineLastModified() throws IOException {
return Optional.empty();
}
}

View file

@ -2,7 +2,9 @@ package io.xpipe.core.util;
import io.xpipe.core.store.DataStore;
import java.nio.file.Path;
import java.util.ServiceLoader;
import java.util.UUID;
import java.util.function.Supplier;
public abstract class DataStateProvider {
@ -22,4 +24,6 @@ public abstract class DataStateProvider {
public abstract void putState(DataStore store, String key, Object value);
public abstract <T> T getState(DataStore store, String key, Class<T> c, Supplier<T> def);
public abstract Path getInternalStreamStore(UUID id);
}

View file

@ -0,0 +1,25 @@
package io.xpipe.core.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class Identifiers {
@SafeVarargs
public static List<String> join(List<String>... s) {
return Arrays.stream(s).flatMap(Collection::stream).toList();
}
public static List<String> get(String... s) {
return nameAlternatives(Arrays.asList(s));
}
private static List<String> nameAlternatives(List<String> split) {
return List.of(
String.join("", split),
String.join(" ", split),
String.join("_", split),
String.join("-", split));
}
}

View file

@ -8,11 +8,16 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Builder
@Getter
public class TrackEvent {
public static TrackEventBuilder storage() {
return TrackEvent.builder().category("storage");
}
private final Thread thread = Thread.currentThread();
private final Instant instant = Instant.now();
private String type;
@ -107,10 +112,17 @@ public class TrackEvent {
if (tags.size() > 0) {
s.append(" {\n");
for (var e : tags.entrySet()) {
var value = e.toString().contains("\n")
? "\n"
+ (e.toString()
.lines()
.map(line -> " | " + line)
.collect(Collectors.joining("\n")))
: e.toString();
s.append(" ")
.append(e.getKey())
.append("=")
.append(e.getValue())
.append(value)
.append("\n");
}
s.append("}");
@ -120,6 +132,11 @@ public class TrackEvent {
public static class TrackEventBuilder {
public TrackEventBuilder trace() {
this.type("trace");
return this;
}
public TrackEventBuilder windowCategory() {
this.category("window");
return this;

View file

@ -47,6 +47,10 @@ public abstract class Comp<S extends CompStructure<?>> {
return (T) this;
}
public Comp<S> visible(ObservableValue<Boolean> o) {
return apply(struc -> struc.get().visibleProperty().bind(o));
}
public Comp<S> disable(ObservableValue<Boolean> o) {
return apply(struc -> struc.get().disableProperty().bind(o));
}

View file

@ -3,21 +3,19 @@ package io.xpipe.extension.fxcomps.impl;
import io.xpipe.extension.fxcomps.Comp;
import io.xpipe.extension.fxcomps.CompStructure;
import io.xpipe.extension.fxcomps.SimpleCompStructure;
import io.xpipe.extension.fxcomps.util.PlatformThread;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.*;
import java.util.ArrayList;
import java.util.List;
public class DynamicOptionsComp extends Comp<CompStructure<FlowPane>> {
public class DynamicOptionsComp extends Comp<CompStructure<Pane>> {
private final List<Entry> entries;
private final boolean wrap;
@ -27,12 +25,28 @@ public class DynamicOptionsComp extends Comp<CompStructure<FlowPane>> {
this.wrap = wrap;
}
public Entry queryEntry(String key) {
return entries.stream()
.filter(entry -> entry.key != null && entry.key.equals(key))
.findAny()
.orElseThrow();
}
@Override
public CompStructure<FlowPane> createBase() {
var flow = new FlowPane(Orientation.HORIZONTAL);
flow.setAlignment(Pos.CENTER);
flow.setHgap(14);
flow.setVgap(7);
public CompStructure<Pane> createBase() {
Pane pane;
if (wrap) {
var content = new FlowPane(Orientation.HORIZONTAL);
content.setAlignment(Pos.CENTER);
content.setHgap(14);
content.setVgap(7);
pane = content;
} else {
var content = new VBox();
content.setAlignment(Pos.CENTER);
content.setSpacing(7);
pane = content;
}
var nameRegions = new ArrayList<Region>();
var compRegions = new ArrayList<Region>();
@ -41,30 +55,38 @@ public class DynamicOptionsComp extends Comp<CompStructure<FlowPane>> {
var line = new HBox();
line.setFillHeight(true);
if (!wrap) {
line.prefWidthProperty().bind(flow.widthProperty());
line.prefWidthProperty().bind(pane.widthProperty());
}
line.setSpacing(8);
Region compRegion = null;
if (entry.comp() != null) {
compRegion = entry.comp().createRegion();
}
if (entry.name() != null) {
var name = new Label();
name.textProperty().bind(entry.name());
name.prefHeightProperty().bind(line.heightProperty());
name.setMinWidth(Region.USE_PREF_SIZE);
name.setAlignment(Pos.CENTER_LEFT);
if (compRegion != null) {
name.visibleProperty().bind(PlatformThread.sync(compRegion.visibleProperty()));
name.managedProperty().bind(PlatformThread.sync(compRegion.managedProperty()));
}
nameRegions.add(name);
line.getChildren().add(name);
}
if (entry.comp() != null) {
var r = entry.comp().createRegion();
compRegions.add(r);
line.getChildren().add(r);
compRegions.add(compRegion);
line.getChildren().add(compRegion);
if (!wrap) {
HBox.setHgrow(r, Priority.ALWAYS);
HBox.setHgrow(compRegion, Priority.ALWAYS);
}
}
flow.getChildren().add(line);
pane.getChildren().add(line);
}
if (wrap) {
@ -101,12 +123,12 @@ public class DynamicOptionsComp extends Comp<CompStructure<FlowPane>> {
nameRegions.forEach(r -> r.prefWidthProperty().bind(nameWidthBinding));
}
return new SimpleCompStructure<>(flow);
return new SimpleCompStructure<>(pane);
}
public List<Entry> getEntries() {
return entries;
}
public record Entry(ObservableValue<String> name, Comp<?> comp) {}
public record Entry(String key, ObservableValue<String> name, Comp<?> comp) {}
}

View file

@ -0,0 +1,28 @@
package io.xpipe.extension.fxcomps.impl;
import io.xpipe.extension.fxcomps.Comp;
import io.xpipe.extension.fxcomps.CompStructure;
import io.xpipe.extension.fxcomps.SimpleCompStructure;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import java.util.List;
public class GrowPaneComp extends Comp<CompStructure<Pane>> {
private final List<Comp<?>> comps;
public GrowPaneComp(List<Comp<?>> comps) {
this.comps = List.copyOf(comps);
}
@Override
public CompStructure<Pane> createBase() {
var pane = new BorderPane();
for (var c : comps) {
pane.setCenter(c.createRegion());
}
pane.setPickOnBounds(false);
return new SimpleCompStructure<>(pane);
}
}

View file

@ -0,0 +1,27 @@
package io.xpipe.extension.fxcomps.impl;
import io.xpipe.extension.fxcomps.Comp;
import io.xpipe.extension.fxcomps.CompStructure;
import io.xpipe.extension.fxcomps.SimpleCompStructure;
import javafx.scene.layout.Pane;
import java.util.List;
public class PaneComp extends Comp<CompStructure<Pane>> {
private final List<Comp<?>> comps;
public PaneComp(List<Comp<?>> comps) {
this.comps = List.copyOf(comps);
}
@Override
public CompStructure<Pane> createBase() {
var pane = new Pane();
for (var c : comps) {
pane.getChildren().add(c.createRegion());
}
pane.setPickOnBounds(false);
return new SimpleCompStructure<>(pane);
}
}

View file

@ -43,10 +43,14 @@ public class ToggleGroupComp<T> extends Comp<CompStructure<HBox>> {
box.getChildren().add(b);
b.setToggleGroup(group);
value.addListener((c, o, n) -> {
PlatformThread.runLaterIfNeeded(() -> b.setSelected(entry.equals(n)));
PlatformThread.runLaterIfNeeded(() -> {
if (entry.getKey().equals(n)) {
group.selectToggle(b);
}
});
});
if (entry.getKey().equals(value.getValue())) {
b.setSelected(true);
group.selectToggle(b);
}
}

View file

@ -5,7 +5,6 @@ import io.xpipe.core.charsetter.StreamCharset;
import io.xpipe.core.util.SecretValue;
import io.xpipe.extension.I18n;
import io.xpipe.extension.fxcomps.Comp;
import io.xpipe.extension.fxcomps.CompStructure;
import io.xpipe.extension.fxcomps.impl.*;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
@ -43,12 +42,14 @@ public class DynamicOptionsBuilder {
}
public DynamicOptionsBuilder addTitle(String titleKey) {
return addTitle(I18n.observable(titleKey));
entries.add(new DynamicOptionsComp.Entry(
titleKey, null, new LabelComp(I18n.observable(titleKey)).styleClass("title-header")));
return this;
}
public DynamicOptionsBuilder addTitle(ObservableValue<String> title) {
entries.add(new DynamicOptionsComp.Entry(
null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header")));
null, null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header")));
return this;
}
@ -69,7 +70,7 @@ public class DynamicOptionsBuilder {
map.put(e, I18n.observable("extension." + e.getId()));
}
var comp = new ChoiceComp<>(prop, map, false);
entries.add(new DynamicOptionsComp.Entry(I18n.observable("extension.newLine"), comp));
entries.add(new DynamicOptionsComp.Entry("newLine", I18n.observable("extension.newLine"), comp));
props.add(prop);
return this;
}
@ -77,7 +78,7 @@ public class DynamicOptionsBuilder {
public DynamicOptionsBuilder addCharacter(
Property<Character> prop, ObservableValue<String> name, Map<Character, ObservableValue<String>> names) {
var comp = new CharChoiceComp(prop, names, null);
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
props.add(prop);
return this;
}
@ -88,7 +89,7 @@ public class DynamicOptionsBuilder {
Map<Character, ObservableValue<String>> names,
ObservableValue<String> customName) {
var comp = new CharChoiceComp(prop, names, customName);
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
props.add(prop);
return this;
}
@ -96,7 +97,7 @@ public class DynamicOptionsBuilder {
public DynamicOptionsBuilder addToggle(String nameKey,
Property<Boolean> prop) {
var comp = new ToggleGroupComp<>(prop, new SimpleObjectProperty<>(Map.of(Boolean.TRUE, I18n.observable("extension.yes"), Boolean.FALSE, I18n.observable("extension.no"))));
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
props.add(prop);
return this;
}
@ -104,7 +105,7 @@ public class DynamicOptionsBuilder {
public <V> DynamicOptionsBuilder addToggle(
Property<V> prop, ObservableValue<String> name, Map<V, ObservableValue<String>> names) {
var comp = new ToggleGroupComp<>(prop, new SimpleObjectProperty<>(names));
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
props.add(prop);
return this;
}
@ -113,7 +114,7 @@ public class DynamicOptionsBuilder {
Property<V> prop, ObservableValue<String> name, Map<V, ObservableValue<String>> names, boolean includeNone
) {
var comp = new ChoiceComp<>(prop, names, includeNone);
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
props.add(prop);
return this;
}
@ -122,49 +123,49 @@ public class DynamicOptionsBuilder {
Property<V> prop, ObservableValue<String> name, ObservableValue<Map<V, ObservableValue<String>>> names, boolean includeNone
) {
var comp = new ChoiceComp<>(prop, names, includeNone);
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
props.add(prop);
return this;
}
public DynamicOptionsBuilder addCharset(Property<StreamCharset> prop) {
var comp = new CharsetChoiceComp(prop);
entries.add(new DynamicOptionsComp.Entry(I18n.observable("extension.charset"), comp));
entries.add(new DynamicOptionsComp.Entry("charset", I18n.observable("extension.charset"), comp));
props.add(prop);
return this;
}
public DynamicOptionsBuilder addStringArea(String nameKey, Property<String> prop, boolean lazy) {
var comp = new TextAreaComp(prop, lazy);
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
props.add(prop);
return this;
}
public DynamicOptionsBuilder addString(String nameKey, Property<String> prop) {
var comp = new TextFieldComp(prop);
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
props.add(prop);
return this;
}
public DynamicOptionsBuilder addString(String nameKey, Property<String> prop, boolean lazy) {
var comp = new TextFieldComp(prop, lazy);
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
props.add(prop);
return this;
}
public DynamicOptionsBuilder addString(ObservableValue<String> name, Property<String> prop) {
var comp = new TextFieldComp(prop);
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
props.add(prop);
return this;
}
public DynamicOptionsBuilder addString(ObservableValue<String> name, Property<String> prop, boolean lazy) {
var comp = new TextFieldComp(prop, lazy);
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
props.add(prop);
return this;
}
@ -178,11 +179,13 @@ public class DynamicOptionsBuilder {
}
public DynamicOptionsBuilder addComp(String nameKey, Comp<?> comp, Property<?> prop) {
return addComp(I18n.observable(nameKey), comp, prop);
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
if (prop != null) props.add(prop);
return this;
}
public DynamicOptionsBuilder addComp(ObservableValue<String> name, Comp<?> comp, Property<?> prop) {
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
if (prop != null) props.add(prop);
return this;
}
@ -193,21 +196,21 @@ public class DynamicOptionsBuilder {
public DynamicOptionsBuilder addSecret(ObservableValue<String> name, Property<SecretValue> prop) {
var comp = new SecretFieldComp(prop);
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
props.add(prop);
return this;
}
public DynamicOptionsBuilder addInteger(ObservableValue<String> name, Property<Integer> prop) {
var comp = new IntFieldComp(prop);
entries.add(new DynamicOptionsComp.Entry(name, comp));
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
props.add(prop);
return this;
}
public DynamicOptionsBuilder addInteger(String nameKey, Property<Integer> prop) {
var comp = new IntFieldComp(prop);
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
props.add(prop);
return this;
}
@ -239,12 +242,12 @@ public class DynamicOptionsBuilder {
return this;
}
public Comp<? extends CompStructure<?>> buildComp() {
public DynamicOptionsComp buildComp() {
if (title != null) {
entries.add(
0,
new DynamicOptionsComp.Entry(
null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header")));
null, null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header")));
}
return new DynamicOptionsComp(entries, wrap);
}