mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 23:20:23 +00:00
More file browser improvements
This commit is contained in:
parent
69ed60b611
commit
06cb31bf55
19 changed files with 369 additions and 131 deletions
|
@ -0,0 +1,65 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import atlantafx.base.controls.Breadcrumbs;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||
import io.xpipe.core.impl.FileNames;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ButtonBase;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.util.Callback;
|
||||
|
||||
public class FileBrowserBreadcrumbBar extends SimpleComp {
|
||||
|
||||
private final OpenFileSystemModel model;
|
||||
|
||||
public FileBrowserBreadcrumbBar(OpenFileSystemModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
Callback<Breadcrumbs.BreadCrumbItem<String>, ButtonBase> crumbFactory = crumb -> {
|
||||
var btn = new Button(FileNames.getFileName(crumb.getValue()), null);
|
||||
btn.getStyleClass().add(Styles.FLAT);
|
||||
btn.setFocusTraversable(false);
|
||||
return btn;
|
||||
};
|
||||
return createBreadcrumbs(crumbFactory, null);
|
||||
}
|
||||
|
||||
private Region createBreadcrumbs(
|
||||
Callback<Breadcrumbs.BreadCrumbItem<String>, ButtonBase> crumbFactory,
|
||||
Callback<Breadcrumbs.BreadCrumbItem<String>, ? extends Node> dividerFactory) {
|
||||
|
||||
var breadcrumbs = new Breadcrumbs<String>();
|
||||
SimpleChangeListener.apply(PlatformThread.sync(model.getCurrentPath()), val -> {
|
||||
if (val == null) {
|
||||
breadcrumbs.setSelectedCrumb(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var elements = FileNames.splitHierarchy(val);
|
||||
Breadcrumbs.BreadCrumbItem<String> items = Breadcrumbs.buildTreeModel(elements.toArray(String[]::new));
|
||||
breadcrumbs.setSelectedCrumb(items);
|
||||
});
|
||||
|
||||
if (crumbFactory != null) {
|
||||
breadcrumbs.setCrumbFactory(crumbFactory);
|
||||
}
|
||||
if (dividerFactory != null) {
|
||||
breadcrumbs.setDividerFactory(dividerFactory);
|
||||
}
|
||||
|
||||
breadcrumbs.selectedCrumbProperty().addListener((obs, old, val) -> {
|
||||
model.cd(val.getValue()).ifPresent(s -> {
|
||||
model.cd(s);
|
||||
});
|
||||
});
|
||||
|
||||
return breadcrumbs;
|
||||
}
|
||||
}
|
|
@ -32,7 +32,6 @@ public class FileBrowserModel {
|
|||
public static final FileBrowserModel DEFAULT = new FileBrowserModel(Mode.BROWSER);
|
||||
|
||||
private final Mode mode;
|
||||
private final ObservableList<FileBrowserEntry> selectedFiles = FXCollections.observableArrayList();
|
||||
|
||||
@Setter
|
||||
private Consumer<List<FileStore>> onFinish;
|
||||
|
@ -46,6 +45,7 @@ public class FileBrowserModel {
|
|||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
var selectedFiles = openFileSystems.get(0).getFileList().getSelected();
|
||||
closeFileSystem(openFileSystems.get(0));
|
||||
|
||||
if (selectedFiles.size() == 0) {
|
||||
|
|
|
@ -3,6 +3,8 @@ package io.xpipe.app.browser;
|
|||
import atlantafx.base.controls.Spacer;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
||||
import io.xpipe.app.fxcomps.impl.LabelComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -54,10 +56,7 @@ public class FileBrowserStatusBarComp extends SimpleComp {
|
|||
AppFont.small(bar);
|
||||
|
||||
// Use status bar as an extension of file list
|
||||
bar.setOnMouseClicked(event -> {
|
||||
model.getFileList().getSelected().clear();
|
||||
event.consume();
|
||||
});
|
||||
new ContextMenuAugment<>(false, () -> new FileContextMenu(model,true)).augment(new SimpleCompStructure<>(bar));
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
|
|
@ -5,17 +5,12 @@ package io.xpipe.app.browser;
|
|||
import io.xpipe.app.browser.action.BranchAction;
|
||||
import io.xpipe.app.browser.action.BrowserAction;
|
||||
import io.xpipe.app.browser.action.LeafAction;
|
||||
import io.xpipe.app.util.BusyProperty;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.control.SeparatorMenuItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
final class FileContextMenu extends ContextMenu {
|
||||
|
||||
private final OpenFileSystemModel model;
|
||||
|
@ -29,6 +24,8 @@ final class FileContextMenu extends ContextMenu {
|
|||
}
|
||||
|
||||
private void createMenu() {
|
||||
AppFont.normal(this.getStyleableNode());
|
||||
|
||||
var selected = empty ? FXCollections.<FileBrowserEntry>observableArrayList() : model.getFileList().getSelected();
|
||||
|
||||
for (BrowserAction.Category cat : BrowserAction.Category.values()) {
|
||||
|
@ -53,7 +50,7 @@ final class FileContextMenu extends ContextMenu {
|
|||
|
||||
for (BrowserAction a : all) {
|
||||
if (a instanceof LeafAction la) {
|
||||
getItems().add(toItem(la, selected, s -> s));
|
||||
getItems().add(la.toItem(model, selected, s -> s));
|
||||
}
|
||||
|
||||
if (a instanceof BranchAction la) {
|
||||
|
@ -62,7 +59,7 @@ final class FileContextMenu extends ContextMenu {
|
|||
if (!sub.isApplicable(model, selected)) {
|
||||
continue;
|
||||
}
|
||||
m.getItems().add(toItem(sub, selected, s -> "... " + s));
|
||||
m.getItems().add(sub.toItem(model, selected, s -> s));
|
||||
}
|
||||
var graphic = a.getIcon(model, selected);
|
||||
if (graphic != null) {
|
||||
|
@ -74,26 +71,4 @@ final class FileContextMenu extends ContextMenu {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MenuItem toItem(LeafAction a, List<FileBrowserEntry> selected, UnaryOperator<String> nameFunc) {
|
||||
var mi = new MenuItem(nameFunc.apply(a.getName(model, selected)));
|
||||
mi.setOnAction(event -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
BusyProperty.execute(model.getBusy(), () -> {
|
||||
a.execute(model, selected);
|
||||
});
|
||||
});
|
||||
event.consume();
|
||||
});
|
||||
if (a.getShortcut() != null) {
|
||||
mi.setAccelerator(a.getShortcut());
|
||||
}
|
||||
var graphic = a.getIcon(model, selected);
|
||||
if (graphic != null) {
|
||||
mi.setGraphic(graphic);
|
||||
}
|
||||
mi.setMnemonicParsing(false);
|
||||
mi.setDisable(!a.isActive(model, selected));
|
||||
return mi;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,6 @@ final class FileListComp extends AnchorPane {
|
|||
.getPath()))
|
||||
.toList();
|
||||
fileList.getSelected().setAll(toSelect);
|
||||
fileList.getFileSystemModel().getBrowserModel().getSelectedFiles().setAll(toSelect);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
var toUnselect = table.getSelectionModel().getSelectedItems().stream()
|
||||
|
@ -380,7 +379,7 @@ final class FileListComp extends AnchorPane {
|
|||
public FilenameCell(Property<FileBrowserEntry> editing) {
|
||||
editing.addListener((observable, oldValue, newValue) -> {
|
||||
if (getTableRow().getItem() != null && getTableRow().getItem().equals(newValue)) {
|
||||
textField.requestFocus();
|
||||
PlatformThread.runLaterIfNeeded(() -> textField.requestFocus());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,24 +1,31 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import io.xpipe.app.browser.action.BranchAction;
|
||||
import io.xpipe.app.browser.action.BrowserAction;
|
||||
import io.xpipe.app.comp.base.AlertOverlayComp;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageComp;
|
||||
import io.xpipe.app.fxcomps.impl.TextFieldComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.MenuButton;
|
||||
import javafx.scene.control.ToolBar;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.kordamp.ikonli.feather.Feather;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import static io.xpipe.app.browser.FileListModel.PREDICATE_NOT_HIDDEN;
|
||||
import static io.xpipe.app.util.Controls.iconButton;
|
||||
|
||||
|
@ -32,7 +39,13 @@ public class OpenFileSystemComp extends SimpleComp {
|
|||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var creatingProperty = new SimpleBooleanProperty();
|
||||
var alertOverlay = new AlertOverlayComp(
|
||||
Comp.of(() -> createContent()),
|
||||
model.getOverlay());
|
||||
return alertOverlay.createRegion();
|
||||
}
|
||||
|
||||
private Region createContent() {
|
||||
var backBtn = iconButton(Feather.ARROW_LEFT, false);
|
||||
backBtn.setOnAction(e -> model.back());
|
||||
backBtn.disableProperty().bind(model.getHistory().canGoBackProperty().not());
|
||||
|
@ -57,30 +70,23 @@ public class OpenFileSystemComp extends SimpleComp {
|
|||
Shortcuts.addShortcut(refreshBtn, new KeyCodeCombination(KeyCode.F5));
|
||||
|
||||
var terminalBtn = new Button(null, new FontIcon("mdi2c-code-greater-than"));
|
||||
terminalBtn.setOnAction(e -> model.openTerminalAsync(model.getCurrentPath().get()));
|
||||
terminalBtn.setOnAction(
|
||||
e -> model.openTerminalAsync(model.getCurrentPath().get()));
|
||||
terminalBtn.disableProperty().bind(PlatformThread.sync(model.getNoDirectory()));
|
||||
|
||||
var addBtn = new Button(null, new FontIcon("mdmz-plus"));
|
||||
addBtn.setOnAction(e -> {
|
||||
creatingProperty.set(true);
|
||||
var addBtn = new MenuButton(null, new FontIcon("mdmz-plus"));
|
||||
var s = model.getFileList().getSelected();
|
||||
var action = (BranchAction) BrowserAction.ALL.stream().filter(browserAction -> browserAction.getName(model, s).equals("New")).findFirst().orElseThrow();
|
||||
action.getBranchingActions().forEach(action1 -> {
|
||||
addBtn.getItems().add(action1.toItem(model, s, UnaryOperator.identity()));
|
||||
});
|
||||
addBtn.disableProperty().bind(PlatformThread.sync(model.getNoDirectory()));
|
||||
Shortcuts.addShortcut(addBtn, new KeyCodeCombination(KeyCode.PLUS));
|
||||
|
||||
var filter = new FileFilterComp(model.getFilter()).createStructure();
|
||||
Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
|
||||
|
||||
var topBar = new ToolBar();
|
||||
topBar.getItems().setAll(
|
||||
backBtn,
|
||||
forthBtn,
|
||||
new Spacer(10),
|
||||
pathBar,
|
||||
filter.get(),
|
||||
refreshBtn,
|
||||
terminalBtn,
|
||||
addBtn
|
||||
);
|
||||
topBar.getItems()
|
||||
.setAll(backBtn, forthBtn, new Spacer(10), new FileBrowserBreadcrumbBar(model).createRegion(), filter.get(), refreshBtn, terminalBtn, addBtn);
|
||||
|
||||
// ~
|
||||
|
||||
|
@ -93,56 +99,6 @@ public class OpenFileSystemComp extends SimpleComp {
|
|||
VBox.setVgrow(directoryView, Priority.ALWAYS);
|
||||
root.setPadding(Insets.EMPTY);
|
||||
model.getFileList().predicateProperty().set(PREDICATE_NOT_HIDDEN);
|
||||
|
||||
var pane = new StackPane();
|
||||
pane.getChildren().add(root);
|
||||
|
||||
var creation = createCreationWindow(creatingProperty);
|
||||
var creationPane = new StackPane(creation);
|
||||
creationPane.setAlignment(Pos.CENTER);
|
||||
creationPane.setOnMouseClicked(event -> {
|
||||
creatingProperty.set(false);
|
||||
});
|
||||
pane.getChildren().add(creationPane);
|
||||
creationPane.visibleProperty().bind(creatingProperty);
|
||||
creationPane.managedProperty().bind(creatingProperty);
|
||||
|
||||
return pane;
|
||||
}
|
||||
|
||||
private Region createCreationWindow(BooleanProperty creating) {
|
||||
var creationName = new TextField();
|
||||
creating.addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue) {
|
||||
creationName.setText("");
|
||||
}
|
||||
});
|
||||
var createFileButton = new Button("File", new PrettyImageComp(new SimpleStringProperty("file_drag_icon.png"), 20, 20).createRegion());
|
||||
createFileButton.setOnAction(event -> {
|
||||
model.createFileAsync(creationName.getText());
|
||||
creating.set(false);
|
||||
});
|
||||
var createDirectoryButton = new Button("Directory", new PrettyImageComp(new SimpleStringProperty("folder_closed.svg"), 20, 20).createRegion());
|
||||
createDirectoryButton.setOnAction(event -> {
|
||||
model.createDirectoryAsync(creationName.getText());
|
||||
creating.set(false);
|
||||
});
|
||||
var buttonBar = new ButtonBar();
|
||||
buttonBar.getButtons().addAll(createFileButton, createDirectoryButton);
|
||||
var creationContent = new VBox(creationName, buttonBar);
|
||||
creationContent.setSpacing(15);
|
||||
var creation = new TitledPane("New ...", creationContent);
|
||||
creation.setMaxWidth(400);
|
||||
creation.setCollapsible(false);
|
||||
creationContent.setPadding(new Insets(15));
|
||||
creation.getStyleClass().add("elevated-3");
|
||||
|
||||
creating.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
creationName.requestFocus();
|
||||
}
|
||||
});
|
||||
|
||||
return creation;
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
package io.xpipe.app.browser;
|
||||
|
||||
import io.xpipe.app.comp.base.AlertOverlayComp;
|
||||
import io.xpipe.app.core.AppCache;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
|
@ -43,6 +44,7 @@ public final class OpenFileSystemModel {
|
|||
private final BooleanProperty noDirectory = new SimpleBooleanProperty();
|
||||
private final Property<OpenFileSystemSavedState> savedState = new SimpleObjectProperty<>();
|
||||
private final OpenFileSystemCache cache = new OpenFileSystemCache(this);
|
||||
private final Property<AlertOverlayComp.OverlayContent> overlay = new SimpleObjectProperty<>();
|
||||
|
||||
public OpenFileSystemModel(FileBrowserModel browserModel) {
|
||||
this.browserModel = browserModel;
|
||||
|
@ -239,7 +241,7 @@ public final class OpenFileSystemModel {
|
|||
}
|
||||
|
||||
public void createFileAsync(String name) {
|
||||
if (name.isBlank()) {
|
||||
if (name == null || name.isBlank()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,37 @@ package io.xpipe.app.browser.action;
|
|||
|
||||
import io.xpipe.app.browser.FileBrowserEntry;
|
||||
import io.xpipe.app.browser.OpenFileSystemModel;
|
||||
import io.xpipe.app.util.BusyProperty;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.scene.control.MenuItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public interface LeafAction extends BrowserAction {
|
||||
|
||||
public abstract void execute(OpenFileSystemModel model, List<FileBrowserEntry> entries) throws Exception;
|
||||
|
||||
default MenuItem toItem(OpenFileSystemModel model, List<FileBrowserEntry> selected, UnaryOperator<String> nameFunc) {
|
||||
var mi = new MenuItem(nameFunc.apply(getName(model, selected)));
|
||||
mi.setOnAction(event -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
BusyProperty.execute(model.getBusy(), () -> {
|
||||
execute(model, selected);
|
||||
});
|
||||
});
|
||||
event.consume();
|
||||
});
|
||||
if (getShortcut() != null) {
|
||||
mi.setAccelerator(getShortcut());
|
||||
}
|
||||
var graphic = getIcon(model, selected);
|
||||
if (graphic != null) {
|
||||
mi.setGraphic(graphic);
|
||||
}
|
||||
mi.setMnemonicParsing(false);
|
||||
mi.setDisable(!isActive(model, selected));
|
||||
return mi;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,13 @@ import io.xpipe.app.fxcomps.impl.PrettyImageComp;
|
|||
import io.xpipe.core.store.FileSystem;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
||||
public class FileIcons {
|
||||
|
||||
public class FileBrowserIcons {
|
||||
public static PrettyImageComp createDefaultFileIcon() {
|
||||
return new PrettyImageComp(new SimpleStringProperty("default_file.svg"), 22, 22);
|
||||
}
|
||||
public static PrettyImageComp createDefaultDirectoryIcon() {
|
||||
return new PrettyImageComp(new SimpleStringProperty("default_folder.svg"), 22, 22);
|
||||
}
|
||||
public static PrettyImageComp createIcon(FileType type) {
|
||||
return new PrettyImageComp(new SimpleStringProperty(type.getIcon()), 22, 22);
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package io.xpipe.app.comp.base;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
import javafx.scene.control.TitledPane;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import lombok.Value;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
public class AlertOverlayComp extends SimpleComp {
|
||||
|
||||
|
||||
public AlertOverlayComp(Comp<?> background, Property<OverlayContent> overlayContent) {
|
||||
this.background = background;
|
||||
this.overlayContent = overlayContent;
|
||||
}
|
||||
|
||||
@Value
|
||||
public static class OverlayContent {
|
||||
|
||||
ObservableValue<String> title;
|
||||
Comp<?> content;
|
||||
Runnable onFinish;
|
||||
}
|
||||
|
||||
private final Comp<?> background;
|
||||
private final Property<OverlayContent> overlayContent;
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var bgRegion = background.createRegion();
|
||||
var pane = new StackPane(bgRegion);
|
||||
pane.setPickOnBounds(false);
|
||||
PlatformThread.sync(overlayContent).addListener((observable, oldValue, newValue) -> {
|
||||
if (oldValue != null) {
|
||||
pane.getChildren().remove(1);
|
||||
}
|
||||
|
||||
if (newValue != null) {
|
||||
var r = newValue.content.createRegion();
|
||||
|
||||
var finishButton = new Button("Finish");
|
||||
Styles.toggleStyleClass(finishButton, Styles.FLAT);
|
||||
finishButton.setOnAction(event -> {
|
||||
newValue.onFinish.run();
|
||||
overlayContent.setValue(null);
|
||||
});
|
||||
|
||||
var buttonBar = new ButtonBar();
|
||||
buttonBar.getButtons().addAll(finishButton);
|
||||
var box = new VBox(r, buttonBar);
|
||||
box.setSpacing(15);
|
||||
box.setPadding(new Insets(15));
|
||||
var tp = new TitledPane(newValue.title.getValue(), box);
|
||||
tp.setMaxWidth(400);
|
||||
tp.setCollapsible(false);
|
||||
tp.getStyleClass().add("elevated-3");
|
||||
|
||||
var closeButton = new Button(null, new FontIcon("mdi2w-window-close"));
|
||||
closeButton.setOnAction(event -> {
|
||||
overlayContent.setValue(null);
|
||||
});
|
||||
Shortcuts.addShortcut(closeButton, new KeyCodeCombination(KeyCode.ESCAPE));
|
||||
Styles.toggleStyleClass(closeButton, Styles.FLAT);
|
||||
var close = new AnchorPane(closeButton);
|
||||
close.setPickOnBounds(false);
|
||||
AnchorPane.setTopAnchor(closeButton, 10.0);
|
||||
AnchorPane.setRightAnchor(closeButton, 10.0);
|
||||
|
||||
var stack = new StackPane(tp, close);
|
||||
stack.setOnMouseClicked(event -> {
|
||||
if (overlayContent.getValue() != null) {
|
||||
overlayContent.setValue(null);
|
||||
}
|
||||
});
|
||||
stack.setAlignment(Pos.CENTER);
|
||||
close.maxWidthProperty().bind(tp.widthProperty());
|
||||
close.maxHeightProperty().bind(tp.heightProperty());
|
||||
|
||||
pane.getChildren().add(stack);
|
||||
}
|
||||
});
|
||||
return pane;
|
||||
}
|
||||
}
|
|
@ -22,20 +22,18 @@ public class ContextMenuAugment<S extends CompStructure<?>> implements Augment<S
|
|||
public void augment(S struc) {
|
||||
var r = struc.get();
|
||||
r.setOnMousePressed(event -> {
|
||||
if (currentContextMenu != null && currentContextMenu.isShowing()) {
|
||||
currentContextMenu.hide();
|
||||
currentContextMenu = null;
|
||||
}
|
||||
|
||||
if ((showOnPrimaryButton && event.getButton() == MouseButton.PRIMARY)
|
||||
|| (!showOnPrimaryButton && event.getButton() == MouseButton.SECONDARY)) {
|
||||
if (currentContextMenu != null && currentContextMenu.isShowing()) {
|
||||
currentContextMenu.hide();
|
||||
currentContextMenu = null;
|
||||
}
|
||||
|
||||
if (event.getButton() == MouseButton.SECONDARY) {
|
||||
var cm = contextMenu.get();
|
||||
if (cm != null) {
|
||||
cm.setAutoHide(true);
|
||||
cm.show(r, event.getScreenX(), event.getScreenY());
|
||||
currentContextMenu = cm;
|
||||
}
|
||||
var cm = contextMenu.get();
|
||||
if (cm != null) {
|
||||
cm.setAutoHide(true);
|
||||
cm.show(r, event.getScreenX(), event.getScreenY());
|
||||
currentContextMenu = cm;
|
||||
}
|
||||
|
||||
event.consume();
|
||||
|
|
|
@ -81,6 +81,10 @@
|
|||
-fx-border-insets: 0px;
|
||||
}
|
||||
|
||||
.browser .breadcrumbs .button {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.browser .context-menu .accelerator-text {
|
||||
-fx-padding: 3px 0px 3px 50px;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.core.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -49,6 +50,26 @@ public class FileNames {
|
|||
return components.get(components.size() - 1);
|
||||
}
|
||||
|
||||
public static List<String> splitHierarchy(String file) {
|
||||
if (file.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
file = file + "/";
|
||||
var list = new ArrayList<String>();
|
||||
int lastElementStart = 0;
|
||||
for (int i = 0; i < file.length(); i++) {
|
||||
if (file.charAt(i) == '\\' || file.charAt(i) == '/') {
|
||||
if (i - lastElementStart > 0) {
|
||||
list.add(file.substring(0, i));
|
||||
}
|
||||
|
||||
lastElementStart = i + 1;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static String getBaseName(String file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return null;
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.xpipe.ext.base.browser;
|
|||
import io.xpipe.app.browser.FileBrowserEntry;
|
||||
import io.xpipe.app.browser.OpenFileSystemModel;
|
||||
import io.xpipe.app.browser.action.BrowserAction;
|
||||
import io.xpipe.app.browser.icon.FileIcons;
|
||||
import io.xpipe.app.browser.icon.FileBrowserIcons;
|
||||
import io.xpipe.app.browser.icon.FileType;
|
||||
import javafx.scene.Node;
|
||||
|
||||
|
@ -19,7 +19,7 @@ public interface FileTypeAction extends BrowserAction {
|
|||
|
||||
@Override
|
||||
default Node getIcon(OpenFileSystemModel model, List<FileBrowserEntry> entries) {
|
||||
return FileIcons.createIcon(getType()).createRegion();
|
||||
return FileBrowserIcons.createIcon(getType()).createRegion();
|
||||
}
|
||||
|
||||
FileType getType();
|
||||
|
|
|
@ -5,7 +5,13 @@ import io.xpipe.app.browser.OpenFileSystemModel;
|
|||
import io.xpipe.app.browser.action.BranchAction;
|
||||
import io.xpipe.app.browser.action.BrowserAction;
|
||||
import io.xpipe.app.browser.action.LeafAction;
|
||||
import io.xpipe.app.browser.icon.FileBrowserIcons;
|
||||
import io.xpipe.app.comp.base.AlertOverlayComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.TextField;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -19,7 +25,7 @@ public class NewItemAction implements BrowserAction, BranchAction {
|
|||
|
||||
@Override
|
||||
public String getName(OpenFileSystemModel model, List<FileBrowserEntry> entries) {
|
||||
return "Create new";
|
||||
return "New";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,21 +49,46 @@ public class NewItemAction implements BrowserAction, BranchAction {
|
|||
new LeafAction() {
|
||||
@Override
|
||||
public String getName(OpenFileSystemModel model, List<FileBrowserEntry> entries) {
|
||||
return "file";
|
||||
return "File";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(OpenFileSystemModel model, List<FileBrowserEntry> entries) throws Exception {
|
||||
var name = new SimpleStringProperty();
|
||||
model.getOverlay().setValue(new AlertOverlayComp.OverlayContent(AppI18n.observable("newFile"), Comp.of(() -> {
|
||||
var creationName = new TextField();
|
||||
creationName.textProperty().bindBidirectional(name);
|
||||
return creationName;
|
||||
}), () -> {
|
||||
model.createFileAsync(name.getValue());
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getIcon(OpenFileSystemModel model, List<FileBrowserEntry> entries) {
|
||||
return FileBrowserIcons.createDefaultFileIcon().createRegion();
|
||||
}
|
||||
},
|
||||
new LeafAction() {
|
||||
@Override
|
||||
public String getName(OpenFileSystemModel model, List<FileBrowserEntry> entries) {
|
||||
return "directory";
|
||||
return "Directory";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(OpenFileSystemModel model, List<FileBrowserEntry> entries) throws Exception {
|
||||
var name = new SimpleStringProperty();
|
||||
model.getOverlay().setValue(new AlertOverlayComp.OverlayContent(AppI18n.observable("newDirectory"), Comp.of(() -> {
|
||||
var creationName = new TextField();
|
||||
creationName.textProperty().bindBidirectional(name);
|
||||
return creationName;
|
||||
}), () -> {
|
||||
model.createDirectoryAsync(name.getValue());
|
||||
}));
|
||||
}
|
||||
@Override
|
||||
public Node getIcon(OpenFileSystemModel model, List<FileBrowserEntry> entries) {
|
||||
return FileBrowserIcons.createDefaultDirectoryIcon().createRegion();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,11 +16,21 @@ public class OpenTerminalAction implements LeafAction {
|
|||
|
||||
@Override
|
||||
public void execute(OpenFileSystemModel model, List<FileBrowserEntry> entries) throws Exception {
|
||||
if (entries.size() == 0) {
|
||||
model.openTerminalAsync(model.getCurrentDirectory().getPath());
|
||||
return;
|
||||
}
|
||||
|
||||
for (var entry : entries) {
|
||||
model.openTerminalAsync(entry.getRawFileEntry().getPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsEmptySelection() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Category getCategory() {
|
||||
return Category.OPEN;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package io.xpipe.ext.base.browser;
|
||||
|
||||
import io.xpipe.app.browser.FileBrowserEntry;
|
||||
import io.xpipe.app.browser.OpenFileSystemModel;
|
||||
import io.xpipe.app.browser.action.LeafAction;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RenameAction implements LeafAction {
|
||||
|
||||
@Override
|
||||
public void execute(OpenFileSystemModel model, List<FileBrowserEntry> entries) throws Exception {
|
||||
model.getFileList().getEditing().setValue(entries.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Category getCategory() {
|
||||
return Category.MUTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getIcon(OpenFileSystemModel model, List<FileBrowserEntry> entries) {
|
||||
return new FontIcon("mdi2r-rename-box");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(OpenFileSystemModel model, List<FileBrowserEntry> entries) {
|
||||
return entries.size() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyCombination getShortcut() {
|
||||
return new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(OpenFileSystemModel model, List<FileBrowserEntry> entries) {
|
||||
return "Rename";
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ open module io.xpipe.ext.base {
|
|||
CopyPathAction,
|
||||
PasteAction,
|
||||
NewItemAction,
|
||||
RenameAction,
|
||||
DeleteAction,
|
||||
UnzipAction,
|
||||
JarAction;
|
||||
|
|
|
@ -9,6 +9,8 @@ destination=Destination
|
|||
configuration=Configuration
|
||||
selectOutput=Select Output
|
||||
options=Options
|
||||
newFile=New file
|
||||
newDirectory=New directory
|
||||
copyShareLink=Copy share link
|
||||
selectStore=Select Store
|
||||
saveSource=Save for later
|
||||
|
|
Loading…
Reference in a new issue