mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 09:00:26 +00:00
More script rework, state rework, category fixes, and bug fixes
This commit is contained in:
parent
87d1d45cae
commit
43d7e0830c
33 changed files with 500 additions and 309 deletions
|
@ -10,9 +10,11 @@ import javafx.beans.binding.Bindings;
|
|||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.scene.layout.Region;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class StoreToggleComp extends SimpleComp {
|
||||
|
||||
private final String nameKey;
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Optional;
|
|||
@Getter
|
||||
public class StoreCategoryWrapper {
|
||||
|
||||
private final DataStoreCategory root;
|
||||
private final int depth;
|
||||
private final Property<String> name;
|
||||
private final DataStoreCategory category;
|
||||
|
@ -30,15 +31,18 @@ public class StoreCategoryWrapper {
|
|||
|
||||
public StoreCategoryWrapper(DataStoreCategory category) {
|
||||
var d = 0;
|
||||
DataStoreCategory last = category;
|
||||
DataStoreCategory p = category;
|
||||
while ((p = DataStorage.get()
|
||||
.getStoreCategoryIfPresent(p.getParentCategory())
|
||||
.orElse(null))
|
||||
!= null) {
|
||||
d++;
|
||||
last = p;
|
||||
}
|
||||
depth = d;
|
||||
|
||||
this.root = last;
|
||||
this.category = category;
|
||||
this.name = new SimpleStringProperty();
|
||||
this.lastAccess = new SimpleObjectProperty<>();
|
||||
|
|
|
@ -290,6 +290,16 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
contextMenu.getItems().add(item);
|
||||
|
||||
if (menu != null) {
|
||||
var run = new MenuItem(null, new FontIcon("mdi2c-code-greater-than"));
|
||||
run.textProperty().bind(AppI18n.observable("base.execute"));
|
||||
run.setOnAction(event -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
p.getKey().getDataStoreCallSite().createAction(wrapper.getEntry().getStore().asNeeded()).execute();
|
||||
});
|
||||
});
|
||||
menu.getItems().add(run);
|
||||
|
||||
|
||||
var sc = new MenuItem(null, new FontIcon("mdi2c-code-greater-than"));
|
||||
var url = "xpipe://action/" + p.getKey().getId() + "/"
|
||||
+ wrapper.getEntry().getUuid();
|
||||
|
@ -329,15 +339,17 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
contextMenu.getItems().add(browse);
|
||||
}
|
||||
|
||||
var move = new Menu(AppI18n.get("moveTo"), new FontIcon("mdi2f-folder-move-outline"));
|
||||
StoreViewState.get().getSortedCategories().forEach(storeCategoryWrapper -> {
|
||||
MenuItem m = new MenuItem(storeCategoryWrapper.getName());
|
||||
m.setOnAction(event -> {
|
||||
wrapper.moveTo(storeCategoryWrapper.getCategory());
|
||||
if (wrapper.getEntry().getProvider() != null && wrapper.getEntry().getProvider().canMoveCategories()) {
|
||||
var move = new Menu(AppI18n.get("moveTo"), new FontIcon("mdi2f-folder-move-outline"));
|
||||
StoreViewState.get().getSortedCategories().forEach(storeCategoryWrapper -> {
|
||||
MenuItem m = new MenuItem(storeCategoryWrapper.getName());
|
||||
m.setOnAction(event -> {
|
||||
wrapper.moveTo(storeCategoryWrapper.getCategory());
|
||||
});
|
||||
move.getItems().add(m);
|
||||
});
|
||||
move.getItems().add(m);
|
||||
});
|
||||
contextMenu.getItems().add(move);
|
||||
contextMenu.getItems().add(move);
|
||||
}
|
||||
|
||||
var del = new MenuItem(AppI18n.get("remove"), new FontIcon("mdal-delete_outline"));
|
||||
del.disableProperty().bind(wrapper.getDeletable().not());
|
||||
|
|
|
@ -7,7 +7,9 @@ import io.xpipe.app.fxcomps.SimpleComp;
|
|||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.app.fxcomps.impl.FilterComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
|
@ -21,17 +23,28 @@ import org.kordamp.ikonli.javafx.FontIcon;
|
|||
public class StoreEntryListSideComp extends SimpleComp {
|
||||
|
||||
private Region createGroupListHeader() {
|
||||
var label = new Label("Connections");
|
||||
var label = new Label();
|
||||
label.textProperty().bind(Bindings.createStringBinding(() -> {
|
||||
return StoreViewState.get().getActiveCategory().getValue().getRoot().equals(StoreViewState.get().getAllConnectionsCategory().getCategory()) ? "Connections" : "Scripts";
|
||||
}, StoreViewState.get().getActiveCategory()));
|
||||
label.getStyleClass().add("name");
|
||||
|
||||
var shownList = BindingsHelper.filteredContentBinding(
|
||||
var all = BindingsHelper.filteredContentBinding(
|
||||
StoreViewState.get().getAllEntries(),
|
||||
storeEntryWrapper -> {
|
||||
var cat = DataStorage.get().getStoreCategoryIfPresent(storeEntryWrapper.getEntry().getCategoryUuid()).orElse(null);
|
||||
var storeRoot = cat != null ? DataStorage.get().getRootCategory(cat) : null;
|
||||
return StoreViewState.get().getActiveCategory().getValue().getRoot().equals(storeRoot);
|
||||
},
|
||||
StoreViewState.get().getActiveCategory());
|
||||
var shownList = BindingsHelper.filteredContentBinding(
|
||||
all,
|
||||
storeEntryWrapper -> {
|
||||
return storeEntryWrapper.shouldShow(
|
||||
StoreViewState.get().getFilterString().getValue());
|
||||
},
|
||||
StoreViewState.get().getFilterString());
|
||||
var count = new CountComp<>(shownList, StoreViewState.get().getAllEntries());
|
||||
var count = new CountComp<>(shownList, all);
|
||||
|
||||
var spacer = new Region();
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import io.xpipe.app.util.ThreadHelper;
|
|||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.Duration;
|
||||
|
@ -34,8 +35,8 @@ public class StoreEntryWrapper {
|
|||
private final Property<ActionProvider.DefaultDataStoreCallSite<?>> defaultActionProvider;
|
||||
private final BooleanProperty deletable = new SimpleBooleanProperty();
|
||||
private final BooleanProperty expanded = new SimpleBooleanProperty();
|
||||
private final Property<StoreCategoryWrapper> category = new SimpleObjectProperty<>();
|
||||
private final Property<Object> persistentState = new SimpleObjectProperty<>();
|
||||
private final MapProperty<String, Object> cache = new SimpleMapProperty<>(FXCollections.observableHashMap());
|
||||
|
||||
public StoreEntryWrapper(DataStoreEntry entry) {
|
||||
this.entry = entry;
|
||||
|
@ -144,6 +145,7 @@ public class StoreEntryWrapper {
|
|||
expanded.setValue(entry.isExpanded());
|
||||
observing.setValue(entry.isObserving());
|
||||
persistentState.setValue(entry.getStorePersistentState());
|
||||
cache.putAll(entry.getStoreCache());
|
||||
|
||||
inRefresh.setValue(entry.isInRefresh());
|
||||
deletable.setValue(entry.getConfiguration().isDeletable()
|
||||
|
|
|
@ -2,8 +2,8 @@ package io.xpipe.app.comp.storage.store;
|
|||
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.StoreCategoryListComp;
|
||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.app.util.FeatureProvider;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
@ -17,7 +17,7 @@ public class StoreSidebarComp extends SimpleComp {
|
|||
var sideBar = new VerticalComp(List.of(
|
||||
new StoreEntryListSideComp(),
|
||||
new StoreSortComp(),
|
||||
FeatureProvider.get().organizationComp(),
|
||||
new StoreCategoryListComp(),
|
||||
Comp.of(() -> new Region()).styleClass("bar").styleClass("filler-bar")));
|
||||
sideBar.apply(struc -> struc.get().setFillWidth(true));
|
||||
sideBar.apply(s -> VBox.setVgrow(s.get().getChildren().get(2), Priority.ALWAYS));
|
||||
|
|
|
@ -48,7 +48,7 @@ public class StoreViewState {
|
|||
} catch (Exception exception) {
|
||||
tl = new StoreSection(null, FXCollections.emptyObservableList(), FXCollections.emptyObservableList(), 0);
|
||||
categories.setAll(new StoreCategoryWrapper(DataStorage.get().getAllCategory()));
|
||||
activeCategory.setValue(getAllCategory());
|
||||
activeCategory.setValue(getAllConnectionsCategory());
|
||||
ErrorEvent.fromThrowable(exception).handle();
|
||||
}
|
||||
topLevelSection = tl;
|
||||
|
@ -58,6 +58,17 @@ public class StoreViewState {
|
|||
Comparator<StoreCategoryWrapper> comparator = new Comparator<>() {
|
||||
@Override
|
||||
public int compare(StoreCategoryWrapper o1, StoreCategoryWrapper o2) {
|
||||
var o1Root = o1.getRoot();
|
||||
var o2Root = o2.getRoot();
|
||||
|
||||
if (o1Root.equals(getAllConnectionsCategory().getCategory()) && !o1Root.equals(o2Root)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (o2Root.equals(getAllConnectionsCategory().getCategory()) && !o1Root.equals(o2Root)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (o1.getParent() == null && o2.getParent() == null) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -81,19 +92,18 @@ public class StoreViewState {
|
|||
return categories.sorted(comparator);
|
||||
}
|
||||
|
||||
public StoreCategoryWrapper getAllCategory() {
|
||||
public StoreCategoryWrapper getAllConnectionsCategory() {
|
||||
return categories.stream()
|
||||
.filter(storeCategoryWrapper ->
|
||||
storeCategoryWrapper.getCategory().getUuid().equals(DataStorage.ALL_CATEGORY_UUID))
|
||||
storeCategoryWrapper.getCategory().getUuid().equals(DataStorage.ALL_CONNECTIONS_CATEGORY_UUID))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
|
||||
public StoreCategoryWrapper getScriptsCategory() {
|
||||
public StoreCategoryWrapper getAllScriptsCategory() {
|
||||
return categories.stream()
|
||||
.filter(storeCategoryWrapper ->
|
||||
storeCategoryWrapper.getCategory().getUuid().equals(DataStorage.SCRIPTS_CATEGORY_UUID))
|
||||
storeCategoryWrapper.getCategory().getUuid().equals(DataStorage.ALL_SCRIPTS_CATEGORY_UUID))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
}
|
||||
|
|
|
@ -64,8 +64,8 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
|||
Property<DataStore> store,
|
||||
Predicate<DataStoreProvider> filter,
|
||||
String initialName,
|
||||
boolean exists, boolean staticDisplay
|
||||
) {
|
||||
boolean exists,
|
||||
boolean staticDisplay) {
|
||||
this.parent = parent;
|
||||
this.provider = provider;
|
||||
this.store = store;
|
||||
|
@ -115,7 +115,8 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
|||
}
|
||||
});
|
||||
},
|
||||
true, true);
|
||||
true,
|
||||
true);
|
||||
}
|
||||
|
||||
public static void showCreation(DataStoreProvider selected, Predicate<DataStoreProvider> filter) {
|
||||
|
@ -134,7 +135,8 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
|||
ErrorEvent.fromThrowable(ex).handle();
|
||||
}
|
||||
},
|
||||
false, false);
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
public static void show(
|
||||
|
@ -155,8 +157,8 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
|||
window -> {
|
||||
return new MultiStepComp() {
|
||||
|
||||
private final GuiDsStoreCreator creator =
|
||||
new GuiDsStoreCreator(this, prop, store, filter, initialName, exists, staticDisplay);
|
||||
private final GuiDsStoreCreator creator = new GuiDsStoreCreator(
|
||||
this, prop, store, filter, initialName, exists, staticDisplay);
|
||||
|
||||
@Override
|
||||
protected List<Entry> setup() {
|
||||
|
@ -227,7 +229,21 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
|||
return null;
|
||||
}
|
||||
|
||||
return DataStoreEntry.createNew(UUID.randomUUID(), DataStorage.get().getSelectedCategory().getUuid(), name.getValue(), store.getValue());
|
||||
var testE = DataStoreEntry.createNew(
|
||||
UUID.randomUUID(),
|
||||
DataStorage.get().getSelectedCategory().getUuid(),
|
||||
name.getValue(),
|
||||
store.getValue());
|
||||
var parent = provider.getValue().getDisplayParent(testE);
|
||||
return DataStoreEntry.createNew(
|
||||
UUID.randomUUID(),
|
||||
parent != null
|
||||
? parent.getCategoryUuid()
|
||||
: DataStorage.get()
|
||||
.getSelectedCategory()
|
||||
.getUuid(),
|
||||
name.getValue(),
|
||||
store.getValue());
|
||||
},
|
||||
entry)
|
||||
.build();
|
||||
|
|
|
@ -22,6 +22,10 @@ import java.util.List;
|
|||
|
||||
public interface DataStoreProvider {
|
||||
|
||||
default boolean canMoveCategories() {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean alwaysShowSummary() {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -39,31 +39,16 @@ import java.util.function.Predicate;
|
|||
public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
||||
|
||||
public static <T extends DataStore> DataStoreChoiceComp<T> other(
|
||||
Property<DataStoreEntryRef<T>> selected, Class<T> clazz, Predicate<DataStoreEntryRef<T>> filter) {
|
||||
return new DataStoreChoiceComp<>(Mode.OTHER, null, selected, clazz, filter);
|
||||
Property<DataStoreEntryRef<T>> selected, Class<T> clazz, Predicate<DataStoreEntryRef<T>> filter, StoreCategoryWrapper initialCategory) {
|
||||
return new DataStoreChoiceComp<>(Mode.OTHER, null, selected, clazz, filter, initialCategory);
|
||||
}
|
||||
|
||||
public static DataStoreChoiceComp<ShellStore> proxy(Property<DataStoreEntryRef<ShellStore>> selected) {
|
||||
return new DataStoreChoiceComp<>(Mode.PROXY, null, selected, ShellStore.class, null);
|
||||
public static DataStoreChoiceComp<ShellStore> proxy(Property<DataStoreEntryRef<ShellStore>> selected, StoreCategoryWrapper initialCategory) {
|
||||
return new DataStoreChoiceComp<>(Mode.PROXY, null, selected, ShellStore.class, null, initialCategory);
|
||||
}
|
||||
|
||||
public static DataStoreChoiceComp<ShellStore> host(Property<DataStoreEntryRef<ShellStore>> selected) {
|
||||
return new DataStoreChoiceComp<>(Mode.HOST, null, selected, ShellStore.class, null);
|
||||
}
|
||||
|
||||
public static DataStoreChoiceComp<ShellStore> environment(
|
||||
DataStoreEntry self, Property<DataStoreEntryRef<ShellStore>> selected) {
|
||||
return new DataStoreChoiceComp<>(Mode.HOST, self, selected, ShellStore.class, shellStoreDataStoreEntryRef -> shellStoreDataStoreEntryRef.get().getProvider().canHaveSubShells());
|
||||
}
|
||||
|
||||
public static DataStoreChoiceComp<ShellStore> proxy(
|
||||
DataStoreEntry self, Property<DataStoreEntryRef<ShellStore>> selected) {
|
||||
return new DataStoreChoiceComp<>(Mode.PROXY, self, selected, ShellStore.class, null);
|
||||
}
|
||||
|
||||
public static DataStoreChoiceComp<ShellStore> host(
|
||||
DataStoreEntry self, Property<DataStoreEntryRef<ShellStore>> selected) {
|
||||
return new DataStoreChoiceComp<>(Mode.HOST, self, selected, ShellStore.class, null);
|
||||
public static DataStoreChoiceComp<ShellStore> host(Property<DataStoreEntryRef<ShellStore>> selected, StoreCategoryWrapper initialCategory) {
|
||||
return new DataStoreChoiceComp<>(Mode.HOST, null, selected, ShellStore.class, null, initialCategory);
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
|
@ -77,6 +62,7 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
|||
private final Property<DataStoreEntryRef<T>> selected;
|
||||
private final Class<T> storeClass;
|
||||
private final Predicate<DataStoreEntryRef<T>> applicableCheck;
|
||||
private final StoreCategoryWrapper initialCategory;
|
||||
|
||||
private Popover popover;
|
||||
|
||||
|
@ -84,8 +70,9 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
|||
// Rebuild popover if we have a non-null condition to allow for the content to be updated in case the condition
|
||||
// changed
|
||||
if (popover == null || applicableCheck != null) {
|
||||
var cur = StoreViewState.get().getActiveCategory().getValue();
|
||||
var selectedCategory = new SimpleObjectProperty<>(
|
||||
StoreViewState.get().getActiveCategory().getValue());
|
||||
initialCategory != null ? (initialCategory.getRoot().equals(cur.getRoot()) ? cur : initialCategory) : cur);
|
||||
var filterText = new SimpleStringProperty();
|
||||
popover = new Popover();
|
||||
Predicate<StoreEntryWrapper> applicable = storeEntryWrapper -> {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.app.fxcomps.impl;
|
||||
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
import io.xpipe.app.comp.storage.store.StoreCategoryWrapper;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
|
@ -19,11 +20,15 @@ public class DataStoreListChoiceComp<T extends DataStore> extends SimpleComp {
|
|||
private final ListProperty<DataStoreEntryRef<T>> selectedList;
|
||||
private final Class<T> storeClass;
|
||||
private final Predicate<DataStoreEntryRef<T>> applicableCheck;
|
||||
private final StoreCategoryWrapper initialCategory;
|
||||
|
||||
public DataStoreListChoiceComp(ListProperty<DataStoreEntryRef<T>> selectedList, Class<T> storeClass, Predicate<DataStoreEntryRef<T>> applicableCheck) {
|
||||
public DataStoreListChoiceComp(ListProperty<DataStoreEntryRef<T>> selectedList, Class<T> storeClass, Predicate<DataStoreEntryRef<T>> applicableCheck,
|
||||
StoreCategoryWrapper initialCategory
|
||||
) {
|
||||
this.selectedList = selectedList;
|
||||
this.storeClass = storeClass;
|
||||
this.applicableCheck = applicableCheck;
|
||||
this.initialCategory = initialCategory;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,7 +47,7 @@ public class DataStoreListChoiceComp<T extends DataStore> extends SimpleComp {
|
|||
return hbox;
|
||||
}).padding(new Insets(0)).apply(struc -> struc.get().setMinHeight(0)).apply(struc -> ((VBox) struc.get().getContent()).setSpacing(5));
|
||||
var selected = new SimpleObjectProperty<DataStoreEntryRef<T>>();
|
||||
var add = new DataStoreChoiceComp<T>(DataStoreChoiceComp.Mode.OTHER, null, selected, storeClass, applicableCheck);
|
||||
var add = new DataStoreChoiceComp<T>(DataStoreChoiceComp.Mode.OTHER, null, selected, storeClass, applicableCheck, initialCategory);
|
||||
selected.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
if (!selectedList.contains(newValue)
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
package io.xpipe.app.fxcomps.impl;
|
||||
|
||||
import io.xpipe.app.comp.base.CountComp;
|
||||
import io.xpipe.app.comp.base.LazyTextFieldComp;
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
import io.xpipe.app.comp.storage.store.StoreCategoryWrapper;
|
||||
import io.xpipe.app.comp.storage.store.StoreViewState;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.Region;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Value
|
||||
public class StoreCategoryComp extends SimpleComp {
|
||||
|
||||
private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
|
||||
|
||||
StoreCategoryWrapper category;
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var i = Bindings.createStringBinding(
|
||||
() -> {
|
||||
if (!DataStorage.get().supportsSharing()) {
|
||||
return "mdal-keyboard_arrow_right";
|
||||
}
|
||||
|
||||
return category.getShare().getValue() ?
|
||||
"mdi2a-account-convert" : "mdi2a-account-cancel";
|
||||
},
|
||||
category.getShare());
|
||||
var icon = new IconButtonComp(i).apply(struc -> AppFont.small(struc.get())).apply(struc -> {
|
||||
struc.get().setAlignment(Pos.CENTER);
|
||||
struc.get().setPadding(new Insets(0, 0, 6, 0));
|
||||
});
|
||||
var name = new LazyTextFieldComp(category.nameProperty())
|
||||
.apply(struc -> {
|
||||
struc.get().prefWidthProperty().unbind();
|
||||
struc.get().setPrefWidth(100);
|
||||
struc.getTextField().minWidthProperty().bind(struc.get().widthProperty());
|
||||
})
|
||||
.styleClass("name")
|
||||
.createRegion();
|
||||
var showing = new SimpleBooleanProperty();
|
||||
var settings = new IconButtonComp("mdomz-settings")
|
||||
.styleClass("settings")
|
||||
.apply(new ContextMenuAugment<>(mouseEvent -> mouseEvent.getButton() == MouseButton.PRIMARY, () -> {
|
||||
var cm = createContextMenu(name);
|
||||
showing.bind(cm.showingProperty());
|
||||
return cm;
|
||||
}));
|
||||
var shownList = BindingsHelper.filteredContentBinding(
|
||||
category.getContainedEntries(),
|
||||
storeEntryWrapper -> {
|
||||
return storeEntryWrapper.shouldShow(
|
||||
StoreViewState.get().getFilterString().getValue());
|
||||
},
|
||||
StoreViewState.get().getFilterString());
|
||||
var count = new CountComp<>(shownList, category.getContainedEntries(), string -> "(" + string + ")");
|
||||
var hover = new SimpleBooleanProperty();
|
||||
var h = new HorizontalComp(List.of(
|
||||
icon,
|
||||
Comp.hspacer(4),
|
||||
Comp.of(() -> name),
|
||||
Comp.hspacer(),
|
||||
count.hide(BindingsHelper.persist(hover.or(showing))),
|
||||
settings.hide(BindingsHelper.persist(hover.not().and(showing.not())))));
|
||||
h.apply(struc -> hover.bind(struc.get().hoverProperty()));
|
||||
h.apply(struc -> struc.get().setOnMouseClicked(event -> {
|
||||
category.select();
|
||||
event.consume();
|
||||
}));
|
||||
h.apply(new ContextMenuAugment<>(
|
||||
mouseEvent -> mouseEvent.getButton() == MouseButton.SECONDARY, () -> createContextMenu(name)));
|
||||
h.padding(new Insets(0, 10, 0, (category.getDepth() * 8)));
|
||||
h.styleClass("category-button");
|
||||
var l = category.getChildren()
|
||||
.sorted(Comparator.comparing(
|
||||
storeCategoryWrapper -> storeCategoryWrapper.getName().toLowerCase(Locale.ROOT)));
|
||||
var children = new ListBoxViewComp<>(l, l, storeCategoryWrapper -> new StoreCategoryComp(storeCategoryWrapper));
|
||||
|
||||
var emptyBinding = Bindings.isEmpty(category.getChildren());
|
||||
var v = new VerticalComp(List.of(h, Comp.separator().hide(emptyBinding), children.hide(emptyBinding)));
|
||||
v.styleClass("category");
|
||||
v.apply(struc -> {
|
||||
SimpleChangeListener.apply(StoreViewState.get().getActiveCategory(), val -> {
|
||||
struc.get().pseudoClassStateChanged(SELECTED, val.equals(category));
|
||||
});
|
||||
});
|
||||
|
||||
return v.createRegion();
|
||||
}
|
||||
|
||||
private ContextMenu createContextMenu(Region text) {
|
||||
var contextMenu = new ContextMenu();
|
||||
AppFont.normal(contextMenu.getStyleableNode());
|
||||
|
||||
var newCategory = new MenuItem(AppI18n.get("newCategory"), new FontIcon("mdi2p-plus-thick"));
|
||||
newCategory.setOnAction(event -> {
|
||||
DataStorage.get()
|
||||
.addStoreCategory(
|
||||
DataStoreCategory.createNew(category.getCategory().getUuid(), "New category"));
|
||||
});
|
||||
contextMenu.getItems().add(newCategory);
|
||||
|
||||
var share = new MenuItem();
|
||||
share.textProperty().bind(Bindings.createStringBinding(() -> {
|
||||
if (category.getShare().getValue()) {
|
||||
return AppI18n.get("unshare");
|
||||
} else {
|
||||
return AppI18n.get("share");
|
||||
}
|
||||
},category.getShare()));
|
||||
share.graphicProperty().bind(Bindings.createObjectBinding(() -> {
|
||||
if (category.getShare().getValue()) {
|
||||
return new FontIcon("mdi2b-block-helper");
|
||||
} else {
|
||||
return new FontIcon("mdi2s-share");
|
||||
}
|
||||
},category.getShare()));
|
||||
share.setOnAction(event -> {
|
||||
category.getShare().setValue(!category.getShare().getValue());
|
||||
});
|
||||
contextMenu.getItems().add(share);
|
||||
|
||||
var refresh = new MenuItem(AppI18n.get("rename"), new FontIcon("mdal-360"));
|
||||
refresh.setOnAction(event -> {
|
||||
text.requestFocus();
|
||||
});
|
||||
contextMenu.getItems().add(refresh);
|
||||
|
||||
var del = new MenuItem(AppI18n.get("remove"), new FontIcon("mdal-delete_outline"));
|
||||
del.setOnAction(event -> {
|
||||
category.delete();
|
||||
});
|
||||
contextMenu.getItems().add(del);
|
||||
|
||||
return contextMenu;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.xpipe.app.fxcomps.impl;
|
||||
|
||||
import io.xpipe.app.comp.storage.store.StoreViewState;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class StoreCategoryListComp extends SimpleComp {
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var all = StoreViewState.get().getAllConnectionsCategory();
|
||||
var scripts = StoreViewState.get().getAllScriptsCategory();
|
||||
return new VerticalComp(List.of(
|
||||
new StoreCategoryComp(all),
|
||||
Comp.vspacer(10),
|
||||
new StoreCategoryComp(scripts)))
|
||||
.apply(struc -> struc.get().setFillWidth(true))
|
||||
.apply(struc -> struc.get().setSpacing(3))
|
||||
.styleClass("store-category-bar")
|
||||
.createRegion();
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ public class DataStateProviderImpl extends DataStateProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
var old = entry.get().getStoreCache().put(key, value);
|
||||
entry.get().setStoreCache(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,8 +73,12 @@ public class DataStateProviderImpl extends DataStateProvider {
|
|||
return def.get();
|
||||
}
|
||||
|
||||
var result = entry.get().getStoreCache().computeIfAbsent(key, k -> def.get());
|
||||
return c.cast(result);
|
||||
var r = entry.get().getStoreCache().get(key);
|
||||
if (r == null) {
|
||||
r = def .get();
|
||||
entry.get().setStoreCache(key, r);
|
||||
}
|
||||
return c.cast(r);
|
||||
}
|
||||
|
||||
public boolean isInStorage(DataStore store) {
|
||||
|
|
|
@ -23,8 +23,8 @@ import java.util.stream.Stream;
|
|||
|
||||
public abstract class DataStorage {
|
||||
|
||||
public static final UUID ALL_CATEGORY_UUID = UUID.fromString("bfb0b51a-e7a3-4ce4-8878-8d4cb5828d6c");
|
||||
public static final UUID SCRIPTS_CATEGORY_UUID = UUID.fromString("19024cf9-d192-41a9-88a6-a22694cf716a");
|
||||
public static final UUID ALL_CONNECTIONS_CATEGORY_UUID = UUID.fromString("bfb0b51a-e7a3-4ce4-8878-8d4cb5828d6c");
|
||||
public static final UUID ALL_SCRIPTS_CATEGORY_UUID = UUID.fromString("19024cf9-d192-41a9-88a6-a22694cf716a");
|
||||
public static final UUID PREDEFINED_SCRIPTS_CATEGORY_UUID = UUID.fromString("5faf1d71-0efc-4293-8b70-299406396973");
|
||||
public static final UUID CUSTOM_SCRIPTS_CATEGORY_UUID = UUID.fromString("d3496db5-b709-41f9-abc0-ee0a660fbab9");
|
||||
public static final UUID DEFAULT_CATEGORY_UUID = UUID.fromString("97458c07-75c0-4f9d-a06e-92d8cdf67c40");
|
||||
|
@ -68,7 +68,7 @@ public abstract class DataStorage {
|
|||
}
|
||||
|
||||
public DataStoreCategory getAllCategory() {
|
||||
return getStoreCategoryIfPresent(ALL_CATEGORY_UUID).orElseThrow();
|
||||
return getStoreCategoryIfPresent(ALL_CONNECTIONS_CATEGORY_UUID).orElseThrow();
|
||||
}
|
||||
|
||||
private static boolean shouldPersist() {
|
||||
|
@ -370,6 +370,17 @@ public abstract class DataStorage {
|
|||
|
||||
public abstract boolean supportsSharing();
|
||||
|
||||
public DataStoreCategory getRootCategory(DataStoreCategory category) {
|
||||
DataStoreCategory last = category;
|
||||
DataStoreCategory p = category;
|
||||
while ((p = DataStorage.get()
|
||||
.getStoreCategoryIfPresent(p.getParentCategory())
|
||||
.orElse(null))
|
||||
!= null) {
|
||||
last = p;
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
public Optional<DataStoreCategory> getStoreCategoryIfPresent(UUID uuid) {
|
||||
if (uuid == null) {
|
||||
|
@ -565,7 +576,7 @@ public abstract class DataStorage {
|
|||
}
|
||||
|
||||
public void deleteStoreCategory(@NonNull DataStoreCategory cat) {
|
||||
if (cat.getUuid().equals(DEFAULT_CATEGORY_UUID) || cat.getUuid().equals(ALL_CATEGORY_UUID)) {
|
||||
if (cat.getUuid().equals(DEFAULT_CATEGORY_UUID) || cat.getUuid().equals(ALL_CONNECTIONS_CATEGORY_UUID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -211,6 +211,12 @@ public class DataStoreEntry extends StorageElement {
|
|||
return new DataStoreEntryRef<T>(this);
|
||||
}
|
||||
|
||||
public void setStoreCache(String key, Object value) {
|
||||
if (!Objects.equals(storeCache.put(key, value), value)) {
|
||||
notifyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStorePersistentState(Object value) {
|
||||
var changed = !Objects.equals(storePersistentState, value);
|
||||
this.storePersistentState = value;
|
||||
|
|
|
@ -150,20 +150,20 @@ public class StandardStorage extends DataStorage {
|
|||
ErrorEvent.fromThrowable(exception.get()).handle();
|
||||
}
|
||||
|
||||
if (getStoreCategoryIfPresent(ALL_CATEGORY_UUID).isEmpty()) {
|
||||
var cat = DataStoreCategory.createNew(null, ALL_CATEGORY_UUID,"All connections");
|
||||
cat.setDirectory(categoriesDir.resolve(ALL_CATEGORY_UUID.toString()));
|
||||
if (getStoreCategoryIfPresent(ALL_CONNECTIONS_CATEGORY_UUID).isEmpty()) {
|
||||
var cat = DataStoreCategory.createNew(null, ALL_CONNECTIONS_CATEGORY_UUID, "All connections");
|
||||
cat.setDirectory(categoriesDir.resolve(ALL_CONNECTIONS_CATEGORY_UUID.toString()));
|
||||
storeCategories.add(cat);
|
||||
}
|
||||
|
||||
if (getStoreCategoryIfPresent(SCRIPTS_CATEGORY_UUID).isEmpty()) {
|
||||
var cat = DataStoreCategory.createNew(null, SCRIPTS_CATEGORY_UUID,"All scripts");
|
||||
cat.setDirectory(categoriesDir.resolve(SCRIPTS_CATEGORY_UUID.toString()));
|
||||
if (getStoreCategoryIfPresent(ALL_SCRIPTS_CATEGORY_UUID).isEmpty()) {
|
||||
var cat = DataStoreCategory.createNew(null, ALL_SCRIPTS_CATEGORY_UUID, "All scripts");
|
||||
cat.setDirectory(categoriesDir.resolve(ALL_SCRIPTS_CATEGORY_UUID.toString()));
|
||||
storeCategories.add(cat);
|
||||
}
|
||||
|
||||
if (getStoreCategoryIfPresent(PREDEFINED_SCRIPTS_CATEGORY_UUID).isEmpty()) {
|
||||
var cat = DataStoreCategory.createNew(SCRIPTS_CATEGORY_UUID, PREDEFINED_SCRIPTS_CATEGORY_UUID,"Predefined");
|
||||
var cat = DataStoreCategory.createNew(ALL_SCRIPTS_CATEGORY_UUID, PREDEFINED_SCRIPTS_CATEGORY_UUID, "Predefined");
|
||||
cat.setDirectory(categoriesDir.resolve(PREDEFINED_SCRIPTS_CATEGORY_UUID.toString()));
|
||||
storeCategories.add(cat);
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ public class StandardStorage extends DataStorage {
|
|||
Instant.now(),
|
||||
Instant.now(),
|
||||
true,
|
||||
ALL_CATEGORY_UUID,
|
||||
ALL_CONNECTIONS_CATEGORY_UUID,
|
||||
StoreSortMode.ALPHABETICAL_ASC, false
|
||||
));
|
||||
}
|
||||
|
@ -187,9 +187,10 @@ public class StandardStorage extends DataStorage {
|
|||
if (dataStoreCategory.getParentCategory() != null
|
||||
&& getStoreCategoryIfPresent(dataStoreCategory.getParentCategory())
|
||||
.isEmpty()) {
|
||||
dataStoreCategory.setParentCategory(ALL_CATEGORY_UUID);
|
||||
} else if (dataStoreCategory.getParentCategory() == null && !dataStoreCategory.getUuid().equals(ALL_CATEGORY_UUID) && !dataStoreCategory.getUuid().equals(SCRIPTS_CATEGORY_UUID)) {
|
||||
dataStoreCategory.setParentCategory(ALL_CATEGORY_UUID);
|
||||
dataStoreCategory.setParentCategory(ALL_CONNECTIONS_CATEGORY_UUID);
|
||||
} else if (dataStoreCategory.getParentCategory() == null && !dataStoreCategory.getUuid().equals(ALL_CONNECTIONS_CATEGORY_UUID) && !dataStoreCategory.getUuid().equals(
|
||||
ALL_SCRIPTS_CATEGORY_UUID)) {
|
||||
dataStoreCategory.setParentCategory(ALL_CONNECTIONS_CATEGORY_UUID);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ public class DataStoreCategoryChoiceComp extends SimpleComp {
|
|||
textProperty().unbind();
|
||||
if (w != null) {
|
||||
textProperty().bind(w.nameProperty());
|
||||
setPadding(new Insets(6, 6, 6, 8 + (indent ? w.getDepth() * 6 : 0)));
|
||||
setPadding(new Insets(6, 6, 6, 8 + (indent ? w.getDepth() * 8 : 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,6 @@ public abstract class FeatureProvider {
|
|||
|
||||
public abstract void init();
|
||||
|
||||
public abstract Comp<?> organizationComp();
|
||||
|
||||
public abstract Comp<?> overviewPage();
|
||||
|
||||
public abstract GitStorageHandler createStorageHandler();
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.xpipe.app.util;
|
|||
|
||||
import io.xpipe.app.comp.base.ListSelectorComp;
|
||||
import io.xpipe.app.comp.base.MultiStepComp;
|
||||
import io.xpipe.app.comp.storage.store.StoreViewState;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.AppWindowHelper;
|
||||
import io.xpipe.app.ext.ScanProvider;
|
||||
|
@ -83,11 +84,12 @@ public class ScanAlert {
|
|||
.name("scanAlertChoiceHeader")
|
||||
.description("scanAlertChoiceHeaderDescription")
|
||||
.addComp(new DataStoreChoiceComp<>(
|
||||
DataStoreChoiceComp.Mode.OTHER,
|
||||
null,
|
||||
entry,
|
||||
ShellStore.class,
|
||||
store1 -> true)
|
||||
DataStoreChoiceComp.Mode.OTHER,
|
||||
null,
|
||||
entry,
|
||||
ShellStore.class,
|
||||
store1 -> true,
|
||||
StoreViewState.get().getAllConnectionsCategory())
|
||||
.disable(new SimpleBooleanProperty(initialStore != null)))
|
||||
.name("scanAlertHeader")
|
||||
.description("scanAlertHeaderDescription")
|
||||
|
|
|
@ -112,7 +112,7 @@ reportOnGithubDescription=Open a new issue in the GitHub repository
|
|||
reportErrorDescription=Send an error report with optional user feedback and diagnostics info
|
||||
ignoreError=Ignore error
|
||||
ignoreErrorDescription=Ignore this error and continue like nothing happened
|
||||
provideEmail=Email address (optional, in case you want to get notified about fixes)
|
||||
provideEmail=How to contact you (optional, only if you want to get notified about fixes)
|
||||
additionalErrorInfo=Provide additional information (optional)
|
||||
additionalErrorAttachments=Select attachments (optional)
|
||||
dataHandlingPolicies=Privacy policy
|
||||
|
|
|
@ -5,7 +5,7 @@ import io.xpipe.core.store.DataStore;
|
|||
|
||||
public interface GroupStore<T extends DataStore> extends DataStore {
|
||||
|
||||
DataStoreEntryRef<T> getParent();
|
||||
DataStoreEntryRef<? extends T> getParent();
|
||||
|
||||
@Override
|
||||
default void checkComplete() throws Exception {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package io.xpipe.ext.base;
|
||||
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface SelfReferentialStore extends DataStore {
|
||||
|
||||
default DataStoreEntry getSelfEntry() {
|
||||
return DataStorage.get().getStoreEntries().stream().filter(dataStoreEntry -> dataStoreEntry.getStore() == this).findFirst().orElseGet(() -> {
|
||||
return DataStoreEntry.createNew(UUID.randomUUID(),DataStorage.DEFAULT_CATEGORY_UUID, "Invalid", this);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package io.xpipe.ext.base.script;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@SuperBuilder
|
||||
@Getter
|
||||
@Jacksonized
|
||||
@JsonTypeName("multiScript")
|
||||
public class MultiScriptStore extends ScriptStore {
|
||||
|
||||
@Override
|
||||
public String prepareDumbScript(ShellControl shellControl) {
|
||||
return getEffectiveScripts().stream().map(scriptStore -> {
|
||||
return ((ScriptStore) scriptStore.getStore()).prepareDumbScript(shellControl);
|
||||
}).filter(
|
||||
Objects::nonNull).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prepareTerminalScript(ShellControl shellControl) {
|
||||
return getEffectiveScripts().stream().map(scriptStore -> {
|
||||
return ((ScriptStore) scriptStore.getStore()).prepareDumbScript(shellControl);
|
||||
}).filter(
|
||||
Objects::nonNull).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws Exception {
|
||||
if (scripts != null) {
|
||||
Validators.contentNonNull(scripts);
|
||||
for (var script : scripts) {
|
||||
script.getStore().checkComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DataStoreEntryRef<ScriptStore>> getEffectiveScripts() {
|
||||
return scripts != null ? scripts.stream().filter(scriptStore -> scriptStore != null).toList() : List.of();
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package io.xpipe.ext.base.script;
|
||||
|
||||
import io.xpipe.app.comp.storage.store.DenseStoreEntryComp;
|
||||
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
|
||||
import io.xpipe.app.comp.storage.store.StoreSection;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp;
|
||||
import io.xpipe.app.fxcomps.impl.DataStoreListChoiceComp;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.util.Identifiers;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleListProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MultiScriptStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public Comp<?> customEntryComp(StoreSection s, boolean preferLarge) {
|
||||
return new DenseStoreEntryComp(s.getWrapper(),true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean alwaysShowSummary() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldHaveChildren() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEdit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShareable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CreationCategory getCreationCategory() {
|
||||
return CreationCategory.SCRIPT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "multiScript";
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public String getDisplayIconFileName(DataStore store) {
|
||||
return "proc:shellEnvironment_icon.svg";
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
MultiScriptStore st = store.getValue().asNeeded();
|
||||
var group = new SimpleObjectProperty<>(st.getGroup());
|
||||
var others = new SimpleListProperty<>(FXCollections.observableArrayList(new ArrayList<>(st.getEffectiveScripts())));
|
||||
return new OptionsBuilder()
|
||||
.name("scriptGroup")
|
||||
.description("scriptGroupDescription")
|
||||
.addComp(DataStoreChoiceComp.other(group, ScriptGroupStore.class, null), group)
|
||||
.name("snippets")
|
||||
.description("snippetsDependenciesDescription")
|
||||
.addComp(new DataStoreListChoiceComp<>(others, ScriptStore.class, scriptStore -> !scriptStore.get().equals(entry) && others.stream().noneMatch(scriptStoreDataStoreEntryRef -> scriptStoreDataStoreEntryRef.getStore().equals(scriptStore))), others)
|
||||
.nonEmpty()
|
||||
.bind(
|
||||
() -> {
|
||||
return MultiScriptStore.builder().group(group.get()).scripts(others.get()).description(st.getDescription()).build();
|
||||
},
|
||||
store)
|
||||
.buildDialog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableValue<String> informationString(StoreEntryWrapper wrapper) {
|
||||
MultiScriptStore st = wrapper.getEntry().getStore().asNeeded();
|
||||
return new SimpleStringProperty(st.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStoreEntry getDisplayParent(DataStoreEntry store) {
|
||||
MultiScriptStore st = store.getStore().asNeeded();
|
||||
return st.getGroup().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getStoreClasses() {
|
||||
return List.of(MultiScriptStore.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore defaultStore() {
|
||||
return MultiScriptStore.builder().scripts(List.of()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPossibleNames() {
|
||||
return Identifiers.get("multiScript");
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import lombok.Setter;
|
|||
@Getter
|
||||
public enum PredefinedScriptGroup {
|
||||
CLINK("Clink", null),
|
||||
STARSHIP("Starship", "Scripts to enable the starship shell extension");
|
||||
STARSHIP("Starship", "Sets up and enables the starship shell prompt");
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
|
|
|
@ -1,24 +1,40 @@
|
|||
package io.xpipe.ext.base.script;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import io.xpipe.ext.base.GroupStore;
|
||||
import io.xpipe.ext.base.SelfReferentialStore;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@Jacksonized
|
||||
@JsonTypeName("scriptGroup")
|
||||
public class ScriptGroupStore extends JacksonizedValue implements GroupStore<DataStore> {
|
||||
|
||||
private final String description;
|
||||
public class ScriptGroupStore extends ScriptStore implements GroupStore<ScriptStore>, SelfReferentialStore {
|
||||
|
||||
@Override
|
||||
public DataStoreEntryRef<DataStore> getParent() {
|
||||
return null;
|
||||
public DataStoreEntryRef<? extends ScriptStore> getParent() {
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SimpleScriptStore> getFlattenedScripts(Set<SimpleScriptStore> seen) {
|
||||
return getEffectiveScripts().stream().map(scriptStoreDataStoreEntryRef -> {
|
||||
return scriptStoreDataStoreEntryRef.getStore().getFlattenedScripts(seen);
|
||||
}).flatMap(List::stream).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DataStoreEntryRef<ScriptStore>> getEffectiveScripts() {
|
||||
var self = getSelfEntry();
|
||||
return DataStorage.get().getStoreChildren(self, true).stream()
|
||||
.map(dataStoreEntry -> dataStoreEntry.<ScriptStore>ref())
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,58 @@ import io.xpipe.app.comp.base.SystemStateComp;
|
|||
import io.xpipe.app.comp.storage.store.DenseStoreEntryComp;
|
||||
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
|
||||
import io.xpipe.app.comp.storage.store.StoreSection;
|
||||
import io.xpipe.app.comp.storage.store.StoreViewState;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ScriptGroupStoreProvider implements DataStoreProvider {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
ScriptGroupStore st = store.getValue().asNeeded();
|
||||
|
||||
var group = new SimpleObjectProperty<>(st.getGroup());
|
||||
Property<String> description = new SimpleObjectProperty<>(st.getDescription());
|
||||
return new OptionsBuilder()
|
||||
.name("description")
|
||||
.description("descriptionDescription")
|
||||
.addString(description)
|
||||
.name("scriptGroup")
|
||||
.description("scriptGroupDescription")
|
||||
.addComp(
|
||||
new DataStoreChoiceComp<>(
|
||||
DataStoreChoiceComp.Mode.OTHER, null, group, ScriptGroupStore.class, ref->! ref.getEntry().equals(entry), StoreViewState.get().getAllScriptsCategory()),
|
||||
group)
|
||||
.nonNull()
|
||||
.bind(
|
||||
() -> {
|
||||
return ScriptGroupStore.builder()
|
||||
.group(group.get())
|
||||
.description(st.getDescription())
|
||||
.build();
|
||||
},
|
||||
store)
|
||||
.buildDialog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore defaultStore() {
|
||||
return ScriptGroupStore.builder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comp<?> stateDisplay(StoreEntryWrapper w) {
|
||||
return new SystemStateComp(new SimpleObjectProperty<>(SystemStateComp.State.SUCCESS));
|
||||
|
|
|
@ -13,8 +13,10 @@ import lombok.experimental.FieldDefaults;
|
|||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuperBuilder
|
||||
|
@ -22,19 +24,22 @@ import java.util.stream.Collectors;
|
|||
@AllArgsConstructor
|
||||
public abstract class ScriptStore extends JacksonizedValue implements DataStore, StatefulDataStore<ScriptStore.State> {
|
||||
|
||||
|
||||
public static ShellControl controlWithDefaultScripts(ShellControl pc) {
|
||||
return controlWithScripts(pc,getDefaultScripts());
|
||||
}
|
||||
|
||||
public static ShellControl controlWithScripts(ShellControl pc, List<DataStoreEntryRef<ScriptStore>> refs) {
|
||||
pc.onInit(shellControl -> {
|
||||
var scripts = getDefaultScripts().stream()
|
||||
.map(simpleScriptStore -> simpleScriptStore.getStore().prepareDumbScript(shellControl))
|
||||
var scripts = flatten(refs).stream()
|
||||
.map(simpleScriptStore -> simpleScriptStore.prepareDumbScript(shellControl))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.joining("\n"));
|
||||
if (!scripts.isBlank()) {
|
||||
shellControl.executeSimpleBooleanCommand(scripts);
|
||||
}
|
||||
|
||||
var terminalCommands = getDefaultScripts().stream()
|
||||
.map(simpleScriptStore -> simpleScriptStore.getStore().prepareTerminalScript(shellControl))
|
||||
var terminalCommands = flatten(refs).stream()
|
||||
.map(simpleScriptStore -> simpleScriptStore.prepareTerminalScript(shellControl))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.joining("\n"));
|
||||
if (!terminalCommands.isBlank()) {
|
||||
|
@ -44,14 +49,21 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
|
|||
return pc;
|
||||
}
|
||||
|
||||
public static List<DataStoreEntryRef<ScriptStore>> getDefaultScripts() {
|
||||
var list = DataStorage.get().getStoreEntries().stream()
|
||||
private static List<DataStoreEntryRef<ScriptStore>> getDefaultScripts() {
|
||||
return DataStorage.get().getStoreEntries().stream()
|
||||
.filter(dataStoreEntry -> dataStoreEntry.getStore() instanceof ScriptStore scriptStore
|
||||
&& scriptStore.getState().isDefault())
|
||||
.map(e -> e.<ScriptStore>ref())
|
||||
.toList();
|
||||
// TODO: Make unique
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<SimpleScriptStore> flatten(List<DataStoreEntryRef<ScriptStore>> scripts) {
|
||||
var seen = new HashSet<SimpleScriptStore>();
|
||||
return scripts.stream()
|
||||
.map(scriptStoreDataStoreEntryRef ->
|
||||
scriptStoreDataStoreEntryRef.getStore().getFlattenedScripts(seen))
|
||||
.flatMap(List::stream)
|
||||
.toList();
|
||||
}
|
||||
|
||||
protected final DataStoreEntryRef<ScriptGroupStore> group;
|
||||
|
@ -83,9 +95,11 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
|
|||
}
|
||||
}
|
||||
|
||||
public abstract String prepareDumbScript(ShellControl shellControl);
|
||||
public List<SimpleScriptStore> getFlattenedScripts() {
|
||||
return getFlattenedScripts(new HashSet<>());
|
||||
}
|
||||
|
||||
public abstract String prepareTerminalScript(ShellControl shellControl);
|
||||
protected abstract List<SimpleScriptStore> getFlattenedScripts(Set<SimpleScriptStore> seen);
|
||||
|
||||
public abstract List<DataStoreEntryRef<ScriptStore>> getEffectiveScripts();
|
||||
}
|
||||
|
|
|
@ -11,10 +11,9 @@ import lombok.Getter;
|
|||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SuperBuilder
|
||||
@Getter
|
||||
|
@ -22,37 +21,47 @@ import java.util.function.BiFunction;
|
|||
@JsonTypeName("script")
|
||||
public class SimpleScriptStore extends ScriptStore {
|
||||
|
||||
@Override
|
||||
public String prepareDumbScript(ShellControl shellControl) {
|
||||
return assemble(shellControl, ExecutionType.DUMB_ONLY, ScriptStore::prepareDumbScript);
|
||||
return assemble(shellControl, ExecutionType.DUMB_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prepareTerminalScript(ShellControl shellControl) {
|
||||
return assemble(shellControl, ExecutionType.TERMINAL_ONLY, ScriptStore::prepareTerminalScript);
|
||||
return assemble(shellControl, ExecutionType.TERMINAL_ONLY);
|
||||
}
|
||||
|
||||
private String assemble(ShellControl shellControl, ExecutionType type, BiFunction<ScriptStore, ShellControl, String> function) {
|
||||
var list = new ArrayList<String>();
|
||||
scripts.forEach(scriptStoreDataStoreEntryRef -> {
|
||||
var s = function.apply(scriptStoreDataStoreEntryRef.getStore(), shellControl);
|
||||
if (s != null) {
|
||||
list.add(s);
|
||||
}
|
||||
});
|
||||
|
||||
if ((executionType == type || executionType == ExecutionType.BOTH) && minimumDialect.isCompatibleTo(shellControl.getShellDialect())) {
|
||||
private String assemble(
|
||||
ShellControl shellControl, ExecutionType type) {
|
||||
if ((executionType == type || executionType == ExecutionType.BOTH)
|
||||
&& minimumDialect.isCompatibleTo(shellControl.getShellDialect())) {
|
||||
var script = ScriptHelper.createExecScript(minimumDialect, shellControl, commands);
|
||||
list.add(shellControl.getShellDialect().sourceScriptCommand(shellControl, script));
|
||||
return shellControl.getShellDialect().sourceScriptCommand(shellControl, script);
|
||||
}
|
||||
|
||||
var cmd = String.join("\n", list);
|
||||
return cmd.isEmpty() ? null : cmd;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DataStoreEntryRef<ScriptStore>> getEffectiveScripts() {
|
||||
return scripts != null ? scripts.stream().filter(scriptStore -> scriptStore != null).toList() : List.of();
|
||||
return scripts != null
|
||||
? scripts.stream().filter(scriptStore -> scriptStore != null).toList()
|
||||
: List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SimpleScriptStore> getFlattenedScripts(Set<SimpleScriptStore> seen) {
|
||||
var isLoop = seen.contains(this);
|
||||
seen.add(this);
|
||||
return Stream.concat(
|
||||
getEffectiveScripts().stream()
|
||||
.map(scriptStoreDataStoreEntryRef -> {
|
||||
return scriptStoreDataStoreEntryRef.getStore().getFlattenedScripts(seen).stream()
|
||||
.filter(simpleScriptStore -> !seen.contains(simpleScriptStore))
|
||||
.peek(simpleScriptStore -> seen.add(simpleScriptStore))
|
||||
.toList();
|
||||
})
|
||||
.flatMap(List::stream),
|
||||
isLoop ? Stream.of() : Stream.of(this))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.xpipe.app.comp.base.SystemStateComp;
|
|||
import io.xpipe.app.comp.storage.store.DenseStoreEntryComp;
|
||||
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
|
||||
import io.xpipe.app.comp.storage.store.StoreSection;
|
||||
import io.xpipe.app.comp.storage.store.StoreViewState;
|
||||
import io.xpipe.app.core.AppExtensionManager;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
|
@ -50,11 +51,6 @@ public class SimpleScriptStoreProvider implements DataStoreProvider {
|
|||
return new DenseStoreEntryComp(sec.getWrapper(), true, dropdown);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean alwaysShowSummary() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldHaveChildren() {
|
||||
return false;
|
||||
|
@ -132,7 +128,8 @@ public class SimpleScriptStoreProvider implements DataStoreProvider {
|
|||
new DataStoreListChoiceComp<>(
|
||||
others,
|
||||
ScriptStore.class,
|
||||
scriptStore -> !scriptStore.get().equals(entry) && !others.contains(scriptStore)),
|
||||
scriptStore -> !scriptStore.get().equals(entry) && !others.contains(scriptStore), StoreViewState.get().getAllScriptsCategory()
|
||||
),
|
||||
others)
|
||||
.name("minimumShellDialect")
|
||||
.description("minimumShellDialectDescription")
|
||||
|
@ -160,7 +157,7 @@ public class SimpleScriptStoreProvider implements DataStoreProvider {
|
|||
.description("scriptGroupDescription")
|
||||
.addComp(
|
||||
new DataStoreChoiceComp<>(
|
||||
DataStoreChoiceComp.Mode.OTHER, null, group, ScriptGroupStore.class, null),
|
||||
DataStoreChoiceComp.Mode.OTHER, null, group, ScriptGroupStore.class, null, StoreViewState.get().getAllScriptsCategory()),
|
||||
group)
|
||||
.nonNull()
|
||||
.bind(
|
||||
|
@ -180,31 +177,30 @@ public class SimpleScriptStoreProvider implements DataStoreProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String summaryString(StoreEntryWrapper wrapper) {
|
||||
SimpleScriptStore scriptStore = wrapper.getEntry().getStore().asNeeded();
|
||||
return (scriptStore.isRequiresElevation() ? "Elevated " : "")
|
||||
+ (scriptStore.getMinimumDialect() != null
|
||||
? scriptStore.getMinimumDialect().getDisplayName() + " "
|
||||
: "")
|
||||
+ (scriptStore.getExecutionType() == SimpleScriptStore.ExecutionType.TERMINAL_ONLY
|
||||
? "Terminal"
|
||||
: scriptStore.getExecutionType() == SimpleScriptStore.ExecutionType.DUMB_ONLY
|
||||
? "Background"
|
||||
: "")
|
||||
+ " Snippet";
|
||||
public boolean canMoveCategories() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableValue<String> informationString(StoreEntryWrapper wrapper) {
|
||||
SimpleScriptStore scriptStore = wrapper.getEntry().getStore().asNeeded();
|
||||
return new SimpleStringProperty(scriptStore.getDescription());
|
||||
return new SimpleStringProperty((scriptStore.isRequiresElevation() ? "Elevated " : "")
|
||||
+ (scriptStore.getMinimumDialect() != null
|
||||
? scriptStore.getMinimumDialect().getDisplayName() + " "
|
||||
: "")
|
||||
+ (scriptStore.getExecutionType() == SimpleScriptStore.ExecutionType.TERMINAL_ONLY
|
||||
? "Terminal"
|
||||
: scriptStore.getExecutionType() == SimpleScriptStore.ExecutionType.DUMB_ONLY
|
||||
? "Background"
|
||||
: "")
|
||||
+ " Snippet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storageInit() throws Exception {
|
||||
var cat = DataStorage.get()
|
||||
.addStoreCategoryIfNotPresent(DataStoreCategory.createNew(
|
||||
DataStorage.SCRIPTS_CATEGORY_UUID, DataStorage.CUSTOM_SCRIPTS_CATEGORY_UUID, "My scripts"));
|
||||
DataStorage.ALL_SCRIPTS_CATEGORY_UUID, DataStorage.CUSTOM_SCRIPTS_CATEGORY_UUID, "My scripts"));
|
||||
DataStorage.get()
|
||||
.addStoreEntryIfNotPresent(DataStoreEntry.createNew(
|
||||
UUID.fromString("a9945ad2-db61-4304-97d7-5dc4330691a7"),
|
||||
|
|
|
@ -24,7 +24,9 @@ newDirectory=New directory
|
|||
copyShareLink=Copy link
|
||||
selectStore=Select Store
|
||||
saveSource=Save for later
|
||||
deleteChildren=Remove children
|
||||
execute=Execute
|
||||
deleteChildren=Remove all children
|
||||
descriptionDescription=Give this group an optional description
|
||||
selectSource=Select Source
|
||||
commandLineRead=Update
|
||||
commandLineWrite=Write
|
||||
|
|
Loading…
Reference in a new issue