diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserAbstractSessionModel.java b/app/src/main/java/io/xpipe/app/browser/BrowserAbstractSessionModel.java index 86fcb3816..41bb00a4e 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserAbstractSessionModel.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserAbstractSessionModel.java @@ -12,6 +12,9 @@ import javafx.collections.ObservableList; import lombok.Getter; +import java.util.ArrayList; +import java.util.List; + @Getter public class BrowserAbstractSessionModel { @@ -43,4 +46,10 @@ public class BrowserAbstractSessionModel { this.sessionEntries.remove(e); } } + + public List getSessionEntriesSnapshot() { + synchronized (this) { + return new ArrayList<>(sessionEntries); + } + } } diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserFullSessionModel.java b/app/src/main/java/io/xpipe/app/browser/BrowserFullSessionModel.java index 795b6e2fc..768465f1c 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserFullSessionModel.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserFullSessionModel.java @@ -25,6 +25,7 @@ import javafx.collections.ListChangeListener; import javafx.collections.ObservableMap; import lombok.Getter; +import lombok.SneakyThrows; import java.util.*; @@ -34,7 +35,12 @@ public class BrowserFullSessionModel extends BrowserAbstractSessionModel { - if (newValue != null) { - Platform.runLater(() -> { - tabs.setClip(null); - tabs.setPickOnBounds(false); - tabs.lookupAll(".tab-header-area").forEach(node -> { - node.setClip(null); - node.setPickOnBounds(false); + setupCustomStyle(tabs); + // Sync to guarantee that no external changes are made during this + synchronized (model) { + setupTabEntries(tabs); + } + setupKeyEvents(tabs); - var r = (Region) node; - r.prefHeightProperty().bind(r.maxHeightProperty()); - r.setMinHeight(Region.USE_PREF_SIZE); - }); - tabs.lookupAll(".headers-region").forEach(node -> { - node.setClip(null); - node.setPickOnBounds(false); - - var r = (Region) node; - r.prefHeightProperty().bind(r.maxHeightProperty()); - r.setMinHeight(Region.USE_PREF_SIZE); - }); - - Region headerArea = (Region) tabs.lookup(".tab-header-area"); - headerArea - .paddingProperty() - .bind(Bindings.createObjectBinding( - () -> new Insets(2, 0, 4, -leftPadding.get() + 2), leftPadding)); - headerHeight.bind(headerArea.heightProperty()); - }); - } - }); + return tabs; + } + private void setupTabEntries(TabPane tabs) { var map = new HashMap(); // Restore state @@ -117,9 +96,7 @@ public class BrowserSessionTabsComp extends SimpleComp { map.put(v, t); tabs.getTabs().add(t); }); - tabs.getSelectionModel() - .select(model.getSessionEntries() - .indexOf(model.getSelectedEntry().getValue())); + tabs.getSelectionModel().select(model.getSessionEntries().indexOf(model.getSelectedEntry().getValue())); // Used for ignoring changes by the tabpane when new tabs are added. We want to perform the selections manually! var addingTab = new SimpleBooleanProperty(); @@ -135,9 +112,9 @@ public class BrowserSessionTabsComp extends SimpleComp { return; } - var source = map.entrySet().stream() - .filter(openFileSystemModelTabEntry -> - openFileSystemModelTabEntry.getValue().equals(newValue)) + var source = map.entrySet() + .stream() + .filter(openFileSystemModelTabEntry -> openFileSystemModelTabEntry.getValue().equals(newValue)) .findAny() .map(Map.Entry::getKey) .orElse(null); @@ -152,9 +129,9 @@ public class BrowserSessionTabsComp extends SimpleComp { return; } - var toSelect = map.entrySet().stream() - .filter(openFileSystemModelTabEntry -> - openFileSystemModelTabEntry.getKey().equals(newValue)) + var toSelect = map.entrySet() + .stream() + .filter(openFileSystemModelTabEntry -> openFileSystemModelTabEntry.getKey().equals(newValue)) .findAny() .map(Map.Entry::getValue) .orElse(null); @@ -194,9 +171,9 @@ public class BrowserSessionTabsComp extends SimpleComp { tabs.getTabs().addListener((ListChangeListener) c -> { while (c.next()) { for (var r : c.getRemoved()) { - var source = map.entrySet().stream() - .filter(openFileSystemModelTabEntry -> - openFileSystemModelTabEntry.getValue().equals(r)) + var source = map.entrySet() + .stream() + .filter(openFileSystemModelTabEntry -> openFileSystemModelTabEntry.getValue().equals(r)) .findAny() .orElse(null); @@ -209,7 +186,43 @@ public class BrowserSessionTabsComp extends SimpleComp { } } }); + } + private void setupCustomStyle(TabPane tabs) { + tabs.skinProperty().subscribe(newValue -> { + if (newValue != null) { + Platform.runLater(() -> { + tabs.setClip(null); + tabs.setPickOnBounds(false); + tabs.lookupAll(".tab-header-area").forEach(node -> { + node.setClip(null); + node.setPickOnBounds(false); + + var r = (Region) node; + r.prefHeightProperty().bind(r.maxHeightProperty()); + r.setMinHeight(Region.USE_PREF_SIZE); + }); + tabs.lookupAll(".headers-region").forEach(node -> { + node.setClip(null); + node.setPickOnBounds(false); + + var r = (Region) node; + r.prefHeightProperty().bind(r.maxHeightProperty()); + r.setMinHeight(Region.USE_PREF_SIZE); + }); + + Region headerArea = (Region) tabs.lookup(".tab-header-area"); + headerArea + .paddingProperty() + .bind(Bindings.createObjectBinding( + () -> new Insets(2, 0, 4, -leftPadding.get() + 2), leftPadding)); + headerHeight.bind(headerArea.heightProperty()); + }); + } + }); + } + + private static void setupKeyEvents(TabPane tabs) { tabs.addEventHandler(KeyEvent.KEY_PRESSED, keyEvent -> { var current = tabs.getSelectionModel().getSelectedItem(); if (current == null) { @@ -255,8 +268,6 @@ public class BrowserSessionTabsComp extends SimpleComp { keyEvent.consume(); } }); - - return tabs; } private ContextMenu createContextMenu(TabPane tabs, Tab tab, BrowserSessionTab tabModel) { diff --git a/lang/app/strings/translations_de.properties b/lang/app/strings/translations_de.properties index 55b7f0729..b2b73f318 100644 --- a/lang/app/strings/translations_de.properties +++ b/lang/app/strings/translations_de.properties @@ -447,7 +447,8 @@ copyPassword=copyPassword lockVaultOnHibernation=Tresor im Ruhezustand des Computers sperren lockVaultOnHibernationDescription=Wenn diese Funktion aktiviert ist, wird der Tresor automatisch gesperrt, sobald dein Computer in den Ruhezustand versetzt wird. Nach dem Aufwachen musst du deine Tresor-Passphrase erneut eingeben. overview=Übersicht -history=Geschichte +#custom +history=Verlauf skipAll=Alles überspringen notes=Anmerkungen addNotes=Notizen hinzufügen