More appearance fixes

This commit is contained in:
crschnick 2024-04-09 18:24:00 +00:00
parent 311b24976f
commit f79cbc9aef
42 changed files with 321 additions and 155 deletions

View file

@ -4,6 +4,7 @@ import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.core.util.ModuleLayerLoader;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCombination;
@ -53,7 +54,7 @@ public interface BrowserAction {
return false;
}
String getName(OpenFileSystemModel model, List<BrowserEntry> entries);
ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries);
default boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return true;

View file

@ -7,13 +7,11 @@ import io.xpipe.app.fxcomps.util.Shortcuts;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.LicenseProvider;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.control.Button;
import javafx.scene.control.MenuItem;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.List;
import java.util.function.UnaryOperator;
public interface LeafAction extends BrowserAction {
@ -39,13 +37,14 @@ public interface LeafAction extends BrowserAction {
if (getShortcut() != null) {
Shortcuts.addShortcut(b, getShortcut());
}
new TooltipAugment<>(new SimpleStringProperty(getName(model, selected))).augment(b);
var name = getName(model, selected);
new TooltipAugment<>(name).augment(b);
var graphic = getIcon(model, selected);
if (graphic != null) {
b.setGraphic(graphic);
}
b.setMnemonicParsing(false);
b.setAccessibleText(getName(model, selected));
b.accessibleTextProperty().bind(name);
b.setDisable(!isActive(model, selected));
model.getCurrentPath().addListener((observable, oldValue, newValue) -> {
@ -62,9 +61,10 @@ public interface LeafAction extends BrowserAction {
}
default MenuItem toMenuItem(
OpenFileSystemModel model, List<BrowserEntry> selected, UnaryOperator<String> nameFunc) {
var name = nameFunc.apply(getName(model, selected));
var mi = new MenuItem(name);
OpenFileSystemModel model, List<BrowserEntry> selected) {
var name = getName(model, selected);
var mi = new MenuItem();
mi.textProperty().bind(name);
mi.setOnAction(event -> {
ThreadHelper.runFailableAsync(() -> {
BooleanScope.execute(model.getBusy(), () -> {

View file

@ -2,10 +2,12 @@ package io.xpipe.app.browser.action;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.TerminalLauncher;
import io.xpipe.core.process.CommandBuilder;
import io.xpipe.core.process.ShellControl;
import javafx.beans.value.ObservableValue;
import org.apache.commons.io.FilenameUtils;
import java.util.List;
@ -39,9 +41,9 @@ public abstract class MultiExecuteAction implements BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
var t = AppPrefs.get().terminalType().getValue();
return "in " + (t != null ? t.toTranslatedString().getValue() : "?");
return AppI18n.observable("executeInTerminal", t != null ? t.toTranslatedString().getValue() : "?");
}
@Override
@ -66,8 +68,8 @@ public abstract class MultiExecuteAction implements BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "in background";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("executeInBackground");
}
});
}

View file

@ -75,17 +75,17 @@ public final class BrowserContextMenu extends ContextMenu {
for (BrowserAction a : all) {
var used = resolveIfNeeded(a, selected);
if (a instanceof LeafAction la) {
getItems().add(la.toMenuItem(model, used, s -> s));
getItems().add(la.toMenuItem(model, used));
}
if (a instanceof BranchAction la) {
var m = new Menu(a.getName(model, used) + " ...");
var m = new Menu(a.getName(model, used).getValue() + " ...");
for (LeafAction sub : la.getBranchingActions(model, used)) {
var subUsed = resolveIfNeeded(sub, selected);
if (!sub.isApplicable(model, subUsed)) {
continue;
}
m.getItems().add(sub.toMenuItem(model, subUsed, s -> s));
m.getItems().add(sub.toMenuItem(model, subUsed));
}
var graphic = a.getIcon(model, used);
if (graphic != null) {

View file

@ -48,11 +48,9 @@ public class AppI18n {
private final Property<LoadedTranslations> currentLanguage = new SimpleObjectProperty<>();
public static void init() throws Exception {
if (INSTANCE != null) {
return;
if (INSTANCE == null) {
INSTANCE = new AppI18n();
}
INSTANCE = new AppI18n();
INSTANCE.load();
}
@ -274,7 +272,7 @@ public class AppI18n {
var prettyTime = new PrettyTime(
AppPrefs.get() != null
? AppPrefs.get().language().getValue().getLocale()
: SupportedLocale.ENGLISH.getLocale());
: SupportedLocale.getEnglish().getLocale());
return new LoadedTranslations(translations,markdownDocumentations, prettyTime);
}

View file

@ -29,6 +29,7 @@ public class AppProperties {
UUID buildUuid;
String sentryUrl;
String arch;
List<String> languages;
@Getter
boolean image;
@ -53,6 +54,7 @@ public class AppProperties {
.orElse(UUID.randomUUID());
sentryUrl = System.getProperty("io.xpipe.app.sentryUrl");
arch = System.getProperty("io.xpipe.app.arch");
languages = Arrays.asList(System.getProperty("io.xpipe.app.languages").split(";"));
staging = XPipeInstallation.isStaging();
useVirtualThreads = Optional.ofNullable(System.getProperty("io.xpipe.app.useVirtualThreads"))
.map(Boolean::parseBoolean)

View file

@ -6,21 +6,21 @@ import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.WindowControl;
import io.xpipe.core.process.OsType;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.animation.*;
import javafx.application.Application;
import javafx.application.ColorScheme;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.event.EventHandler;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -43,21 +43,69 @@ public class AppTheme {
return;
}
AppPrefs.get().theme.subscribe(t -> {
Theme.ALL.forEach(
theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId()));
if (t == null) {
return;
}
initWindowsThemeHandler(stage);
stage.getScene().getRoot().getStyleClass().add(t.getCssId());
stage.getScene().getRoot().pseudoClassStateChanged(LIGHT, !t.isDark());
stage.getScene().getRoot().pseudoClassStateChanged(DARK, t.isDark());
// If we set the theme pseudo classes earlier when the window is not shown
// they do not apply. Is this a bug in JavaFX?
Platform.runLater(() -> {
AppPrefs.get().theme.subscribe(t -> {
Theme.ALL.forEach(
theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId()));
if (t == null) {
return;
}
stage.getScene().getRoot().getStyleClass().add(t.getCssId());
stage.getScene().getRoot().pseudoClassStateChanged(LIGHT, !t.isDark());
stage.getScene().getRoot().pseudoClassStateChanged(DARK, t.isDark());
});
AppPrefs.get().performanceMode().subscribe(val -> {
stage.getScene().getRoot().pseudoClassStateChanged(PRETTY, !val);
stage.getScene().getRoot().pseudoClassStateChanged(PERFORMANCE, val);
});
});
}
AppPrefs.get().performanceMode().subscribe(val -> {
stage.getScene().getRoot().pseudoClassStateChanged(PRETTY, !val);
stage.getScene().getRoot().pseudoClassStateChanged(PERFORMANCE, val);
private static void initWindowsThemeHandler(Window stage) {
if (OsType.getLocal() != OsType.WINDOWS) {
return;
}
EventHandler<WindowEvent> windowTheme = new EventHandler<>() {
@Override
public void handle(WindowEvent event) {
if (!stage.isShowing()) {
return;
}
try {
var c = new WindowControl(stage);
c.setWindowAttribute(20, AppPrefs.get().theme.getValue().isDark());
stage.setWidth(stage.getWidth() - 1);
Platform.runLater( () -> {
stage.setWidth(stage.getWidth() + 1);
});
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
}
stage.removeEventFilter(WindowEvent.WINDOW_SHOWN, this);
}
};
if (stage.isShowing()) {
windowTheme.handle(null);
} else {
stage.addEventFilter(WindowEvent.WINDOW_SHOWN, windowTheme);
}
AppPrefs.get().theme.addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> {
var transition = new PauseTransition(Duration.millis(300));
transition.setOnFinished(e -> {
windowTheme.handle(null);
});
transition.play();
});
});
}

View file

@ -82,12 +82,7 @@ public class AppWindowHelper {
}
stage.setOnShown(e -> {
// If we set the theme pseudo classes earlier when the window is not shown
// they do not apply. Is this a bug in JavaFX?
Platform.runLater(() -> {
AppTheme.initThemeHandlers(stage);
});
AppTheme.initThemeHandlers(stage);
centerToMainWindow(stage);
clampWindow(stage).ifPresent(rectangle2D -> {
stage.setX(rectangle2D.getMinX());

View file

@ -13,7 +13,7 @@ public class AppFontLoadingCheck {
// This can fail if the found system fonts can somehow not be loaded
Font.getDefault();
} catch (Throwable e) {
var event = ErrorEvent.fromThrowable("Unable to load fonts", e).build();
var event = ErrorEvent.fromThrowable("Unable to load fonts. Do you have valid font packages installed?", e).build();
// We can't use the normal error handling facility
// as the platform reports as working but opening windows still does not work
new LogErrorHandler().handle(event);

View file

@ -107,7 +107,7 @@ public class AppPrefs {
private final ObservableBooleanValue developerDisableGuiRestrictionsEffective =
bindDeveloperTrue(developerDisableGuiRestrictions);
final ObjectProperty<SupportedLocale> language =
map(new SimpleObjectProperty<>(SupportedLocale.ENGLISH), "language", SupportedLocale.class);
map(new SimpleObjectProperty<>(SupportedLocale.getEnglish()), "language", SupportedLocale.class);
@Getter
private final Property<InPlaceSecretValue> lockPassword = new SimpleObjectProperty<>();

View file

@ -15,7 +15,6 @@ import javafx.geometry.Pos;
import javafx.scene.control.Slider;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.Arrays;
import java.util.List;
public class AppearanceCategory extends AppPrefsCategory {
@ -27,7 +26,7 @@ public class AppearanceCategory extends AppPrefsCategory {
private Comp<?> languageChoice() {
var prefs = AppPrefs.get();
var c = ChoiceComp.ofTranslatable(prefs.language, Arrays.asList(SupportedLocale.values()), false);
var c = ChoiceComp.ofTranslatable(prefs.language, SupportedLocale.ALL, false);
var visit = new ButtonComp(AppI18n.observable("translate"), new FontIcon("mdi2w-web"), () -> {
Hyperlinks.open(Hyperlinks.TRANSLATE);
});

View file

@ -1,25 +1,31 @@
package io.xpipe.app.prefs;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.ext.PrefsChoiceValue;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.Locale;
@AllArgsConstructor
@Getter
public enum SupportedLocale implements PrefsChoiceValue {
ENGLISH(Locale.ENGLISH, "english"),
GERMAN(Locale.GERMAN, "german");
public class SupportedLocale implements PrefsChoiceValue {
public static List<SupportedLocale> ALL = AppProperties.get().getLanguages().stream().map(s -> new SupportedLocale(Locale.of(s), s)).toList();
public static SupportedLocale getEnglish() {
return ALL.stream().filter(supportedLocale -> supportedLocale.getId().equals("en")).findFirst().orElseThrow();
}
private final Locale locale;
private final String id;
@Override
public ObservableValue<String> toTranslatedString() {
return new SimpleStringProperty(locale.getDisplayName());
return new SimpleStringProperty(locale.getDisplayName(locale));
}
@Override

View file

@ -1,17 +1,35 @@
package io.xpipe.app.util;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.PointerType;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import javafx.stage.Stage;
import com.sun.jna.platform.win32.WinNT;
import javafx.stage.Window;
import lombok.Getter;
import java.lang.reflect.Method;
@Getter
public class WindowControl {
public interface DwmSupport extends Library {
DwmSupport INSTANCE = Native.load("dwmapi", DwmSupport.class);
WinNT.HRESULT DwmSetWindowAttribute(
WinDef.HWND hwnd,
int dwAttribute,
PointerType pvAttribute,
int cbAttribute
);
}
private final WinDef.HWND windowHandle;
public WindowControl(Stage stage) throws Exception {
public WindowControl(Window stage) throws Exception {
Method tkStageGetter = stage.getClass().getSuperclass().getDeclaredMethod("getPeer");
tkStageGetter.setAccessible(true);
Object tkStage = tkStageGetter.invoke(stage);
@ -21,7 +39,7 @@ public class WindowControl {
Method getNativeHandle = platformWindow.getClass().getMethod("getNativeHandle");
getNativeHandle.setAccessible(true);
Object nativeHandle = getNativeHandle.invoke(platformWindow);
var hwnd = new WinDef.HWND(Pointer.createConstant((long) nativeHandle));
var hwnd = new WinDef.HWND(new Pointer((long) nativeHandle));
this.windowHandle = hwnd;
}
@ -32,4 +50,14 @@ public class WindowControl {
public void move(int x, int y, int w, int h) {
User32.INSTANCE.SetWindowPos(windowHandle, new WinDef.HWND(), x,y,w,h, 0);
}
public void setWindowAttribute(int attribute, boolean attributeValue) {
DwmSupport.INSTANCE.DwmSetWindowAttribute(
windowHandle,
attribute,
new WinDef.BOOLByReference(new WinDef.BOOL(attributeValue)),
WinDef.BOOL.SIZE
);
User32.INSTANCE.UpdateWindow(windowHandle);
}
}

View file

@ -77,6 +77,7 @@ project.ext {
javafxVersion = '22'
platformName = getPlatformName()
artifactChecksums = new HashMap<String, String>()
languages = ["en", "de", "fr"]
jvmRunArgs = [
"--add-opens", "java.base/java.lang=io.xpipe.app",
"--add-opens", "java.base/java.lang=io.xpipe.core",
@ -89,6 +90,7 @@ project.ext {
"--add-opens", "javafx.graphics/com.sun.javafx.tk.quantum=io.xpipe.app",
"-Xmx8g",
"-Dio.xpipe.app.arch=$rootProject.arch",
"-Dio.xpipe.app.languages=${String.join(";", languages)}",
"-Dfile.encoding=UTF-8",
// Disable this for now as it requires Windows 10+
// '-XX:+UseZGC',

View file

@ -3,6 +3,8 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -33,8 +35,8 @@ public class BackAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Back";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("back");
}
@Override

View file

@ -3,10 +3,12 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellDialect;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import java.util.List;
@ -58,11 +60,11 @@ public class BrowseInNativeManagerAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return switch (OsType.getLocal()) {
case OsType.Windows windows -> "Browse in Windows Explorer";
case OsType.Linux linux -> "Browse in default file manager";
case OsType.MacOs macOs -> "Browse in Finder";
case OsType.Windows windows -> AppI18n.observable("browseInWindowsExplorer");
case OsType.Linux linux -> AppI18n.observable("browseInDefaultFileManager");
case OsType.MacOs macOs -> AppI18n.observable("browseInFinder");
};
}

View file

@ -4,8 +4,11 @@ import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.BranchAction;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.process.CommandBuilder;
import io.xpipe.core.process.OsType;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import org.kordamp.ikonli.javafx.FontIcon;
@ -24,8 +27,8 @@ public class ChmodAction implements BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Chmod";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("chmod");
}
@Override
@ -55,8 +58,8 @@ public class ChmodAction implements BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return option;
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return new SimpleStringProperty(option);
}
@Override

View file

@ -4,6 +4,8 @@ import io.xpipe.app.browser.BrowserClipboard;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -42,7 +44,7 @@ public class CopyAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Copy";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("copy");
}
}

View file

@ -6,9 +6,12 @@ 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.app.core.AppI18n;
import io.xpipe.app.util.ClipboardHelper;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileNames;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
@ -29,8 +32,8 @@ public class CopyPathAction implements BrowserAction, BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Copy location";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("copyLocation");
}
@Override
@ -43,14 +46,13 @@ public class CopyPathAction implements BrowserAction, BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return " "
+ BrowserActionFormatter.centerEllipsis(
entries.getFirst().getRawFileEntry().getPath(), 50);
return new SimpleObjectProperty<>(BrowserActionFormatter.centerEllipsis(
entries.getFirst().getRawFileEntry().getPath(), 50));
}
return "Absolute Paths";
return AppI18n.observable("absolutePaths");
}
@Override
@ -63,14 +65,13 @@ public class CopyPathAction implements BrowserAction, BranchAction {
},
new LeafAction() {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return " "
+ BrowserActionFormatter.centerEllipsis(
entries.getFirst().getRawFileEntry().getPath(), 50);
return new SimpleObjectProperty<>(BrowserActionFormatter.centerEllipsis(
entries.getFirst().getRawFileEntry().getPath(), 50));
}
return "Absolute Link Paths";
return AppI18n.observable("absoluteLinkPaths");
}
@Override
@ -95,15 +96,15 @@ public class CopyPathAction implements BrowserAction, BranchAction {
},
new LeafAction() {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return "\""
return new SimpleObjectProperty<>("\""
+ BrowserActionFormatter.centerEllipsis(
entries.getFirst().getRawFileEntry().getPath(), 50)
+ "\"";
+ "\"");
}
return "Absolute Paths (Quoted)";
return AppI18n.observable("absolutePathsQuoted");
}
@Override
@ -129,17 +130,16 @@ public class CopyPathAction implements BrowserAction, BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return " "
+ BrowserActionFormatter.centerEllipsis(
return new SimpleObjectProperty<>(BrowserActionFormatter.centerEllipsis(
FileNames.getFileName(entries.getFirst()
.getRawFileEntry()
.getPath()),
50);
50));
}
return "File Names";
return AppI18n.observable("fileNames");
}
@Override
@ -153,17 +153,16 @@ public class CopyPathAction implements BrowserAction, BranchAction {
},
new LeafAction() {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return " "
+ BrowserActionFormatter.centerEllipsis(
return new SimpleObjectProperty<>(BrowserActionFormatter.centerEllipsis(
FileNames.getFileName(entries.getFirst()
.getRawFileEntry()
.getPath()),
50);
50));
}
return "Link File Names";
return AppI18n.observable("linkFileNames");
}
@Override
@ -195,18 +194,18 @@ public class CopyPathAction implements BrowserAction, BranchAction {
},
new LeafAction() {
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
if (entries.size() == 1) {
return "\""
return new SimpleObjectProperty<>("\""
+ BrowserActionFormatter.centerEllipsis(
FileNames.getFileName(entries.getFirst()
.getRawFileEntry()
.getPath()),
50)
+ "\"";
+ "\"");
}
return "File Names (Quoted)";
return AppI18n.observable("fileNamesQuoted");
}
@Override

View file

@ -5,7 +5,9 @@ import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.FileSystemHelper;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -43,12 +45,11 @@ public class DeleteAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Delete"
+ (entries.stream()
.allMatch(browserEntry ->
browserEntry.getRawFileEntry().getKind() == FileKind.LINK)
? " link"
: "");
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("deleteFile",entries.stream()
.allMatch(browserEntry ->
browserEntry.getRawFileEntry().getKind() == FileKind.LINK)
? " link"
: "");
}
}

View file

@ -4,7 +4,9 @@ import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.FileSystemHelper;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import org.kordamp.ikonli.javafx.FontIcon;
@ -30,8 +32,8 @@ public class DeleteLinkAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Delete link";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("deleteLink");
}
@Override

View file

@ -3,9 +3,11 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.FileOpener;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import org.kordamp.ikonli.javafx.FontIcon;
@ -31,9 +33,9 @@ public class EditFileAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
var e = AppPrefs.get().externalEditor().getValue();
return "Edit with " + (e != null ? e.toTranslatedString().getValue() : "?");
return AppI18n.observable("editWithEditor", e.toTranslatedString().getValue());
}
@Override

View file

@ -1,10 +1,12 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileNames;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import org.kordamp.ikonli.javafx.FontIcon;
@ -30,8 +32,8 @@ public class FollowLinkAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Follow link";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("followLink");
}
@Override

View file

@ -3,6 +3,8 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -33,8 +35,8 @@ public class ForwardAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Forward";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("goForward");
}
@Override

View file

@ -7,6 +7,8 @@ import io.xpipe.app.browser.action.MultiExecuteAction;
import io.xpipe.app.browser.icon.BrowserIconFileType;
import io.xpipe.core.process.CommandBuilder;
import io.xpipe.core.process.ShellControl;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import java.util.List;
@ -18,8 +20,8 @@ public class JarAction extends MultiExecuteAction implements JavaAction, FileTyp
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "java -jar " + BrowserActionFormatter.filesArgument(entries);
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return new SimpleStringProperty("java -jar " + BrowserActionFormatter.filesArgument(entries));
}
@Override

View file

@ -5,6 +5,8 @@ import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.BrowserActionFormatter;
import io.xpipe.app.browser.action.ToFileCommandAction;
import io.xpipe.app.browser.icon.BrowserIconFileType;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import java.util.List;
@ -16,8 +18,8 @@ public class JavapAction extends ToFileCommandAction implements FileTypeAction,
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "javap -c -p " + BrowserActionFormatter.filesArgument(entries);
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return new SimpleStringProperty("javap -c -p " + BrowserActionFormatter.filesArgument(entries));
}
@Override

View file

@ -7,10 +7,12 @@ import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.icon.BrowserIcons;
import io.xpipe.app.comp.base.ModalOverlayComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.util.OptionsBuilder;
import io.xpipe.core.process.OsType;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.control.TextField;
import org.kordamp.ikonli.javafx.FontIcon;
@ -35,8 +37,8 @@ public class NewItemAction implements BrowserAction, BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "New";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("new");
}
@Override
@ -76,8 +78,8 @@ public class NewItemAction implements BrowserAction, BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "File";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("file");
}
},
new LeafAction() {
@ -105,8 +107,8 @@ public class NewItemAction implements BrowserAction, BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Directory";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("directory");
}
},
new LeafAction() {
@ -137,8 +139,8 @@ public class NewItemAction implements BrowserAction, BranchAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Symbolic link";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("symbolicLink");
}
@Override

View file

@ -3,7 +3,9 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -35,8 +37,8 @@ public class OpenDirectoryAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Open";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("open");
}
@Override

View file

@ -4,7 +4,9 @@ import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.browser.session.BrowserSessionModel;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import org.kordamp.ikonli.javafx.FontIcon;
@ -35,8 +37,8 @@ public class OpenDirectoryInNewTabAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Open in new tab";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("openInNewTab");
}
@Override

