mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 23:20:23 +00:00
Rework wsl and k8s system
This commit is contained in:
parent
0b6aee858c
commit
9321af9998
26 changed files with 711 additions and 175 deletions
|
@ -7,6 +7,7 @@ import io.xpipe.app.fxcomps.SimpleCompStructure;
|
|||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
@ -27,8 +28,6 @@ public class LoadingOverlayComp extends Comp<CompStructure<StackPane>> {
|
|||
|
||||
var loading = new RingProgressIndicator(0, false);
|
||||
loading.setProgress(-1);
|
||||
loading.setPrefWidth(50);
|
||||
loading.setPrefHeight(50);
|
||||
|
||||
var loadingBg = new StackPane(loading);
|
||||
loadingBg.getStyleClass().add("loading-comp");
|
||||
|
@ -69,7 +68,14 @@ public class LoadingOverlayComp extends Comp<CompStructure<StackPane>> {
|
|||
};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ public class DsStoreProviderChoiceComp extends Comp<CompStructure<ComboBox<Node>
|
|||
var comboBox = new CustomComboBoxBuilder<>(provider, this::createGraphic, createDefaultNode(), v -> true);
|
||||
comboBox.setAccessibleNames(dataStoreProvider -> dataStoreProvider.getDisplayName());
|
||||
getProviders().stream()
|
||||
.filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.shouldShow())
|
||||
.filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.canManuallyCreate())
|
||||
.forEach(comboBox::add);
|
||||
ComboBox<Node> cb = comboBox.build();
|
||||
cb.getStyleClass().add("data-source-type");
|
||||
|
|
|
@ -7,7 +7,6 @@ import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
|||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataSourceEntry;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.DesktopHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
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"));
|
||||
validate.setOnAction(event -> {
|
||||
DataStorage.get().refreshAsync(entry.getEntry(), true);
|
||||
});
|
||||
cm.getItems().add(validate);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,54 +1,59 @@
|
|||
package io.xpipe.app.comp.storage.store;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import io.xpipe.app.comp.base.LoadingOverlayComp;
|
||||
import atlantafx.base.theme.Styles;
|
||||
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.BindingsHelper;
|
||||
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 io.xpipe.core.store.FixedHierarchyStore;
|
||||
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 StoreEntryComp extends SimpleComp {
|
||||
public abstract class StoreEntryComp extends SimpleComp {
|
||||
|
||||
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 static Comp<?> customSection(StoreEntryWrapper e) {
|
||||
var prov = e.getEntry().getProvider();
|
||||
if (prov != null) {
|
||||
return prov.customDisplay(e);
|
||||
} else {
|
||||
return new StandardStoreEntryComp(e);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
private Label createInformation() {
|
||||
protected Label createInformation() {
|
||||
var information = new Label();
|
||||
information.textProperty().bind(PlatformThread.sync(entry.getInformation()));
|
||||
information.getStyleClass().add("information");
|
||||
|
@ -56,7 +61,7 @@ public class StoreEntryComp extends SimpleComp {
|
|||
return information;
|
||||
}
|
||||
|
||||
private Label createSummary() {
|
||||
protected Label createSummary() {
|
||||
var summary = new Label();
|
||||
summary.textProperty().bind(PlatformThread.sync(entry.getSummary()));
|
||||
summary.getStyleClass().add("summary");
|
||||
|
@ -64,7 +69,7 @@ public class StoreEntryComp extends SimpleComp {
|
|||
return summary;
|
||||
}
|
||||
|
||||
private void applyState(Node node) {
|
||||
protected void applyState(Node node) {
|
||||
SimpleChangeListener.apply(PlatformThread.sync(entry.getState()), val -> {
|
||||
switch (val) {
|
||||
case LOAD_FAILED -> {
|
||||
|
@ -83,21 +88,41 @@ public class StoreEntryComp extends SimpleComp {
|
|||
});
|
||||
}
|
||||
|
||||
private Comp<?> createName() {
|
||||
var name = new LabelComp(entry.nameProperty())
|
||||
.apply(struc -> struc.get().setTextOverrun(OverrunStyle.CENTER_ELLIPSIS))
|
||||
protected Comp<?> createName() {
|
||||
var filtered = BindingsHelper.filteredContentBinding(
|
||||
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)));
|
||||
name.apply(s -> AppFont.header(s.get()));
|
||||
return name;
|
||||
}
|
||||
|
||||
private Node createIcon() {
|
||||
protected Node createIcon(int w, int h) {
|
||||
var img = entry.isDisabled()
|
||||
? "disabled_icon.png"
|
||||
: entry.getEntry()
|
||||
.getProvider()
|
||||
.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();
|
||||
storeIcon.getStyleClass().add("icon");
|
||||
if (entry.getState().getValue().isUsable()) {
|
||||
|
@ -108,68 +133,7 @@ public class StoreEntryComp extends SimpleComp {
|
|||
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<>(() -> StoreEntryComp.this.createContextMenu())
|
||||
.augment(new SimpleCompStructure<>(button));
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private Comp<?> createButtonBar() {
|
||||
protected Comp<?> createButtonBar() {
|
||||
var list = new ArrayList<Comp<?>>();
|
||||
for (var p : entry.getActionProviders().entrySet()) {
|
||||
var actionProvider = p.getKey().getDataStoreCallSite();
|
||||
|
@ -198,33 +162,35 @@ public class StoreEntryComp extends SimpleComp {
|
|||
|
||||
var settingsButton = createSettingsButton();
|
||||
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)
|
||||
.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()));
|
||||
}
|
||||
});
|
||||
struc.get().setAlignment(Pos.CENTER_RIGHT);
|
||||
struc.get().setPadding(new Insets(5));
|
||||
})
|
||||
.styleClass("button-bar");
|
||||
}
|
||||
|
||||
private Comp<?> createSettingsButton() {
|
||||
protected Comp<?> createSettingsButton() {
|
||||
var settingsButton = new IconButtonComp("mdomz-settings");
|
||||
settingsButton.styleClass("settings");
|
||||
settingsButton.accessibleText("Settings");
|
||||
settingsButton.apply(new ContextMenuAugment<>(
|
||||
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"));
|
||||
return settingsButton;
|
||||
}
|
||||
|
||||
private ContextMenu createContextMenu() {
|
||||
protected ContextMenu createContextMenu() {
|
||||
var contextMenu = new ContextMenu();
|
||||
AppFont.normal(contextMenu.getStyleableNode());
|
||||
|
||||
|
@ -279,17 +245,9 @@ public class StoreEntryComp extends SimpleComp {
|
|||
return contextMenu;
|
||||
}
|
||||
|
||||
private ColumnConstraints createShareConstraint(Region r, double share) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ public class StoreEntryListComp extends SimpleComp {
|
|||
.getFilterString()
|
||||
.map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s))));
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -14,17 +14,17 @@ import javafx.scene.paint.Color;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public class StoreEntrySection extends Comp<CompStructure<VBox>> {
|
||||
public class StoreEntrySectionComp extends Comp<CompStructure<VBox>> {
|
||||
|
||||
private final StoreSection section;
|
||||
|
||||
public StoreEntrySection(StoreSection section) {
|
||||
public StoreEntrySectionComp(StoreSection section) {
|
||||
this.section = section;
|
||||
}
|
||||
|
||||
@Override
|
||||
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(
|
||||
Bindings.createStringBinding(
|
||||
() -> section.getWrapper().getExpanded().get()
|
||||
|
@ -51,7 +51,7 @@ public class StoreEntrySection extends Comp<CompStructure<VBox>> {
|
|||
.getFilterString()
|
||||
.map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s))));
|
||||
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 -> 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 new VerticalComp(List.of(
|
||||
new HorizontalComp(topEntryList),
|
||||
new HorizontalComp(topEntryList)
|
||||
.apply(struc -> struc.get().setFillHeight(true)),
|
||||
new HorizontalComp(List.of(spacer, content))
|
||||
.apply(struc -> struc.get().setFillHeight(true))
|
||||
.hide(BindingsHelper.persist(Bindings.or(
|
|
@ -8,6 +8,7 @@ import io.xpipe.app.issue.ErrorEvent;
|
|||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.core.store.FixedHierarchyStore;
|
||||
import javafx.beans.property.*;
|
||||
import lombok.Getter;
|
||||
|
||||
|
@ -94,10 +95,7 @@ public class StoreEntryWrapper implements StorageFilter.Filterable {
|
|||
disabled.setValue(entry.isDisabled());
|
||||
state.setValue(entry.getState());
|
||||
expanded.setValue(entry.isExpanded());
|
||||
information.setValue(
|
||||
entry.getInformation() != null
|
||||
? entry.getInformation()
|
||||
: entry.isDisabled() ? null : entry.getProvider().getDisplayName());
|
||||
information.setValue(entry.getInformation());
|
||||
|
||||
loading.setValue(entry.getState() == DataStoreEntry.State.VALIDATING);
|
||||
if (entry.getState().isUsable()) {
|
||||
|
@ -178,6 +176,8 @@ public class StoreEntryWrapper implements StorageFilter.Filterable {
|
|||
if (found != null) {
|
||||
entry.updateLastUsed();
|
||||
found.createAction(entry.getStore().asNeeded()).execute();
|
||||
} else if (getEntry().getStore() instanceof FixedHierarchyStore) {
|
||||
DataStorage.get().refreshChildren(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.app.comp.storage.store;
|
||||
|
||||
import io.xpipe.app.comp.storage.StorageFilter;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
|
@ -14,6 +15,15 @@ import java.util.Comparator;
|
|||
@Value
|
||||
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;
|
||||
ObservableList<StoreSection> children;
|
||||
|
||||
|
@ -36,7 +46,7 @@ public class StoreSection implements StorageFilter.Filterable {
|
|||
var parent = section.getWrapper()
|
||||
.getEntry()
|
||||
.getProvider()
|
||||
.getParent(section.getWrapper().getEntry().getStore());
|
||||
.getLogicalParent(section.getWrapper().getEntry().getStore());
|
||||
return parent == null
|
||||
|| (DataStorage.get().getStoreEntryIfPresent(parent).isEmpty());
|
||||
});
|
||||
|
@ -56,7 +66,7 @@ public class StoreSection implements StorageFilter.Filterable {
|
|||
.getStore()
|
||||
.equals(other.getEntry()
|
||||
.getProvider()
|
||||
.getParent(other.getEntry().getStore())));
|
||||
.getLogicalParent(other.getEntry().getStore())));
|
||||
var children = BindingsHelper.mappedContentBinding(filtered, entry1 -> create(entry1));
|
||||
var ordered = BindingsHelper.orderedContentBinding(children, COMPARATOR);
|
||||
return new StoreSection(e, ordered);
|
||||
|
|
|
@ -15,7 +15,7 @@ public class ListStoresExchangeImpl extends ListStoresExchange
|
|||
public Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
DataStorage s = DataStorage.get();
|
||||
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()))
|
||||
.map(col -> StoreListEntry.builder()
|
||||
.name(col.getName())
|
||||
|
|
|
@ -24,7 +24,7 @@ public class StoreProviderListExchangeImpl extends StoreProviderListExchange
|
|||
.map(p -> ProviderEntry.builder()
|
||||
.id(p.getId())
|
||||
.description(p.getDisplayDescription())
|
||||
.hidden(!p.shouldShow())
|
||||
.hidden(!p.canManuallyCreate())
|
||||
.build())
|
||||
.toList()));
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package io.xpipe.app.ext;
|
||||
|
||||
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.fxcomps.Comp;
|
||||
import io.xpipe.core.dialog.Dialog;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
import io.xpipe.core.store.*;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import javafx.beans.binding.Bindings;
|
||||
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) {
|
||||
var content = Bindings.createStringBinding(
|
||||
() -> {
|
||||
|
@ -77,10 +86,14 @@ public interface DataStoreProvider {
|
|||
return DisplayCategory.OTHER;
|
||||
}
|
||||
|
||||
default DataStore getParent(DataStore store) {
|
||||
default DataStore getLogicalParent(DataStore store) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default DataStore getDisplayParent(DataStore store) {
|
||||
return getLogicalParent(store);
|
||||
}
|
||||
|
||||
default GuiDialog guiDialog(Property<DataStore> store) {
|
||||
return null;
|
||||
}
|
||||
|
@ -129,7 +142,13 @@ public interface DataStoreProvider {
|
|||
return null;
|
||||
}
|
||||
|
||||
DataStore defaultStore();
|
||||
default boolean requiresFrequentRefresh() {
|
||||
return getStoreClasses().stream().anyMatch(aClass -> FixedHierarchyStore.class.isAssignableFrom(aClass));
|
||||
}
|
||||
|
||||
default DataStore defaultStore() {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> getPossibleNames();
|
||||
|
||||
|
@ -139,7 +158,7 @@ public interface DataStoreProvider {
|
|||
|
||||
List<Class<?>> getStoreClasses();
|
||||
|
||||
default boolean shouldShow() {
|
||||
default boolean canManuallyCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
|
@ -9,9 +9,10 @@ import javafx.beans.property.SimpleObjectProperty;
|
|||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.css.Size;
|
||||
import javafx.css.SizeUnits;
|
||||
import javafx.scene.control.Button;
|
||||
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 Runnable listener;
|
||||
|
@ -30,8 +31,9 @@ public class IconButtonComp extends Comp<CompStructure<JFXButton>> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<JFXButton> createBase() {
|
||||
var button = new JFXButton();
|
||||
public CompStructure<Button> createBase() {
|
||||
var button = new Button();
|
||||
button.getStyleClass().add(Styles.FLAT);
|
||||
|
||||
var fi = new FontIcon(icon.getValue());
|
||||
fi.setFocusTraversable(false);
|
||||
|
|
|
@ -97,19 +97,23 @@ public abstract class DataStorage {
|
|||
}
|
||||
|
||||
public synchronized void refreshChildren(DataStoreEntry e) {
|
||||
refreshChildren(e, List.of());
|
||||
}
|
||||
|
||||
|
||||
public synchronized void refreshChildren(DataStoreEntry e, List<DataStoreEntry> oldChildren) {
|
||||
if (!(e.getStore() instanceof FixedHierarchyStore)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var newChildren = ((FixedHierarchyStore) e.getStore()).listChildren();
|
||||
deleteChildren(e, true);
|
||||
newChildren.forEach((key, value) -> {
|
||||
try {
|
||||
addStoreEntry(key, value);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
oldChildren.stream().filter(entry -> !newChildren.containsValue(entry.getStore())).forEach(entry -> {
|
||||
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());
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).handle();
|
||||
|
@ -133,8 +137,8 @@ public abstract class DataStorage {
|
|||
return false;
|
||||
}
|
||||
|
||||
var parent = other.getProvider().getParent(other.getStore());
|
||||
return entry.getStore().equals(parent);
|
||||
var parent = other.getProvider().getLogicalParent(other.getStore());
|
||||
return Objects.equals(entry.getStore(), parent);
|
||||
})
|
||||
.toList());
|
||||
|
||||
|
@ -386,11 +390,28 @@ public abstract class DataStorage {
|
|||
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(() -> {
|
||||
try {
|
||||
element.refresh(deep);
|
||||
propagateUpdate();
|
||||
propagateUpdate(element);
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).reportable(false).handle();
|
||||
}
|
||||
|
@ -398,14 +419,11 @@ public abstract class DataStorage {
|
|||
});
|
||||
}
|
||||
|
||||
private void propagateUpdate() {
|
||||
for (DataStoreEntry dataStoreEntry : getStoreEntries()) {
|
||||
dataStoreEntry.simpleRefresh();
|
||||
}
|
||||
|
||||
for (var e : getSourceEntries()) {
|
||||
e.simpleRefresh();
|
||||
}
|
||||
void propagateUpdate(DataStoreEntry origin) {
|
||||
getStoreChildren(origin, false).forEach(entry -> {
|
||||
entry.simpleRefresh();
|
||||
propagateUpdate(entry);
|
||||
});
|
||||
}
|
||||
|
||||
public void addStoreEntry(@NonNull DataStoreEntry e) {
|
||||
|
@ -417,19 +435,21 @@ public abstract class DataStorage {
|
|||
e.setDirectory(getStoresDir().resolve(e.getUuid().toString()));
|
||||
this.storeEntries.add(e);
|
||||
}
|
||||
propagateUpdate();
|
||||
propagateUpdate(e);
|
||||
save();
|
||||
|
||||
this.listeners.forEach(l -> l.onStoreAdd(e));
|
||||
}
|
||||
|
||||
public void addStoreEntryIfNotPresent(@NonNull String name, DataStore store) {
|
||||
if (getStoreEntryIfPresent(store).isPresent()) {
|
||||
return;
|
||||
public DataStoreEntry addStoreEntryIfNotPresent(@NonNull String name, DataStore store) {
|
||||
var found = getStoreEntryIfPresent(store);
|
||||
if (found.isPresent()) {
|
||||
return found.get();
|
||||
}
|
||||
|
||||
var e = DataStoreEntry.createNew(UUID.randomUUID(), createUniqueStoreEntryName(name), store);
|
||||
addStoreEntry(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
public DataStoreEntry addStoreEntry(@NonNull String name, DataStore store) {
|
||||
|
@ -446,7 +466,7 @@ public abstract class DataStorage {
|
|||
synchronized (this) {
|
||||
this.storeEntries.remove(store);
|
||||
}
|
||||
propagateUpdate();
|
||||
propagateUpdate(store);
|
||||
save();
|
||||
this.listeners.forEach(l -> l.onStoreRemove(store));
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import io.xpipe.app.ext.DataStoreProvider;
|
|||
import io.xpipe.app.ext.DataStoreProviders;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.FixedHierarchyStore;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
import lombok.*;
|
||||
import lombok.experimental.NonFinal;
|
||||
|
@ -204,6 +203,13 @@ public class DataStoreEntry extends StorageElement {
|
|||
simpleRefresh();
|
||||
}
|
||||
|
||||
void setStoreInternal(DataStore store) {
|
||||
this.store = store;
|
||||
this.storeNode = DataStorageWriter.storeToNode(store);
|
||||
lastModified = Instant.now();
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Implement singular change functions
|
||||
*/
|
||||
|
@ -244,11 +250,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
state = State.VALIDATING;
|
||||
listeners.forEach(l -> l.onUpdate());
|
||||
store.validate();
|
||||
|
||||
if (store instanceof FixedHierarchyStore) {
|
||||
DataStorage.get().refreshChildren(this);
|
||||
}
|
||||
|
||||
state = State.COMPLETE_AND_VALID;
|
||||
information = getProvider().queryInformationString(getStore(), 50);
|
||||
dirty = true;
|
||||
|
|
|
@ -40,6 +40,7 @@ open module io.xpipe.app {
|
|||
exports io.xpipe.app.browser.action;
|
||||
exports io.xpipe.app.browser;
|
||||
exports io.xpipe.app.browser.icon;
|
||||
exports io.xpipe.app.comp.storage.store;
|
||||
|
||||
requires com.sun.jna;
|
||||
requires com.sun.jna.platform;
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
-fx-padding: 6px 6px 6px 0;
|
||||
}
|
||||
|
||||
.condensed-store-entry-comp {
|
||||
-fx-padding: 1px 6px 1px 0;
|
||||
}
|
||||
|
||||
.store-entry-comp:hover {
|
||||
-fx-background-color: -color-neutral-muted;
|
||||
}
|
||||
|
@ -35,6 +39,10 @@
|
|||
-fx-background-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
.store-entry-comp .button-bar .button {
|
||||
-fx-padding: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import java.util.List;
|
|||
public class FileStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public boolean shouldShow() {
|
||||
public boolean canManuallyCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.List;
|
|||
public class InMemoryStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public boolean shouldShow() {
|
||||
public boolean canManuallyCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import java.util.List;
|
|||
public class InternalStreamProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public boolean shouldShow() {
|
||||
public boolean canManuallyCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ public class SinkDrainStoreProvider implements DataStoreProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShow() {
|
||||
public boolean canManuallyCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import org.kordamp.ikonli.javafx.FontIcon;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public class RefreshAction implements LeafAction {
|
||||
public class RefreshDirectoryAction implements LeafAction {
|
||||
|
||||
public String getId() {
|
||||
return "refresh";
|
|
@ -31,7 +31,7 @@ open module io.xpipe.ext.base {
|
|||
FollowLinkAction,
|
||||
BackAction,
|
||||
ForwardAction,
|
||||
RefreshAction,
|
||||
RefreshDirectoryAction,
|
||||
OpenFileDefaultAction,
|
||||
OpenFileWithAction,
|
||||
OpenDirectoryAction,
|
||||
|
|
Loading…
Reference in a new issue