File browser improvements

This commit is contained in:
crschnick 2024-07-10 04:20:55 +00:00
parent 1893464432
commit fdbfcb6ddc
8 changed files with 64 additions and 39 deletions

View file

@ -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()

View file

@ -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 {

View file

@ -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()));
}

View file

@ -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;
}

View file

@ -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(() -> {

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}