View file

@ -3,8 +3,10 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.FileOpener;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -38,8 +40,8 @@ public class OpenFileDefaultAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Open with default application";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("openWithDefaultApplication");
}
@Override

View file

@ -3,9 +3,11 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.FileOpener;
import io.xpipe.core.process.OsType;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -38,8 +40,8 @@ public class OpenFileWithAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Open with ...";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("openFileWith");
}
@Override

View file

@ -3,10 +3,12 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.LocalShell;
import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.store.FileNames;
import javafx.beans.value.ObservableValue;
import java.util.List;
@ -70,8 +72,8 @@ public class OpenNativeFileDetailsAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Show details";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("showDetails");
}
@Override

View file

@ -3,8 +3,10 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -50,8 +52,8 @@ public class OpenTerminalAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Open in terminal";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("openInTerminal");
}
@Override

View file

@ -4,7 +4,9 @@ import io.xpipe.app.browser.BrowserClipboard;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -54,8 +56,8 @@ public class PasteAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Paste";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("paste");
}
@Override

View file

@ -3,6 +3,8 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -33,8 +35,8 @@ public class RefreshDirectoryAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Refresh";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("refresh");
}
@Override

View file

@ -3,7 +3,9 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.store.FileKind;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@ -35,8 +37,8 @@ public class RenameAction implements LeafAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Rename";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("openWithDefaultApplication");
}
@Override

