More user interface improvements

This commit is contained in:
crschnick 2023-04-28 18:30:31 +00:00
parent 62a6438e97
commit 1596483250
15 changed files with 157 additions and 49 deletions

View file

@ -1,18 +1,30 @@
package io.xpipe.app.browser; package io.xpipe.app.browser;
import com.jfoenix.controls.JFXButton;
import io.xpipe.app.comp.storage.store.StoreEntryFlatMiniSection; import io.xpipe.app.comp.storage.store.StoreEntryFlatMiniSection;
import io.xpipe.app.comp.storage.store.StoreEntryWrapper; import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.augment.DragPseudoClassAugment;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
import javafx.application.Platform;
import javafx.geometry.Point2D;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.input.DragEvent;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import java.util.Map; import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
final class BookmarkList extends SimpleComp { final class BookmarkList extends SimpleComp {
public static final Timer DROP_TIMER = new Timer("dnd", true);
private Point2D lastOver = new Point2D(-1, -1);
private TimerTask activeTask;
private final FileBrowserModel model; private final FileBrowserModel model;
BookmarkList(FileBrowserModel model) { BookmarkList(FileBrowserModel model) {
@ -28,16 +40,26 @@ final class BookmarkList extends SimpleComp {
continue; continue;
} }
var button = new JFXButton(null, e.getValue()); var button = new Button(null, e.getValue());
button.setOnAction(event -> { button.setOnAction(event -> {
var fileSystem = ((ShellStore) e.getKey().getEntry().getStore()); var fileSystem = ((ShellStore) e.getKey().getEntry().getStore());
model.openFileSystem(fileSystem); model.openFileSystem(fileSystem);
event.consume(); event.consume();
}); });
button.prefWidthProperty().bind(list.widthProperty()); button.prefWidthProperty().bind(list.widthProperty());
DragPseudoClassAugment.create().augment(new SimpleCompStructure<>(button));
button.addEventHandler(
DragEvent.DRAG_OVER,
mouseEvent -> handleHoverTimer(e.getKey().getEntry().getStore(), mouseEvent));
button.addEventHandler(
DragEvent.DRAG_EXITED,
mouseEvent -> activeTask = null);
list.getChildren().add(button); list.getChildren().add(button);
} }
list.setFillWidth(true); list.setFillWidth(true);
list.getStyleClass().add("bookmark-list");
var sp = new ScrollPane(list); var sp = new ScrollPane(list);
sp.setFitToWidth(true); sp.setFitToWidth(true);
@ -45,4 +67,23 @@ final class BookmarkList extends SimpleComp {
return sp; return sp;
} }
private void handleHoverTimer(DataStore store, DragEvent event) {
if (lastOver.getX() == event.getX() && lastOver.getY() == event.getY()) {
return;
}
lastOver = (new Point2D(event.getX(), event.getY()));
activeTask = new TimerTask() {
@Override
public void run() {
if (activeTask != this) {
return;
}
Platform.runLater(() -> model.openExistingFileSystemIfPresent(store.asNeeded()));
}
};
DROP_TIMER.schedule(activeTask, 500);
}
} }

View file

