Final fixes [release]

This commit is contained in:
crschnick 2023-05-21 14:12:06 +00:00
parent f48b1fa212
commit cc1fb789c6
24 changed files with 209 additions and 60 deletions

View file

@ -30,6 +30,7 @@ import javafx.scene.control.*;
import javafx.scene.control.skin.TableViewSkin;
import javafx.scene.control.skin.VirtualFlow;
import javafx.scene.input.DragEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
@ -221,7 +222,17 @@ final class BrowserFileListComp extends SimpleComp {
table.setRowFactory(param -> {
TableRow<BrowserEntry> row = new TableRow<>();
new ContextMenuAugment<>(true, true, () -> {
new ContextMenuAugment<>(event -> {
if (row.getItem() != null && row.getItem().getRawFileEntry().isDirectory()) {
return event.getButton() == MouseButton.SECONDARY;
}
if (row.getItem() != null && !row.getItem().getRawFileEntry().isDirectory()) {
return event.getButton() == MouseButton.SECONDARY || event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2;
}
return false;
}, () -> {
if (row.getItem() != null && row.getItem().isSynthetic()) {
return null;
}

View file

@ -49,11 +49,11 @@ public class BrowserFileListCompEntry {
if (t.getButton() == MouseButton.PRIMARY && t.isShiftDown()) {
var tv = ((TableView<BrowserEntry>) row.getParent().getParent().getParent().getParent());
var all = tv.getItems();
var min = tv.getSelectionModel().getSelectedItems().stream().mapToInt(entry -> all.indexOf(entry)).min().orElse(1);
var max = tv.getSelectionModel().getSelectedItems().stream().mapToInt(entry -> all.indexOf(entry)).max().orElse(all.size() - 1);
var end = all.indexOf(item);
var min = tv.getSelectionModel().getSelectedIndices().stream().mapToInt(value -> value).min().orElse(1);
var max = tv.getSelectionModel().getSelectedIndices().stream().mapToInt(value -> value).max().orElse(all.size() - 1);
var end = tv.getSelectionModel().getFocusedIndex();
var start = end > min ? min : max;
model.getSelection().setAll(all.subList(Math.min(start, end), Math.max(start, end) + 1));
tv.getSelectionModel().selectRange(Math.min(start, end), Math.max(start, end) + 1);
t.consume();
return;
}

View file

@ -9,6 +9,7 @@ import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import lombok.Getter;
@ -34,6 +35,7 @@ public final class BrowserFileListModel {
private final Property<List<BrowserEntry>> shown = new SimpleObjectProperty<>(new ArrayList<>());
private final ObjectProperty<Predicate<BrowserEntry>> predicateProperty =
new SimpleObjectProperty<>(path -> true);
private final ObservableList<BrowserEntry> previousSelection = FXCollections.observableArrayList();
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
private final ObservableList<FileSystem.FileEntry> selectedRaw =
BindingsHelper.mappedContentBinding(selection, entry -> entry.getRawFileEntry());
@ -48,6 +50,10 @@ public final class BrowserFileListModel {
fileSystemModel.getFilter().addListener((observable, oldValue, newValue) -> {
refreshShown();
});
selection.addListener((ListChangeListener<? super BrowserEntry>) c -> {
previousSelection.setAll(c.getList());
});
}
public BrowserModel.Mode getMode() {

View file

@ -60,7 +60,7 @@ public class BrowserStatusBarComp extends SimpleComp {
AppFont.small(bar);
// Use status bar as an extension of file list
new ContextMenuAugment<>(false, true, () -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(bar));
new ContextMenuAugment<>(() -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(bar));
return bar;
}

View file

@ -15,6 +15,7 @@ import javafx.scene.control.ToolBar;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
@ -57,7 +58,7 @@ public class OpenFileSystemComp extends SimpleComp {
terminalBtn.disableProperty().bind(PlatformThread.sync(model.getNoDirectory()));
var menuButton = new MenuButton(null, new FontIcon("mdral-folder_open"));
new ContextMenuAugment<>(true, false, () -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(menuButton));
new ContextMenuAugment<>(event -> event.getButton() == MouseButton.PRIMARY, () -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(menuButton));
var filter = new BrowserFilterComp(model.getFilter()).createStructure();
Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));

View file

@ -9,17 +9,6 @@ public interface ApplicationPathAction extends BrowserAction {
public abstract String getExecutable();
@Override
public default boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 0) {
return false;
}
return entries.stream().allMatch(entry -> isApplicable(model, entry));
}
boolean isApplicable(OpenFileSystemModel model, BrowserEntry entry);
@Override
public default boolean isActive(OpenFileSystemModel model, List<BrowserEntry> entries) {
return model.getCache().isApplicationInPath(getExecutable());

View file

@ -0,0 +1,25 @@
package io.xpipe.app.browser.action;
import io.xpipe.app.browser.BrowserEntry;
import java.util.List;
public class BrowserActionFormatter {
public static String filesArgument(List<BrowserEntry> entries) {
return entries.size() == 1 ? entries.get(0).getOptionallyQuotedFileName() : "(" + entries.size() + ")";
}
public static String centerEllipsis(String input, int length) {
if (input == null) {
return "";
}
if (input.length() <= length) {
return input;
}
var half = (length / 2) - 5;
return input.substring(0, half) + " ... " + input.substring(input.length() - half);
}
}

View file

@ -18,12 +18,24 @@ public abstract class ExecuteApplicationAction implements LeafAction, Applicatio
cc.discardOrThrow();
}
}
if (detach() && refresh()) {
throw new IllegalStateException();
}
if (refresh()) {
model.refreshSync();
}
}
protected boolean detach() {
return false;
}
protected boolean refresh() {
return false;
}
protected abstract String createCommand(OpenFileSystemModel model, BrowserEntry entry);
}

View file

@ -0,0 +1,25 @@
package io.xpipe.app.browser.action;
import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.util.FileOpener;
import io.xpipe.core.process.ShellControl;
import java.util.List;
public abstract class ToFileCommandAction implements LeafAction, ApplicationPathAction {
@Override
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
ShellControl sc = model.getFileSystem().getShell().orElseThrow();
for (BrowserEntry entry : entries) {
var command = createCommand(model, entry);
try (var cc = sc.command(command).workingDirectory(model.getCurrentDirectory().getPath()).start()) {
cc.discardErr();
FileOpener.openCommandOutput(entry.getFileName(), entry, cc);
}
}
}
protected abstract String createCommand(OpenFileSystemModel model, BrowserEntry entry);
}

View file

@ -17,7 +17,7 @@ public class SourceCollectionContextMenu<S extends CompStructure<?>> extends Con
public SourceCollectionContextMenu(
boolean showOnPrimaryButton, SourceCollectionWrapper group, Region renameTextField) {
super(showOnPrimaryButton, true, () -> createContextMenu(group, renameTextField));
super(() -> createContextMenu(group, renameTextField));
}
private static void onDelete(SourceCollectionWrapper group) {

View file

@ -20,7 +20,7 @@ public class SourceEntryContextMenu<S extends CompStructure<?>> extends ContextM
public SourceEntryContextMenu(boolean showOnPrimaryButton, SourceEntryWrapper entry, Region renameTextField) {
super(showOnPrimaryButton, true, () -> createContextMenu(entry, renameTextField));
super(() -> createContextMenu(entry, renameTextField));
}
protected static ContextMenu createContextMenu(SourceEntryWrapper entry, Region renameTextField) {

View file

@ -26,6 +26,7 @@ import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region;
@ -164,7 +165,7 @@ public class StoreEntryComp extends SimpleComp {
});
});
new ContextMenuAugment<>(false, true, () -> StoreEntryComp.this.createContextMenu()).augment(new SimpleCompStructure<>(button));
new ContextMenuAugment<>(() -> StoreEntryComp.this.createContextMenu()).augment(new SimpleCompStructure<>(button));
return button;
}
@ -213,7 +214,7 @@ public class StoreEntryComp extends SimpleComp {
private Comp<?> createSettingsButton() {
var settingsButton = new IconButtonComp("mdomz-settings");
settingsButton.styleClass("settings");
settingsButton.apply(new ContextMenuAugment<>(true, false, () -> StoreEntryComp.this.createContextMenu()));
settingsButton.apply(new ContextMenuAugment<>(event -> event.getButton() == MouseButton.PRIMARY, () -> StoreEntryComp.this.createContextMenu()));
settingsButton.apply(GrowAugment.create(false, true));
settingsButton.apply(s -> {
s.get().prefWidthProperty().bind(Bindings.divide(s.get().heightProperty(), 1.35));

View file

@ -3,22 +3,27 @@ package io.xpipe.app.fxcomps.augment;
import io.xpipe.app.fxcomps.CompStructure;
import javafx.scene.control.ContextMenu;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class ContextMenuAugment<S extends CompStructure<?>> implements Augment<S> {
private final boolean showOnPrimaryButton;
private final boolean showOnSecondaryButton;
private final Predicate<MouseEvent> show;
private final Supplier<ContextMenu> contextMenu;
public ContextMenuAugment(boolean showOnPrimaryButton, boolean showOnSecondaryButton, Supplier<ContextMenu> contextMenu) {
this.showOnPrimaryButton = showOnPrimaryButton;
this.showOnSecondaryButton = showOnSecondaryButton;
private static ContextMenu currentContextMenu;
public ContextMenuAugment(Predicate<MouseEvent> show, Supplier<ContextMenu> contextMenu) {
this.show = show;
this.contextMenu = contextMenu;
}
private static ContextMenu currentContextMenu;
public ContextMenuAugment(Supplier<ContextMenu> contextMenu) {
this.show = event -> event.getButton() == MouseButton.SECONDARY;
this.contextMenu = contextMenu;
}
@Override
public void augment(S struc) {
@ -29,8 +34,7 @@ public class ContextMenuAugment<S extends CompStructure<?>> implements Augment<S
currentContextMenu = null;
}
if ((showOnPrimaryButton && event.getButton() == MouseButton.PRIMARY)
|| (showOnSecondaryButton && event.getButton() == MouseButton.SECONDARY)) {
if (show.test(event)) {
var cm = contextMenu.get();
if (cm != null) {
cm.setAutoHide(true);

View file

@ -166,10 +166,12 @@ public class FileBridge {
}
var entry = new Entry(file, key, keyName, in -> {
try (var out = output.get()) {
in.transferTo(out);
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle();
if (output != null) {
try (var out = output.get()) {
in.transferTo(out);
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle();
}
}
});
entry.registerChange();

View file

@ -4,9 +4,12 @@ import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.impl.FileNames;
import io.xpipe.core.impl.LocalStore;
import io.xpipe.core.process.CommandControl;
import io.xpipe.core.process.OsType;
import io.xpipe.core.store.FileSystem;
import lombok.SneakyThrows;
import java.io.FilterInputStream;
import java.nio.file.Path;
import java.util.function.Consumer;
@ -75,11 +78,28 @@ public class FileOpener {
}
} catch (Exception e) {
ErrorEvent.fromThrowable(e)
.description("Unable to open file " + file).handle();
.description("Unable to open file " + file)
.handle();
}
}
public static void openString(String keyName, Object key, String input, Consumer<String> output) {
FileBridge.get().openString(keyName, key, input, output, file -> openInTextEditor(file));
}
public static void openCommandOutput(String keyName, Object key, CommandControl cc) {
FileBridge.get()
.openIO(
keyName,
key,
() -> new FilterInputStream(cc.getStdout()) {
@Override
@SneakyThrows
public void close() {
cc.close();
}
},
null,
file -> openInTextEditor(file));
}
}

View file

@ -76,7 +76,7 @@ public class FileNames {
}
var name = FileNames.getFileName(file);
var split = file.lastIndexOf("\\.");
var split = file.lastIndexOf(".");
if (split == -1) {
return name;
}

2
dist/build.gradle vendored
View file

@ -41,7 +41,7 @@ if (rootProject.fullVersion) {
apply from: 'portable.gradle'
apply from: 'proguard.gradle'
apply from: 'jreleaser.gradle'
//apply from: 'choco.gradle'
apply from: 'choco.gradle'
//apply from: 'homebrew.gradle'
//apply from: 'flatpak.gradle'

View file

@ -4,6 +4,7 @@ import io.xpipe.app.browser.BrowserEntry;
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.BrowserActionFormatter;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.core.impl.FileNames;
@ -36,6 +37,10 @@ public class CopyPathAction implements BrowserAction, BranchAction {
new LeafAction() {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return " " + BrowserActionFormatter.centerEllipsis(entries.get(0).getRawFileEntry().getPath(), 50);
}
return "Absolute Path";
}
@ -52,6 +57,10 @@ public class CopyPathAction implements BrowserAction, BranchAction {
new LeafAction() {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return "\"" + BrowserActionFormatter.centerEllipsis(entries.get(0).getRawFileEntry().getPath(), 50) + "\"";
}
return "Absolute Path (Quoted)";
}
@ -73,6 +82,10 @@ public class CopyPathAction implements BrowserAction, BranchAction {
new LeafAction() {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return " " + BrowserActionFormatter.centerEllipsis(FileNames.getFileName(entries.get(0).getRawFileEntry().getPath()), 50);
}
return "File Name";
}
@ -90,6 +103,10 @@ public class CopyPathAction implements BrowserAction, BranchAction {
new LeafAction() {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return "\"" + BrowserActionFormatter.centerEllipsis(FileNames.getFileName(entries.get(0).getRawFileEntry().getPath()), 50) + "\"";
}
return "File Name (Quoted)";
}

View file

@ -2,12 +2,14 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.BrowserActionFormatter;
import io.xpipe.app.browser.action.MultiExecuteAction;
import io.xpipe.app.browser.icon.FileType;
import io.xpipe.core.process.ShellControl;
import java.util.List;
public class JarAction extends JavaAction implements FileTypeAction {
public class JarAction extends MultiExecuteAction implements JavaAction, FileTypeAction {
@Override
public Category getCategory() {
@ -19,11 +21,6 @@ public class JarAction extends JavaAction implements FileTypeAction {
return super.isApplicable(model, entries) && FileTypeAction.super.isApplicable(model, entries);
}
@Override
public boolean isApplicable(OpenFileSystemModel model, BrowserEntry entry) {
return entry.getFileName().endsWith(".jar");
}
@Override
protected String createCommand(ShellControl sc, OpenFileSystemModel model, BrowserEntry entry) {
return "java -jar " + entry.getOptionallyQuotedFileName();
@ -31,7 +28,7 @@ public class JarAction extends JavaAction implements FileTypeAction {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "java -jar " + filesArgument(entries);
return "java -jar " + BrowserActionFormatter.filesArgument(entries);
}
@Override

View file

@ -1,21 +1,11 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.ApplicationPathAction;
import io.xpipe.app.browser.action.MultiExecuteAction;
import java.util.List;
public abstract class JavaAction extends MultiExecuteAction implements ApplicationPathAction {
public interface JavaAction extends ApplicationPathAction {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Java";
}
@Override
public String getExecutable() {
default String getExecutable() {
return "java";
}
}

View file

@ -0,0 +1,37 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.BrowserActionFormatter;
import io.xpipe.app.browser.action.ToFileCommandAction;
import io.xpipe.app.browser.icon.FileType;
import java.util.List;
public class JavapAction extends ToFileCommandAction implements FileTypeAction, JavaAction {
@Override
public Category getCategory() {
return Category.CUSTOM;
}
@Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return super.isApplicable(model, entries) && FileTypeAction.super.isApplicable(model, entries);
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "javap -c -p " + BrowserActionFormatter.filesArgument(entries);
}
@Override
public FileType getType() {
return FileType.byId("class");
}
@Override
protected String createCommand(OpenFileSystemModel model, BrowserEntry entry) {
return "javap -c -p " + entry.getOptionallyQuotedFileName();
}
}

View file

@ -59,7 +59,7 @@ public class OpenNativeFileDetailsAction implements LeafAction {
@Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
var sc = model.getFileSystem().getShell();
return sc.isPresent() && !sc.get().getOsType().equals(OsType.WINDOWS);
return model.isLocal() && !sc.get().getOsType().equals(OsType.WINDOWS);
}
@Override

View file

@ -3,12 +3,13 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.ExecuteApplicationAction;
import io.xpipe.app.browser.icon.FileType;
import io.xpipe.core.impl.FileNames;
import io.xpipe.core.process.OsType;
import java.util.List;
public class UnzipAction extends ExecuteApplicationAction {
public class UnzipAction extends ExecuteApplicationAction implements FileTypeAction {
@Override
public String getExecutable() {
@ -16,8 +17,8 @@ public class UnzipAction extends ExecuteApplicationAction {
}
@Override
public boolean isApplicable(OpenFileSystemModel model, BrowserEntry entry) {
return entry.getRawFileEntry().getPath().endsWith(".zip") && !OsType.getLocal().equals(OsType.WINDOWS);
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return FileTypeAction.super.isApplicable(model, entries) && !model.getFileSystem().getShell().orElseThrow().getOsType().equals(OsType.WINDOWS);
}
@Override
@ -25,6 +26,11 @@ public class UnzipAction extends ExecuteApplicationAction {
return "unzip -o " + entry.getOptionallyQuotedFileName() + " -d " + FileNames.quoteIfNecessary(FileNames.getBaseName(entry.getFileName()));
}
@Override
protected boolean refresh() {
return true;
}
@Override
public Category getCategory() {
return Category.CUSTOM;
@ -34,4 +40,9 @@ public class UnzipAction extends ExecuteApplicationAction {
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "unzip [...]";
}
@Override
public FileType getType() {
return FileType.byId("zip");
}
}

View file

@ -43,6 +43,7 @@ open module io.xpipe.ext.base {
RenameAction,
DeleteAction,
UnzipAction,
JavapAction,
JarAction;
provides ActionProvider with
DeleteStoreChildrenAction,