|
20 | 20 | import com.jfoenix.controls.JFXDialog; |
21 | 21 | import com.jfoenix.controls.JFXSnackbar; |
22 | 22 | import javafx.application.Platform; |
23 | | -import javafx.beans.binding.Bindings; |
| 23 | +import javafx.beans.InvalidationListener; |
| 24 | +import javafx.beans.WeakInvalidationListener; |
24 | 25 | import javafx.beans.value.ChangeListener; |
25 | 26 | import javafx.beans.value.ObservableValue; |
26 | 27 | import javafx.event.EventHandler; |
|
35 | 36 | import org.jackhuang.hmcl.Launcher; |
36 | 37 | import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDnD; |
37 | 38 | import org.jackhuang.hmcl.setting.EnumBackgroundImage; |
| 39 | +import org.jackhuang.hmcl.task.Schedulers; |
38 | 40 | import org.jackhuang.hmcl.ui.Controllers; |
39 | 41 | import org.jackhuang.hmcl.ui.FXUtils; |
40 | 42 | import org.jackhuang.hmcl.ui.account.AddAuthlibInjectorServerPane; |
|
55 | 57 | import java.util.Locale; |
56 | 58 | import java.util.Optional; |
57 | 59 | import java.util.Random; |
| 60 | +import java.util.concurrent.CompletableFuture; |
58 | 61 | import java.util.logging.Level; |
59 | 62 | import java.util.stream.Stream; |
60 | 63 |
|
@@ -89,10 +92,23 @@ public DecoratorController(Stage stage, Node mainPage) { |
89 | 92 | decorator.onBackNavButtonActionProperty().set(e -> back()); |
90 | 93 | decorator.onRefreshNavButtonActionProperty().set(e -> refresh()); |
91 | 94 |
|
92 | | - setupBackground(); |
93 | | - |
94 | 95 | setupAuthlibInjectorDnD(); |
95 | 96 |
|
| 97 | + // Setup background |
| 98 | + decorator.setContentBackground(getBackground()); |
| 99 | + changeBackgroundListener = o -> { |
| 100 | + final int currentCount = ++this.changeBackgroundCount; |
| 101 | + CompletableFuture.supplyAsync(this::getBackground, Schedulers.io()) |
| 102 | + .thenAcceptAsync(background -> { |
| 103 | + if (this.changeBackgroundCount == currentCount) |
| 104 | + decorator.setContentBackground(background); |
| 105 | + }, Schedulers.javafx()); |
| 106 | + }; |
| 107 | + WeakInvalidationListener weakListener = new WeakInvalidationListener(changeBackgroundListener); |
| 108 | + config().backgroundImageTypeProperty().addListener(weakListener); |
| 109 | + config().backgroundImageProperty().addListener(weakListener); |
| 110 | + config().backgroundImageUrlProperty().addListener(weakListener); |
| 111 | + |
96 | 112 | // pass key events to current dialog / current page |
97 | 113 | decorator.addEventFilter(KeyEvent.ANY, e -> { |
98 | 114 | if (!(e.getTarget() instanceof Node)) { |
@@ -134,37 +150,40 @@ public Decorator getDecorator() { |
134 | 150 |
|
135 | 151 | // ==== Background ==== |
136 | 152 |
|
137 | | - private void setupBackground() { |
138 | | - decorator.contentBackgroundProperty().bind( |
139 | | - Bindings.createObjectBinding( |
140 | | - () -> { |
141 | | - Image image = null; |
142 | | - if (config().getBackgroundImageType() == EnumBackgroundImage.CUSTOM && config().getBackgroundImage() != null) { |
143 | | - image = tryLoadImage(Paths.get(config().getBackgroundImage())) |
144 | | - .orElse(null); |
145 | | - } |
146 | | - if (config().getBackgroundImageType() == EnumBackgroundImage.NETWORK) { |
147 | | - if (!NetworkUtils.isURL(config().getBackgroundImageUrl())) { |
148 | | - image = loadDefaultBackgroundImage(); |
149 | | - } else { |
150 | | - image = new Image(config().getBackgroundImageUrl(), true); |
151 | | - } |
152 | | - } else if (config().getBackgroundImageType() == EnumBackgroundImage.CLASSIC) { |
153 | | - image = newImage("/assets/img/background-classic.jpg"); |
154 | | - } else if (config().getBackgroundImageType() == EnumBackgroundImage.TRANSLUCENT) { |
155 | | - return new Background(new BackgroundFill(new Color(1, 1, 1, 0.5), CornerRadii.EMPTY, Insets.EMPTY)); |
156 | | - } |
157 | | - if (image == null) { |
158 | | - image = loadDefaultBackgroundImage(); |
159 | | - } |
160 | | - return new Background(new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(800, 480, false, false, true, true))); |
161 | | - }, |
162 | | - config().backgroundImageTypeProperty(), |
163 | | - config().backgroundImageProperty(), |
164 | | - config().backgroundImageUrlProperty())); |
| 153 | + //FXThread |
| 154 | + private int changeBackgroundCount = 0; |
| 155 | + |
| 156 | + @SuppressWarnings("FieldCanBeLocal") // Strong reference |
| 157 | + private final InvalidationListener changeBackgroundListener; |
| 158 | + |
| 159 | + private Background getBackground() { |
| 160 | + EnumBackgroundImage imageType = config().getBackgroundImageType(); |
| 161 | + |
| 162 | + Image image = null; |
| 163 | + switch (imageType) { |
| 164 | + case CUSTOM: |
| 165 | + String backgroundImage = config().getBackgroundImage(); |
| 166 | + if (backgroundImage != null) |
| 167 | + image = tryLoadImage(Paths.get(backgroundImage)).orElse(null); |
| 168 | + break; |
| 169 | + case NETWORK: |
| 170 | + String backgroundImageUrl = config().getBackgroundImageUrl(); |
| 171 | + if (backgroundImageUrl != null && NetworkUtils.isURL(backgroundImageUrl)) |
| 172 | + image = tryLoadImage(backgroundImageUrl).orElse(null); |
| 173 | + break; |
| 174 | + case CLASSIC: |
| 175 | + image = newImage("/assets/img/background-classic.jpg"); |
| 176 | + break; |
| 177 | + case TRANSLUCENT: |
| 178 | + return new Background(new BackgroundFill(new Color(1, 1, 1, 0.5), CornerRadii.EMPTY, Insets.EMPTY)); |
| 179 | + } |
| 180 | + if (image == null) { |
| 181 | + image = loadDefaultBackgroundImage(); |
| 182 | + } |
| 183 | + return new Background(new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(800, 480, false, false, true, true))); |
165 | 184 | } |
166 | 185 |
|
167 | | - private Image defaultBackground; |
| 186 | + private volatile Image defaultBackground; |
168 | 187 |
|
169 | 188 | /** |
170 | 189 | * Load background image from bg/, background.png, background.jpg, background.gif |
@@ -196,7 +215,7 @@ private Optional<Image> randomImageIn(Path imageDir) { |
196 | 215 | List<Path> candidates; |
197 | 216 | try (Stream<Path> stream = Files.list(imageDir)) { |
198 | 217 | candidates = stream |
199 | | - .filter(Files::isReadable) |
| 218 | + .filter(Files::isReadable) |
200 | 219 | .filter(it -> { |
201 | 220 | String ext = getExtension(it).toLowerCase(Locale.ROOT); |
202 | 221 | return ext.equals("png") || ext.equals("jpg") || ext.equals("gif"); |
@@ -224,9 +243,13 @@ private Optional<Image> tryLoadImage(Path path) { |
224 | 243 | if (!Files.isReadable(path)) |
225 | 244 | return Optional.empty(); |
226 | 245 |
|
| 246 | + return tryLoadImage(path.toAbsolutePath().toUri().toString()); |
| 247 | + } |
| 248 | + |
| 249 | + private Optional<Image> tryLoadImage(String url) { |
227 | 250 | Image img; |
228 | 251 | try { |
229 | | - img = new Image(path.toAbsolutePath().toUri().toString()); |
| 252 | + img = new Image(url); |
230 | 253 | } catch (IllegalArgumentException e) { |
231 | 254 | LOG.log(WARNING, "Couldn't load background image", e); |
232 | 255 | return Optional.empty(); |
|
0 commit comments