From 1566e069ed4880241c4d8c59f001f34fa2aa329f Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:09:05 +0800 Subject: [PATCH] Add selectedMinecraftVersion --- .../org/jackhuang/hmcl/setting/Profile.java | 94 +++++++++++++------ .../jackhuang/hmcl/ui/WeakListenerHelper.java | 58 ++++++++++++ .../hmcl/ui/construct/FontComboBox.java | 1 + .../jackhuang/hmcl/event/EventManager.java | 45 ++++++--- 4 files changed, 153 insertions(+), 45 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/WeakListenerHelper.java 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 6fa7c0651..a134c5e28 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java @@ -19,14 +19,18 @@ package org.jackhuang.hmcl.setting; import com.google.gson.*; import javafx.beans.InvalidationListener; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.*; +import org.jackhuang.hmcl.event.EventBus; +import org.jackhuang.hmcl.event.RefreshedVersionsEvent; import org.jackhuang.hmcl.game.HMCLDependencyManager; 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 java.io.File; @@ -38,66 +42,80 @@ import java.util.Optional; * @author huangyuhui */ public final class Profile { - + private final WeakListenerHelper helper = new WeakListenerHelper(); private final HMCLGameRepository repository; private final ModManager modManager; - private final ImmediateObjectProperty gameDirProperty; + private final StringProperty selectedVersion = new SimpleStringProperty(); - public ImmediateObjectProperty gameDirProperty() { - return gameDirProperty; + public StringProperty selectedVersionProperty() { + return selectedVersion; + } + + public String getSelectedVersion() { + return selectedVersion.get(); + } + + public void setSelectedVersion(String selectedVersion) { + this.selectedVersion.set(selectedVersion); + } + + private final ObjectProperty gameDir; + + public ObjectProperty gameDirProperty() { + return gameDir; } public File getGameDir() { - return gameDirProperty.get(); + return gameDir.get(); } public void setGameDir(File gameDir) { - gameDirProperty.set(gameDir); + this.gameDir.set(gameDir); } - private final ImmediateObjectProperty globalProperty = new ImmediateObjectProperty<>(this, "global", new VersionSetting()); + private final ObjectProperty global = new ImmediateObjectProperty<>(this, "global", new VersionSetting()); - public ImmediateObjectProperty globalProperty() { - return globalProperty; + public ObjectProperty globalProperty() { + return global; } public VersionSetting getGlobal() { - return globalProperty.get(); + return global.get(); } private void setGlobal(VersionSetting global) { if (global == null) global = new VersionSetting(); - globalProperty.set(global); + this.global.set(global); } - private final ImmediateStringProperty nameProperty; + private final ImmediateStringProperty name; public ImmediateStringProperty nameProperty() { - return nameProperty; + return name; } public String getName() { - return nameProperty.get(); + return name.get(); } public void setName(String name) { - nameProperty.set(name); + this.name.set(name); } - private BooleanProperty useRelativePathProperty = new SimpleBooleanProperty(this, "useRelativePath", false); + private BooleanProperty useRelativePath = new SimpleBooleanProperty(this, "useRelativePath", false); public BooleanProperty useRelativePathProperty() { - return useRelativePathProperty(); + return useRelativePath; } public boolean isUseRelativePath() { - return useRelativePathProperty.get(); + return useRelativePath.get(); } public void setUseRelativePath(boolean useRelativePath) { - useRelativePathProperty.set(useRelativePath); + this.useRelativePath.set(useRelativePath); } public Profile(String name) { @@ -105,12 +123,26 @@ public final class Profile { } public Profile(String name, File initialGameDir) { - nameProperty = new ImmediateStringProperty(this, "name", name); - gameDirProperty = new ImmediateObjectProperty<>(this, "gameDir", initialGameDir); + this.name = new ImmediateStringProperty(this, "name", name); + gameDir = new ImmediateObjectProperty<>(this, "gameDir", initialGameDir); repository = new HMCLGameRepository(this, initialGameDir); modManager = new ModManager(repository); - gameDirProperty.addListener((a, b, newValue) -> repository.changeDirectory(newValue)); + gameDir.addListener((a, b, newValue) -> repository.changeDirectory(newValue)); + selectedVersion.addListener(o -> checkSelectedVersion()); + helper.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion())); + } + + private void checkSelectedVersion() { + if (!repository.isLoaded()) return; + String newValue = selectedVersion.get(); + if (!repository.hasVersion(newValue)) { + Optional version = repository.getVersions().stream().findFirst().map(Version::getId); + if (version.isPresent()) + selectedVersion.setValue(version.get()); + else if (StringUtils.isNotBlank(newValue)) + selectedVersion.setValue(null); + } } public HMCLGameRepository getRepository() { @@ -171,11 +203,12 @@ public final class Profile { } public void addPropertyChangedListener(InvalidationListener listener) { - nameProperty.addListener(listener); - globalProperty.addListener(listener); - gameDirProperty.addListener(listener); - useRelativePathProperty.addListener(listener); - globalProperty.get().addPropertyChangedListener(listener); + name.addListener(listener); + global.addListener(listener); + gameDir.addListener(listener); + useRelativePath.addListener(listener); + global.get().addPropertyChangedListener(listener); + selectedVersion.addListener(listener); } public static final class Serializer implements JsonSerializer, JsonDeserializer { @@ -193,6 +226,7 @@ public final class Profile { jsonObject.add("global", context.serialize(src.getGlobal())); jsonObject.addProperty("gameDir", src.getGameDir().getPath()); jsonObject.addProperty("useRelativePath", src.isUseRelativePath()); + jsonObject.addProperty("selectedMinecraftVersion", src.getSelectedVersion()); return jsonObject; } @@ -205,7 +239,7 @@ public final class Profile { Profile profile = new Profile("Default", new File(gameDir)); profile.setGlobal(context.deserialize(obj.get("global"), VersionSetting.class)); - + profile.setSelectedVersion(Optional.ofNullable(obj.get("selectedMinecraftVersion")).map(JsonElement::getAsString).orElse("")); profile.setUseRelativePath(Optional.ofNullable(obj.get("useRelativePath")).map(JsonElement::getAsBoolean).orElse(false)); return profile; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/WeakListenerHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/WeakListenerHelper.java new file mode 100644 index 000000000..3d8f0dde8 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/WeakListenerHelper.java @@ -0,0 +1,58 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2017 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.ui; + +import javafx.beans.InvalidationListener; +import javafx.beans.WeakInvalidationListener; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.WeakChangeListener; +import javafx.collections.ListChangeListener; +import javafx.collections.WeakListChangeListener; + +import java.util.LinkedList; +import java.util.List; + +public class WeakListenerHelper { + List refs = new LinkedList<>(); + + public WeakListenerHelper() { + } + + public WeakInvalidationListener weak(InvalidationListener listener) { + refs.add(listener); + return new WeakInvalidationListener(listener); + } + + public WeakChangeListener weak(ChangeListener listener) { + refs.add(listener); + return new WeakChangeListener<>(listener); + } + + public WeakListChangeListener weak(ListChangeListener listener) { + refs.add(listener); + return new WeakListChangeListener<>(listener); + } + + public void add(Object obj) { + refs.add(obj); + } + + public boolean remove(Object obj) { + return refs.remove(obj); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FontComboBox.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FontComboBox.java index aab8ce69c..51d3fc115 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FontComboBox.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FontComboBox.java @@ -47,6 +47,7 @@ public class FontComboBox extends JFXComboBox { setOnMouseClicked(e -> { if (loaded) return; getItems().setAll(Font.getFamilies()); + loaded = true; }); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventManager.java index c66764c93..2bb4830b5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventManager.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.event; import org.jackhuang.hmcl.util.SimpleMultimap; +import java.lang.ref.WeakReference; import java.util.EnumMap; import java.util.HashSet; import java.util.function.Consumer; @@ -31,8 +32,16 @@ public final class EventManager { private final SimpleMultimap> handlers = new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), HashSet::new); - private final SimpleMultimap handlers2 - = new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), HashSet::new); + + public Consumer registerWeak(Consumer consumer) { + register(new WeakListener(consumer)); + return consumer; + } + + public Consumer registerWeak(Consumer consumer, EventPriority priority) { + register(new WeakListener(consumer), priority); + return consumer; + } public void register(Consumer consumer) { register(consumer, EventPriority.NORMAL); @@ -44,28 +53,17 @@ public final class EventManager { } public void register(Runnable runnable) { - register(runnable, EventPriority.NORMAL); + register(t -> runnable.run()); } public void register(Runnable runnable, EventPriority priority) { - if (!handlers2.get(priority).contains(runnable)) - handlers2.put(priority, runnable); - } - - public void unregister(Consumer consumer) { - handlers.removeValue(consumer); - } - - public void unregister(Runnable runnable) { - handlers2.removeValue(runnable); + register(t -> runnable.run(), priority); } public Event.Result fireEvent(T event) { for (EventPriority priority : EventPriority.values()) { for (Consumer handler : handlers.get(priority)) handler.accept(event); - for (Runnable runnable : handlers2.get(priority)) - runnable.run(); } if (event.hasResult()) @@ -74,4 +72,21 @@ public final class EventManager { return Event.Result.DEFAULT; } + private class WeakListener implements Consumer { + private final WeakReference> ref; + + public WeakListener(Consumer listener) { + this.ref = new WeakReference<>(listener); + } + + @Override + public void accept(T t) { + Consumer listener = ref.get(); + if (listener == null) { + handlers.removeValue(this); + } else { + listener.accept(t); + } + } + } }