[stage]
|
@ -115,7 +115,6 @@ Alternatively, you can also use your favorite package manager (if supported):
|
|||
- [choco](https://community.chocolatey.org/packages/xpipe): `choco install xpipe`
|
||||
- [AUR package](https://aur.archlinux.org/packages/xpipe): `yay -S xpipe`
|
||||
- [Homebrew](https://github.com/xpipe-io/homebrew-tap): `brew install --cask xpipe-io/tap/xpipe`
|
||||
- [nixpkg](https://github.com/xpipe-io/nixpkg): You can install XPipe by following the linked repository instructions
|
||||
|
||||
## Open source model
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ public class BrowserWelcomeComp extends SimpleComp {
|
|||
var listBox = new ListBoxViewComp<>(list, list, e -> {
|
||||
var entry = DataStorage.get().getStoreEntryIfPresent(e.getUuid());
|
||||
var graphic = entry.get().getProvider().getDisplayIconFileName(entry.get().getStore());
|
||||
var view = PrettyImageHelper.ofFixedSize(graphic, 50, 40);
|
||||
var view = PrettyImageHelper.ofFixedSquare(graphic, 45);
|
||||
view.padding(new Insets(2, 8, 2, 8));
|
||||
var content =
|
||||
JfxHelper.createNamedEntry(DataStorage.get().getStoreDisplayName(entry.get()), e.getPath(), graphic);
|
||||
|
|
|
@ -53,7 +53,7 @@ public class FileSystemHelper {
|
|||
}
|
||||
|
||||
var shell = model.getFileSystem().getShell();
|
||||
if (shell.isEmpty() || !shell.get().isRunning()) {
|
||||
if (shell.isEmpty()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,9 +131,6 @@ public final class OpenFileSystemModel {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
// Start shell in case we exited
|
||||
getFileSystem().getShell().orElseThrow().start();
|
||||
|
||||
// Fix common issues with paths
|
||||
var adjustedPath = FileSystemHelper.adjustPath(this, path);
|
||||
if (!Objects.equals(path, adjustedPath)) {
|
||||
|
|
|
@ -29,8 +29,6 @@ public interface LeafAction extends BrowserAction {
|
|||
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
BooleanScope.execute(model.getBusy(), () -> {
|
||||
// Start shell in case we exited
|
||||
model.getFileSystem().getShell().orElseThrow().start();
|
||||
execute(model, selected);
|
||||
});
|
||||
});
|
||||
|
@ -66,8 +64,6 @@ public interface LeafAction extends BrowserAction {
|
|||
mi.setOnAction(event -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
BooleanScope.execute(model.getBusy(), () -> {
|
||||
// Start shell in case we exited
|
||||
model.getFileSystem().getShell().orElseThrow().start();
|
||||
execute(model, selected);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -51,7 +51,7 @@ public class OsLogoComp extends SimpleComp {
|
|||
var hide = BindingsHelper.map(img, s -> s != null);
|
||||
return new StackComp(List.of(
|
||||
new SystemStateComp(state).hide(hide),
|
||||
PrettyImageHelper.ofRasterized(img, 24, 24).visible(hide)))
|
||||
PrettyImageHelper.ofSvg(img, 24, 24).visible(hide)))
|
||||
.createRegion();
|
||||
}
|
||||
|
||||
|
@ -66,9 +66,8 @@ public class OsLogoComp extends SimpleComp {
|
|||
if (ICONS.isEmpty()) {
|
||||
AppResources.with(AppResources.XPIPE_MODULE, "img/os", file -> {
|
||||
try (var list = Files.list(file)) {
|
||||
list.filter(path -> path.toString().endsWith(".svg") && !path.toString().endsWith(LINUX_DEFAULT))
|
||||
.map(path -> FileNames.getFileName(path.toString())).forEach(path -> {
|
||||
var base = FileNames.getBaseName(path).replace("-dark", "") + "-24.png";
|
||||
list.filter(path -> !path.toString().endsWith(LINUX_DEFAULT)).map(path -> FileNames.getFileName(path.toString())).forEach(path -> {
|
||||
var base = FileNames.getBaseName(path).replace("-dark", "") + ".svg";
|
||||
ICONS.put(FileNames.getBaseName(base).split("-")[0], "os/" + base);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public class DenseStoreEntryComp extends StoreEntryComp {
|
|||
}, grid.widthProperty()));
|
||||
|
||||
if (showIcon) {
|
||||
var storeIcon = createIcon(30, 24);
|
||||
var storeIcon = createIcon(30, 25);
|
||||
grid.getColumnConstraints().add(new ColumnConstraints(46));
|
||||
grid.add(storeIcon, 0, 0);
|
||||
GridPane.setHalignment(storeIcon, HPos.CENTER);
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
package io.xpipe.app.comp.store;
|
||||
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.DataStoreProviders;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.util.CustomComboBoxBuilder;
|
||||
import io.xpipe.app.util.JfxHelper;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.layout.Region;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
@ -18,23 +19,27 @@ import lombok.experimental.FieldDefaults;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||
@AllArgsConstructor
|
||||
public class DataStoreProviderChoiceComp extends Comp<CompStructure<ComboBox<DataStoreProvider>>> {
|
||||
public class DsStoreProviderChoiceComp extends Comp<CompStructure<ComboBox<Node>>> {
|
||||
|
||||
Predicate<DataStoreProvider> filter;
|
||||
Property<DataStoreProvider> provider;
|
||||
boolean staticDisplay;
|
||||
|
||||
private Region createDefaultNode() {
|
||||
return JfxHelper.createNamedEntry(
|
||||
AppI18n.get("selectType"), AppI18n.get("selectTypeDescription"), "connection_icon.svg");
|
||||
}
|
||||
|
||||
private List<DataStoreProvider> getProviders() {
|
||||
return DataStoreProviders.getAll().stream().filter(filter).toList();
|
||||
}
|
||||
|
||||
private Region createGraphic(DataStoreProvider provider) {
|
||||
if (provider == null) {
|
||||
return null;
|
||||
return createDefaultNode();
|
||||
}
|
||||
|
||||
var graphic = provider.getDisplayIconFileName(null);
|
||||
|
@ -42,40 +47,20 @@ public class DataStoreProviderChoiceComp extends Comp<CompStructure<ComboBox<Dat
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<ComboBox<DataStoreProvider>> createBase() {
|
||||
Supplier<ListCell<DataStoreProvider>> cellFactory = () -> new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(DataStoreProvider item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setGraphic(createGraphic(item));
|
||||
setAccessibleText(item != null ? item.getDisplayName() : null);
|
||||
setAccessibleHelp(item != null ? item.getDisplayDescription() : null);
|
||||
}
|
||||
};
|
||||
var cb = new ComboBox<DataStoreProvider>();
|
||||
cb.setCellFactory(param -> {
|
||||
return cellFactory.get();
|
||||
});
|
||||
cb.setButtonCell(cellFactory.get());
|
||||
public CompStructure<ComboBox<Node>> createBase() {
|
||||
var comboBox = new CustomComboBoxBuilder<>(provider, this::createGraphic, createDefaultNode(), v -> true);
|
||||
comboBox.setAccessibleNames(dataStoreProvider -> dataStoreProvider.getDisplayName());
|
||||
var l = getProviders().stream()
|
||||
.filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.getCreationCategory() != null || staticDisplay)
|
||||
.toList();
|
||||
l.forEach(dataStoreProvider -> cb.getItems().add(dataStoreProvider));
|
||||
if (provider.getValue() == null) {
|
||||
provider.setValue(l.getFirst());
|
||||
.filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.getCreationCategory() != null || staticDisplay).toList();
|
||||
l
|
||||
.forEach(comboBox::add);
|
||||
if (l.size() == 1) {
|
||||
provider.setValue(l.get(0));
|
||||
}
|
||||
cb.setValue(provider.getValue());
|
||||
provider.bind(cb.valueProperty());
|
||||
ComboBox<Node> cb = comboBox.build();
|
||||
cb.getStyleClass().add("data-source-type");
|
||||
cb.getStyleClass().add("choice-comp");
|
||||
cb.setAccessibleText("Choose connection type");
|
||||
cb.setOnKeyPressed(event -> {
|
||||
if (!event.getCode().equals(KeyCode.ENTER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cb.show();
|
||||
event.consume();
|
||||
});
|
||||
return new SimpleCompStructure<>(cb);
|
||||
}
|
||||
}
|
|
@ -276,7 +276,7 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
|||
var layout = new BorderPane();
|
||||
layout.getStyleClass().add("store-creator");
|
||||
layout.setPadding(new Insets(20));
|
||||
var providerChoice = new DataStoreProviderChoiceComp(filter, provider, staticDisplay);
|
||||
var providerChoice = new DsStoreProviderChoiceComp(filter, provider, staticDisplay);
|
||||
if (staticDisplay) {
|
||||
providerChoice.apply(struc -> struc.get().setDisable(true));
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public class StandardStoreEntryComp extends StoreEntryComp {
|
|||
grid.setHgap(7);
|
||||
grid.setVgap(0);
|
||||
|
||||
var storeIcon = createIcon(50, 40);
|
||||
var storeIcon = createIcon(50, 39);
|
||||
grid.add(storeIcon, 0, 0, 1, 2);
|
||||
grid.getColumnConstraints().add(new ColumnConstraints(66));
|
||||
|
||||
|
|
|
@ -3,9 +3,7 @@ package io.xpipe.app.comp.store;
|
|||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.DataStoreProviders;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import io.xpipe.app.util.ScanAlert;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuButton;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.control.SeparatorMenuItem;
|
||||
|
@ -14,71 +12,84 @@ import org.kordamp.ikonli.javafx.FontIcon;
|
|||
public class StoreCreationMenu {
|
||||
|
||||
public static void addButtons(MenuButton menu) {
|
||||
var automatically = new MenuItem();
|
||||
automatically.setGraphic(new FontIcon("mdi2e-eye-plus-outline"));
|
||||
automatically.textProperty().bind(AppI18n.observable("addAutomatically"));
|
||||
automatically.setOnAction(event -> {
|
||||
ScanAlert.showAsync(null);
|
||||
event.consume();
|
||||
});
|
||||
menu.getItems().add(automatically);
|
||||
menu.getItems().add(new SeparatorMenuItem());
|
||||
|
||||
menu.getItems().add(category("addHost", "mdi2h-home-plus",
|
||||
DataStoreProvider.CreationCategory.HOST, "ssh"));
|
||||
|
||||
menu.getItems().add(category("addShell", "mdi2t-text-box-multiple",
|
||||
DataStoreProvider.CreationCategory.SHELL, null));
|
||||
|
||||
menu.getItems().add(category("addScript", "mdi2s-script-text-outline",
|
||||
DataStoreProvider.CreationCategory.SCRIPT, "script"));
|
||||
|
||||
menu.getItems().add(category("addCommand", "mdi2c-code-greater-than",
|
||||
DataStoreProvider.CreationCategory.COMMAND, "cmd"));
|
||||
|
||||
menu.getItems().add(category("addTunnel", "mdi2v-vector-polyline-plus",
|
||||
DataStoreProvider.CreationCategory.TUNNEL, null));
|
||||
|
||||
menu.getItems().add(category("addDatabase", "mdi2d-database-plus",
|
||||
DataStoreProvider.CreationCategory.DATABASE, null));
|
||||
}
|
||||
|
||||
private static MenuItem category(String name, String graphic, DataStoreProvider.CreationCategory category, String defaultProvider) {
|
||||
var sub = DataStoreProviders.getAll().stream().filter(dataStoreProvider -> category.equals(dataStoreProvider.getCreationCategory())).toList();
|
||||
if (sub.size() < 2) {
|
||||
var item = new MenuItem();
|
||||
item.setGraphic(new FontIcon(graphic));
|
||||
item.textProperty().bind(AppI18n.observable(name));
|
||||
item.setOnAction(event -> {
|
||||
GuiDsStoreCreator.showCreation(defaultProvider != null ? DataStoreProviders.byName(defaultProvider).orElseThrow() : null,
|
||||
v -> category.equals(v.getCreationCategory()));
|
||||
{
|
||||
var automatically = new MenuItem();
|
||||
automatically.setGraphic(new FontIcon("mdi2e-eye-plus-outline"));
|
||||
automatically.textProperty().bind(AppI18n.observable("addAutomatically"));
|
||||
automatically.setOnAction(event -> {
|
||||
ScanAlert.showAsync(null);
|
||||
event.consume();
|
||||
});
|
||||
return item;
|
||||
menu.getItems().add(automatically);
|
||||
menu.getItems().add(new SeparatorMenuItem());
|
||||
}
|
||||
|
||||
var menu = new Menu();
|
||||
menu.setGraphic(new FontIcon(graphic));
|
||||
menu.textProperty().bind(AppI18n.observable(name));
|
||||
menu.setOnAction(event -> {
|
||||
if (event.getTarget() != menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
GuiDsStoreCreator.showCreation(defaultProvider != null ? DataStoreProviders.byName(defaultProvider).orElseThrow() : null,
|
||||
v -> category.equals(v.getCreationCategory()));
|
||||
event.consume();
|
||||
});
|
||||
sub.forEach(dataStoreProvider -> {
|
||||
var item = new MenuItem(dataStoreProvider.getDisplayName());
|
||||
item.setGraphic(PrettyImageHelper.ofFixedSmallSquare(dataStoreProvider.getDisplayIconFileName(null)).createRegion());
|
||||
item.setOnAction(event -> {
|
||||
GuiDsStoreCreator.showCreation(dataStoreProvider,
|
||||
v -> category.equals(v.getCreationCategory()));
|
||||
{
|
||||
var host = new MenuItem();
|
||||
host.setGraphic(new FontIcon("mdi2h-home-plus"));
|
||||
host.textProperty().bind(AppI18n.observable("addHost"));
|
||||
host.setOnAction(event -> {
|
||||
GuiDsStoreCreator.showCreation(DataStoreProviders.byName("ssh").orElseThrow(),
|
||||
v -> DataStoreProvider.CreationCategory.HOST.equals(v.getCreationCategory()));
|
||||
event.consume();
|
||||
});
|
||||
menu.getItems().add(item);
|
||||
});
|
||||
return menu;
|
||||
menu.getItems().add(host);
|
||||
}
|
||||
{
|
||||
var shell = new MenuItem();
|
||||
shell.setGraphic(new FontIcon("mdi2t-text-box-multiple"));
|
||||
shell.textProperty().bind(AppI18n.observable("addShell"));
|
||||
shell.setOnAction(event -> {
|
||||
GuiDsStoreCreator.showCreation(null,
|
||||
v -> DataStoreProvider.CreationCategory.SHELL.equals(v.getCreationCategory()));
|
||||
event.consume();
|
||||
});
|
||||
menu.getItems().add(shell);
|
||||
}
|
||||
{
|
||||
var cmd = new MenuItem();
|
||||
cmd.setGraphic(new FontIcon("mdi2c-code-greater-than"));
|
||||
cmd.textProperty().bind(AppI18n.observable("addCommand"));
|
||||
cmd.setOnAction(event -> {
|
||||
GuiDsStoreCreator.showCreation(DataStoreProviders.byName("cmd").orElseThrow(),
|
||||
v -> DataStoreProvider.CreationCategory.COMMAND.equals(v.getCreationCategory()));
|
||||
event.consume();
|
||||
});
|
||||
menu.getItems().add(cmd);
|
||||
}
|
||||
{
|
||||
var db = new MenuItem();
|
||||
db.setGraphic(new FontIcon("mdi2d-database-plus"));
|
||||
db.textProperty().bind(AppI18n.observable("addDatabase"));
|
||||
db.setOnAction(event -> {
|
||||
GuiDsStoreCreator.showCreation(null,
|
||||
v -> DataStoreProvider.CreationCategory.DATABASE.equals(v.getCreationCategory()));
|
||||
event.consume();
|
||||
});
|
||||
menu.getItems().add(db);
|
||||
}
|
||||
{
|
||||
var tunnel = new MenuItem();
|
||||
tunnel.setGraphic(new FontIcon("mdi2v-vector-polyline-plus"));
|
||||
tunnel.textProperty().bind(AppI18n.observable("addTunnel"));
|
||||
tunnel.setOnAction(event -> {
|
||||
GuiDsStoreCreator.showCreation(null,
|
||||
v -> DataStoreProvider.CreationCategory.TUNNEL.equals(v.getCreationCategory()));
|
||||
event.consume();
|
||||
});
|
||||
menu.getItems().add(tunnel);
|
||||
}
|
||||
{
|
||||
var script = new MenuItem();
|
||||
script.setGraphic(new FontIcon("mdi2s-script-text-outline"));
|
||||
script.textProperty().bind(AppI18n.observable("addScript"));
|
||||
script.setOnAction(event -> {
|
||||
GuiDsStoreCreator.showCreation(DataStoreProviders.byName("script").orElseThrow(),
|
||||
v -> DataStoreProvider.CreationCategory.SCRIPT.equals(v.getCreationCategory()));
|
||||
event.consume();
|
||||
});
|
||||
menu.getItems().add(script);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
: wrapper.getEntry()
|
||||
.getProvider()
|
||||
.getDisplayIconFileName(wrapper.getEntry().getStore());
|
||||
var imageComp = PrettyImageHelper.ofFixedSize(img, w, h);
|
||||
var imageComp = PrettyImageHelper.ofFixed(img, w, h);
|
||||
var storeIcon = imageComp.createRegion();
|
||||
if (wrapper.getValidity().getValue().isUsable()) {
|
||||
new FancyTooltipAugment<>(new SimpleStringProperty(
|
||||
|
|
|
@ -124,10 +124,6 @@ public class AppTheme {
|
|||
}
|
||||
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
if (AppMainWindow.getInstance() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var window = AppMainWindow.getInstance().getStage();
|
||||
var scene = window.getScene();
|
||||
Pane root = (Pane) scene.getRoot();
|
||||
|
|
|
@ -269,11 +269,6 @@ public class AppWindowHelper {
|
|||
changed = true;
|
||||
}
|
||||
|
||||
// This should not happen but on weird Linux systems nothing is impossible
|
||||
if (w < 0 || h < 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return changed ? Optional.of(new Rectangle2D(x, y, w, h)) : Optional.empty();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.app.ext;
|
|||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
|
@ -16,6 +17,7 @@ public class DataStoreProviders {
|
|||
if (ALL == null) {
|
||||
ALL = ServiceLoader.load(layer, DataStoreProvider.class).stream()
|
||||
.map(ServiceLoader.Provider::get)
|
||||
.sorted(Comparator.comparing(DataStoreProvider::getId))
|
||||
.collect(Collectors.toList());
|
||||
ALL.removeIf(p -> {
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package io.xpipe.app.fxcomps.impl;
|
||||
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.util.CustomComboBoxBuilder;
|
||||
import io.xpipe.core.charsetter.StreamCharset;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
public class CharsetChoiceComp extends SimpleComp {
|
||||
|
||||
private final Property<StreamCharset> charset;
|
||||
|
||||
public CharsetChoiceComp(Property<StreamCharset> charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var builder = new CustomComboBoxBuilder<>(
|
||||
charset,
|
||||
streamCharset -> {
|
||||
return new Label(streamCharset.getCharset().displayName()
|
||||
+ (streamCharset.hasByteOrderMark() ? " (BOM)" : ""));
|
||||
},
|
||||
new Label(AppI18n.get("app.none")),
|
||||
null);
|
||||
builder.setAccessibleNames(streamCharset -> streamCharset.getNames().get(0));
|
||||
builder.addFilter((charset, filter) -> {
|
||||
return charset.getCharset().displayName().contains(filter);
|
||||
});
|
||||
builder.addHeader(AppI18n.get("app.common"));
|
||||
for (var e : StreamCharset.COMMON) {
|
||||
builder.add(e);
|
||||
}
|
||||
|
||||
builder.addHeader(AppI18n.get("app.other"));
|
||||
for (var e : StreamCharset.RARE) {
|
||||
builder.add(e);
|
||||
}
|
||||
var comboBox = builder.build();
|
||||
comboBox.setVisibleRowCount(16);
|
||||
return comboBox;
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import io.xpipe.app.storage.DataStoreEntryRef;
|
|||
import io.xpipe.app.util.DataStoreCategoryChoiceComp;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
@ -105,7 +106,18 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
|||
selectedCategory).styleClass(Styles.LEFT_PILL);
|
||||
var filter = new FilterComp(filterText)
|
||||
.styleClass(Styles.CENTER_PILL)
|
||||
.hgrow();
|
||||
.hgrow()
|
||||
.apply(struc -> {
|
||||
popover.setOnShowing(event -> {
|
||||
Platform.runLater(() -> {
|
||||
Platform.runLater(() -> {
|
||||
Platform.runLater(() -> {
|
||||
struc.getText().requestFocus();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var addButton = Comp.of(() -> {
|
||||
MenuButton m = new MenuButton(null, new FontIcon("mdi2p-plus-box-outline"));
|
||||
|
@ -120,16 +132,7 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
|||
var top = new HorizontalComp(List.of(category, filter.hgrow(), addButton))
|
||||
.styleClass("top")
|
||||
.apply(struc -> struc.get().setFillHeight(true))
|
||||
.apply(struc -> {
|
||||
// Ugly solution to focus the text field
|
||||
// Somehow this does not work through the normal on shown listeners
|
||||
struc.get().getChildren().get(0).focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
((StackPane) struc.get().getChildren().get(1)).getChildren().get(1).requestFocus();
|
||||
}
|
||||
});
|
||||
})
|
||||
.createStructure().get();
|
||||
.createRegion();
|
||||
var r = section.vgrow().createRegion();
|
||||
var content = new VBox(top, r);
|
||||
content.setFillWidth(true);
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
|||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
@ -17,12 +18,14 @@ import org.kordamp.ikonli.javafx.FontIcon;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public class ContextualFileReferenceChoiceComp extends SimpleComp {
|
||||
public class FileReferenceChoiceComp extends SimpleComp {
|
||||
|
||||
private final boolean hideFileSystem;
|
||||
private final Property<DataStoreEntryRef<? extends FileSystemStore>> fileSystem;
|
||||
private final Property<String> filePath;
|
||||
|
||||
public <T extends FileSystemStore> ContextualFileReferenceChoiceComp(ObservableValue<DataStoreEntryRef<T>> fileSystem, Property<String> filePath) {
|
||||
public <T extends FileSystemStore> FileReferenceChoiceComp(ObservableValue<DataStoreEntryRef<T>> fileSystem, Property<String> filePath) {
|
||||
this.hideFileSystem = true;
|
||||
this.fileSystem = new SimpleObjectProperty<>();
|
||||
SimpleChangeListener.apply(fileSystem, val -> {
|
||||
this.fileSystem.setValue(val);
|
||||
|
@ -30,15 +33,27 @@ public class ContextualFileReferenceChoiceComp extends SimpleComp {
|
|||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public FileReferenceChoiceComp(boolean hideFileSystem, Property<DataStoreEntryRef<? extends FileSystemStore>> fileSystem, Property<String> filePath) {
|
||||
this.hideFileSystem = hideFileSystem;
|
||||
this.fileSystem = fileSystem != null ? fileSystem : new SimpleObjectProperty<>();
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var fileSystemChoiceComp =
|
||||
new FileSystemStoreChoiceComp(fileSystem).grow(false, true).styleClass(Styles.LEFT_PILL);
|
||||
if (hideFileSystem) {
|
||||
fileSystemChoiceComp.hide(new SimpleBooleanProperty(true));
|
||||
}
|
||||
|
||||
var fileNameComp = new TextFieldComp(filePath)
|
||||
.apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS))
|
||||
.styleClass(Styles.LEFT_PILL)
|
||||
.styleClass(hideFileSystem ? Styles.LEFT_PILL : Styles.CENTER_PILL)
|
||||
.grow(false, true);
|
||||
|
||||
var fileBrowseButton = new ButtonComp(null, new FontIcon("mdi2f-folder-open-outline"), () -> {
|
||||
StandaloneFileBrowser.openSingleFile(() -> fileSystem.getValue(), fileStore -> {
|
||||
StandaloneFileBrowser.openSingleFile(() -> hideFileSystem ? fileSystem.getValue() : null, fileStore -> {
|
||||
if (fileStore == null) {
|
||||
filePath.setValue(null);
|
||||
fileSystem.setValue(null);
|
||||
|
@ -51,7 +66,7 @@ public class ContextualFileReferenceChoiceComp extends SimpleComp {
|
|||
.styleClass(Styles.RIGHT_PILL)
|
||||
.grow(false, true);
|
||||
|
||||
var layout = new HorizontalComp(List.of(fileNameComp, fileBrowseButton))
|
||||
var layout = new HorizontalComp(List.of(fileSystemChoiceComp, fileNameComp, fileBrowseButton))
|
||||
.apply(struc -> struc.get().setFillHeight(true));
|
||||
|
||||
layout.apply(struc -> {
|
|
@ -0,0 +1,53 @@
|
|||
package io.xpipe.app.fxcomps.impl;
|
||||
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.CustomComboBoxBuilder;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
public class FileSystemStoreChoiceComp extends SimpleComp {
|
||||
|
||||
private final Property<DataStoreEntryRef<? extends FileSystemStore>> selected;
|
||||
|
||||
public FileSystemStoreChoiceComp(Property<DataStoreEntryRef<? extends FileSystemStore>> selected) {
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
private static String getName(DataStoreEntryRef<? extends FileSystemStore> store) {
|
||||
return store.get().getName();
|
||||
}
|
||||
|
||||
private Region createGraphic(DataStoreEntryRef<? extends FileSystemStore> s) {
|
||||
var provider = s.get().getProvider();
|
||||
var img = PrettyImageHelper.ofFixedSquare(provider.getDisplayIconFileName(s.getStore()), 16);
|
||||
return new Label(getName(s), img.createRegion());
|
||||
}
|
||||
|
||||
private Region createDisplayGraphic(DataStoreEntryRef<? extends FileSystemStore> s) {
|
||||
var provider = s.get().getProvider();
|
||||
var img = PrettyImageHelper.ofFixedSquare(provider.getDisplayIconFileName(s.getStore()), 16);
|
||||
return new Label(null, img.createRegion());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var comboBox = new CustomComboBoxBuilder<>(selected, this::createGraphic, null, v -> true);
|
||||
comboBox.setAccessibleNames(FileSystemStoreChoiceComp::getName);
|
||||
comboBox.setSelectedDisplay(this::createDisplayGraphic);
|
||||
DataStorage.get().getUsableEntries().stream()
|
||||
.filter(e -> e.getStore() instanceof FileSystemStore)
|
||||
.map(DataStoreEntry::<FileSystemStore>ref)
|
||||
.forEach(comboBox::add);
|
||||
ComboBox<Node> cb = comboBox.build();
|
||||
cb.getStyleClass().add("choice-comp");
|
||||
cb.setMaxWidth(45);
|
||||
return cb;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ import io.xpipe.app.core.AppImages;
|
|||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -79,7 +78,6 @@ public class PrettyImageComp extends SimpleComp {
|
|||
} else if (AppImages.hasNormalImage(image.getValue().replace("-dark", ""))) {
|
||||
return AppImages.image(image.getValue().replace("-dark", ""));
|
||||
} else {
|
||||
TrackEvent.withWarn("Image file not found").tag("file",image.getValue()).handle();
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -6,49 +6,37 @@ import io.xpipe.core.store.FileNames;
|
|||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class PrettyImageHelper {
|
||||
|
||||
public static Optional<Comp<?>> rasterizedIfExists(String img, int width, int height) {
|
||||
public static Comp<?> ofFixedSquare(String img, int size) {
|
||||
if (img != null && img.endsWith(".svg")) {
|
||||
var base = FileNames.getBaseName(img);
|
||||
var renderedName = base + "-" + height + ".png";
|
||||
if (AppImages.hasNormalImage(base + "-" + height + ".png")) {
|
||||
return Optional.of(new PrettyImageComp(new SimpleStringProperty(renderedName), width, height));
|
||||
var renderedName = base + "-" + size + ".png";
|
||||
if (AppImages.hasNormalImage(base + "-" + size + ".png")) {
|
||||
return new PrettyImageComp(new SimpleStringProperty(renderedName), size, size);
|
||||
} else {
|
||||
return new PrettySvgComp(new SimpleStringProperty(img), size, size);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
return new PrettyImageComp(new SimpleStringProperty(img), size, size);
|
||||
}
|
||||
|
||||
public static Comp<?> ofFixedSquare(String img, int size) {
|
||||
return ofFixedSize(img, size, size);
|
||||
}
|
||||
|
||||
public static Comp<?> ofFixedSize(String img, int w, int h) {
|
||||
if (img == null) {
|
||||
return new PrettyImageComp(new SimpleStringProperty(null), w, h);
|
||||
public static Comp<?> ofFixed(String img, int w, int h) {
|
||||
if (w == h) {
|
||||
return ofFixedSquare(img, w);
|
||||
}
|
||||
|
||||
var rasterized = rasterizedIfExists(img, w, h);
|
||||
if (rasterized.isPresent()) {
|
||||
return rasterized.get();
|
||||
} else {
|
||||
return img.endsWith(".svg") ? new PrettySvgComp(new SimpleStringProperty(img), w, h) : new PrettyImageComp(new SimpleStringProperty(img), w, h);
|
||||
}
|
||||
return img.endsWith(".svg") ? new PrettySvgComp(new SimpleStringProperty(img), w, h) : new PrettyImageComp(new SimpleStringProperty(img), w, h);
|
||||
}
|
||||
|
||||
|
||||
public static Comp<?> ofSvg(ObservableValue<String> img, int w, int h) {
|
||||
return new PrettySvgComp(img, w, h);
|
||||
}
|
||||
|
||||
public static Comp<?> ofRasterized(ObservableValue<String> img, int w, int h) {
|
||||
return new PrettyImageComp(img, w, h);
|
||||
}
|
||||
|
||||
public static Comp<?> ofFixedSmallSquare(String img) {
|
||||
return ofFixedSize(img, 16, 16);
|
||||
return ofFixed(img, 16, 16);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package io.xpipe.app.fxcomps.impl;
|
||||
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -9,24 +8,11 @@ import javafx.beans.property.Property;
|
|||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.scene.control.TextArea;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class TextAreaComp extends Comp<TextAreaComp.Structure> {
|
||||
|
||||
@Value
|
||||
@Builder
|
||||
public static class Structure implements CompStructure<AnchorPane> {
|
||||
AnchorPane pane;
|
||||
TextArea textArea;
|
||||
|
||||
@Override
|
||||
public AnchorPane get() {
|
||||
return pane;
|
||||
}
|
||||
}
|
||||
public class TextAreaComp extends SimpleComp {
|
||||
|
||||
private final Property<String> currentValue;
|
||||
private final Property<String> lastAppliedValue;
|
||||
|
@ -46,7 +32,7 @@ public class TextAreaComp extends Comp<TextAreaComp.Structure> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Structure createBase() {
|
||||
protected Region createSimple() {
|
||||
var text = new TextArea(currentValue.getValue() != null ? currentValue.getValue() : null);
|
||||
text.setPrefRowCount(5);
|
||||
text.textProperty().addListener((c, o, n) -> {
|
||||
|
@ -71,12 +57,6 @@ public class TextAreaComp extends Comp<TextAreaComp.Structure> {
|
|||
}
|
||||
});
|
||||
|
||||
var anchorPane = new AnchorPane(text);
|
||||
AnchorPane.setBottomAnchor(text, 0.0);
|
||||
AnchorPane.setTopAnchor(text, 0.0);
|
||||
AnchorPane.setLeftAnchor(text, 0.0);
|
||||
AnchorPane.setRightAnchor(text, 0.0);
|
||||
|
||||
if (lazy) {
|
||||
var isEqual = Bindings.createBooleanBinding(
|
||||
() -> Objects.equals(lastAppliedValue.getValue(), currentValue.getValue()),
|
||||
|
@ -85,14 +65,16 @@ public class TextAreaComp extends Comp<TextAreaComp.Structure> {
|
|||
var button = new IconButtonComp("mdi2c-checkbox-marked-outline")
|
||||
.hide(isEqual)
|
||||
.createRegion();
|
||||
anchorPane.getChildren().add(button);
|
||||
var anchorPane = new AnchorPane(text, button);
|
||||
AnchorPane.setBottomAnchor(button, 10.0);
|
||||
AnchorPane.setRightAnchor(button, 10.0);
|
||||
|
||||
text.prefWidthProperty().bind(anchorPane.widthProperty());
|
||||
text.prefHeightProperty().bind(anchorPane.heightProperty());
|
||||
|
||||
return anchorPane;
|
||||
}
|
||||
|
||||
return new Structure(anchorPane, text);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -304,10 +304,8 @@ public class BindingsHelper {
|
|||
if (target.size() - 1 == newList.size() && targetSet.containsAll(newSet)) {
|
||||
var l = new HashSet<>(targetSet);
|
||||
l.removeAll(newSet);
|
||||
if (l.size() > 0) {
|
||||
target.remove(l.iterator().next());
|
||||
return;
|
||||
}
|
||||
target.remove(l.iterator().next());
|
||||
return;
|
||||
}
|
||||
|
||||
// Other cases are more difficult
|
||||
|
|
|
@ -180,7 +180,7 @@ public class SentryErrorHandler implements ErrorHandler {
|
|||
s.setTag("terminal", Boolean.toString(ee.isTerminal()));
|
||||
s.setTag("omitted", Boolean.toString(ee.isOmitted()));
|
||||
s.setTag("diagnostics", Boolean.toString(ee.isShouldSendDiagnostics()));
|
||||
s.setTag("logs", Boolean.toString(ee.isShouldSendDiagnostics() && !ee.getAttachments().isEmpty()));
|
||||
s.setTag("logs", Boolean.toString(!ee.getAttachments().isEmpty()));
|
||||
|
||||
var exMessage = ee.getThrowable() != null ? ee.getThrowable().getMessage() : null;
|
||||
if (ee.getDescription() != null && !ee.getDescription().equals(exMessage) && ee.isShouldSendDiagnostics()) {
|
||||
|
|
|
@ -9,7 +9,6 @@ import lombok.NonNull;
|
|||
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
|
@ -37,24 +36,6 @@ public class ContextualFileReference {
|
|||
}
|
||||
}
|
||||
|
||||
public static Optional<ContextualFileReference> parseIfInDataDirectory(String s) {
|
||||
var cf = of(s);
|
||||
if (cf.serialize().contains("<DATA>")) {
|
||||
return Optional.of(cf);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<String> resolveIfInDataDirectory(ShellControl shellControl, String s) {
|
||||
if (s.contains("<DATA>")) {
|
||||
var cf = of(s);
|
||||
return Optional.of(cf.toFilePath(shellControl));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static ContextualFileReference of(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
|
|
266
app/src/main/java/io/xpipe/app/util/CustomComboBoxBuilder.java
Normal file
|
@ -0,0 +1,266 @@
|
|||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.impl.FilterComp;
|
||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.Separator;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class CustomComboBoxBuilder<T> {
|
||||
|
||||
private final Property<T> selected;
|
||||
private final Function<T, Node> nodeFunction;
|
||||
private ObservableValue<String> emptyAccessibilityName = AppI18n.observable("none");
|
||||
private Function<T, String> accessibleNameFunction;
|
||||
private Function<T, Node> selectedDisplayNodeFunction;
|
||||
private final Map<Node, T> nodeMap = new HashMap<>();
|
||||
private final Map<Node, Runnable> actionsMap = new HashMap<>();
|
||||
private final List<Node> nodes = new ArrayList<>();
|
||||
private final Set<Node> disabledNodes = new HashSet<>();
|
||||
private final Node emptyNode;
|
||||
private final Predicate<T> veto;
|
||||
private final Property<String> filterString = new SimpleStringProperty();
|
||||
private final List<T> filterable = new ArrayList<>();
|
||||
private BiPredicate<T, String> filterPredicate;
|
||||
private Node filterNode;
|
||||
private Function<T, Node> unknownNode;
|
||||
|
||||
public CustomComboBoxBuilder(
|
||||
Property<T> selected, Function<T, Node> nodeFunction, Node emptyNode, Predicate<T> veto) {
|
||||
this.selected = selected;
|
||||
this.nodeFunction = nodeFunction;
|
||||
this.selectedDisplayNodeFunction = nodeFunction;
|
||||
this.emptyNode = emptyNode;
|
||||
this.veto = veto;
|
||||
}
|
||||
|
||||
public void setSelectedDisplay(Function<T, Node> nodeFunction) {
|
||||
selectedDisplayNodeFunction = nodeFunction;
|
||||
}
|
||||
|
||||
public void setAccessibleNames(Function<T, String> function) {
|
||||
accessibleNameFunction = function;
|
||||
}
|
||||
|
||||
public void setEmptyAccessibilityName(ObservableValue<String> n) {
|
||||
emptyAccessibilityName = n;
|
||||
}
|
||||
|
||||
public void addAction(Node node, Runnable run) {
|
||||
nodes.add(node);
|
||||
actionsMap.put(node, run);
|
||||
}
|
||||
|
||||
public void disable(Node node) {
|
||||
disabledNodes.add(node);
|
||||
}
|
||||
|
||||
public void setUnknownNode(Function<T, Node> node) {
|
||||
unknownNode = node;
|
||||
}
|
||||
|
||||
public Node add(T val) {
|
||||
var node = nodeFunction.apply(val);
|
||||
nodeMap.put(node, val);
|
||||
nodes.add(node);
|
||||
if (filterPredicate != null) {
|
||||
filterable.add(val);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public void addSeparator() {
|
||||
var sep = new Separator(Orientation.HORIZONTAL);
|
||||
nodes.add(sep);
|
||||
disabledNodes.add(sep);
|
||||
}
|
||||
|
||||
public void addHeader(String name) {
|
||||
var spacer = new Region();
|
||||
spacer.setPrefHeight(10);
|
||||
var header = new Label(name);
|
||||
header.setAlignment(Pos.CENTER);
|
||||
var v = new VBox(spacer, header, new Separator(Orientation.HORIZONTAL));
|
||||
v.setAccessibleText(name);
|
||||
v.setAlignment(Pos.CENTER);
|
||||
nodes.add(v);
|
||||
disabledNodes.add(v);
|
||||
}
|
||||
|
||||
public void addFilter(BiPredicate<T, String> filterPredicate) {
|
||||
this.filterPredicate = filterPredicate;
|
||||
|
||||
var spacer = new Region();
|
||||
spacer.setPrefHeight(10);
|
||||
var header = new FilterComp(filterString).createStructure();
|
||||
var v = new VBox(header.get());
|
||||
v.setAlignment(Pos.CENTER);
|
||||
nodes.add(v);
|
||||
filterNode = header.getText();
|
||||
}
|
||||
|
||||
public ComboBox<Node> build() {
|
||||
var cb = new ComboBox<Node>();
|
||||
cb.getItems().addAll(nodes);
|
||||
|
||||
cb.setCellFactory((lv) -> {
|
||||
return new Cell();
|
||||
});
|
||||
cb.setButtonCell(new SelectedCell());
|
||||
SimpleChangeListener.apply(selected, c -> {
|
||||
var item = nodeMap.entrySet().stream()
|
||||
.filter(e -> Objects.equals(c, e.getValue()))
|
||||
.map(e -> e.getKey())
|
||||
.findAny()
|
||||
.orElse(c == null || unknownNode == null ? emptyNode : unknownNode.apply(c));
|
||||
cb.setValue(item);
|
||||
});
|
||||
cb.valueProperty().addListener((c, o, n) -> {
|
||||
if (nodeMap.containsKey(n)) {
|
||||
if (veto != null && !veto.test(nodeMap.get(n))) {
|
||||
return;
|
||||
}
|
||||
selected.setValue(nodeMap.get(n));
|
||||
}
|
||||
|
||||
if (actionsMap.containsKey(n)) {
|
||||
cb.setValue(o);
|
||||
actionsMap.get(n).run();
|
||||
}
|
||||
});
|
||||
|
||||
if (filterPredicate != null) {
|
||||
SimpleChangeListener.apply(filterString, c -> {
|
||||
var filteredNodes = nodes.stream()
|
||||
.filter(e -> e.equals(cb.getValue())
|
||||
|| !(nodeMap.get(e) != null
|
||||
&& (filterable.contains(nodeMap.get(e))
|
||||
&& filterString.getValue() != null
|
||||
&& !filterPredicate.test(nodeMap.get(e), c))))
|
||||
.toList();
|
||||
cb.setItems(FXCollections.observableList(filteredNodes));
|
||||
});
|
||||
|
||||
filterNode.sceneProperty().addListener((c, o, n) -> {
|
||||
if (n != null) {
|
||||
n.getWindow().focusedProperty().addListener((c2, o2, n2) -> {
|
||||
Platform.runLater(() -> {
|
||||
filterNode.requestFocus();
|
||||
});
|
||||
});
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
filterNode.requestFocus();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (emptyNode != null) {
|
||||
emptyNode.setAccessibleText(emptyAccessibilityName.getValue());
|
||||
}
|
||||
if (accessibleNameFunction != null) {
|
||||
nodes.forEach(node -> node.setAccessibleText(accessibleNameFunction.apply(nodeMap.get(node))));
|
||||
}
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
private class SelectedCell extends ListCell<Node> {
|
||||
|
||||
@Override
|
||||
protected void updateItem(Node item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
accessibleTextProperty().unbind();
|
||||
if (empty || item.equals(emptyNode)) {
|
||||
if (emptyAccessibilityName != null) {
|
||||
accessibleTextProperty().bind(emptyAccessibilityName);
|
||||
} else {
|
||||
setAccessibleText(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.equals(emptyNode)) {
|
||||
setGraphic(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// Case for dynamically created unknown nodes
|
||||
if (!nodeMap.containsKey(item)) {
|
||||
setGraphic(item);
|
||||
// Don't expect the accessible name function to properly map this item
|
||||
setAccessibleText(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var val = nodeMap.get(item);
|
||||
var newNode = selectedDisplayNodeFunction.apply(val);
|
||||
setGraphic(newNode);
|
||||
setAccessibleText(newNode.getAccessibleText());
|
||||
}
|
||||
}
|
||||
|
||||
private class Cell extends ListCell<Node> {
|
||||
|
||||
public Cell() {
|
||||
addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
|
||||
if (!nodeMap.containsKey(getItem())) {
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||
if (event.getCode() == KeyCode.ENTER && !nodeMap.containsKey(getItem())) {
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(Node item, boolean empty) {
|
||||
setGraphic(item);
|
||||
if (getItem() == item) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.updateItem(item, empty);
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setGraphic(item);
|
||||
if (disabledNodes.contains(item)) {
|
||||
this.setDisable(true);
|
||||
this.setFocusTraversable(false);
|
||||
// this.setPadding(Insets.EMPTY);
|
||||
} else {
|
||||
this.setDisable(false);
|
||||
this.setFocusTraversable(true);
|
||||
setAccessibleText(item.getAccessibleText());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 872 B |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 708 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 750 B |
Before Width: | Height: | Size: 780 B After Width: | Height: | Size: 780 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1,014 B |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 623 B |
Before Width: | Height: | Size: 575 B |
Before Width: | Height: | Size: 915 B |
Before Width: | Height: | Size: 958 B |
Before Width: | Height: | Size: 925 B |
Before Width: | Height: | Size: 911 B After Width: | Height: | Size: 911 B |
Before Width: | Height: | Size: 749 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 730 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 941 B |
Before Width: | Height: | Size: 466 B |
Before Width: | Height: | Size: 434 B |
Before Width: | Height: | Size: 238 B |
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 210 B |
Before Width: | Height: | Size: 801 B |
Before Width: | Height: | Size: 985 B After Width: | Height: | Size: 985 B |
Before Width: | Height: | Size: 715 B |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 817 B After Width: | Height: | Size: 817 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 686 B |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 594 B |
Before Width: | Height: | Size: 696 B After Width: | Height: | Size: 696 B |
Before Width: | Height: | Size: 683 B |
Before Width: | Height: | Size: 360 B After Width: | Height: | Size: 360 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1,023 B |
Before Width: | Height: | Size: 963 B After Width: | Height: | Size: 963 B |
Before Width: | Height: | Size: 202 B |
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 662 B |
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
|
@ -242,10 +242,6 @@
|
|||
-fx-background-color: -color-success-subtle;
|
||||
}
|
||||
|
||||
.root.nord .browser .table-row-cell:selected, .root.nord .browser .table-row-cell:hover:selected {
|
||||
-fx-background-color: -color-success-7;
|
||||
}
|
||||
|
||||
.browser .table-row-cell:folder:drag-over {
|
||||
-fx-background-color: -color-success-muted;
|
||||
}
|
||||
|
|
|
@ -141,14 +141,14 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO
|
|||
try (CommandControl c = pc.command("lsb_release -a").start()) {
|
||||
var text = c.readStdoutDiscardErr();
|
||||
if (c.getExitCode() == 0) {
|
||||
return PropertiesFormatsParser.parse(text, ":").getOrDefault("Description", "Unknown");
|
||||
return PropertiesFormatsParser.parse(text, ":").getOrDefault("Description", null);
|
||||
}
|
||||
}
|
||||
|
||||
try (CommandControl c = pc.command("cat /etc/*release").start()) {
|
||||
var text = c.readStdoutDiscardErr();
|
||||
if (c.getExitCode() == 0) {
|
||||
return PropertiesFormatsParser.parse(text, "=").getOrDefault("PRETTY_NAME", "Unknown");
|
||||
return PropertiesFormatsParser.parse(text, "=").getOrDefault("PRETTY_NAME", null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ public interface ProcessControl extends AutoCloseable {
|
|||
|
||||
ProcessControl withExceptionConverter(ExceptionConverter converter);
|
||||
|
||||
void resetData(boolean cache);
|
||||
void resetData();
|
||||
|
||||
String prepareTerminalOpen(TerminalInitScriptConfig config) throws Exception;
|
||||
|
||||
|
@ -33,7 +33,7 @@ public interface ProcessControl extends AutoCloseable {
|
|||
@Override
|
||||
void close() throws Exception;
|
||||
|
||||
void kill();
|
||||
void kill() throws Exception;
|
||||
|
||||
ProcessControl start() throws Exception;
|
||||
|
||||
|
|
|
@ -16,8 +16,6 @@ import java.util.function.Function;
|
|||
|
||||
public interface ShellControl extends ProcessControl {
|
||||
|
||||
List<UUID> getExitUuids();
|
||||
|
||||
Optional<ShellStore> getSourceStore();
|
||||
|
||||
ShellControl withSourceStore(ShellStore store);
|
||||
|
|
|
@ -22,9 +22,8 @@ public class ShellDialects {
|
|||
public static ShellDialect ZSH;
|
||||
public static ShellDialect CSH;
|
||||
public static ShellDialect FISH;
|
||||
|
||||
public static ShellDialect UNSUPPORTED;
|
||||
public static ShellDialect CISCO;
|
||||
public static ShellDialect RBASH;
|
||||
|
||||
public static List<ShellDialect> getStartableDialects() {
|
||||
return ALL.stream().filter(dialect -> dialect.getOpenCommand() != null).filter(dialect -> dialect != SH_BSD).toList();
|
||||
|
@ -51,8 +50,8 @@ public class ShellDialects {
|
|||
ASH = byId("ash");
|
||||
SH = byId("sh");
|
||||
SH_BSD = byId("shBsd");
|
||||
UNSUPPORTED = byId("unsupported");
|
||||
CISCO = byId("cisco");
|
||||
RBASH = byId("rbash");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
3
dist/changelogs/1.7.14.md
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
This is just a small hotfix update to fix a few breaking issues:
|
||||
- Fix performance regression in JavaFX by downgrading version temporarily
|
||||
- Fix .deb installers not being able to resolve some packages on Ubuntu < 22
|
|
@ -59,7 +59,7 @@ open module io.xpipe.ext.base {
|
|||
DeleteStoreChildrenAction,
|
||||
BrowseStoreAction;
|
||||
provides DataStoreProvider with
|
||||
SimpleScriptStoreProvider,
|
||||
ScriptGroupStoreProvider,
|
||||
SimpleScriptStoreProvider,
|
||||
InMemoryStoreProvider;
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 4 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.6 KiB |
2
version
|
@ -1 +1 @@
|
|||
1.7.13
|
||||
1.7.14
|