Reformat and fr translations

This commit is contained in:
crschnick 2024-04-10 17:23:56 +00:00
parent cd485525b3
commit ea0e34dab4
145 changed files with 2294 additions and 973 deletions

View file

@ -12,9 +12,11 @@ public class Main {
return;
}
// Since this is not marked as a console application, it will not print anything when you run it in a console on Windows
// Since this is not marked as a console application, it will not print anything when you run it in a console on
// Windows
if (args.length == 1 && args[0].equals("--help")) {
System.out.println("""
System.out.println(
"""
The daemon executable xpiped does not accept any command-line arguments.
For a reference on what you can do from the CLI, take a look at the xpipe CLI executable instead.

View file

@ -39,8 +39,7 @@ public final class BrowserBookmarkComp extends SimpleComp {
public BrowserBookmarkComp(
ObservableValue<DataStoreEntry> selected,
Predicate<StoreEntryWrapper> applicable,
BiConsumer<StoreEntryWrapper, BooleanProperty> action
) {
BiConsumer<StoreEntryWrapper, BooleanProperty> action) {
this.selected = selected;
this.applicable = applicable;
this.action = action;
@ -66,8 +65,8 @@ public final class BrowserBookmarkComp extends SimpleComp {
.pseudoClassStateChanged(
SELECTED,
newValue != null
&& newValue
.equals(s.getWrapper().getEntry()));
&& newValue.equals(
s.getWrapper().getEntry()));
});
});
});

View file

