mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 09:00:26 +00:00
File browser improvements
This commit is contained in:
parent
1893464432
commit
fdbfcb6ddc
8 changed files with 64 additions and 39 deletions
|
@ -32,9 +32,9 @@ public class BrowserStatusBarComp extends SimpleComp {
|
|||
@Override
|
||||
protected Region createSimple() {
|
||||
var bar = new HorizontalComp(List.of(
|
||||
createProgressEstimateStatus(),
|
||||
createProgressNameStatus(),
|
||||
createProgressStatus(),
|
||||
createProgressEstimateStatus(),
|
||||
Comp.hspacer(),
|
||||
createClipboardStatus(),
|
||||
createSelectionStatus()
|
||||
|
|
|
@ -8,7 +8,6 @@ import io.xpipe.app.fxcomps.SimpleComp;
|
|||
import io.xpipe.app.fxcomps.augment.DragOverPseudoClassAugment;
|
||||
import io.xpipe.app.fxcomps.impl.*;
|
||||
import io.xpipe.app.fxcomps.util.DerivedObservableList;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
@ -38,22 +37,20 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var syncItems = PlatformThread.sync(model.getItems());
|
||||
|
||||
var background = new LabelComp(AppI18n.observable("transferDescription"))
|
||||
.apply(struc -> struc.get().setGraphic(new FontIcon("mdi2d-download-outline")))
|
||||
.apply(struc -> struc.get().setWrapText(true))
|
||||
.visible(Bindings.isEmpty(syncItems));
|
||||
.visible(model.getEmpty());
|
||||
var backgroundStack =
|
||||
new StackComp(List.of(background)).grow(true, true).styleClass("download-background");
|
||||
|
||||
var binding = new DerivedObservableList<>(syncItems, true)
|
||||
var binding = new DerivedObservableList<>(model.getItems(), true)
|
||||
.mapped(item -> item.getBrowserEntry())
|
||||
.getList();
|
||||
var list = new BrowserSelectionListComp(
|
||||
binding,
|
||||
entry -> {
|
||||
var sourceItem = syncItems.stream()
|
||||
var sourceItem = model.getCurrentItems().stream()
|
||||
.filter(item -> item.getBrowserEntry() == entry)
|
||||
.findAny();
|
||||
if (sourceItem.isEmpty()) {
|
||||
|
@ -61,7 +58,7 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
}
|
||||
return Bindings.createStringBinding(() -> {
|
||||
var p = sourceItem.get().getProgress().getValue();
|
||||
var progressSuffix = sourceItem.get().downloadFinished().get() ? "" : " " + (p.getTransferred() * 100 / p.getTotal()) + "%";
|
||||
var progressSuffix = p == null || sourceItem.get().downloadFinished().get() ? "" : " " + (p.getTransferred() * 100 / p.getTotal()) + "%";
|
||||
return entry.getFileName() + progressSuffix;
|
||||
}, sourceItem.get().getProgress());
|
||||
})
|
||||
|
@ -70,14 +67,14 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
.apply(struc -> struc.get().setGraphic(new FontIcon("mdi2h-hand-left")))
|
||||
.apply(struc -> AppFont.medium(struc.get()))
|
||||
.apply(struc -> struc.get().setWrapText(true))
|
||||
.hide(Bindings.isEmpty(syncItems));
|
||||
.hide(model.getEmpty());
|
||||
|
||||
var clearButton = new IconButtonComp("mdi2c-close", () -> {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
model.clear(true);
|
||||
});
|
||||
})
|
||||
.hide(Bindings.isEmpty(syncItems))
|
||||
.hide(model.getEmpty())
|
||||
.tooltipKey("clearTransferDescription");
|
||||
|
||||
var downloadButton = new IconButtonComp("mdi2f-folder-move-outline", () -> {
|
||||
|
@ -85,7 +82,7 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
model.transferToDownloads();
|
||||
});
|
||||
})
|
||||
.hide(Bindings.isEmpty(syncItems))
|
||||
.hide(model.getEmpty())
|
||||
.tooltipKey("downloadStageDescription");
|
||||
|
||||
var bottom =
|
||||
|
@ -127,13 +124,14 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
}
|
||||
});
|
||||
struc.get().setOnDragDetected(event -> {
|
||||
var selected = syncItems.stream()
|
||||
var items = model.getCurrentItems();
|
||||
var selected = items.stream()
|
||||
.map(item -> item.getBrowserEntry())
|
||||
.toList();
|
||||
Dragboard db = struc.get().startDragAndDrop(TransferMode.COPY);
|
||||
|
||||
var cc = new ClipboardContent();
|
||||
var files = syncItems.stream()
|
||||
var files = items.stream()
|
||||
.filter(item -> item.downloadFinished().get())
|
||||
.map(item -> {
|
||||
try {
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.io.IOException;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -33,6 +34,7 @@ public class BrowserTransferModel {
|
|||
|
||||
BrowserSessionModel browserSessionModel;
|
||||
ObservableList<Item> items = FXCollections.observableArrayList();
|
||||
ObservableBooleanValue empty = Bindings.createBooleanBinding(() -> items.isEmpty(), items);
|
||||
|
||||
public BrowserTransferModel(BrowserSessionModel browserSessionModel) {
|
||||
this.browserSessionModel = browserSessionModel;
|
||||
|
@ -51,27 +53,41 @@ public class BrowserTransferModel {
|
|||
thread.start();
|
||||
}
|
||||
|
||||
private void cleanDirectory() {
|
||||
public List<Item> getCurrentItems() {
|
||||
synchronized (items) {
|
||||
return new ArrayList<>(items);
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanItem(Item item) {
|
||||
if (!Files.isDirectory(TEMP)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (var ls = Files.list(TEMP)) {
|
||||
var list = ls.toList();
|
||||
for (Path path : list) {
|
||||
FileUtils.forceDelete(path.toFile());
|
||||
}
|
||||
if (!Files.exists(item.getLocalFile())) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtils.forceDelete(item.getLocalFile().toFile());
|
||||
} catch (IOException e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear(boolean delete) {
|
||||
List<Item> toClear;
|
||||
synchronized (items) {
|
||||
items.clear();
|
||||
toClear = items.stream().filter(item -> item.downloadFinished().get()).toList();
|
||||
if (toClear.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
items.removeAll(toClear);
|
||||
}
|
||||
if (delete) {
|
||||
cleanDirectory();
|
||||
for (Item item : toClear) {
|
||||
cleanItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,16 +143,20 @@ public class BrowserTransferModel {
|
|||
}
|
||||
|
||||
public void transferToDownloads() throws Exception {
|
||||
if (items.isEmpty()) {
|
||||
return;
|
||||
List<Item> toMove;
|
||||
synchronized (items) {
|
||||
toMove = items.stream().filter(item -> item.downloadFinished().get()).toList();
|
||||
if (toMove.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
items.removeAll(toMove);
|
||||
}
|
||||
|
||||
var files = items.stream().map(item -> item.getLocalFile()).toList();
|
||||
var files = toMove.stream().map(item -> item.getLocalFile()).toList();
|
||||
var downloads = DesktopHelper.getDownloadsDirectory();
|
||||
for (Path file : files) {
|
||||
Files.move(file, downloads.resolve(file.getFileName()), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
clear(true);
|
||||
DesktopHelper.browseFileInDirectory(downloads.resolve(files.getFirst().getFileName()));
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.app.browser;
|
|||
import io.xpipe.app.browser.session.BrowserSessionModel;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
import io.xpipe.app.comp.base.LoadingOverlayComp;
|
||||
import io.xpipe.app.comp.base.TileButtonComp;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
|
@ -55,7 +56,9 @@ public class BrowserWelcomeComp extends SimpleComp {
|
|||
var img = new PrettySvgComp(new SimpleStringProperty("Hips.svg"), 50, 75)
|
||||
.padding(new Insets(5, 0, 0, 0))
|
||||
.createRegion();
|
||||
var hbox = new HBox(img, vbox);
|
||||
|
||||
var loading = LoadingOverlayComp.noProgress(Comp.empty(),model.getBusy()).createRegion();
|
||||
var hbox = new HBox(img, vbox, new Spacer(), loading);
|
||||
hbox.setAlignment(Pos.CENTER_LEFT);
|
||||
hbox.setSpacing(15);
|
||||
|
||||
|
@ -139,7 +142,6 @@ public class BrowserWelcomeComp extends SimpleComp {
|
|||
.hide(empty)
|
||||
.accessibleTextKey("restoreAllSessions");
|
||||
layout.getChildren().add(tile.createRegion());
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ public class BrowserAbstractSessionModel<T extends BrowserSessionTab<?>> {
|
|||
|
||||
protected final ObservableList<T> sessionEntries = FXCollections.observableArrayList();
|
||||
protected final Property<T> selectedEntry = new SimpleObjectProperty<>();
|
||||
protected final BooleanProperty busy = new SimpleBooleanProperty();
|
||||
|
||||
public void closeAsync(BrowserSessionTab<?> e) {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
|
|
|
@ -94,13 +94,15 @@ public class BrowserSessionModel extends BrowserAbstractSessionModel<BrowserSess
|
|||
|
||||
OpenFileSystemModel model;
|
||||
try (var b = new BooleanScope(externalBusy != null ? externalBusy : new SimpleBooleanProperty()).start()) {
|
||||
model = new OpenFileSystemModel(this, store, OpenFileSystemModel.SelectionMode.ALL);
|
||||
model.init();
|
||||
// Prevent multiple calls from interfering with each other
|
||||
synchronized (BrowserSessionModel.this) {
|
||||
sessionEntries.add(model);
|
||||
// The tab pane doesn't automatically select new tabs
|
||||
selectedEntry.setValue(model);
|
||||
try (var sessionBusy = new BooleanScope(busy).exclusive().start()) {
|
||||
model = new OpenFileSystemModel(this, store, OpenFileSystemModel.SelectionMode.ALL);
|
||||
model.init();
|
||||
// Prevent multiple calls from interfering with each other
|
||||
synchronized (BrowserSessionModel.this) {
|
||||
sessionEntries.add(model);
|
||||
// The tab pane doesn't automatically select new tabs
|
||||
selectedEntry.setValue(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (path != null) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.xpipe.app.browser.session;
|
||||
|
||||
import atlantafx.base.controls.RingProgressIndicator;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.browser.BrowserWelcomeComp;
|
||||
import io.xpipe.app.comp.base.MultiContentComp;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
|
@ -10,7 +12,6 @@ import io.xpipe.app.fxcomps.util.PlatformThread;
|
|||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
@ -28,9 +29,6 @@ import javafx.scene.input.KeyCode;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import atlantafx.base.controls.RingProgressIndicator;
|
||||
import atlantafx.base.theme.Styles;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
}
|
||||
|
||||
.transfer .button:hover {
|
||||
-fx-background-color: -color-bg-subtle;
|
||||
-fx-background-color: -color-accent-subtle;
|
||||
-fx-opacity: 1.0;
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,10 @@
|
|||
-fx-background-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
.browser .welcome .loading-comp {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.browser .tile > * {
|
||||
-fx-padding: 0.6em 0 0.6em 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue