This commit is contained in:
crschnick
2025-09-09 03:52:48 +00:00
parent 3e400aef02
commit 1e4e23fe3d
24 changed files with 238 additions and 234 deletions
@@ -9,8 +9,11 @@ import com.sun.net.httpserver.HttpExchange;
public class DaemonFocusExchangeImpl extends DaemonFocusExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) {
AppOperationMode.switchUp(AppOperationMode.GUI);
public Object handle(HttpExchange exchange, Request msg) throws Throwable {
if (AppOperationMode.GUI.isSupported()) {
AppOperationMode.switchToSyncOrThrow(AppOperationMode.GUI);
}
var w = AppMainWindow.get();
if (w != null) {
w.focus();
@@ -10,7 +10,7 @@ import javafx.beans.property.SimpleObjectProperty;
public class BrowserDialogs {
public static FileConflictChoice showFileConflictAlert(FilePath file, boolean multiple) {
public static FileConflictChoice showFileConflictDialog(FilePath file, boolean multiple) {
var choice = new SimpleObjectProperty<FileConflictChoice>();
var key = multiple ? "fileConflictAlertContentMultiple" : "fileConflictAlertContent";
var w = multiple ? 1050 : 400;
@@ -109,7 +109,7 @@ public class BrowserFileTransferOperation {
return BrowserDialogs.FileConflictChoice.SKIP;
}
var choice = BrowserDialogs.showFileConflictAlert(target, multiple);
var choice = BrowserDialogs.showFileConflictDialog(target, multiple);
if (choice == BrowserDialogs.FileConflictChoice.CANCEL) {
lastConflictChoice = BrowserDialogs.FileConflictChoice.CANCEL;
return BrowserDialogs.FileConflictChoice.CANCEL;
+1 -1
View File
@@ -21,7 +21,7 @@ public class App extends Application {
@Override
@SneakyThrows
public void start(Stage primaryStage) {
TrackEvent.info("Application launched");
TrackEvent.info("Platform application started");
APP = this;
stage = primaryStage;
}
@@ -28,7 +28,7 @@ import io.xpipe.app.storage.DataStorageSyncHandler;
import io.xpipe.app.terminal.TerminalLauncherManager;
import io.xpipe.app.terminal.TerminalView;
import io.xpipe.app.update.UpdateAvailableDialog;
import io.xpipe.app.update.UpdateChangelogAlert;
import io.xpipe.app.update.UpdateChangelogDialog;
import io.xpipe.app.update.UpdateNagDialog;
import io.xpipe.app.util.*;
import io.xpipe.core.XPipeDaemonMode;
@@ -167,7 +167,7 @@ public class AppBaseMode extends AppOperationMode {
AppGreetingsDialog.showAndWaitIfNeeded();
TrackEvent.info("Waiting for startup dialogs to close");
AppDialog.waitForAllDialogsClose();
UpdateChangelogAlert.showIfNeeded();
UpdateChangelogDialog.showIfNeeded();
ActionProvider.initProviders();
DataStoreProviders.init();
@@ -2,6 +2,7 @@ package io.xpipe.app.core.mode;
import io.xpipe.app.core.window.AppMainWindow;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.platform.PlatformInit;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.util.LicenseProvider;
import io.xpipe.core.OsType;
@@ -9,7 +10,14 @@ import io.xpipe.core.OsType;
import javafx.application.Platform;
import javafx.stage.Stage;
public class AppGuiMode extends AppPlatformMode {
public class AppGuiMode extends AppOperationMode {
@Override
public boolean isSupported() {
// We force GUI to be supported and fail with a terminal
// exception if we can't initialize the platform
return true;
}
@Override
public String getId() {
@@ -32,7 +40,8 @@ public class AppGuiMode extends AppPlatformMode {
@Override
public void onSwitchTo() throws Throwable {
super.onSwitchTo();
AppOperationMode.BACKGROUND.onSwitchTo();
PlatformInit.init(true);
// Refresh license check
// In case our exit behavior is set to continue in background,
@@ -43,4 +52,10 @@ public class AppGuiMode extends AppPlatformMode {
AppMainWindow.get().show();
});
}
@Override
public void finalTeardown() throws Throwable {
onSwitchFrom();
BACKGROUND.finalTeardown();
}
}
@@ -5,7 +5,7 @@ import io.xpipe.app.core.*;
import io.xpipe.app.core.check.AppDebugModeCheck;
import io.xpipe.app.core.window.AppMainWindow;
import io.xpipe.app.issue.*;
import io.xpipe.app.platform.NodeCallback;
import io.xpipe.app.platform.PlatformThreadWatcher;
import io.xpipe.app.platform.PlatformInit;
import io.xpipe.app.platform.PlatformState;
import io.xpipe.app.prefs.AppPrefs;
@@ -111,7 +111,7 @@ public abstract class AppOperationMode {
AppMainWindow.loadingText("initializingApp");
GlobalTimer.init();
AppProperties.init(args);
NodeCallback.init();
PlatformThreadWatcher.init();
AppLogs.init();
AppDebugModeCheck.printIfNeeded();
AppProperties.get().logArguments();
@@ -222,23 +222,6 @@ public abstract class AppOperationMode {
return true;
}
public static void switchUp(AppOperationMode newMode) {
if (newMode == BACKGROUND) {
return;
}
TrackEvent.info("Attempting to switch mode up to " + newMode.getId());
if (newMode.equals(TRAY) && TRAY.isSupported() && AppOperationMode.get() == BACKGROUND) {
set(TRAY);
return;
}
if (newMode.equals(GUI) && GUI.isSupported()) {
set(GUI);
}
}
public static void close() {
set(null);
}
@@ -1,23 +0,0 @@
package io.xpipe.app.core.mode;
import io.xpipe.app.platform.PlatformInit;
public abstract class AppPlatformMode extends AppOperationMode {
@Override
public boolean isSupported() {
return true;
}
@Override
public void onSwitchTo() throws Throwable {
AppOperationMode.BACKGROUND.onSwitchTo();
PlatformInit.init(true);
}
@Override
public void finalTeardown() throws Throwable {
onSwitchFrom();
BACKGROUND.finalTeardown();
}
}
@@ -2,24 +2,26 @@ package io.xpipe.app.core.mode;
import io.xpipe.app.core.AppTray;
import io.xpipe.app.issue.*;
import io.xpipe.app.platform.PlatformInit;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.core.OsType;
import java.awt.*;
public class AppTrayMode extends AppPlatformMode {
public class AppTrayMode extends AppOperationMode {
@Override
public boolean isSupported() {
return OsType.getLocal() == OsType.WINDOWS
&& super.isSupported()
&& Desktop.isDesktopSupported()
&& SystemTray.isSupported();
}
@Override
public void onSwitchTo() throws Throwable {
super.onSwitchTo();
AppOperationMode.BACKGROUND.onSwitchTo();
PlatformInit.init(true);
PlatformThread.runLaterIfNeededBlocking(() -> {
if (AppTray.get() == null) {
TrackEvent.info("Initializing tray");
@@ -56,4 +58,10 @@ public class AppTrayMode extends AppPlatformMode {
ErrorAction.ignore().handle(event);
});
}
@Override
public void finalTeardown() throws Throwable {
onSwitchFrom();
BACKGROUND.finalTeardown();
}
}
@@ -27,11 +27,6 @@ public class AppDialog {
@Getter
private static final ObservableList<ModalOverlay> modalOverlays = FXCollections.observableArrayList();
private static void showMainWindow() {
PlatformInit.init(true);
AppMainWindow.init(true);
}
public static void closeDialog(ModalOverlay overlay) {
PlatformThread.runLaterIfNeeded(() -> {
synchronized (modalOverlays) {
@@ -67,7 +62,9 @@ public class AppDialog {
}
public static void show(ModalOverlay o, boolean wait) {
showMainWindow();
PlatformInit.init(true);
AppMainWindow.init(true);
if (!Platform.isFxApplicationThread()) {
PlatformThread.runLaterIfNeededBlocking(() -> {
synchronized (modalOverlays) {
@@ -105,7 +105,7 @@ public class AppMainWindow {
}
AppWindowStyle.addIcons(stage);
AppWindowStyle.addStylesheets(stage.getScene());
AppWindowStyle.addNavigationStyleClasses(stage.getScene());
AppWindowStyle.addNavigationPseudoClasses(stage.getScene());
AppWindowStyle.addClickShield(stage);
AppWindowStyle.addMaximizedPseudoClass(stage);
AppWindowStyle.addFontSize(stage);
@@ -91,7 +91,7 @@ public class AppSideWindow {
AppModifiedStage.prepareStage(stage);
AppWindowStyle.addIcons(stage);
AppWindowStyle.addStylesheets(alert.getDialogPane().getScene());
AppWindowStyle.addNavigationStyleClasses(alert.getDialogPane().getScene());
AppWindowStyle.addNavigationPseudoClasses(alert.getDialogPane().getScene());
return alert;
}
}
@@ -33,7 +33,7 @@ public class AppWindowStyle {
});
}
public static void addNavigationStyleClasses(Scene scene) {
public static void addNavigationPseudoClasses(Scene scene) {
Consumer<Boolean> onInput = kb -> {
var r = scene.getRoot();
if (r != null) {
@@ -8,23 +8,10 @@ import io.xpipe.core.FailableSupplier;
public interface ErrorAction {
static ErrorAction openDocumentation(String link) {
return new ErrorAction() {
@Override
public String getName() {
return AppI18n.get("openDocumentation");
}
@Override
public String getDescription() {
return AppI18n.get("openDocumentationDescription");
}
@Override
public boolean handle(ErrorEvent event) {
Hyperlinks.open(link);
return false;
}
};
return translated("openDocumentation", () -> {
Hyperlinks.open(link);
return false;
});
}
static ErrorAction translated(String key, FailableSupplier<Boolean> r) {
@@ -1,40 +0,0 @@
package io.xpipe.app.issue;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.SimpleComp;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.util.Deobfuscator;
import javafx.geometry.Insets;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Region;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class ErrorDetailsComp extends SimpleComp {
private ErrorEvent event;
private Region createStrackTraceContent() {
if (event.getThrowable() != null) {
String stackTrace = Deobfuscator.deobfuscateToString(event.getThrowable());
stackTrace = stackTrace.replace("\t", "");
var tf = new TextArea(stackTrace);
AppFontSizes.xs(tf);
tf.setWrapText(true);
tf.setEditable(false);
tf.setPadding(new Insets(10, 0, 10, 0));
return tf;
}
return new Region();
}
@Override
protected Region createSimple() {
var tb = Comp.of(this::createStrackTraceContent);
tb.apply(r -> AppFontSizes.xs(r.get()));
return tb.createRegion();
}
}
@@ -3,12 +3,17 @@ package io.xpipe.app.issue;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.base.ModalButton;
import io.xpipe.app.comp.base.ModalOverlay;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.core.mode.AppOperationMode;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.platform.LabelGraphic;
import io.xpipe.app.util.Deobfuscator;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Region;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.concurrent.atomic.AtomicReference;
@@ -39,9 +44,12 @@ public class ErrorHandlerDialog {
errorModal.addButton(new ModalButton(
"stackTrace",
() -> {
var content =
new ErrorDetailsComp(event).prefWidth(650).prefHeight(750);
var detailsModal = ModalOverlay.of("errorDetails", content);
var detailsModal = ModalOverlay.of("errorDetails", Comp.of(() -> {
var content = createStrackTraceContent(event);
content.setPrefWidth(650);
content.setPrefHeight(750);
return content;
}));
detailsModal.show();
},
false,
@@ -76,4 +84,20 @@ public class ErrorHandlerDialog {
ErrorAction.ignore().handle(event);
}
}
private static Region createStrackTraceContent(ErrorEvent event) {
if (event.getThrowable() != null) {
String stackTrace = Deobfuscator.deobfuscateToString(event.getThrowable());
stackTrace = stackTrace.replace("\t", "");
var tf = new TextArea(stackTrace);
AppFontSizes.xs(tf);
tf.setWrapText(true);
tf.setEditable(false);
tf.setPadding(new Insets(10, 0, 10, 0));
return tf;
}
return new Region();
}
}
@@ -23,7 +23,8 @@ public class LogErrorHandler implements ErrorHandler {
System.err.println(event.getDescription());
}
if (event.getThrowable() != null) {
Deobfuscator.printStackTrace(event.getThrowable());
var s = Deobfuscator.deobfuscateToString(event.getThrowable());
System.err.println(s);
}
}
}
@@ -13,7 +13,6 @@ import java.util.stream.Collectors;
@Getter
public class TrackEvent {
private final Thread thread = Thread.currentThread();
private final Instant instant = Instant.now();
private String type;
private String message;
@@ -14,7 +14,7 @@ import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
public class NodeCallback {
public class PlatformThreadWatcher {
private static final Set<Window> windows = new HashSet<>();
private static final Set<Node> nodes = new HashSet<>();
@@ -47,6 +47,36 @@ public final class AppPrefs {
private static AppPrefs INSTANCE;
private final List<Mapping> mapping = new ArrayList<>();
public static void initLocal() {
INSTANCE = new AppPrefs();
PrefsProvider.getAll().forEach(prov -> prov.addPrefs(INSTANCE.extensionHandler));
INSTANCE.loadLocal();
INSTANCE.vaultStorageHandler =
new AppPrefsStorageHandler(DataStorage.getStorageDirectory().resolve("preferences.json"));
INSTANCE.fixLocalValues();
}
public static void initSynced() throws Exception {
INSTANCE.loadSharedRemote();
INSTANCE.encryptAllVaultData.addListener((observableValue, aBoolean, t1) -> {
if (DataStorage.get() != null) {
DataStorage.get().forceRewrite();
}
});
}
public static void reset() {
INSTANCE.save();
// Keep instance as we might need some values on shutdown, e.g. on update with terminals
// INSTANCE = null;
}
public static AppPrefs get() {
return INSTANCE;
}
@Getter
private final BooleanProperty requiresRestart = new GlobalBooleanProperty(false);
@@ -83,22 +113,54 @@ public final class AppPrefs {
.documentationLink(DocumentationLink.MCP)
.build());
final BooleanProperty enableMcpMutationTools =
mapLocal(new GlobalBooleanProperty(false), "enableMcpMutationTools", Boolean.class, false);
map(Mapping.builder()
.property(new GlobalBooleanProperty(false))
.key("enableMcpMutationTools")
.valueClass(Boolean.class)
.build());
final BooleanProperty dontAutomaticallyStartVmSshServer =
mapVaultShared(new GlobalBooleanProperty(false), "dontAutomaticallyStartVmSshServer", Boolean.class, false);
final BooleanProperty dontAcceptNewHostKeys =
mapVaultShared(new GlobalBooleanProperty(false), "dontAcceptNewHostKeys", Boolean.class, false);
public final BooleanProperty performanceMode =
mapLocal(new GlobalBooleanProperty(), "performanceMode", Boolean.class, false);
map(Mapping.builder()
.property(new GlobalObjectProperty<>())
.key("performanceMode")
.valueClass(Boolean.class)
.build());
public final ObjectProperty<AppTheme.Theme> theme =
mapLocal(new GlobalObjectProperty<>(), "theme", AppTheme.Theme.class, false);
final BooleanProperty useSystemFont = mapLocal(
new GlobalBooleanProperty(OsType.getLocal() != OsType.MACOS), "useSystemFont", Boolean.class, false);
final Property<Integer> uiScale = mapLocal(new GlobalObjectProperty<>(null), "uiScale", Integer.class, true);
map(Mapping.builder()
.property(new GlobalObjectProperty<>())
.key("theme")
.valueClass(AppTheme.Theme.class)
.build());
final BooleanProperty useSystemFont =
map(Mapping.builder()
.property(new GlobalBooleanProperty(OsType.getLocal() != OsType.MACOS))
.key("useSystemFont")
.valueClass(Boolean.class)
.build());
final Property<Integer> uiScale =
map(Mapping.builder()
.property(new GlobalObjectProperty<>())
.key("uiScale")
.valueClass(Integer.class)
.requiresRestart(true)
.build());
final BooleanProperty saveWindowLocation =
mapLocal(new GlobalBooleanProperty(true), "saveWindowLocation", Boolean.class, false);
map(Mapping.builder()
.property(new GlobalBooleanProperty(true))
.key("saveWindowLocation")
.valueClass(Boolean.class)
.requiresRestart(false)
.build());
final BooleanProperty preferTerminalTabs =
mapLocal(new GlobalBooleanProperty(true), "preferTerminalTabs", Boolean.class, false);
map(Mapping.builder()
.property(new GlobalBooleanProperty(true))
.key("preferTerminalTabs")
.valueClass(Boolean.class)
.requiresRestart(false)
.build());
final ObjectProperty<ExternalTerminalType> terminalType = map(Mapping.builder()
.property(new GlobalObjectProperty<>())
.key("terminalType")
@@ -113,11 +175,27 @@ public final class AppPrefs {
.requiresRestart(false)
.documentationLink(DocumentationLink.RDP)
.build());
final DoubleProperty windowOpacity = mapLocal(new GlobalDoubleProperty(1.0), "windowOpacity", Double.class, false);
final DoubleProperty windowOpacity =
map(Mapping.builder()
.property(new GlobalDoubleProperty(1.0))
.key("windowOpacity")
.valueClass(Double.class)
.requiresRestart(false)
.build());
final StringProperty customTerminalCommand =
mapLocal(new GlobalStringProperty(null), "customTerminalCommand", String.class, false);
map(Mapping.builder()
.property(new GlobalStringProperty(null))
.key("customTerminalCommand")
.valueClass(String.class)
.requiresRestart(false)
.build());
final BooleanProperty clearTerminalOnInit =
mapLocal(new GlobalBooleanProperty(true), "clearTerminalOnInit", Boolean.class, false);
map(Mapping.builder()
.property(new GlobalBooleanProperty(true))
.key("clearTerminalOnInit")
.valueClass(Boolean.class)
.requiresRestart(false)
.build());
final Property<List<SystemIconSource>> iconSources = map(Mapping.builder()
.property(new GlobalObjectProperty<>(new ArrayList<>()))
.key("iconSources")
@@ -125,9 +203,18 @@ public final class AppPrefs {
.vaultSpecific(true)
.build());
public final BooleanProperty disableCertutilUse =
mapLocal(new GlobalBooleanProperty(false), "disableCertutilUse", Boolean.class, false);
map(Mapping.builder()
.property(new GlobalBooleanProperty(false))
.key("disableCertutilUse")
.valueClass(Boolean.class)
.build());
public final BooleanProperty useLocalFallbackShell =
mapLocal(new GlobalBooleanProperty(false), "useLocalFallbackShell", Boolean.class, true);
map(Mapping.builder()
.property(new GlobalBooleanProperty(false))
.key("useLocalFallbackShell")
.valueClass(Boolean.class)
.requiresRestart(true)
.build());
final Property<ShellDialect> localShellDialect = map(Mapping.builder()
.property(new GlobalObjectProperty<>(
ProcessControlProvider.get().getAvailableLocalDialects().getFirst()))
@@ -142,7 +229,11 @@ public final class AppPrefs {
public final Property<Boolean> alwaysConfirmElevation =
mapVaultShared(new GlobalObjectProperty<>(false), "alwaysConfirmElevation", Boolean.class, false);
public final BooleanProperty focusWindowOnNotifications =
mapLocal(new GlobalBooleanProperty(true), "focusWindowOnNotifications", Boolean.class, false);
map(Mapping.builder()
.property(new GlobalBooleanProperty(false))
.key("focusWindowOnNotifications")
.valueClass(Boolean.class)
.build());
public final BooleanProperty dontCachePasswords =
mapVaultShared(new GlobalBooleanProperty(false), "dontCachePasswords", Boolean.class, false);
public final Property<ExternalVncClient> vncClient = map(Mapping.builder()
@@ -178,7 +269,11 @@ public final class AppPrefs {
.documentationLink(DocumentationLink.TERMINAL_MULTIPLEXER)
.build());
final Property<Boolean> terminalAlwaysPauseOnExit =
mapLocal(new GlobalBooleanProperty(true), "terminalAlwaysPauseOnExit", Boolean.class, false);
map(Mapping.builder()
.property(new GlobalBooleanProperty(false))
.key("terminalAlwaysPauseOnExit")
.valueClass(Boolean.class)
.build());
final Property<TerminalPrompt> terminalPrompt = map(Mapping.builder()
.property(new GlobalObjectProperty<>(null))
.key("terminalPrompt")
@@ -186,8 +281,13 @@ public final class AppPrefs {
.log(false)
.documentationLink(DocumentationLink.TERMINAL_PROMPT)
.build());
final ObjectProperty<StartupBehaviour> startupBehaviour = mapLocal(
new GlobalObjectProperty<>(StartupBehaviour.GUI), "startupBehaviour", StartupBehaviour.class, true);
final ObjectProperty<StartupBehaviour> startupBehaviour =
map(Mapping.builder()
.property(new GlobalObjectProperty<>(StartupBehaviour.GUI))
.key("startupBehaviour")
.valueClass(StartupBehaviour.class)
.requiresRestart(true)
.build());
public final BooleanProperty enableGitStorage = map(Mapping.builder()
.property(new GlobalBooleanProperty(false))
.key("enableGitStorage")
@@ -203,7 +303,11 @@ public final class AppPrefs {
.documentationLink(DocumentationLink.SYNC)
.build());
final ObjectProperty<CloseBehaviour> closeBehaviour =
mapLocal(new GlobalObjectProperty<>(CloseBehaviour.QUIT), "closeBehaviour", CloseBehaviour.class, false);
map(Mapping.builder()
.property(new GlobalObjectProperty<>(CloseBehaviour.QUIT))
.key("closeBehaviour")
.valueClass(CloseBehaviour.class)
.build());
final ObjectProperty<ExternalEditorType> externalEditor =
mapLocal(new GlobalObjectProperty<>(), "externalEditor", ExternalEditorType.class, false);
final StringProperty customEditorCommand =
@@ -288,75 +392,42 @@ public final class AppPrefs {
mapVaultShared(new GlobalStringProperty(), "workspaceLock", String.class, true);
@Getter
private final List<AppPrefsCategory> categories;
private final List<AppPrefsCategory> categories = List.of(
new AboutCategory(),
new AppearanceCategory(),
new VaultCategory(),
new SyncCategory(),
new PasswordManagerCategory(),
new TerminalCategory(),
new LoggingCategory(),
new EditorCategory(),
new RdpCategory(),
new VncCategory(),
new SshCategory(),
new ConnectionHubCategory(),
new FileBrowserCategory(),
new IconsCategory(),
new SystemCategory(),
new ApiCategory(),
new McpCategory(),
new UpdatesCategory(),
new SecurityCategory(),
new WorkspacesCategory(),
new DeveloperCategory(),
new TroubleshootCategory(),
new LinksCategory());
private final AppPrefsStorageHandler globalStorageHandler = new AppPrefsStorageHandler(
AppProperties.get().getDataDir().resolve("settings").resolve("preferences.json"));
private final Map<Mapping, OptionsBuilder> customEntries = new LinkedHashMap<>();
@Getter
private final Property<AppPrefsCategory> selectedCategory;
private final Property<AppPrefsCategory> selectedCategory = new GlobalObjectProperty<>(categories.getFirst());
private final PrefsHandler extensionHandler = new PrefsHandlerImpl();
private AppPrefsStorageHandler vaultStorageHandler;
private AppPrefs() {
this.categories = Stream.of(
new AboutCategory(),
new AppearanceCategory(),
new VaultCategory(),
new SyncCategory(),
new PasswordManagerCategory(),
new TerminalCategory(),
new LoggingCategory(),
new EditorCategory(),
new RdpCategory(),
new VncCategory(),
new SshCategory(),
new ConnectionHubCategory(),
new FileBrowserCategory(),
new IconsCategory(),
new SystemCategory(),
new ApiCategory(),
new McpCategory(),
new UpdatesCategory(),
new SecurityCategory(),
new WorkspacesCategory(),
new DeveloperCategory(),
new TroubleshootCategory(),
new LinksCategory())
.toList();
this.selectedCategory = new GlobalObjectProperty<>(categories.getFirst());
}
public static void initLocal() {
INSTANCE = new AppPrefs();
PrefsProvider.getAll().forEach(prov -> prov.addPrefs(INSTANCE.extensionHandler));
INSTANCE.loadLocal();
INSTANCE.vaultStorageHandler =
new AppPrefsStorageHandler(DataStorage.getStorageDirectory().resolve("preferences.json"));
INSTANCE.fixLocalValues();
}
public static void initSynced() throws Exception {
INSTANCE.loadSharedRemote();
INSTANCE.encryptAllVaultData.addListener((observableValue, aBoolean, t1) -> {
if (DataStorage.get() != null) {
DataStorage.get().forceRewrite();
}
});
}
public static void reset() {
INSTANCE.save();
// Keep instance as we might need some values on shutdown, e.g. on update with terminals
// INSTANCE = null;
}
public static AppPrefs get() {
return INSTANCE;
}
private AppPrefs() {}
public ObservableBooleanValue disableHardwareAcceleration() {
return disableHardwareAcceleration;
@@ -647,8 +718,8 @@ public final class AppPrefs {
var writable = (Property<T>) prop;
PlatformThread.runLaterIfNeededBlocking(() -> {
writable.setValue(newValue);
save();
});
save();
}
private void fixLocalValues() {
@@ -751,7 +822,7 @@ public final class AppPrefs {
return val;
}
public void save() {
public synchronized void save() {
for (Mapping m : mapping) {
AppPrefsStorageHandler handler = m.isVaultSpecific() ? vaultStorageHandler : globalStorageHandler;
// It might be possible that we save while the vault handler is not initialized yet / has no file or
@@ -23,8 +23,6 @@ import java.util.stream.Collectors;
public class AppPrefsSidebarComp extends SimpleComp {
private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
@Override
protected Region createSimple() {
var effectiveCategories = AppPrefs.get().getCategories().stream()
@@ -43,7 +41,7 @@ public class AppPrefsSidebarComp extends SimpleComp {
struc.get().setTextAlignment(TextAlignment.LEFT);
struc.get().setAlignment(Pos.CENTER_LEFT);
AppPrefs.get().getSelectedCategory().subscribe(val -> {
struc.get().pseudoClassStateChanged(SELECTED, appPrefsCategory.equals(val));
struc.get().pseudoClassStateChanged(PseudoClass.getPseudoClass("selected"), appPrefsCategory.equals(val));
});
})
.grow(true, false);
@@ -12,7 +12,7 @@ import javafx.scene.layout.Region;
public class UpdateCheckComp extends SimpleComp {
private void showAlert() {
private void showDialog() {
ThreadHelper.runFailableAsync(() -> {
AppDistributionType.get().getUpdateHandler().refreshUpdateCheckSilent(false, false);
UpdateAvailableDialog.showIfNeeded(false);
@@ -96,7 +96,7 @@ public class UpdateCheckComp extends SimpleComp {
return new TileButtonComp(name, description, graphic, actionEvent -> {
actionEvent.consume();
if (uh.getPreparedUpdate().getValue() != null) {
showAlert();
showDialog();
return;
}
@@ -7,12 +7,11 @@ import io.xpipe.app.comp.base.ModalOverlay;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.issue.ErrorAction;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.util.DocumentationLink;
import io.xpipe.app.util.Hyperlinks;
public class UpdateChangelogAlert {
public class UpdateChangelogDialog {
private static boolean shown = false;
@@ -21,23 +20,10 @@ public class UpdateChangelogAlert {
if (update != null && !AppDistributionType.get().getUpdateHandler().isUpdateSucceeded()) {
ErrorEventFactory.fromMessage(AppI18n.get("updateFail"))
.documentationLink(DocumentationLink.UPDATE_FAIL)
.customAction(new ErrorAction() {
@Override
public String getName() {
return AppI18n.get("updateFailAction");
}
@Override
public String getDescription() {
return AppI18n.get("updateFailActionDescription");
}
@Override
public boolean handle(ErrorEvent event) {
Hyperlinks.open(Hyperlinks.GITHUB_LATEST);
return true;
}
})
.customAction(ErrorAction.translated("updateFailAction", () -> {
Hyperlinks.open(Hyperlinks.GITHUB_LATEST);
return true;
}))
.handle();
return;
}
@@ -45,11 +45,6 @@ public class Deobfuscator {
return stackTrace;
}
public static void printStackTrace(Throwable t) {
var s = deobfuscateToString(t);
System.err.println(s);
}
private static boolean canDeobfuscate() {
if (!System.getenv().containsKey("XPIPE_MAPPING")) {
return false;