mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
More user interface improvements
This commit is contained in:
parent
62a6438e97
commit
1596483250
15 changed files with 157 additions and 49 deletions
|
@ -1,18 +1,30 @@
|
|||
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.StoreEntryWrapper;
|
||||
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 javafx.application.Platform;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
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;
|
||||
|
||||
BookmarkList(FileBrowserModel model) {
|
||||
|
@ -28,16 +40,26 @@ final class BookmarkList extends SimpleComp {
|
|||
continue;
|
||||
}
|
||||
|
||||
var button = new JFXButton(null, e.getValue());
|
||||
var button = new Button(null, e.getValue());
|
||||
button.setOnAction(event -> {
|
||||
var fileSystem = ((ShellStore) e.getKey().getEntry().getStore());
|
||||
model.openFileSystem(fileSystem);
|
||||
event.consume();
|
||||
});
|
||||
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.setFillWidth(true);
|
||||
list.getStyleClass().add("bookmark-list");
|
||||
|
||||
var sp = new ScrollPane(list);
|
||||
sp.setFitToWidth(true);
|
||||
|
@ -45,4 +67,23 @@ final class BookmarkList extends SimpleComp {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,15 @@ import atlantafx.base.controls.Spacer;
|
|||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.browser.icon.FileIconManager;
|
||||
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.util.PlatformThread;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.BusyProperty;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
|
@ -237,7 +240,7 @@ public class FileBrowserComp extends SimpleComp {
|
|||
label.addEventHandler(DragEvent.DRAG_ENTERED, new EventHandler<DragEvent>() {
|
||||
@Override
|
||||
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())));
|
||||
|
||||
tab.setGraphic(label);
|
||||
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(label));
|
||||
|
||||
if (!this.model.getMode().equals(FileBrowserModel.Mode.BROWSER)) {
|
||||
label.setManaged(false);
|
||||
|
|
|
@ -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) {
|
||||
// Prevent multiple tabs in non browser modes
|
||||
if (!mode.equals(Mode.BROWSER)) {
|
||||
|
|
|
@ -29,14 +29,14 @@ public class FileFilterComp extends SimpleComp {
|
|||
protected Region createSimple() {
|
||||
var expanded = new SimpleBooleanProperty();
|
||||
var text = new TextFieldComp(filterString, false).createRegion();
|
||||
var button = new Button();
|
||||
text.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue && filterString.getValue() == null) {
|
||||
expanded.set(false);
|
||||
return;
|
||||
}
|
||||
if (button.isFocused()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue) {
|
||||
expanded.set(true);
|
||||
expanded.set(false);
|
||||
}
|
||||
});
|
||||
filterString.addListener((observable, oldValue, newValue) -> {
|
||||
|
@ -56,21 +56,26 @@ public class FileFilterComp extends SimpleComp {
|
|||
});
|
||||
|
||||
var fi = new FontIcon("mdi2m-magnify");
|
||||
var button = new Button();
|
||||
GrowAugment.create(false, true).augment(new SimpleCompStructure<>(button));
|
||||
Shortcuts.addShortcut(button, new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
|
||||
button.setGraphic(fi);
|
||||
button.setOnAction(event -> {
|
||||
if (expanded.get() && filterString.getValue() == null) {
|
||||
expanded.set(false);
|
||||
return;
|
||||
if (expanded.get()) {
|
||||
if (filterString.getValue() == null) {
|
||||
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) {
|
||||
text.setPrefWidth(250);
|
||||
button.getStyleClass().add(Styles.RIGHT_PILL);
|
||||
|
|
|
@ -89,6 +89,10 @@ public class FileListCompEntry {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (model.getFileSystemModel().getCurrentDirectory() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent drag and drops of files into the current directory
|
||||
if (FileBrowserClipboard.currentDragClipboard
|
||||
.getBaseDirectory().getPath()
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.xpipe.app.comp.base.LoadingOverlayComp;
|
|||
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;
|
||||
|
@ -13,9 +14,6 @@ import io.xpipe.app.fxcomps.util.PlatformThread;
|
|||
import javafx.beans.binding.Bindings;
|
||||
import javafx.collections.FXCollections;
|
||||
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.Dragboard;
|
||||
import javafx.scene.input.TransferMode;
|
||||
|
@ -63,7 +61,7 @@ public class LocalFileTransferComp extends SimpleComp {
|
|||
|
||||
var listBox = new VerticalComp(List.of(list, dragNotice));
|
||||
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 -> {
|
||||
// Accept drops from inside the app window
|
||||
if (event.getGestureSource() != null && event.getGestureSource() != struc.get()) {
|
||||
|
@ -99,12 +97,9 @@ public class LocalFileTransferComp extends SimpleComp {
|
|||
cc.putFiles(files);
|
||||
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())
|
||||
.toList()))
|
||||
.createRegion();
|
||||
new Scene(r);
|
||||
WritableImage image = r.snapshot(new SnapshotParameters(), null);
|
||||
.toList()));
|
||||
db.setDragView(image, -20, 15);
|
||||
|
||||
event.setDragDetect(true);
|
||||
|
|
|
@ -19,9 +19,11 @@ import javafx.scene.input.KeyCode;
|
|||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.Region;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class AppLayoutComp extends Comp<CompStructure<BorderPane>> {
|
||||
|
@ -71,6 +73,9 @@ public class AppLayoutComp extends Comp<CompStructure<BorderPane>> {
|
|||
|
||||
@Override
|
||||
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 sidebar = new SideMenuBarComp(selected, entries);
|
||||
pane.setCenter(selected.getValue().comp().createRegion());
|
||||
|
@ -80,10 +85,9 @@ public class AppLayoutComp extends Comp<CompStructure<BorderPane>> {
|
|||
AppPrefs.get().save();
|
||||
}
|
||||
|
||||
var r = selected.getValue().comp().createRegion();
|
||||
pane.setCenter(r);
|
||||
pane.setCenter(map.get(n));
|
||||
});
|
||||
pane.setCenter(selected.getValue().comp().createRegion());
|
||||
pane.setCenter(map.get(selected.getValue()));
|
||||
pane.setPrefWidth(1280);
|
||||
pane.setPrefHeight(720);
|
||||
AppFont.normal(pane);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.app.comp.storage.store;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.source.store.GuiDsStoreCreator;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
|
@ -20,24 +21,19 @@ public class StoreCreationBarComp extends SimpleComp {
|
|||
|
||||
@Override
|
||||
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(
|
||||
AppI18n.observable("addCommand"), new FontIcon("mdi2c-code-greater-than"), () -> {
|
||||
GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.COMMAND));
|
||||
})
|
||||
.styleClass(Styles.FLAT)
|
||||
.shortcut(new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN))
|
||||
.apply(new FancyTooltipAugment<>("addCommand"));
|
||||
|
||||
var newHostStore = new ButtonComp(
|
||||
AppI18n.observable("addHost"), new FontIcon("mdi2h-home-plus"), () -> {
|
||||
GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.HOST));
|
||||
})
|
||||
var newHostStore = new ButtonComp(AppI18n.observable("addHost"), new FontIcon("mdi2h-home-plus"), () -> {
|
||||
GuiDsStoreCreator.showCreation(
|
||||
v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.HOST));
|
||||
})
|
||||
.styleClass(Styles.FLAT)
|
||||
.shortcut(new KeyCodeCombination(KeyCode.H, KeyCombination.SHORTCUT_DOWN))
|
||||
.apply(new FancyTooltipAugment<>("addHost"));
|
||||
|
||||
|
@ -45,6 +41,7 @@ public class StoreCreationBarComp extends SimpleComp {
|
|||
AppI18n.observable("addShell"), new FontIcon("mdi2t-text-box-multiple"), () -> {
|
||||
GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.SHELL));
|
||||
})
|
||||
.styleClass(Styles.FLAT)
|
||||
.shortcut(new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN))
|
||||
.apply(new FancyTooltipAugment<>("addShell"));
|
||||
|
||||
|
@ -52,10 +49,11 @@ public class StoreCreationBarComp extends SimpleComp {
|
|||
AppI18n.observable("addDatabase"), new FontIcon("mdi2d-database-plus"), () -> {
|
||||
GuiDsStoreCreator.showCreation(v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.DATABASE));
|
||||
})
|
||||
.styleClass(Styles.FLAT)
|
||||
.shortcut(new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN))
|
||||
.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()));
|
||||
var bar = box.createRegion();
|
||||
bar.getStyleClass().add("bar");
|
||||
|
|
|
@ -40,7 +40,7 @@ public class StoreEntrySection extends Comp<CompStructure<VBox>> {
|
|||
.apply(struc -> struc.get().setPrefWidth(40))
|
||||
.disable(BindingsHelper.persist(
|
||||
Bindings.size(section.getChildren()).isEqualTo(0)))
|
||||
.grow(false, true);
|
||||
.grow(false, true).styleClass("expand-button");
|
||||
List<Comp<?>> topEntryList = List.of(button, root);
|
||||
|
||||
var all = section.getChildren();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -65,7 +65,7 @@ public class SvgCacheComp extends SimpleComp {
|
|||
}
|
||||
|
||||
var pt = new PauseTransition();
|
||||
pt.setDuration(Duration.millis(1000));
|
||||
pt.setDuration(Duration.millis(500));
|
||||
pt.setOnFinished(actionEvent -> {
|
||||
if (newValue == null || cache.getCached(newValue).isPresent()) {
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.download-background {
|
||||
-fx-border-color: -color-neutral-muted;
|
||||
-fx-border-width: 4px 0 0 0;
|
||||
-fx-border-width: 2px 0 0 0;
|
||||
-fx-padding: 1em;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,21 @@
|
|||
-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 {
|
||||
-fx-border-width: 0 0 1 1;
|
||||
}
|
||||
|
|
|
@ -29,12 +29,7 @@
|
|||
}
|
||||
|
||||
.bar .button-comp {
|
||||
-fx-border-width: 0;
|
||||
-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;
|
||||
-fx-padding: 0.3em 0em 0.3em 0em;
|
||||
}
|
||||
|
||||
.collections-bar {
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.sidebar-comp .button:hover {
|
||||
-fx-background-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
.sidebar-comp .big-icon-button-comp {
|
||||
-fx-background-radius: 0;
|
||||
}
|
||||
|
|
|
@ -22,9 +22,19 @@
|
|||
.store-entry-grid:incomplete .icon {
|
||||
-fx-opacity: 0.5;
|
||||
}
|
||||
|
||||
.store-entry-comp {
|
||||
-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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue