mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 23:20:23 +00:00
Allow for the creation of connection desktop shortcuts for all distributions
This commit is contained in:
parent
2763ca40c8
commit
e2308366fb
7 changed files with 122 additions and 93 deletions
|
@ -38,7 +38,13 @@ public class FileBrowserComp extends SimpleComp {
|
|||
@Override
|
||||
protected Region createSimple() {
|
||||
var bookmarksList = new BookmarkList(model).createRegion();
|
||||
var localDownloadStage = new LocalFileTransferComp(model.getLocalTransfersStage()).createRegion();
|
||||
var localDownloadStage = new LocalFileTransferComp(model.getLocalTransfersStage()).hide(Bindings.createBooleanBinding(() -> {
|
||||
if (model.getOpenFileSystems().size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !model.getMode().equals(FileBrowserModel.Mode.BROWSER);
|
||||
}, PlatformThread.sync(model.getOpenFileSystems()))).createRegion();
|
||||
var vertical = new VBox(bookmarksList, localDownloadStage);
|
||||
vertical.setFillWidth(true);
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@ package io.xpipe.app.browser;
|
|||
|
||||
import io.xpipe.app.comp.base.LoadingOverlayComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.fxcomps.impl.LabelComp;
|
||||
import io.xpipe.app.fxcomps.impl.StackComp;
|
||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
|
@ -17,6 +19,7 @@ import javafx.scene.image.WritableImage;
|
|||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.input.Dragboard;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Region;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
|
@ -38,67 +41,85 @@ public class LocalFileTransferComp extends SimpleComp {
|
|||
.visible(BindingsHelper.persist(Bindings.isEmpty(stage.getItems())));
|
||||
var backgroundStack =
|
||||
new StackComp(List.of(background)).grow(true, true).styleClass("download-background");
|
||||
|
||||
var binding = BindingsHelper.mappedContentBinding(stage.getItems(), item -> item.getFileEntry());
|
||||
var list = new SelectedFileListComp(binding).apply(struc -> struc.get().setMinHeight(200));
|
||||
var list = new SelectedFileListComp(binding).apply(struc -> struc.get().setMinHeight(200)).grow(false, true);
|
||||
var dragNotice = new LabelComp(AppI18n.observable("dragFiles"))
|
||||
.apply(struc -> struc.get().setGraphic(new FontIcon("mdi2e-export")))
|
||||
.hide(BindingsHelper.persist(Bindings.isEmpty(stage.getItems())))
|
||||
.grow(true, false)
|
||||
.apply(struc -> struc.get().setPadding(new Insets(8)));
|
||||
var loading = new LoadingOverlayComp(
|
||||
new VerticalComp(List.of(list, dragNotice)), PlatformThread.sync(stage.getDownloading()));
|
||||
var stack = new StackComp(List.of(backgroundStack, loading)).apply(struc -> {
|
||||
struc.get().setOnDragOver(event -> {
|
||||
// Accept drops from inside the app window
|
||||
if (event.getGestureSource() != null) {
|
||||
event.acceptTransferModes(TransferMode.ANY);
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
struc.get().setOnDragDropped(event -> {
|
||||
if (event.getGestureSource() != null) {
|
||||
var files = FileBrowserClipboard.retrieveDrag(event.getDragboard())
|
||||
.getEntries();
|
||||
stage.drop(files);
|
||||
event.setDropCompleted(true);
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
struc.get().setOnDragDetected(event -> {
|
||||
if (stage.getDownloading().get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var files = stage.getItems().stream()
|
||||
.map(item -> {
|
||||
try {
|
||||
return item.getLocalFile().toRealPath().toFile();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
Dragboard db = struc.get().startDragAndDrop(TransferMode.MOVE);
|
||||
var cc = new ClipboardContent();
|
||||
cc.putFiles(files);
|
||||
db.setContent(cc);
|
||||
|
||||
var r = new SelectedFileListComp(FXCollections.observableList(stage.getItems().stream()
|
||||
.map(item -> item.getFileEntry())
|
||||
.toList()))
|
||||
.createRegion();
|
||||
new Scene(r);
|
||||
WritableImage image = r.snapshot(new SnapshotParameters(), null);
|
||||
db.setDragView(image, -20, 15);
|
||||
|
||||
event.setDragDetect(true);
|
||||
event.consume();
|
||||
});
|
||||
struc.get().setOnDragDone(event -> {
|
||||
stage.getItems().clear();
|
||||
event.consume();
|
||||
});
|
||||
var clearButton = new IconButtonComp("mdi2d-delete", () -> {
|
||||
stage.getItems().clear();
|
||||
})
|
||||
.hide(BindingsHelper.persist(Bindings.isEmpty(stage.getItems())));
|
||||
var clearPane = Comp.derive(clearButton, button -> {
|
||||
var p = new AnchorPane(button);
|
||||
AnchorPane.setRightAnchor(button, 10.0);
|
||||
AnchorPane.setTopAnchor(button, 10.0);
|
||||
return p;
|
||||
});
|
||||
|
||||
var listBox = new VerticalComp(List.of(list, dragNotice));
|
||||
var stack = new LoadingOverlayComp(
|
||||
new StackComp(List.of(backgroundStack, listBox, clearPane)).apply(struc -> {
|
||||
struc.get().setOnDragOver(event -> {
|
||||
// Accept drops from inside the app window
|
||||
if (event.getGestureSource() != null && event.getGestureSource() != struc.get()) {
|
||||
event.acceptTransferModes(TransferMode.ANY);
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
struc.get().setOnDragDropped(event -> {
|
||||
if (event.getGestureSource() != null) {
|
||||
var files = FileBrowserClipboard.retrieveDrag(event.getDragboard())
|
||||
.getEntries();
|
||||
stage.drop(files);
|
||||
event.setDropCompleted(true);
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
struc.get().setOnDragDetected(event -> {
|
||||
if (stage.getDownloading().get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var files = stage.getItems().stream()
|
||||
.map(item -> {
|
||||
try {
|
||||
return item.getLocalFile().toRealPath().toFile();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
Dragboard db = struc.get().startDragAndDrop(TransferMode.MOVE);
|
||||
var cc = new ClipboardContent();
|
||||
cc.putFiles(files);
|
||||
db.setContent(cc);
|
||||
|
||||
var r = new SelectedFileListComp(FXCollections.observableList(stage.getItems().stream()
|
||||
.map(item -> item.getFileEntry())
|
||||
.toList()))
|
||||
.createRegion();
|
||||
new Scene(r);
|
||||
WritableImage image = r.snapshot(new SnapshotParameters(), null);
|
||||
db.setDragView(image, -20, 15);
|
||||
|
||||
event.setDragDetect(true);
|
||||
event.consume();
|
||||
});
|
||||
struc.get().setOnDragDone(event -> {
|
||||
if (!event.isAccepted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
stage.getItems().clear();
|
||||
event.consume();
|
||||
});
|
||||
}),
|
||||
PlatformThread.sync(stage.getDownloading()));
|
||||
return stack.createRegion();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.util.BusyProperty;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.impl.FileNames;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
|
@ -13,6 +13,8 @@ import org.apache.commons.io.FileUtils;
|
|||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Value
|
||||
public class LocalFileTransferStage {
|
||||
|
@ -20,8 +22,16 @@ public class LocalFileTransferStage {
|
|||
private static final Path TEMP =
|
||||
FileUtils.getTempDirectory().toPath().resolve("xpipe").resolve("download");
|
||||
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = Executors.defaultThreadFactory().newThread(r);
|
||||
t.setDaemon(true);
|
||||
t.setName("file downloader");
|
||||
return t;
|
||||
});
|
||||
|
||||
@Value
|
||||
public static class Item {
|
||||
String name;
|
||||
FileSystem.FileEntry fileEntry;
|
||||
Path localFile;
|
||||
BooleanProperty finishedDownload = new SimpleBooleanProperty();
|
||||
|
@ -32,15 +42,24 @@ public class LocalFileTransferStage {
|
|||
|
||||
public void drop(List<FileSystem.FileEntry> entries) {
|
||||
entries.forEach(entry -> {
|
||||
Path file = TEMP.resolve(FileNames.getFileName(entry.getPath()));
|
||||
var item = new Item(entry, file);
|
||||
var name = FileNames.getFileName(entry.getPath());
|
||||
if (items.stream().anyMatch(item -> item.getName().equals(name))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Path file = TEMP.resolve(name);
|
||||
var item = new Item(name, entry, file);
|
||||
items.add(item);
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
FileUtils.forceMkdirParent(TEMP.toFile());
|
||||
try (var b = new BusyProperty(downloading)) {
|
||||
FileSystemHelper.dropFilesInto(FileSystemHelper.getLocal(TEMP),List.of(entry), false);
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
FileUtils.forceMkdirParent(TEMP.toFile());
|
||||
try (var b = new BusyProperty(downloading)) {
|
||||
FileSystemHelper.dropFilesInto(FileSystemHelper.getLocal(TEMP), List.of(entry), false);
|
||||
}
|
||||
item.finishedDownload.set(true);
|
||||
} catch (Throwable t) {
|
||||
ErrorEvent.fromThrowable(t).handle();
|
||||
}
|
||||
item.finishedDownload.set(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,19 +11,21 @@ public class DesktopShortcuts {
|
|||
|
||||
private static void createWindowsShortcut(String target, String name) throws Exception {
|
||||
var icon = XPipeInstallation.getLocalDefaultInstallationIcon();
|
||||
var shortcutTarget = XPipeInstallation.getCurrentInstallationBasePath().resolve(XPipeInstallation.getRelativeCliExecutablePath(OsType.WINDOWS));
|
||||
var content = String.format(
|
||||
"""
|
||||
set "TARGET=%s"
|
||||
set "SHORTCUT=%%HOMEDRIVE%%%%HOMEPATH%%\\Desktop\\%s.lnk"
|
||||
set PWS=powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile
|
||||
|
||||
%%PWS%% -Command "$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut('%%SHORTCUT%%'); $S.IconLocation='%s'; $S.TargetPath = '%%TARGET%%'; $S.Save()"
|
||||
%%PWS%% -Command "$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut('%%SHORTCUT%%'); $S.IconLocation='%s'; $S.TargetPath = '%%TARGET%%'; $S.Arguments = '%s'; $S.Save()"
|
||||
""",
|
||||
target, name, icon.toString());
|
||||
shortcutTarget, name, icon.toString(), target);
|
||||
LocalStore.getShell().executeSimpleCommand(content);
|
||||
}
|
||||
|
||||
private static void createLinuxShortcut(String target, String name) throws Exception {
|
||||
var exec = XPipeInstallation.getCurrentInstallationBasePath().resolve(XPipeInstallation.getRelativeCliExecutablePath(OsType.LINUX));
|
||||
var icon = XPipeInstallation.getLocalDefaultInstallationIcon();
|
||||
var content = String.format(
|
||||
"""
|
||||
|
@ -31,27 +33,27 @@ public class DesktopShortcuts {
|
|||
Type=Application
|
||||
Name=%s
|
||||
Comment=Open with X-Pipe
|
||||
TryExec=/opt/xpipe/app/bin/xpiped
|
||||
Exec=/opt/xpipe/cli/bin/xpipe open %s
|
||||
Exec="%s" open %s
|
||||
Icon=%s
|
||||
Terminal=false
|
||||
Categories=Utility;Development;Office;
|
||||
""",
|
||||
name, target, icon.toString());
|
||||
name, exec, target, icon.toString());
|
||||
var file = Path.of(System.getProperty("user.home") + "/Desktop/" + name + ".desktop");
|
||||
Files.writeString(file, content);
|
||||
file.toFile().setExecutable(true);
|
||||
}
|
||||
|
||||
private static void createMacOSShortcut(String target, String name) throws Exception {
|
||||
var exec = XPipeInstallation.getCurrentInstallationBasePath().resolve(XPipeInstallation.getRelativeCliExecutablePath(OsType.MACOS));
|
||||
var icon = XPipeInstallation.getLocalDefaultInstallationIcon();
|
||||
var base = System.getProperty("user.home") + "/Desktop/" + name + ".app";
|
||||
var content = String.format(
|
||||
"""
|
||||
#!/bin/bash
|
||||
open %s
|
||||
"%s" %s
|
||||
""",
|
||||
target);
|
||||
exec, target);
|
||||
|
||||
try (var pc = LocalStore.getShell()) {
|
||||
pc.executeSimpleCommand(pc.getShellDialect().getMkdirsCommand(base + "/Contents/MacOS"));
|
||||
|
|
|
@ -19,15 +19,15 @@ public final class HumanReadableFormat {
|
|||
public static final DateTimeFormatter HOUR_MINUTE = DateTimeFormatter.ofPattern("HH:mm");
|
||||
|
||||
public static String byteCount(long bytes) {
|
||||
if (-1000 < bytes && bytes < 1000) {
|
||||
if (-1024 < bytes && bytes < 1024) {
|
||||
return bytes + " B";
|
||||
}
|
||||
CharacterIterator ci = new StringCharacterIterator("kMGTPE");
|
||||
while (bytes <= -999_950 || bytes >= 999_950) {
|
||||
bytes /= 1000;
|
||||
while (bytes <= -1024 * 1024 || bytes >= 1024 * 1024) {
|
||||
bytes /= 1024;
|
||||
ci.next();
|
||||
}
|
||||
return String.format("%.1f %cB", bytes / 1000.0, ci.current());
|
||||
return String.format("%.1f %cB", bytes / 1024.0, ci.current());
|
||||
}
|
||||
|
||||
public static String date(LocalDateTime x) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.util.ModuleHelper;
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
|
@ -19,12 +18,6 @@ public interface XPipeDistributionType {
|
|||
TrackEvent.info("Development mode update executed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsURLs() {
|
||||
// Enabled for testing
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "development";
|
||||
|
@ -40,11 +33,6 @@ public interface XPipeDistributionType {
|
|||
@Override
|
||||
public void performUpdateAction() {}
|
||||
|
||||
@Override
|
||||
public boolean supportsURLs() {
|
||||
return OsType.getLocal().equals(OsType.MACOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "portable";
|
||||
|
@ -62,11 +50,6 @@ public interface XPipeDistributionType {
|
|||
TrackEvent.info("Update action called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsURLs() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "install";
|
||||
|
@ -89,7 +72,5 @@ public interface XPipeDistributionType {
|
|||
|
||||
void performUpdateAction();
|
||||
|
||||
boolean supportsURLs();
|
||||
|
||||
String getName();
|
||||
}
|
||||
|
|
|
@ -272,7 +272,7 @@ public class XPipeInstallation {
|
|||
|
||||
public static String getRelativeCliExecutablePath(OsType type) {
|
||||
if (type.equals(OsType.WINDOWS)) {
|
||||
return FileNames.join("cli", "xpipe.exe");
|
||||
return FileNames.join("cli", "bin", "xpipe.exe");
|
||||
} else if (type.equals(OsType.LINUX)) {
|
||||
return FileNames.join("cli", "bin", "xpipe");
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue