Rework wsl and k8s system

This commit is contained in:
crschnick 2023-06-27 00:04:02 +00:00
parent 0b6aee858c
commit 9321af9998
26 changed files with 711 additions and 175 deletions

View file

@ -7,6 +7,7 @@ import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
@ -27,8 +28,6 @@ public class LoadingOverlayComp extends Comp<CompStructure<StackPane>> {
var loading = new RingProgressIndicator(0, false); var loading = new RingProgressIndicator(0, false);
loading.setProgress(-1); loading.setProgress(-1);
loading.setPrefWidth(50);
loading.setPrefHeight(50);
var loadingBg = new StackPane(loading); var loadingBg = new StackPane(loading);
loadingBg.getStyleClass().add("loading-comp"); loadingBg.getStyleClass().add("loading-comp");
@ -69,7 +68,14 @@ public class LoadingOverlayComp extends Comp<CompStructure<StackPane>> {
}; };
PlatformThread.sync(showLoading).addListener(listener); PlatformThread.sync(showLoading).addListener(listener);
var stack = new StackPane(compStruc.get(), loadingBg); var r = compStruc.get();
var stack = new StackPane(r, loadingBg);
loading.prefWidthProperty().bind(Bindings.createDoubleBinding(() -> {
return Math.min(r.getHeight() - 20, 50);
}, r.heightProperty()));
loading.prefHeightProperty().bind(loading.prefWidthProperty());
return new SimpleCompStructure<>(stack); return new SimpleCompStructure<>(stack);
} }
} }

View file

@ -0,0 +1,35 @@
package io.xpipe.app.comp.base;
import atlantafx.base.controls.ToggleSwitch;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.util.PlatformThread;
import javafx.beans.property.BooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.Region;
public class NamedToggleComp extends SimpleComp {
private final BooleanProperty selected;
private final ObservableValue<String> name;
public NamedToggleComp(BooleanProperty selected, ObservableValue<String> name) {
this.selected = selected;
this.name = name;
}
@Override
protected Region createSimple() {
var s = new ToggleSwitch();
s.setSelected(selected.getValue());
s.selectedProperty().addListener((observable, oldValue, newValue) -> {
selected.set(newValue);
});
selected.addListener((observable, oldValue, newValue) -> {
PlatformThread.runLaterIfNeeded(() -> {
s.setSelected(newValue);
});
});
s.textProperty().bind(PlatformThread.sync(name));
return s;
}
}

View file

@ -50,7 +50,7 @@ public class DsStoreProviderChoiceComp extends Comp<CompStructure<ComboBox<Node>
var comboBox = new CustomComboBoxBuilder<>(provider, this::createGraphic, createDefaultNode(), v -> true); var comboBox = new CustomComboBoxBuilder<>(provider, this::createGraphic, createDefaultNode(), v -> true);
comboBox.setAccessibleNames(dataStoreProvider -> dataStoreProvider.getDisplayName()); comboBox.setAccessibleNames(dataStoreProvider -> dataStoreProvider.getDisplayName());
getProviders().stream() getProviders().stream()
.filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.shouldShow()) .filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.canManuallyCreate())
.forEach(comboBox::add); .forEach(comboBox::add);
ComboBox<Node> cb = comboBox.build(); ComboBox<Node> cb = comboBox.build();
cb.getStyleClass().add("data-source-type"); cb.getStyleClass().add("data-source-type");

View file

@ -7,7 +7,6 @@ import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataSourceEntry; import io.xpipe.app.storage.DataSourceEntry;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.DesktopHelper; import io.xpipe.app.util.DesktopHelper;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.scene.control.ContextMenu; import javafx.scene.control.ContextMenu;
@ -63,7 +62,6 @@ public class SourceEntryContextMenu<S extends CompStructure<?>> extends ContextM
var validate = new MenuItem(AppI18n.get("refresh"), new FontIcon("mdal-360")); var validate = new MenuItem(AppI18n.get("refresh"), new FontIcon("mdal-360"));
validate.setOnAction(event -> { validate.setOnAction(event -> {
DataStorage.get().refreshAsync(entry.getEntry(), true);
}); });
cm.getItems().add(validate); cm.getItems().add(validate);

View file

@ -0,0 +1,109 @@
package io.xpipe.app.comp.storage.store;
import atlantafx.base.controls.Spacer;
import com.jfoenix.controls.JFXButton;
import io.xpipe.app.comp.base.LoadingOverlayComp;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
import io.xpipe.app.fxcomps.augment.GrowAugment;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.binding.Bindings;
import javafx.geometry.HPos;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import lombok.SneakyThrows;
public class DenseStoreEntryComp extends StoreEntryComp {
private final boolean showIcon;
private final Comp<?> content;
public DenseStoreEntryComp(StoreEntryWrapper entry, boolean showIcon, Comp<?> content) {
super(entry);
this.showIcon = showIcon;
this.content = content;
}
protected Region createContent() {
var name = createName().createRegion();
var size = createInformation();
var date = new Label();
date.textProperty().bind(AppI18n.readableDuration("usedDate", PlatformThread.sync(entry.lastAccessProperty())));
AppFont.small(date);
date.getStyleClass().add("date");
var grid = new GridPane();
if (showIcon) {
var storeIcon = createIcon(30, 25);
grid.getColumnConstraints().add(new ColumnConstraints(45));
grid.add(storeIcon, 0, 0);
GridPane.setHalignment(storeIcon, HPos.CENTER);
} else {
grid.add(new Region(), 0, 0);
grid.getColumnConstraints().add(new ColumnConstraints(5));
}
var fill = new ColumnConstraints();
fill.setHgrow(Priority.ALWAYS);
grid.getColumnConstraints().addAll(new ColumnConstraints(450), fill);
grid.add(name, 1, 0);
var c = content != null ? content.createRegion() : new Region();
grid.add(c, 2, 0);
GridPane.setHalignment(c, HPos.CENTER);
grid.add(createButtonBar().createRegion(), 3, 0, 1, 1);
GrowAugment.create(true, false).augment(grid);
AppFont.small(size);
AppFont.small(date);
grid.getStyleClass().add("store-entry-grid");
applyState(grid);
var button = new JFXButton();
button.setGraphic(grid);
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(grid));
button.getStyleClass().add("store-entry-comp");
button.getStyleClass().add("condensed-store-entry-comp");
button.setMaxWidth(2000);
button.setFocusTraversable(true);
button.accessibleTextProperty()
.bind(Bindings.createStringBinding(
() -> {
return entry.getName();
},
entry.nameProperty()));
button.accessibleHelpProperty().bind(entry.getInformation());
button.setOnAction(event -> {
event.consume();
ThreadHelper.runFailableAsync(() -> {
entry.refreshIfNeeded();
entry.executeDefaultAction();
});
});
HBox.setHgrow(button, Priority.ALWAYS);
new ContextMenuAugment<>(() -> DenseStoreEntryComp.this.createContextMenu())
.augment(new SimpleCompStructure<>(button));
return new HBox(button, new Spacer(25));
}
@SneakyThrows
@Override
protected Region createSimple() {
var loading = new LoadingOverlayComp(Comp.of(() -> createContent()), entry.getLoading());
var region = loading.createRegion();
return region;
}
}

