mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-06-23 08:30:38 +12:00
File browser transfer adjustments
This commit is contained in:
parent
6909019231
commit
324a48f157
|
@ -68,8 +68,9 @@ public class BrowserComp extends SimpleComp {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Also show on local
|
||||
if (model.getSelected().getValue() != null) {
|
||||
return model.getSelected().getValue().isLocal();
|
||||
// return model.getSelected().getValue().isLocal();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -35,7 +35,10 @@ public class BrowserFileListCompEntry {
|
|||
|
||||
public void onMouseClick(MouseEvent t) {
|
||||
if (item == null) {
|
||||
model.getSelection().clear();
|
||||
// Only clear for normal clicks
|
||||
if (t.isStillSincePress()) {
|
||||
model.getSelection().clear();
|
||||
}
|
||||
t.consume();
|
||||
return;
|
||||
}
|
||||
|
@ -85,7 +88,8 @@ public class BrowserFileListCompEntry {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (BrowserClipboard.currentDragClipboard == null) {
|
||||
BrowserClipboard.Instance cb = BrowserClipboard.currentDragClipboard;
|
||||
if (cb == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -94,7 +98,7 @@ public class BrowserFileListCompEntry {
|
|||
}
|
||||
|
||||
// Prevent drag and drops of files into the current directory
|
||||
if (BrowserClipboard.currentDragClipboard
|
||||
if (cb.getBaseDirectory() != null && cb
|
||||
.getBaseDirectory()
|
||||
.getPath()
|
||||
.equals(model.getFileSystemModel().getCurrentDirectory().getPath())
|
||||
|
@ -103,7 +107,7 @@ public class BrowserFileListCompEntry {
|
|||
}
|
||||
|
||||
// Prevent dropping items onto themselves
|
||||
if (item != null && BrowserClipboard.currentDragClipboard.getEntries().contains(item.getRawFileEntry())) {
|
||||
if (item != null && cb.getEntries().contains(item.getRawFileEntry())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public class BrowserModel {
|
|||
|
||||
private final ObservableList<OpenFileSystemModel> openFileSystems = FXCollections.observableArrayList();
|
||||
private final Property<OpenFileSystemModel> selected = new SimpleObjectProperty<>();
|
||||
private final BrowserTransferModel localTransfersStage = new BrowserTransferModel();
|
||||
private final BrowserTransferModel localTransfersStage = new BrowserTransferModel(this);
|
||||
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
|
||||
|
||||
public void restoreState(BrowserSavedState state) {
|
||||
|
|
|
@ -6,22 +6,31 @@ import io.xpipe.app.core.AppStyle;
|
|||
import io.xpipe.app.core.AppWindowHelper;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.SvgCacheComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.core.impl.FileNames;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.SnapshotParameters;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.OverrunStyle;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.paint.Color;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Value
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AllArgsConstructor
|
||||
public class BrowserSelectionListComp extends SimpleComp {
|
||||
|
||||
public static Image snapshot(ObservableList<FileSystem.FileEntry> list) {
|
||||
|
@ -36,17 +45,25 @@ public class BrowserSelectionListComp extends SimpleComp {
|
|||
}
|
||||
|
||||
ObservableList<FileSystem.FileEntry> list;
|
||||
Function<FileSystem.FileEntry, ObservableValue<String>> nameTransformation;
|
||||
|
||||
public BrowserSelectionListComp(ObservableList<FileSystem.FileEntry> list) {
|
||||
this(list, entry -> new SimpleStringProperty(FileNames.getFileName(entry.getPath())));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var c = new ListBoxViewComp<>(list, list, entry -> {
|
||||
return Comp.of(() -> {
|
||||
var icon = new ImageView(FileIconManager.getSvgCache()
|
||||
.getCached(FileIconManager.getFileIcon(entry, false))
|
||||
.orElse(null));
|
||||
icon.setFitWidth(20);
|
||||
icon.setFitHeight(20);
|
||||
var l = new Label(FileNames.getFileName(entry.getPath()), icon);
|
||||
var wv = new SvgCacheComp(
|
||||
new SimpleDoubleProperty(20),
|
||||
new SimpleDoubleProperty(20),
|
||||
new SimpleStringProperty(FileIconManager.getFileIcon(entry, false)),
|
||||
FileIconManager.getSvgCache())
|
||||
.createRegion();
|
||||
var l = new Label(null, wv);
|
||||
l.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
|
||||
l.textProperty().bind(PlatformThread.sync(nameTransformation.apply(entry)));
|
||||
return l;
|
||||
});
|
||||
})
|
||||
|
|
|
@ -5,15 +5,15 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.augment.DragPseudoClassAugment;
|
||||
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;
|
||||
import io.xpipe.app.fxcomps.impl.*;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.core.impl.FileNames;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.input.Dragboard;
|
||||
import javafx.scene.input.TransferMode;
|
||||
|
@ -41,27 +41,45 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
new StackComp(List.of(background)).grow(true, true).styleClass("download-background");
|
||||
|
||||
var binding = BindingsHelper.mappedContentBinding(stage.getItems(), item -> item.getFileEntry());
|
||||
var list = new BrowserSelectionListComp(binding)
|
||||
var list = new BrowserSelectionListComp(binding, entry -> Bindings.createStringBinding(() -> {
|
||||
var sourceItem = stage.getItems().stream().filter(item -> item.getFileEntry() == entry).findAny();
|
||||
if (sourceItem.isEmpty()) {
|
||||
return "?";
|
||||
}
|
||||
var name = sourceItem.get().getFinishedDownload().get() ? "Local" : DataStorage.get().getStoreDisplayName(entry.getFileSystem().getStore()).orElse("?");
|
||||
return FileNames.getFileName(entry.getPath()) + " (" + name + ")";
|
||||
}, stage.getAllDownloaded()))
|
||||
.apply(struc -> struc.get().setMinHeight(150))
|
||||
.grow(false, true);
|
||||
var dragNotice = new LabelComp(AppI18n.observable("dragFiles"))
|
||||
var dragNotice = new LabelComp(stage.getAllDownloaded().flatMap(aBoolean -> aBoolean ? AppI18n.observable("dragLocalFiles") : AppI18n.observable("dragFiles")))
|
||||
.apply(struc -> struc.get().setGraphic(new FontIcon("mdi2e-export")))
|
||||
.hide(BindingsHelper.persist(Bindings.isEmpty(stage.getItems())))
|
||||
.hide(PlatformThread.sync(
|
||||
BindingsHelper.persist(Bindings.isEmpty(stage.getItems()))))
|
||||
.grow(true, false)
|
||||
.apply(struc -> struc.get().setPadding(new Insets(8)));
|
||||
|
||||
var clearButton = new IconButtonComp("mdi2d-delete", () -> {
|
||||
stage.getItems().clear();
|
||||
var downloadButton = new IconButtonComp("mdi2d-download", () -> {
|
||||
stage.download();
|
||||
})
|
||||
.hide(BindingsHelper.persist(Bindings.isEmpty(stage.getItems())))
|
||||
.disable(PlatformThread.sync(stage.getAllDownloaded()))
|
||||
.apply(new FancyTooltipAugment<>("downloadStageDescription"));
|
||||
var clearButton = new IconButtonComp("mdi2c-close", () -> {
|
||||
stage.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 clearPane = Comp.derive(
|
||||
new HorizontalComp(List.of(downloadButton, clearButton))
|
||||
.apply(struc -> struc.get().setSpacing(10)),
|
||||
button -> {
|
||||
var p = new AnchorPane(button);
|
||||
AnchorPane.setRightAnchor(button, 20.0);
|
||||
AnchorPane.setTopAnchor(button, 20.0);
|
||||
p.setPickOnBounds(false);
|
||||
return p;
|
||||
});
|
||||
|
||||
var listBox = new VerticalComp(List.of(list, dragNotice));
|
||||
var listBox = new VerticalComp(List.of(list, dragNotice)).padding(new Insets(10, 10, 5, 10));
|
||||
var stack = new LoadingOverlayComp(
|
||||
new StackComp(List.of(backgroundStack, listBox, clearPane))
|
||||
.apply(DragPseudoClassAugment.create())
|
||||
|
@ -87,6 +105,21 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
return;
|
||||
}
|
||||
|
||||
// Drag within browser
|
||||
if (!stage.getAllDownloaded().get()) {
|
||||
var selected = stage.getItems().stream().map(item -> item.getFileEntry()).toList();
|
||||
Dragboard db = struc.get().startDragAndDrop(TransferMode.COPY);
|
||||
db.setContent(BrowserClipboard.startDrag(null, selected));
|
||||
|
||||
Image image = BrowserSelectionListComp.snapshot(FXCollections.observableList(selected));
|
||||
db.setDragView(image, -20, 15);
|
||||
|
||||
event.setDragDetect(true);
|
||||
event.consume();
|
||||
return;
|
||||
}
|
||||
|
||||
// Drag outside browser
|
||||
var files = stage.getItems().stream()
|
||||
.map(item -> {
|
||||
try {
|
||||
|
|
|
@ -11,7 +11,9 @@ import javafx.collections.ObservableList;
|
|||
import lombok.Value;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -37,8 +39,19 @@ public class BrowserTransferModel {
|
|||
BooleanProperty finishedDownload = new SimpleBooleanProperty();
|
||||
}
|
||||
|
||||
BrowserModel browserModel;
|
||||
ObservableList<Item> items = FXCollections.observableArrayList();
|
||||
BooleanProperty downloading = new SimpleBooleanProperty();
|
||||
BooleanProperty allDownloaded = new SimpleBooleanProperty();
|
||||
|
||||
public void clear() {
|
||||
try {
|
||||
FileUtils.deleteDirectory(TEMP.toFile());
|
||||
} catch (IOException e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
}
|
||||
items.clear();
|
||||
}
|
||||
|
||||
public void drop(List<FileSystem.FileEntry> entries) {
|
||||
entries.forEach(entry -> {
|
||||
|
@ -50,18 +63,34 @@ public class BrowserTransferModel {
|
|||
Path file = TEMP.resolve(name);
|
||||
var item = new Item(name, entry, file);
|
||||
items.add(item);
|
||||
executor.submit(() -> {
|
||||
allDownloaded.set(false);
|
||||
});
|
||||
}
|
||||
|
||||
public void download() {
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
FileUtils.forceMkdir(TEMP.toFile());
|
||||
} catch (IOException e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
return;
|
||||
}
|
||||
|
||||
for (Item item : new ArrayList<>(items)) {
|
||||
try {
|
||||
FileUtils.forceMkdir(TEMP.toFile());
|
||||
try (var b = new BusyProperty(downloading)) {
|
||||
FileSystemHelper.dropFilesInto(FileSystemHelper.getLocal(TEMP), List.of(entry), false);
|
||||
FileSystemHelper.dropFilesInto(
|
||||
FileSystemHelper.getLocal(TEMP),
|
||||
List.of(item.getFileEntry()),
|
||||
true);
|
||||
}
|
||||
item.finishedDownload.set(true);
|
||||
} catch (Throwable t) {
|
||||
ErrorEvent.fromThrowable(t).handle();
|
||||
items.remove(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
allDownloaded.set(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,13 +32,13 @@ public class FileStoreChoiceComp extends SimpleComp {
|
|||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var fileProperty = new SimpleStringProperty(
|
||||
var filePathProperty = new SimpleStringProperty(
|
||||
selected.getValue() != null ? selected.getValue().getPath() : null);
|
||||
fileProperty.addListener((observable, oldValue, newValue) -> {
|
||||
filePathProperty.addListener((observable, oldValue, newValue) -> {
|
||||
setSelected(selected.getValue() != null ? selected.getValue().getFileSystem() : null, newValue);
|
||||
});
|
||||
selected.addListener((observable, oldValue, newValue) -> {
|
||||
fileProperty.setValue(newValue != null ? newValue.getPath() : null);
|
||||
filePathProperty.setValue(newValue != null ? newValue.getPath() : null);
|
||||
});
|
||||
|
||||
var fileSystemChoiceComp =
|
||||
|
@ -47,7 +47,7 @@ public class FileStoreChoiceComp extends SimpleComp {
|
|||
fileSystemChoiceComp.hide(new SimpleBooleanProperty(true));
|
||||
}
|
||||
|
||||
var fileNameComp = new TextFieldComp(fileProperty)
|
||||
var fileNameComp = new TextFieldComp(filePathProperty)
|
||||
.apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS))
|
||||
.styleClass(onlyLocal ? Styles.LEFT_PILL : Styles.CENTER_PILL)
|
||||
.grow(false, true);
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.app.fxcomps.impl;
|
|||
import io.xpipe.app.core.AppImages;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
@ -50,7 +51,7 @@ public class SvgCacheComp extends SimpleComp {
|
|||
var back = SvgView.create(webViewContent).createWebview();
|
||||
back.prefWidthProperty().bind(width);
|
||||
back.prefHeightProperty().bind(height);
|
||||
svgFile.addListener((observable, oldValue, newValue) -> {
|
||||
SimpleChangeListener.apply(svgFile, newValue -> {
|
||||
if (newValue == null) {
|
||||
back.setVisible(false);
|
||||
front.setVisible(false);
|
||||
|
@ -122,6 +123,8 @@ public class SvgCacheComp extends SimpleComp {
|
|||
// active.set(timer);
|
||||
});
|
||||
});
|
||||
svgFile.addListener((observable, oldValue, newValue) -> {
|
||||
});
|
||||
|
||||
var stack = new StackPane(back, front);
|
||||
stack.prefWidthProperty().bind(width);
|
||||
|
|
|
@ -11,6 +11,7 @@ lockCreationAlertTitle=Create Lock
|
|||
lockCreationAlertHeader=Set your new lock password
|
||||
finish=Finish
|
||||
error=Error
|
||||
downloadStageDescription=Downloads files to your local machine, so you can drag and drop them into your native desktop environment.
|
||||
ok=Ok
|
||||
search=Search
|
||||
newFile=New file
|
||||
|
@ -30,7 +31,8 @@ deleteAlertHeader=Do you want to delete the ($COUNT$) selected elements?
|
|||
selectedElements=Selected elements:
|
||||
mustNotBeEmpty=$NAME$ must not be empty
|
||||
download=Drop to download
|
||||
dragFiles=Drag files from here
|
||||
dragFiles=Drag files within browser
|
||||
dragLocalFiles=Drag local files from here
|
||||
null=$VALUE$ must be not null
|
||||
roots=Roots
|
||||
recent=Recent
|
||||
|
|
Loading…
Reference in a new issue