View file

@ -3,12 +3,14 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.browser.action.MultiExecuteAction;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.process.CommandBuilder;
import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellDialects;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import org.kordamp.ikonli.javafx.FontIcon;
@ -61,8 +63,8 @@ public class RunAction extends MultiExecuteAction {
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Run";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return AppI18n.observable("run");
}
@Override

View file

@ -6,6 +6,8 @@ import io.xpipe.app.browser.action.ExecuteApplicationAction;
import io.xpipe.app.browser.icon.BrowserIconFileType;
import io.xpipe.core.process.OsType;
import io.xpipe.core.store.FileNames;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import java.util.List;
@ -33,8 +35,8 @@ public class UnzipAction extends ExecuteApplicationAction implements FileTypeAct
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "unzip [...]";
public ObservableValue<String> getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return new SimpleStringProperty("unzip [...]");
}
@Override

View file

@ -63,8 +63,8 @@ executionType=Execution type
executionTypeDescription=When to run this snippet
minimumShellDialect=Shell type
minimumShellDialectDescription=The required shell type for this script
dumbOnly=Dumb only
terminalOnly=Terminal only
dumbOnly=Dumb
terminalOnly=Terminal
both=Both
shouldElevate=Should elevate
shouldElevateDescription=Whether to run this script with elevated permissions
@ -73,4 +73,39 @@ script.displayDescription=Create a reusable script
scriptGroup.displayName=Script Group
scriptGroup.displayDescription=Create a group for scripts
scriptGroup=Group
scriptGroupDescription=The group to assign this script to
scriptGroupDescription=The group to assign this script to
openInNewTab=Open in new tab
executeInBackground=in background
executeInTerminal=in $TERM$
back=Go back
browseInWindowsExplorer=Browse in Windows explorer
browseInDefaultFileManager=Browse in default file manager
browseInFinder=Browse in finder
chmod=Chmod
copy=Copy
paste=Paste
copyLocation=Copy location
absolutePaths=Absolute paths
absoluteLinkPaths=Absolute link paths
absolutePathsQuoted=Absolute quoted paths
fileNames=File names
linkFileNames=Link file names
fileNamesQuoted=File names (Quoted)
deleteFile=Delete $FILE$
deleteLink=Delete link
editWithEditor=Edit with $EDITOR
followLink=Follow link
goForward=Go forward
showDetails=Show details
openFileWith=Open with ...
openWithDefaultApplication=Open with default application
rename=Rename
run=Run
new=New
openInTerminal=Open in terminal
file=File
directory=Directory
symbolicLink=Symbolic link