View file

@ -0,0 +1,304 @@
package io.xpipe.app.comp.storage.store;
import com.jfoenix.controls.JFXButton;
import io.xpipe.app.comp.base.LoadingOverlayComp;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
import io.xpipe.app.fxcomps.augment.GrowAugment;
import io.xpipe.app.fxcomps.impl.*;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.DesktopHelper;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.css.PseudoClass;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region;
import lombok.SneakyThrows;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.ArrayList;
public class StandardStoreEntryComp extends SimpleComp {
public static Comp<?> customSection(StoreEntryWrapper e) {
var prov = e.getEntry().getProvider();
if (prov != null) {
return prov.customDisplay(e);
} else {
return new StandardStoreEntryComp(e);
}
}
private static final double NAME_WIDTH = 0.30;
private static final double STORE_TYPE_WIDTH = 0.08;
private static final double DETAILS_WIDTH = 0.52;
private static final double BUTTONS_WIDTH = 0.1;
private static final PseudoClass FAILED = PseudoClass.getPseudoClass("failed");
private static final PseudoClass INCOMPLETE = PseudoClass.getPseudoClass("incomplete");
private final StoreEntryWrapper entry;
public StandardStoreEntryComp(StoreEntryWrapper entry) {
this.entry = entry;
}
private Label createInformation() {
var information = new Label();
information.textProperty().bind(PlatformThread.sync(entry.getInformation()));
information.getStyleClass().add("information");
AppFont.header(information);
return information;
}
private Label createSummary() {
var summary = new Label();
summary.textProperty().bind(PlatformThread.sync(entry.getSummary()));
summary.getStyleClass().add("summary");
AppFont.small(summary);
return summary;
}
private void applyState(Node node) {
SimpleChangeListener.apply(PlatformThread.sync(entry.getState()), val -> {
switch (val) {
case LOAD_FAILED -> {
node.pseudoClassStateChanged(FAILED, true);
node.pseudoClassStateChanged(INCOMPLETE, false);
}
case INCOMPLETE -> {
node.pseudoClassStateChanged(FAILED, false);
node.pseudoClassStateChanged(INCOMPLETE, true);
}
default -> {
node.pseudoClassStateChanged(FAILED, false);
node.pseudoClassStateChanged(INCOMPLETE, false);
}
}
});
}
private Comp<?> createName() {
var name = new LabelComp(entry.nameProperty())
.apply(struc -> struc.get().setTextOverrun(OverrunStyle.CENTER_ELLIPSIS))
.apply(struc -> struc.get().setPadding(new Insets(5, 5, 5, 0)));
name.apply(s -> AppFont.header(s.get()));
return name;
}
private Node createIcon() {
var img = entry.isDisabled()
? "disabled_icon.png"
: entry.getEntry()
.getProvider()
.getDisplayIconFileName(entry.getEntry().getStore());
var imageComp = new PrettyImageComp(new SimpleStringProperty(img), 55, 45);
var storeIcon = imageComp.createRegion();
storeIcon.getStyleClass().add("icon");
if (entry.getState().getValue().isUsable()) {
new FancyTooltipAugment<>(new SimpleStringProperty(
entry.getEntry().getProvider().getDisplayName()))
.augment(storeIcon);
}
return storeIcon;
}
protected Region createContent() {
var name = createName().createRegion();
var size = createInformation();
var date = new Label();
date.textProperty().bind(AppI18n.readableDuration("usedDate", PlatformThread.sync(entry.lastAccessProperty())));
AppFont.small(date);
date.getStyleClass().add("date");
var grid = new GridPane();
var storeIcon = createIcon();
grid.getColumnConstraints()
.addAll(
createShareConstraint(grid, STORE_TYPE_WIDTH), createShareConstraint(grid, NAME_WIDTH),
createShareConstraint(grid, DETAILS_WIDTH), createShareConstraint(grid, BUTTONS_WIDTH));
grid.add(storeIcon, 0, 0, 1, 2);
grid.add(name, 1, 0);
grid.add(date, 1, 1);
grid.add(createSummary(), 2, 1);
grid.add(createInformation(), 2, 0);
grid.add(createButtonBar().createRegion(), 3, 0, 1, 2);
grid.setVgap(5);
GridPane.setHalignment(storeIcon, HPos.CENTER);
AppFont.small(size);
AppFont.small(date);
grid.getStyleClass().add("store-entry-grid");
applyState(grid);
var button = new JFXButton();
button.setGraphic(grid);
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(grid));
button.getStyleClass().add("store-entry-comp");
button.setMaxWidth(2000);
button.setFocusTraversable(true);
button.accessibleTextProperty()
.bind(Bindings.createStringBinding(
() -> {
return entry.getName();
},
entry.nameProperty()));
button.accessibleHelpProperty().bind(entry.getInformation());
button.setOnAction(event -> {
event.consume();
ThreadHelper.runFailableAsync(() -> {
entry.refreshIfNeeded();
entry.executeDefaultAction();
});
});
new ContextMenuAugment<>(() -> StandardStoreEntryComp.this.createContextMenu())
.augment(new SimpleCompStructure<>(button));
return button;
}
protected Comp<?> createButtonBar() {
var list = new ArrayList<Comp<?>>();
for (var p : entry.getActionProviders().entrySet()) {
var actionProvider = p.getKey().getDataStoreCallSite();
if (!actionProvider.isMajor()
|| p.getKey().equals(entry.getDefaultActionProvider().getValue())) {
continue;
}
var button = new IconButtonComp(
actionProvider.getIcon(entry.getEntry().getStore().asNeeded()), () -> {
ThreadHelper.runFailableAsync(() -> {
var action = actionProvider.createAction(
entry.getEntry().getStore().asNeeded());
action.execute();
});
});
button.apply(new FancyTooltipAugment<>(
actionProvider.getName(entry.getEntry().getStore().asNeeded())));
if (actionProvider.activeType() == ActionProvider.DataStoreCallSite.ActiveType.ONLY_SHOW_IF_ENABLED) {
button.hide(Bindings.not(p.getValue()));
} else if (actionProvider.activeType() == ActionProvider.DataStoreCallSite.ActiveType.ALWAYS_SHOW) {
button.disable(Bindings.not(p.getValue()));
}
list.add(button);
}
var settingsButton = createSettingsButton();
list.add(settingsButton);
return new HorizontalComp(list)
.apply(struc -> struc.get().setAlignment(Pos.CENTER_RIGHT))
.apply(struc -> {
for (Node child : struc.get().getChildren()) {
((Region) child)
.prefWidthProperty()
.bind((struc.get().heightProperty().divide(1.7)));
((Region) child).prefHeightProperty().bind((struc.get().heightProperty()));
}
});
}
protected Comp<?> createSettingsButton() {
var settingsButton = new IconButtonComp("mdomz-settings");
settingsButton.styleClass("settings");
settingsButton.accessibleText("Settings");
settingsButton.apply(new ContextMenuAugment<>(
event -> event.getButton() == MouseButton.PRIMARY, () -> StandardStoreEntryComp.this.createContextMenu()));
settingsButton.apply(GrowAugment.create(false, true));
settingsButton.apply(s -> {
s.get().prefWidthProperty().bind(Bindings.divide(s.get().heightProperty(), 1.35));
});
settingsButton.apply(new FancyTooltipAugment<>("more"));
return settingsButton;
}
protected ContextMenu createContextMenu() {
var contextMenu = new ContextMenu();
AppFont.normal(contextMenu.getStyleableNode());
for (var p : entry.getActionProviders().entrySet()) {
var actionProvider = p.getKey().getDataStoreCallSite();
if (actionProvider.isMajor()) {
continue;
}
var name = actionProvider.getName(entry.getEntry().getStore().asNeeded());
var icon = actionProvider.getIcon(entry.getEntry().getStore().asNeeded());
var item = new MenuItem(null, new FontIcon(icon));
item.setOnAction(event -> {
ThreadHelper.runFailableAsync(() -> {
var action = actionProvider.createAction(
entry.getEntry().getStore().asNeeded());
action.execute();
});
});
item.textProperty().bind(name);
if (actionProvider.activeType() == ActionProvider.DataStoreCallSite.ActiveType.ONLY_SHOW_IF_ENABLED) {
item.visibleProperty().bind(p.getValue());
} else if (actionProvider.activeType() == ActionProvider.DataStoreCallSite.ActiveType.ALWAYS_SHOW) {
item.disableProperty().bind(Bindings.not(p.getValue()));
}
contextMenu.getItems().add(item);
}
if (entry.getActionProviders().size() > 0) {
contextMenu.getItems().add(new SeparatorMenuItem());
}
if (AppPrefs.get().developerMode().getValue()) {
var browse = new MenuItem(AppI18n.get("browse"), new FontIcon("mdi2f-folder-open-outline"));
browse.setOnAction(
event -> DesktopHelper.browsePath(entry.getEntry().getDirectory()));
contextMenu.getItems().add(browse);
}
var refresh = new MenuItem(AppI18n.get("refresh"), new FontIcon("mdal-360"));
refresh.disableProperty().bind(entry.getRefreshable().not());
refresh.setOnAction(event -> {
DataStorage.get().refreshAsync(entry.getEntry(), true);
});
contextMenu.getItems().add(refresh);
var del = new MenuItem(AppI18n.get("delete"), new FontIcon("mdal-delete_outline"));
del.disableProperty().bind(entry.getDeletable().not());
del.setOnAction(event -> entry.delete());
contextMenu.getItems().add(del);
return contextMenu;
}
protected ColumnConstraints createShareConstraint(Region r, double share) {
var cc = new ColumnConstraints();
cc.prefWidthProperty().bind(Bindings.createDoubleBinding(() -> r.getWidth() * share, r.widthProperty()));
return cc;
}
@SneakyThrows
@Override
protected Region createSimple() {
var loading = new LoadingOverlayComp(Comp.of(() -> createContent()), entry.getLoading());
var region = loading.createRegion();
return region;
}
}

