More script rework, state rework, category fixes, and bug fixes

This commit is contained in:
crschnick 2023-10-05 23:40:52 +00:00
parent 87d1d45cae
commit 43d7e0830c
33 changed files with 500 additions and 309 deletions

View file

@ -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;

View file

@ -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<>();

View file

@ -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());

View file

@ -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();

View file

@ -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()

View file

@ -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));

View file

@ -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();
}

View file

@ -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();

View file

@ -22,6 +22,10 @@ import java.util.List;
public interface DataStoreProvider {
default boolean canMoveCategories() {
return true;
}
default boolean alwaysShowSummary() {
return false;
}

View file

@ -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 -> {

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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) {

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}
});

View file

@ -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)));
}
}
}

View file

@ -38,8 +38,6 @@ public abstract class FeatureProvider {
public abstract void init();
public abstract Comp<?> organizationComp();
public abstract Comp<?> overviewPage();
public abstract GitStorageHandler createStorageHandler();

View file

@ -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")

View file

@ -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

View file

@ -84,7 +84,7 @@
.browser .bookmark-list *.scroll-bar:horizontal *.thumb,
.browser .bookmark-list *.scroll-bar:horizontal *.increment-button,
.browser .bookmark-list *.scroll-bar:horizontal *.decrement-button,
.browser .bookmark-list *.scroll-bar:horizontal *.increment-arrow,
.browser .bookmark-list *.scroll-bar:horizontal *.increment-arrow,
.browser .bookmark-list *.scroll-bar:horizontal *.decrement-arrow {
-fx-background-color: null;
-fx-background-radius: 0;

View file

@ -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 {

View file

@ -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);
});
}
}

View file

@ -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();
}
}

View file

@ -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");
}
}

View file

@ -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;

View file

@ -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();
}
}

View file

@ -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));

View file

@ -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();
}

View file

@ -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

View file

@ -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"),

View file

@ -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