View file

@ -77,7 +77,7 @@ shellEnvironment.displayName=Custom Shell Environment
shellEnvironment.displayDescription=Create a customized shell init environment
shellEnvironment.informationFormat=$TYPE$ environment
shellEnvironment.elevatedInformationFormat=$ELEVATION$ $TYPE$ environment
environmentConnectionDescription=The base connection to create an environment from
environmentConnectionDescription=The base connection to create an environment for
environmentScriptDescription=The optional custom init script to run in the shell
environmentSnippets=Script snippets
commandSnippetsDescription=The optional predefined script snippets to run first
@ -199,7 +199,7 @@ none=None
commandDescription=The commands to execute in a shell script on the host.
commandHostDescription=The host to run the command on
commandDataFlowDescription=How this command handles input and output
commandElevationDescription=Whether to run this command with elevated permissions
commandElevationDescription=Run this command with elevated permissions
commandShellTypeDescription=The shell to use for this command
ssh.passwordDescription=The optional password to use when authenticating
keyAuthentication=Key-based authentication
@ -276,7 +276,8 @@ gpgAgent=GPG Agent (Pro)
gateway=Gateway
gatewayDescription=The optional gateway to use when connecting.
connectionInformation=Connection information
connectionInformationDescription=Where to connect to
#context: title
connectionInformationDescription=Which system to connect to
passwordAuthentication=Password authentication
passwordDescription=The optional password to use to authenticate.
sshConfigString.displayName=Customized SSH Connection
@ -293,3 +294,7 @@ vncPassword=Password
vncPasswordDescription=The VNC password
x11WslInstance=X11 Forward WSL instance
x11WslInstanceDescription=The local Windows Subsystem for Linux distribution to use as an X11 server when using X11 forwarding in an SSH connection. This distribution must be a WSL2 distribution.\n\nRequires a restart to apply.
openAsRoot=Open as root
openInVsCodeRemote=Open in VSCode remote
openInWSL=Open in WSL
launch=Launch

View file

@ -0,0 +1,4 @@
community=Community
professional=Professional
proPreview=Pro preview
preview=Professional Preview

View file

@ -1,8 +1,5 @@
community=Community
professional=Professional
communityDescription=A connection power-tool perfect for your personal use cases.
professionalDescription=Professional connection management for your entire server infrastructure.
proPreview=Pro preview
buyProfessional=Try XPipe professional
extendProfessional=Upgrade to latest professional features
communityItem1=Unlimited connections to non-commercial systems and tools
@ -19,7 +16,6 @@ status=Status
type=Type
licenseAlertTitle=Commercial usage
useCommunity=Continue with community
preview=Professional Preview
previewDescription=Try out new features for a couple of weeks after release.
tryPreview=Activate XPipe preview
previewItem1=Full access to newly released professional features for 2 weeks after release