View file

@ -1,54 +1,59 @@
package io.xpipe.app.comp.storage.store; package io.xpipe.app.comp.storage.store;
import com.jfoenix.controls.JFXButton; import atlantafx.base.theme.Styles;
import io.xpipe.app.comp.base.LoadingOverlayComp;
import io.xpipe.app.core.AppFont; import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ActionProvider; import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.augment.ContextMenuAugment; import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
import io.xpipe.app.fxcomps.augment.GrowAugment;
import io.xpipe.app.fxcomps.impl.*; import io.xpipe.app.fxcomps.impl.*;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.fxcomps.util.SimpleChangeListener; import io.xpipe.app.fxcomps.util.SimpleChangeListener;
import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.DesktopHelper; import io.xpipe.app.util.DesktopHelper;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FixedHierarchyStore;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.css.PseudoClass; import javafx.css.PseudoClass;
import javafx.geometry.HPos;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.input.MouseButton; import javafx.scene.input.MouseButton;
import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import lombok.SneakyThrows;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;
import java.util.ArrayList; import java.util.ArrayList;
public class StoreEntryComp extends SimpleComp { public abstract class StoreEntryComp extends SimpleComp {
private static final double NAME_WIDTH = 0.30; public static Comp<?> customSection(StoreEntryWrapper e) {
private static final double STORE_TYPE_WIDTH = 0.08; var prov = e.getEntry().getProvider();
private static final double DETAILS_WIDTH = 0.52; if (prov != null) {
private static final double BUTTONS_WIDTH = 0.1; return prov.customDisplay(e);
private static final PseudoClass FAILED = PseudoClass.getPseudoClass("failed"); } else {
private static final PseudoClass INCOMPLETE = PseudoClass.getPseudoClass("incomplete"); return new StandardStoreEntryComp(e);
private final StoreEntryWrapper entry; }
}
public static final double NAME_WIDTH = 0.30;
public static final double STORE_TYPE_WIDTH = 0.08;
public static final double DETAILS_WIDTH = 0.52;
public static final double BUTTONS_WIDTH = 0.1;
public static final PseudoClass FAILED = PseudoClass.getPseudoClass("failed");
public static final PseudoClass INCOMPLETE = PseudoClass.getPseudoClass("incomplete");
protected final StoreEntryWrapper entry;
public StoreEntryComp(StoreEntryWrapper entry) { public StoreEntryComp(StoreEntryWrapper entry) {
this.entry = entry; this.entry = entry;
} }
private Label createInformation() { protected Label createInformation() {
var information = new Label(); var information = new Label();
information.textProperty().bind(PlatformThread.sync(entry.getInformation())); information.textProperty().bind(PlatformThread.sync(entry.getInformation()));
information.getStyleClass().add("information"); information.getStyleClass().add("information");
@ -56,7 +61,7 @@ public class StoreEntryComp extends SimpleComp {
return information; return information;
} }
private Label createSummary() { protected Label createSummary() {
var summary = new Label(); var summary = new Label();
summary.textProperty().bind(PlatformThread.sync(entry.getSummary())); summary.textProperty().bind(PlatformThread.sync(entry.getSummary()));
summary.getStyleClass().add("summary"); summary.getStyleClass().add("summary");
@ -64,7 +69,7 @@ public class StoreEntryComp extends SimpleComp {
return summary; return summary;
} }
private void applyState(Node node) { protected void applyState(Node node) {
SimpleChangeListener.apply(PlatformThread.sync(entry.getState()), val -> { SimpleChangeListener.apply(PlatformThread.sync(entry.getState()), val -> {
switch (val) { switch (val) {
case LOAD_FAILED -> { case LOAD_FAILED -> {
@ -83,21 +88,41 @@ public class StoreEntryComp extends SimpleComp {
}); });
} }
private Comp<?> createName() { protected Comp<?> createName() {
var name = new LabelComp(entry.nameProperty()) var filtered = BindingsHelper.filteredContentBinding(
.apply(struc -> struc.get().setTextOverrun(OverrunStyle.CENTER_ELLIPSIS)) StoreViewState.get().getAllEntries(),
other -> other.getEntry().getState().isUsable()
&& entry.getEntry()
.getStore()
.equals(other.getEntry()
.getProvider()
.getLogicalParent(other.getEntry().getStore())));
LabelComp name = new LabelComp(Bindings.createStringBinding(
() -> {
return entry.getName()
+ (entry.getInformation().get() != null
? " [" + entry.getInformation().get() + "]"
: "")
+ (filtered.size() > 0 && entry.getEntry().getStore() instanceof FixedHierarchyStore
? " (" + filtered.size() + ")"
: "");
},
entry.nameProperty(),
entry.getInformation(),
filtered));
name.apply(struc -> struc.get().setTextOverrun(OverrunStyle.CENTER_ELLIPSIS))
.apply(struc -> struc.get().setPadding(new Insets(5, 5, 5, 0))); .apply(struc -> struc.get().setPadding(new Insets(5, 5, 5, 0)));
name.apply(s -> AppFont.header(s.get())); name.apply(s -> AppFont.header(s.get()));
return name; return name;
} }
private Node createIcon() { protected Node createIcon(int w, int h) {
var img = entry.isDisabled() var img = entry.isDisabled()
? "disabled_icon.png" ? "disabled_icon.png"
: entry.getEntry() : entry.getEntry()
.getProvider() .getProvider()
.getDisplayIconFileName(entry.getEntry().getStore()); .getDisplayIconFileName(entry.getEntry().getStore());
var imageComp = new PrettyImageComp(new SimpleStringProperty(img), 55, 45); var imageComp = new PrettyImageComp(new SimpleStringProperty(img), w, h);
var storeIcon = imageComp.createRegion(); var storeIcon = imageComp.createRegion();
storeIcon.getStyleClass().add("icon"); storeIcon.getStyleClass().add("icon");
if (entry.getState().getValue().isUsable()) { if (entry.getState().getValue().isUsable()) {
@ -108,68 +133,7 @@ public class StoreEntryComp extends SimpleComp {
return storeIcon; return storeIcon;
} }
protected Region createContent() { protected Comp<?> createButtonBar() {
var name = createName().createRegion();
var size = createInformation();
var date = new Label();
date.textProperty().bind(AppI18n.readableDuration("usedDate", PlatformThread.sync(entry.lastAccessProperty())));
AppFont.small(date);
date.getStyleClass().add("date");
var grid = new GridPane();
var storeIcon = createIcon();
grid.getColumnConstraints()
.addAll(
createShareConstraint(grid, STORE_TYPE_WIDTH), createShareConstraint(grid, NAME_WIDTH),
createShareConstraint(grid, DETAILS_WIDTH), createShareConstraint(grid, BUTTONS_WIDTH));
grid.add(storeIcon, 0, 0, 1, 2);
grid.add(name, 1, 0);
grid.add(date, 1, 1);
grid.add(createSummary(), 2, 1);
grid.add(createInformation(), 2, 0);
grid.add(createButtonBar().createRegion(), 3, 0, 1, 2);
grid.setVgap(5);
GridPane.setHalignment(storeIcon, HPos.CENTER);
AppFont.small(size);
AppFont.small(date);
grid.getStyleClass().add("store-entry-grid");
applyState(grid);
var button = new JFXButton();
button.setGraphic(grid);
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(grid));
button.getStyleClass().add("store-entry-comp");
button.setMaxWidth(2000);
button.setFocusTraversable(true);
button.accessibleTextProperty()
.bind(Bindings.createStringBinding(
() -> {
return entry.getName();
},
entry.nameProperty()));
button.accessibleHelpProperty().bind(entry.getInformation());
button.setOnAction(event -> {
event.consume();
ThreadHelper.runFailableAsync(() -> {
entry.refreshIfNeeded();
entry.executeDefaultAction();
});
});
new ContextMenuAugment<>(() -> StoreEntryComp.this.createContextMenu())
.augment(new SimpleCompStructure<>(button));
return button;
}
private Comp<?> createButtonBar() {
var list = new ArrayList<Comp<?>>(); var list = new ArrayList<Comp<?>>();
for (var p : entry.getActionProviders().entrySet()) { for (var p : entry.getActionProviders().entrySet()) {
var actionProvider = p.getKey().getDataStoreCallSite(); var actionProvider = p.getKey().getDataStoreCallSite();
@ -198,33 +162,35 @@ public class StoreEntryComp extends SimpleComp {
var settingsButton = createSettingsButton(); var settingsButton = createSettingsButton();
list.add(settingsButton); list.add(settingsButton);
if (list.size() > 1) {
list.get(0).styleClass(Styles.LEFT_PILL);
for (int i = 1; i < list.size() - 1; i++) {
list.get(i).styleClass(Styles.CENTER_PILL);
}
list.get(list.size() - 1).styleClass(Styles.RIGHT_PILL);
}
list.forEach(comp -> {
comp.apply(struc -> struc.get().getStyleClass().remove(Styles.FLAT));
});
return new HorizontalComp(list) return new HorizontalComp(list)
.apply(struc -> struc.get().setAlignment(Pos.CENTER_RIGHT))
.apply(struc -> { .apply(struc -> {
for (Node child : struc.get().getChildren()) { struc.get().setAlignment(Pos.CENTER_RIGHT);
((Region) child) struc.get().setPadding(new Insets(5));
.prefWidthProperty() })
.bind((struc.get().heightProperty().divide(1.7))); .styleClass("button-bar");
((Region) child).prefHeightProperty().bind((struc.get().heightProperty()));
}
});
} }
private Comp<?> createSettingsButton() { protected Comp<?> createSettingsButton() {
var settingsButton = new IconButtonComp("mdomz-settings"); var settingsButton = new IconButtonComp("mdomz-settings");
settingsButton.styleClass("settings"); settingsButton.styleClass("settings");
settingsButton.accessibleText("Settings"); settingsButton.accessibleText("Settings");
settingsButton.apply(new ContextMenuAugment<>( settingsButton.apply(new ContextMenuAugment<>(
event -> event.getButton() == MouseButton.PRIMARY, () -> StoreEntryComp.this.createContextMenu())); event -> event.getButton() == MouseButton.PRIMARY, () -> StoreEntryComp.this.createContextMenu()));
settingsButton.apply(GrowAugment.create(false, true));
settingsButton.apply(s -> {
s.get().prefWidthProperty().bind(Bindings.divide(s.get().heightProperty(), 1.35));
});
settingsButton.apply(new FancyTooltipAugment<>("more")); settingsButton.apply(new FancyTooltipAugment<>("more"));
return settingsButton; return settingsButton;
} }
private ContextMenu createContextMenu() { protected ContextMenu createContextMenu() {
var contextMenu = new ContextMenu(); var contextMenu = new ContextMenu();
AppFont.normal(contextMenu.getStyleableNode()); AppFont.normal(contextMenu.getStyleableNode());
@ -279,17 +245,9 @@ public class StoreEntryComp extends SimpleComp {
return contextMenu; return contextMenu;
} }
private ColumnConstraints createShareConstraint(Region r, double share) { protected ColumnConstraints createShareConstraint(Region r, double share) {
var cc = new ColumnConstraints(); var cc = new ColumnConstraints();
cc.prefWidthProperty().bind(Bindings.createDoubleBinding(() -> r.getWidth() * share, r.widthProperty())); cc.prefWidthProperty().bind(Bindings.createDoubleBinding(() -> r.getWidth() * share, r.widthProperty()));
return cc; return cc;
} }
@SneakyThrows
@Override
protected Region createSimple() {
var loading = new LoadingOverlayComp(Comp.of(() -> createContent()), entry.getLoading());
var region = loading.createRegion();
return region;
}
} }

View file

@ -23,7 +23,7 @@ public class StoreEntryListComp extends SimpleComp {
.getFilterString() .getFilterString()
.map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s)))); .map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s))));
var content = new ListBoxViewComp<>(filtered, topLevel.getChildren(), (StoreSection e) -> { var content = new ListBoxViewComp<>(filtered, topLevel.getChildren(), (StoreSection e) -> {
return new StoreEntrySection(e); return StoreSection.customSection(e);
}); });
return content.styleClass("store-list-comp").styleClass(Styles.STRIPED); return content.styleClass("store-list-comp").styleClass(Styles.STRIPED);
} }

View file

@ -14,17 +14,17 @@ import javafx.scene.paint.Color;
import java.util.List; import java.util.List;
public class StoreEntrySection extends Comp<CompStructure<VBox>> { public class StoreEntrySectionComp extends Comp<CompStructure<VBox>> {
private final StoreSection section; private final StoreSection section;
public StoreEntrySection(StoreSection section) { public StoreEntrySectionComp(StoreSection section) {
this.section = section; this.section = section;
} }
@Override @Override
public CompStructure<VBox> createBase() { public CompStructure<VBox> createBase() {
var root = new StoreEntryComp(section.getWrapper()).apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS)); var root = StandardStoreEntryComp.customSection(section.getWrapper()).apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS));
var button = new IconButtonComp( var button = new IconButtonComp(
Bindings.createStringBinding( Bindings.createStringBinding(
() -> section.getWrapper().getExpanded().get() () -> section.getWrapper().getExpanded().get()
@ -51,7 +51,7 @@ public class StoreEntrySection extends Comp<CompStructure<VBox>> {
.getFilterString() .getFilterString()
.map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s)))); .map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s))));
var content = new ListBoxViewComp<>(shown, all, (StoreSection e) -> { var content = new ListBoxViewComp<>(shown, all, (StoreSection e) -> {
return new StoreEntrySection(e).apply(GrowAugment.create(true, false)); return StoreSection.customSection(e).apply(GrowAugment.create(true, false));
}) })
.apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS)) .apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS))
.apply(struc -> struc.get().backgroundProperty().set(Background.fill(Color.color(0, 0, 0, 0.01)))); .apply(struc -> struc.get().backgroundProperty().set(Background.fill(Color.color(0, 0, 0, 0.01))));
@ -62,7 +62,8 @@ public class StoreEntrySection extends Comp<CompStructure<VBox>> {
return padding; return padding;
}); });
return new VerticalComp(List.of( return new VerticalComp(List.of(
new HorizontalComp(topEntryList), new HorizontalComp(topEntryList)
.apply(struc -> struc.get().setFillHeight(true)),
new HorizontalComp(List.of(spacer, content)) new HorizontalComp(List.of(spacer, content))
.apply(struc -> struc.get().setFillHeight(true)) .apply(struc -> struc.get().setFillHeight(true))
.hide(BindingsHelper.persist(Bindings.or( .hide(BindingsHelper.persist(Bindings.or(

View file

@ -8,6 +8,7 @@ import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.AppPrefs;
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.FixedHierarchyStore;
import javafx.beans.property.*; import javafx.beans.property.*;
import lombok.Getter; import lombok.Getter;
@ -94,10 +95,7 @@ public class StoreEntryWrapper implements StorageFilter.Filterable {
disabled.setValue(entry.isDisabled()); disabled.setValue(entry.isDisabled());
state.setValue(entry.getState()); state.setValue(entry.getState());
expanded.setValue(entry.isExpanded()); expanded.setValue(entry.isExpanded());
information.setValue( information.setValue(entry.getInformation());
entry.getInformation() != null
? entry.getInformation()
: entry.isDisabled() ? null : entry.getProvider().getDisplayName());
loading.setValue(entry.getState() == DataStoreEntry.State.VALIDATING); loading.setValue(entry.getState() == DataStoreEntry.State.VALIDATING);
if (entry.getState().isUsable()) { if (entry.getState().isUsable()) {
@ -178,6 +176,8 @@ public class StoreEntryWrapper implements StorageFilter.Filterable {
if (found != null) { if (found != null) {
entry.updateLastUsed(); entry.updateLastUsed();
found.createAction(entry.getStore().asNeeded()).execute(); found.createAction(entry.getStore().asNeeded()).execute();
} else if (getEntry().getStore() instanceof FixedHierarchyStore) {
DataStorage.get().refreshChildren(entry);
} }
} }

View file

@ -1,6 +1,7 @@
package io.xpipe.app.comp.storage.store; package io.xpipe.app.comp.storage.store;
import io.xpipe.app.comp.storage.StorageFilter; import io.xpipe.app.comp.storage.StorageFilter;
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 io.xpipe.app.storage.DataStoreEntry;
@ -14,6 +15,15 @@ import java.util.Comparator;
@Value @Value
public class StoreSection implements StorageFilter.Filterable { public class StoreSection implements StorageFilter.Filterable {
public static Comp<?> customSection(StoreSection e) {
var prov = e.getWrapper().getEntry().getProvider();
if (prov != null) {
return prov.customContainer(e);
} else {
return new StoreEntrySectionComp(e);
}
}
StoreEntryWrapper wrapper; StoreEntryWrapper wrapper;
ObservableList<StoreSection> children; ObservableList<StoreSection> children;
@ -36,7 +46,7 @@ public class StoreSection implements StorageFilter.Filterable {
var parent = section.getWrapper() var parent = section.getWrapper()
.getEntry() .getEntry()
.getProvider() .getProvider()
.getParent(section.getWrapper().getEntry().getStore()); .getLogicalParent(section.getWrapper().getEntry().getStore());
return parent == null return parent == null
|| (DataStorage.get().getStoreEntryIfPresent(parent).isEmpty()); || (DataStorage.get().getStoreEntryIfPresent(parent).isEmpty());
}); });
@ -56,7 +66,7 @@ public class StoreSection implements StorageFilter.Filterable {
.getStore() .getStore()
.equals(other.getEntry() .equals(other.getEntry()
.getProvider() .getProvider()
.getParent(other.getEntry().getStore()))); .getLogicalParent(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

@ -15,7 +15,7 @@ public class ListStoresExchangeImpl extends ListStoresExchange
public Response handleRequest(BeaconHandler handler, Request msg) { public Response handleRequest(BeaconHandler handler, Request msg) {
DataStorage s = DataStorage.get(); DataStorage s = DataStorage.get();
var e = s.getStoreEntries().stream() var e = s.getStoreEntries().stream()
.filter(entry -> !entry.isDisabled() && entry.getProvider().shouldShow()) .filter(entry -> !entry.isDisabled() && entry.getProvider().canManuallyCreate())
.sorted(Comparator.comparing(dataStoreEntry -> dataStoreEntry.getLastUsed())) .sorted(Comparator.comparing(dataStoreEntry -> dataStoreEntry.getLastUsed()))
.map(col -> StoreListEntry.builder() .map(col -> StoreListEntry.builder()
.name(col.getName()) .name(col.getName())

View file

@ -24,7 +24,7 @@ public class StoreProviderListExchangeImpl extends StoreProviderListExchange
.map(p -> ProviderEntry.builder() .map(p -> ProviderEntry.builder()
.id(p.getId()) .id(p.getId())
.description(p.getDisplayDescription()) .description(p.getDisplayDescription())
.hidden(!p.shouldShow()) .hidden(!p.canManuallyCreate())
.build()) .build())
.toList())); .toList()));

View file

@ -1,13 +1,14 @@
package io.xpipe.app.ext; package io.xpipe.app.ext;
import io.xpipe.app.comp.base.MarkdownComp; import io.xpipe.app.comp.base.MarkdownComp;
import io.xpipe.app.comp.storage.store.StandardStoreEntryComp;
import io.xpipe.app.comp.storage.store.StoreEntrySectionComp;
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
import io.xpipe.app.comp.storage.store.StoreSection;
import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.Comp;
import io.xpipe.core.dialog.Dialog; import io.xpipe.core.dialog.Dialog;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.*;
import io.xpipe.core.store.FileSystem;
import io.xpipe.core.store.ShellStore;
import io.xpipe.core.store.StreamDataStore;
import io.xpipe.core.util.JacksonizedValue; import io.xpipe.core.util.JacksonizedValue;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.Property; import javafx.beans.property.Property;
@ -31,6 +32,14 @@ public interface DataStoreProvider {
} }
} }
default Comp<?> customDisplay(StoreEntryWrapper w) {
return new StandardStoreEntryComp(w);
}
default Comp<?> customContainer(StoreSection section) {
return new StoreEntrySectionComp(section);
}
default Comp<?> createInsightsComp(ObservableValue<DataStore> store) { default Comp<?> createInsightsComp(ObservableValue<DataStore> store) {
var content = Bindings.createStringBinding( var content = Bindings.createStringBinding(
() -> { () -> {
@ -77,10 +86,14 @@ public interface DataStoreProvider {
return DisplayCategory.OTHER; return DisplayCategory.OTHER;
} }
default DataStore getParent(DataStore store) { default DataStore getLogicalParent(DataStore store) {
return null; return null;
} }
default DataStore getDisplayParent(DataStore store) {
return getLogicalParent(store);
}
default GuiDialog guiDialog(Property<DataStore> store) { default GuiDialog guiDialog(Property<DataStore> store) {
return null; return null;
} }
@ -129,7 +142,13 @@ public interface DataStoreProvider {
return null; return null;
} }
DataStore defaultStore(); default boolean requiresFrequentRefresh() {
return getStoreClasses().stream().anyMatch(aClass -> FixedHierarchyStore.class.isAssignableFrom(aClass));
}
default DataStore defaultStore() {
return null;
}
List<String> getPossibleNames(); List<String> getPossibleNames();
@ -139,7 +158,7 @@ public interface DataStoreProvider {
List<Class<?>> getStoreClasses(); List<Class<?>> getStoreClasses();
default boolean shouldShow() { default boolean canManuallyCreate() {
return true; return true;
} }

View file

@ -1,6 +1,6 @@
package io.xpipe.app.fxcomps.impl; package io.xpipe.app.fxcomps.impl;
import com.jfoenix.controls.JFXButton; import atlantafx.base.theme.Styles;
import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure;
@ -9,9 +9,10 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.css.Size; import javafx.css.Size;
import javafx.css.SizeUnits; import javafx.css.SizeUnits;
import javafx.scene.control.Button;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;
public class IconButtonComp extends Comp<CompStructure<JFXButton>> { public class IconButtonComp extends Comp<CompStructure<Button>> {
private final ObservableValue<String> icon; private final ObservableValue<String> icon;
private final Runnable listener; private final Runnable listener;
@ -30,8 +31,9 @@ public class IconButtonComp extends Comp<CompStructure<JFXButton>> {
} }
@Override @Override
public CompStructure<JFXButton> createBase() { public CompStructure<Button> createBase() {
var button = new JFXButton(); var button = new Button();
button.getStyleClass().add(Styles.FLAT);
var fi = new FontIcon(icon.getValue()); var fi = new FontIcon(icon.getValue());
fi.setFocusTraversable(false); fi.setFocusTraversable(false);

View file

@ -97,19 +97,23 @@ public abstract class DataStorage {
} }
public synchronized void refreshChildren(DataStoreEntry e) { public synchronized void refreshChildren(DataStoreEntry e) {
refreshChildren(e, List.of());
}
public synchronized void refreshChildren(DataStoreEntry e, List<DataStoreEntry> oldChildren) {
if (!(e.getStore() instanceof FixedHierarchyStore)) { if (!(e.getStore() instanceof FixedHierarchyStore)) {
return; return;
} }
try { try {
var newChildren = ((FixedHierarchyStore) e.getStore()).listChildren(); var newChildren = ((FixedHierarchyStore) e.getStore()).listChildren();
deleteChildren(e, true); oldChildren.stream().filter(entry -> !newChildren.containsValue(entry.getStore())).forEach(entry -> {
newChildren.forEach((key, value) -> { deleteChildren(entry, true);
try { deleteStoreEntry(entry);
addStoreEntry(key, value); });
} catch (Exception ex) { newChildren.entrySet().stream().filter(entry -> oldChildren.stream().noneMatch(old -> old.getStore().equals(entry.getValue()))).forEach(entry -> {
throw new RuntimeException(ex); addStoreEntryIfNotPresent(entry.getKey(), entry.getValue());
}
}); });
} catch (Exception ex) { } catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle(); ErrorEvent.fromThrowable(ex).handle();
@ -133,8 +137,8 @@ public abstract class DataStorage {
return false; return false;
} }
var parent = other.getProvider().getParent(other.getStore()); var parent = other.getProvider().getLogicalParent(other.getStore());
return entry.getStore().equals(parent); return Objects.equals(entry.getStore(), parent);
}) })
.toList()); .toList());
@ -386,11 +390,28 @@ public abstract class DataStorage {
latest = e; latest = e;
} }
public void refreshAsync(StorageElement element, boolean deep) { public void setAndRefreshAsync(DataStoreEntry entry, DataStore s) {
ThreadHelper.runAsync(() -> {
var old = entry.getStore();
var children = getStoreChildren(entry, false);
try {
entry.setStoreInternal(s);
entry.refresh(true);
// Update old children
children.forEach(entry1 -> propagateUpdate(entry1));
DataStorage.get().refreshChildren(entry, children);
} catch (Exception e) {
entry.setStoreInternal(old);
entry.simpleRefresh();
}
});
}
public void refreshAsync(DataStoreEntry element, boolean deep) {
ThreadHelper.runAsync(() -> { ThreadHelper.runAsync(() -> {
try { try {
element.refresh(deep); element.refresh(deep);
propagateUpdate(); propagateUpdate(element);
} catch (Exception e) { } catch (Exception e) {
ErrorEvent.fromThrowable(e).reportable(false).handle(); ErrorEvent.fromThrowable(e).reportable(false).handle();
} }
@ -398,14 +419,11 @@ public abstract class DataStorage {
}); });
} }
private void propagateUpdate() { void propagateUpdate(DataStoreEntry origin) {
for (DataStoreEntry dataStoreEntry : getStoreEntries()) { getStoreChildren(origin, false).forEach(entry -> {
dataStoreEntry.simpleRefresh(); entry.simpleRefresh();
} propagateUpdate(entry);
});
for (var e : getSourceEntries()) {
e.simpleRefresh();
}
} }
public void addStoreEntry(@NonNull DataStoreEntry e) { public void addStoreEntry(@NonNull DataStoreEntry e) {
@ -417,19 +435,21 @@ public abstract class DataStorage {
e.setDirectory(getStoresDir().resolve(e.getUuid().toString())); e.setDirectory(getStoresDir().resolve(e.getUuid().toString()));
this.storeEntries.add(e); this.storeEntries.add(e);
} }
propagateUpdate(); propagateUpdate(e);
save(); save();
this.listeners.forEach(l -> l.onStoreAdd(e)); this.listeners.forEach(l -> l.onStoreAdd(e));
} }
public void addStoreEntryIfNotPresent(@NonNull String name, DataStore store) { public DataStoreEntry addStoreEntryIfNotPresent(@NonNull String name, DataStore store) {
if (getStoreEntryIfPresent(store).isPresent()) { var found = getStoreEntryIfPresent(store);
return; if (found.isPresent()) {
return found.get();
} }
var e = DataStoreEntry.createNew(UUID.randomUUID(), createUniqueStoreEntryName(name), store); var e = DataStoreEntry.createNew(UUID.randomUUID(), createUniqueStoreEntryName(name), store);
addStoreEntry(e); addStoreEntry(e);
return e;
} }
public DataStoreEntry addStoreEntry(@NonNull String name, DataStore store) { public DataStoreEntry addStoreEntry(@NonNull String name, DataStore store) {
@ -446,7 +466,7 @@ public abstract class DataStorage {
synchronized (this) { synchronized (this) {
this.storeEntries.remove(store); this.storeEntries.remove(store);
} }
propagateUpdate(); propagateUpdate(store);
save(); save();
this.listeners.forEach(l -> l.onStoreRemove(store)); this.listeners.forEach(l -> l.onStoreRemove(store));
} }

View file

@ -10,7 +10,6 @@ import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.DataStoreProviders; import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.FixedHierarchyStore;
import io.xpipe.core.util.JacksonMapper; import io.xpipe.core.util.JacksonMapper;
import lombok.*; import lombok.*;
import lombok.experimental.NonFinal; import lombok.experimental.NonFinal;
@ -204,6 +203,13 @@ public class DataStoreEntry extends StorageElement {
simpleRefresh(); simpleRefresh();
} }
void setStoreInternal(DataStore store) {
this.store = store;
this.storeNode = DataStorageWriter.storeToNode(store);
lastModified = Instant.now();
dirty = true;
}
/* /*
TODO: Implement singular change functions TODO: Implement singular change functions
*/ */
@ -244,11 +250,6 @@ public class DataStoreEntry extends StorageElement {
state = State.VALIDATING; state = State.VALIDATING;
listeners.forEach(l -> l.onUpdate()); listeners.forEach(l -> l.onUpdate());
store.validate(); store.validate();
if (store instanceof FixedHierarchyStore) {
DataStorage.get().refreshChildren(this);
}
state = State.COMPLETE_AND_VALID; state = State.COMPLETE_AND_VALID;
information = getProvider().queryInformationString(getStore(), 50); information = getProvider().queryInformationString(getStore(), 50);
dirty = true; dirty = true;

View file

@ -40,6 +40,7 @@ open module io.xpipe.app {
exports io.xpipe.app.browser.action; exports io.xpipe.app.browser.action;
exports io.xpipe.app.browser; exports io.xpipe.app.browser;
exports io.xpipe.app.browser.icon; exports io.xpipe.app.browser.icon;
exports io.xpipe.app.comp.storage.store;
requires com.sun.jna; requires com.sun.jna;
requires com.sun.jna.platform; requires com.sun.jna.platform;

View file

@ -27,6 +27,10 @@
-fx-padding: 6px 6px 6px 0; -fx-padding: 6px 6px 6px 0;
} }
.condensed-store-entry-comp {
-fx-padding: 1px 6px 1px 0;
}
.store-entry-comp:hover { .store-entry-comp:hover {
-fx-background-color: -color-neutral-muted; -fx-background-color: -color-neutral-muted;
} }
@ -35,6 +39,10 @@
-fx-background-color: -color-neutral-muted; -fx-background-color: -color-neutral-muted;
} }
.store-entry-comp .button-bar .button {
-fx-padding: 8px;
}

View file

@ -17,7 +17,7 @@ import java.util.List;
public class FileStoreProvider implements DataStoreProvider { public class FileStoreProvider implements DataStoreProvider {
@Override @Override
public boolean shouldShow() { public boolean canManuallyCreate() {
return false; return false;
} }

View file

@ -19,7 +19,7 @@ import java.util.List;
public class InMemoryStoreProvider implements DataStoreProvider { public class InMemoryStoreProvider implements DataStoreProvider {
@Override @Override
public boolean shouldShow() { public boolean canManuallyCreate() {
return false; return false;
} }

View file

@ -11,7 +11,7 @@ import java.util.List;
public class InternalStreamProvider implements DataStoreProvider { public class InternalStreamProvider implements DataStoreProvider {
@Override @Override
public boolean shouldShow() { public boolean canManuallyCreate() {
return false; return false;
} }

View file

@ -42,7 +42,7 @@ public class SinkDrainStoreProvider implements DataStoreProvider {
} }
@Override @Override
public boolean shouldShow() { public boolean canManuallyCreate() {
return false; return false;
} }

View file

@ -0,0 +1,64 @@
package io.xpipe.ext.base.actions;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.store.DataStore;
import javafx.beans.value.ObservableValue;
import lombok.Value;
public class RefreshStoreAction implements ActionProvider {
@Value
static class Action implements ActionProvider.Action {
DataStoreEntry store;
@Override
public boolean requiresJavaFXPlatform() {
return false;
}
@Override
public void execute() throws Exception {
store.refresh(true);
}
}
@Override
public ActionProvider.DataStoreCallSite<?> getDataStoreCallSite() {
return new ActionProvider.DataStoreCallSite<>() {
@Override
public boolean isMajor() {
return true;
}
@Override
public ActiveType activeType() {
return ActiveType.ALWAYS_ENABLE;
}
@Override
public ActionProvider.Action createAction(DataStore store) {
return new Action(DataStorage.get().getStoreEntry(store));
}
@Override
public Class<DataStore> getApplicableClass() {
return DataStore.class;
}
@Override
public ObservableValue<String> getName(DataStore store) {
return AppI18n.observable("base.refresh");
}
@Override
public String getIcon(DataStore store) {
return "mdal-edit";
}
};
}
}

View file

@ -12,7 +12,7 @@ import org.kordamp.ikonli.javafx.FontIcon;
import java.util.List; import java.util.List;
public class RefreshAction implements LeafAction { public class RefreshDirectoryAction implements LeafAction {
public String getId() { public String getId() {
return "refresh"; return "refresh";

View file

@ -31,7 +31,7 @@ open module io.xpipe.ext.base {
FollowLinkAction, FollowLinkAction,
BackAction, BackAction,
ForwardAction, ForwardAction,
RefreshAction, RefreshDirectoryAction,
OpenFileDefaultAction, OpenFileDefaultAction,
OpenFileWithAction, OpenFileWithAction,
OpenDirectoryAction, OpenDirectoryAction,