mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 15:40:23 +00:00
Rework browser welcome screen
This commit is contained in:
parent
2bc508d448
commit
f67e9ad247
10 changed files with 136 additions and 94 deletions
|
@ -84,7 +84,7 @@ final class BrowserBookmarkList extends SimpleComp {
|
|||
});
|
||||
});
|
||||
var category = new DataStoreCategoryChoiceComp(StoreViewState.get().getAllConnectionsCategory(), StoreViewState.get().getActiveCategory(),
|
||||
selectedCategory).styleClass(Styles.LEFT_PILL).grow(false, true);
|
||||
selectedCategory).styleClass(Styles.LEFT_PILL);
|
||||
var filter = new FilterComp(filterText).styleClass(Styles.RIGHT_PILL).hgrow().apply(struc -> {});
|
||||
|
||||
var top = new HorizontalComp(List.of(category, filter.hgrow())).styleClass("categories").apply(struc -> {
|
||||
|
|
|
@ -22,7 +22,7 @@ public class BrowserGreetingComp extends SimpleComp {
|
|||
text = "Good afternoon";
|
||||
}
|
||||
var r = new Label(text);
|
||||
AppFont.setSize(r, 12);
|
||||
AppFont.setSize(r, 7);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,17 +25,20 @@ import java.util.function.Function;
|
|||
@Getter
|
||||
public class BrowserModel {
|
||||
|
||||
public static final BrowserModel DEFAULT = new BrowserModel(Mode.BROWSER);
|
||||
public static final BrowserModel DEFAULT = new BrowserModel(Mode.BROWSER, BrowserSavedStateImpl.load());
|
||||
|
||||
private final Mode mode;
|
||||
private final ObservableList<OpenFileSystemModel> openFileSystems = FXCollections.observableArrayList();
|
||||
private final Property<OpenFileSystemModel> selected = new SimpleObjectProperty<>();
|
||||
private final BrowserTransferModel localTransfersStage = new BrowserTransferModel(this);
|
||||
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
|
||||
private final BrowserSavedState savedState;
|
||||
@Setter
|
||||
private Consumer<List<FileReference>> onFinish;
|
||||
|
||||
public BrowserModel(Mode mode) {
|
||||
public BrowserModel(Mode mode, BrowserSavedState savedState) {
|
||||
this.mode = mode;
|
||||
this.savedState = savedState;
|
||||
|
||||
selected.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue == null) {
|
||||
|
@ -48,7 +51,7 @@ public class BrowserModel {
|
|||
}
|
||||
|
||||
public void restoreState(BrowserSavedState state) {
|
||||
state.getLastSystems().forEach(e -> {
|
||||
state.getEntries().forEach(e -> {
|
||||
restoreState(e, null);
|
||||
});
|
||||
}
|
||||
|
@ -61,27 +64,14 @@ public class BrowserModel {
|
|||
}
|
||||
|
||||
public void reset() {
|
||||
var list = new ArrayList<BrowserSavedState.Entry>();
|
||||
synchronized (BrowserModel.this) {
|
||||
openFileSystems.forEach(model -> {
|
||||
if (DataStorage.get().getStoreEntries().contains(model.getEntry().get())) {
|
||||
list.add(new BrowserSavedState.Entry(model.getEntry().get().getUuid(), model.getCurrentPath().get()));
|
||||
}
|
||||
closeFileSystemSync(model);
|
||||
});
|
||||
if (savedState != null) {
|
||||
savedState.save();
|
||||
}
|
||||
|
||||
// Don't override state if it is empty
|
||||
if (list.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var meaningful = list.size() > 1 || list.stream().allMatch(s -> s.getPath() != null);
|
||||
if (!meaningful) {
|
||||
return;
|
||||
}
|
||||
|
||||
var state = BrowserSavedState.builder().lastSystems(list).build();
|
||||
state.save();
|
||||
}
|
||||
|
||||
public void finishChooser() {
|
||||
|
@ -108,11 +98,18 @@ public class BrowserModel {
|
|||
|
||||
public void closeFileSystemAsync(OpenFileSystemModel open) {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
closeFileSystemSync(open);
|
||||
});
|
||||
}
|
||||
|
||||
private void closeFileSystemSync(OpenFileSystemModel open) {
|
||||
if (DataStorage.get().getStoreEntries().contains(open.getEntry().get()) && savedState != null && open.getCurrentPath().get() != null) {
|
||||
savedState.add(new BrowserSavedState.Entry(open.getEntry().get().getUuid(), open.getCurrentPath().get()));
|
||||
}
|
||||
open.closeSync();
|
||||
synchronized (BrowserModel.this) {
|
||||
openFileSystems.remove(open);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void openFileSystemAsync(DataStoreEntryRef<? extends FileSystemStore> store, Function<OpenFileSystemModel, String> path, BooleanProperty externalBusy) {
|
||||
|
|
|
@ -1,27 +1,40 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import io.xpipe.app.core.AppCache;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Getter
|
||||
public class BrowserSavedState {
|
||||
public interface BrowserSavedState {
|
||||
|
||||
static BrowserSavedState none() {
|
||||
return new BrowserSavedState() {
|
||||
@Override
|
||||
public void add(Entry entry) {
|
||||
|
||||
static BrowserSavedState load() {
|
||||
return AppCache.get("browser-state", BrowserSavedState.class, () -> {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<Entry> getEntries() {
|
||||
return FXCollections.observableArrayList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void add(Entry entry);
|
||||
|
||||
void save();
|
||||
|
||||
ObservableList<Entry> getEntries();
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
|
@ -30,10 +43,4 @@ public class BrowserSavedState {
|
|||
UUID uuid;
|
||||
String path;
|
||||
}
|
||||
|
||||
@NonNull List<Entry> lastSystems;
|
||||
|
||||
public void save() {
|
||||
AppCache.update("browser-state", this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import io.xpipe.app.core.AppCache;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Getter
|
||||
public class BrowserSavedStateImpl implements BrowserSavedState {
|
||||
|
||||
static BrowserSavedStateImpl load() {
|
||||
return AppCache.get("browser-state", BrowserSavedStateImpl.class, () -> {
|
||||
return new BrowserSavedStateImpl(FXCollections.observableArrayList());
|
||||
});
|
||||
}
|
||||
|
||||
ObservableList<Entry> lastSystems;
|
||||
|
||||
@Override
|
||||
public synchronized void add(BrowserSavedState.Entry entry) {
|
||||
lastSystems.removeIf(s -> s.getUuid().equals(entry.getUuid()));
|
||||
lastSystems.addFirst(entry);
|
||||
if (lastSystems.size() > 10) {
|
||||
lastSystems.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
AppCache.update("browser-state", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<Entry> getEntries() {
|
||||
return lastSystems;
|
||||
}
|
||||
}
|
|
@ -5,19 +5,21 @@ import atlantafx.base.theme.Styles;
|
|||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
import io.xpipe.app.comp.base.TileButtonComp;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.LabelComp;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.JfxHelper;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Separator;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
|
@ -33,33 +35,26 @@ public class BrowserWelcomeComp extends SimpleComp {
|
|||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var state = BrowserSavedState.load();
|
||||
var state = model.getSavedState();
|
||||
|
||||
var welcome = new BrowserGreetingComp().createSimple();
|
||||
|
||||
var vbox = new VBox(welcome, new Spacer(Orientation.VERTICAL));
|
||||
var vbox = new VBox(welcome, new Spacer(4, Orientation.VERTICAL));
|
||||
vbox.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
var img = PrettyImageHelper.ofSvg(new SimpleStringProperty("Hips.svg"), 50, 75).padding(new Insets(5, 0, 0, 0)).createRegion();
|
||||
var hbox = new HBox(img, vbox);
|
||||
hbox.setAlignment(Pos.CENTER_LEFT);
|
||||
hbox.setSpacing(15);
|
||||
|
||||
if (state == null) {
|
||||
var header = new Label("Here you will be able to see where you left off last time you exited XPipe.");
|
||||
AppFont.header(header);
|
||||
vbox.getChildren().add(header);
|
||||
hbox.setPadding(new Insets(40, 40, 40, 50));
|
||||
return new VBox(hbox);
|
||||
}
|
||||
|
||||
var header = new Label("Last time you were connected to the following systems:");
|
||||
header.getStyleClass().add(Styles.TEXT_MUTED);
|
||||
AppFont.header(header);
|
||||
vbox.getChildren().add(header);
|
||||
|
||||
var storeList = new VBox();
|
||||
storeList.setSpacing(8);
|
||||
|
||||
var list = FXCollections.observableList(state.getLastSystems().stream().filter(e -> {
|
||||
var list = BindingsHelper.filteredContentBinding(state.getEntries(), e -> {
|
||||
var entry = DataStorage.get().getStoreEntryIfPresent(e.getUuid());
|
||||
if (entry.isEmpty()) {
|
||||
return false;
|
||||
|
@ -70,8 +65,20 @@ public class BrowserWelcomeComp extends SimpleComp {
|
|||
}
|
||||
|
||||
return true;
|
||||
}).toList());
|
||||
var box = new ListBoxViewComp<>(list, list, e -> {
|
||||
});
|
||||
var empty = Bindings.createBooleanBinding(() -> list.isEmpty(), list);
|
||||
|
||||
var header = new LabelComp(Bindings.createStringBinding(() -> {
|
||||
return !empty.get() ? "Last time you were connected to the following systems:" :
|
||||
"Here you will be able to see where you left off last time you exited XPipe.";
|
||||
}, empty)).createRegion();
|
||||
header.getStyleClass().add(Styles.TEXT_MUTED);
|
||||
vbox.getChildren().add(header);
|
||||
|
||||
var storeList = new VBox();
|
||||
storeList.setSpacing(8);
|
||||
|
||||
var listBox = new ListBoxViewComp<>(list, list, e -> {
|
||||
var entry = DataStorage.get().getStoreEntryIfPresent(e.getUuid());
|
||||
var graphic = entry.get().getProvider().getDisplayIconFileName(entry.get().getStore());
|
||||
var view = PrettyImageHelper.ofFixedSquare(graphic, 45);
|
||||
|
@ -83,26 +90,26 @@ public class BrowserWelcomeComp extends SimpleComp {
|
|||
ThreadHelper.runAsync(() -> {
|
||||
model.restoreState(e, disable);
|
||||
});
|
||||
}).accessibleText(DataStorage.get().getStoreDisplayName(entry.get())).disable(disable).styleClass("color-box").apply(struc -> struc.get().setMaxWidth(2000)).grow(true, false);
|
||||
}).accessibleText(DataStorage.get().getStoreDisplayName(entry.get())).disable(disable).styleClass("color-listBox").apply(struc -> struc.get().setMaxWidth(2000)).grow(true, false);
|
||||
}).apply(struc -> {
|
||||
VBox vBox = (VBox) struc.get().getContent();
|
||||
vBox.setSpacing(10);
|
||||
}).createRegion();
|
||||
}).hide(empty).createRegion();
|
||||
|
||||
var layout = new VBox();
|
||||
layout.getStyleClass().add("welcome");
|
||||
layout.setPadding(new Insets(40, 40, 40, 50));
|
||||
layout.setSpacing(18);
|
||||
layout.getChildren().add(hbox);
|
||||
layout.getChildren().add(new Separator(Orientation.HORIZONTAL));
|
||||
layout.getChildren().add(box);
|
||||
layout.getChildren().add(Comp.separator().hide(empty).createRegion());
|
||||
layout.getChildren().add(listBox);
|
||||
VBox.setVgrow(layout.getChildren().get(2), Priority.NEVER);
|
||||
layout.getChildren().add(new Separator(Orientation.HORIZONTAL));
|
||||
layout.getChildren().add(Comp.separator().hide(empty).createRegion());
|
||||
|
||||
var tile = new TileButtonComp("restore", "restoreAllSessions", "mdmz-restore", actionEvent -> {
|
||||
model.restoreState(state);
|
||||
actionEvent.consume();
|
||||
}).grow(true, false).accessibleTextKey("restoreAllSessions");
|
||||
}).grow(true, false).hide(empty).accessibleTextKey("restoreAllSessions");
|
||||
layout.getChildren().add(tile.createRegion());
|
||||
|
||||
return layout;
|
||||
|
|
|
@ -41,7 +41,7 @@ public class StandaloneFileBrowser {
|
|||
|
||||
public static void openSingleFile(Supplier<DataStoreEntryRef<? extends FileSystemStore>> store, Consumer<FileReference> file) {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
var model = new BrowserModel(BrowserModel.Mode.SINGLE_FILE_CHOOSER);
|
||||
var model = new BrowserModel(BrowserModel.Mode.SINGLE_FILE_CHOOSER, null);
|
||||
var comp = new BrowserComp(model)
|
||||
.apply(struc -> struc.get().setPrefSize(1200, 700))
|
||||
.apply(struc -> AppFont.normal(struc.get()));
|
||||
|
@ -57,7 +57,7 @@ public class StandaloneFileBrowser {
|
|||
|
||||
public static void saveSingleFile(Property<FileReference> file) {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
var model = new BrowserModel(BrowserModel.Mode.SINGLE_FILE_SAVE);
|
||||
var model = new BrowserModel(BrowserModel.Mode.SINGLE_FILE_SAVE, null);
|
||||
var comp = new BrowserComp(model)
|
||||
.apply(struc -> struc.get().setPrefSize(1200, 700))
|
||||
.apply(struc -> AppFont.normal(struc.get()));
|
||||
|
|
|
@ -5,13 +5,11 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.Hyperlinks;
|
||||
import io.xpipe.app.util.ScanAlert;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Separator;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
@ -26,7 +24,6 @@ public class StoreIntroComp extends SimpleComp {
|
|||
public Region createSimple() {
|
||||
var title = new Label(AppI18n.get("storeIntroTitle"));
|
||||
AppFont.setSize(title, 7);
|
||||
title.getStyleClass().add("title-header");
|
||||
|
||||
var introDesc = new Label(AppI18n.get("storeIntroDescription"));
|
||||
|
||||
|
@ -41,22 +38,10 @@ public class StoreIntroComp extends SimpleComp {
|
|||
var scanPane = new StackPane(scanButton);
|
||||
scanPane.setAlignment(Pos.CENTER);
|
||||
|
||||
var dofi = new FontIcon("mdi2b-book-open-variant");
|
||||
var documentation = new Label(AppI18n.get("introDocumentation"), dofi);
|
||||
documentation.heightProperty().addListener((c, o, n) -> {
|
||||
dofi.iconSizeProperty().set(n.intValue());
|
||||
});
|
||||
var docLink = new Hyperlink(Hyperlinks.DOCUMENTATION);
|
||||
docLink.setOnAction(e -> {
|
||||
Hyperlinks.open(Hyperlinks.DOCUMENTATION);
|
||||
});
|
||||
var docLinkPane = new StackPane(docLink);
|
||||
docLinkPane.setAlignment(Pos.CENTER);
|
||||
|
||||
var img = PrettyImageHelper.ofSvg(new SimpleStringProperty("Wave.svg"), 80, 180).createRegion();
|
||||
var hbox = new HBox(img, new VBox(
|
||||
title, introDesc, new Separator(Orientation.HORIZONTAL), machine
|
||||
));
|
||||
var img = PrettyImageHelper.ofSvg(new SimpleStringProperty("Wave.svg"), 80, 150).createRegion();
|
||||
var text = new VBox(title, introDesc, new Separator(Orientation.HORIZONTAL), machine);
|
||||
text.setAlignment(Pos.CENTER_LEFT);
|
||||
var hbox = new HBox(img, text);
|
||||
hbox.setSpacing(35);
|
||||
hbox.setAlignment(Pos.CENTER);
|
||||
|
||||
|
|
|
@ -289,14 +289,16 @@ public class BindingsHelper {
|
|||
var newSet = new HashSet<>(newList);
|
||||
|
||||
// Only add missing element
|
||||
if (targetSet.size() + 1 == newList.size() && newSet.containsAll(targetSet)) {
|
||||
if (target.size() + 1 == newList.size() && newSet.containsAll(targetSet)) {
|
||||
var l = new HashSet<>(newSet);
|
||||
l.removeAll(targetSet);
|
||||
if (l.size() > 0) {
|
||||
var found = l.iterator().next();
|
||||
var index = newList.indexOf(found);
|
||||
target.add(index, found);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only remove not needed element
|
||||
if (target.size() - 1 == newList.size() && targetSet.containsAll(newSet)) {
|
||||
|
|
|
@ -58,6 +58,7 @@ public class JfxHelper {
|
|||
var desc = new Label(descString);
|
||||
AppFont.small(desc);
|
||||
var text = new VBox(header, new Spacer(), desc);
|
||||
text.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
if (image == null) {
|
||||
return text;
|
||||
|
|
Loading…
Reference in a new issue