mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-10-07 10:22:19 -04:00
Rework
This commit is contained in:
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user