Store improvements

This commit is contained in:
crschnick 2023-07-01 00:25:38 +00:00
parent 94f8a41dfb
commit 397fc785d6
12 changed files with 174 additions and 146 deletions

View file

@ -170,10 +170,14 @@ final class BrowserBookmarkList extends SimpleComp {
super.updateItem(item, empty); super.updateItem(item, empty);
if (empty || item == null) { if (empty || item == null) {
setText(null); setText(null);
// Don't set image as that would trigger image comp update // Don't set image as that would trigger image comp update
// and cells are emptied on each change, leading to unnecessary changes // and cells are emptied on each change, leading to unnecessary changes
// img.set(null); // img.set(null);
setGraphic(null);
// Use opacity instead of visibility as visibility is kinda bugged with web views
setOpacity(0.0);
setFocusTraversable(false); setFocusTraversable(false);
setAccessibleText(null); setAccessibleText(null);
} else { } else {
@ -190,7 +194,7 @@ final class BrowserBookmarkList extends SimpleComp {
img.set(item.getEntry() img.set(item.getEntry()
.getProvider() .getProvider()
.getDisplayIconFileName(item.getEntry().getStore())); .getDisplayIconFileName(item.getEntry().getStore()));
setGraphic(imageView); setOpacity(1.0);
setFocusTraversable(true); setFocusTraversable(true);
setAccessibleText( setAccessibleText(
item.getName() + " " + item.getEntry().getProvider().getDisplayName()); item.getName() + " " + item.getEntry().getProvider().getDisplayName());

View file

@ -39,11 +39,11 @@ public class ListBoxViewComp<T> extends Comp<CompStructure<ScrollPane>> {
VBox listView = new VBox(); VBox listView = new VBox();
listView.setFocusTraversable(false); listView.setFocusTraversable(false);
refresh(listView, shown, cache, false); refresh(listView, shown, all, cache, false);
listView.requestLayout(); listView.requestLayout();
shown.addListener((ListChangeListener<? super T>) (c) -> { shown.addListener((ListChangeListener<? super T>) (c) -> {
refresh(listView, c.getList(), cache, true); refresh(listView, c.getList(), all, cache, true);
}); });
all.addListener((ListChangeListener<? super T>) c -> { all.addListener((ListChangeListener<? super T>) c -> {
@ -57,9 +57,12 @@ public class ListBoxViewComp<T> extends Comp<CompStructure<ScrollPane>> {
return new SimpleCompStructure<>(scroll); return new SimpleCompStructure<>(scroll);
} }
private void refresh(VBox listView, List<? extends T> c, Map<T, Region> cache, boolean asynchronous) { private void refresh(VBox listView, List<? extends T> shown, List<? extends T> all, Map<T, Region> cache, boolean asynchronous) {
Runnable update = () -> { Runnable update = () -> {
var newShown = c.stream() // Clear cache of unused values
cache.keySet().removeIf(t -> !all.contains(t));
var newShown = shown.stream()
.map(v -> { .map(v -> {
if (!cache.containsKey(v)) { if (!cache.containsKey(v)) {
cache.put(v, compFunction.apply(v).createRegion()); cache.put(v, compFunction.apply(v).createRegion());

View file

@ -1,7 +1,7 @@
package io.xpipe.app.comp.storage.store; package io.xpipe.app.comp.storage.store;
import io.xpipe.app.comp.store.GuiDsStoreCreator;
import io.xpipe.app.comp.storage.StorageFilter; import io.xpipe.app.comp.storage.StorageFilter;
import io.xpipe.app.comp.store.GuiDsStoreCreator;
import io.xpipe.app.ext.ActionProvider; import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
@ -181,7 +181,8 @@ public class StoreEntryWrapper implements StorageFilter.Filterable {
public void executeDefaultAction() throws Exception { public void executeDefaultAction() throws Exception {
var found = getDefaultActionProvider().getValue(); var found = getDefaultActionProvider().getValue();
if (found != null) { if (found != null) {
if (entry.getState().equals(DataStoreEntry.State.COMPLETE_BUT_INVALID) || entry.getState().equals(DataStoreEntry.State.COMPLETE_NOT_VALIDATED)) { if (entry.getState().equals(DataStoreEntry.State.COMPLETE_BUT_INVALID)
|| entry.getState().equals(DataStoreEntry.State.COMPLETE_NOT_VALIDATED)) {
executeRefreshAction(); executeRefreshAction();
} }

View file

@ -4,7 +4,6 @@ import io.xpipe.app.comp.storage.StorageFilter;
import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.util.BindingsHelper; import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import lombok.Value; import lombok.Value;
@ -28,7 +27,7 @@ public class StoreSection implements StorageFilter.Filterable {
ObservableList<StoreSection> children; ObservableList<StoreSection> children;
private static final Comparator<StoreSection> COMPARATOR = Comparator.<StoreSection, Instant>comparing( private static final Comparator<StoreSection> COMPARATOR = Comparator.<StoreSection, Instant>comparing(
o -> o.wrapper.getEntry().getState().equals(DataStoreEntry.State.COMPLETE_AND_VALID) o -> o.wrapper.getEntry().getState().isUsable()
? o.wrapper.getEntry().getLastModified() ? o.wrapper.getEntry().getLastModified()
: Instant.EPOCH) : Instant.EPOCH)
.reversed() .reversed()
@ -39,16 +38,9 @@ public class StoreSection implements StorageFilter.Filterable {
var topLevel = BindingsHelper.mappedContentBinding( var topLevel = BindingsHelper.mappedContentBinding(
StoreViewState.get().getAllEntries(), storeEntryWrapper -> create(storeEntryWrapper)); StoreViewState.get().getAllEntries(), storeEntryWrapper -> create(storeEntryWrapper));
var filtered = BindingsHelper.filteredContentBinding(topLevel, section -> { var filtered = BindingsHelper.filteredContentBinding(topLevel, section -> {
if (!section.getWrapper().getEntry().getState().isUsable()) { return DataStorage.get()
return true; .getParent(section.getWrapper().getEntry(), true)
} .isEmpty();
var parent = section.getWrapper()
.getEntry()
.getProvider()
.getDisplayParent(section.getWrapper().getEntry().getStore());
return parent == null
|| (DataStorage.get().getStoreEntryIfPresent(parent).isEmpty());
}); });
var ordered = BindingsHelper.orderedContentBinding(filtered, COMPARATOR); var ordered = BindingsHelper.orderedContentBinding(filtered, COMPARATOR);
return new StoreSection(null, ordered); return new StoreSection(null, ordered);
@ -59,14 +51,13 @@ public class StoreSection implements StorageFilter.Filterable {
return new StoreSection(e, FXCollections.observableArrayList()); return new StoreSection(e, FXCollections.observableArrayList());
} }
var filtered = BindingsHelper.filteredContentBinding( var filtered =
StoreViewState.get().getAllEntries(), BindingsHelper.filteredContentBinding(StoreViewState.get().getAllEntries(), other -> {
other -> other.getEntry().getState().isUsable() return DataStorage.get()
&& e.getEntry() .getParent(other.getEntry(), true)
.getStore() .map(found -> found.equals(e.getEntry()))
.equals(other.getEntry() .orElse(false);
.getProvider() });
.getDisplayParent(other.getEntry().getStore())));
var children = BindingsHelper.mappedContentBinding(filtered, entry1 -> create(entry1)); var children = BindingsHelper.mappedContentBinding(filtered, entry1 -> create(entry1));
var ordered = BindingsHelper.orderedContentBinding(children, COMPARATOR); var ordered = BindingsHelper.orderedContentBinding(children, COMPARATOR);
return new StoreSection(e, ordered); return new StoreSection(e, ordered);

View file

@ -134,6 +134,10 @@ public interface DataStoreProvider {
String queryInformationString(DataStore store, int length) throws Exception; String queryInformationString(DataStore store, int length) throws Exception;
default String queryInvalidInformationString(DataStore store, int length) throws Exception {
return null;
}
String toSummaryString(DataStore store, int length); String toSummaryString(DataStore store, int length);
default String i18n(String key) { default String i18n(String key) {

View file

@ -6,15 +6,12 @@ import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.fxcomps.util.SimpleChangeListener; import io.xpipe.app.fxcomps.util.SimpleChangeListener;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.web.WebView;
public class PrettyImageComp extends SimpleComp { public class PrettyImageComp extends SimpleComp {
@ -31,6 +28,8 @@ public class PrettyImageComp extends SimpleComp {
@Override @Override
protected Region createSimple() { protected Region createSimple() {
var aspectRatioProperty = new SimpleDoubleProperty(1); var aspectRatioProperty = new SimpleDoubleProperty(1);
var imageAspectRatioProperty = new SimpleDoubleProperty(1);
var svgAspectRatioProperty = new SimpleDoubleProperty(1);
var widthProperty = Bindings.createDoubleBinding( var widthProperty = Bindings.createDoubleBinding(
() -> { () -> {
boolean widthLimited = width / height < aspectRatioProperty.doubleValue(); boolean widthLimited = width / height < aspectRatioProperty.doubleValue();
@ -51,89 +50,84 @@ public class PrettyImageComp extends SimpleComp {
} }
}, },
aspectRatioProperty); aspectRatioProperty);
var image = new SimpleStringProperty(); var image = new SimpleStringProperty();
var currentNode = new SimpleObjectProperty<Node>(); var stack = new StackPane();
{
var svgImageContent = new SimpleStringProperty();
var storeIcon = SvgView.create(svgImageContent);
SimpleChangeListener.apply(image, newValue -> {
if (AppImages.hasSvgImage(newValue)) {
svgImageContent.set(AppImages.svgImage(newValue));
}
});
var ar = Bindings.createDoubleBinding(
() -> {
return storeIcon.getWidth().getValue().doubleValue()
/ storeIcon.getHeight().getValue().doubleValue();
},
storeIcon.getWidth(),
storeIcon.getHeight());
svgAspectRatioProperty.bind(ar);
var node = storeIcon.createWebview();
node.prefWidthProperty().bind(widthProperty);
node.maxWidthProperty().bind(widthProperty);
node.minWidthProperty().bind(widthProperty);
node.prefHeightProperty().bind(heightProperty);
node.maxHeightProperty().bind(heightProperty);
node.minHeightProperty().bind(heightProperty);
stack.getChildren().add(node);
}
{
var storeIcon = new ImageView();
storeIcon.setFocusTraversable(false);
storeIcon
.imageProperty()
.bind(Bindings.createObjectBinding(
() -> {
if (!AppImages.hasNormalImage(image.getValue())) {
return null;
}
return AppImages.image(image.getValue());
},
image));
var ar = Bindings.createDoubleBinding(
() -> {
if (storeIcon.getImage() == null) {
return 1.0;
}
return storeIcon.getImage().getWidth()
/ storeIcon.getImage().getHeight();
},
storeIcon.imageProperty());
imageAspectRatioProperty.bind(ar);
storeIcon.fitWidthProperty().bind(widthProperty);
storeIcon.fitHeightProperty().bind(heightProperty);
storeIcon.setSmooth(true);
stack.getChildren().add(storeIcon);
}
SimpleChangeListener.apply(PlatformThread.sync(value), val -> { SimpleChangeListener.apply(PlatformThread.sync(value), val -> {
image.set(val); image.set(val);
var requiresChange = val == null
|| (val.endsWith(".svg") && !(currentNode.get() instanceof WebView)
|| !(currentNode.get() instanceof ImageView));
if (!requiresChange) {
return;
}
aspectRatioProperty.unbind(); aspectRatioProperty.unbind();
if (val == null) { if (val == null) {
currentNode.set(new Region()); stack.getChildren().get(0).setOpacity(0.0);
stack.getChildren().get(1).setOpacity(0.0);
} else if (val.endsWith(".svg")) { } else if (val.endsWith(".svg")) {
var storeIcon = SvgView.create(Bindings.createStringBinding( aspectRatioProperty.bind(svgAspectRatioProperty);
() -> { stack.getChildren().get(0).setOpacity(1.0);
if (!AppImages.hasSvgImage(image.getValue())) { stack.getChildren().get(1).setOpacity(0.0);
return null;
}
return AppImages.svgImage(image.getValue());
},
image));
var ar = Bindings.createDoubleBinding(
() -> {
return storeIcon.getWidth().getValue().doubleValue()
/ storeIcon.getHeight().getValue().doubleValue();
},
storeIcon.getWidth(),
storeIcon.getHeight());
aspectRatioProperty.bind(ar);
var node = storeIcon.createWebview();
node.prefWidthProperty().bind(widthProperty);
node.maxWidthProperty().bind(widthProperty);
node.minWidthProperty().bind(widthProperty);
node.prefHeightProperty().bind(heightProperty);
node.maxHeightProperty().bind(heightProperty);
node.minHeightProperty().bind(heightProperty);
currentNode.set(node);
} else { } else {
var storeIcon = new ImageView(); aspectRatioProperty.bind(imageAspectRatioProperty);
storeIcon.setFocusTraversable(false); stack.getChildren().get(0).setOpacity(0.0);
storeIcon stack.getChildren().get(1).setOpacity(1.0);
.imageProperty()
.bind(Bindings.createObjectBinding(
() -> {
if (!AppImages.hasNormalImage(image.getValue())) {
return null;
}
return AppImages.image(image.getValue());
},
image));
var ar = Bindings.createDoubleBinding(
() -> {
if (storeIcon.getImage() == null) {
return 1.0;
}
return storeIcon.getImage().getWidth()
/ storeIcon.getImage().getHeight();
},
storeIcon.imageProperty());
aspectRatioProperty.bind(ar);
storeIcon.fitWidthProperty().bind(widthProperty);
storeIcon.fitHeightProperty().bind(heightProperty);
storeIcon.setSmooth(true);
currentNode.set(storeIcon);
} }
}); });
var stack = new StackPane();
SimpleChangeListener.apply(currentNode, val -> {
if (val == null) {
stack.getChildren().clear();
return;
}
stack.getChildren().setAll(val);
});
stack.setFocusTraversable(false); stack.setFocusTraversable(false);
stack.setPrefWidth(width); stack.setPrefWidth(width);
stack.setMinWidth(width); stack.setMinWidth(width);

View file

@ -68,24 +68,14 @@ public abstract class DataStorage {
} }
public synchronized boolean refreshChildren(DataStoreEntry e) { public synchronized boolean refreshChildren(DataStoreEntry e) {
var children = getLogicalStoreChildren(e, false);
return refreshChildren(e, children);
}
public synchronized boolean refreshChildren(DataStoreEntry e, List<DataStoreEntry> oldChildren) {
if (!(e.getStore() instanceof FixedHierarchyStore)) { if (!(e.getStore() instanceof FixedHierarchyStore)) {
return false; return false;
} }
try { try {
deleteChildren(e, true);
var newChildren = ((FixedHierarchyStore) e.getStore()).listChildren(); var newChildren = ((FixedHierarchyStore) e.getStore()).listChildren();
oldChildren.stream().filter(entry -> !newChildren.containsValue(entry.getStore())).forEach(entry -> { newChildren.forEach((key, value) -> addStoreEntryIfNotPresent(key, value));
deleteChildren(entry, true);
deleteStoreEntry(entry);
});
newChildren.entrySet().stream().filter(entry -> oldChildren.stream().noneMatch(old -> old.getStore().equals(entry.getValue()))).forEach(entry -> {
addStoreEntryIfNotPresent(entry.getKey(), entry.getValue());
});
return newChildren.size() > 0; return newChildren.size() > 0;
} catch (Exception ex) { } catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle(); ErrorEvent.fromThrowable(ex).handle();
@ -95,29 +85,44 @@ public abstract class DataStorage {
public synchronized void deleteChildren(DataStoreEntry e, boolean deep) { public synchronized void deleteChildren(DataStoreEntry e, boolean deep) {
// Reverse to delete deepest children first // Reverse to delete deepest children first
var ordered = getLogicalStoreChildren(e, deep); var ordered = getStoreChildren(e, false, deep);
Collections.reverse(ordered); Collections.reverse(ordered);
ordered.forEach(entry -> { ordered.forEach(entry -> {
deleteStoreEntry(entry); synchronized (this) {
this.storeEntries.remove(entry);
}
this.listeners.forEach(l -> l.onStoreRemove(entry));
}); });
save();
} }
public synchronized List<DataStoreEntry> getLogicalStoreChildren(DataStoreEntry entry, boolean deep) { public synchronized Optional<DataStoreEntry> getParent(DataStoreEntry entry, boolean display) {
var children = new ArrayList<>(getStoreEntries().stream() if (!entry.getState().isUsable()) {
.filter(other -> { return Optional.empty();
if (!other.getState().isUsable()) { }
return false;
}
var parent = other.getProvider().getLogicalParent(other.getStore()); var provider = entry.getProvider();
return Objects.equals(entry.getStore(), parent); var parent = display ? provider.getDisplayParent(entry.getStore()) : provider.getLogicalParent(entry.getStore());
}) return parent != null ? getStoreEntryIfPresent(parent) : Optional.empty();
.toList()); }
public synchronized List<DataStoreEntry> getStoreChildren(DataStoreEntry entry, boolean display, boolean deep) {
var children = new ArrayList<>(getStoreEntries().stream()
.filter(other -> {
if (!other.getState().isUsable()) {
return false;
}
var provider = other.getProvider();
var parent = display ? provider.getDisplayParent(other.getStore()) : provider.getLogicalParent(other.getStore());
return parent != null && entry.getStore().getClass().equals(parent.getClass()) && entry.getStore().equals(parent);
})
.toList());
if (deep) { if (deep) {
for (DataStoreEntry dataStoreEntry : new ArrayList<>(children)) { for (DataStoreEntry dataStoreEntry : new ArrayList<>(children)) {
children.addAll(getLogicalStoreChildren(dataStoreEntry, true)); children.addAll(getStoreChildren(dataStoreEntry, display, true));
} }
} }
@ -174,7 +179,7 @@ public abstract class DataStorage {
public synchronized DataStoreEntry getStoreEntry(@NonNull DataStore store) { public synchronized DataStoreEntry getStoreEntry(@NonNull DataStore store) {
var entry = storeEntries.stream() var entry = storeEntries.stream()
.filter(n -> store.equals(n.getStore())) .filter(n -> Objects.equals(store.getClass(), n.getStore().getClass()) && store.equals(n.getStore()))
.findFirst() .findFirst()
.orElseThrow(() -> new IllegalArgumentException("Store not found")); .orElseThrow(() -> new IllegalArgumentException("Store not found"));
return entry; return entry;
@ -195,13 +200,11 @@ public abstract class DataStorage {
public void setAndRefreshAsync(DataStoreEntry entry, DataStore s) { public void setAndRefreshAsync(DataStoreEntry entry, DataStore s) {
ThreadHelper.runAsync(() -> { ThreadHelper.runAsync(() -> {
var old = entry.getStore(); var old = entry.getStore();
var children = getLogicalStoreChildren(entry, false); deleteChildren(entry, true);
try { try {
entry.setStoreInternal(s, false); entry.setStoreInternal(s, false);
entry.refresh(true); entry.refresh(true);
// Update old children DataStorage.get().refreshChildren(entry);
children.forEach(entry1 -> propagateUpdate(entry1));
DataStorage.get().refreshChildren(entry, children);
} catch (Exception e) { } catch (Exception e) {
entry.setStoreInternal(old, false); entry.setStoreInternal(old, false);
entry.simpleRefresh(); entry.simpleRefresh();
@ -222,7 +225,7 @@ public abstract class DataStorage {
} }
void propagateUpdate(DataStoreEntry origin) { void propagateUpdate(DataStoreEntry origin) {
getLogicalStoreChildren(origin, false).forEach(entry -> { getStoreChildren(origin, false, false).forEach(entry -> {
entry.simpleRefresh(); entry.simpleRefresh();
propagateUpdate(entry); propagateUpdate(entry);
}); });

View file

@ -46,6 +46,9 @@ public class DataStoreEntry extends StorageElement {
@NonFinal @NonFinal
boolean expanded; boolean expanded;
@NonFinal
DataStoreProvider provider;
private DataStoreEntry( private DataStoreEntry(
Path directory, Path directory,
UUID uuid, UUID uuid,
@ -65,6 +68,7 @@ public class DataStoreEntry extends StorageElement {
this.state = state; this.state = state;
this.configuration = configuration; this.configuration = configuration;
this.expanded = expanded; this.expanded = expanded;
this.provider = store != null ? DataStoreProviders.byStoreClass(store.getClass()).orElse(null) : null;
} }
@SneakyThrows @SneakyThrows
@ -207,6 +211,7 @@ public class DataStoreEntry extends StorageElement {
lastModified = Instant.now(); lastModified = Instant.now();
information = e.information; information = e.information;
dirty = true; dirty = true;
provider = e.provider;
simpleRefresh(); simpleRefresh();
} }
@ -230,6 +235,7 @@ public class DataStoreEntry extends StorageElement {
store = null; store = null;
state = State.LOAD_FAILED; state = State.LOAD_FAILED;
information = null; information = null;
provider = null;
dirty = dirty || oldStore != null; dirty = dirty || oldStore != null;
listeners.forEach(l -> l.onUpdate()); listeners.forEach(l -> l.onUpdate());
} else { } else {
@ -241,6 +247,7 @@ public class DataStoreEntry extends StorageElement {
dirty = dirty || !nodesEqual; dirty = dirty || !nodesEqual;
store = newStore; store = newStore;
provider = DataStoreProviders.byStoreClass(newStore.getClass()).orElse(null);
var complete = newStore.isComplete(); var complete = newStore.isComplete();
try { try {
@ -261,13 +268,18 @@ public class DataStoreEntry extends StorageElement {
? State.COMPLETE_NOT_VALIDATED ? State.COMPLETE_NOT_VALIDATED
: state; : state;
state = stateToUse; state = stateToUse;
information = state == State.COMPLETE_AND_VALID
? information
: state == State.COMPLETE_BUT_INVALID
? getProvider().queryInvalidInformationString(getStore(), 50)
: null;
} else { } else {
var stateToUse = state == State.LOAD_FAILED ? State.COMPLETE_BUT_INVALID : State.INCOMPLETE; var stateToUse = state == State.LOAD_FAILED ? State.COMPLETE_BUT_INVALID : State.INCOMPLETE;
state = stateToUse; state = stateToUse;
} }
} catch (Exception e) { } catch (Exception e) {
state = store.isComplete() ? State.COMPLETE_BUT_INVALID : State.INCOMPLETE; state = State.COMPLETE_BUT_INVALID;
information = null; information = getProvider().queryInvalidInformationString(getStore(), 50);
throw e; throw e;
} finally { } finally {
propagateUpdate(); propagateUpdate();
@ -318,11 +330,7 @@ public class DataStoreEntry extends StorageElement {
} }
public DataStoreProvider getProvider() { public DataStoreProvider getProvider() {
if (store == null) { return provider;
return null;
}
return DataStoreProviders.byStoreClass(store.getClass()).orElse(null);
} }
@Getter @Getter

View file

@ -121,8 +121,23 @@ public class FileBridge {
return Optional.empty(); return Optional.empty();
} }
public void openReadOnlyString(String input, Consumer<String> fileConsumer) {
if (input == null) {
input = "";
}
var id = UUID.randomUUID();
String s = input;
openIO(
id.toString(),
id,
() -> new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8)),
null,
fileConsumer);
}
public void openString( public void openString(
String keyName, Object key, String input, Consumer<String> output, Consumer<String> consumer) { String keyName, Object key, String input, Consumer<String> output, Consumer<String> fileConsumer) {
if (input == null) { if (input == null) {
input = ""; input = "";
} }
@ -139,7 +154,7 @@ public class FileBridge {
output.accept(new String(toByteArray(), StandardCharsets.UTF_8)); output.accept(new String(toByteArray(), StandardCharsets.UTF_8));
} }
}, },
consumer); fileConsumer);
} }
public void openIO( public void openIO(

View file

@ -83,6 +83,10 @@ public class FileOpener {
} }
} }
public static void openReadOnlyString(String input) {
FileBridge.get().openReadOnlyString(input, s -> openInTextEditor(s));
}
public static void openString(String keyName, Object key, String input, Consumer<String> output) { public static void openString(String keyName, Object key, String input, Consumer<String> output) {
FileBridge.get().openString(keyName, key, input, output, file -> openInTextEditor(file)); FileBridge.get().openString(keyName, key, input, output, file -> openInTextEditor(file));
} }

View file

@ -5,6 +5,7 @@ import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.FixedHierarchyStore;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import lombok.Value; import lombok.Value;
@ -47,8 +48,8 @@ public class DeleteStoreChildrenAction implements ActionProvider {
@Override @Override
public boolean isApplicable(DataStore o) { public boolean isApplicable(DataStore o) {
return DataStorage.get() return !(o instanceof FixedHierarchyStore) && DataStorage.get()
.getLogicalStoreChildren(DataStorage.get().getStoreEntry(o), true) .getStoreChildren(DataStorage.get().getStoreEntry(o), true, true)
.size() .size()
> 1; > 1;
} }

View file

@ -24,7 +24,7 @@ tilda=Tilda
copyShareLink=Copy share link copyShareLink=Copy share link
selectStore=Select Store selectStore=Select Store
saveSource=Save for later saveSource=Save for later
deleteChildren=Delete children deleteChildren=Remove children
selectSource=Select Source selectSource=Select Source
commandLineRead=Update commandLineRead=Update
commandLineWrite=Write commandLineWrite=Write