@ -5,12 +5,15 @@ import atlantafx.base.controls.Spacer;
import atlantafx.base.theme.Styles; import atlantafx.base.theme.Styles;
import io.xpipe.app.browser.icon.FileIconManager; import io.xpipe.app.browser.icon.FileIconManager;
import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.augment.GrowAugment;
import io.xpipe.app.fxcomps.impl.PrettyImageComp; import io.xpipe.app.fxcomps.impl.PrettyImageComp;
import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.BusyProperty; import io.xpipe.app.util.BusyProperty;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import javafx.application.Platform;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
@ -237,7 +240,7 @@ public class FileBrowserComp extends SimpleComp {
label.addEventHandler(DragEvent.DRAG_ENTERED, new EventHandler<DragEvent>() { label.addEventHandler(DragEvent.DRAG_ENTERED, new EventHandler<DragEvent>() {
@Override @Override
public void handle(DragEvent mouseEvent) { public void handle(DragEvent mouseEvent) {
tabs.getSelectionModel().select(tab); Platform.runLater(() -> tabs.getSelectionModel().select(tab));
} }
}); });
@ -249,6 +252,7 @@ public class FileBrowserComp extends SimpleComp {
PlatformThread.sync(model.getBusy()))); PlatformThread.sync(model.getBusy())));
tab.setGraphic(label); tab.setGraphic(label);
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(label));
if (!this.model.getMode().equals(FileBrowserModel.Mode.BROWSER)) { if (!this.model.getMode().equals(FileBrowserModel.Mode.BROWSER)) {
label.setManaged(false); label.setManaged(false);

View file

@ -66,6 +66,15 @@ public class FileBrowserModel {
}); });
} }
public void openExistingFileSystemIfPresent(ShellStore store) {
var found = openFileSystems.stream().filter(model -> Objects.equals(model.getStore().getValue(), store)).findFirst();
if (found.isPresent()) {
selected.setValue(found.get());
} else {
openFileSystem(store);
}
}
public void openFileSystem(ShellStore store) { public void openFileSystem(ShellStore store) {
// Prevent multiple tabs in non browser modes // Prevent multiple tabs in non browser modes
if (!mode.equals(Mode.BROWSER)) { if (!mode.equals(Mode.BROWSER)) {

View file

@ -29,14 +29,14 @@ public class FileFilterComp extends SimpleComp {
protected Region createSimple() { protected Region createSimple() {
var expanded = new SimpleBooleanProperty(); var expanded = new SimpleBooleanProperty();
var text = new TextFieldComp(filterString, false).createRegion(); var text = new TextFieldComp(filterString, false).createRegion();
var button = new Button();
text.focusedProperty().addListener((observable, oldValue, newValue) -> { text.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue && filterString.getValue() == null) { if (!newValue && filterString.getValue() == null) {
expanded.set(false); if (button.isFocused()) {
return; return;
} }
if (newValue) { expanded.set(false);
expanded.set(true);
} }
}); });
filterString.addListener((observable, oldValue, newValue) -> { filterString.addListener((observable, oldValue, newValue) -> {
@ -56,21 +56,26 @@ public class FileFilterComp extends SimpleComp {
}); });
var fi = new FontIcon("mdi2m-magnify"); var fi = new FontIcon("mdi2m-magnify");
var button = new Button();
GrowAugment.create(false, true).augment(new SimpleCompStructure<>(button)); GrowAugment.create(false, true).augment(new SimpleCompStructure<>(button));
Shortcuts.addShortcut(button, new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN)); Shortcuts.addShortcut(button, new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
button.setGraphic(fi); button.setGraphic(fi);
button.setOnAction(event -> { button.setOnAction(event -> {
if (expanded.get() && filterString.getValue() == null) { if (expanded.get()) {
expanded.set(false); if (filterString.getValue() == null) {
return; expanded.set(false);
}
event.consume();
} else {
expanded.set(true);
text.requestFocus();
event.consume();
} }
text.requestFocus();
event.consume();
}); });
SimpleChangeListener.apply(expanded, val -> { text.setPrefWidth(0);
button.getStyleClass().add(Styles.FLAT);
expanded.addListener((observable, oldValue, val) -> {
System.out.println(val);
if (val) { if (val) {
text.setPrefWidth(250); text.setPrefWidth(250);
button.getStyleClass().add(Styles.RIGHT_PILL); button.getStyleClass().add(Styles.RIGHT_PILL);

View file

@ -89,6 +89,10 @@ public class FileListCompEntry {
return false; return false;
} }
if (model.getFileSystemModel().getCurrentDirectory() == null) {
return false;
}
// Prevent drag and drops of files into the current directory // Prevent drag and drops of files into the current directory
if (FileBrowserClipboard.currentDragClipboard if (FileBrowserClipboard.currentDragClipboard
.getBaseDirectory().getPath() .getBaseDirectory().getPath()

View file

@ -4,6 +4,7 @@ import io.xpipe.app.comp.base.LoadingOverlayComp;
import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp; 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.IconButtonComp;
import io.xpipe.app.fxcomps.impl.LabelComp; import io.xpipe.app.fxcomps.impl.LabelComp;
import io.xpipe.app.fxcomps.impl.StackComp; import io.xpipe.app.fxcomps.impl.StackComp;
@ -13,9 +14,6 @@ import io.xpipe.app.fxcomps.util.PlatformThread;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.input.ClipboardContent; import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard; import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode; import javafx.scene.input.TransferMode;
@ -63,7 +61,7 @@ public class LocalFileTransferComp extends SimpleComp {
var listBox = new VerticalComp(List.of(list, dragNotice)); var listBox = new VerticalComp(List.of(list, dragNotice));
var stack = new LoadingOverlayComp( var stack = new LoadingOverlayComp(
new StackComp(List.of(backgroundStack, listBox, clearPane)).apply(struc -> { new StackComp(List.of(backgroundStack, listBox, clearPane)).apply(DragPseudoClassAugment.create()).apply(struc -> {
struc.get().setOnDragOver(event -> { struc.get().setOnDragOver(event -> {
// Accept drops from inside the app window // Accept drops from inside the app window
if (event.getGestureSource() != null && event.getGestureSource() != struc.get()) { if (event.getGestureSource() != null && event.getGestureSource() != struc.get()) {
@ -99,12 +97,9 @@ public class LocalFileTransferComp extends SimpleComp {
cc.putFiles(files); cc.putFiles(files);
db.setContent(cc); db.setContent(cc);
var r = new SelectedFileListComp(FXCollections.observableList(stage.getItems().stream() var image = SelectedFileListComp.snapshot(FXCollections.observableList(stage.getItems().stream()
.map(item -> item.getFileEntry()) .map(item -> item.getFileEntry())
.toList())) .toList()));
.createRegion();
new Scene(r);
WritableImage image = r.snapshot(new SnapshotParameters(), null);
db.setDragView(image, -20, 15); db.setDragView(image, -20, 15);
event.setDragDetect(true); event.setDragDetect(true);

View file

@ -19,9 +19,11 @@ import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyCombination;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
public class AppLayoutComp extends Comp<CompStructure<BorderPane>> { public class AppLayoutComp extends Comp<CompStructure<BorderPane>> {
@ -71,6 +73,9 @@ public class AppLayoutComp extends Comp<CompStructure<BorderPane>> {
@Override @Override
public CompStructure<BorderPane> createBase() { public CompStructure<BorderPane> createBase() {
var map = new HashMap<SideMenuBarComp.Entry, Region>();
entries.forEach(entry -> map.put(entry, entry.comp().createRegion()));
var pane = new BorderPane(); var pane = new BorderPane();
var sidebar = new SideMenuBarComp(selected, entries); var sidebar = new SideMenuBarComp(selected, entries);
pane.setCenter(selected.getValue().comp().createRegion()); pane.setCenter(selected.getValue().comp().createRegion());
@ -80,10 +85,9 @@ public class AppLayoutComp extends Comp<CompStructure<BorderPane>> {
AppPrefs.get().save(); AppPrefs.get().save();
} }
var r = selected.getValue().comp().createRegion(); pane.setCenter(map.get(n));
pane.setCenter(r);
}); });
pane.setCenter(selected.getValue().comp().createRegion()); pane.setCenter(map.get(selected.getValue()));
pane.setPrefWidth(1280); pane.setPrefWidth(1280);
pane.setPrefHeight(720); pane.setPrefHeight(720);
AppFont.normal(pane); AppFont.normal(pane);

View file

@ -1,5 +1,6 @@
package io.xpipe.app.comp.storage.store; package io.xpipe.app.comp.storage.store;
import atlantafx.base.theme.Styles;
import io.xpipe.app.comp.base.ButtonComp; import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.comp.source.store.GuiDsStoreCreator; import io.xpipe.app.comp.source.store.GuiDsStoreCreator;
import io.xpipe.app.core.AppFont; import io.xpipe.app.core.AppFont;
@ -20,24 +21,19 @@ public class StoreCreationBarComp extends SimpleComp {
@Override @Override
protected Region createSimple() { protected Region createSimple() {
var newOtherStore = new ButtonComp(
AppI18n.observable("addOther"), new FontIcon("mdi2c-card-plus-outline"), () -> {
GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.OTHER));
})
.shortcut(new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN))
.apply(new FancyTooltipAugment<>("addOther"));
var newStreamStore = new ButtonComp( var newStreamStore = new ButtonComp(
AppI18n.observable("addCommand"), new FontIcon("mdi2c-code-greater-than"), () -> { AppI18n.observable("addCommand"), new FontIcon("mdi2c-code-greater-than"), () -> {
GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.COMMAND)); GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.COMMAND));
}) })
.styleClass(Styles.FLAT)
.shortcut(new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN)) .shortcut(new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN))
.apply(new FancyTooltipAugment<>("addCommand")); .apply(new FancyTooltipAugment<>("addCommand"));
var newHostStore = new ButtonComp( var newHostStore = new ButtonComp(AppI18n.observable("addHost"), new FontIcon("mdi2h-home-plus"), () -> {
AppI18n.observable("addHost"), new FontIcon("mdi2h-home-plus"), () -> { GuiDsStoreCreator.showCreation(
GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.HOST)); v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.HOST));
}) })
.styleClass(Styles.FLAT)
.shortcut(new KeyCodeCombination(KeyCode.H, KeyCombination.SHORTCUT_DOWN)) .shortcut(new KeyCodeCombination(KeyCode.H, KeyCombination.SHORTCUT_DOWN))
.apply(new FancyTooltipAugment<>("addHost")); .apply(new FancyTooltipAugment<>("addHost"));
@ -45,6 +41,7 @@ public class StoreCreationBarComp extends SimpleComp {
AppI18n.observable("addShell"), new FontIcon("mdi2t-text-box-multiple"), () -> { AppI18n.observable("addShell"), new FontIcon("mdi2t-text-box-multiple"), () -> {
GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.SHELL)); GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.SHELL));
}) })
.styleClass(Styles.FLAT)
.shortcut(new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN)) .shortcut(new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN))
.apply(new FancyTooltipAugment<>("addShell")); .apply(new FancyTooltipAugment<>("addShell"));
@ -52,10 +49,11 @@ public class StoreCreationBarComp extends SimpleComp {
AppI18n.observable("addDatabase"), new FontIcon("mdi2d-database-plus"), () -> { AppI18n.observable("addDatabase"), new FontIcon("mdi2d-database-plus"), () -> {
GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.DATABASE)); GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.DATABASE));
}) })
.styleClass(Styles.FLAT)
.shortcut(new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN)) .shortcut(new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN))
.apply(new FancyTooltipAugment<>("addDatabase")); .apply(new FancyTooltipAugment<>("addDatabase"));
var box = new VerticalComp(List.of(newHostStore, newShellStore, newStreamStore, newDbStore)); var box = new VerticalComp(List.of(newHostStore, newShellStore, newStreamStore, newDbStore)).apply(struc -> struc.get().setFillWidth(true));
box.apply(s -> AppFont.medium(s.get())); box.apply(s -> AppFont.medium(s.get()));
var bar = box.createRegion(); var bar = box.createRegion();
bar.getStyleClass().add("bar"); bar.getStyleClass().add("bar");

View file

@ -40,7 +40,7 @@ public class StoreEntrySection extends Comp<CompStructure<VBox>> {
.apply(struc -> struc.get().setPrefWidth(40)) .apply(struc -> struc.get().setPrefWidth(40))
.disable(BindingsHelper.persist( .disable(BindingsHelper.persist(
Bindings.size(section.getChildren()).isEqualTo(0))) Bindings.size(section.getChildren()).isEqualTo(0)))
.grow(false, true); .grow(false, true).styleClass("expand-button");
List<Comp<?>> topEntryList = List.of(button, root); List<Comp<?>> topEntryList = List.of(button, root);
var all = section.getChildren(); var all = section.getChildren();

View file

@ -0,0 +1,24 @@
package io.xpipe.app.fxcomps.augment;
import io.xpipe.app.fxcomps.CompStructure;
import javafx.css.PseudoClass;
import javafx.scene.input.DragEvent;
public class DragPseudoClassAugment<S extends CompStructure<?>> implements Augment<S> {
public static final PseudoClass DRAGGED_PSEUDOCLASS = PseudoClass.getPseudoClass("drag-over");
public static <S extends CompStructure<?>> DragPseudoClassAugment<S> create() {
return new DragPseudoClassAugment<>();
}
@Override
public void augment(S struc) {
struc.get().addEventFilter(DragEvent.DRAG_ENTERED, event -> {
struc.get().pseudoClassStateChanged(DRAGGED_PSEUDOCLASS, true);
});
struc.get().addEventFilter(DragEvent.DRAG_EXITED, event -> struc.get()
.pseudoClassStateChanged(DRAGGED_PSEUDOCLASS, false));
}
}

View file

@ -65,7 +65,7 @@ public class SvgCacheComp extends SimpleComp {
} }
var pt = new PauseTransition(); var pt = new PauseTransition();
pt.setDuration(Duration.millis(1000)); pt.setDuration(Duration.millis(500));
pt.setOnFinished(actionEvent -> { pt.setOnFinished(actionEvent -> {
if (newValue == null || cache.getCached(newValue).isPresent()) { if (newValue == null || cache.getCached(newValue).isPresent()) {
return; return;

View file

@ -1,6 +1,6 @@
.download-background { .download-background {
-fx-border-color: -color-neutral-muted; -fx-border-color: -color-neutral-muted;
-fx-border-width: 4px 0 0 0; -fx-border-width: 2px 0 0 0;
-fx-padding: 1em; -fx-padding: 1em;
} }
@ -21,6 +21,21 @@
-fx-border-radius: 1px; -fx-border-radius: 1px;
} }
.bookmark-list .button {
-fx-border-width: 0;
-fx-border-radius: 0;
-fx-background-radius: 0;
-fx-background-insets: 1px 2px 1px 0;
}
*:drag-over .download-background {
-fx-background-color: -color-success-muted;
}
.bookmark-list .button:drag-over {
-fx-background-color: -color-success-muted;
}
.browser .bookmark-list { .browser .bookmark-list {
-fx-border-width: 0 0 1 1; -fx-border-width: 0 0 1 1;
} }

View file

@ -29,12 +29,7 @@
} }
.bar .button-comp { .bar .button-comp {
-fx-border-width: 0; -fx-padding: 0.3em 0em 0.3em 0em;
-fx-border-color: -color-neutral-emphasis;
-fx-background-color: transparent;
-fx-border-radius: 2px;
-fx-background-radius: 2px;
-fx-padding: 0.2em 0em 0.2em 0em;
} }
.collections-bar { .collections-bar {

View file

@ -6,6 +6,10 @@
-fx-padding: 0; -fx-padding: 0;
} }
.sidebar-comp .button:hover {
-fx-background-color: -color-neutral-muted;
}
.sidebar-comp .big-icon-button-comp { .sidebar-comp .big-icon-button-comp {
-fx-background-radius: 0; -fx-background-radius: 0;
} }

View file

@ -22,9 +22,19 @@
.store-entry-grid:incomplete .icon { .store-entry-grid:incomplete .icon {
-fx-opacity: 0.5; -fx-opacity: 0.5;
} }
.store-entry-comp { .store-entry-comp {
-fx-padding: 6px 6px 6px 0; -fx-padding: 6px 6px 6px 0;
} }
.store-entry-comp:hover {
-fx-background-color: -color-neutral-muted;
}
.expand-button:hover {
-fx-background-color: -color-neutral-muted;
}