This commit is contained in:
crschnick 2024-09-27 17:24:24 +00:00
parent f5e63fdf52
commit f810ce0f81
39 changed files with 180 additions and 108 deletions

View file

@ -20,7 +20,8 @@ import java.util.Objects;
public class BrowserFileOpener { public class BrowserFileOpener {
private static OutputStream openFileOutput(OpenFileSystemModel model, FileEntry file, long totalBytes) throws Exception { private static OutputStream openFileOutput(OpenFileSystemModel model, FileEntry file, long totalBytes)
throws Exception {
var fileSystem = model.getFileSystem(); var fileSystem = model.getFileSystem();
if (model.isClosed() || fileSystem.getShell().isEmpty()) { if (model.isClosed() || fileSystem.getShell().isEmpty()) {
return OutputStream.nullOutputStream(); return OutputStream.nullOutputStream();
@ -39,12 +40,15 @@ public class BrowserFileOpener {
return fileSystem.openOutput(file.getPath(), totalBytes); return fileSystem.openOutput(file.getPath(), totalBytes);
} }
var elevate = AppWindowHelper.showConfirmationAlert("app.fileWriteSudoTitle", "app.fileWriteSudoHeader", "app.fileWriteSudoContent"); var elevate = AppWindowHelper.showConfirmationAlert(
"app.fileWriteSudoTitle", "app.fileWriteSudoHeader", "app.fileWriteSudoContent");
if (!elevate) { if (!elevate) {
return fileSystem.openOutput(file.getPath(), totalBytes); return fileSystem.openOutput(file.getPath(), totalBytes);
} }
var rootSc = sc.identicalSubShell().elevated(ElevationFunction.elevated("sudo")).start(); var rootSc = sc.identicalSubShell()
.elevated(ElevationFunction.elevated("sudo"))
.start();
var rootFs = new ConnectionFileSystem(rootSc); var rootFs = new ConnectionFileSystem(rootSc);
try { try {
return new FilterOutputStream(rootFs.openOutput(file.getPath(), totalBytes)) { return new FilterOutputStream(rootFs.openOutput(file.getPath(), totalBytes)) {
@ -123,7 +127,7 @@ public class BrowserFileOpener {
return entry.getFileSystem().openInput(file); return entry.getFileSystem().openInput(file);
}, },
(size) -> { (size) -> {
return openFileOutput(model,entry, size); return openFileOutput(model, entry, size);
}, },
FileOpener::openInTextEditor); FileOpener::openInTextEditor);
} }

View file

@ -142,8 +142,7 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
this.menu = new Menu( this.menu = new Menu(
// Use original name, not the link target // Use original name, not the link target
browserEntry.getRawFileEntry().getName(), browserEntry.getRawFileEntry().getName(),
PrettyImageHelper.ofFixedSize( PrettyImageHelper.ofFixedSize(FileIconManager.getFileIcon(browserEntry.getRawFileEntry()), 24, 24)
FileIconManager.getFileIcon(browserEntry.getRawFileEntry()), 24, 24)
.createRegion()); .createRegion());
createMenu(); createMenu();
addInputListeners(); addInputListeners();

View file

@ -25,8 +25,10 @@ import io.xpipe.core.process.ShellDialects;
import io.xpipe.core.process.ShellOpenFunction; import io.xpipe.core.process.ShellOpenFunction;
import io.xpipe.core.store.*; import io.xpipe.core.store.*;
import io.xpipe.core.util.FailableConsumer; import io.xpipe.core.util.FailableConsumer;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.*; import javafx.beans.property.*;
import lombok.Getter; import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
@ -71,7 +73,9 @@ public final class OpenFileSystemModel extends BrowserSessionTab<FileSystemStore
@Override @Override
public boolean canImmediatelyClose() { public boolean canImmediatelyClose() {
if (fileSystem == null || fileSystem.getShell().isEmpty() || !fileSystem.getShell().get().getLock().isLocked()) { if (fileSystem == null
|| fileSystem.getShell().isEmpty()
|| !fileSystem.getShell().get().getLock().isLocked()) {
return true; return true;
} }
@ -251,7 +255,11 @@ public final class OpenFileSystemModel extends BrowserSessionTab<FileSystemStore
entry.getEntry(), entry.getEntry(),
name, name,
directory, directory,
fileSystem.getShell().get().singularSubShell(ShellOpenFunction.of(CommandBuilder.ofString(adjustedPath), false))); fileSystem
.getShell()
.get()
.singularSubShell(
ShellOpenFunction.of(CommandBuilder.ofString(adjustedPath), false)));
} else { } else {
TerminalLauncher.open( TerminalLauncher.open(
entry.getEntry(), entry.getEntry(),

View file

@ -63,10 +63,7 @@ public abstract class BrowserIconDirectoryType {
var closedIcon = "browser/" + split[2].trim(); var closedIcon = "browser/" + split[2].trim();
var lightClosedIcon = split.length > 4 ? "browser/" + split[4].trim() : closedIcon; var lightClosedIcon = split.length > 4 ? "browser/" + split[4].trim() : closedIcon;
ALL.add(new Simple( ALL.add(new Simple(id, new IconVariant(lightClosedIcon, closedIcon), filter));
id,
new IconVariant(lightClosedIcon, closedIcon),
filter));
} }
} }
}); });

