This commit is contained in:
Christopher Schnick committed 2022-10-11 12:23:37 +02:00
1 parent f12ce82933
commit 8d95f451bb
40 files changed
+471 -191

No files matched your search

@@ -61,10 +61,17 @@ public interface DataSourceProvider<T extends DataSource<?>> {
return i18n("displayDescription");
}
default String getDisplayIconFileName() {
return getId() + ":icon.png";
default String getModuleName() {
var n = getClass().getPackageName();
var i = n.lastIndexOf('.');
return i != -1 ? n.substring(i + 1) : n;
}
default String getDisplayIconFileName() {
return getModuleName() + ":" + getId() + "_icon.png";
}
default String getSourceDescription(T source) {
return getDisplayName();
}
@@ -2,6 +2,7 @@ package io.xpipe.extension;
import io.xpipe.core.dialog.Dialog;
import io.xpipe.core.store.*;
import io.xpipe.core.util.JacksonizedValue;
import javafx.beans.property.Property;
import java.util.List;
@@ -14,6 +15,16 @@ public interface DataStoreProvider {
DATABASE;
}
default void validate() throws Exception {
getCategory();
for (Class<?> storeClass : getStoreClasses()) {
if (!JacksonizedValue.class.isAssignableFrom(storeClass)) {
throw new ExtensionException(String.format("Store class %s is not a Jacksonized value", storeClass.getSimpleName()));
}
}
}
default Category getCategory() {
var c = getStoreClasses().get(0);
if (StreamDataStore.class.isAssignableFrom(c)) {
@@ -19,9 +19,12 @@ public class DataStoreProviders {
.collect(Collectors.toList());
ALL.removeIf(p -> {
try {
return !p.init();
p.init();
p.validate();
return false;
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
ErrorEvent.fromThrowable(e)
.handle();
return true;
}
});
@@ -56,17 +59,17 @@ public class DataStoreProviders {
@SuppressWarnings("unchecked")
public static <T extends DataStoreProvider> T byStore(DataStore store) {
return (T) byStoreClass(store.getClass());
return (T) byStoreClass(store.getClass()).orElseThrow();
}
@SuppressWarnings("unchecked")
public static <T extends DataStoreProvider> T byStoreClass(Class<?> c) {
public static <T extends DataStoreProvider> Optional<T> byStoreClass(Class<?> c) {
if (ALL == null) {
throw new IllegalStateException("Not initialized");
}
return (T) ALL.stream().filter(d -> d.getStoreClasses().contains(c)).findAny().orElseThrow();
return (Optional<T>) ALL.stream().filter(d -> d.getStoreClasses().contains(c)).findAny();
}
public static List<DataStoreProvider> getAll() {
@@ -80,7 +80,10 @@ public class FancyTooltipAugment<S extends CompStructure<?>> implements Augment<
public void hide() {
Window owner = getOwnerWindow();
if (owner == null || owner.isFocused()) {
super.hide();
try {
super.hide();
} catch (Exception e) {
}
}
}
@@ -47,8 +47,6 @@ public class FilterComp extends Comp<FilterComp.Structure> {
var stack = new StackPane(bgLabel, filter);
stack.getStyleClass().add("filter-comp");
bgLabel.prefHeightProperty().bind(stack.heightProperty());
filter.prefHeightProperty().bind(stack.heightProperty());
return Structure.builder().inactiveIcon(fi).inactiveText(bgLabel).text(filter).pane(stack).build();
}
@@ -0,0 +1,124 @@
package io.xpipe.extension.comp;
import io.xpipe.fxcomps.CompStructure;
import io.xpipe.fxcomps.util.PlatformThread;
import io.xpipe.fxcomps.util.SimpleChangeListener;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.css.Size;
import javafx.css.SizeUnits;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.web.WebView;
import lombok.Builder;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.Value;
import java.util.Set;
import java.util.regex.Pattern;
@Getter
public class SvgComp {
private static Size parseSize(String string) {
for (SizeUnits unit : SizeUnits.values()) {
if (string.endsWith(unit.toString())) {
return new Size(
Double.parseDouble(string.substring(
0, string.length() - unit.toString().length())),
unit);
}
}
return new Size(Double.parseDouble(string), SizeUnits.PX);
}
@SneakyThrows
public static SvgComp create(ObservableValue<String> content) {
var widthProperty = new SimpleIntegerProperty();
var heightProperty = new SimpleIntegerProperty();
SimpleChangeListener.apply(content, val -> {
var regularExpression = Pattern.compile("<svg.+?width=\"([^\s]+)\"", Pattern.DOTALL);
var matcher = regularExpression.matcher(val);
matcher.find();
var width = matcher.group(1);
regularExpression = Pattern.compile("<svg.+?height=\"([^\s]+)\"", Pattern.DOTALL);
matcher = regularExpression.matcher(val);
matcher.find();
var height = matcher.group(1);
var widthInteger = parseSize(width).pixels();
var heightInteger = parseSize(height).pixels();
widthProperty.set((int) Math.ceil(widthInteger));
heightProperty.set((int) Math.ceil(heightInteger));
});
return new SvgComp(widthProperty, heightProperty, content);
}
@Value
@Builder
public static class Structure implements CompStructure<StackPane> {
StackPane pane;
WebView webView;
@Override
public StackPane get() {
return pane;
}
}
private final ObservableValue<Number> width;
private final ObservableValue<Number> height;
private final ObservableValue<String> svgContent;
public SvgComp(ObservableValue<Number> width, ObservableValue<Number> height, ObservableValue<String> svgContent) {
this.width = PlatformThread.sync(width);
this.height = PlatformThread.sync(height);
this.svgContent = PlatformThread.sync(svgContent);
}
private String getHtml(String content) {
return "<html><body style='margin: 0; padding: 0; border: none;' >" + content + "</body></html>";
}
private WebView createWebView() {
var wv = new WebView();
wv.setPageFill(Color.TRANSPARENT);
wv.setDisable(true);
wv.getEngine().loadContent(getHtml(svgContent.getValue()));
svgContent.addListener((c, o, n) -> {
wv.getEngine().loadContent(getHtml(n));
});
// Hide scrollbars that popup on every content change. Bug in WebView?
wv.getChildrenUnmodifiable().addListener((ListChangeListener<Node>) change -> {
Set<Node> scrolls = wv.lookupAll(".scroll-bar");
for (Node scroll : scrolls) {
scroll.setVisible(false);
}
});
// As the aspect ratio of the WebView is kept constant, we can compute the zoom only using the width
wv.zoomProperty()
.bind(Bindings.createDoubleBinding(
() -> {
return wv.getWidth() / width.getValue().doubleValue();
},
wv.widthProperty(),
width));
wv.maxWidthProperty().bind(wv.prefWidthProperty());
wv.maxHeightProperty().bind(wv.prefHeightProperty());
return wv;
}
public WebView createWebview() {
var wv = createWebView();
wv.getStyleClass().add("svg-comp");
return wv;
}
}
@@ -14,6 +14,7 @@ import lombok.EqualsAndHashCode;
import lombok.Value;
import net.synedra.validatorfx.Check;
import java.util.Arrays;
import java.util.LinkedHashMap;
@Value
@@ -28,19 +29,41 @@ public class WriteModeChoiceComp extends SimpleComp implements Validatable {
public WriteModeChoiceComp(Property<WriteMode> selected, WriteMode[] available) {
this.selected = selected;
this.available = available;
if (available.length == 1) {
selected.setValue(available[0]);
}
check = Validators.nonNull(validator, I18n.observable("mode"), selected);
}
@Override
protected Region createSimple() {
var a = Arrays.asList(available);
var map = new LinkedHashMap<WriteMode, ObservableValue<String>>();
map.put(WriteMode.REPLACE, I18n.observable("replace"));
map.put(WriteMode.APPEND, I18n.observable("append"));
map.put(WriteMode.PREPEND, I18n.observable("prepend"));
var replaceIndex = -1;
if (a.contains(WriteMode.REPLACE)) {
map.put(WriteMode.REPLACE, I18n.observable("replace"));
replaceIndex = 0;
}
var appendIndex = -1;
if (a.contains(WriteMode.APPEND)) {
map.put(WriteMode.APPEND, I18n.observable("append"));
appendIndex = replaceIndex + 1;
}
var prependIndex = -1;
if (a.contains(WriteMode.PREPEND)) {
map.put(WriteMode.PREPEND, I18n.observable("prepend"));
prependIndex = Math.max(replaceIndex, appendIndex) + 1;
}
int finalReplaceIndex = replaceIndex;
int finalAppendIndex = appendIndex;
int finalPrependIndex = prependIndex;
return new ToggleGroupComp<>(selected, map).apply(struc -> {
new FancyTooltipAugment<>("extension.replaceDescription").augment(struc.get().getChildren().get(0));
new FancyTooltipAugment<>("extension.appendDescription").augment(struc.get().getChildren().get(1));
new FancyTooltipAugment<>("extension.prependDescription").augment(struc.get().getChildren().get(2));
if (finalReplaceIndex != -1) new FancyTooltipAugment<>("extension.replaceDescription").augment(struc.get().getChildren().get(0));
if (finalAppendIndex != -1) new FancyTooltipAugment<>("extension.appendDescription").augment(struc.get().getChildren().get(finalAppendIndex));
if (finalPrependIndex != -1) new FancyTooltipAugment<>("extension.prependDescription").augment(struc.get().getChildren().get(finalPrependIndex));
}).apply(struc -> check.decorates(struc.get())).createRegion();
}
}
@@ -47,9 +47,5 @@ public interface PrefsChoiceValue extends Translatable {
return I18n.get(getId());
}
default String toDefaultString() {
return I18n.get(getId());
}
String getId();
}
@@ -101,8 +101,6 @@ public class CustomComboBoxBuilder<T> {
cb.valueProperty().addListener((c, o, n) -> {
if (nodeMap.containsKey(n)) {
if (veto != null && !veto.test(nodeMap.get(n))) {
cb.setValue(o);
;
return;
}
selected.setValue(nodeMap.get(n));
@@ -1,15 +1,85 @@
package io.xpipe.extension.util;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.ListView;
import javafx.scene.control.MultipleSelectionModel;
import javafx.scene.control.ScrollBar;
import java.util.Set;
public class PrettyListView<T> extends ListView<T> {
public static class NoSelectionModel<T> extends MultipleSelectionModel<T> {
@Override
public ObservableList<Integer> getSelectedIndices() {
return FXCollections.emptyObservableList();
}
@Override
public ObservableList<T> getSelectedItems() {
return FXCollections.emptyObservableList();
}
@Override
public void selectIndices(int index, int... indices) {
}
@Override
public void selectAll() {
}
@Override
public void selectFirst() {
}
@Override
public void selectLast() {
}
@Override
public void clearAndSelect(int index) {
}
@Override
public void select(int index) {
}
@Override
public void select(T obj) {
}
@Override
public void clearSelection(int index) {
}
@Override
public void clearSelection() {
}
@Override
public boolean isSelected(int index) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public void selectPrevious() {
}
@Override
public void selectNext() {
}
}
private final ScrollBar vBar = new ScrollBar();
private final ScrollBar hBar = new ScrollBar();
@@ -34,6 +104,10 @@ public class PrettyListView<T> extends ListView<T> {
hBar.visibleProperty().setValue(false);
}
public void disableSelection() {
setSelectionModel(new NoSelectionModel<>());
}
private void bindScrollBars() {
final Set<Node> nodes = lookupAll("VirtualScrollBar");
for (Node node : nodes) {
@@ -1,5 +1,7 @@
package io.xpipe.extension.util;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.LocalStore;
import io.xpipe.core.store.ShellStore;
import io.xpipe.extension.I18n;
import javafx.beans.value.ObservableValue;
@@ -11,7 +13,7 @@ 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 ) {
if (c.get("val") == null) {
c.error(I18n.get("extension.mustNotBeEmpty", name.getValue()));
}
});
@@ -23,6 +25,12 @@ public class Validators {
}
}
public static void namedStoreExists(DataStore store, String name) {
if (!XPipeDaemon.getInstance().getNamedStores().contains(store) && !(store instanceof LocalStore)) {
throw new IllegalArgumentException(I18n.get("extension.missingStore", name));
}
}
public static void hostFeature(ShellStore host, Predicate<ShellStore> predicate, String name) {
if (!predicate.test(host)) {
throw new IllegalArgumentException(I18n.get("extension.hostFeatureUnsupported", name));
@@ -25,7 +25,9 @@ public interface XPipeDaemon {
public Image image(String file);
<T extends Comp<?> & Validatable> T streamStoreChooser(Property<DataStore> storeProperty, Property<DataSourceProvider<?>> provider);
<T extends Comp<?> & Validatable> T streamStoreChooser(Property<DataStore> storeProperty, Property<DataSourceProvider<?>> provider,
boolean showAnonymous,
boolean showSaved);
<T extends Comp<?> & Validatable> T namedStoreChooser(
ObservableValue<Predicate<DataStore>> filter, Property<? extends DataStore> selected, DataStoreProvider.Category category
+1
View File
@@ -18,6 +18,7 @@ open module io.xpipe.extension {
requires static javafx.base;
requires static javafx.graphics;
requires static javafx.controls;
requires static javafx.web;
requires static io.xpipe.fxcomps;
requires static lombok;
requires static org.controlsfx.controls;