@ -40,7 +40,7 @@ public class BrowserBreadcrumbBar extends SimpleComp {
var breadcrumbs = new Breadcrumbs<String>();
breadcrumbs.setMinWidth(0);
PlatformThread.sync(model.getCurrentPath()).subscribe( val -> {
PlatformThread.sync(model.getCurrentPath()).subscribe(val -> {
if (val == null) {
breadcrumbs.setSelectedCrumb(null);
return;

View file

@ -4,8 +4,8 @@ import atlantafx.base.theme.Styles;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.impl.TooltipAugment;
import io.xpipe.app.fxcomps.impl.TextFieldComp;
import io.xpipe.app.fxcomps.impl.TooltipAugment;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Pos;

View file

@ -128,6 +128,10 @@ public class BrowserStatusBarComp extends SimpleComp {
});
// Use status bar as an extension of file list
new ContextMenuAugment<>(mouseEvent -> mouseEvent.getButton() == MouseButton.SECONDARY, null, () -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(r));
new ContextMenuAugment<>(
mouseEvent -> mouseEvent.getButton() == MouseButton.SECONDARY,
null,
() -> new BrowserContextMenu(model, null))
.augment(new SimpleCompStructure<>(r));
}
}

View file

@ -123,14 +123,15 @@ public class BrowserTransferComp extends SimpleComp {
return;
}
if (!(model.getBrowserSessionModel().getSelectedEntry().getValue() instanceof OpenFileSystemModel fileSystemModel)) {
if (!(model.getBrowserSessionModel()
.getSelectedEntry()
.getValue()
instanceof OpenFileSystemModel fileSystemModel)) {
return;
}
var files = drag.getEntries();
model.drop(
fileSystemModel,
files);
model.drop(fileSystemModel, files);
event.setDropCompleted(true);
event.consume();
}

View file

@ -78,15 +78,14 @@ public class BrowserWelcomeComp extends SimpleComp {
});
var empty = Bindings.createBooleanBinding(() -> list.isEmpty(), list);
var headerBinding = BindingsHelper.flatMap(empty,b -> {
var headerBinding = BindingsHelper.flatMap(empty, b -> {
if (b) {
return AppI18n.observable("browserWelcomeEmpty");
} else {
return AppI18n.observable("browserWelcomeSystems");
}
});
var header = new LabelComp(headerBinding)
.createRegion();
var header = new LabelComp(headerBinding).createRegion();
AppFont.setSize(header, 1);
vbox.getChildren().add(header);
@ -130,11 +129,13 @@ public class BrowserWelcomeComp extends SimpleComp {
private Comp<?> entryButton(BrowserSavedState.Entry e, BooleanProperty disable) {
var entry = DataStorage.get().getStoreEntryIfPresent(e.getUuid());
var graphic = entry.get()
.getProvider()
.getDisplayIconFileName(entry.get().getStore());
var graphic =
entry.get().getProvider().getDisplayIconFileName(entry.get().getStore());
var view = PrettyImageHelper.ofFixedSize(graphic, 30, 24);
return new ButtonComp(new SimpleStringProperty(DataStorage.get().getStoreDisplayName(entry.get())), view.createRegion(), () -> {
return new ButtonComp(
new SimpleStringProperty(DataStorage.get().getStoreDisplayName(entry.get())),
view.createRegion(),
() -> {
ThreadHelper.runAsync(() -> {
model.restoreStateAsync(e, disable);
});

View file

@ -92,6 +92,5 @@ public interface BrowserAction {
})
.toList());
}
}
}

View file

@ -60,8 +60,7 @@ public interface LeafAction extends BrowserAction {
return b;
}
default MenuItem toMenuItem(
OpenFileSystemModel model, List<BrowserEntry> selected) {
default MenuItem toMenuItem(OpenFileSystemModel model, List<BrowserEntry> selected) {
var name = getName(model, selected);
var mi = new MenuItem();
mi.textProperty().bind(name);

View file

@ -43,7 +43,9 @@ public abstract class MultiExecuteAction implements BranchAction {
@Override
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
var t = AppPrefs.get().terminalType().getValue();
return AppI18n.observable("executeInTerminal", t != null ? t.toTranslatedString().getValue() : "?");
return AppI18n.observable(
"executeInTerminal",
t != null ? t.toTranslatedString().getValue() : "?");
}
@Override

View file

@ -1,6 +1,5 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.browser.file.BrowserFileListModel;
import io.xpipe.app.browser.icon.BrowserIconDirectoryType;
import io.xpipe.app.browser.icon.BrowserIconFileType;
import io.xpipe.core.store.FileKind;

View file

@ -507,13 +507,18 @@ public final class BrowserFileListComp extends SimpleComp {
.hide(Bindings.createBooleanBinding(
() -> {
var item = getTableRow().getItem();
var notDir = item.getRawFileEntry().resolved().getKind() != FileKind.DIRECTORY;
var isParentLink = item
.getRawFileEntry()
.equals(fileList.getFileSystemModel().getCurrentParentDirectory());
var notDir = item.getRawFileEntry()
.resolved()
.getKind()
!= FileKind.DIRECTORY;
var isParentLink = item.getRawFileEntry()
.equals(fileList.getFileSystemModel()
.getCurrentParentDirectory());
return notDir || isParentLink;
},
itemProperty()).not().not())
itemProperty())
.not()
.not())
.createRegion();
editing.addListener((observable, oldValue, newValue) -> {

View file

@ -32,8 +32,10 @@ public class BrowserFileOverviewComp extends SimpleComp {
Function<FileSystem.FileEntry, Comp<?>> factory = entry -> {
return Comp.of(() -> {
var icon = BrowserIcons.createIcon(entry);
var graphic = new HorizontalComp(List.of(icon,
new BrowserQuickAccessButtonComp(() -> new BrowserEntry(entry, model.getFileList(),false),model)));
var graphic = new HorizontalComp(List.of(
icon,
new BrowserQuickAccessButtonComp(
() -> new BrowserEntry(entry, model.getFileList(), false), model)));
var l = new Button(entry.getPath(), graphic.createRegion());
l.setGraphicTextGap(1);
l.setOnAction(event -> {

View file

@ -29,18 +29,105 @@ import java.util.stream.Collectors;
public class BrowserQuickAccessContextMenu extends ContextMenu {
private final Supplier<BrowserEntry> base;
private final OpenFileSystemModel model;
private ContextMenu shownBrowserActionsMenu;
private boolean expandBrowserActionMenuKey;
private boolean keyBasedNavigation;
private boolean closeBrowserActionMenuKey;
public BrowserQuickAccessContextMenu(Supplier<BrowserEntry> base, OpenFileSystemModel model) {
this.base = base;
this.model = model;
addEventFilter(Menu.ON_SHOWING, e -> {
Node content = getSkin().getNode();
if (content instanceof Region r) {
r.setMaxWidth(500);
}
});
addEventFilter(Menu.ON_SHOWN, e -> {
Platform.runLater(() -> {
getItems().getFirst().getStyleableNode().requestFocus();
});
});
InputHelper.onLeft(this, false, e -> {
hide();
e.consume();
});
setAutoHide(true);
getStyleClass().add("condensed");
}
public void showMenu(Node anchor) {
getItems().clear();
ThreadHelper.runFailableAsync(() -> {
var entry = base.get();
if (entry.getRawFileEntry().resolved().getKind() != FileKind.DIRECTORY) {
return;
}
var actionsMenu = new AtomicReference<ContextMenu>();
var r = new Menu();
var newItems = updateMenuItems(r, entry, true);
Platform.runLater(() -> {
getItems().addAll(r.getItems());
show(anchor, Side.RIGHT, 0, 0);
});
});
}
private MenuItem createItem(BrowserEntry browserEntry) {
return new QuickAccessMenu(browserEntry).getMenu();
}
private List<MenuItem> updateMenuItems(Menu m, BrowserEntry entry, boolean updateInstantly) throws Exception {
var newFiles = model.getFileSystem()
.listFiles(entry.getRawFileEntry().resolved().getPath());
try (var s = newFiles) {
var list = s.map(fileEntry -> fileEntry.resolved()).toList();
// Wait until all files are listed, i.e. do not skip the stream elements
list = list.subList(0, Math.min(list.size(), 150));
var newItems = new ArrayList<MenuItem>();
if (list.isEmpty()) {
var empty = new Menu("<empty>");
empty.getStyleClass().add("leaf");
newItems.add(empty);
} else {
var browserEntries = list.stream()
.map(fileEntry -> new BrowserEntry(fileEntry, model.getFileList(), false))
.toList();
var menus = browserEntries.stream()
.sorted(model.getFileList().order())
.collect(Collectors.toMap(e -> e, e -> createItem(e), (v1, v2) -> v2, LinkedHashMap::new));
var dirs = browserEntries.stream()
.filter(e -> e.getRawFileEntry().getKind() == FileKind.DIRECTORY)
.toList();
if (dirs.size() == 1) {
updateMenuItems((Menu) menus.get(dirs.getFirst()), dirs.getFirst(), true);
}
newItems.addAll(menus.values());
}
if (updateInstantly) {
m.getItems().setAll(newItems);
}
return newItems;
}
}
@Getter
class QuickAccessMenu {
private final BrowserEntry browserEntry;
private ContextMenu browserActionMenu;
private final Menu menu;
private ContextMenu browserActionMenu;
public QuickAccessMenu(BrowserEntry browserEntry) {
this.browserEntry = browserEntry;
this.menu = new Menu(
// Use original name, not the link target
browserEntry.getRawFileEntry().getName(),
PrettyImageHelper.ofFixedSizeSquare(FileIconManager.getFileIcon(browserEntry.getRawFileEntry(), false), 24)
PrettyImageHelper.ofFixedSizeSquare(
FileIconManager.getFileIcon(browserEntry.getRawFileEntry(), false), 24)
.createRegion());
createMenu();
addInputListeners();
@ -142,7 +229,8 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
});
new BooleanAnimationTimer(hover, 100, () -> {
expandDirectoryMenu(empty);
}).start();
})
.start();
}
private void addInputListeners() {
@ -155,13 +243,15 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
} else {
expandBrowserActionMenuKey = false;
}
if (event.getCode().equals(KeyCode.LEFT) && browserActionMenu != null && browserActionMenu.isShowing()) {
if (event.getCode().equals(KeyCode.LEFT)
&& browserActionMenu != null
&& browserActionMenu.isShowing()) {
closeBrowserActionMenuKey = true;
} else {
closeBrowserActionMenuKey = false;
}
});
contextMenu.addEventFilter(MouseEvent.ANY,event -> {
contextMenu.addEventFilter(MouseEvent.ANY, event -> {
keyBasedNavigation = false;
});
}
@ -217,102 +307,4 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
});
}
}
private final Supplier<BrowserEntry> base;
private final OpenFileSystemModel model;
private ContextMenu shownBrowserActionsMenu;
private boolean expandBrowserActionMenuKey;
private boolean keyBasedNavigation;
private boolean closeBrowserActionMenuKey;
public BrowserQuickAccessContextMenu(Supplier<BrowserEntry> base, OpenFileSystemModel model) {
this.base = base;
this.model = model;
addEventFilter(Menu.ON_SHOWING, e -> {
Node content = getSkin().getNode();
if (content instanceof Region r) {
r.setMaxWidth(500);
}
});
addEventFilter(Menu.ON_SHOWN, e -> {
Platform.runLater(() -> {
getItems().getFirst().getStyleableNode().requestFocus();
});
});
InputHelper.onLeft(this, false, e -> {
hide();
e.consume();
});
setAutoHide(true);
getStyleClass().add("condensed");
}
public void showMenu(Node anchor) {
getItems().clear();
ThreadHelper.runFailableAsync(() -> {
var entry = base.get();
if (entry.getRawFileEntry().resolved().getKind() != FileKind.DIRECTORY) {
return;
}
var actionsMenu = new AtomicReference<ContextMenu>();
var r = new Menu();
var newItems = updateMenuItems(r, entry, true);
Platform.runLater(() -> {
getItems().addAll(r.getItems());
show(anchor, Side.RIGHT, 0, 0);
});
});
}
private MenuItem createItem(BrowserEntry browserEntry) {
return new QuickAccessMenu(browserEntry).getMenu();
}
private List<MenuItem> updateMenuItems(
Menu m,
BrowserEntry entry,
boolean updateInstantly)
throws Exception {
var newFiles = model.getFileSystem().listFiles(entry.getRawFileEntry().resolved().getPath());
try (var s = newFiles) {
var list = s.map(fileEntry -> fileEntry.resolved()).toList();
// Wait until all files are listed, i.e. do not skip the stream elements
list = list.subList(0, Math.min(list.size(), 150));
var newItems = new ArrayList<MenuItem>();
if (list.isEmpty()) {
var empty = new Menu("<empty>");
empty.getStyleClass().add("leaf");
newItems.add(empty);
} else {
var browserEntries = list.stream()
.map(fileEntry -> new BrowserEntry(fileEntry, model.getFileList(), false))
.toList();
var menus = browserEntries.stream()
.sorted(model.getFileList().order())
.collect(Collectors.toMap(
e -> e,
e -> createItem(e),
(v1, v2) -> v2,
LinkedHashMap::new));
var dirs = browserEntries.stream()
.filter(e -> e.getRawFileEntry().getKind() == FileKind.DIRECTORY)
.toList();
if (dirs.size() == 1) {
updateMenuItems(
(Menu) menus.get(dirs.getFirst()),
dirs.getFirst(),
true);
}
newItems.addAll(menus.values());
}
if (updateInstantly) {
m.getItems().setAll(newItems);
}
return newItems;
}
}
}

View file

@ -296,8 +296,11 @@ public class FileSystemHelper {
AtomicLong transferred = new AtomicLong();
for (var e : flatFiles.entrySet()) {
var sourceFile = e.getKey();
var targetFile = target.getFileSystem().getShell().orElseThrow().getOsType().makeFileSystemCompatible(
FileNames.join(target.getPath(), e.getValue()));
var targetFile = target.getFileSystem()
.getShell()
.orElseThrow()
.getOsType()
.makeFileSystemCompatible(FileNames.join(target.getPath(), e.getValue()));
if (sourceFile.getFileSystem().equals(target.getFileSystem())) {
throw new IllegalStateException();
}

View file

@ -60,7 +60,9 @@ public class OpenFileSystemComp extends SimpleComp {
var menuButton = new MenuButton(null, new FontIcon("mdral-folder_open"));
new ContextMenuAugment<>(
event -> event.getButton() == MouseButton.PRIMARY, null, () -> new BrowserContextMenu(model, null))
event -> event.getButton() == MouseButton.PRIMARY,
null,
() -> new BrowserContextMenu(model, null))
.augment(new SimpleCompStructure<>(menuButton));
menuButton.disableProperty().bind(model.getInOverview());
menuButton.setAccessibleText("Directory options");

View file

@ -1,10 +1,10 @@
package io.xpipe.app.browser.fs;
import io.xpipe.app.browser.file.BrowserFileListModel;
import io.xpipe.app.browser.BrowserSavedState;
import io.xpipe.app.browser.BrowserTransferProgress;
import io.xpipe.app.browser.file.FileSystemHelper;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.browser.file.BrowserFileListModel;
import io.xpipe.app.browser.file.FileSystemHelper;
import io.xpipe.app.browser.session.BrowserAbstractSessionModel;
import io.xpipe.app.browser.session.BrowserSessionModel;
import io.xpipe.app.browser.session.BrowserSessionTab;
@ -49,7 +49,10 @@ public final class OpenFileSystemModel extends BrowserSessionTab<FileSystemStore
private OpenFileSystemSavedState savedState;
private OpenFileSystemCache cache;
public OpenFileSystemModel(BrowserAbstractSessionModel<?> model, DataStoreEntryRef<? extends FileSystemStore> entry, SelectionMode selectionMode) {
public OpenFileSystemModel(
BrowserAbstractSessionModel<?> model,
DataStoreEntryRef<? extends FileSystemStore> entry,
SelectionMode selectionMode) {
super(model, entry);
this.inOverview.bind(Bindings.createBooleanBinding(
() -> {
@ -103,7 +106,9 @@ public final class OpenFileSystemModel extends BrowserSessionTab<FileSystemStore
&& savedState != null
&& getCurrentPath().get() != null) {
if (getBrowserModel() instanceof BrowserSessionModel bm) {
bm.getSavedState().add(new BrowserSavedState.Entry(getEntry().get().getUuid(), getCurrentPath().get()));
bm.getSavedState()
.add(new BrowserSavedState.Entry(
getEntry().get().getUuid(), getCurrentPath().get()));
}
}
try {
@ -114,7 +119,6 @@ public final class OpenFileSystemModel extends BrowserSessionTab<FileSystemStore
fileSystem = null;
}
private void startIfNeeded() throws Exception {
if (fileSystem == null) {
return;

View file

@ -19,14 +19,14 @@ public abstract class BrowserIconDirectoryType {
private static final List<BrowserIconDirectoryType> ALL = new ArrayList<>();
public synchronized static BrowserIconDirectoryType byId(String id) {
public static synchronized BrowserIconDirectoryType byId(String id) {
return ALL.stream()
.filter(fileType -> fileType.getId().equals(id))
.findAny()
.orElseThrow();
}
public synchronized static void loadDefinitions() {
public static synchronized void loadDefinitions() {
ALL.add(new BrowserIconDirectoryType() {
@Override

View file

@ -16,14 +16,14 @@ public abstract class BrowserIconFileType {
private static final List<BrowserIconFileType> ALL = new ArrayList<>();
public synchronized static BrowserIconFileType byId(String id) {
public static synchronized BrowserIconFileType byId(String id) {
return ALL.stream()
.filter(fileType -> fileType.getId().equals(id))
.findAny()
.orElseThrow();
}
public synchronized static void loadDefinitions() {
public static synchronized void loadDefinitions() {
AppResources.with(AppResources.XPIPE_MODULE, "file_list.txt", path -> {
try (var reader =
new BufferedReader(new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8))) {

View file

@ -50,7 +50,8 @@ public class BrowserChooserComp extends SimpleComp {
var comp = new BrowserChooserComp(model)
.apply(struc -> struc.get().setPrefSize(1200, 700))
.apply(struc -> AppFont.normal(struc.get()));
var window = AppWindowHelper.sideWindow(AppI18n.get(save ? "saveFileTitle" : "openFileTitle"), stage -> comp, false, null);
var window = AppWindowHelper.sideWindow(
AppI18n.get(save ? "saveFileTitle" : "openFileTitle"), stage -> comp, false, null);
model.setOnFinish(fileStores -> {
file.accept(fileStores.size() > 0 ? fileStores.getFirst() : null);
window.close();
@ -81,7 +82,12 @@ public class BrowserChooserComp extends SimpleComp {
});
};
var bookmarksList = new BrowserBookmarkComp(BindingsHelper.map(model.getSelectedEntry(), v -> v.getEntry().get()), applicable, action).vgrow();
var bookmarksList = new BrowserBookmarkComp(
BindingsHelper.map(
model.getSelectedEntry(), v -> v.getEntry().get()),
applicable,
action)
.vgrow();
var stack = Comp.of(() -> {
var s = new StackPane();
model.getSelectedEntry().subscribe(selected -> {

View file

@ -103,5 +103,4 @@ public class BrowserChooserModel extends BrowserAbstractSessionModel<OpenFileSys
}
});
}
}

View file

@ -46,7 +46,12 @@ public class BrowserSessionComp extends SimpleComp {
});
};
var bookmarksList = new BrowserBookmarkComp(BindingsHelper.map(model.getSelectedEntry(), v -> v.getEntry().get()), applicable, action).vgrow();
var bookmarksList = new BrowserBookmarkComp(
BindingsHelper.map(
model.getSelectedEntry(), v -> v.getEntry().get()),
applicable,
action)
.vgrow();
var localDownloadStage = new BrowserTransferComp(model.getLocalTransfersStage())
.hide(PlatformThread.sync(Bindings.createBooleanBinding(
() -> {

View file

@ -28,7 +28,6 @@ public class BrowserSessionModel extends BrowserAbstractSessionModel<BrowserSess
private final BrowserTransferModel localTransfersStage = new BrowserTransferModel(this);
private final BrowserSavedState savedState;
public BrowserSessionModel(BrowserSavedState savedState) {
this.savedState = savedState;
}

View file

@ -38,11 +38,7 @@ public class BrowserSessionMultiTab extends BrowserSessionTab<DataStore> {
return true;
}
public void init() throws Exception {
public void init() throws Exception {}
}
public void close() {
}
public void close() {}
}

View file

@ -72,7 +72,8 @@ public class BrowserSessionTabsComp extends SimpleComp {
tabs.getTabs().add(t);
});
tabs.getSelectionModel()
.select(model.getSessionEntries().indexOf(model.getSelectedEntry().getValue()));
.select(model.getSessionEntries()
.indexOf(model.getSelectedEntry().getValue()));
// Used for ignoring changes by the tabpane when new tabs are added. We want to perform the selections manually!
var modifying = new SimpleBooleanProperty();

View file

@ -24,9 +24,14 @@ public class AppLayoutComp extends Comp<CompStructure<Pane>> {
@Override
public CompStructure<Pane> createBase() {
Map<Comp<?>, ObservableValue<Boolean>> map = model.getEntries().stream().collect(Collectors.toMap(entry -> entry.comp(), entry -> Bindings.createBooleanBinding(() -> {
Map<Comp<?>, ObservableValue<Boolean>> map = model.getEntries().stream()
.collect(Collectors.toMap(
entry -> entry.comp(),
entry -> Bindings.createBooleanBinding(
() -> {
return model.getSelected().getValue().equals(entry);
}, model.getSelected())));
},
model.getSelected())));
var multi = new MultiContentComp(map);
var pane = new BorderPane();

View file

@ -2,7 +2,6 @@ package io.xpipe.app.comp.base;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.PlatformThread;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
@ -12,19 +11,7 @@ import lombok.Value;
import org.kordamp.ikonli.javafx.FontIcon;
@AllArgsConstructor
public class FontIconComp extends Comp <FontIconComp.Structure>{
@Value
public static class Structure implements CompStructure<StackPane> {
FontIcon icon;
StackPane pane;
@Override
public StackPane get() {
return pane;
}
}
public class FontIconComp extends Comp<FontIconComp.Structure> {
private final ObservableValue<String> icon;
@ -45,4 +32,16 @@ public class FontIconComp extends Comp <FontIconComp.Structure>{
var pane = new StackPane(fi);
return new FontIconComp.Structure(fi, pane);
}
@Value
public static class Structure implements CompStructure<StackPane> {
FontIcon icon;
StackPane pane;
@Override
public StackPane get() {
return pane;
}
}
}

View file

@ -53,9 +53,8 @@ public class OsLogoComp extends SimpleComp {
wrapper.getPersistentState(),
state);
var hide = BindingsHelper.map(img, s -> s != null);
return new StackComp(List.of(
new SystemStateComp(state).hide(hide),
new PrettyImageComp(img, 24, 24).visible(hide)))
return new StackComp(
List.of(new SystemStateComp(state).hide(hide), new PrettyImageComp(img, 24, 24).visible(hide)))
.createRegion();
}

View file

@ -7,8 +7,8 @@ import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.augment.Augment;
import io.xpipe.app.fxcomps.impl.TooltipAugment;
import io.xpipe.app.fxcomps.impl.IconButtonComp;
import io.xpipe.app.fxcomps.impl.TooltipAugment;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.UserReportComp;

View file

@ -22,7 +22,7 @@ public class ToggleSwitchComp extends SimpleComp {
@Override
protected Region createSimple() {
var s = new ToggleSwitch();
s.addEventFilter(KeyEvent.KEY_PRESSED,event -> {
s.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.SPACE || event.getCode() == KeyCode.ENTER) {
s.setSelected(!s.isSelected());
event.consume();

View file

@ -95,7 +95,11 @@ public abstract class StoreEntryComp extends SimpleComp {
wrapper.executeDefaultAction();
});
});
new ContextMenuAugment<>(mouseEvent -> mouseEvent.getButton() == MouseButton.SECONDARY, null, () -> this.createContextMenu()).augment(button);
new ContextMenuAugment<>(
mouseEvent -> mouseEvent.getButton() == MouseButton.SECONDARY,
null,
() -> this.createContextMenu())
.augment(button);
var loading = LoadingOverlayComp.noProgress(
Comp.of(() -> button),
@ -244,7 +248,9 @@ public abstract class StoreEntryComp extends SimpleComp {
settingsButton.styleClass("settings");
settingsButton.accessibleText("More");
settingsButton.apply(new ContextMenuAugment<>(
event -> event.getButton() == MouseButton.PRIMARY, null, () -> StoreEntryComp.this.createContextMenu()));
event -> event.getButton() == MouseButton.PRIMARY,
null,
() -> StoreEntryComp.this.createContextMenu()));
settingsButton.apply(new TooltipAugment<>("more"));
return settingsButton;
}

View file

@ -5,9 +5,9 @@ import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.TooltipAugment;
import io.xpipe.app.fxcomps.impl.FilterComp;
import io.xpipe.app.fxcomps.impl.IconButtonComp;
import io.xpipe.app.fxcomps.impl.TooltipAugment;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.ListBindingsHelper;
import io.xpipe.app.util.ThreadHelper;
@ -51,8 +51,12 @@ public class StoreEntryListStatusComp extends SimpleComp {
private Region createGroupListHeader() {
var label = new Label();
var name = BindingsHelper.flatMap(StoreViewState.get().getActiveCategory(),
categoryWrapper -> AppI18n.observable(categoryWrapper.getRoot().equals(StoreViewState.get().getAllConnectionsCategory()) ? "connections" : "scripts"));
var name = BindingsHelper.flatMap(
StoreViewState.get().getActiveCategory(),
categoryWrapper -> AppI18n.observable(
categoryWrapper.getRoot().equals(StoreViewState.get().getAllConnectionsCategory())
? "connections"
: "scripts"));
label.textProperty().bind(name);
label.getStyleClass().add("name");

View file

@ -46,9 +46,7 @@ public class StoreIntroComp extends SimpleComp {
hbox.setSpacing(35);
hbox.setAlignment(Pos.CENTER);
var v = new VBox(
hbox, scanPane
);
var v = new VBox(hbox, scanPane);
v.setMinWidth(Region.USE_PREF_SIZE);
v.setMaxWidth(Region.USE_PREF_SIZE);
v.setMinHeight(Region.USE_PREF_SIZE);

View file

@ -42,7 +42,9 @@ public class StoreQuickAccessButtonComp extends Comp<CompStructure<Button>> {
var graphic =
w.getEntry().getProvider().getDisplayIconFileName(w.getEntry().getStore());
if (c.isEmpty()) {
var item = ContextMenuHelper.item(PrettyImageHelper.ofFixedSizeSquare(graphic, 16), w.getName().getValue());
var item = ContextMenuHelper.item(
PrettyImageHelper.ofFixedSizeSquare(graphic, 16),
w.getName().getValue());
item.setOnAction(event -> {
action.accept(w);
contextMenu.hide();
@ -83,7 +85,7 @@ public class StoreQuickAccessButtonComp extends Comp<CompStructure<Button>> {
}
struc.get().setOnAction(event -> {
ContextMenuHelper.toggleShow(cm,struc.get(), Side.RIGHT);
ContextMenuHelper.toggleShow(cm, struc.get(), Side.RIGHT);
event.consume();
});
});

View file

@ -94,7 +94,9 @@ public class StoreSection {
},
category);
var cached = ListBindingsHelper.cachedMappedContentBinding(
topLevel, topLevel, storeEntryWrapper -> create(storeEntryWrapper, 1, all, entryFilter, filterString, category));
topLevel,
topLevel,
storeEntryWrapper -> create(storeEntryWrapper, 1, all, entryFilter, filterString, category));
var ordered = sorted(cached, category);
var shown = ListBindingsHelper.filteredContentBinding(
ordered,
@ -133,7 +135,9 @@ public class StoreSection {
return DataStorage.get().getStoreChildren(e.getEntry()).contains(other.getEntry());
});
var cached = ListBindingsHelper.cachedMappedContentBinding(
allChildren, allChildren, entry1 -> create(entry1, depth + 1, all, entryFilter, filterString, category));
allChildren,
allChildren,
entry1 -> create(entry1, depth + 1, all, entryFilter, filterString, category));
var ordered = sorted(cached, category);
var filtered = ListBindingsHelper.filteredContentBinding(
ordered,

View file

@ -270,7 +270,9 @@ public class StoreViewState {
return parent;
}
return o1.nameProperty().getValue().compareToIgnoreCase(o2.nameProperty().getValue());
return o1.nameProperty()
.getValue()
.compareToIgnoreCase(o2.nameProperty().getValue());
}
};
return ListBindingsHelper.filteredContentBinding(

View file

@ -34,18 +34,10 @@ import java.util.regex.Pattern;
public class AppI18n {
@Value
static class LoadedTranslations {
Map<String, String> translations;
Map<String, String> markdownDocumentations;
PrettyTime prettyTime;
}
private static final Pattern VAR_PATTERN = Pattern.compile("\\$\\w+?\\$");
private static AppI18n INSTANCE;
private LoadedTranslations english;
private final Property<LoadedTranslations> currentLanguage = new SimpleObjectProperty<>();
private LoadedTranslations english;
public static void init() throws Exception {
if (INSTANCE == null) {
@ -54,39 +46,21 @@ public class AppI18n {
INSTANCE.load();
}
private void load() throws Exception {
if (english == null) {
english = load(Locale.ENGLISH);
}
if (AppPrefs.get() != null) {
AppPrefs.get().language().subscribe(n -> {
try {
currentLanguage.setValue(n != null ? load(n.getLocale()) : null);
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
}
});
}
}
public static AppI18n get() {
return INSTANCE;
}
private LoadedTranslations getLoaded() {
return currentLanguage.getValue() != null ? currentLanguage.getValue() : english;
}
public static ObservableValue<String> observable(String s, Object... vars) {
if (s == null) {
return null;
}
var key = INSTANCE.getKey(s);
return Bindings.createStringBinding(() -> {
return Bindings.createStringBinding(
() -> {
return get(key, vars);
}, INSTANCE.currentLanguage);
},
INSTANCE.currentLanguage);
}
public static String get(String s, Object... vars) {
@ -131,6 +105,26 @@ public class AppI18n {
return "";
}
private void load() throws Exception {
if (english == null) {
english = load(Locale.ENGLISH);
}
if (AppPrefs.get() != null) {
AppPrefs.get().language().subscribe(n -> {
try {
currentLanguage.setValue(n != null ? load(n.getLocale()) : null);
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
}
});
}
}
private LoadedTranslations getLoaded() {
return currentLanguage.getValue() != null ? currentLanguage.getValue() : english;
}
public String getKey(String s) {
var key = s;
if (!s.contains(".")) {
@ -147,7 +141,8 @@ public class AppI18n {
return s;
}
if (currentLanguage.getValue() != null && currentLanguage.getValue().getTranslations().containsKey(key)) {
if (currentLanguage.getValue() != null
&& currentLanguage.getValue().getTranslations().containsKey(key)) {
var localisedString = currentLanguage.getValue().getTranslations().get(key);
return getValue(localisedString, vars);
}
@ -168,8 +163,10 @@ public class AppI18n {
}
public String getMarkdownDocumentation(String name) {
if (currentLanguage.getValue() != null && currentLanguage.getValue().getMarkdownDocumentations().containsKey(name)) {
var localisedString = currentLanguage.getValue().getMarkdownDocumentations().get(name);
if (currentLanguage.getValue() != null
&& currentLanguage.getValue().getMarkdownDocumentations().containsKey(name)) {
var localisedString =
currentLanguage.getValue().getMarkdownDocumentations().get(name);
return localisedString;
}
@ -192,7 +189,8 @@ public class AppI18n {
var translations = new HashMap<String, String>();
for (var module : AppExtensionManager.getInstance().getContentModules()) {
var basePath = getModuleLangPath(FilenameUtils.getExtension(module.getName())).resolve("strings");
var basePath = getModuleLangPath(FilenameUtils.getExtension(module.getName()))
.resolve("strings");
if (!Files.exists(basePath)) {
continue;
}
@ -237,7 +235,8 @@ public class AppI18n {
var markdownDocumentations = new HashMap<String, String>();
for (var module : AppExtensionManager.getInstance().getContentModules()) {
var basePath = getModuleLangPath(FilenameUtils.getExtension(module.getName())).resolve("texts");
var basePath = getModuleLangPath(FilenameUtils.getExtension(module.getName()))
.resolve("texts");
if (!Files.exists(basePath)) {
continue;
}
@ -274,7 +273,15 @@ public class AppI18n {
? AppPrefs.get().language().getValue().getLocale()
: SupportedLocale.getEnglish().getLocale());
return new LoadedTranslations(translations,markdownDocumentations, prettyTime);
return new LoadedTranslations(translations, markdownDocumentations, prettyTime);
}
@Value
static class LoadedTranslations {
Map<String, String> translations;
Map<String, String> markdownDocumentations;
PrettyTime prettyTime;
}
@SuppressWarnings("removal")

View file

@ -71,7 +71,10 @@ public class AppLayoutModel {
private List<Entry> createEntryList() {
var l = new ArrayList<>(List.of(
new Entry(AppI18n.observable("browser"), "mdi2f-file-cabinet", new BrowserSessionComp(BrowserSessionModel.DEFAULT)),
new Entry(
AppI18n.observable("browser"),
"mdi2f-file-cabinet",
new BrowserSessionComp(BrowserSessionModel.DEFAULT)),
new Entry(AppI18n.observable("connections"), "mdi2c-connection", new StoreLayoutComp()),
new Entry(AppI18n.observable("settings"), "mdsmz-miscellaneous_services", new AppPrefsComp()),
new Entry(

View file

@ -48,7 +48,8 @@ public class AppTheme {
Runnable r = () -> {
AppPrefs.get().theme.subscribe(t -> {
Theme.ALL.forEach(theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId()));
Theme.ALL.forEach(
theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId()));
if (t == null) {
return;
}
@ -88,7 +89,7 @@ public class AppTheme {
var c = new WindowControl(stage);
c.setWindowAttribute(20, AppPrefs.get().theme.getValue().isDark());
stage.setWidth(stage.getWidth() + 1);
Platform.runLater( () -> {
Platform.runLater(() -> {
stage.setWidth(stage.getWidth() - 1);
});
} catch (Throwable e) {

View file

@ -53,7 +53,8 @@ public class AppWindowHelper {
// This allows for assigning logos even if AppImages has not been initialized yet
var dir = OsType.getLocal() == OsType.MACOS ? "img/logo/padded" : "img/logo/full";
AppResources.with(AppResources.XPIPE_MODULE, dir, path -> {
var size = switch (OsType.getLocal()) {
var size =
switch (OsType.getLocal()) {
case OsType.Linux linux -> 128;
case OsType.MacOs macOs -> 128;
case OsType.Windows windows -> 32;

View file

@ -13,7 +13,8 @@ public class AppFontLoadingCheck {
// This can fail if the found system fonts can somehow not be loaded
Font.getDefault();
} catch (Throwable e) {
var event = ErrorEvent.fromThrowable("Unable to load fonts. Do you have valid font packages installed?", e).build();
var event = ErrorEvent.fromThrowable("Unable to load fonts. Do you have valid font packages installed?", e)
.build();
// We can't use the normal error handling facility
// as the platform reports as working but opening windows still does not work
new LogErrorHandler().handle(event);

View file

@ -15,9 +15,9 @@ public class LaunchExchangeImpl extends LaunchExchange
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
var store = getStoreEntryById(msg.getId(), false);
if (store.getStore() instanceof LaunchableStore s) {
// var command = s.prepareLaunchCommand()
// .prepareTerminalOpen(TerminalInitScriptConfig.ofName(store.getName()), sc -> null);
// return Response.builder().command(split(command)).build();
// var command = s.prepareLaunchCommand()
// .prepareTerminalOpen(TerminalInitScriptConfig.ofName(store.getName()), sc -> null);
// return Response.builder().command(split(command)).build();
}
throw new IllegalArgumentException(store.getName() + " is not launchable");

View file

@ -12,6 +12,28 @@ import java.util.stream.Collectors;
public class MessageExchangeImpls {
private static List<MessageExchangeImpl<?, ?>> ALL;
@SuppressWarnings("unchecked")
public static <RQ extends RequestMessage, RS extends ResponseMessage> Optional<MessageExchangeImpl<RQ, RS>> byId(
String name) {
var r = ALL.stream().filter(d -> d.getId().equals(name)).findAny();
return Optional.ofNullable((MessageExchangeImpl<RQ, RS>) r.orElse(null));
}
@SuppressWarnings("unchecked")
public static <RQ extends RequestMessage, RS extends ResponseMessage>
Optional<MessageExchangeImpl<RQ, RS>> byRequest(RQ req) {
var r = ALL.stream()
.filter(d -> d.getRequestClass().equals(req.getClass()))
.findAny();
return Optional.ofNullable((MessageExchangeImpl<RQ, RS>) r.orElse(null));
}
public static List<MessageExchangeImpl<?, ?>> getAll() {
return ALL;
}
public static class Loader implements ModuleLayerLoader {
@Override
@ -36,27 +58,4 @@ public class MessageExchangeImpls {
});
}
}
private static List<MessageExchangeImpl<?, ?>> ALL;
@SuppressWarnings("unchecked")
public static <RQ extends RequestMessage, RS extends ResponseMessage> Optional<MessageExchangeImpl<RQ, RS>> byId(
String name) {
var r = ALL.stream().filter(d -> d.getId().equals(name)).findAny();
return Optional.ofNullable((MessageExchangeImpl<RQ, RS>) r.orElse(null));
}
@SuppressWarnings("unchecked")
public static <RQ extends RequestMessage, RS extends ResponseMessage>
Optional<MessageExchangeImpl<RQ, RS>> byRequest(RQ req) {
var r = ALL.stream()
.filter(d -> d.getRequestClass().equals(req.getClass()))
.findAny();
return Optional.ofNullable((MessageExchangeImpl<RQ, RS>) r.orElse(null));
}
public static List<MessageExchangeImpl<?, ?>> getAll() {
return ALL;
}
}

View file

@ -143,6 +143,5 @@ public interface ActionProvider {
})
.toList());
}
}
}

View file

@ -14,37 +14,6 @@ import java.util.stream.Collectors;
public class DataStoreProviders {
public static class Loader implements ModuleLayerLoader {
@Override
public void init(ModuleLayer layer) {
TrackEvent.info("Loading extension providers ...");
ALL = ServiceLoader.load(layer, DataStoreProvider.class).stream().map(ServiceLoader.Provider::get).collect(Collectors.toList());
ALL.removeIf(p -> {
try {
if (!p.init()) {
return true;
}
p.validate();
return false;
} catch (Throwable e) {
ErrorEvent.fromThrowable(e).handle();
return true;
}
});
for (DataStoreProvider p : getAll()) {
TrackEvent.trace("Loaded data store provider " + p.getId());
JacksonMapper.configure(objectMapper -> {
for (Class<?> storeClass : p.getStoreClasses()) {
objectMapper.registerSubtypes(new NamedType(storeClass));
}
});
}
}
}
private static List<DataStoreProvider> ALL;
public static void postInit(ModuleLayer layer) {
@ -100,4 +69,37 @@ public class DataStoreProviders {
public static List<DataStoreProvider> getAll() {
return ALL;
}
public static class Loader implements ModuleLayerLoader {
@Override
public void init(ModuleLayer layer) {
TrackEvent.info("Loading extension providers ...");
ALL = ServiceLoader.load(layer, DataStoreProvider.class).stream()
.map(ServiceLoader.Provider::get)
.collect(Collectors.toList());
ALL.removeIf(p -> {
try {
if (!p.init()) {
return true;
}
p.validate();
return false;
} catch (Throwable e) {
ErrorEvent.fromThrowable(e).handle();
return true;
}
});
for (DataStoreProvider p : getAll()) {
TrackEvent.trace("Loaded data store provider " + p.getId());
JacksonMapper.configure(objectMapper -> {
for (Class<?> storeClass : p.getStoreClasses()) {
objectMapper.registerSubtypes(new NamedType(storeClass));
}
});
}
}
}
}

View file

@ -36,9 +36,7 @@ public interface PrefsChoiceValue extends Translatable {
throw new AssertionError();
}
return all.stream()
.filter(t -> ((PrefsChoiceValue) t).isSelectable())
.toList();
return all.stream().filter(t -> ((PrefsChoiceValue) t).isSelectable()).toList();
}
default boolean isAvailable() {

View file

@ -34,6 +34,5 @@ public abstract class PrefsProvider {
.map(ServiceLoader.Provider::get)
.collect(Collectors.toList());
}
}
}

View file

@ -62,6 +62,5 @@ public abstract class ScanProvider {
scanProvider -> scanProvider.getClass().getName()))
.collect(Collectors.toList());
}
}
}

View file

@ -18,7 +18,10 @@ public class ContextMenuAugment<S extends CompStructure<?>> implements Augment<S
private final Predicate<KeyEvent> keyEventCheck;
private final Supplier<ContextMenu> contextMenu;
public ContextMenuAugment(Predicate<MouseEvent> mouseEventCheck, Predicate<KeyEvent> keyEventCheck, Supplier<ContextMenu> contextMenu) {
public ContextMenuAugment(
Predicate<MouseEvent> mouseEventCheck,
Predicate<KeyEvent> keyEventCheck,
Supplier<ContextMenu> contextMenu) {
this.mouseEventCheck = mouseEventCheck;
this.keyEventCheck = keyEventCheck;
this.contextMenu = contextMenu;

View file

@ -53,7 +53,9 @@ public class ContextualFileReferenceChoiceComp extends Comp<CompStructure<HBox>>
.grow(false, true);
var fileBrowseButton = new ButtonComp(null, new FontIcon("mdi2f-folder-open-outline"), () -> {
BrowserChooserComp.openSingleFile(() -> fileSystem.getValue(), fileStore -> {
BrowserChooserComp.openSingleFile(
() -> fileSystem.getValue(),
fileStore -> {
if (fileStore == null) {
filePath.setValue(null);
fileSystem.setValue(null);
@ -61,7 +63,8 @@ public class ContextualFileReferenceChoiceComp extends Comp<CompStructure<HBox>>
filePath.setValue(fileStore.getPath());
fileSystem.setValue(fileStore.getFileSystem());
}
}, false);
},
false);
})
.styleClass(Styles.CENTER_PILL)
.grow(false, true);
@ -85,7 +88,8 @@ public class ContextualFileReferenceChoiceComp extends Comp<CompStructure<HBox>>
return;
}
if (filePath.getValue() == null || ContextualFileReference.of(filePath.getValue()).isInDataDirectory()) {
if (filePath.getValue() == null
|| ContextualFileReference.of(filePath.getValue()).isInDataDirectory()) {
return;
}

View file

@ -3,7 +3,6 @@ package io.xpipe.app.fxcomps.impl;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.PlatformThread;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;

View file

@ -71,7 +71,8 @@ public class StoreCategoryComp extends SimpleComp {
var showing = new SimpleBooleanProperty();
var settings = new IconButtonComp("mdomz-settings")
.styleClass("settings")
.apply(new ContextMenuAugment<>(mouseEvent -> mouseEvent.getButton() == MouseButton.PRIMARY, null, () -> {
.apply(new ContextMenuAugment<>(
mouseEvent -> mouseEvent.getButton() == MouseButton.PRIMARY, null, () -> {
var cm = createContextMenu(name);
showing.bind(cm.showingProperty());
return cm;
@ -102,11 +103,13 @@ public class StoreCategoryComp extends SimpleComp {
.accessibleText(category.nameProperty())
.grow(true, false);
categoryButton.apply(new ContextMenuAugment<>(
mouseEvent -> mouseEvent.getButton() == MouseButton.SECONDARY, keyEvent -> keyEvent.getCode() == KeyCode.SPACE, () -> createContextMenu(name)));
mouseEvent -> mouseEvent.getButton() == MouseButton.SECONDARY,
keyEvent -> keyEvent.getCode() == KeyCode.SPACE,
() -> createContextMenu(name)));
var l = category.getChildren()
.sorted(Comparator.comparing(
storeCategoryWrapper -> storeCategoryWrapper.nameProperty().getValue().toLowerCase(Locale.ROOT)));
.sorted(Comparator.comparing(storeCategoryWrapper ->
storeCategoryWrapper.nameProperty().getValue().toLowerCase(Locale.ROOT)));
var children = new ListBoxViewComp<>(l, l, storeCategoryWrapper -> new StoreCategoryComp(storeCategoryWrapper));
var emptyBinding = Bindings.isEmpty(category.getChildren());

View file

@ -17,7 +17,8 @@ public class StringSourceComp extends SimpleComp {
private final Property<DataStoreEntryRef<ShellStore>> fileSystem;
private final Property<StringSource> stringSource;
public <T extends ShellStore> StringSourceComp(ObservableValue<DataStoreEntryRef<T>> fileSystem, Property<StringSource> stringSource) {
public <T extends ShellStore> StringSourceComp(
ObservableValue<DataStoreEntryRef<T>> fileSystem, Property<StringSource> stringSource) {
this.stringSource = stringSource;
this.fileSystem = new SimpleObjectProperty<>();
fileSystem.subscribe(val -> {
@ -27,9 +28,13 @@ public class StringSourceComp extends SimpleComp {
@Override
protected Region createSimple() {
var inPlace = new SimpleObjectProperty<>(stringSource.getValue() instanceof StringSource.InPlace i ? i.get() : null);
var inPlace =
new SimpleObjectProperty<>(stringSource.getValue() instanceof StringSource.InPlace i ? i.get() : null);
var fs = stringSource.getValue() instanceof StringSource.File f ? f.getFile() : null;
var file = new SimpleObjectProperty<>(stringSource.getValue() instanceof StringSource.File f ? f.getFile().serialize() : null);
var file = new SimpleObjectProperty<>(
stringSource.getValue() instanceof StringSource.File f
? f.getFile().serialize()
: null);
var showText = new SimpleBooleanProperty(inPlace.get() != null);
var stringField = new TextAreaComp(inPlace);
@ -40,7 +45,8 @@ public class StringSourceComp extends SimpleComp {
var tr = stringField.createRegion();
var button = new IconButtonComp("mdi2c-checkbox-marked-outline", () -> {
showText.set(!showText.getValue());
}).createRegion();
})
.createRegion();
AnchorPane.setBottomAnchor(button, 10.0);
AnchorPane.setRightAnchor(button, 10.0);
var anchorPane = new AnchorPane(tr, button);

View file

@ -68,7 +68,7 @@ public class SvgView {
wv.setDisable(true);
wv.getEngine().loadContent(svgContent.getValue() != null ? getHtml(svgContent.getValue()) : null);
svgContent.subscribe( n -> {
svgContent.subscribe(n -> {
if (n == null) {
wv.setOpacity(0.0);
return;

View file

@ -22,29 +22,19 @@ public class TooltipAugment<S extends CompStructure<?>> implements Augment<S> {
this.text = AppI18n.observable(key);
}
private static class FixedTooltip extends Tooltip {
public FixedTooltip() {
super();
}
@Override
protected void show() {
Window owner = getOwnerWindow();
if (owner.isFocused())
super.show();
}
}
@Override
public void augment(S struc) {
var region = struc.get();
var tt = new FixedTooltip();
if (Shortcuts.getDisplayShortcut(region) != null) {
var s = AppI18n.observable("shortcut");
var binding = Bindings.createStringBinding(() -> {
return text.getValue() + "\n\n" + s.getValue() + ": " + Shortcuts.getDisplayShortcut(region).getDisplayText();
}, text, s);
var binding = Bindings.createStringBinding(
() -> {
return text.getValue() + "\n\n" + s.getValue() + ": "
+ Shortcuts.getDisplayShortcut(region).getDisplayText();
},
text,
s);
tt.textProperty().bind(binding);
} else {
tt.textProperty().bind(text);
@ -56,4 +46,17 @@ public class TooltipAugment<S extends CompStructure<?>> implements Augment<S> {
Tooltip.install(struc.get(), tt);
}
private static class FixedTooltip extends Tooltip {
public FixedTooltip() {
super();
}
@Override
protected void show() {
Window owner = getOwnerWindow();
if (owner.isFocused()) super.show();
}
}
}

View file

@ -119,7 +119,8 @@ public abstract class LauncherInput {
var dir = Files.isDirectory(file) ? file : file.getParent();
AppLayoutModel.get().selectBrowser();
BrowserSessionModel.DEFAULT.openFileSystemAsync(DataStorage.get().local().ref(), model -> dir.toString(), null);
BrowserSessionModel.DEFAULT.openFileSystemAsync(
DataStorage.get().local().ref(), model -> dir.toString(), null);
}
}

View file

@ -24,18 +24,6 @@ public class AppearanceCategory extends AppPrefsCategory {
return "appearance";
}
private Comp<?> languageChoice() {
var prefs = AppPrefs.get();
var c = ChoiceComp.ofTranslatable(prefs.language, SupportedLocale.ALL, false);
var visit = new ButtonComp(AppI18n.observable("translate"), new FontIcon("mdi2w-web"), () -> {
Hyperlinks.open(Hyperlinks.TRANSLATE);
});
return new HorizontalComp(List.of(c, visit)).apply(struc -> {
struc.get().setAlignment(Pos.CENTER_LEFT);
struc.get().setSpacing(10);
});
}
@Override
protected Comp<?> create() {
var prefs = AppPrefs.get();
@ -43,9 +31,7 @@ public class AppearanceCategory extends AppPrefsCategory {
.addTitle("uiOptions")
.sub(new OptionsBuilder()
.nameAndDescription("language")
.addComp(
languageChoice(),
prefs.language)
.addComp(languageChoice(), prefs.language)
.nameAndDescription("theme")
.addComp(
ChoiceComp.ofTranslatable(prefs.theme, AppTheme.Theme.ALL, false)
@ -83,4 +69,16 @@ public class AppearanceCategory extends AppPrefsCategory {
.addToggle(prefs.enforceWindowModality))
.buildComp();
}
private Comp<?> languageChoice() {
var prefs = AppPrefs.get();
var c = ChoiceComp.ofTranslatable(prefs.language, SupportedLocale.ALL, false);
var visit = new ButtonComp(AppI18n.observable("translate"), new FontIcon("mdi2w-web"), () -> {
Hyperlinks.open(Hyperlinks.TRANSLATE);
});
return new HorizontalComp(List.of(c, visit)).apply(struc -> {
struc.get().setAlignment(Pos.CENTER_LEFT);
struc.get().setSpacing(10);
});
}
}

View file

@ -106,8 +106,8 @@ public interface ExternalEditorType extends PrefsChoiceValue {
var format =
customCommand.toLowerCase(Locale.ROOT).contains("$file") ? customCommand : customCommand + " $FILE";
ExternalApplicationHelper.startAsync(
CommandBuilder.of().add(ExternalApplicationHelper.replaceFileArgument(format, "FILE", file.toString())));
ExternalApplicationHelper.startAsync(CommandBuilder.of()
.add(ExternalApplicationHelper.replaceFileArgument(format, "FILE", file.toString())));
}
@Override
@ -199,8 +199,7 @@ public interface ExternalEditorType extends PrefsChoiceValue {
throw new IOException("Application " + applicationName + ".app not found");
}
ExternalApplicationHelper.startAsync(
CommandBuilder.of()
ExternalApplicationHelper.startAsync(CommandBuilder.of()
.add("open", "-a")
.addFile(execFile.orElseThrow().toString())
.addFile(file.toString()));

View file

@ -14,44 +14,14 @@ import java.util.function.Supplier;
public interface ExternalRdpClientType extends PrefsChoiceValue {
@Value
class LaunchConfiguration {
String title;
RdpConfig config;
UUID storeId;
SecretRetrievalStrategy password;
}
abstract class PathCheckType extends ExternalApplicationType.PathApplication implements ExternalRdpClientType {
public PathCheckType(String id, String executable, boolean explicityAsync) {
super(id, executable, explicityAsync);
}
}
abstract class MacOsType extends ExternalApplicationType.MacApplication implements ExternalRdpClientType {
public MacOsType(String id, String applicationName) {
super(id, applicationName);
}
}
void launch(LaunchConfiguration configuration) throws Exception;
default Path writeConfig(RdpConfig input) throws Exception {
var file = LocalShell.getShell().getSystemTemporaryDirectory().join("exec-" + ScriptHelper.getScriptId() + ".rdp");
var string = input.toString();
Files.writeString(file.toLocalPath(), string);
return file.toLocalPath();
}
ExternalRdpClientType MSTSC = new PathCheckType("app.mstsc", "mstsc.exe", true) {
@Override
public void launch(LaunchConfiguration configuration) throws Exception {
var adaptedRdpConfig = getAdaptedConfig(configuration);
var file = writeConfig(adaptedRdpConfig);
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of().add(executable).addFile(file.toString()));
LocalShell.getShell()
.executeSimpleCommand(CommandBuilder.of().add(executable).addFile(file.toString()));
}
private RdpConfig getAdaptedConfig(LaunchConfiguration configuration) throws Exception {
@ -85,17 +55,18 @@ public interface ExternalRdpClientType extends PrefsChoiceValue {
return cmd.readStdoutOrThrow();
}
};
ExternalRdpClientType REMMINA = new PathCheckType("app.remmina", "remmina", true) {
@Override
public void launch(LaunchConfiguration configuration) throws Exception {
var file = writeConfig(configuration.getConfig());
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of().add(executable).add("-c").addFile(file.toString()));
LocalShell.getShell()
.executeSimpleCommand(
CommandBuilder.of().add(executable).add("-c").addFile(file.toString()));
}
};
ExternalRdpClientType MICROSOFT_REMOTE_DESKTOP_MACOS_APP = new MacOsType("app.microsoftRemoteDesktopApp", "Microsoft Remote Desktop.app") {
ExternalRdpClientType MICROSOFT_REMOTE_DESKTOP_MACOS_APP =
new MacOsType("app.microsoftRemoteDesktopApp", "Microsoft Remote Desktop.app") {
@Override
public void launch(LaunchConfiguration configuration) throws Exception {
@ -107,36 +78,10 @@ public interface ExternalRdpClientType extends PrefsChoiceValue {
.addFile(file.toString()));
}
};
class CustomType extends ExternalApplicationType implements ExternalRdpClientType {
public CustomType() {
super("app.custom");
}
@Override
public void launch(LaunchConfiguration configuration) throws Exception {
var customCommand = AppPrefs.get().customRdpClientCommand().getValue();
if (customCommand == null || customCommand.isBlank()) {
throw ErrorEvent.expected(new IllegalStateException("No custom RDP command specified"));
}
var format = customCommand.toLowerCase(Locale.ROOT).contains("$file") ? customCommand : customCommand + " $FILE";
ExternalApplicationHelper.startAsync(CommandBuilder.of().add(ExternalApplicationHelper.replaceFileArgument(format, "FILE", writeConfig(configuration.getConfig()).toString())));
}
@Override
public boolean isAvailable() {
return true;
}
}
ExternalRdpClientType CUSTOM = new CustomType();
List<ExternalRdpClientType> WINDOWS_CLIENTS = List.of(MSTSC);
List<ExternalRdpClientType> LINUX_CLIENTS = List.of(REMMINA);
List<ExternalRdpClientType> MACOS_CLIENTS = List.of(MICROSOFT_REMOTE_DESKTOP_MACOS_APP);
@SuppressWarnings("TrivialFunctionalExpressionUsage")
List<ExternalRdpClientType> ALL = ((Supplier<List<ExternalRdpClientType>>) () -> {
var all = new ArrayList<ExternalRdpClientType>();
@ -151,7 +96,8 @@ public interface ExternalRdpClientType extends PrefsChoiceValue {
}
all.add(CUSTOM);
return all;
}).get();
})
.get();
static ExternalRdpClientType determineDefault() {
return ALL.stream()
@ -160,4 +106,64 @@ public interface ExternalRdpClientType extends PrefsChoiceValue {
.findFirst()
.orElse(null);
}
void launch(LaunchConfiguration configuration) throws Exception;
default Path writeConfig(RdpConfig input) throws Exception {
var file =
LocalShell.getShell().getSystemTemporaryDirectory().join("exec-" + ScriptHelper.getScriptId() + ".rdp");
var string = input.toString();
Files.writeString(file.toLocalPath(), string);
return file.toLocalPath();
}
@Value
class LaunchConfiguration {
String title;
RdpConfig config;
UUID storeId;
SecretRetrievalStrategy password;
}
abstract class PathCheckType extends ExternalApplicationType.PathApplication implements ExternalRdpClientType {
public PathCheckType(String id, String executable, boolean explicityAsync) {
super(id, executable, explicityAsync);
}
}
abstract class MacOsType extends ExternalApplicationType.MacApplication implements ExternalRdpClientType {
public MacOsType(String id, String applicationName) {
super(id, applicationName);
}
}
class CustomType extends ExternalApplicationType implements ExternalRdpClientType {
public CustomType() {
super("app.custom");
}
@Override
public void launch(LaunchConfiguration configuration) throws Exception {
var customCommand = AppPrefs.get().customRdpClientCommand().getValue();
if (customCommand == null || customCommand.isBlank()) {
throw ErrorEvent.expected(new IllegalStateException("No custom RDP command specified"));
}
var format =
customCommand.toLowerCase(Locale.ROOT).contains("$file") ? customCommand : customCommand + " $FILE";
ExternalApplicationHelper.startAsync(CommandBuilder.of()
.add(ExternalApplicationHelper.replaceFileArgument(
format,
"FILE",
writeConfig(configuration.getConfig()).toString())));
}
@Override
public boolean isAvailable() {
return true;
}
}
}

View file

@ -25,8 +25,7 @@ public class RdpCategory extends AppPrefsCategory {
.nameAndDescription("customRdpClientCommand")
.addComp(new TextFieldComp(prefs.customRdpClientCommand, true)
.apply(struc -> struc.get().setPromptText("myrdpclient -c $FILE"))
.hide(prefs.rdpClientType.isNotEqualTo(ExternalRdpClientType.CUSTOM)))
)
.hide(prefs.rdpClientType.isNotEqualTo(ExternalRdpClientType.CUSTOM))))
.buildComp();
}
}

View file

@ -14,15 +14,19 @@ import java.util.Locale;
@Getter
public class SupportedLocale implements PrefsChoiceValue {
public static List<SupportedLocale> ALL = AppProperties.get().getLanguages().stream().map(s -> new SupportedLocale(Locale.of(s), s)).toList();
public static SupportedLocale getEnglish() {
return ALL.stream().filter(supportedLocale -> supportedLocale.getId().equals("en")).findFirst().orElseThrow();
}
public static List<SupportedLocale> ALL = AppProperties.get().getLanguages().stream()
.map(s -> new SupportedLocale(Locale.of(s), s))
.toList();
private final Locale locale;
private final String id;
public static SupportedLocale getEnglish() {
return ALL.stream()
.filter(supportedLocale -> supportedLocale.getId().equals("en"))
.findFirst()
.orElseThrow();
}
@Override
public ObservableValue<String> toTranslatedString() {
return new SimpleStringProperty(locale.getDisplayName(locale));

View file

@ -30,57 +30,6 @@ public class TerminalCategory extends AppPrefsCategory {
return "terminal";
}
private Comp<?> terminalChoice() {
var prefs = AppPrefs.get();
var c = ChoiceComp.ofTranslatable(
prefs.terminalType, PrefsChoiceValue.getSupported(ExternalTerminalType.class), false);
c.apply(struc -> {
struc.get().setCellFactory(param -> {
return new ListCell<>() {
@Override
protected void updateItem(ExternalTerminalType item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
return;
}
setText(item.toTranslatedString().getValue());
if (item != ExternalTerminalType.CUSTOM) {
var graphic = new FontIcon(item.isRecommended() ? "mdi2c-check-decagram" : "mdi2a-alert-circle-check");
graphic.setFill(item.isRecommended() ? Color.GREEN : Color.ORANGE);
setGraphic(graphic);
} else {
setGraphic(new FontIcon("mdi2m-minus-circle"));
}
}
};
});
});
var visit = new ButtonComp(AppI18n.observable("website"), new FontIcon("mdi2w-web"), () -> {
var t = prefs.terminalType().getValue();
if (t == null || t.getWebsite() == null) {
return;
}
Hyperlinks.open(t.getWebsite());
});
var visitVisible = Bindings.createBooleanBinding(() -> {
var t = prefs.terminalType().getValue();
if (t == null || t.getWebsite() == null) {
return false;
}
return true;
}, prefs.terminalType());
visit.visible(visitVisible);
return new HorizontalComp(List.of(c, visit)).apply(struc -> {
struc.get().setAlignment(Pos.CENTER_LEFT);
struc.get().setSpacing(10);
});
}
@Override
protected Comp<?> create() {
var prefs = AppPrefs.get();
@ -117,4 +66,58 @@ public class TerminalCategory extends AppPrefsCategory {
.addToggle(prefs.clearTerminalOnInit))
.buildComp();
}
private Comp<?> terminalChoice() {
var prefs = AppPrefs.get();
var c = ChoiceComp.ofTranslatable(
prefs.terminalType, PrefsChoiceValue.getSupported(ExternalTerminalType.class), false);
c.apply(struc -> {
struc.get().setCellFactory(param -> {
return new ListCell<>() {
@Override
protected void updateItem(ExternalTerminalType item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
return;
}
setText(item.toTranslatedString().getValue());
if (item != ExternalTerminalType.CUSTOM) {
var graphic = new FontIcon(
item.isRecommended() ? "mdi2c-check-decagram" : "mdi2a-alert-circle-check");
graphic.setFill(item.isRecommended() ? Color.GREEN : Color.ORANGE);
setGraphic(graphic);
} else {
setGraphic(new FontIcon("mdi2m-minus-circle"));
}
}
};
});
});
var visit = new ButtonComp(AppI18n.observable("website"), new FontIcon("mdi2w-web"), () -> {
var t = prefs.terminalType().getValue();
if (t == null || t.getWebsite() == null) {
return;
}
Hyperlinks.open(t.getWebsite());
});
var visitVisible = Bindings.createBooleanBinding(
() -> {
var t = prefs.terminalType().getValue();
if (t == null || t.getWebsite() == null) {
return false;
}
return true;
},
prefs.terminalType());
visit.visible(visitVisible);
return new HorizontalComp(List.of(c, visit)).apply(struc -> {
struc.get().setAlignment(Pos.CENTER_LEFT);
struc.get().setSpacing(10);
});
}
}

View file

@ -45,7 +45,8 @@ public class TroubleshootCategory extends AppPrefsCategory {
XPipeInstallation.getCurrentInstallationBasePath()
.toString(),
XPipeInstallation.getDaemonDebugScriptPath(OsType.getLocal()));
TerminalLauncher.openDirect("XPipe Debug", sc -> sc.getShellDialect().runScriptCommand(sc, script));
TerminalLauncher.openDirect("XPipe Debug", sc -> sc.getShellDialect()
.runScriptCommand(sc, script));
});
e.consume();
})

View file

@ -5,6 +5,30 @@ import io.xpipe.core.process.CommandBuilder;
public interface AlacrittyTerminalType extends ExternalTerminalType {
ExternalTerminalType ALACRITTY_WINDOWS = new Windows();
ExternalTerminalType ALACRITTY_LINUX = new Linux();
ExternalTerminalType ALACRITTY_MAC_OS = new MacOs();
@Override
default boolean supportsTabs() {
return false;
}
@Override
default String getWebsite() {
return "https://github.com/alacritty/alacritty";
}
@Override
default boolean isRecommended() {
return false;
}
@Override
default boolean supportsColoredTitle() {
return false;
}
static class Windows extends SimplePathType implements AlacrittyTerminalType {
public Windows() {
@ -15,11 +39,11 @@ public interface AlacrittyTerminalType extends ExternalTerminalType {
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
var b = CommandBuilder.of();
// if (configuration.getColor() != null) {
// b.add("-o")
// .addQuoted("colors.primary.background='%s'"
// .formatted(configuration.getColor().toHexString()));
// }
// if (configuration.getColor() != null) {
// b.add("-o")
// .addQuoted("colors.primary.background='%s'"
// .formatted(configuration.getColor().toHexString()));
// }
// Alacritty is bugged and will not accept arguments with spaces even if they are correctly passed/escaped
// So this will not work when the script file has spaces
@ -28,7 +52,6 @@ public interface AlacrittyTerminalType extends ExternalTerminalType {
.add("-e")
.add(configuration.getDialectLaunchCommand());
}
}
static class Linux extends SimplePathType implements AlacrittyTerminalType {
@ -45,31 +68,6 @@ public interface AlacrittyTerminalType extends ExternalTerminalType {
.add("-e")
.addFile(configuration.getScriptFile());
}
}
ExternalTerminalType ALACRITTY_WINDOWS = new Windows();
ExternalTerminalType ALACRITTY_LINUX = new Linux();
ExternalTerminalType ALACRITTY_MAC_OS = new MacOs();
@Override
default String getWebsite() {
return "https://github.com/alacritty/alacritty";
}
@Override
default boolean isRecommended() {
return false;
}
@Override
default boolean supportsTabs() {
return false;
}
@Override
default boolean supportsColoredTitle() {
return false;
}
class MacOs extends MacOsType implements AlacrittyTerminalType {

View file

@ -39,7 +39,8 @@ public class CustomTerminalType extends ExternalApplicationType implements Exter
var format = custom.toLowerCase(Locale.ROOT).contains("$cmd") ? custom : custom + " $CMD";
try (var pc = LocalShell.getShell()) {
var toExecute = ExternalApplicationHelper.replaceFileArgument(format, "CMD", configuration.getScriptFile().toString());
var toExecute = ExternalApplicationHelper.replaceFileArgument(
format, "CMD", configuration.getScriptFile().toString());
// We can't be sure whether the command is blocking or not, so always make it not blocking
if (pc.getOsType().equals(OsType.WINDOWS)) {
toExecute = "start \"" + configuration.getCleanTitle() + "\" " + toExecute;

View file

@ -3,7 +3,8 @@ package io.xpipe.app.terminal;
import io.xpipe.app.ext.PrefsChoiceValue;
import io.xpipe.app.prefs.ExternalApplicationType;
import io.xpipe.app.storage.DataStoreColor;
import io.xpipe.app.util.*;
import io.xpipe.app.util.CommandSupport;
import io.xpipe.app.util.LocalShell;
import io.xpipe.core.process.*;
import io.xpipe.core.store.FilePath;
import lombok.Getter;
@ -13,7 +14,10 @@ import lombok.With;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.*;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Comparator;
import java.util.List;
import java.util.function.Supplier;
public interface ExternalTerminalType extends PrefsChoiceValue {
@ -21,12 +25,12 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
ExternalTerminalType CMD = new SimplePathType("app.cmd", "cmd.exe", true) {
@Override
public boolean isRecommended() {
public boolean supportsTabs() {
return false;
}
@Override
public boolean supportsTabs() {
public boolean isRecommended() {
return false;
}
@ -48,12 +52,12 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
ExternalTerminalType POWERSHELL = new SimplePathType("app.powershell", "powershell", true) {
@Override
public boolean isRecommended() {
public boolean supportsTabs() {
return false;
}
@Override
public boolean supportsTabs() {
public boolean isRecommended() {
return false;
}
@ -88,12 +92,12 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
ExternalTerminalType PWSH = new SimplePathType("app.pwsh", "pwsh", true) {
@Override
public boolean isRecommended() {
public boolean supportsTabs() {
return false;
}
@Override
public boolean supportsTabs() {
public boolean isRecommended() {
return false;
}
@ -118,8 +122,8 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType GNOME_TERMINAL = new PathCheckType("app.gnomeTerminal", "gnome-terminal", true) {
@Override
public boolean supportsColoredTitle() {
return true;
public boolean supportsTabs() {
return false;
}
@Override
@ -128,8 +132,8 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
}
@Override
public boolean supportsTabs() {
return false;
public boolean supportsColoredTitle() {
return true;
}
@Override
@ -152,12 +156,12 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
ExternalTerminalType KONSOLE = new SimplePathType("app.konsole", "konsole", true) {
@Override
public boolean isRecommended() {
public boolean supportsTabs() {
return true;
}
@Override
public boolean supportsTabs() {
public boolean isRecommended() {
return true;
}
@ -176,18 +180,17 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType XFCE = new SimplePathType("app.xfce", "xfce4-terminal", true) {
@Override
public boolean supportsColoredTitle() {
public boolean supportsTabs() {
return true;
}
@Override
public boolean isRecommended() {
return true;
}
@Override
public boolean supportsTabs() {
public boolean supportsColoredTitle() {
return true;
}
@ -202,18 +205,17 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType ELEMENTARY = new SimplePathType("app.elementaryTerminal", "io.elementary.terminal", true) {
@Override
public boolean supportsColoredTitle() {
public boolean supportsTabs() {
return true;
}
@Override
public boolean isRecommended() {
return true;
}
@Override
public boolean supportsTabs() {
public boolean supportsColoredTitle() {
return true;
}
@ -224,19 +226,18 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType TILIX = new SimplePathType("app.tilix", "tilix", true) {
@Override
public boolean supportsColoredTitle() {
return true;
public boolean supportsTabs() {
return false;
}
@Override
public boolean isRecommended() {
return false;
}
@Override
public boolean supportsTabs() {
return false;
public boolean supportsColoredTitle() {
return true;
}
@Override
@ -250,18 +251,17 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType TERMINATOR = new SimplePathType("app.terminator", "terminator", true) {
@Override
public boolean supportsColoredTitle() {
public boolean supportsTabs() {
return true;
}
@Override
public boolean isRecommended() {
return true;
}
@Override
public boolean supportsTabs() {
public boolean supportsColoredTitle() {
return true;
}
@ -277,18 +277,17 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType TERMINOLOGY = new SimplePathType("app.terminology", "terminology", true) {
@Override
public boolean supportsColoredTitle() {
public boolean supportsTabs() {
return true;
}
@Override
public boolean isRecommended() {
return true;
}
@Override
public boolean supportsTabs() {
public boolean supportsColoredTitle() {
return true;
}
@ -304,19 +303,18 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType COOL_RETRO_TERM = new SimplePathType("app.coolRetroTerm", "cool-retro-term", true) {
@Override
public boolean supportsColoredTitle() {
return true;
public boolean supportsTabs() {
return false;
}
@Override
public boolean isRecommended() {
return false;
}
@Override
public boolean supportsTabs() {
return false;
public boolean supportsColoredTitle() {
return true;
}
@Override
@ -330,18 +328,17 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType GUAKE = new SimplePathType("app.guake", "guake", true) {
@Override
public boolean supportsColoredTitle() {
public boolean supportsTabs() {
return true;
}
@Override
public boolean isRecommended() {
return true;
}
@Override
public boolean supportsTabs() {
public boolean supportsColoredTitle() {
return true;
}
@ -357,18 +354,17 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType TILDA = new SimplePathType("app.tilda", "tilda", true) {
@Override
public boolean supportsColoredTitle() {
public boolean supportsTabs() {
return true;
}
@Override
public boolean isRecommended() {
return true;
}
@Override
public boolean supportsTabs() {
public boolean supportsColoredTitle() {
return true;
}
@ -379,19 +375,18 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType XTERM = new SimplePathType("app.xterm", "xterm", true) {
@Override
public boolean supportsColoredTitle() {
return true;
public boolean supportsTabs() {
return false;
}
@Override
public boolean isRecommended() {
return false;
}
@Override
public boolean supportsTabs() {
return false;
public boolean supportsColoredTitle() {
return true;
}
@Override
@ -405,19 +400,18 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType DEEPIN_TERMINAL = new SimplePathType("app.deepinTerminal", "deepin-terminal", true) {
@Override
public boolean supportsColoredTitle() {
return true;
public boolean supportsTabs() {
return false;
}
@Override
public boolean isRecommended() {
return false;
}
@Override
public boolean supportsTabs() {
return false;
public boolean supportsColoredTitle() {
return true;
}
@Override
@ -427,8 +421,8 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType Q_TERMINAL = new SimplePathType("app.qTerminal", "qterminal", true) {
@Override
public boolean supportsColoredTitle() {
return true;
public boolean supportsTabs() {
return false;
}
@Override
@ -436,10 +430,9 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
return false;
}
@Override
public boolean supportsTabs() {
return false;
public boolean supportsColoredTitle() {
return true;
}
@Override
@ -449,8 +442,8 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType MACOS_TERMINAL = new MacOsType("app.macosTerminal", "Terminal") {
@Override
public boolean supportsColoredTitle() {
return true;
public boolean supportsTabs() {
return false;
}
@Override
@ -459,8 +452,8 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
}
@Override
public boolean supportsTabs() {
return false;
public boolean supportsColoredTitle() {
return true;
}
@Override
@ -480,7 +473,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
};
ExternalTerminalType ITERM2 = new MacOsType("app.iterm2", "iTerm") {
@Override
public boolean supportsColoredTitle() {
public boolean supportsTabs() {
return true;
}
@ -490,7 +483,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
}
@Override
public boolean supportsTabs() {
public boolean supportsColoredTitle() {
return true;
}
@ -519,7 +512,11 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
create window with default profile command "%s"
end tell
""",
a, a, a, a, configuration.getScriptFile().toString().replaceAll("\"", "\\\\\"")))
a,
a,
a,
a,
configuration.getScriptFile().toString().replaceAll("\"", "\\\\\"")))
.execute();
}
}
@ -527,7 +524,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
ExternalTerminalType WARP = new MacOsType("app.warp", "Warp") {
@Override
public boolean supportsColoredTitle() {
public boolean supportsTabs() {
return true;
}
@ -537,7 +534,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
}
@Override
public boolean supportsTabs() {
public boolean supportsColoredTitle() {
return true;
}
@ -582,8 +579,14 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
XTERM,
DEEPIN_TERMINAL,
Q_TERMINAL);
List<ExternalTerminalType> MACOS_TERMINALS =
List.of(ITERM2, TabbyTerminalType.TABBY_MAC_OS, AlacrittyTerminalType.ALACRITTY_MAC_OS, KittyTerminalType.KITTY_MACOS, WARP, WezTerminalType.WEZTERM_MAC_OS, MACOS_TERMINAL);
List<ExternalTerminalType> MACOS_TERMINALS = List.of(
ITERM2,
TabbyTerminalType.TABBY_MAC_OS,
AlacrittyTerminalType.ALACRITTY_MAC_OS,
KittyTerminalType.KITTY_MACOS,
WARP,
WezTerminalType.WEZTERM_MAC_OS,
MACOS_TERMINAL);
@SuppressWarnings("TrivialFunctionalExpressionUsage")
List<ExternalTerminalType> ALL = ((Supplier<List<ExternalTerminalType>>) () -> {
@ -714,5 +717,4 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
protected abstract CommandBuilder toCommand(LaunchConfiguration configuration) throws Exception;
}
}

View file

@ -11,14 +11,61 @@ import io.xpipe.core.util.XPipeInstallation;
public interface KittyTerminalType extends ExternalTerminalType {
@Override
default boolean supportsColoredTitle() {
return true;
public static final ExternalTerminalType KITTY_LINUX = new Linux();
public static final ExternalTerminalType KITTY_MACOS = new MacOs();
private static FilePath getSocket() throws Exception {
try (var sc = LocalShell.getShell().start()) {
var temp = ShellTemp.getUserSpecificTempDataDirectory(sc, null);
sc.executeSimpleCommand(sc.getShellDialect().getMkdirsCommand(temp.toString()));
return temp.join("xpipe_kitty");
}
}
@Override
default boolean isRecommended() {
return true;
private static void open(ExternalTerminalType.LaunchConfiguration configuration, CommandBuilder socketWrite)
throws Exception {
try (var sc = LocalShell.getShell().start()) {
var payload = JsonNodeFactory.instance.objectNode();
var args = configuration.getDialectLaunchCommand().buildBaseParts(sc);
var argsArray = payload.putArray("args");
args.forEach(argsArray::add);
payload.put("tab_title", configuration.getColoredTitle());
payload.put("type", "tab");
payload.put("logo_alpha", 0.01);
payload.put(
"logo", XPipeInstallation.getLocalDefaultInstallationIcon().toString());
var json = JsonNodeFactory.instance.objectNode();
json.put("cmd", "launch");
json.set("payload", payload);
json.putArray("version").add(0).add(14).add(2);
var jsonString = json.toString();
var echoString = "'\\eP@kitty-cmd" + jsonString + "\\e\\\\'";
sc.executeSimpleCommand(CommandBuilder.of()
.add("echo", "-en", echoString, "|")
.add(socketWrite)
.addFile(getSocket()));
}
}
private static void closeInitial(CommandBuilder socketWrite) throws Exception {
try (var sc = LocalShell.getShell().start()) {
var payload = JsonNodeFactory.instance.objectNode();
payload.put("match", "not recent:0");
var json = JsonNodeFactory.instance.objectNode();
json.put("cmd", "close-tab");
json.set("payload", payload);
json.putArray("version").add(0).add(14).add(2);
var jsonString = json.toString();
var echoString = "'\\eP@kitty-cmd" + jsonString + "\\e\\\\'";
sc.executeSimpleCommand(CommandBuilder.of()
.add("echo", "-en", echoString, "|")
.add(socketWrite)
.addFile(getSocket()));
}
}
@Override
@ -31,55 +78,14 @@ public interface KittyTerminalType extends ExternalTerminalType {
return "https://github.com/kovidgoyal/kitty";
}
public static final ExternalTerminalType KITTY_LINUX = new Linux();
public static final ExternalTerminalType KITTY_MACOS = new MacOs();
private static FilePath getSocket() throws Exception {
try (var sc = LocalShell.getShell().start()) {
var temp = ShellTemp.getUserSpecificTempDataDirectory(sc, null);
sc.executeSimpleCommand(sc.getShellDialect().getMkdirsCommand(temp.toString()));
return temp.join("xpipe_kitty");
}
@Override
default boolean isRecommended() {
return true;
}
private static void open(ExternalTerminalType.LaunchConfiguration configuration, CommandBuilder socketWrite) throws Exception {
try (var sc = LocalShell.getShell().start()) {
var payload = JsonNodeFactory.instance.objectNode();
var args = configuration.getDialectLaunchCommand().buildBaseParts(sc);
var argsArray = payload.putArray("args");
args.forEach(argsArray::add);
payload.put("tab_title",configuration.getColoredTitle());
payload.put("type", "tab");
payload.put("logo_alpha", 0.01);
payload.put("logo", XPipeInstallation.getLocalDefaultInstallationIcon().toString());
var json = JsonNodeFactory.instance.objectNode();
json.put("cmd", "launch");
json.set("payload", payload);
json.putArray("version").add(0).add(14).add(2);
var jsonString = json.toString();
var echoString = "'\\eP@kitty-cmd" + jsonString + "\\e\\\\'";
sc.executeSimpleCommand(CommandBuilder.of().add("echo", "-en", echoString, "|").add(socketWrite).addFile(getSocket()));
}
}
private static void closeInitial( CommandBuilder socketWrite) throws Exception {
try (var sc = LocalShell.getShell().start()) {
var payload = JsonNodeFactory.instance.objectNode();
payload.put("match", "not recent:0");
var json = JsonNodeFactory.instance.objectNode();
json.put("cmd", "close-tab");
json.set("payload", payload);
json.putArray("version").add(0).add(14).add(2);
var jsonString = json.toString();
var echoString = "'\\eP@kitty-cmd" + jsonString + "\\e\\\\'";
sc.executeSimpleCommand(CommandBuilder.of().add("echo", "-en", echoString, "|").add(socketWrite).addFile(getSocket()));
}
@Override
default boolean supportsColoredTitle() {
return true;
}
class Linux implements KittyTerminalType {
@ -107,11 +113,19 @@ public interface KittyTerminalType extends ExternalTerminalType {
private boolean prepare() throws Exception {
var socket = getSocket();
try (var sc = LocalShell.getShell().start()) {
if (sc.executeSimpleBooleanCommand("test -w " + sc.getShellDialect().fileArgument(socket))) {
if (sc.executeSimpleBooleanCommand(
"test -w " + sc.getShellDialect().fileArgument(socket))) {
return false;
}
sc.executeSimpleCommand(CommandBuilder.of().add("kitty").add("-o", "allow_remote_control=socket-only", "--listen-on", "unix:" + getSocket(), "--detach"));
sc.executeSimpleCommand(CommandBuilder.of()
.add("kitty")
.add(
"-o",
"allow_remote_control=socket-only",
"--listen-on",
"unix:" + getSocket(),
"--detach"));
ThreadHelper.sleep(1500);
return true;
}
@ -120,7 +134,9 @@ public interface KittyTerminalType extends ExternalTerminalType {
class MacOs extends MacOsType implements KittyTerminalType {
public MacOs() {super("app.kitty", "kitty");}
public MacOs() {
super("app.kitty", "kitty");
}
@Override
public void launch(LaunchConfiguration configuration) throws Exception {
@ -139,11 +155,14 @@ public interface KittyTerminalType extends ExternalTerminalType {
private boolean prepare() throws Exception {
var socket = getSocket();
try (var sc = LocalShell.getShell().start()) {
if (sc.executeSimpleBooleanCommand("test -w " + sc.getShellDialect().fileArgument(socket))) {
if (sc.executeSimpleBooleanCommand(
"test -w " + sc.getShellDialect().fileArgument(socket))) {
return false;
}
sc.executeSimpleCommand(CommandBuilder.of().add("open", "-a", "kitty.app", "--args").add("-o", "allow_remote_control=socket-only", "--listen-on", "unix:" + getSocket()));
sc.executeSimpleCommand(CommandBuilder.of()
.add("open", "-a", "kitty.app", "--args")
.add("-o", "allow_remote_control=socket-only", "--listen-on", "unix:" + getSocket()));
ThreadHelper.sleep(1000);
return true;
}

View file

@ -10,52 +10,14 @@ import java.util.Optional;
public interface TabbyTerminalType extends ExternalTerminalType {
static class Windows extends ExternalTerminalType.WindowsType implements TabbyTerminalType {
public Windows() {
super("app.tabby", "Tabby.exe");
}
@Override
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
// Tabby has a very weird handling of output, even detaching with start does not prevent it from printing
if (configuration.getScriptDialect().equals(ShellDialects.CMD)) {
// It also freezes with any other input than .bat files, why?
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of()
.addFile(file.toString())
.add("run")
.addFile(configuration.getScriptFile())
.discardOutput());
}
// This is probably not going to work as it does not launch a bat file
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of()
.addFile(file.toString())
.add("run")
.add(configuration.getDialectLaunchCommand())
.discardOutput());
}
@Override
protected Optional<Path> determineInstallation() {
var perUser = WindowsRegistry.readString(WindowsRegistry.HKEY_CURRENT_USER, "SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5",
"InstallLocation").map(p -> p + "\\Tabby.exe").map(Path::of);
if (perUser.isPresent()) {
return perUser;
}
var systemWide = WindowsRegistry.readString(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5",
"InstallLocation").map(p -> p + "\\Tabby.exe").map(Path::of);
return systemWide;
}
}
ExternalTerminalType TABBY_WINDOWS = new Windows();
ExternalTerminalType TABBY_MAC_OS = new MacOs();
@Override
default boolean supportsTabs() {
return true;
}
@Override
default String getWebsite() {
return "https://tabby.sh";
@ -67,13 +29,58 @@ public interface TabbyTerminalType extends ExternalTerminalType {
}
@Override
default boolean supportsTabs() {
default boolean supportsColoredTitle() {
return true;
}
static class Windows extends ExternalTerminalType.WindowsType implements TabbyTerminalType {
public Windows() {
super("app.tabby", "Tabby.exe");
}
@Override
default boolean supportsColoredTitle() {
return true;
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
// Tabby has a very weird handling of output, even detaching with start does not prevent it from printing
if (configuration.getScriptDialect().equals(ShellDialects.CMD)) {
// It also freezes with any other input than .bat files, why?
LocalShell.getShell()
.executeSimpleCommand(CommandBuilder.of()
.addFile(file.toString())
.add("run")
.addFile(configuration.getScriptFile())
.discardOutput());
}
// This is probably not going to work as it does not launch a bat file
LocalShell.getShell()
.executeSimpleCommand(CommandBuilder.of()
.addFile(file.toString())
.add("run")
.add(configuration.getDialectLaunchCommand())
.discardOutput());
}
@Override
protected Optional<Path> determineInstallation() {
var perUser = WindowsRegistry.readString(
WindowsRegistry.HKEY_CURRENT_USER,
"SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5",
"InstallLocation")
.map(p -> p + "\\Tabby.exe")
.map(Path::of);
if (perUser.isPresent()) {
return perUser;
}
var systemWide = WindowsRegistry.readString(
WindowsRegistry.HKEY_LOCAL_MACHINE,
"SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5",
"InstallLocation")
.map(p -> p + "\\Tabby.exe")
.map(Path::of);
return systemWide;
}
}
class MacOs extends MacOsType implements TabbyTerminalType {

View file

@ -10,47 +10,15 @@ import java.util.Optional;
public interface WezTerminalType extends ExternalTerminalType {
static class Windows extends WindowsType implements WezTerminalType {
public Windows() {
super("app.wezterm", "wezterm-gui");
}
@Override
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of().addFile(file.toString()).add("start").add(configuration.getDialectLaunchCommand()));
}
@Override
protected Optional<Path> determineInstallation() {
Optional<String> launcherDir;
launcherDir = WindowsRegistry.readString(
WindowsRegistry.HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{BCF6F0DA-5B9A-408D-8562-F680AE6E1EAF}_is1",
"InstallLocation")
.map(p -> p + "\\wezterm-gui.exe");
return launcherDir.map(Path::of);
}
}
static class Linux extends SimplePathType implements WezTerminalType {
public Linux() {
super("app.wezterm", "wezterm-gui", true);
}
@Override
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
return CommandBuilder.of().add("start").addFile(configuration.getScriptFile());
}
}
ExternalTerminalType WEZTERM_WINDOWS = new Windows();
ExternalTerminalType WEZTERM_LINUX = new Linux();
ExternalTerminalType WEZTERM_MAC_OS = new MacOs();
@Override
default boolean supportsTabs() {
return false;
}
@Override
default String getWebsite() {
return "https://wezfurlong.org/wezterm/index.html";
@ -62,13 +30,47 @@ public interface WezTerminalType extends ExternalTerminalType {
}
@Override
default boolean supportsTabs() {
return false;
default boolean supportsColoredTitle() {
return true;
}
static class Windows extends WindowsType implements WezTerminalType {
public Windows() {
super("app.wezterm", "wezterm-gui");
}
@Override
default boolean supportsColoredTitle() {
return true;
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
LocalShell.getShell()
.executeSimpleCommand(CommandBuilder.of()
.addFile(file.toString())
.add("start")
.add(configuration.getDialectLaunchCommand()));
}
@Override
protected Optional<Path> determineInstallation() {
Optional<String> launcherDir;
launcherDir = WindowsRegistry.readString(
WindowsRegistry.HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{BCF6F0DA-5B9A-408D-8562-F680AE6E1EAF}_is1",
"InstallLocation")
.map(p -> p + "\\wezterm-gui.exe");
return launcherDir.map(Path::of);
}
}
static class Linux extends SimplePathType implements WezTerminalType {
public Linux() {
super("app.wezterm", "wezterm-gui", true);
}
@Override
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
return CommandBuilder.of().add("start").addFile(configuration.getScriptFile());
}
}
class MacOs extends MacOsType implements WezTerminalType {

View file

@ -10,23 +10,7 @@ import java.nio.file.Path;
public interface WindowsTerminalType extends ExternalTerminalType {
@Override
default boolean isRecommended() {
return true;
}
@Override
default boolean supportsTabs() {
return true;
}
@Override
default boolean supportsColoredTitle() {
return false;
}
public static final ExternalTerminalType WINDOWS_TERMINAL = new Standard();
public static final ExternalTerminalType WINDOWS_TERMINAL_PREVIEW = new Preview();
private static CommandBuilder toCommand(ExternalTerminalType.LaunchConfiguration configuration) throws Exception {
@ -48,9 +32,26 @@ public interface WindowsTerminalType extends ExternalTerminalType {
return cmd.add("--title").addQuoted(fixedName).add(toExec);
}
@Override
default boolean supportsTabs() {
return true;
}
@Override
default boolean isRecommended() {
return true;
}
@Override
default boolean supportsColoredTitle() {
return false;
}
class Standard extends SimplePathType implements WindowsTerminalType {
public Standard() {super("app.windowsTerminal", "wt.exe", false);}
public Standard() {
super("app.windowsTerminal", "wt.exe", false);
}
@Override
public String getWebsite() {

View file

@ -68,7 +68,9 @@ public class DesktopShortcuts {
.createTextFileWriteCommand(pc, "APPL????", base + "/Contents/PkgInfo")
.execute();
pc.getShellDialect()
.createTextFileWriteCommand(pc, """
.createTextFileWriteCommand(
pc,
"""
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
@ -77,7 +79,8 @@ public class DesktopShortcuts {
<string>icon.icns</string>
</dict>
</plist>
""", base + "/Contents/Info.plist")
""",
base + "/Contents/Info.plist")
.execute();
pc.executeSimpleCommand("cp \"" + icon + "\" \"" + base + "/Contents/Resources/icon.icns\"");
}

View file

@ -40,7 +40,19 @@ public class InputHelper {
public static void onNavigationInput(EventTarget target, Consumer<Boolean> r) {
target.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
var c = event.getCode();
var list = List.of(KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP, KeyCode.DOWN, KeyCode.SPACE, KeyCode.ENTER, KeyCode.SHIFT, KeyCode.TAB, KeyCode.NUMPAD2, KeyCode.NUMPAD4, KeyCode.NUMPAD6, KeyCode.NUMPAD8);
var list = List.of(
KeyCode.LEFT,
KeyCode.RIGHT,
KeyCode.UP,
KeyCode.DOWN,
KeyCode.SPACE,
KeyCode.ENTER,
KeyCode.SHIFT,
KeyCode.TAB,
KeyCode.NUMPAD2,
KeyCode.NUMPAD4,
KeyCode.NUMPAD6,
KeyCode.NUMPAD8);
r.accept(list.stream().anyMatch(keyCode -> keyCode == c));
});
target.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {

View file

@ -41,6 +41,5 @@ public abstract class LicenseProvider {
.findFirst()
.orElseThrow(() -> ExtensionException.corrupt("Missing license provider"));
}
}
}

View file

@ -26,15 +26,15 @@ public enum PlatformState {
public static void teardown() {
// This is bad and can get sometimes stuck
// PlatformThread.runLaterIfNeededBlocking(() -> {
// try {
// // Fix to preserve clipboard contents after shutdown
// var string = Clipboard.getSystemClipboard().getString();
// var s = new StringSelection(string);
// Toolkit.getDefaultToolkit().getSystemClipboard().setContents(s, s);
// } catch (IllegalStateException ignored) {
// }
// });
// PlatformThread.runLaterIfNeededBlocking(() -> {
// try {
// // Fix to preserve clipboard contents after shutdown
// var string = Clipboard.getSystemClipboard().getString();
// var s = new StringSelection(string);
// Toolkit.getDefaultToolkit().getSystemClipboard().setContents(s, s);
// } catch (IllegalStateException ignored) {
// }
// });
Platform.exit();
setCurrent(PlatformState.EXITED);

View file

@ -13,6 +13,8 @@ import java.util.stream.Collectors;
@Value
public class RdpConfig {
Map<String, TypedValue> content;
public static RdpConfig parseFile(String file) throws IOException {
var content = Files.readString(Path.of(file));
return parseContent(content);
@ -30,7 +32,6 @@ public class RdpConfig {
map.put(split[0].trim(), new RdpConfig.TypedValue("s", split[1].trim()));
}
if (split.length == 3) {
map.put(split[0].trim(), new RdpConfig.TypedValue(split[1].trim(), split[2].trim()));
}
@ -38,14 +39,6 @@ public class RdpConfig {
return new RdpConfig(map);
}
@Value
public static class TypedValue {
String type;
String value;
}
Map<String, TypedValue> content;
public RdpConfig overlay(Map<String, TypedValue> override) {
var newMap = new LinkedHashMap<>(content);
newMap.putAll(override);
@ -53,12 +46,21 @@ public class RdpConfig {
}
public String toString() {
return content.entrySet().stream().map(e -> {
return e.getKey() + ":" + e.getValue().getType() + ":" + e.getValue().getValue();
}).collect(Collectors.joining("\n"));
return content.entrySet().stream()
.map(e -> {
return e.getKey() + ":" + e.getValue().getType() + ":"
+ e.getValue().getValue();
})
.collect(Collectors.joining("\n"));
}
public Optional<TypedValue> get(String key) {
return Optional.ofNullable(content.get(key));
}
@Value
public static class TypedValue {
String type;
String value;
}
}

View file

@ -104,7 +104,8 @@ public class ScriptHelper {
}
@SneakyThrows
public static FilePath createExecScript(ShellDialect type, ShellControl processControl, FilePath file, String content) {
public static FilePath createExecScript(
ShellDialect type, ShellControl processControl, FilePath file, String content) {
content = type.prepareScriptContent(content);
TrackEvent.withTrace("Writing exec script")

View file

@ -59,7 +59,8 @@ public class ShellTemp {
var d = proc.getShellDialect();
var systemTemp = proc.getSystemTemporaryDirectory();
if (!d.directoryExists(proc, systemTemp.toString()).executeAndCheck() || !checkDirectoryPermissions(proc, systemTemp.toString())) {
if (!d.directoryExists(proc, systemTemp.toString()).executeAndCheck()
|| !checkDirectoryPermissions(proc, systemTemp.toString())) {
throw ErrorEvent.expected(new IOException("No permissions to access %s".formatted(systemTemp)));
}

View file

@ -38,8 +38,7 @@ public abstract class StringSource {
try (var sc = host.control().start()) {
var path = file.toAbsoluteFilePath(sc);
if (!sc.getShellDialect().createFileExistsCommand(sc, path).executeAndCheck()) {
throw ErrorEvent.expected(
new IllegalArgumentException("File " + path + " does not exist"));
throw ErrorEvent.expected(new IllegalArgumentException("File " + path + " does not exist"));
}
var abs = file.toAbsoluteFilePath(sc);

View file

@ -18,14 +18,23 @@ import java.util.UUID;
public class TerminalLauncher {
public static void openDirect(String title, FailableFunction<ShellControl,String, Exception> command) throws Exception {
public static void openDirect(String title, FailableFunction<ShellControl, String, Exception> command)
throws Exception {
try (var sc = LocalShell.getShell().start()) {
var type = AppPrefs.get().terminalType().getValue();
if (type == null) {
throw ErrorEvent.expected(new IllegalStateException(AppI18n.get("noTerminalSet")));
}
var script = ScriptHelper.constructTerminalInitFile(sc.getShellDialect(), sc, ignored -> null, List.of(),
command.apply(sc), new TerminalInitScriptConfig(title, type.shouldClear() && AppPrefs.get().clearTerminalOnInit().get()));
var script = ScriptHelper.constructTerminalInitFile(
sc.getShellDialect(),
sc,
ignored -> null,
List.of(),
command.apply(sc),
new TerminalInitScriptConfig(
title,
type.shouldClear()
&& AppPrefs.get().clearTerminalOnInit().get()));
var config = new ExternalTerminalType.LaunchConfiguration(null, title, title, script, sc.getShellDialect());
type.launch(config);
}

View file

@ -15,18 +15,6 @@ import java.lang.reflect.Method;
@Getter
public class WindowControl {
public interface DwmSupport extends Library {
DwmSupport INSTANCE = Native.load("dwmapi", DwmSupport.class);
WinNT.HRESULT DwmSetWindowAttribute(
WinDef.HWND hwnd,
int dwAttribute,
PointerType pvAttribute,
int cbAttribute
);
}
private final WinDef.HWND windowHandle;
public WindowControl(Window stage) throws Exception {
@ -48,16 +36,20 @@ public class WindowControl {
}
public void move(int x, int y, int w, int h) {
User32.INSTANCE.SetWindowPos(windowHandle, new WinDef.HWND(), x,y,w,h, 0);
User32.INSTANCE.SetWindowPos(windowHandle, new WinDef.HWND(), x, y, w, h, 0);
}
public void setWindowAttribute(int attribute, boolean attributeValue) {
DwmSupport.INSTANCE.DwmSetWindowAttribute(
windowHandle,
attribute,
new WinDef.BOOLByReference(new WinDef.BOOL(attributeValue)),
WinDef.BOOL.SIZE
);
windowHandle, attribute, new WinDef.BOOLByReference(new WinDef.BOOL(attributeValue)), WinDef.BOOL.SIZE);
User32.INSTANCE.UpdateWindow(windowHandle);
}
public interface DwmSupport extends Library {
DwmSupport INSTANCE = Native.load("dwmapi", DwmSupport.class);
WinNT.HRESULT DwmSetWindowAttribute(
WinDef.HWND hwnd, int dwAttribute, PointerType pvAttribute, int cbAttribute);
}
}

View file

@ -117,7 +117,8 @@ open module io.xpipe.app {
provides Module with
StorageJacksonModule;
provides ModuleLayerLoader with
MessageExchangeImpls.Loader, DataStoreProviders.Loader,
MessageExchangeImpls.Loader,
DataStoreProviders.Loader,
ActionProvider.Loader,
PrefsProvider.Loader,
BrowserAction.Loader,

View file

@ -5,8 +5,7 @@ import java.util.function.Consumer;
public interface ModuleLayerLoader {
static void loadAll(
ModuleLayer layer, Consumer<Throwable> errorHandler) {
static void loadAll(ModuleLayer layer, Consumer<Throwable> errorHandler) {
ServiceLoader.load(layer, ModuleLayerLoader.class).stream().forEach(moduleLayerLoaderProvider -> {
var instance = moduleLayerLoaderProvider.get();
try {
@ -20,5 +19,4 @@ public interface ModuleLayerLoader {
default void init(ModuleLayer layer) {}
default void reset() {}
}

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellControl;

View file

@ -1,9 +1,9 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.BranchAction;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.process.CommandBuilder;
import io.xpipe.core.process.OsType;

View file

@ -1,9 +1,9 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.BrowserClipboard;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;

View file

@ -1,11 +1,11 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.BranchAction;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.browser.action.BrowserActionFormatter;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.ClipboardHelper;
import io.xpipe.core.store.FileKind;
@ -133,9 +133,8 @@ public class CopyPathAction implements BrowserAction, BranchAction {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return new SimpleObjectProperty<>(BrowserActionFormatter.centerEllipsis(
FileNames.getFileName(entries.getFirst()
.getRawFileEntry()
.getPath()),
FileNames.getFileName(
entries.getFirst().getRawFileEntry().getPath()),
50));
}
@ -156,9 +155,8 @@ public class CopyPathAction implements BrowserAction, BranchAction {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return new SimpleObjectProperty<>(BrowserActionFormatter.centerEllipsis(
FileNames.getFileName(entries.getFirst()
.getRawFileEntry()
.getPath()),
FileNames.getFileName(
entries.getFirst().getRawFileEntry().getPath()),
50));
}

View file

@ -1,10 +1,10 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserAlerts;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.FileSystemHelper;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
@ -46,7 +46,9 @@ public class DeleteAction implements LeafAction {
@Override
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("deleteFile",entries.stream()
return AppI18n.observable(
"deleteFile",
entries.stream()
.allMatch(browserEntry ->
browserEntry.getRawFileEntry().getKind() == FileKind.LINK)
? " link"

View file

@ -1,9 +1,9 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.FileSystemHelper;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.FileOpener;

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.browser.icon.BrowserIconFileType;
import io.xpipe.app.browser.icon.BrowserIcons;
import javafx.scene.Node;

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;

View file

@ -1,9 +1,9 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.BrowserActionFormatter;
import io.xpipe.app.browser.action.MultiExecuteAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.icon.BrowserIconFileType;
import io.xpipe.core.process.CommandBuilder;
import io.xpipe.core.process.ShellControl;
@ -31,7 +31,9 @@ public class JarAction extends MultiExecuteAction implements JavaAction, FileTyp
@Override
protected CommandBuilder createCommand(ShellControl sc, OpenFileSystemModel model, BrowserEntry entry) {
return CommandBuilder.of().add("java", "-jar").addFile(entry.getRawFileEntry().getPath());
return CommandBuilder.of()
.add("java", "-jar")
.addFile(entry.getRawFileEntry().getPath());
}
@Override

View file

@ -1,9 +1,9 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.BrowserActionFormatter;
import io.xpipe.app.browser.action.ToFileCommandAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.icon.BrowserIconFileType;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;

View file

@ -1,10 +1,10 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.BranchAction;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.icon.BrowserIcons;
import io.xpipe.app.comp.base.ModalOverlayComp;
import io.xpipe.app.core.AppI18n;

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.session.BrowserSessionModel;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
@ -17,7 +17,8 @@ public class OpenDirectoryInNewTabAction implements LeafAction {
@Override
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (model.getBrowserModel() instanceof BrowserSessionModel bm) {
bm.openFileSystemAsync(model.getEntry(), m -> entries.getFirst().getRawFileEntry().getPath(), null);
bm.openFileSystemAsync(
model.getEntry(), m -> entries.getFirst().getRawFileEntry().getPath(), null);
}
}
@ -43,7 +44,8 @@ public class OpenDirectoryInNewTabAction implements LeafAction {
@Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return model.getBrowserModel() instanceof BrowserSessionModel && entries.size() == 1
return model.getBrowserModel() instanceof BrowserSessionModel
&& entries.size() == 1
&& entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.DIRECTORY);
}
}

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.FileOpener;
import io.xpipe.core.store.FileKind;

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.FileOpener;
import io.xpipe.core.process.OsType;

View file

@ -1,8 +1,8 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.LocalShell;
import io.xpipe.core.process.OsType;

Some files were not shown because too many files have changed in this diff Show more