diff --git a/app/src/main/java/io/xpipe/app/browser/FileContextMenu.java b/app/src/main/java/io/xpipe/app/browser/FileContextMenu.java index 764bc3b6..2ba7624a 100644 --- a/app/src/main/java/io/xpipe/app/browser/FileContextMenu.java +++ b/app/src/main/java/io/xpipe/app/browser/FileContextMenu.java @@ -2,9 +2,10 @@ package io.xpipe.app.browser; -import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.util.ExternalEditor; +import io.xpipe.app.util.ScriptHelper; import io.xpipe.app.util.TerminalHelper; +import io.xpipe.app.util.ThreadHelper; import io.xpipe.core.process.OsType; import io.xpipe.core.process.ShellProcessControl; import io.xpipe.core.store.FileSystem; @@ -36,6 +37,10 @@ final class FileContextMenu extends ContextMenu { return true; } + if (List.of("sh", "command").contains(ending)) { + return true; + } + return false; } @@ -52,6 +57,61 @@ final class FileContextMenu extends ContextMenu { } private void createMenu() { + if (entry.isDirectory()) { + var terminal = new MenuItem("Open terminal"); + terminal.setOnAction(event -> { + event.consume(); + model.openTerminalAsync(entry.getPath()); + }); + getItems().add(terminal); + } else { + if (isScript(entry)) { + var execute = new MenuItem("Run in terminal"); + execute.setOnAction(event -> { + ThreadHelper.runFailableAsync(() -> { + ShellProcessControl pc = + model.getFileSystem().getShell().orElseThrow(); + pc.executeSimpleCommand(pc.getShellDialect().getMakeExecutableCommand(entry.getPath())); + var cmd = pc.command("\"" + entry.getPath() + "\"").prepareTerminalOpen(); + TerminalHelper.open(FilenameUtils.getBaseName(entry.getPath()), cmd); + }); + event.consume(); + }); + getItems().add(execute); + + var executeInBackground = new MenuItem("Run in background"); + executeInBackground.setOnAction(event -> { + ThreadHelper.runFailableAsync(() -> { + ShellProcessControl pc = + model.getFileSystem().getShell().orElseThrow(); + pc.executeSimpleCommand(pc.getShellDialect().getMakeExecutableCommand(entry.getPath())); + var cmd = ScriptHelper.createDetachCommand(pc, "\"" + entry.getPath() + "\""); + pc.executeBooleanSimpleCommand(cmd); + }); + event.consume(); + }); + getItems().add(executeInBackground); + } + + var open = new MenuItem("Open default"); + open.setOnAction(event -> { + ThreadHelper.runFailableAsync(() -> { + ShellProcessControl pc = model.getFileSystem().getShell().orElseThrow(); + var cmd = "\"" + entry.getPath() + "\""; + pc.executeBooleanSimpleCommand(cmd); + }); + event.consume(); + }); + getItems().add(open); + + var edit = new MenuItem("Edit"); + edit.setOnAction(event -> { + event.consume(); + ExternalEditor.get().openInEditor(model.getFileSystem(), entry.getPath()); + }); + getItems().add(edit); + } + var cut = new MenuItem("Delete"); cut.setOnAction(event -> { event.consume(); @@ -66,56 +126,6 @@ final class FileContextMenu extends ContextMenu { }); rename.setAccelerator(new KeyCodeCombination(KeyCode.F2)); - getItems().setAll( - new SeparatorMenuItem(), - cut, - rename - ); - - if (entry.isDirectory()) { - var terminal = new MenuItem("Terminal"); - terminal.setOnAction(event -> { - event.consume(); - model.openTerminalAsync(entry.getPath()); - }); - getItems().add(0, terminal); - } else { - var open = new MenuItem("Open"); - open.setOnAction(event -> { - event.consume(); - ExternalEditor.get().openInEditor(model.getFileSystem(), entry.getPath()); - }); - getItems().add(0, open); - - if (isScript(entry)) { - var executeInBackground = new MenuItem("Run in background"); - executeInBackground.setOnAction(event -> { - event.consume(); - ExternalEditor.get().openInEditor(model.getFileSystem(), entry.getPath()); - }); - getItems().add(0, executeInBackground); - - var execute = new MenuItem("Run in terminal"); - execute.setOnAction(event -> { - event.consume(); - try { - ShellProcessControl pc = model.getFileSystem().getShell().orElseThrow(); - pc.executeSimpleCommand(pc.getShellDialect().getMakeExecutableCommand(entry.getPath())); - var cmd = pc.command(entry.getPath()).prepareTerminalOpen(); - TerminalHelper.open(FilenameUtils.getName(entry.getPath()), cmd); - } catch (Exception e) { - ErrorEvent.fromThrowable(e).handle(); - } - }); - getItems().add(0, execute); - } - - var edit = new MenuItem("Edit"); - edit.setOnAction(event -> { - event.consume(); - ExternalEditor.get().openInEditor(model.getFileSystem(), entry.getPath()); - }); - getItems().add(0, edit); - } + getItems().addAll(new SeparatorMenuItem(), cut, rename); } } diff --git a/app/src/main/java/io/xpipe/app/core/App.java b/app/src/main/java/io/xpipe/app/core/App.java index 03ff4778..bc463aeb 100644 --- a/app/src/main/java/io/xpipe/app/core/App.java +++ b/app/src/main/java/io/xpipe/app/core/App.java @@ -66,7 +66,7 @@ public class App extends Application { var content = new AppLayoutComp(); content.apply(struc -> { struc.get().addEventFilter(MouseEvent.MOUSE_CLICKED, event -> { - AppActionLinkDetector.detectOnFocus(); + // AppActionLinkDetector.detectOnFocus(); }); }); var title = diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/PrettyImageComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/PrettyImageComp.java index ed5c5686..dbf65e86 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/PrettyImageComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/PrettyImageComp.java @@ -4,7 +4,6 @@ import io.xpipe.app.core.AppImages; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.SimpleChangeListener; -import io.xpipe.app.util.XPipeDaemon; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleObjectProperty; @@ -73,7 +72,7 @@ public class PrettyImageComp extends SimpleComp { return null; } - return XPipeDaemon.getInstance().svgImage(value.getValue()); + return AppImages.svgImage(value.getValue()); }, value)); var ar = Bindings.createDoubleBinding( () -> { @@ -101,7 +100,7 @@ public class PrettyImageComp extends SimpleComp { return null; } - return XPipeDaemon.getInstance().image(value.getValue()); + return AppImages.image(value.getValue()); }, PlatformThread.sync(value))); var ar = Bindings.createDoubleBinding( diff --git a/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java b/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java index a366007e..1bb0621e 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java @@ -37,10 +37,7 @@ public class ErrorHandlerComp extends SimpleComp { } public static void showAndWait(ErrorEvent event) { - // Always run later to prevent any issues when an exception - // is thrown within an animation or layout processing task - // Otherwise, the show and wait method might fail - PlatformThread.alwaysRunLaterBlocking(() -> { + PlatformThread.runLaterIfNeededBlocking(() -> { synchronized (showing) { if (!showing.get()) { showing.set(true); @@ -49,7 +46,15 @@ public class ErrorHandlerComp extends SimpleComp { window.setOnHidden(e -> { showing.set(false); }); - window.showAndWait(); + + // An exception is thrown when show and wait is called + // within an animation or layout processing task + try { + window.showAndWait(); + } catch (Throwable t) { + window.show(); + t.printStackTrace(); + } } } }); diff --git a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java index fa89342d..d079061a 100644 --- a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java +++ b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java @@ -2,6 +2,7 @@ package io.xpipe.app.util; import io.xpipe.app.issue.TrackEvent; import io.xpipe.core.impl.FileNames; +import io.xpipe.core.process.OsType; import io.xpipe.core.process.ShellProcessControl; import io.xpipe.core.process.ShellDialect; import io.xpipe.core.process.ShellDialects; @@ -14,6 +15,14 @@ import java.util.Random; public class ScriptHelper { + public static String createDetachCommand(ShellProcessControl pc, String command) { + if (pc.getOsType().equals(OsType.WINDOWS)) { + return "start \"\" " + command; + } else { + return "nohup " + command + " /dev/null & disown"; + } + } + public static int getScriptId() { // A deterministic approach can cause permission problems when two different users execute the same command on a // system diff --git a/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css b/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css index b5fbb493..00d34f51 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css @@ -1,7 +1,7 @@ .bar { -fx-padding: 0.8em 1.0em 0.8em 1.0em; -fx-background-color: -color-neutral-subtle; --fx-border-color: -color-border-default; +-fx-border-color: -color-neutral-emphasis; } .store-header-bar {