Small bugfixes and refactor

This commit is contained in:
crschnick 2023-10-19 16:52:35 +00:00
parent e78dba1f2e
commit 1f3afa3ad4
52 changed files with 227 additions and 167 deletions

View file

@ -1,15 +1,16 @@
package io.xpipe.app.browser;
import atlantafx.base.theme.Styles;
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
import io.xpipe.app.comp.storage.store.StoreSection;
import io.xpipe.app.comp.storage.store.StoreSectionMiniComp;
import io.xpipe.app.comp.storage.store.StoreViewState;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.comp.store.StoreSection;
import io.xpipe.app.comp.store.StoreSectionMiniComp;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.FilterComp;
import io.xpipe.app.fxcomps.impl.HorizontalComp;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.DataStoreCategoryChoiceComp;
import io.xpipe.app.util.FixedHierarchyStore;
@ -91,7 +92,7 @@ final class BrowserBookmarkList extends SimpleComp {
});
});
});
var category = new DataStoreCategoryChoiceComp(StoreViewState.get().getActiveCategory())
var category = new DataStoreCategoryChoiceComp(DataStorage.get().getAllConnectionsCategory(), StoreViewState.get().getActiveCategory())
.styleClass(Styles.LEFT_PILL)
.grow(false, true);
var filter =

View file

@ -392,8 +392,11 @@ public final class OpenFileSystemModel {
if (entry.getStore() instanceof ShellStore s) {
var connection = ((ConnectionFileSystem) fileSystem).getShellControl();
var name = directory + " - " + entry.get().getName();
var toOpen = ProcessControlProvider.get().withDefaultScripts(s.control());
var toOpen = ProcessControlProvider.get().withDefaultScripts(connection);
TerminalHelper.open(entry.getEntry(), name, toOpen.initWith(connection.getShellDialect().getCdCommand(directory)));
// Restart connection as we will have to start it anyway, so we speed it up by doing it preemptively
connection.start();
}
});
});

View file

@ -1,6 +1,6 @@
package io.xpipe.app.comp.base;
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.core.AppResources;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;

View file