View file

@ -35,8 +35,6 @@ public class FileIconManager {
} }
} }
return "browser/" + (r.getKind() == FileKind.DIRECTORY return "browser/" + (r.getKind() == FileKind.DIRECTORY ? "default_folder.svg" : "default_file.svg");
? "default_folder.svg"
: "default_file.svg");
} }
} }

View file

@ -20,6 +20,7 @@ import io.xpipe.app.util.FileReference;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FileSystemStore; import io.xpipe.core.store.FileSystemStore;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.geometry.Pos; import javafx.geometry.Pos;
@ -64,10 +65,6 @@ public class BrowserChooserComp extends DialogComp {
}); });
} }
@Override @Override
protected String finishKey() { protected String finishKey() {
return "select"; return "select";

View file

@ -329,11 +329,14 @@ public class BrowserSessionTabsComp extends SimpleComp {
ring.setMaxSize(16, 16); ring.setMaxSize(16, 16);
ring.progressProperty() ring.progressProperty()
.bind(Bindings.createDoubleBinding( .bind(Bindings.createDoubleBinding(
() -> model.getBusy().get() && !AppPrefs.get().performanceMode().get() ? -1d : 0, PlatformThread.sync(model.getBusy()), AppPrefs.get().performanceMode())); () -> model.getBusy().get()
&& !AppPrefs.get().performanceMode().get()
? -1d
: 0,
PlatformThread.sync(model.getBusy()),
AppPrefs.get().performanceMode()));
var image = model.getEntry() var image = model.getEntry().get().getEffectiveIconFile();
.get()
.getEffectiveIconFile();
var logo = PrettyImageHelper.ofFixedSizeSquare(image, 16).createRegion(); var logo = PrettyImageHelper.ofFixedSizeSquare(image, 16).createRegion();
tab.graphicProperty() tab.graphicProperty()

View file

@ -8,6 +8,7 @@ import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.resources.AppResources; import io.xpipe.app.resources.AppResources;
import io.xpipe.core.process.OsNameState; import io.xpipe.core.process.OsNameState;
import io.xpipe.core.store.FileNames; import io.xpipe.core.store.FileNames;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
@ -52,8 +53,9 @@ public class OsLogoComp extends SimpleComp {
wrapper.getPersistentState(), wrapper.getPersistentState(),
state); state);
var hide = BindingsHelper.map(img, s -> s != null); var hide = BindingsHelper.map(img, s -> s != null);
return new StackComp( return new StackComp(List.of(
List.of(new SystemStateComp(state).hide(hide), PrettyImageHelper.ofFixedSize(img, 24, 24).visible(hide))) new SystemStateComp(state).hide(hide),
PrettyImageHelper.ofFixedSize(img, 24, 24).visible(hide)))
.createRegion(); .createRegion();
} }
@ -66,7 +68,8 @@ public class OsLogoComp extends SimpleComp {
AppResources.with(AppResources.XPIPE_MODULE, "img/os", file -> { AppResources.with(AppResources.XPIPE_MODULE, "img/os", file -> {
try (var list = Files.list(file)) { try (var list = Files.list(file)) {
list.filter(path -> path.toString().endsWith(".png") list.filter(path -> path.toString().endsWith(".png")
&& !path.toString().endsWith(LINUX_DEFAULT_24) && !path.toString().endsWith("-40.png")) && !path.toString().endsWith(LINUX_DEFAULT_24)
&& !path.toString().endsWith("-40.png"))
.map(path -> FileNames.getFileName(path.toString())) .map(path -> FileNames.getFileName(path.toString()))
.forEach(path -> { .forEach(path -> {
var base = path.replace("-dark", "").replace("-24.png", ".svg"); var base = path.replace("-dark", "").replace("-24.png", ".svg");

View file

@ -199,7 +199,8 @@ public class StoreCreationComp extends DialogComp {
(e, context, validated) -> { (e, context, validated) -> {
try { try {
DataStorage.get().addStoreEntryIfNotPresent(e); DataStorage.get().addStoreEntryIfNotPresent(e);
if (context != null && validated if (context != null
&& validated
&& e.getProvider().shouldShowScan() && e.getProvider().shouldShowScan()
&& AppPrefs.get() && AppPrefs.get()
.openConnectionSearchWindowOnConnectionCreation() .openConnectionSearchWindowOnConnectionCreation()

View file

@ -97,7 +97,8 @@ public class StoreCreationMenu {
var item = new MenuItem(); var item = new MenuItem();
item.textProperty().bind(dataStoreProvider.displayName()); item.textProperty().bind(dataStoreProvider.displayName());
item.setGraphic(PrettyImageHelper.ofFixedSizeSquare(dataStoreProvider.getDisplayIconFileName(null), 16).createRegion()); item.setGraphic(PrettyImageHelper.ofFixedSizeSquare(dataStoreProvider.getDisplayIconFileName(null), 16)
.createRegion());
item.setOnAction(event -> { item.setOnAction(event -> {
StoreCreationComp.showCreation(dataStoreProvider, category); StoreCreationComp.showCreation(dataStoreProvider, category);
event.consume(); event.consume();

View file

@ -245,15 +245,14 @@ public abstract class StoreEntryComp extends SimpleComp {
button.apply(new ContextMenuAugment<>( button.apply(new ContextMenuAugment<>(
mouseEvent -> mouseEvent.getButton() == MouseButton.PRIMARY, keyEvent -> false, () -> { mouseEvent -> mouseEvent.getButton() == MouseButton.PRIMARY, keyEvent -> false, () -> {
var cm = ContextMenuHelper.create(); var cm = ContextMenuHelper.create();
branch.getChildren(getWrapper().getEntry().ref()) branch.getChildren(getWrapper().getEntry().ref()).stream()
.stream()
.filter(actionProvider -> getWrapper().showActionProvider(actionProvider)) .filter(actionProvider -> getWrapper().showActionProvider(actionProvider))
.forEach(childProvider -> { .forEach(childProvider -> {
var menu = buildMenuItemForAction(childProvider); var menu = buildMenuItemForAction(childProvider);
if (menu != null) { if (menu != null) {
cm.getItems().add(menu); cm.getItems().add(menu);
} }
}); });
return cm; return cm;
})); }));
} }

View file

@ -9,8 +9,10 @@ import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory; import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.*; import javafx.beans.property.*;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import lombok.Getter; import lombok.Getter;
import java.time.Duration; import java.time.Duration;
@ -155,7 +157,8 @@ public class StoreEntryWrapper {
summary.setValue(null); summary.setValue(null);
} else { } else {
try { try {
summary.setValue(entry.getProvider() != null ? entry.getProvider().summaryString(this) : null); summary.setValue(
entry.getProvider() != null ? entry.getProvider().summaryString(this) : null);
} catch (Exception ex) { } catch (Exception ex) {
// Summary creation might fail or have a bug // Summary creation might fail or have a bug
ErrorEvent.fromThrowable(ex).handle(); ErrorEvent.fromThrowable(ex).handle();

View file

@ -3,12 +3,14 @@ package io.xpipe.app.comp.store;
import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.PrettyImageHelper; import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
import io.xpipe.app.fxcomps.impl.TooltipAugment; import io.xpipe.app.fxcomps.impl.TooltipAugment;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.input.MouseButton; import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;

View file

@ -133,7 +133,8 @@ public class StoreNotesComp extends Comp<StoreNotesComp.Structure> {
popover.setTitle(wrapper.getName().getValue()); popover.setTitle(wrapper.getName().getValue());
popover.showingProperty().addListener((observable, oldValue, newValue) -> { popover.showingProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) { if (!newValue) {
n.setValue(new StoreNotes(n.getValue().getCommited(), n.getValue().getCommited())); n.setValue(
new StoreNotes(n.getValue().getCommited(), n.getValue().getCommited()));
DataStorage.get().saveAsync(); DataStorage.get().saveAsync();
ref.set(null); ref.set(null);
} }

View file

@ -70,10 +70,14 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
private Comp<CompStructure<Button>> createExpandButton() { private Comp<CompStructure<Button>> createExpandButton() {
var expandButton = new IconButtonComp( var expandButton = new IconButtonComp(
Bindings.createObjectBinding( Bindings.createObjectBinding(
() -> new LabelGraphic.IconGraphic(section.getWrapper().getExpanded().get() () -> new LabelGraphic.IconGraphic(
&& section.getShownChildren().getList().size() > 0 section.getWrapper().getExpanded().get()
? "mdal-keyboard_arrow_down" && section.getShownChildren()
: "mdal-keyboard_arrow_right"), .getList()
.size()
> 0
? "mdal-keyboard_arrow_down"
: "mdal-keyboard_arrow_right"),
section.getWrapper().getExpanded(), section.getWrapper().getExpanded(),
section.getShownChildren().getList()), section.getShownChildren().getList()),
() -> { () -> {

View file

@ -55,9 +55,7 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
.apply(struc -> { .apply(struc -> {
struc.get() struc.get()
.setGraphic(PrettyImageHelper.ofFixedSize( .setGraphic(PrettyImageHelper.ofFixedSize(
section.getWrapper().getIconFile(), section.getWrapper().getIconFile(), 16, 16)
16,
16)
.createRegion()); .createRegion());
}) })
.apply(struc -> { .apply(struc -> {
@ -79,7 +77,8 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
&& section.getShownChildren().getList().size() > 0); && section.getShownChildren().getList().size() > 0);
var button = new IconButtonComp( var button = new IconButtonComp(
Bindings.createObjectBinding( Bindings.createObjectBinding(
() -> new LabelGraphic.IconGraphic(expanded.get() ? "mdal-keyboard_arrow_down" : "mdal-keyboard_arrow_right"), () -> new LabelGraphic.IconGraphic(
expanded.get() ? "mdal-keyboard_arrow_down" : "mdal-keyboard_arrow_right"),
expanded), expanded),
() -> { () -> {
expanded.set(!expanded.get()); expanded.set(!expanded.get());

View file

@ -9,6 +9,7 @@ import io.xpipe.app.fxcomps.util.LabelGraphic;
import io.xpipe.app.prefs.AppPrefsComp; import io.xpipe.app.prefs.AppPrefsComp;
import io.xpipe.app.util.Hyperlinks; import io.xpipe.app.util.Hyperlinks;
import io.xpipe.app.util.LicenseProvider; import io.xpipe.app.util.LicenseProvider;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
@ -16,6 +17,7 @@ import javafx.beans.value.ObservableValue;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyCombination;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
@ -117,18 +119,18 @@ public class AppLayoutModel {
() -> Hyperlinks.open( () -> Hyperlinks.open(
"http://localhost:" + AppBeaconServer.get().getPort()), "http://localhost:" + AppBeaconServer.get().getPort()),
null) null)
// new Entry( // new Entry(
// AppI18n.observable("webtop"), // AppI18n.observable("webtop"),
// "mdi2d-desktop-mac", // "mdi2d-desktop-mac",
// null, // null,
// () -> Hyperlinks.open(Hyperlinks.GITHUB_WEBTOP), // () -> Hyperlinks.open(Hyperlinks.GITHUB_WEBTOP),
// null) // null)
)); ));
var now = Instant.now(); var now = Instant.now();
var zone = ZoneId.of(ZoneId.SHORT_IDS.get("PST")); var zone = ZoneId.of(ZoneId.SHORT_IDS.get("PST"));
var phStart = ZonedDateTime.of(2024,10,22,0,1, 0, 0, zone).toInstant(); var phStart = ZonedDateTime.of(2024, 10, 22, 0, 1, 0, 0, zone).toInstant();
var phEnd = ZonedDateTime.of(2024,10,22,23,59, 0, 0, zone).toInstant(); var phEnd = ZonedDateTime.of(2024, 10, 22, 23, 59, 0, 0, zone).toInstant();
var phShow = now.isAfter(phStart) && now.isBefore(phEnd); var phShow = now.isAfter(phStart) && now.isBefore(phEnd);
if (phShow) { if (phShow) {
l.add(new Entry( l.add(new Entry(
@ -150,5 +152,10 @@ public class AppLayoutModel {
double browserConnectionsWidth; double browserConnectionsWidth;
} }
public record Entry(ObservableValue<String> name, LabelGraphic icon, Comp<?> comp, Runnable action, KeyCombination combination) {} public record Entry(
ObservableValue<String> name,
LabelGraphic icon,
Comp<?> comp,
Runnable action,
KeyCombination combination) {}
} }

View file

@ -11,10 +11,12 @@ import io.xpipe.app.resources.AppResources;
import io.xpipe.app.util.PlatformState; import io.xpipe.app.util.PlatformState;
import io.xpipe.app.util.WindowsRegistry; import io.xpipe.app.util.WindowsRegistry;
import io.xpipe.core.process.OsType; import io.xpipe.core.process.OsType;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType; import javafx.scene.control.ButtonType;
import lombok.Getter; import lombok.Getter;
import java.nio.file.Files; import java.nio.file.Files;

View file

@ -2,6 +2,7 @@ package io.xpipe.app.core.check;
import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.PlatformState; import io.xpipe.app.util.PlatformState;
import javafx.application.ConditionalFeature; import javafx.application.ConditionalFeature;
import javafx.application.Platform; import javafx.application.Platform;

View file

@ -11,7 +11,8 @@ public class AppJavaOptionsCheck {
} }
ErrorEvent.fromMessage( ErrorEvent.fromMessage(
"You have configured the global environment variable _JAVA_OPTIONS=%s on your system.".formatted(env) "You have configured the global environment variable _JAVA_OPTIONS=%s on your system."
.formatted(env)
+ " This will forcefully apply all custom JVM options to XPipe and can cause a variety of different issues." + " This will forcefully apply all custom JVM options to XPipe and can cause a variety of different issues."
+ " Please remove this global environment variable and use local configuration instead for your other JVM programs.") + " Please remove this global environment variable and use local configuration instead for your other JVM programs.")
.noDefaultActions() .noDefaultActions()

View file

@ -13,6 +13,7 @@ import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.update.UpdateChangelogAlert; import io.xpipe.app.update.UpdateChangelogAlert;
import io.xpipe.app.util.NativeBridge; import io.xpipe.app.util.NativeBridge;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import javafx.stage.Stage; import javafx.stage.Stage;
public class GuiMode extends PlatformMode { public class GuiMode extends PlatformMode {

View file

@ -16,7 +16,9 @@ import io.xpipe.app.util.XPipeSession;
import io.xpipe.core.util.FailableRunnable; import io.xpipe.core.util.FailableRunnable;
import io.xpipe.core.util.XPipeDaemonMode; import io.xpipe.core.util.XPipeDaemonMode;
import io.xpipe.core.util.XPipeInstallation; import io.xpipe.core.util.XPipeInstallation;
import javafx.application.Platform; import javafx.application.Platform;
import lombok.Getter; import lombok.Getter;
import java.util.ArrayList; import java.util.ArrayList;

View file

@ -10,6 +10,7 @@ import io.xpipe.app.resources.AppImages;
import io.xpipe.app.update.UpdateAvailableAlert; import io.xpipe.app.update.UpdateAvailableAlert;
import io.xpipe.app.util.PlatformState; import io.xpipe.app.util.PlatformState;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import javafx.application.Application; import javafx.application.Application;
public abstract class PlatformMode extends OperationMode { public abstract class PlatformMode extends OperationMode {

View file

@ -1,7 +1,5 @@
package io.xpipe.app.fxcomps.impl; 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.base.ButtonComp;
import io.xpipe.app.comp.store.*; import io.xpipe.app.comp.store.*;
import io.xpipe.app.core.AppFont; import io.xpipe.app.core.AppFont;
@ -15,6 +13,7 @@ import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.DataStoreCategoryChoiceComp; import io.xpipe.app.util.DataStoreCategoryChoiceComp;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
@ -26,6 +25,9 @@ import javafx.scene.control.MenuButton;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import atlantafx.base.controls.Popover;
import atlantafx.base.theme.Styles;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;

View file

@ -1,16 +1,18 @@
package io.xpipe.app.fxcomps.impl; package io.xpipe.app.fxcomps.impl;
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;
import io.xpipe.app.fxcomps.util.LabelGraphic; import io.xpipe.app.fxcomps.util.LabelGraphic;
import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.PlatformThread;
import javafx.beans.property.SimpleObjectProperty; 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 javafx.scene.control.Button;
import atlantafx.base.theme.Styles;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;
public class IconButtonComp extends Comp<CompStructure<Button>> { public class IconButtonComp extends Comp<CompStructure<Button>> {

View file

@ -17,6 +17,7 @@ import io.xpipe.app.storage.DataColor;
import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory; import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.util.ContextMenuHelper; import io.xpipe.app.util.ContextMenuHelper;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.css.PseudoClass; import javafx.css.PseudoClass;
@ -31,6 +32,7 @@ import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton; import javafx.scene.input.MouseButton;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Value; import lombok.Value;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;

View file

@ -42,10 +42,7 @@ public class AppPrefs {
mapVaultSpecific(new SimpleBooleanProperty(false), "dontAutomaticallyStartVmSshServer", Boolean.class); mapVaultSpecific(new SimpleBooleanProperty(false), "dontAutomaticallyStartVmSshServer", Boolean.class);
final BooleanProperty dontAcceptNewHostKeys = final BooleanProperty dontAcceptNewHostKeys =
mapVaultSpecific(new SimpleBooleanProperty(false), "dontAcceptNewHostKeys", Boolean.class); mapVaultSpecific(new SimpleBooleanProperty(false), "dontAcceptNewHostKeys", Boolean.class);
public final BooleanProperty performanceMode = map( public final BooleanProperty performanceMode = map(new SimpleBooleanProperty(), "performanceMode", Boolean.class);
new SimpleBooleanProperty(),
"performanceMode",
Boolean.class);
public final BooleanProperty useBundledTools = public final BooleanProperty useBundledTools =
map(new SimpleBooleanProperty(false), "useBundledTools", Boolean.class); map(new SimpleBooleanProperty(false), "useBundledTools", Boolean.class);
public final ObjectProperty<AppTheme.Theme> theme = public final ObjectProperty<AppTheme.Theme> theme =

View file

@ -3,8 +3,10 @@ package io.xpipe.app.resources;
import io.xpipe.app.core.AppExtensionManager; import io.xpipe.app.core.AppExtensionManager;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.issue.TrackEvent;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;

View file

@ -361,7 +361,8 @@ public abstract class DataStorage {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends ValidationContext<?>> boolean refreshChildren(DataStoreEntry e, T context, boolean throwOnFail) throws Exception { public <T extends ValidationContext<?>> boolean refreshChildren(DataStoreEntry e, T context, boolean throwOnFail)
throws Exception {
if (!(e.getStore() instanceof FixedHierarchyStore<?> h)) { if (!(e.getStore() instanceof FixedHierarchyStore<?> h)) {
return false; return false;
} }
@ -377,9 +378,10 @@ public abstract class DataStorage {
} }
} }
newChildren = ((FixedHierarchyStore<T>)h).listChildren(context).stream() newChildren = ((FixedHierarchyStore<T>) h)
.filter(dataStoreEntryRef -> dataStoreEntryRef != null && dataStoreEntryRef.get() != null) .listChildren(context).stream()
.toList(); .filter(dataStoreEntryRef -> dataStoreEntryRef != null && dataStoreEntryRef.get() != null)
.toList();
} catch (Exception ex) { } catch (Exception ex) {
if (throwOnFail) { if (throwOnFail) {
throw ex; throw ex;

View file

@ -1,17 +1,18 @@
package io.xpipe.app.storage; package io.xpipe.app.storage;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.resources.SystemIcons;
import io.xpipe.core.store.*;
import io.xpipe.core.util.JacksonMapper;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.resources.SystemIcons;
import io.xpipe.core.store.*;
import io.xpipe.core.util.JacksonMapper;
import lombok.*; import lombok.*;
import lombok.experimental.NonFinal; import lombok.experimental.NonFinal;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
@ -172,15 +173,13 @@ public class DataStoreEntry extends StorageElement {
return entry; return entry;
} }
public String getEffectiveIconFile() { public String getEffectiveIconFile() {
if (getValidity() == Validity.LOAD_FAILED) { if (getValidity() == Validity.LOAD_FAILED) {
return "disabled_icon.png"; return "disabled_icon.png";
} }
if (icon == null) { if (icon == null) {
return getProvider() return getProvider().getDisplayIconFileName(getStore());
.getDisplayIconFileName(getStore());
} }
return "app:system/" + icon + ".svg"; return "app:system/" + icon + ".svg";
@ -530,7 +529,8 @@ public class DataStoreEntry extends StorageElement {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> ValidationContext<?> validateAndKeepOpenOrThrowAndClose(ValidationContext<?> existingContext) throws Throwable { public <T> ValidationContext<?> validateAndKeepOpenOrThrowAndClose(ValidationContext<?> existingContext)
throws Throwable {
if (store == null) { if (store == null) {
return null; return null;
} }
@ -542,7 +542,9 @@ public class DataStoreEntry extends StorageElement {
try { try {
store.checkComplete(); store.checkComplete();
incrementBusyCounter(); incrementBusyCounter();
ValidationContext<T> context = existingContext != null ? (ValidationContext<T>) existingContext : (ValidationContext<T>) l.createContext(); ValidationContext<T> context = existingContext != null
? (ValidationContext<T>) existingContext
: (ValidationContext<T>) l.createContext();
if (context == null) { if (context == null) {
return null; return null;
} }

View file

@ -130,8 +130,7 @@ public interface WezTerminalType extends ExternalTerminalType {
.readStdoutOrThrow(); .readStdoutOrThrow();
var path = Path.of(pathOut); var path = Path.of(pathOut);
var spawn = sc.command(CommandBuilder.of() var spawn = sc.command(CommandBuilder.of()
.addFile(path .addFile(path.resolve("Contents")
.resolve("Contents")
.resolve("MacOS") .resolve("MacOS")
.resolve("wezterm") .resolve("wezterm")
.toString()) .toString())
@ -140,8 +139,7 @@ public interface WezTerminalType extends ExternalTerminalType {
.executeAndCheck(); .executeAndCheck();
if (!spawn) { if (!spawn) {
ExternalApplicationHelper.startAsync(CommandBuilder.of() ExternalApplicationHelper.startAsync(CommandBuilder.of()
.addFile(path .addFile(path.resolve("Contents")
.resolve("Contents")
.resolve("MacOS") .resolve("MacOS")
.resolve("wezterm-gui") .resolve("wezterm-gui")
.toString()) .toString())

View file

@ -5,6 +5,7 @@ import io.xpipe.app.core.AppStyle;
import io.xpipe.app.core.AppTheme; import io.xpipe.app.core.AppTheme;
import io.xpipe.app.core.window.AppWindowHelper; import io.xpipe.app.core.window.AppWindowHelper;
import io.xpipe.app.fxcomps.impl.TextFieldComp; import io.xpipe.app.fxcomps.impl.TextFieldComp;
import javafx.animation.AnimationTimer; import javafx.animation.AnimationTimer;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;

View file

@ -11,7 +11,9 @@ import java.util.Optional;
public class CommandSupport { public class CommandSupport {
public static Optional<String> findProgram(ShellControl processControl, String name) throws Exception { public static Optional<String> findProgram(ShellControl processControl, String name) throws Exception {
var out = processControl.command(processControl.getShellDialect().getWhichCommand(name)).readStdoutIfPossible(); var out = processControl
.command(processControl.getShellDialect().getWhichCommand(name))
.readStdoutIfPossible();
return out.flatMap(s -> s.lines().findFirst()).map(String::trim); return out.flatMap(s -> s.lines().findFirst()).map(String::trim);
} }

View file

@ -14,8 +14,10 @@ public class Hyperlinks {
public static final String DISCORD = "https://discord.gg/8y89vS8cRb"; public static final String DISCORD = "https://discord.gg/8y89vS8cRb";
public static final String GITHUB_WEBTOP = "https://github.com/xpipe-io/xpipe-webtop"; public static final String GITHUB_WEBTOP = "https://github.com/xpipe-io/xpipe-webtop";
public static final String SELFHST_ICONS = "https://github.com/selfhst/icons"; public static final String SELFHST_ICONS = "https://github.com/selfhst/icons";
public static final String SLACK = "https://join.slack.com/t/XPipe/shared_invite/zt-1awjq0t5j-5i4UjNJfNe1VN4b_auu6Cg"; public static final String SLACK =
public static final String PRODUCT_HUNT = "https://www.producthunt.com/posts/xpipe?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-xpipe"; "https://join.slack.com/t/XPipe/shared_invite/zt-1awjq0t5j-5i4UjNJfNe1VN4b_auu6Cg";
public static final String PRODUCT_HUNT =
"https://www.producthunt.com/posts/xpipe?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-xpipe";
static final String[] browsers = { static final String[] browsers = {
"xdg-open", "google-chrome", "firefox", "opera", "konqueror", "mozilla", "gnome-open", "open" "xdg-open", "google-chrome", "firefox", "opera", "konqueror", "mozilla", "gnome-open", "open"

View file

@ -74,5 +74,4 @@ public class ScanAlert {
stage -> new ScanDialog( stage -> new ScanDialog(
stage, initialStore != null ? initialStore.ref() : null, applicable, shellValidationContext)); stage, initialStore != null ? initialStore.ref() : null, applicable, shellValidationContext));
} }
} }

View file

@ -14,6 +14,7 @@ import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellControl;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
import io.xpipe.core.store.ShellValidationContext; import io.xpipe.core.store.ShellValidationContext;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.*; import javafx.beans.property.*;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
@ -36,14 +37,16 @@ class ScanDialog extends DialogComp {
private final BiFunction<DataStoreEntry, ShellControl, List<ScanProvider.ScanOperation>> applicable; private final BiFunction<DataStoreEntry, ShellControl, List<ScanProvider.ScanOperation>> applicable;
private final Stage window; private final Stage window;
private final ObjectProperty<DataStoreEntryRef<ShellStore>> entry; private final ObjectProperty<DataStoreEntryRef<ShellStore>> entry;
private final ListProperty<ScanProvider.ScanOperation> selected = new SimpleListProperty<>(FXCollections.observableArrayList()); private final ListProperty<ScanProvider.ScanOperation> selected =
new SimpleListProperty<>(FXCollections.observableArrayList());
private final BooleanProperty busy = new SimpleBooleanProperty(); private final BooleanProperty busy = new SimpleBooleanProperty();
private ShellValidationContext shellValidationContext; private ShellValidationContext shellValidationContext;
ScanDialog( ScanDialog(
Stage window, DataStoreEntryRef<ShellStore> entry, BiFunction<DataStoreEntry, ShellControl, List<ScanProvider.ScanOperation>> applicable, Stage window,
ShellValidationContext shellValidationContext DataStoreEntryRef<ShellStore> entry,
) { BiFunction<DataStoreEntry, ShellControl, List<ScanProvider.ScanOperation>> applicable,
ShellValidationContext shellValidationContext) {
this.window = window; this.window = window;
this.initialStore = entry; this.initialStore = entry;
this.entry = new SimpleObjectProperty<>(entry); this.entry = new SimpleObjectProperty<>(entry);
@ -74,7 +77,9 @@ class ScanDialog extends DialogComp {
for (var a : copy) { for (var a : copy) {
// If the user decided to remove the selected entry // If the user decided to remove the selected entry
// while the scan is running, just return instantly // while the scan is running, just return instantly
if (!DataStorage.get().getStoreEntriesSet().contains(entry.get().get())) { if (!DataStorage.get()
.getStoreEntriesSet()
.contains(entry.get().get())) {
return; return;
} }
@ -115,10 +120,17 @@ class ScanDialog extends DialogComp {
StackPane stackPane = new StackPane(); StackPane stackPane = new StackPane();
stackPane.getStyleClass().add("scan-list"); stackPane.getStyleClass().add("scan-list");
var b = new OptionsBuilder().name("scanAlertChoiceHeader") var b = new OptionsBuilder()
.name("scanAlertChoiceHeader")
.description("scanAlertChoiceHeaderDescription") .description("scanAlertChoiceHeaderDescription")
.addComp(new DataStoreChoiceComp<>(DataStoreChoiceComp.Mode.OTHER, null, entry, ShellStore.class, store1 -> true, .addComp(new DataStoreChoiceComp<>(
StoreViewState.get().getAllConnectionsCategory()).disable(new SimpleBooleanProperty(initialStore != null))) DataStoreChoiceComp.Mode.OTHER,
null,
entry,
ShellStore.class,
store1 -> true,
StoreViewState.get().getAllConnectionsCategory())
.disable(new SimpleBooleanProperty(initialStore != null)))
.name("scanAlertHeader") .name("scanAlertHeader")
.description("scanAlertHeaderDescription") .description("scanAlertHeaderDescription")
.addComp(Comp.of(() -> stackPane).vgrow()) .addComp(Comp.of(() -> stackPane).vgrow())
@ -152,7 +164,8 @@ class ScanDialog extends DialogComp {
shellValidationContext = null; shellValidationContext = null;
} }
shellValidationContext = new ShellValidationContext(newValue.getStore().control().withoutLicenseCheck().start()); shellValidationContext = new ShellValidationContext(
newValue.getStore().control().withoutLicenseCheck().start());
var a = applicable.apply(entry.get().get(), shellValidationContext.get()); var a = applicable.apply(entry.get().get(), shellValidationContext.get());
Platform.runLater(() -> { Platform.runLater(() -> {
@ -161,8 +174,9 @@ class ScanDialog extends DialogComp {
return; return;
} }
selected.setAll( selected.setAll(a.stream()
a.stream().filter(scanOperation -> scanOperation.isDefaultSelected() && !scanOperation.isDisabled()).toList()); .filter(scanOperation -> scanOperation.isDefaultSelected() && !scanOperation.isDisabled())
.toList());
Function<ScanProvider.ScanOperation, String> nameFunc = (ScanProvider.ScanOperation s) -> { Function<ScanProvider.ScanOperation, String> nameFunc = (ScanProvider.ScanOperation s) -> {
var n = AppI18n.get(s.getNameKey()); var n = AppI18n.get(s.getNameKey());
if (s.getLicensedFeatureId() == null) { if (s.getLicensedFeatureId() == null) {
@ -170,10 +184,14 @@ class ScanDialog extends DialogComp {
} }
var suffix = LicenseProvider.get().getFeature(s.getLicensedFeatureId()); var suffix = LicenseProvider.get().getFeature(s.getLicensedFeatureId());
return n + suffix.getDescriptionSuffix().map(d -> " (" + d + ")").orElse(""); return n
+ suffix.getDescriptionSuffix()
.map(d -> " (" + d + ")")
.orElse("");
}; };
var r = new ListSelectorComp<>(a, nameFunc, selected, scanOperation -> scanOperation.isDisabled(), var r = new ListSelectorComp<>(
a.size() > 3).createRegion(); a, nameFunc, selected, scanOperation -> scanOperation.isDisabled(), a.size() > 3)
.createRegion();
stackPane.getChildren().add(r); stackPane.getChildren().add(r);
}); });
}); });

View file

@ -34,11 +34,14 @@ public class SimpleScriptQuickEditAction implements ActionProvider {
@Override @Override
public void execute() { public void execute() {
var predefined = DataStorage.get().getStoreCategoryIfPresent(ref.get().getCategoryUuid()) var predefined = DataStorage.get()
.map(category -> category.getUuid().equals(DataStorage.PREDEFINED_SCRIPTS_CATEGORY_UUID)) .getStoreCategoryIfPresent(ref.get().getCategoryUuid())
.orElse(false) && .map(category -> category.getUuid().equals(DataStorage.PREDEFINED_SCRIPTS_CATEGORY_UUID))
Arrays.stream(PredefinedScriptStore.values()) .orElse(false)
.anyMatch(predefinedScriptStore -> predefinedScriptStore.getName().equals(ref.get().getName())); && Arrays.stream(PredefinedScriptStore.values())
.anyMatch(predefinedScriptStore -> predefinedScriptStore
.getName()
.equals(ref.get().getName()));
if (predefined) { if (predefined) {
StoreCreationComp.showEdit(ref.get()); StoreCreationComp.showEdit(ref.get());
return; return;

View file

@ -22,11 +22,13 @@ import io.xpipe.app.util.Validator;
import io.xpipe.core.process.ShellDialect; import io.xpipe.core.process.ShellDialect;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import io.xpipe.core.util.Identifiers; import io.xpipe.core.util.Identifiers;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleListProperty; import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.util.ArrayList; import java.util.ArrayList;
@ -218,8 +220,12 @@ public class SimpleScriptStoreProvider implements EnabledParentStoreProvider, Da
var file = st.isRunnableScript() ? AppI18n.get("file") : null; var file = st.isRunnableScript() ? AppI18n.get("file") : null;
var shell = st.isRunnableScript() ? AppI18n.get("shell") : null; var shell = st.isRunnableScript() ? AppI18n.get("shell") : null;
var runnable = st.isRunnableScript() ? AppI18n.get("hub") : null; var runnable = st.isRunnableScript() ? AppI18n.get("hub") : null;
var type = st.getMinimumDialect() != null ? st.getMinimumDialect().getDisplayName() + " " + AppI18n.get("script") : null; var type = st.getMinimumDialect() != null
var suffix = String.join(" / ", Stream.of(init, shell, file, runnable).filter(s -> s != null).toList()); ? st.getMinimumDialect().getDisplayName() + " " + AppI18n.get("script")
: null;
var suffix = String.join(
" / ",
Stream.of(init, shell, file, runnable).filter(s -> s != null).toList());
if (!suffix.isEmpty()) { if (!suffix.isEmpty()) {
suffix = "(" + suffix + ")"; suffix = "(" + suffix + ")";
} else { } else {

View file

@ -5,9 +5,9 @@ import io.xpipe.app.util.FixedHierarchyStore;
import io.xpipe.app.util.Validators; import io.xpipe.app.util.Validators;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.FixedChildStore; import io.xpipe.core.store.FixedChildStore;
import io.xpipe.core.store.ValidationContext;
import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.store.ValidationContext;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;