Improve memory usage

This commit is contained in:
crschnick
2026-01-27 19:09:04 +00:00
parent 6241d6112a
commit bbe912c3fc
5 changed files with 140 additions and 77 deletions
@@ -17,12 +17,15 @@ import javafx.beans.value.ObservableBooleanValue;
import javafx.css.PseudoClass;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public abstract class StoreSectionBaseComp extends RegionBuilder<VBox> {
@@ -66,7 +69,7 @@ public abstract class StoreSectionBaseComp extends RegionBuilder<VBox> {
if (section.getWrapper() != null) {
if (section.getDepth() == 1) {
section.getWrapper().getColor().subscribe(val -> {
BindingsHelper.attach(vbox, section.getWrapper().getColor(), val -> {
var newList = new ArrayList<>(vbox.getStyleClass());
newList.removeIf(s -> Arrays.stream(DataStoreColor.values())
.anyMatch(dataStoreColor -> dataStoreColor.getId().equals(s)));
@@ -81,33 +84,42 @@ public abstract class StoreSectionBaseComp extends RegionBuilder<VBox> {
});
}
section.getWrapper().getPerUser().subscribe(val -> {
BindingsHelper.attach(vbox, section.getWrapper().getPerUser(), val -> {
vbox.pseudoClassStateChanged(PseudoClass.getPseudoClass("per-user"), val);
});
}
}
protected void addVisibilityListeners(VBox root, HBox hbox) {
var children = new ArrayList<>(hbox.getChildren());
hbox.getChildren().clear();
root.visibleProperty().subscribe((newValue) -> {
Platform.runLater(() -> {
if (newValue) {
if (!root.isVisible()) {
return;
}
if (hbox.getChildren().size() == 0) {
hbox.getChildren().addAll(children);
}
} else {
if (root.isVisible()) {
return;
}
hbox.getChildren().clear();
protected void addVisibilityListeners(VBox root, Pane pane, Supplier<HBox> hbox) {
AtomicReference<HBox> built = new AtomicReference<>();
Consumer<Boolean> update = (visible) -> {
if (visible) {
if (root.getScene() == null || !root.isVisible()) {
return;
}
});
if (built.get() == null) {
built.set(hbox.get());
}
pane.getChildren().setAll(built.get());
} else {
if (root.isVisible()) {
return;
}
pane.getChildren().clear();
}
};
root.visibleProperty().subscribe((newValue) -> {
if (root.getScene() == null) {
update.accept(newValue);
} else {
Platform.runLater(() -> {
update.accept(newValue);
});
}
});
}
@@ -10,13 +10,16 @@ import io.xpipe.app.comp.base.VerticalComp;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import org.int4.fx.builders.pane.StackPaneBuilder;
import java.util.ArrayList;
import java.util.List;
@@ -30,6 +33,27 @@ public class StoreSectionComp extends StoreSectionBaseComp {
@Override
public VBox createSimple() {
var entryButton = StoreEntryComp.customSection(section);
var paneComp = new StackPaneBuilder();
paneComp.minHeight(entryButton.getHeight());
paneComp.maxHeight(entryButton.getHeight());
paneComp.prefHeight(entryButton.getHeight());
var effectiveExpanded = effectiveExpanded(section.getWrapper().getExpanded());
var content = createChildrenList(c -> StoreSection.customSection(c), Bindings.not(effectiveExpanded));
var full = new VerticalComp(List.of(paneComp, RegionBuilder.hseparator().hide(Bindings.not(effectiveExpanded)), content));
full.style("store-entry-section-comp");
full.apply(struc -> {
struc.setFillWidth(true);
var pane = ((Pane) struc.getChildren().getFirst());
addPseudoClassListeners(struc, section.getWrapper().getExpanded());
addVisibilityListeners(struc, pane, () -> buildContent(entryButton).build());
});
return full.build();
}
private RegionBuilder<HBox> buildContent(StoreEntryComp entryButton) {
entryButton.hgrow();
entryButton.apply(struc -> {
struc.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
@@ -78,23 +102,14 @@ public class StoreSectionComp extends StoreSectionBaseComp {
buttonList.add(expandButton);
var buttons = new VerticalComp(buttonList);
var topEntryList = new HorizontalComp(List.of(buttons, entryButton));
topEntryList.apply(struc -> struc.setAlignment(Pos.CENTER_LEFT));
topEntryList.apply(struc -> {
struc.setAlignment(Pos.CENTER_LEFT);
});
topEntryList.minHeight(entryButton.getHeight());
topEntryList.maxHeight(entryButton.getHeight());
topEntryList.prefHeight(entryButton.getHeight());
var effectiveExpanded = effectiveExpanded(section.getWrapper().getExpanded());
var content = createChildrenList(c -> StoreSection.customSection(c), Bindings.not(effectiveExpanded));
var full = new VerticalComp(
List.of(topEntryList, RegionBuilder.hseparator().hide(Bindings.not(effectiveExpanded)), content));
full.style("store-entry-section-comp");
full.apply(struc -> {
struc.setFillWidth(true);
var hbox = ((HBox) struc.getChildren().getFirst());
addPseudoClassListeners(struc, section.getWrapper().getExpanded());
addVisibilityListeners(struc, hbox);
});
return full.build();
return topEntryList;
}
}
@@ -12,9 +12,11 @@ import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import org.int4.fx.builders.common.AbstractRegionBuilder;
import io.xpipe.app.comp.BaseRegionBuilder;
import org.int4.fx.builders.pane.StackPaneBuilder;
import java.util.ArrayList;
import java.util.function.BiConsumer;
@@ -43,38 +45,13 @@ public class StoreSectionMiniComp extends StoreSectionBaseComp {
@Override
public VBox createSimple() {
var list = new ArrayList<BaseRegionBuilder<?,?>>();
var list = new ArrayList<AbstractRegionBuilder<?,?>>();
if (section.getWrapper() != null) {
var root = new ButtonComp(section.getWrapper().getShownName(), () -> {
action.accept(section);
});
root.hgrow();
root.maxWidth(10000);
root.style("item");
root.apply(struc -> {
struc.setAlignment(Pos.CENTER_LEFT);
struc
.setGraphic(PrettyImageHelper.ofFixedSize(
section.getWrapper().getIconFile(), 16, 16)
.build());
struc.setMnemonicParsing(false);
});
augment.accept(section, root);
var expandButton = createExpandButton(() -> expanded.set(!expanded.get()), 20, expanded);
var quickAccessButton = createQuickAccessButton(20, action);
var buttonList = new ArrayList<BaseRegionBuilder<?,?>>();
buttonList.add(expandButton);
buttonList.add(root);
if (section.getDepth() == 1) {
buttonList.add(quickAccessButton);
}
var h = new HorizontalComp(buttonList);
h.apply(struc -> struc.setFillHeight(true));
h.prefHeight(28);
list.add(h);
var paneComp = new StackPaneBuilder();
paneComp.minHeight(28);
paneComp.maxHeight(28);
paneComp.prefHeight(28);
list.add(paneComp);
}
var content = createChildrenList(
@@ -88,10 +65,47 @@ public class StoreSectionMiniComp extends StoreSectionBaseComp {
struc.setFillWidth(true);
addPseudoClassListeners(struc, expanded);
if (section.getWrapper() != null) {
var hbox = ((HBox) struc.getChildren().getFirst());
addVisibilityListeners(struc, hbox);
var pane = ((Pane) struc.getChildren().getFirst());
addVisibilityListeners(struc, pane, () -> buildContent().build());
}
});
return full.build();
}
private RegionBuilder<HBox> buildContent() {
var root = new ButtonComp(section.getWrapper().getShownName(), () -> {
action.accept(section);
});
root.hgrow();
root.maxWidth(10000);
root.style("item");
root.apply(struc -> {
struc.setAlignment(Pos.CENTER_LEFT);
struc
.setGraphic(PrettyImageHelper.ofFixedSize(
section.getWrapper().getIconFile(), 16, 16)
.build());
struc.setMnemonicParsing(false);
});
augment.accept(section, root);
var expandButton = createExpandButton(() -> expanded.set(!expanded.get()), 20, expanded);
var quickAccessButton = createQuickAccessButton(20, action);
var buttonList = new ArrayList<BaseRegionBuilder<?,?>>();
buttonList.add(expandButton);
buttonList.add(root);
if (section.getDepth() == 1) {
buttonList.add(quickAccessButton);
}
var h = new HorizontalComp(buttonList);
h.apply(struc -> struc.setFillHeight(true));
h.minHeight(28);
h.prefHeight(28);
h.maxHeight(28);
return h;
}
}
@@ -6,13 +6,17 @@ import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.layout.Region;
import lombok.Value;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@SuppressWarnings("InfiniteLoopStatement")
@@ -73,6 +77,23 @@ public class BindingsHelper {
return prop;
}
public static <R extends Region, T> void attach(R node, ObservableValue<T> value, Consumer<T> consumer) {
var listener = new ChangeListener<T>() {
@Override
public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) {
consumer.accept(newValue);
}
};
node.sceneProperty().subscribe(scene -> {
if (scene != null) {
consumer.accept(value.getValue());
value.addListener(listener);
} else {
value.removeListener(listener);
}
});
}
@Value
private static class ReferenceEntry {
+9 -8
View File
@@ -161,14 +161,15 @@ def getJvmArgs() {
jvmRunArgs += ['-XX:+UseStringDeduplication']
// GC config
jvmRunArgs += ['-XX:+UseG1GC',
'-Xms300m',
'-Xmx4G',
'-XX:MinHeapFreeRatio=20',
'-XX:MaxHeapFreeRatio=30',
'-XX:GCTimeRatio=9',
// The default makes GC pauses longer for some reason
'-XX:G1HeapRegionSize=4m'
jvmRunArgs += [
'-XX:+UseG1GC',
'-Xms200m',
'-Xmx4G',
'-XX:MinHeapFreeRatio=15',
'-XX:MaxHeapFreeRatio=25',
'-XX:GCTimeRatio=9',
// The default makes GC pauses longer for some reason
'-XX:G1HeapRegionSize=4m'
]
// Why is this not on by default? ...