diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profiles.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profiles.java index 5f868f7b1..4d10e2d8f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profiles.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profiles.java @@ -19,10 +19,8 @@ package org.jackhuang.hmcl.setting; import com.jfoenix.concurrency.JFXUtilities; import javafx.application.Platform; -import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.property.*; -import javafx.beans.value.ObservableValue; import javafx.collections.ObservableList; import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.event.EventBus; @@ -30,9 +28,11 @@ import org.jackhuang.hmcl.event.RefreshedVersionsEvent; import java.io.File; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Consumer; import java.util.stream.Collectors; -import static java.util.stream.Collectors.toList; import static javafx.collections.FXCollections.observableArrayList; import static org.jackhuang.hmcl.setting.ConfigHolder.config; import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; @@ -43,8 +43,6 @@ public final class Profiles { public static final String DEFAULT_PROFILE = "Default"; public static final String HOME_PROFILE = "Home"; - private static InvalidationListener listener = o -> loadVersion(); - private Profiles() { } @@ -65,16 +63,14 @@ public final class Profiles { private static ObjectProperty selectedProfile = new SimpleObjectProperty() { { profiles.addListener(onInvalidating(this::invalidated)); - - this.addListener(this::change); } @Override protected void invalidated() { - Profile profile = get(); + if (!initialized) + return; - if (get() != null) - get().removeListener(listener); + Profile profile = get(); if (profiles.isEmpty()) { if (profile != null) { @@ -88,18 +84,20 @@ public final class Profiles { } } - if (!initialized) - return; - config().setSelectedProfile(profile == null ? "" : profile.getName()); - loadVersion(); - } - - private void change(ObservableValue observableValue, Profile oldProfile, Profile newProfile) { - if (oldProfile != null) - oldProfile.selectedVersionProperty().removeListener(listener); - if (newProfile != null) - newProfile.selectedVersionProperty().addListener(listener); + if (profile != null) { + if (profile.getRepository().isLoaded()) + selectedVersion.bind(profile.selectedVersionProperty()); + else { + selectedVersion.unbind(); + selectedVersion.set(null); + // bind when repository was reloaded. + profile.getRepository().refreshVersionsAsync().start(); + } + } else { + selectedVersion.unbind(); + selectedVersion.set(null); + } } }; @@ -154,19 +152,23 @@ public final class Profiles { // Platform.runLater is necessary or profiles will be empty // since checkProfiles adds 2 base profile later. Platform.runLater(() -> { + initialized = true; + selectedProfile.set( profiles.stream() .filter(it -> it.getName().equals(config().getSelectedProfile())) .findFirst() .orElse(profiles.get(0))); - - initialized = true; }); EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> { JFXUtilities.runInFX(() -> { - if (selectedProfile.get() != null && selectedProfile.get().getRepository() == event.getSource()) - loadVersion(); + Profile profile = selectedProfile.get(); + if (profile != null && profile.getRepository() == event.getSource()) { + selectedVersion.bind(profile.selectedVersionProperty()); + for (Consumer listener : versionsListeners) + listener.accept(profile); + } }); }); } @@ -197,14 +199,17 @@ public final class Profiles { return selectedVersion.getReadOnlyProperty(); } + // Guaranteed that the repository is loaded. public static String getSelectedVersion() { return selectedVersion.get(); } - private static void loadVersion() { - Profile profile = selectedProfile.get(); - if (profile == null || !profile.getRepository().isLoaded()) return; - JFXUtilities.runInFX(() -> - selectedVersion.set(profile.getSelectedVersion())); + private static final List> versionsListeners = new LinkedList<>(); + + public static void registerVersionsListener(Consumer listener) { + Profile profile = getSelectedProfile(); + if (profile != null && profile.getRepository().isLoaded()) + listener.accept(profile); + versionsListeners.add(listener); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index cb2b219d0..097978396 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -90,13 +90,13 @@ public final class FXUtils { } public static void onChangeAndOperate(ObservableValue value, Consumer consumer) { - onChange(value, consumer); consumer.accept(value.getValue()); + onChange(value, consumer); } public static void onWeakChangeAndOperate(ObservableValue value, Consumer consumer) { - onWeakChange(value, consumer); consumer.accept(value.getValue()); + onWeakChange(value, consumer); } public static void runLaterIf(BooleanSupplier condition, Runnable runnable) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java index cc9449437..7feb78c20 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java @@ -33,8 +33,6 @@ import javafx.scene.control.Label; import javafx.scene.layout.StackPane; import javafx.scene.shape.Rectangle; import javafx.util.Duration; -import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.RefreshedVersionsEvent; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; @@ -81,7 +79,6 @@ public final class MainPage extends StackPane implements DecoratorPage { @FXML private Rectangle separator; - private Profile profile; { FXUtils.loadFXML(this, "/assets/fxml/main.fxml"); @@ -126,41 +123,30 @@ public final class MainPage extends StackPane implements DecoratorPage { } }); - EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> { - if (event.getSource() == profile.getRepository()) - loadVersions((HMCLGameRepository) event.getSource()); - }); - Profiles.selectedProfileProperty().addListener((a, b, newValue) -> profile = newValue); - - profile = Profiles.getSelectedProfile(); - if (profile != null) { - if (profile.getRepository().isLoaded()) - loadVersions(profile.getRepository()); - else - profile.getRepository().refreshVersionsAsync().start(); - } + Profiles.registerVersionsListener(this::loadVersions); } - private void loadVersions(HMCLGameRepository repository) { + private void loadVersions(Profile profile) { + HMCLGameRepository repository = profile.getRepository(); List children = repository.getVersions().parallelStream() .filter(version -> !version.isHidden()) .sorted((a, b) -> VersionNumber.COMPARATOR.compare(VersionNumber.asVersion(a.getId()), VersionNumber.asVersion(b.getId()))) .map(version -> { StackPane pane = new StackPane(); - GameItem item = new GameItem(repository.getProfile(), version.getId()); + GameItem item = new GameItem(profile, version.getId()); pane.getChildren().setAll(item); pane.getStyleClass().setAll("menu-container"); item.setMouseTransparent(true); RipplerContainer container = new RipplerContainer(pane); container.setOnMouseClicked(e -> { - repository.getProfile().setSelectedVersion(version.getId()); + profile.setSelectedVersion(version.getId()); popup.hide(); }); return container; }) .collect(Collectors.toList()); JFXUtilities.runInFX(() -> { - if (profile == repository.getProfile()) + if (profile == Profiles.getSelectedProfile()) menu.getContent().setAll(children); }); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentListCell.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentListCell.java index 3f6983e8e..2e952387d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentListCell.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentListCell.java @@ -24,6 +24,7 @@ import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; +import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; @@ -78,7 +79,6 @@ class ComponentListCell extends StackPane { content.getStyleClass().add("options-sublist"); BorderPane groupNode = new BorderPane(); - groupNode.getStyleClass().add("options-list-item-header"); Node expandIcon = SVG.expand(Theme.blackFillBinding(), 10, 10); JFXButton expandButton = new JFXButton(); @@ -86,6 +86,7 @@ class ComponentListCell extends StackPane { expandButton.getStyleClass().add("options-list-item-expand-button"); VBox labelVBox = new VBox(); + labelVBox.setAlignment(Pos.CENTER_LEFT); if (list instanceof ComponentSublist) { Node leftNode = ((ComponentSublist) list).getHeaderLeft(); @@ -94,13 +95,11 @@ class ComponentListCell extends StackPane { } else { Label label = new Label(); label.textProperty().bind(list.titleProperty()); - label.setMouseTransparent(true); labelVBox.getChildren().add(label); if (list.isHasSubtitle()) { Label subtitleLabel = new Label(); subtitleLabel.textProperty().bind(list.subtitleProperty()); - subtitleLabel.setMouseTransparent(true); subtitleLabel.getStyleClass().add("subtitle-label"); labelVBox.getChildren().add(subtitleLabel); } @@ -109,6 +108,7 @@ class ComponentListCell extends StackPane { groupNode.setLeft(labelVBox); HBox right = new HBox(); + right.setAlignment(Pos.CENTER_RIGHT); if (list instanceof ComponentSublist) { Node rightNode = ((ComponentSublist) list).getHeaderRight(); if (rightNode != null) @@ -116,18 +116,13 @@ class ComponentListCell extends StackPane { } right.getChildren().add(expandButton); groupNode.setRight(right); - labelVBox.setAlignment(Pos.CENTER_LEFT); - right.setAlignment(Pos.CENTER_RIGHT); VBox container = new VBox(); - container.setStyle("-fx-padding: 8 0 0 0;"); + container.setPadding(new Insets(8, 0, 0, 0)); FXUtils.setLimitHeight(container, 0); FXUtils.setOverflowHidden(container, true); container.getChildren().setAll(content); - - VBox holder = new VBox(); - holder.getChildren().setAll(groupNode, container); - holder.getStyleClass().add("options-list-item-container"); + groupNode.setBottom(container); expandButton.setOnMouseClicked(e -> { if (expandAnimation != null && expandAnimation.getStatus() == Animation.Status.RUNNING) { @@ -159,7 +154,7 @@ class ComponentListCell extends StackPane { expandedProperty().addListener((a, b, newValue) -> expandIcon.setRotate(newValue ? 180 : 0)); - getChildren().setAll(holder); + getChildren().setAll(groupNode); } else getChildren().setAll(content); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java index 29aec77f9..ad86356c0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java @@ -17,39 +17,28 @@ */ package org.jackhuang.hmcl.ui.versions; -import com.jfoenix.concurrency.JFXUtilities; -import javafx.beans.InvalidationListener; import javafx.scene.image.Image; -import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.RefreshedVersionsEvent; -import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.WeakListenerHolder; import org.jackhuang.hmcl.ui.construct.AdvancedListItem; -import java.io.File; -import java.util.Objects; - import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public class GameAdvancedListItem extends AdvancedListItem { public GameAdvancedListItem() { FXUtils.onChangeAndOperate(Profiles.selectedVersionProperty(), version -> { - FXUtils.runLaterIf(() -> !Objects.nonNull(Profiles.getSelectedProfile()), () -> { - imageProperty().set(Profiles.getSelectedProfile().getRepository().getVersionIconImage(version)); - - if (version != null) { - setTitle(version); - setSubtitle(null); - } else { - setTitle(i18n("version.empty")); - setSubtitle(i18n("version.empty.add")); - } - }); + if (version != null) { + setTitle(version); + setSubtitle(null); + setImage(Profiles.getSelectedProfile().getRepository().getVersionIconImage(version)); + } else { + setTitle(i18n("version.empty")); + setSubtitle(i18n("version.empty.add")); + setImage(new Image("/assets/img/grass.png")); + } }); setRightGraphic(SVG.gear(Theme.blackFillBinding(), -1, -1)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java index f28f66cf0..94799aed5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java @@ -47,28 +47,19 @@ public class GameList extends Control implements DecoratorPage { private final BooleanProperty loading = new SimpleBooleanProperty(true); private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()); - private Profile profile; private ToggleGroup toggleGroup; public GameList() { - EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> { - if (event.getSource() == profile.getRepository()) - loadVersions((HMCLGameRepository) event.getSource()); - }); EventBus.EVENT_BUS.channel(RefreshingVersionsEvent.class).register(event -> { - if (event.getSource() == profile.getRepository()) + if (event.getSource() == Profiles.getSelectedProfile().getRepository()) JFXUtilities.runInFX(() -> loading.set(true)); }); - Profiles.selectedProfileProperty().addListener((a, b, newValue) -> profile = newValue); - profile = Profiles.getSelectedProfile(); - if (profile.getRepository().isLoaded()) - loadVersions(profile.getRepository()); - else - profile.getRepository().refreshVersionsAsync().start(); + Profiles.registerVersionsListener(this::loadVersions); } - private void loadVersions(HMCLGameRepository repository) { + private void loadVersions(Profile profile) { + HMCLGameRepository repository = profile.getRepository(); toggleGroup = new ToggleGroup(); WeakListenerHolder listenerHolder = new WeakListenerHolder(); toggleGroup.getProperties().put("ReferenceHolder", listenerHolder); @@ -78,7 +69,7 @@ public class GameList extends Control implements DecoratorPage { .map(version -> new GameListItem(toggleGroup, profile, version.getId())) .collect(Collectors.toList()); JFXUtilities.runInFX(() -> { - if (profile == repository.getProfile()) { + if (profile == Profiles.getSelectedProfile()) { loading.set(false); items.setAll(children); children.forEach(GameListItem::checkSelection); @@ -115,11 +106,11 @@ public class GameList extends Control implements DecoratorPage { } public void refresh() { - profile.getRepository().refreshVersionsAsync().start(); + Profiles.getSelectedProfile().getRepository().refreshVersionsAsync().start(); } public void modifyGlobalGameSettings() { - Versions.modifyGlobalSettings(profile); + Versions.modifyGlobalSettings(Profiles.getSelectedProfile()); } @Override