|
56 | 56 | import org.jackhuang.hmcl.ui.construct.SpinnerPane; |
57 | 57 | import org.jackhuang.hmcl.ui.construct.TwoLineListItem; |
58 | 58 | import org.jackhuang.hmcl.ui.decorator.DecoratorPage; |
59 | | -import org.jackhuang.hmcl.util.AggregatedObservableList; |
60 | | -import org.jackhuang.hmcl.util.Holder; |
61 | | -import org.jackhuang.hmcl.util.Lang; |
62 | | -import org.jackhuang.hmcl.util.StringUtils; |
| 59 | +import org.jackhuang.hmcl.util.*; |
63 | 60 | import org.jackhuang.hmcl.util.i18n.I18n; |
64 | | -import org.jackhuang.hmcl.util.io.NetworkUtils; |
65 | 61 | import org.jackhuang.hmcl.util.javafx.BindingMapping; |
66 | 62 | import org.jackhuang.hmcl.util.versioning.GameVersionNumber; |
67 | 63 | import org.jetbrains.annotations.NotNull; |
68 | 64 |
|
69 | | -import java.lang.ref.WeakReference; |
70 | 65 | import java.net.URI; |
71 | 66 | import java.util.*; |
72 | | -import java.util.concurrent.CancellationException; |
73 | | -import java.util.concurrent.CompletableFuture; |
74 | | -import java.util.concurrent.CompletionException; |
75 | 67 | import java.util.stream.Collectors; |
76 | 68 |
|
77 | 69 | import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent; |
78 | 70 | import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; |
79 | 71 | import static org.jackhuang.hmcl.util.i18n.I18n.i18n; |
80 | 72 | import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor; |
81 | | -import static org.jackhuang.hmcl.util.logging.Logger.LOG; |
82 | 73 |
|
83 | 74 | public class DownloadListPage extends Control implements DecoratorPage, VersionPage.VersionLoadable { |
84 | 75 | protected final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(); |
@@ -247,6 +238,12 @@ protected Skin<?> createDefaultSkin() { |
247 | 238 |
|
248 | 239 | private static class ModDownloadListPageSkin extends SkinBase<DownloadListPage> { |
249 | 240 | private final JFXListView<RemoteMod> listView = new JFXListView<>(); |
| 241 | + private final RemoteImageLoader iconLoader = new RemoteImageLoader() { |
| 242 | + @Override |
| 243 | + protected @NotNull Task<Image> createLoadTask(@NotNull URI uri) { |
| 244 | + return FXUtils.getRemoteImageTask(uri, 80, 80, true, true); |
| 245 | + } |
| 246 | + }; |
250 | 247 |
|
251 | 248 | protected ModDownloadListPageSkin(DownloadListPage control) { |
252 | 249 | super(control); |
@@ -377,6 +374,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) { |
377 | 374 | IntegerProperty filterID = new SimpleIntegerProperty(this, "Filter ID", 0); |
378 | 375 | IntegerProperty currentFilterID = new SimpleIntegerProperty(this, "Current Filter ID", -1); |
379 | 376 | EventHandler<ActionEvent> searchAction = e -> { |
| 377 | + iconLoader.clearInvalidCache(); |
380 | 378 | if (currentFilterID.get() != -1 && currentFilterID.get() != filterID.get()) { |
381 | 379 | control.pageOffset.set(0); |
382 | 380 | } |
@@ -536,7 +534,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) { |
536 | 534 |
|
537 | 535 | // ListViewBehavior would consume ESC pressed event, preventing us from handling it, so we ignore it here |
538 | 536 | ignoreEvent(listView, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE); |
539 | | - var iconCache = new WeakHashMap<String, WeakReference<CompletableFuture<Image>>>(); |
| 537 | + |
540 | 538 | listView.setCellFactory(x -> new FloatListCell<>(listView) { |
541 | 539 | private final TwoLineListItem content = new TwoLineListItem(); |
542 | 540 | private final ImageView imageView = new ImageView(); |
@@ -564,66 +562,9 @@ protected void updateControl(RemoteMod dataItem, boolean empty) { |
564 | 562 | if (getSkinnable().shouldDisplayCategory(category)) |
565 | 563 | content.addTag(getSkinnable().getLocalizedCategory(category)); |
566 | 564 | } |
567 | | - loadIcon(dataItem); |
| 565 | + iconLoader.load(imageView.imageProperty(), dataItem.getIconUrl()); |
568 | 566 | } |
569 | 567 |
|
570 | | - private void loadIcon(RemoteMod mod) { |
571 | | - if (StringUtils.isBlank(mod.getIconUrl())) { |
572 | | - imageView.setImage(null); |
573 | | - return; |
574 | | - } |
575 | | - |
576 | | - WeakReference<CompletableFuture<Image>> cacheRef = iconCache.get(mod.getIconUrl()); |
577 | | - CompletableFuture<Image> cache; |
578 | | - if (cacheRef != null && (cache = cacheRef.get()) != null) { |
579 | | - loadIcon(cache, mod.getIconUrl()); |
580 | | - return; |
581 | | - } |
582 | | - |
583 | | - URI iconUrl = NetworkUtils.toURIOrNull(mod.getIconUrl()); |
584 | | - if (iconUrl == null) { |
585 | | - imageView.setImage(null); |
586 | | - return; |
587 | | - } |
588 | | - |
589 | | - CompletableFuture<Image> future = new CompletableFuture<>(); |
590 | | - WeakReference<CompletableFuture<Image>> futureRef = new WeakReference<>(future); |
591 | | - iconCache.put(mod.getIconUrl(), futureRef); |
592 | | - |
593 | | - FXUtils.getRemoteImageTask(iconUrl, 80, 80, true, true) |
594 | | - .whenComplete(Schedulers.defaultScheduler(), (result, exception) -> { |
595 | | - if (exception == null) { |
596 | | - future.complete(result); |
597 | | - } else { |
598 | | - LOG.warning("Failed to load image from " + iconUrl, exception); |
599 | | - future.completeExceptionally(exception); |
600 | | - } |
601 | | - }).start(); |
602 | | - loadIcon(future, mod.getIconUrl()); |
603 | | - } |
604 | | - |
605 | | - private void loadIcon(@NotNull CompletableFuture<Image> future, |
606 | | - @NotNull String iconUrl) { |
607 | | - Image image; |
608 | | - try { |
609 | | - image = future.getNow(null); |
610 | | - } catch (CancellationException | CompletionException ignored) { |
611 | | - imageView.setImage(null); |
612 | | - return; |
613 | | - } |
614 | | - |
615 | | - if (image != null) { |
616 | | - imageView.setImage(image); |
617 | | - } else { |
618 | | - imageView.setImage(null); |
619 | | - future.thenAcceptAsync(result -> { |
620 | | - RemoteMod item = getItem(); |
621 | | - if (item != null && iconUrl.equals(item.getIconUrl())) { |
622 | | - this.imageView.setImage(result); |
623 | | - } |
624 | | - }, Schedulers.javafx()); |
625 | | - } |
626 | | - } |
627 | 568 | }); |
628 | 569 | } |
629 | 570 |
|
|
0 commit comments