diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/event/ProfileChangedEvent.java b/HMCL/src/main/java/org/jackhuang/hmcl/event/ProfileChangedEvent.java deleted file mode 100644 index 2155d94cc..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/event/ProfileChangedEvent.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Hello Minecraft! Launcher. - * Copyright (C) 2018 huangyuhui - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see {http://www.gnu.org/licenses/}. - */ -package org.jackhuang.hmcl.event; - -import org.jackhuang.hmcl.setting.Profile; -import org.jackhuang.hmcl.util.ToStringBuilder; - -/** - * This event gets fired when the selected profile changed. - *
- * This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS} - * @author huangyuhui - */ -public final class ProfileChangedEvent extends Event { - private final Profile profile; - - /** - * Constructor. - * - * @param source {@link org.jackhuang.hmcl.setting.Settings} - * @param profile the new profile. - */ - public ProfileChangedEvent(Object source, Profile profile) { - super(source); - - this.profile = profile; - } - - public Profile getProfile() { - return profile; - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .append("source", source) - .append("profile", profile) - .toString(); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/event/ProfileLoadingEvent.java b/HMCL/src/main/java/org/jackhuang/hmcl/event/ProfileLoadingEvent.java deleted file mode 100644 index 81ff05379..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/event/ProfileLoadingEvent.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Hello Minecraft! Launcher. - * Copyright (C) 2018 huangyuhui - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see {http://www.gnu.org/licenses/}. - */ -package org.jackhuang.hmcl.event; - -import org.jackhuang.hmcl.setting.Profile; -import org.jackhuang.hmcl.util.ToStringBuilder; - -import java.util.Collection; -import java.util.Collections; - -/** - * This event gets fired when loading profiles. - *
- * This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS} - * - * @author huangyuhui - */ -public class ProfileLoadingEvent extends Event { - - private final Collection profiles; - - /** - * Constructor. - * - * @param source {@link org.jackhuang.hmcl.setting.Settings} - */ - public ProfileLoadingEvent(Object source, Collection profiles) { - super(source); - - this.profiles = Collections.unmodifiableCollection(profiles); - } - - public Collection getProfiles() { - return profiles; - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .append("source", source) - .append("profiles", profiles) - .toString(); - } -} - diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java index 134b8cd0c..422e236ed 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java @@ -92,7 +92,7 @@ public final class Accounts { } private static ObservableList accounts = observableArrayList(account -> new Observable[] { account }); - private static ReadOnlyListProperty accountsWrapper = new ReadOnlyListWrapper<>(accounts); + private static ReadOnlyListWrapper accountsWrapper = new ReadOnlyListWrapper<>(accounts); private static ObjectProperty selectedAccount = new SimpleObjectProperty() { { @@ -194,7 +194,7 @@ public final class Accounts { } public static ReadOnlyListProperty accountsProperty() { - return accountsWrapper; + return accountsWrapper.getReadOnlyProperty(); } public static Account getSelectedAccount() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java index a134c5e28..1230a9f25 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java @@ -18,7 +18,9 @@ package org.jackhuang.hmcl.setting; import com.google.gson.*; +import com.jfoenix.concurrency.JFXUtilities; import javafx.beans.InvalidationListener; +import javafx.beans.Observable; import javafx.beans.property.*; import org.jackhuang.hmcl.event.EventBus; @@ -28,20 +30,19 @@ import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.mod.ModManager; import org.jackhuang.hmcl.ui.WeakListenerHelper; -import org.jackhuang.hmcl.util.ImmediateObjectProperty; -import org.jackhuang.hmcl.util.ImmediateStringProperty; -import org.jackhuang.hmcl.util.StringUtils; -import org.jackhuang.hmcl.util.ToStringBuilder; +import org.jackhuang.hmcl.util.*; import java.io.File; import java.lang.reflect.Type; import java.util.Optional; +import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; + /** * * @author huangyuhui */ -public final class Profile { +public final class Profile implements Observable { private final WeakListenerHelper helper = new WeakListenerHelper(); private final HMCLGameRepository repository; private final ModManager modManager; @@ -131,6 +132,8 @@ public final class Profile { gameDir.addListener((a, b, newValue) -> repository.changeDirectory(newValue)); selectedVersion.addListener(o -> checkSelectedVersion()); helper.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion())); + + addPropertyChangedListener(onInvalidating(this::invalidate)); } private void checkSelectedVersion() { @@ -202,7 +205,7 @@ public final class Profile { .toString(); } - public void addPropertyChangedListener(InvalidationListener listener) { + private void addPropertyChangedListener(InvalidationListener listener) { name.addListener(listener); global.addListener(listener); gameDir.addListener(listener); @@ -211,6 +214,22 @@ public final class Profile { selectedVersion.addListener(listener); } + private ObservableHelper observableHelper = new ObservableHelper(this); + + @Override + public void addListener(InvalidationListener listener) { + observableHelper.addListener(listener); + } + + @Override + public void removeListener(InvalidationListener listener) { + observableHelper.removeListener(listener); + } + + protected void invalidate() { + JFXUtilities.runInFX(observableHelper::invalidate); + } + public static final class Serializer implements JsonSerializer, JsonDeserializer { public static final Serializer INSTANCE = new Serializer(); 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 5b770d2c9..7bfe7496f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profiles.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profiles.java @@ -17,21 +17,16 @@ */ package org.jackhuang.hmcl.setting; -import javafx.beans.value.ObservableValue; +import javafx.beans.Observable; +import javafx.beans.property.*; +import javafx.collections.ObservableList; import org.jackhuang.hmcl.Launcher; -import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.ProfileChangedEvent; -import org.jackhuang.hmcl.event.ProfileLoadingEvent; -import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.util.StringUtils; -import java.util.Collection; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; +import java.util.HashSet; +import static javafx.collections.FXCollections.observableArrayList; import static org.jackhuang.hmcl.setting.ConfigHolder.config; +import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class Profiles { @@ -53,104 +48,103 @@ public final class Profiles { } } - /**************************************** - * PROFILES * - ****************************************/ + private static final ObservableList profiles = observableArrayList(profile -> new Observable[] { profile }); + private static final ReadOnlyListWrapper profilesWrapper = new ReadOnlyListWrapper<>(profiles); - public static Profile getSelectedProfile() { - checkProfileMap(); - - if (!hasProfile(config().getSelectedProfile())) { - getProfileMap().keySet().stream().findFirst().ifPresent(selectedProfile -> { - config().setSelectedProfile(selectedProfile); - }); - Schedulers.computation().schedule(Profiles::onProfileChanged); + private static ObjectProperty selectedProfile = new SimpleObjectProperty() { + { + profiles.addListener(onInvalidating(this::invalidated)); } - return getProfile(config().getSelectedProfile()); - } - public static void setSelectedProfile(Profile selectedProfile) { - if (hasProfile(selectedProfile.getName()) && !Objects.equals(selectedProfile.getName(), config().getSelectedProfile())) { - config().setSelectedProfile(selectedProfile.getName()); - Schedulers.computation().schedule(Profiles::onProfileChanged); + @Override + protected void invalidated() { + Profile profile = get(); + if (profiles.isEmpty()) { + if (profile != null) { + set(null); + return; + } + } else { + if (!profiles.contains(profile)) { + set(profiles.get(0)); + return; + } + } + + if (!initialized) + return; + config().setSelectedProfile(profile == null ? "" : profile.getName()); } - } + }; - public static Profile getProfile(String name) { - checkProfileMap(); - - Optional p = name == null ? getProfileMap().values().stream().findFirst() : Optional.ofNullable(getProfileMap().get(name)); - return p.orElse(null); - } - - public static boolean hasProfile(String name) { - return getProfileMap().containsKey(name); - } - - public static Map getProfileMap() { - return config().getConfigurations(); - } - - public static Collection getProfiles() { - return getProfileMap().values().stream().filter(t -> StringUtils.isNotBlank(t.getName())).collect(Collectors.toList()); - } - - public static void putProfile(Profile ver) { - if (StringUtils.isBlank(ver.getName())) - throw new IllegalArgumentException("Profile's name is empty"); - - getProfileMap().put(ver.getName(), ver); - Schedulers.computation().schedule(Profiles::onProfileLoading); - - ver.nameProperty().setChangedListener(Profiles::profileNameChanged); - } - - public static void deleteProfile(Profile profile) { - deleteProfile(profile.getName()); - } - - public static void deleteProfile(String profileName) { - getProfileMap().remove(profileName); - checkProfileMap(); - Schedulers.computation().schedule(Profiles::onProfileLoading); - } - - private static void checkProfileMap() { - if (getProfileMap().isEmpty()) { + private static void checkProfiles() { + if (profiles.isEmpty()) { Profile current = new Profile(Profiles.DEFAULT_PROFILE); current.setUseRelativePath(true); - getProfileMap().put(Profiles.DEFAULT_PROFILE, current); Profile home = new Profile(Profiles.HOME_PROFILE, Launcher.MINECRAFT_DIRECTORY); - getProfileMap().put(Profiles.HOME_PROFILE, home); + + profiles.addAll(current, home); } } - private static void onProfileChanged() { - EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(new Object(), getSelectedProfile())); - getSelectedProfile().getRepository().refreshVersionsAsync().start(); - } - - private static void profileNameChanged(ObservableValue observableValue, String oldValue, String newValue) { - getProfileMap().put(newValue, getProfileMap().remove(oldValue)); - } - /** - * Start profiles loading process. - * Invoked by loading GUI phase. + * True if {@link #init()} hasn't been called. */ - public static void onProfileLoading() { - EventBus.EVENT_BUS.fireEvent(new ProfileLoadingEvent(new Object(), getProfiles())); - onProfileChanged(); + private static boolean initialized = false; + + static { + profiles.addListener(onInvalidating(ConfigHolder::markConfigDirty)); + + profiles.addListener(onInvalidating(Profiles::checkProfiles)); + + selectedProfile.addListener((a, b, newValue) -> { + if (newValue != null) + newValue.getRepository().refreshVersionsAsync().start(); + }); } + /** + * Called when it's ready to load profiles from {@link ConfigHolder#config()}. + */ static void init() { - checkProfileMap(); + if (initialized) + throw new IllegalStateException("Already initialized"); - for (Map.Entry profileEntry : getProfileMap().entrySet()) { - profileEntry.getValue().setName(profileEntry.getKey()); - profileEntry.getValue().nameProperty().setChangedListener(Profiles::profileNameChanged); - profileEntry.getValue().addPropertyChangedListener(e -> ConfigHolder.markConfigDirty()); - } + HashSet names = new HashSet<>(); + config().getConfigurations().forEach((name, profile) -> { + if (!names.add(name)) return; + profile.setName(name); + profiles.add(profile); + }); + checkProfiles(); + + initialized = true; + + selectedProfile.set( + profiles.stream() + .filter(it -> it.getName().equals(config().getSelectedProfile())) + .findFirst() + .get()); + } + + public static ObservableList getProfiles() { + return profiles; + } + + public static ReadOnlyListProperty profilesProperty() { + return profilesWrapper.getReadOnlyProperty(); + } + + public static Profile getSelectedProfile() { + return selectedProfile.get(); + } + + public static void setSelectedProfile(Profile profile) { + selectedProfile.set(profile); + } + + public static ObjectProperty selectedProfileProperty() { + return selectedProfile; } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java index 58747a4c6..1b94cf304 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java @@ -90,7 +90,7 @@ public class AdvancedListItemSkin extends SkinBase { JFXButton settings = new JFXButton(); FXUtils.setLimitWidth(settings, 40); settings.getStyleClass().setAll("toggle-icon4"); - settings.setGraphic(SVG.gear(Theme.blackFillBinding(), -1, -1)); + settings.setGraphic(SVG.dotsVertical(Theme.blackFillBinding(), -1, -1)); right.getChildren().setAll(settings); root.setRight(right); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index 62ef628bb..31533d60a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -122,7 +122,6 @@ public final class Controllers { decorator.showPage(null); leftPaneController = new LeftPaneController(decorator.getLeftPane()); - Profiles.onProfileLoading(); Task.of(JavaVersion::initialize).start(); decorator.setCustomMaximize(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java index e45e413b2..e8228c024 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -26,8 +26,6 @@ import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.ProfileChangedEvent; -import org.jackhuang.hmcl.event.ProfileLoadingEvent; import org.jackhuang.hmcl.event.RefreshedVersionsEvent; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ModpackHelper; @@ -49,6 +47,7 @@ import java.util.LinkedList; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; +import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class LeftPaneController { @@ -94,9 +93,11 @@ public final class LeftPaneController { }))) .add(profilePane); - EventBus.EVENT_BUS.channel(ProfileLoadingEvent.class).register(this::onProfilesLoading); - EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged); - EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(this::onRefreshedVersions); + FXUtils.onChangeAndOperate(Profiles.profilesProperty(), a -> onProfilesLoading()); + FXUtils.onChangeAndOperate(Profiles.selectedProfileProperty(), this::onProfileChanged); + EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> onRefreshedVersions((HMCLGameRepository) event.getSource())); + if (Profiles.selectedProfileProperty().get().getRepository().isLoaded()) + onRefreshedVersions(Profiles.selectedProfileProperty().get().getRepository()); } // ==== Accounts ==== @@ -110,9 +111,7 @@ public final class LeftPaneController { } // ==== - private void onProfileChanged(ProfileChangedEvent event) { - Profile profile = event.getProfile(); - + private void onProfileChanged(Profile profile) { Platform.runLater(() -> { for (Node node : profilePane.getChildren()) { if (node instanceof RipplerContainer && node.getProperties().get("profile") instanceof String) { @@ -141,9 +140,8 @@ public final class LeftPaneController { private boolean checkedModpack = false; private static boolean showNewAccount = true; - private void onRefreshedVersions(RefreshedVersionsEvent event) { + private void onRefreshedVersions(HMCLGameRepository repository) { JFXUtilities.runInFX(() -> { - HMCLGameRepository repository = (HMCLGameRepository) event.getSource(); if (!checkedModpack) { checkedModpack = true; 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 7e384fa8b..22e0ac691 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java @@ -17,174 +17,30 @@ */ package org.jackhuang.hmcl.ui; -import com.jfoenix.concurrency.JFXUtilities; -import com.jfoenix.controls.*; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.scene.control.ScrollPane; -import javafx.scene.image.Image; -import javafx.scene.input.MouseButton; import javafx.scene.layout.StackPane; - -import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.ProfileChangedEvent; -import org.jackhuang.hmcl.event.RefreshedVersionsEvent; -import org.jackhuang.hmcl.event.RefreshingVersionsEvent; -import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; -import org.jackhuang.hmcl.setting.Settings; -import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; import org.jackhuang.hmcl.ui.versions.Versions; import org.jackhuang.hmcl.ui.wizard.DecoratorPage; -import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.VersionNumber; - -import java.io.File; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.stream.Collectors; - -import static org.jackhuang.hmcl.util.StringUtils.removePrefix; -import static org.jackhuang.hmcl.util.StringUtils.removeSuffix; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class MainPage extends StackPane implements DecoratorPage { private final StringProperty title = new SimpleStringProperty(this, "title", i18n("main_page")); - private Profile profile; - - @FXML - private JFXButton btnRefresh; - @FXML - private StackPane contentPane; - @FXML - private JFXButton btnAdd; - @FXML - private JFXSpinner spinner; - @FXML - private JFXMasonryPane masonryPane; - @FXML - private ScrollPane scrollPane; { FXUtils.loadFXML(this, "/assets/fxml/main.fxml"); - loadingVersions(); - - 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()) - // This will occupy 0.5s. Too slow! - JFXUtilities.runInFX(this::loadingVersions); - }); - EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(event -> { - this.profile = event.getProfile(); - }); - - btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(0), i18n("install"))); - FXUtils.installTooltip(btnAdd, i18n("install")); - btnRefresh.setOnMouseClicked(e -> Profiles.getSelectedProfile().getRepository().refreshVersionsAsync().start()); - FXUtils.installTooltip(btnRefresh, i18n("button.refresh")); } - private static String modifyVersion(String gameVersion, String version) { - return removeSuffix(removePrefix(removeSuffix(removePrefix(version.replace(gameVersion, "").trim(), "-"), "-"), "_"), "_"); - } - - private Node buildNode(HMCLGameRepository repository, Version version, Callable gameCallable) { - Profile profile = repository.getProfile(); - String id = version.getId(); - VersionItem item = new VersionItem(); - item.setUpdate(repository.isModpack(id)); - Task.ofResult("game", gameCallable).subscribe(Schedulers.javafx(), vars -> { - String game = vars.get("game"); - item.setGameVersion(game); - - StringBuilder libraries = new StringBuilder(); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version); - analyzer.getForge().ifPresent(library -> libraries.append(i18n("install.installer.forge")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)forge", ""))).append("\n")); - analyzer.getLiteLoader().ifPresent(library -> libraries.append(i18n("install.installer.liteloader")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)liteloader", ""))).append("\n")); - analyzer.getOptiFine().ifPresent(library -> libraries.append(i18n("install.installer.optifine")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)optifine", ""))).append("\n")); - - item.setLibraries(libraries.toString()); - }); - item.setVersionName(id); - item.setOnLaunchButtonClicked(e -> Versions.launch(profile, id)); - item.setOnScriptButtonClicked(e -> Versions.generateLaunchScript(profile, id)); - item.setOnSettingsButtonClicked(e -> { - Controllers.getDecorator().showPage(Controllers.getVersionPage()); - Controllers.getVersionPage().load(id, profile); - }); - item.setOnUpdateButtonClicked(event -> Versions.updateVersion(profile, id)); - item.setOnMouseClicked(event -> { - if (event.getButton() == MouseButton.SECONDARY) { - JFXListView versionList = new JFXListView<>(); - JFXPopup versionPopup = new JFXPopup(versionList); - versionList.getStyleClass().add("option-list-view"); - FXUtils.setLimitWidth(versionList, 150); - versionList.getItems().setAll(Lang.immutableListOf( - i18n("version.manage.rename"), - i18n("version.manage.remove"), - i18n("modpack.export"), - i18n("folder.game") - )); - versionList.setOnMouseClicked(e -> { - versionPopup.hide(); - switch (versionList.getSelectionModel().getSelectedIndex()) { - case 0: - Versions.renameVersion(profile, id); - break; - case 1: - Versions.deleteVersion(profile, id); - break; - case 2: - Versions.exportVersion(profile, id); - break; - case 3: - Versions.openFolder(profile, id); - break; - default: - break; - } - }); - versionPopup.show(item, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY()); - } else if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) { - Versions.launch(profile, id); - } - }); - File iconFile = repository.getVersionIcon(id); - if (iconFile.exists()) - item.setImage(new Image("file:" + iconFile.getAbsolutePath())); - return item; - } - - private void loadingVersions() { - getChildren().setAll(spinner); - masonryPane.getChildren().clear(); - } - - private void loadVersions(HMCLGameRepository repository) { - 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 -> buildNode(repository, version, () -> GameVersion.minecraftVersion(repository.getVersionJar(version.getId())).orElse("Unknown"))) - .collect(Collectors.toList()); - JFXUtilities.runInFX(() -> { - if (profile == repository.getProfile()) { - masonryPane.getChildren().setAll(children); - getChildren().setAll(contentPane); - } - }); + @FXML + private void launch() { + Profile profile = Profiles.getSelectedProfile(); + Versions.launch(profile, profile.getSelectedVersion()); } public String getTitle() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ProfilePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/ProfilePage.java index 39a42c104..344a6bd24 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ProfilePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/ProfilePage.java @@ -80,7 +80,7 @@ public final class ProfilePage extends StackPane implements DecoratorPage { @FXML private void onDelete() { if (profile != null) { - Profiles.deleteProfile(profile); + Profiles.getProfiles().remove(profile); Controllers.navigate(null); } } @@ -99,10 +99,9 @@ public final class ProfilePage extends StackPane implements DecoratorPage { } Profile newProfile = new Profile(txtProfileName.getText(), new File(getLocation())); newProfile.setUseRelativePath(toggleUseRelativePath.isSelected()); - Profiles.putProfile(newProfile); + Profiles.getProfiles().add(newProfile); } - Profiles.onProfileLoading(); Controllers.navigate(null); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java index beab5bd96..47a938fd9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java @@ -111,16 +111,6 @@ public final class VersionPage extends StackPane implements DecoratorPage { managementPopup.show(btnManagementMenu, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, -12, 15); } - @FXML - private void onDelete() { - Versions.deleteVersion(profile, version); - } - - @FXML - private void onExport() { - Versions.exportVersion(profile, version); - } - @FXML private void onBrowse() { String sub; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java index 62e3ae16e..d48a671f2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java @@ -250,7 +250,7 @@ public final class VersionSettingsController { if (iconFile.exists()) iconPickerItem.setImage(new Image("file:" + iconFile.getAbsolutePath())); else - iconPickerItem.setImage(Constants.DEFAULT_ICON.get()); + iconPickerItem.setImage(new Image("/assets/img/grass.png")); FXUtils.limitSize(iconPickerItem.getImageView(), 32, 32); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListSkin.java index 8173b067e..d4a2fd206 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListSkin.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.ui.account; import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXScrollPane; import javafx.beans.binding.Bindings; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -48,6 +49,7 @@ public class AccountListSkin extends SkinBase { Bindings.bindContent(accountList.getChildren(), skinnable.itemsProperty()); scrollPane.setContent(accountList); + JFXScrollPane.smoothScrolling(scrollPane); } VBox vBox = new VBox(); 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 e813c937c..34a359e2d 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 @@ -19,17 +19,11 @@ package org.jackhuang.hmcl.ui.versions; import com.jfoenix.concurrency.JFXUtilities; import javafx.beans.InvalidationListener; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; import javafx.scene.image.Image; import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.ProfileChangedEvent; import org.jackhuang.hmcl.event.RefreshedVersionsEvent; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; -import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.ui.AdvancedListItem2; import org.jackhuang.hmcl.ui.WeakListenerHelper; @@ -42,8 +36,8 @@ public class GameAdvancedListItem extends AdvancedListItem2 { private InvalidationListener listener = o -> loadVersion(); public GameAdvancedListItem() { - helper.add(EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).registerWeak(event -> { - JFXUtilities.runInFX(() -> loadProfile(event.getProfile())); + Profiles.selectedProfileProperty().addListener(helper.weak((a, b, newValue) -> { + JFXUtilities.runInFX(() -> loadProfile(newValue)); })); helper.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> { JFXUtilities.runInFX(() -> { @@ -67,7 +61,7 @@ public class GameAdvancedListItem extends AdvancedListItem2 { if (profile == null || !profile.getRepository().isLoaded()) return; String version = profile.getSelectedVersion(); File iconFile = profile.getRepository().getVersionIcon(version); - + JFXUtilities.runInFX(() -> { if (iconFile.exists()) imageProperty().set(new Image("file:" + iconFile.getAbsolutePath())); 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 3e87173c3..f048e493a 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 @@ -24,13 +24,11 @@ import javafx.scene.control.Control; import javafx.scene.control.Skin; import javafx.scene.control.ToggleGroup; import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.ProfileChangedEvent; import org.jackhuang.hmcl.event.RefreshedVersionsEvent; import org.jackhuang.hmcl.event.RefreshingVersionsEvent; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; -import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; import org.jackhuang.hmcl.ui.wizard.DecoratorPage; @@ -59,9 +57,7 @@ public class GameList extends Control implements DecoratorPage { if (event.getSource() == profile.getRepository()) JFXUtilities.runInFX(() -> loading.set(true)); }); - EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(event -> { - this.profile = event.getProfile(); - }); + Profiles.selectedProfileProperty().addListener((a, b, newValue) -> profile = newValue); profile = Profiles.getSelectedProfile(); if (profile.getRepository().isLoaded()) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java index f5b76e594..b7cf3bb13 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.ui.versions; import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXScrollPane; import com.jfoenix.controls.JFXSpinner; import com.jfoenix.effects.JFXDepthManager; import javafx.beans.binding.Bindings; @@ -106,6 +107,7 @@ public class GameListSkin extends SkinBase { Bindings.bindContent(gameList.getChildren(), skinnable.itemsProperty()); scrollPane.setContent(gameList); + JFXScrollPane.smoothScrolling(scrollPane); FXUtils.onChangeAndOperate(skinnable.loadingProperty(), loading -> center.getChildren().setAll(loading ? spinner : scrollPane)); diff --git a/HMCL/src/main/resources/assets/fxml/main.fxml b/HMCL/src/main/resources/assets/fxml/main.fxml index 11835a8fd..3345f3fbc 100644 --- a/HMCL/src/main/resources/assets/fxml/main.fxml +++ b/HMCL/src/main/resources/assets/fxml/main.fxml @@ -1,32 +1,12 @@ - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/HMCL/src/main/resources/assets/fxml/version/version.fxml b/HMCL/src/main/resources/assets/fxml/version/version.fxml index cbb876f00..e7923dae9 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version.fxml @@ -23,18 +23,6 @@ - - - - - - - - - - diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/Account.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/Account.java index 585f45e80..f045c1251 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/Account.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/Account.java @@ -54,7 +54,7 @@ public abstract class Account implements Observable { * * @throws CredentialExpiredException when the stored credentials has expired, in which case a password login will be performed */ - public abstract AuthInfo logIn() throws CredentialExpiredException, AuthenticationException; + public abstract AuthInfo logIn() throws AuthenticationException; /** * Login with specified password.