@ -54,15 +54,6 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
vbox.getChildren().add(b.createRegion());
});
{
var b = new IconButtonComp("mdi2g-github", () -> Hyperlinks.open(Hyperlinks.GITHUB))
.apply(new FancyTooltipAugment<>("visitGithubRepository"));
b.apply(struc -> {
AppFont.setSize(struc.get(), 2);
});
vbox.getChildren().add(b.createRegion());
}
{
var b = new IconButtonComp(
"mdal-bug_report",
@ -80,6 +71,15 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
vbox.getChildren().add(b.createRegion());
}
{
var b = new IconButtonComp("mdi2g-github", () -> Hyperlinks.open(Hyperlinks.GITHUB))
.apply(new FancyTooltipAugment<>("visitGithubRepository"));
b.apply(struc -> {
AppFont.setSize(struc.get(), 2);
});
vbox.getChildren().add(b.createRegion());
}
{
var b = new IconButtonComp("mdi2c-comment-processing-outline", () -> Hyperlinks.open(Hyperlinks.ROADMAP))
.apply(new FancyTooltipAugment<>("roadmap"));
@ -89,6 +89,16 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
vbox.getChildren().add(b.createRegion());
}
{
var b = new IconButtonComp("mdi2d-discord", () -> Hyperlinks.open(Hyperlinks.DISCORD))
.apply(new FancyTooltipAugment<>("discord"));
b.apply(struc -> {
AppFont.setSize(struc.get(), 2);
});
vbox.getChildren().add(b.createRegion());
}
{
var b = new IconButtonComp("mdi2u-update", () -> UpdateAvailableAlert.showIfNeeded())
.apply(new FancyTooltipAugment<>("updateAvailableTooltip"));

View file

@ -1,6 +1,6 @@
package io.xpipe.app.comp.base;
import io.xpipe.app.comp.storage.store.StoreSection;
import io.xpipe.app.comp.store.StoreSection;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.util.BindingsHelper;

View file

@ -1,7 +1,7 @@
package io.xpipe.app.comp.base;
import atlantafx.base.theme.Styles;
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.PlatformThread;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.fxcomps.Comp;

View file

@ -25,6 +25,7 @@ import io.xpipe.core.store.DataStore;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
import javafx.scene.control.ScrollPane;
@ -52,9 +53,10 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
Property<Validator> validator = new SimpleObjectProperty<>(new SimpleValidator());
Property<String> messageProp = new SimpleStringProperty();
BooleanProperty finished = new SimpleBooleanProperty();
Property<DataStoreEntry> entry = new SimpleObjectProperty<>();
ObservableValue<DataStoreEntry> entry;
BooleanProperty changedSinceError = new SimpleBooleanProperty();
StringProperty name;
DataStoreEntry existingEntry;
boolean exists;
boolean staticDisplay;
@ -64,13 +66,14 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
Property<DataStore> store,
Predicate<DataStoreProvider> filter,
String initialName,
boolean exists,
DataStoreEntry existingEntry, boolean exists,
boolean staticDisplay) {
this.parent = parent;
this.provider = provider;
this.store = store;
this.filter = filter;
this.name = new SimpleStringProperty(initialName != null && !initialName.isEmpty() ? initialName : null);
this.existingEntry = existingEntry;
this.exists = exists;
this.staticDisplay = staticDisplay;
this.store.addListener((c, o, n) -> {
@ -98,6 +101,27 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
newValue.validate();
});
});
this.entry = Bindings.createObjectBinding(() -> {
if (name.getValue() == null || store.getValue() == null) {
return null;
}
var testE = DataStoreEntry.createNew(
UUID.randomUUID(),
DataStorage.get().getSelectedCategory().getUuid(),
name.getValue(),
store.getValue());
var p = provider.getValue().getDisplayParent(testE);
return DataStoreEntry.createNew(
UUID.randomUUID(),
p != null
? p.getCategoryUuid()
: DataStorage.get()
.getSelectedCategory()
.getUuid(),
name.getValue(),
store.getValue());
}, name, store);
}
public static void showEdit(DataStoreEntry e) {
@ -116,7 +140,8 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
});
},
true,
true);
true,
e);
}
public static void showCreation(DataStoreProvider selected, Predicate<DataStoreProvider> filter) {
@ -136,17 +161,19 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
}
},
false,
false);
false,
null);
}
public static void show(
private static void show(
String initialName,
DataStoreProvider provider,
DataStore s,
Predicate<DataStoreProvider> filter,
Consumer<DataStoreEntry> con,
boolean exists,
boolean staticDisplay) {
boolean staticDisplay,
DataStoreEntry existingEntry) {
var prop = new SimpleObjectProperty<>(provider);
var store = new SimpleObjectProperty<>(s);
var loading = new SimpleBooleanProperty();
@ -158,7 +185,7 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
return new MultiStepComp() {
private final GuiDsStoreCreator creator = new GuiDsStoreCreator(
this, prop, store, filter, initialName, exists, staticDisplay);
this, prop, store, filter, initialName, existingEntry, exists, staticDisplay);
@Override
protected List<Entry> setup() {
@ -223,29 +250,6 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
.description("connectionNameDescription")
.addString(name, false)
.nonNull(propVal)
.bind(
() -> {
if (name.getValue() == null || store.getValue() == null) {
return null;
}
var testE = DataStoreEntry.createNew(
UUID.randomUUID(),
DataStorage.get().getSelectedCategory().getUuid(),
name.getValue(),
store.getValue());
var parent = provider.getValue().getDisplayParent(testE);
return DataStoreEntry.createNew(
UUID.randomUUID(),
parent != null
? parent.getCategoryUuid()
: DataStorage.get()
.getSelectedCategory()
.getUuid(),
name.getValue(),
store.getValue());
},
entry)
.build();
}
@ -268,15 +272,7 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
SimpleChangeListener.apply(provider, n -> {
if (n != null) {
// var install = n.getRequiredAdditionalInstallation();
// if (install != null && AppExtensionManager.getInstance().isInstalled(install)) {
// layout.setCenter(new InstallExtensionComp((DownloadModuleInstall)
// install).createRegion());
// validator.setValue(new SimpleValidator());
// return;
// }
var d = n.guiDialog(entry.getValue(), store);
var d = n.guiDialog(existingEntry, store);
var propVal = new SimpleValidator();
var propR = createStoreProperties(d == null || d.getComp() == null ? null : d.getComp(), propVal);

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.fxcomps.Comp;
import javafx.geometry.HPos;

View file

@ -1,10 +1,9 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
@ -67,7 +66,7 @@ public class StoreCategoryWrapper {
}
public void select() {
Platform.runLater(() -> {
PlatformThread.runLaterIfNeeded(() -> {
StoreViewState.get().getActiveCategory().setValue(this);
});
}

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.comp.store.GuiDsStoreCreator;
import io.xpipe.app.core.AppI18n;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import atlantafx.base.theme.Styles;
import io.xpipe.app.comp.base.LoadingOverlayComp;
@ -345,7 +345,7 @@ public abstract class StoreEntryComp extends SimpleComp {
if (wrapper.getEntry().getProvider() != null && wrapper.getEntry().getProvider().canMoveCategories()) {
var move = new Menu(AppI18n.get("moveTo"), new FontIcon("mdi2f-folder-move-outline"));
StoreViewState.get().getSortedCategories().forEach(storeCategoryWrapper -> {
StoreViewState.get().getSortedCategories(DataStorage.get().getRootCategory(DataStorage.get().getStoreCategoryIfPresent(wrapper.getEntry().getCategoryUuid()).orElseThrow())).forEach(storeCategoryWrapper -> {
MenuItem m = new MenuItem(storeCategoryWrapper.getName());
m.setOnAction(event -> {
wrapper.moveTo(storeCategoryWrapper.getCategory());

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.comp.base.ListBoxViewComp;
import io.xpipe.app.comp.base.MultiContentComp;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.comp.base.CountComp;
import io.xpipe.app.core.AppFont;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.comp.store.GuiDsStoreCreator;
import io.xpipe.app.ext.ActionProvider;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.core.AppActionLinkDetector;
import io.xpipe.app.fxcomps.SimpleComp;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.fxcomps.SimpleComp;
import javafx.scene.layout.Region;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.util.BindingsHelper;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.comp.base.ListBoxViewComp;
import io.xpipe.app.fxcomps.Comp;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.comp.base.ListBoxViewComp;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp;

View file

@ -1,4 +1,4 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.storage.DataStoreEntry;

View file

@ -1,6 +1,7 @@
package io.xpipe.app.comp.storage.store;
package io.xpipe.app.comp.store;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
@ -47,14 +48,14 @@ public class StoreViewState {
tl = StoreSection.createTopLevel(allEntries, storeEntryWrapper -> true, filter, activeCategory);
} catch (Exception exception) {
tl = new StoreSection(null, FXCollections.emptyObservableList(), FXCollections.emptyObservableList(), 0);
categories.setAll(new StoreCategoryWrapper(DataStorage.get().getAllCategory()));
categories.setAll(new StoreCategoryWrapper(DataStorage.get().getAllConnectionsCategory()));
activeCategory.setValue(getAllConnectionsCategory());
ErrorEvent.fromThrowable(exception).handle();
}
currentTopLevelSection = tl;
}
public ObservableList<StoreCategoryWrapper> getSortedCategories() {
public ObservableList<StoreCategoryWrapper> getSortedCategories(DataStoreCategory root) {
Comparator<StoreCategoryWrapper> comparator = new Comparator<>() {
@Override
public int compare(StoreCategoryWrapper o1, StoreCategoryWrapper o2) {
@ -89,7 +90,7 @@ public class StoreViewState {
return o1.getName().compareToIgnoreCase(o2.getName());
}
};
return categories.sorted(comparator);
return BindingsHelper.filteredContentBinding(categories, cat-> root == null || cat.getRoot().equals(root)).sorted(comparator);
}
public StoreCategoryWrapper getAllConnectionsCategory() {

View file

@ -3,7 +3,7 @@ package io.xpipe.app.core;
import io.xpipe.app.browser.BrowserComp;
import io.xpipe.app.browser.BrowserModel;
import io.xpipe.app.comp.DeveloperTabComp;
import io.xpipe.app.comp.storage.store.StoreLayoutComp;
import io.xpipe.app.comp.store.StoreLayoutComp;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.prefs.PrefsComp;
import io.xpipe.app.util.LicenseProvider;

View file

@ -14,6 +14,7 @@ import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.css.PseudoClass;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
@ -41,7 +42,8 @@ public class AppTheme {
}
SimpleChangeListener.apply(AppPrefs.get().theme, t -> {
Theme.ALL.forEach(theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId()));
Theme.ALL.forEach(
theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId()));
if (t == null) {
return;
}
@ -51,7 +53,7 @@ public class AppTheme {
stage.getScene().getRoot().pseudoClassStateChanged(DARK, t.isDark());
});
SimpleChangeListener.apply(AppPrefs.get().performanceMode(),val -> {
SimpleChangeListener.apply(AppPrefs.get().performanceMode(), val -> {
stage.getScene().getRoot().pseudoClassStateChanged(PRETTY, !val);
stage.getScene().getRoot().pseudoClassStateChanged(PERFORMANCE, val);
});
@ -111,29 +113,30 @@ public class AppTheme {
}
PlatformThread.runLaterIfNeeded(() -> {
for (Window window : Window.getWindows()) {
var window = AppMainWindow.getInstance().getStage();
var scene = window.getScene();
Image snapshot = scene.snapshot(null);
Pane root = (Pane) scene.getRoot();
Image snapshot = scene.snapshot(null);
ImageView imageView = new ImageView(snapshot);
root.getChildren().add(imageView);
newTheme.apply();
TrackEvent.debug("Set theme " + newTheme.getId() + " for scene");
Platform.runLater(() -> {
// Animate!
var transition = new Timeline(
new KeyFrame(
Duration.ZERO, new KeyValue(imageView.opacityProperty(), 1, Interpolator.EASE_OUT)),
Duration.millis(0),
new KeyValue(imageView.opacityProperty(), 1, Interpolator.EASE_OUT)),
new KeyFrame(
Duration.millis(1250),
Duration.millis(600),
new KeyValue(imageView.opacityProperty(), 0, Interpolator.EASE_OUT)));
transition.setOnFinished(e -> {
root.getChildren().remove(imageView);
});
transition.play();
}
newTheme.apply();
TrackEvent.debug("Set theme " + newTheme.getId() + " for scene");
});
});
}
@ -150,14 +153,18 @@ public class AppTheme {
@SneakyThrows
public void apply() {
var builder = new StringBuilder();
AppResources.with(AppResources.XPIPE_MODULE, "theme/" + id + ".css", path->{
AppResources.with(AppResources.XPIPE_MODULE, "theme/" + id + ".css", path -> {
var content = Files.readString(path);
builder.append(content);
});
AppResources.with("atlantafx.base", theme.getUserAgentStylesheet(), path->{
AppResources.with("atlantafx.base", theme.getUserAgentStylesheet(), path -> {
var baseStyleContent = Files.readString(path);
builder.append("\n").append(baseStyleContent.lines().skip(builder.toString().lines().count()).collect(Collectors.joining("\n")));
builder.append("\n")
.append(baseStyleContent
.lines()
.skip(builder.toString().lines().count())
.collect(Collectors.joining("\n")));
});
var out = Files.createTempFile(id, ".css");
@ -166,7 +173,6 @@ public class AppTheme {
Application.setUserAgentStylesheet(out.toUri().toString());
}
@Override
public String toTranslatedString() {
return name;
@ -188,7 +194,8 @@ public class AppTheme {
public static final Theme CUSTOM = new DerivedTheme("custom", "primer", "Custom", new PrimerDark());
// Also include your custom theme here
public static final List<Theme> ALL = List.of(PRIMER_LIGHT, PRIMER_DARK, NORD_LIGHT, NORD_DARK, CUPERTINO_LIGHT, CUPERTINO_DARK, DRACULA);
public static final List<Theme> ALL =
List.of(PRIMER_LIGHT, PRIMER_DARK, NORD_LIGHT, NORD_DARK, CUPERTINO_LIGHT, CUPERTINO_DARK, DRACULA);
static Theme getDefaultLightTheme() {
return switch (OsType.getLocal()) {
@ -207,8 +214,10 @@ public class AppTheme {
}
protected final String id;
@Getter
protected final String cssId;
protected final atlantafx.base.theme.Theme theme;
public boolean isDark() {

View file

@ -1,7 +1,7 @@
package io.xpipe.app.core.mode;
import io.xpipe.app.browser.BrowserModel;
import io.xpipe.app.comp.storage.store.StoreViewState;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.*;
import io.xpipe.app.issue.*;
import io.xpipe.app.prefs.AppPrefs;

View file

@ -1,6 +1,6 @@
package io.xpipe.app.core.mode;
import io.xpipe.app.comp.storage.store.StoreViewState;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.*;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;

View file

@ -1,10 +1,10 @@
package io.xpipe.app.ext;
import io.xpipe.app.comp.base.MarkdownComp;
import io.xpipe.app.comp.storage.store.StoreEntryComp;
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
import io.xpipe.app.comp.storage.store.StoreSection;
import io.xpipe.app.comp.storage.store.StoreSectionComp;
import io.xpipe.app.comp.store.StoreEntryComp;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.comp.store.StoreSection;
import io.xpipe.app.comp.store.StoreSectionComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppImages;
import io.xpipe.app.fxcomps.Comp;
@ -105,10 +105,6 @@ public interface DataStoreProvider {
return null;
}
default DataStoreEntry getLogicalParent(DataStoreEntry store) {
return getDisplayParent(store);
}
default GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
return null;
}

View file

@ -3,7 +3,7 @@ package io.xpipe.app.fxcomps.impl;
import atlantafx.base.controls.Popover;
import atlantafx.base.theme.Styles;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.comp.storage.store.*;
import io.xpipe.app.comp.store.*;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.DataStoreProviders;
@ -106,7 +106,7 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
comp.disable(new SimpleBooleanProperty(true));
}
});
var category = new DataStoreCategoryChoiceComp(selectedCategory).styleClass(Styles.LEFT_PILL);
var category = new DataStoreCategoryChoiceComp(initialCategory != null ? initialCategory.getRoot() : null, selectedCategory).styleClass(Styles.LEFT_PILL);
var filter = new FilterComp(filterText)
.styleClass(Styles.CENTER_PILL)
.hgrow()

View file

@ -1,7 +1,7 @@
package io.xpipe.app.fxcomps.impl;
import io.xpipe.app.comp.base.ListBoxViewComp;
import io.xpipe.app.comp.storage.store.StoreCategoryWrapper;
import io.xpipe.app.comp.store.StoreCategoryWrapper;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.storage.DataStoreEntryRef;

View file

@ -3,8 +3,8 @@ package io.xpipe.app.fxcomps.impl;
import io.xpipe.app.comp.base.CountComp;
import io.xpipe.app.comp.base.LazyTextFieldComp;
import io.xpipe.app.comp.base.ListBoxViewComp;
import io.xpipe.app.comp.storage.store.StoreCategoryWrapper;
import io.xpipe.app.comp.storage.store.StoreViewState;
import io.xpipe.app.comp.store.StoreCategoryWrapper;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.Comp;

View file

@ -1,6 +1,6 @@
package io.xpipe.app.fxcomps.impl;
import io.xpipe.app.comp.storage.store.StoreCategoryWrapper;
import io.xpipe.app.comp.store.StoreCategoryWrapper;
import io.xpipe.app.fxcomps.SimpleComp;
import javafx.scene.layout.Region;

View file

@ -37,8 +37,10 @@ public abstract class DataStorage {
private static DataStorage INSTANCE;
protected final Path dir;
@Getter
protected final List<DataStoreCategory> storeCategories;
@Getter
protected final Set<DataStoreEntry> storeEntries;
@ -59,10 +61,14 @@ public abstract class DataStorage {
return getStoreCategoryIfPresent(DEFAULT_CATEGORY_UUID).orElseThrow();
}
public DataStoreCategory getAllCategory() {
public DataStoreCategory getAllConnectionsCategory() {
return getStoreCategoryIfPresent(ALL_CONNECTIONS_CATEGORY_UUID).orElseThrow();
}
public DataStoreCategory getAllScriptsCategory() {
return getStoreCategoryIfPresent(ALL_SCRIPTS_CATEGORY_UUID).orElseThrow();
}
private static boolean shouldPersist() {
if (System.getProperty(PERSIST_PROP) != null) {
return Boolean.parseBoolean(System.getProperty(PERSIST_PROP));
@ -195,7 +201,7 @@ public abstract class DataStorage {
}
var oldChildren = getStoreEntries().stream()
.filter(other -> e.equals(other.getProvider().getLogicalParent(other)))
.filter(other -> e.equals(other.getProvider().getDisplayParent(other)))
.toList();
var toRemove = oldChildren.stream()
.filter(entry -> newChildren.stream()
@ -258,7 +264,6 @@ public abstract class DataStorage {
saveAsync();
}
public DataStoreCategory addStoreCategoryIfNotPresent(@NonNull DataStoreCategory cat) {
if (storeCategories.contains(cat)) {
return cat;
@ -388,7 +393,23 @@ public abstract class DataStorage {
.getDisplayParent(entry)
.map(p -> !p.getCategoryUuid().equals(entry.getCategoryUuid()))
.orElse(false);
return noParent || diffParentCategory;
var loop = isParentLoop(entry);
return noParent || diffParentCategory || loop;
}
private boolean isParentLoop(DataStoreEntry entry) {
var es = new HashSet<DataStoreEntry>();
DataStoreEntry current = entry;
while ((current = getDisplayParent(current).orElse(null)) != null) {
if (es.contains(current)) {
return true;
}
es.add(current);
}
return false;
}
public DataStoreEntry getRootForEntry(DataStoreEntry entry) {
@ -469,7 +490,7 @@ public abstract class DataStorage {
}
var parent = getDisplayParent(other);
return parent.isPresent() && parent.get().equals(entry);
return parent.isPresent() && parent.get().equals(entry) && !isParentLoop(entry);
})
.collect(Collectors.toSet());
entry.setChildrenCache(children);
@ -483,20 +504,26 @@ public abstract class DataStorage {
.toList());
}
public DataStoreId getId(DataStoreEntry entry) {
var names = new ArrayList<String>();
names.add(entry.getName().replaceAll(":", "_"));
private List<DataStoreEntry> getHierarchy(DataStoreEntry entry) {
var es = new ArrayList<DataStoreEntry>();
DataStoreEntry current = entry;
while ((current = getDisplayParent(current).orElse(null)) != null) {
if (new LocalStore().equals(current.getStore())) {
if (es.contains(current)) {
break;
}
names.add(0, current.getName().replaceAll(":", "_"));
es.add(0, current);
}
return DataStoreId.create(names.toArray(String[]::new));
return es;
}
public DataStoreId getId(DataStoreEntry entry) {
return DataStoreId.create(getHierarchy(entry).stream()
.filter(e -> !(e.getStore() instanceof LocalStore))
.map(e -> e.getName().replaceAll(":", "_"))
.toArray(String[]::new));
}
public Optional<DataStoreEntry> getStoreEntryIfPresent(@NonNull DataStoreId id) {
@ -602,5 +629,4 @@ public abstract class DataStorage {
public DataStoreEntry local() {
return getStoreEntryIfPresent(LOCAL_ID).orElse(null);
}
}

View file

@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.xpipe.app.comp.storage.store.StoreSortMode;
import io.xpipe.app.comp.store.StoreSortMode;
import io.xpipe.core.util.JacksonMapper;
import lombok.EqualsAndHashCode;
import lombok.NonNull;

View file

@ -1,6 +1,6 @@
package io.xpipe.app.storage;
import io.xpipe.app.comp.storage.store.StoreSortMode;
import io.xpipe.app.comp.store.StoreSortMode;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;

View file

@ -1,9 +1,10 @@
package io.xpipe.app.util;
import io.xpipe.app.comp.storage.store.StoreCategoryWrapper;
import io.xpipe.app.comp.storage.store.StoreViewState;
import io.xpipe.app.comp.store.StoreCategoryWrapper;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.storage.DataStoreCategory;
import javafx.beans.property.Property;
import javafx.geometry.Insets;
import javafx.scene.control.ComboBox;
@ -14,15 +15,17 @@ import lombok.Value;
public class DataStoreCategoryChoiceComp extends SimpleComp {
private final DataStoreCategory root;
private final Property<StoreCategoryWrapper> selected;
public DataStoreCategoryChoiceComp(Property<StoreCategoryWrapper> selected) {
public DataStoreCategoryChoiceComp(DataStoreCategory root, Property<StoreCategoryWrapper> selected) {
this.root = root;
this.selected = selected;
}
@Override
protected Region createSimple() {
var box = new ComboBox<>(StoreViewState.get().getSortedCategories());
var box = new ComboBox<>(StoreViewState.get().getSortedCategories(root));
box.setValue(selected.getValue());
box.valueProperty().addListener((observable, oldValue, newValue) -> {
selected.setValue(newValue);

View file

@ -1,6 +1,6 @@
package io.xpipe.app.util;
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;

View file

@ -2,7 +2,7 @@ package io.xpipe.app.util;
import io.xpipe.app.comp.base.ListSelectorComp;
import io.xpipe.app.comp.base.MultiStepComp;
import io.xpipe.app.comp.storage.store.StoreViewState;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppWindowHelper;
import io.xpipe.app.ext.ScanProvider;

View file

@ -37,7 +37,6 @@ 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;

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -30,9 +30,9 @@
.store-header-bar .menu-button {
-fx-background-radius: 4px;
-fx-border-width: 0.05em;
-fx-border-radius: 4px;
-fx-padding: -1px;
-fx-border-radius: 4px;
-fx-border-width: 1px;
-fx-padding: 0;
-fx-background-color: -color-fg-default;
-fx-border-color: -color-bg-default;
}

View file

@ -1,7 +1,7 @@
## Update procedure
# Update procedure
Note that the automatic updater is broken in version 1.6.0. It will freeze the application and not perform the update.
So you have to install it manually from https://github.com/xpipe-io/xpipe/releases/tag/1.7.0. You can easily do this as uninstalling the old version does not delete any user data.
Note that the automatic updater is broken in version 1.6.0. It will freeze the application and not perform the update. **So do not try to click the install button**!
You have to install it manually from https://github.com/xpipe-io/xpipe/releases/tag/1.7.0. You can easily do this as uninstalling the old version does not delete any user data.
## Changes in 1.7.0
@ -21,6 +21,11 @@ This merges the process of validating/refreshing with the process of establishin
It also enables a custom display and instant updates of the information displayed for a connection.
You will definitely notice this change when you connect to a system.
### Performance improvements
The entire storage and UI handling of connections has been reworked to improve performance.
Especially if you're dealing with a large amount of connections, this will be noticeable.
### Colors
You can now assign colors to connections for organizational purposes to help in situations when many connections are opened in the file browser and terminals at the same time.
@ -28,6 +33,7 @@ These colors will be shown to identify tabs everywhere within XPipe and also out
### Other changes
- Codesign executables on Windows
- Fix application not starting up or exiting properly sometimes
- Add support for bsd-based systems
- Fix OPNsense shells timing out

View file

@ -1,6 +1,6 @@
package io.xpipe.ext.base.action;
import io.xpipe.app.comp.storage.store.StoreViewState;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.comp.store.GuiDsStoreCreator;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.storage.DataStorage;
@ -30,7 +30,7 @@ public class XPipeUrlAction implements ActionProvider {
@Override
public void execute() throws Exception {
actionProvider.getDataStoreCallSite().createAction(entry.getStore().asNeeded()).execute();
actionProvider.getDataStoreCallSite().createAction(entry.ref()).execute();
}
}

View file

@ -3,10 +3,10 @@ package io.xpipe.ext.base.script;
import io.xpipe.app.comp.base.DropdownComp;
import io.xpipe.app.comp.base.StoreToggleComp;
import io.xpipe.app.comp.base.SystemStateComp;
import io.xpipe.app.comp.storage.store.DenseStoreEntryComp;
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
import io.xpipe.app.comp.storage.store.StoreSection;
import io.xpipe.app.comp.storage.store.StoreViewState;
import io.xpipe.app.comp.store.DenseStoreEntryComp;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.comp.store.StoreSection;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.fxcomps.Comp;
@ -51,9 +51,8 @@ public class ScriptGroupStoreProvider implements DataStoreProvider {
.description("scriptGroupDescription")
.addComp(
new DataStoreChoiceComp<>(
DataStoreChoiceComp.Mode.OTHER, null, group, ScriptGroupStore.class, ref->! ref.getEntry().equals(entry), StoreViewState.get().getAllScriptsCategory()),
DataStoreChoiceComp.Mode.OTHER, entry, group, ScriptGroupStore.class, null, StoreViewState.get().getAllScriptsCategory()),
group)
.nonNull()
.bind(
() -> {
return ScriptGroupStore.builder()
@ -65,6 +64,12 @@ public class ScriptGroupStoreProvider implements DataStoreProvider {
.buildDialog();
}
@Override
public DataStoreEntry getDisplayParent(DataStoreEntry store) {
ScriptGroupStore scriptStore = store.getStore().asNeeded();
return scriptStore.getParent() != null ? scriptStore.getParent().get() : null;
}
@Override
public DataStore defaultStore() {
return ScriptGroupStore.builder().build();

View file

@ -29,7 +29,8 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
public static ShellControl controlWithScripts(ShellControl pc, List<DataStoreEntryRef<ScriptStore>> refs) {
pc.onInit(shellControl -> {
var scripts = flatten(refs).stream()
var flattened = flatten(refs);
var scripts = flattened.stream()
.map(simpleScriptStore -> simpleScriptStore.prepareDumbScript(shellControl))
.filter(Objects::nonNull)
.collect(Collectors.joining("\n"));
@ -37,7 +38,7 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
shellControl.executeSimpleBooleanCommand(scripts);
}
var terminalCommands = flatten(refs).stream()
var terminalCommands = flattened.stream()
.map(simpleScriptStore -> simpleScriptStore.prepareTerminalScript(shellControl))
.filter(Objects::nonNull)
.collect(Collectors.joining("\n"));
@ -90,6 +91,11 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
if (scripts != null) {
Validators.contentNonNull(scripts);
}
// Prevent possible stack overflow
// for (DataStoreEntryRef<ScriptStore> s : getEffectiveScripts()) {
// s.checkComplete();
// }
}
public LinkedHashSet<SimpleScriptStore> getFlattenedScripts() {

View file

@ -56,12 +56,12 @@ public class SimpleScriptStore extends ScriptStore {
}
public void queryFlattenedScripts(LinkedHashSet<SimpleScriptStore> all) {
all.add(this);
getEffectiveScripts().stream()
.filter(scriptStoreDataStoreEntryRef -> !all.contains(scriptStoreDataStoreEntryRef.getStore()))
.forEach(scriptStoreDataStoreEntryRef -> {
scriptStoreDataStoreEntryRef.getStore().queryFlattenedScripts(all);
});
all.add(this);
}
@Getter

View file

@ -4,10 +4,10 @@ import io.xpipe.app.comp.base.DropdownComp;
import io.xpipe.app.comp.base.IntegratedTextAreaComp;
import io.xpipe.app.comp.base.StoreToggleComp;
import io.xpipe.app.comp.base.SystemStateComp;
import io.xpipe.app.comp.storage.store.DenseStoreEntryComp;
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
import io.xpipe.app.comp.storage.store.StoreSection;
import io.xpipe.app.comp.storage.store.StoreViewState;
import io.xpipe.app.comp.store.DenseStoreEntryComp;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.comp.store.StoreSection;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.AppExtensionManager;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.GuiDialog;

View file

@ -1,6 +1,6 @@
dir=~/.xpipe/scriptdata/starship
export PATH="$PATH:$dir"
which starship > /dev/null
which starship > /dev/null 2>&1
if [ "$?" != 0 ]; then
mkdir -p "$dir" && \
which curl > /dev/null && \

View file

@ -1,6 +1,6 @@
set dir ~/.xpipe/scriptdata/starship
export PATH="$PATH:$dir"
which starship > /dev/null
which starship > /dev/null 2>&1
if [ $status != 0 ]
mkdir -p "$dir" && \
which curl > /dev/null && \

View file

@ -1,6 +1,6 @@
dir=~/.xpipe/scriptdata/starship
export PATH="$PATH:$dir"
which starship > /dev/null
which starship > /dev/null 2>&1
if [ "$?" != 0 ]; then
mkdir -p "$dir" && \
which curl > /dev/null && \