diff --git a/app/src/main/java/io/xpipe/app/core/AppStyle.java b/app/src/main/java/io/xpipe/app/core/AppStyle.java index 6a453f86e..bfcfbabdb 100644 --- a/app/src/main/java/io/xpipe/app/core/AppStyle.java +++ b/app/src/main/java/io/xpipe/app/core/AppStyle.java @@ -22,7 +22,7 @@ public class AppStyle { private static final Map STYLESHEET_CONTENTS = new LinkedHashMap<>(); private static final Map THEME_SPECIFIC_STYLESHEET_CONTENTS = new LinkedHashMap<>(); private static final Map THEME_PREFERENCES_STYLESHEET_CONTENTS = new LinkedHashMap<>(); - private static final List scenes = new ArrayList<>(); + private static final WeakHashMap scenes = new WeakHashMap<>(); private static String FONT_CONTENTS = null; public static void init() { @@ -113,11 +113,11 @@ public class AppStyle { private static void changeFontUsage(boolean use) { if (!use) { - scenes.forEach(scene -> { + scenes.keySet().forEach(scene -> { add(scene, FONT_CONTENTS); }); } else { - scenes.forEach(scene -> { + scenes.keySet().forEach(scene -> { scene.getStylesheets().remove(FONT_CONTENTS); }); } @@ -133,7 +133,7 @@ public class AppStyle { return; } - scenes.forEach(scene -> { + scenes.keySet().forEach(scene -> { scene.getStylesheets().remove(THEME_PREFERENCES_STYLESHEET_CONTENTS.get(t)); }); THEME_PREFERENCES_STYLESHEET_CONTENTS.clear(); @@ -141,13 +141,13 @@ public class AppStyle { THEME_PREFERENCES_STYLESHEET_CONTENTS.put( theme, Styles.toDataURI(theme.getPlatformPreferencesStylesheet())); } - scenes.forEach(scene -> { + scenes.keySet().forEach(scene -> { add(scene, THEME_PREFERENCES_STYLESHEET_CONTENTS.get(t)); }); } private static void changeTheme(AppTheme.Theme theme) { - scenes.forEach(scene -> { + scenes.keySet().forEach(scene -> { scene.getStylesheets().removeAll(THEME_SPECIFIC_STYLESHEET_CONTENTS.values()); scene.getStylesheets().removeAll(THEME_PREFERENCES_STYLESHEET_CONTENTS.values()); add(scene, THEME_SPECIFIC_STYLESHEET_CONTENTS.get(theme)); @@ -183,6 +183,6 @@ public class AppStyle { } TrackEvent.debug("Added stylesheets for scene"); - scenes.add(scene); + scenes.put(scene, null); } } diff --git a/app/src/main/java/io/xpipe/app/core/AppTheme.java b/app/src/main/java/io/xpipe/app/core/AppTheme.java index 96c4e0634..a3ec2f3de 100644 --- a/app/src/main/java/io/xpipe/app/core/AppTheme.java +++ b/app/src/main/java/io/xpipe/app/core/AppTheme.java @@ -32,6 +32,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.SneakyThrows; +import java.lang.ref.WeakReference; import java.nio.file.Files; import java.util.List; import java.util.function.Supplier; @@ -46,41 +47,65 @@ public class AppTheme { private static boolean init; public static void initThemeHandlers(Stage stage) { - stage.getScene().rootProperty().subscribe(root -> { - if (root == null) { - return; - } + if (stage.getScene() == null) { + return; + } + var root = stage.getScene().getRoot(); + if (root != null) { root.pseudoClassStateChanged( PseudoClass.getPseudoClass(OsType.ofLocal().getId()), true); - if (AppPrefs.get() == null) { + } + + if (AppPrefs.get() == null) { + if (root != null) { var def = Theme.getDefaultLightTheme(); root.pseudoClassStateChanged(PseudoClass.getPseudoClass(def.getCssId()), true); root.pseudoClassStateChanged(LIGHT, true); root.pseudoClassStateChanged(DARK, false); root.pseudoClassStateChanged(PRETTY, true); root.pseudoClassStateChanged(PERFORMANCE, false); - return; } + return; + } - AppPrefs.get().theme().subscribe(t -> { - Theme.ALL.forEach(theme -> { - root.pseudoClassStateChanged( - PseudoClass.getPseudoClass(theme.getCssId()), - theme.getCssId().equals(t.getCssId())); - }); - if (t == null) { - return; + // Allow for GC + var ref = new WeakReference<>(stage); + + AppPrefs.get().theme().subscribe(t -> { + var val = ref.get(); + if (val != null) { + var scene = val.getScene(); + if (scene != null) { + var r = scene.getRoot(); + if (r != null) { + Theme.ALL.forEach(theme -> { + r.pseudoClassStateChanged( + PseudoClass.getPseudoClass(theme.getCssId()), + theme.getCssId().equals(t.getCssId())); + }); + + if (t != null) { + r.pseudoClassStateChanged(LIGHT, !t.isDark()); + r.pseudoClassStateChanged(DARK, t.isDark()); + } + } } + } + }); - root.pseudoClassStateChanged(LIGHT, !t.isDark()); - root.pseudoClassStateChanged(DARK, t.isDark()); - }); - - AppPrefs.get().performanceMode().subscribe(val -> { - root.pseudoClassStateChanged(PRETTY, !val); - root.pseudoClassStateChanged(PERFORMANCE, val); - }); + AppPrefs.get().performanceMode().subscribe(pm -> { + var val = ref.get(); + if (val != null) { + var scene = val.getScene(); + if (scene != null) { + var r = scene.getRoot(); + if (r != null) { + r.pseudoClassStateChanged(PRETTY, !pm); + r.pseudoClassStateChanged(PERFORMANCE, pm); + } + } + } }); } diff --git a/app/src/main/java/io/xpipe/app/core/window/AppModifiedStage.java b/app/src/main/java/io/xpipe/app/core/window/AppModifiedStage.java index 70120600f..2fe4098ee 100644 --- a/app/src/main/java/io/xpipe/app/core/window/AppModifiedStage.java +++ b/app/src/main/java/io/xpipe/app/core/window/AppModifiedStage.java @@ -20,6 +20,8 @@ import javafx.util.Duration; import lombok.SneakyThrows; import org.apache.commons.lang3.SystemUtils; +import java.lang.ref.WeakReference; + public class AppModifiedStage extends Stage { public static boolean mergeFrame() { @@ -30,9 +32,11 @@ public class AppModifiedStage extends Stage { ObservableList list = Window.getWindows(); list.addListener((ListChangeListener) c -> { if (c.next() && c.wasAdded()) { - var added = c.getAddedSubList().getFirst(); - if (added instanceof Stage stage) { - hookUpStage(stage); + var added = c.getAddedSubList(); + for (Window window : added) { + if (window instanceof Stage stage) { + hookUpStage(stage); + } } } }); @@ -46,17 +50,30 @@ public class AppModifiedStage extends Stage { private static void hookUpStage(Stage stage) { applyModes(stage); + + // Fix GC not working when the stage is no longer needed + var ref = new WeakReference<>(stage); + if (AppPrefs.get() != null) { AppPrefs.get().theme().addListener((observable, oldValue, newValue) -> { - updateStage(stage); + var val = ref.get(); + if (val != null) { + updateStage(val); + } }); AppPrefs.get().performanceMode().addListener((observable, oldValue, newValue) -> { - updateStage(stage); + var val = ref.get(); + if (val != null) { + updateStage(val); + } }); } if (stage.getScene() != null) { stage.getScene().rootProperty().addListener((observable, oldValue, newValue) -> { - applyModes(stage); + var val = ref.get(); + if (val != null) { + applyModes(val); + } }); } }