diff --git a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java index e591c6859..9a633c0cf 100644 --- a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java +++ b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java @@ -366,8 +366,8 @@ public class AppMainWindow { var screens = Screen.getScreens(); if (screens.size() > 1) { - stage.setWidth(1280); - stage.setHeight(780); + stage.setWidth(1520); + stage.setHeight(860); return; } diff --git a/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryComp.java index 23a56bd25..d4dedf18e 100644 --- a/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryComp.java @@ -53,7 +53,7 @@ public abstract class StoreEntryComp extends SimpleRegionBuilder { public static final ObservableDoubleValue INFO_WIDTH = Bindings.createDoubleBinding( () -> { var w = App.getApp().getStage().getWidth(); - if (w > 1800) { + if (w > 1500) { return (w / 1.8) - 100; } else if (w >= 1000) { return (w / 2.0) - 100; diff --git a/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryWrapper.java b/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryWrapper.java index 399b384ff..f10ae9a5b 100644 --- a/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryWrapper.java +++ b/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryWrapper.java @@ -89,9 +89,7 @@ public class StoreEntryWrapper { var newCat = StoreViewState.get().getCategoryWrapper(category); ThreadHelper.runAsync(() -> { - synchronized (this) { - DataStorage.get().moveEntryToCategory(entry, category); - } + DataStorage.get().moveEntryToCategory(entry, category); Platform.runLater(() -> { oldCat.update(); newCat.update(); @@ -113,9 +111,7 @@ public class StoreEntryWrapper { public void delete() { ThreadHelper.runAsync(() -> { - synchronized (this) { - DataStorage.get().deleteWithChildren(this.entry); - } + DataStorage.get().deleteWithChildren(this.entry); }); } diff --git a/app/src/main/java/io/xpipe/app/hub/list/StoreViewState.java b/app/src/main/java/io/xpipe/app/hub/list/StoreViewState.java index 3baaff0dd..d86caee18 100644 --- a/app/src/main/java/io/xpipe/app/hub/list/StoreViewState.java +++ b/app/src/main/java/io/xpipe/app/hub/list/StoreViewState.java @@ -555,18 +555,6 @@ public class StoreViewState { .filter(storeEntryWrapper -> a.contains(storeEntryWrapper.getEntry())) .toList(); } - List cats; - synchronized (this) { - cats = categories.getList().stream() - .filter(storeCategoryWrapper -> allEntries.getList().stream() - .anyMatch(storeEntryWrapper -> storeEntryWrapper - .getEntry() - .getCategoryUuid() - .equals(storeCategoryWrapper - .getCategory() - .getUuid()))) - .toList(); - } Platform.runLater(() -> { // Don't update anything if we have already reset if (INSTANCE == null) { @@ -576,7 +564,7 @@ public class StoreViewState { synchronized (this) { allEntries.getList().removeAll(l); } - cats.forEach(storeCategoryWrapper -> storeCategoryWrapper.update()); + categories.getList().forEach(storeCategoryWrapper -> storeCategoryWrapper.update()); }); } diff --git a/app/src/main/java/io/xpipe/app/hub/section/StoreSectionConfig.java b/app/src/main/java/io/xpipe/app/hub/section/StoreSectionConfig.java index 47ebee1ea..5ef0dacb6 100644 --- a/app/src/main/java/io/xpipe/app/hub/section/StoreSectionConfig.java +++ b/app/src/main/java/io/xpipe/app/hub/section/StoreSectionConfig.java @@ -22,6 +22,7 @@ public class StoreSectionConfig { StoreCategoryWrapper category; Set selected; StoreSectionDrag dragOperation; + Set added; public boolean isTop(StoreEntryWrapper wrapper) { if (wrapper.getEntry().getValidity() == DataStoreEntry.Validity.LOAD_FAILED) { @@ -74,11 +75,12 @@ public class StoreSectionConfig { } var isBatchSelected = selected.contains(section.getWrapper()); + var wasAddedAfterFilter = added.contains(section.getWrapper()); var matchesFilterThis = filter != null && matchesFilter(section, filter); var matchesFilterParents = filter != null && parents.stream().anyMatch(p -> p.matchesFilter(filter)); var matchesFilter = filter == null || matchesFilterThis || matchesFilterParents; - if (!isBatchSelected && !matchesFilter) { + if (!isBatchSelected && !matchesFilter && !wasAddedAfterFilter) { return false; } @@ -164,6 +166,6 @@ public class StoreSectionConfig { if (parent != null) { l.add(parent); } - return new StoreSectionConfig(l, selector, filter, category, selected, dragOperation); + return new StoreSectionConfig(l, selector, filter, category, selected, dragOperation, added); } } diff --git a/app/src/main/java/io/xpipe/app/hub/section/StoreSectionState.java b/app/src/main/java/io/xpipe/app/hub/section/StoreSectionState.java index 8adbb92cf..96b3b6509 100644 --- a/app/src/main/java/io/xpipe/app/hub/section/StoreSectionState.java +++ b/app/src/main/java/io/xpipe/app/hub/section/StoreSectionState.java @@ -12,6 +12,7 @@ import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.value.ObservableBooleanValue; import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; import javafx.collections.ObservableList; import lombok.Getter; @@ -32,6 +33,7 @@ public class StoreSectionState { private final ObservableList selected; private final ObservableValue sortMode; private final ObservableValue dragOperation; + private final ObservableList added = FXCollections.observableArrayList(); private final IntegerProperty orderUpdateIndex = new SimpleIntegerProperty(); private final ObservableBooleanValue enabled; @@ -92,7 +94,8 @@ public class StoreSectionState { filter.getValue(), category.getValue(), new HashSet<>(selected), - dragOperation.getValue()); + dragOperation.getValue(), + new HashSet<>(added)); rootSection.refreshAll(all, config, 0, orderUpdateIndex.get()); rootSection.refreshShown(config); rootSection.apply(true); @@ -106,7 +109,8 @@ public class StoreSectionState { filter.getValue(), category.getValue(), new HashSet<>(selected), - dragOperation.getValue()); + dragOperation.getValue(), + new HashSet<>(added)); rootSection.refreshShown(config); rootSection.apply(alwaysUpdateAll); } @@ -121,6 +125,16 @@ public class StoreSectionState { }); Listeners.attach(enabled, filter, () -> { + added.clear(); + updateShown(false); + }); + + Listeners.listen(enabled, StoreViewState.get().getAllEntries().getList(), change -> { + while (change.next()) { + if (change.wasAdded()) { + added.addAll(change.getAddedSubList()); + } + } updateShown(false); }); diff --git a/app/src/main/java/io/xpipe/app/platform/Listeners.java b/app/src/main/java/io/xpipe/app/platform/Listeners.java index e205dc7bf..355bdc150 100644 --- a/app/src/main/java/io/xpipe/app/platform/Listeners.java +++ b/app/src/main/java/io/xpipe/app/platform/Listeners.java @@ -98,4 +98,20 @@ public class Listeners { } }); } + + public static void listen(ObservableValue enabled, ObservableList value, Consumer> consumer) { + var listener = new ListChangeListener() { + @Override + public void onChanged(Change c) { + consumer.accept(c); + } + }; + enabled.subscribe(v -> { + if (v) { + value.addListener(listener); + } else { + value.removeListener(listener); + } + }); + } } diff --git a/app/src/main/java/io/xpipe/app/rdp/DevolutionsRdpClient.java b/app/src/main/java/io/xpipe/app/rdp/DevolutionsRdpClient.java index 5435f2b60..302b9230c 100644 --- a/app/src/main/java/io/xpipe/app/rdp/DevolutionsRdpClient.java +++ b/app/src/main/java/io/xpipe/app/rdp/DevolutionsRdpClient.java @@ -47,12 +47,7 @@ public class DevolutionsRdpClient implements ExternalApplicationType.WindowsType @Override public void launch(RdpLaunchConfig configuration) throws Exception { var config = writeRdpConfigFile(configuration.getTitle(), configuration.getConfig()); - launch(CommandBuilder.of().addFile(config)); - ThreadHelper.runFailableAsync(() -> { - // Startup is slow - ThreadHelper.sleep(10000); - FileUtils.deleteQuietly(config.toFile()); - }); + launch(CommandBuilder.of().addQuoted(config.toString())); } @Override diff --git a/app/src/main/java/io/xpipe/app/rdp/ExternalRdpClient.java b/app/src/main/java/io/xpipe/app/rdp/ExternalRdpClient.java index 7c893e273..856c4dc90 100644 --- a/app/src/main/java/io/xpipe/app/rdp/ExternalRdpClient.java +++ b/app/src/main/java/io/xpipe/app/rdp/ExternalRdpClient.java @@ -107,7 +107,7 @@ public interface ExternalRdpClient extends PrefsValue { String getWebsite(); default Path writeRdpConfigFile(String title, RdpConfig input) throws Exception { - var name = OsFileSystem.ofLocal().makeFileSystemCompatible(title); + var name = OsFileSystem.ofLocal().makeFileSystemCompatible(title).replaceAll("\\s+", "_"); var file = AppLocalTemp.getLocalTempDataDirectory("rdp").resolve(name + ".rdp"); var string = input.toString() + "\n"; Files.createDirectories(file.getParent()); diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorage.java b/app/src/main/java/io/xpipe/app/storage/DataStorage.java index df3dccd26..6ef3b82ca 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -917,6 +917,8 @@ public abstract class DataStorage { if (toDelete.isEmpty()) { return; } + + this.listeners.forEach(l -> l.onStoreRemove(toDelete.toArray(DataStoreEntry[]::new))); for (var td : toDelete) { td.finalizeEntry(); diff --git a/app/src/main/java/io/xpipe/app/webtop/WebtopAppListManager.java b/app/src/main/java/io/xpipe/app/webtop/WebtopAppListManager.java index e21f1347b..c2269a49e 100644 --- a/app/src/main/java/io/xpipe/app/webtop/WebtopAppListManager.java +++ b/app/src/main/java/io/xpipe/app/webtop/WebtopAppListManager.java @@ -6,6 +6,7 @@ import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.process.ShellScript; import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.terminal.ExternalTerminalType; import io.xpipe.app.terminal.TerminalLaunch; import io.xpipe.app.update.AppDistributionType; import io.xpipe.app.util.GlobalTimer; @@ -230,10 +231,15 @@ public class WebtopAppListManager { var exec = AppInstallation.ofCurrent().getCliExecutablePath(); var endCommand = requiresRestart ? " && " + exec + " daemon stop --wait && " + exec + " open" : ";" + exec + " open"; + + var termPref = AppPrefs.get().terminalType().getValue(); + var term = termPref == null || !termPref.isAvailable() ? ExternalTerminalType.determineFallbackTerminalToOpen(termPref) : null; + TerminalLaunch.builder() .title("Install packages") .localScript(ShellScript.lines(command, endCommand)) .pauseOnExit(true) + .terminal(term) .launch(); } } diff --git a/version b/version index 99a59ebab..1dba9da6a 100644 --- a/version +++ b/version @@ -1 +1 @@ -24.0-25 +24.0-26