mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 09:00:26 +00:00
Store improvements
This commit is contained in:
parent
94f8a41dfb
commit
397fc785d6
12 changed files with 174 additions and 146 deletions
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue