From 1566e069ed4880241c4d8c59f001f34fa2aa329f Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:09:05 +0800 Subject: [PATCH 01/28] 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); + } + } + } } From ec2b9b19be51b6eb62f9664b1ea826f05ba454ae Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:09:54 +0800 Subject: [PATCH 02/28] Add an entrance to game list page --- .../jackhuang/hmcl/ui/AdvancedListItem2.java | 92 ++++++++++++++++++ .../hmcl/ui/AdvancedListItemViewModel.java | 33 +++++++ .../ui/GameAdvancedListItemViewModel.java | 97 +++++++++++++++++++ .../jackhuang/hmcl/ui/LeftPaneController.java | 6 +- 4 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java new file mode 100644 index 000000000..c09b96ff6 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java @@ -0,0 +1,92 @@ +/* + * 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 com.jfoenix.controls.JFXButton; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import javafx.scene.text.TextAlignment; +import org.jackhuang.hmcl.setting.Theme; + +public class AdvancedListItem2 extends StackPane { + + public AdvancedListItem2(AdvancedListItemViewModel viewModel) { + BorderPane root = new BorderPane(); + root.setPickOnBounds(false); + + HBox left = new HBox(); + left.setAlignment(Pos.CENTER); + left.setMouseTransparent(true); + + StackPane imageViewContainer = new StackPane(); + FXUtils.setLimitWidth(imageViewContainer, 32); + FXUtils.setLimitHeight(imageViewContainer, 32); + + ImageView imageView = new ImageView(); + FXUtils.limitSize(imageView, 32, 32); + imageView.setPreserveRatio(true); + imageView.imageProperty().bind(viewModel.imageProperty()); + imageViewContainer.getChildren().setAll(imageView); + + BorderPane borderPane = new BorderPane(); + borderPane.setPadding(new Insets(0, 0, 0, 10)); + + Label title = new Label(); + title.textProperty().bind(viewModel.titleProperty()); + title.setMaxWidth(90); + title.setStyle("-fx-font-size: 15;"); + title.setTextAlignment(TextAlignment.JUSTIFY); + borderPane.setTop(title); + + if (viewModel.subtitleProperty() != null) { + Label subtitle = new Label(); + subtitle.textProperty().bind(viewModel.subtitleProperty()); + subtitle.setMaxWidth(90); + subtitle.setStyle("-fx-font-size: 10;"); + subtitle.setTextAlignment(TextAlignment.JUSTIFY); + borderPane.setBottom(subtitle); + } else { + title.setWrapText(true); + } + + left.getChildren().setAll(imageViewContainer, borderPane); + root.setLeft(left); + + HBox right = new HBox(); + right.setAlignment(Pos.CENTER); + right.setPickOnBounds(false); + + JFXButton settings = new JFXButton(); + FXUtils.setLimitWidth(settings, 40); + settings.setOnMouseClicked(e -> viewModel.action()); + settings.getStyleClass().setAll("toggle-icon4"); + settings.setGraphic(SVG.gear(Theme.blackFillBinding(), -1, -1)); + right.getChildren().setAll(settings); + root.setRight(right); + + setStyle("-fx-padding: 10 16 10 16;"); + getStyleClass().setAll("transparent"); + setPickOnBounds(false); + getChildren().setAll(root); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java new file mode 100644 index 000000000..d902c6ae9 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java @@ -0,0 +1,33 @@ +/* + * 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.property.ObjectProperty; +import javafx.beans.property.StringProperty; +import javafx.scene.image.Image; + +public abstract class AdvancedListItemViewModel { + + public abstract void action(); + + public abstract ObjectProperty imageProperty(); + + public abstract StringProperty titleProperty(); + + public abstract StringProperty subtitleProperty(); +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java new file mode 100644 index 000000000..a65d2e50d --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java @@ -0,0 +1,97 @@ +/* + * 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 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.beans.value.ChangeListener; +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.event.RefreshingVersionsEvent; +import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.Settings; + +import java.io.File; + +public class GameAdvancedListItemViewModel extends AdvancedListItemViewModel { + private final ObjectProperty image = new SimpleObjectProperty<>(); + private final StringProperty title = new SimpleStringProperty(); + private final WeakListenerHelper helper = new WeakListenerHelper(); + + private Profile profile; + private InvalidationListener listener = o -> loadVersion(); + + public GameAdvancedListItemViewModel() { + helper.add(EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).registerWeak(event -> { + JFXUtilities.runInFX(() -> loadProfile(event.getProfile())); + })); + helper.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> { + JFXUtilities.runInFX(() -> { + if (profile != null && profile.getRepository() == event.getSource()) + loadVersion(); + }); + })); + loadProfile(Settings.instance().getSelectedProfile()); + } + + private void loadProfile(Profile newProfile) { + if (profile != null) + profile.selectedVersionProperty().removeListener(listener); + profile = newProfile; + profile.selectedVersionProperty().addListener(listener); + loadVersion(); + } + + private void loadVersion() { + Profile profile = this.profile; + if (profile == null || !profile.getRepository().isLoaded()) return; + String version = profile.getSelectedVersion(); + File iconFile = profile.getRepository().getVersionIcon(version); + if (iconFile.exists()) + image.set(new Image("file:" + iconFile.getAbsolutePath())); + else + image.set(new Image("/assets/img/grass.png")); + + title.set(version); + } + + @Override + public void action() { + } + + @Override + public ObjectProperty imageProperty() { + return image; + } + + @Override + public StringProperty titleProperty() { + return title; + } + + @Override + public StringProperty subtitleProperty() { + return null; + } +} 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 cf7cbbadd..5db94cebd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -69,6 +69,7 @@ public final class LeftPaneController { private final VBox profilePane = new VBox(); private final VBox accountPane = new VBox(); private final IconedItem launcherSettingsItem; + private final AdvancedListItem2 gameListItem; private ListProperty accountItems = new SimpleListProperty<>(); private ObjectProperty selectedAccount = new SimpleObjectProperty() { @@ -101,9 +102,11 @@ public final class LeftPaneController { .then(Color.RED) .otherwise(Color.BLACK)); - launcherSettingsItem.prefWidthProperty().bind(leftPane.widthProperty()); + launcherSettingsItem.maxWidthProperty().bind(leftPane.widthProperty()); launcherSettingsItem.setOnMouseClicked(e -> Controllers.navigate(Controllers.getSettingsPage())); + gameListItem = new AdvancedListItem2(new GameAdvancedListItemViewModel()); + leftPane .add(new ClassTitle(i18n("account").toUpperCase(), Lang.apply(new JFXButton(), button -> { button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10)); @@ -112,6 +115,7 @@ public final class LeftPaneController { }))) .add(accountPane) .startCategory(i18n("launcher").toUpperCase()) + .add(gameListItem) .add(launcherSettingsItem) .add(new ClassTitle(i18n("profile.title").toUpperCase(), Lang.apply(new JFXButton(), button -> { button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10)); From ec295664d97c5d2627379ff262d385981cb7fc88 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:11:00 +0800 Subject: [PATCH 03/28] Move operations on version to new class --- .../java/org/jackhuang/hmcl/ui/MainPage.java | 76 ++-------- .../jackhuang/hmcl/ui/versions/Versions.java | 133 ++++++++++++++++++ 2 files changed, 143 insertions(+), 66 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java 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 dea720a8f..43249550f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java @@ -26,9 +26,7 @@ import javafx.scene.Node; import javafx.scene.control.ScrollPane; import javafx.scene.image.Image; import javafx.scene.input.MouseButton; -import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; -import javafx.stage.FileChooser; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.event.EventBus; @@ -36,27 +34,19 @@ 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.mod.MismatchedModpackTypeException; -import org.jackhuang.hmcl.mod.UnsupportedModpackException; -import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.task.TaskExecutor; -import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; -import org.jackhuang.hmcl.ui.construct.MessageBox; 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.OperatingSystem; import org.jackhuang.hmcl.util.VersionNumber; import java.io.File; -import java.io.IOException; import java.util.List; import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import static org.jackhuang.hmcl.util.StringUtils.removePrefix; @@ -106,7 +96,7 @@ public final class MainPage extends StackPane implements DecoratorPage { FXUtils.installTooltip(btnRefresh, i18n("button.refresh")); } - private String modifyVersion(String gameVersion, String version) { + private static String modifyVersion(String gameVersion, String version) { return removeSuffix(removePrefix(removeSuffix(removePrefix(version.replace(gameVersion, "").trim(), "-"), "-"), "_"), "_"); } @@ -128,56 +118,13 @@ public final class MainPage extends StackPane implements DecoratorPage { item.setLibraries(libraries.toString()); }); item.setVersionName(id); - item.setOnLaunchButtonClicked(e -> { - if (Accounts.getSelectedAccount() == null) - Controllers.getLeftPaneController().checkAccount(); - else - LauncherHelper.INSTANCE.launch(profile, Accounts.getSelectedAccount(), id, null); - }); - item.setOnScriptButtonClicked(e -> { - if (Accounts.getSelectedAccount() == null) - Controllers.dialog(i18n("login.empty_username")); - else { - FileChooser chooser = new FileChooser(); - if (repository.getRunDirectory(id).isDirectory()) - chooser.setInitialDirectory(repository.getRunDirectory(id)); - chooser.setTitle(i18n("version.launch_script.save")); - chooser.getExtensionFilters().add(OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS - ? new FileChooser.ExtensionFilter(i18n("extension.bat"), "*.bat") - : new FileChooser.ExtensionFilter(i18n("extension.sh"), "*.sh")); - File file = chooser.showSaveDialog(Controllers.getStage()); - if (file != null) - LauncherHelper.INSTANCE.launch(profile, Accounts.getSelectedAccount(), id, file); - } - }); + 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 -> { - FileChooser chooser = new FileChooser(); - chooser.setTitle(i18n("modpack.choose")); - chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); - File selectedFile = chooser.showOpenDialog(Controllers.getStage()); - if (selectedFile != null) { - AtomicReference region = new AtomicReference<>(); - try { - TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, id, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(id))) - .then(Task.of(Schedulers.javafx(), () -> region.get().fireEvent(new DialogCloseEvent()))).executor(); - region.set(Controllers.taskDialog(executor, i18n("modpack.update"), "")); - executor.start(); - } catch (UnsupportedModpackException e) { - region.get().fireEvent(new DialogCloseEvent()); - Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageBox.ERROR_MESSAGE); - } catch (MismatchedModpackTypeException e) { - region.get().fireEvent(new DialogCloseEvent()); - Controllers.dialog(i18n("modpack.mismatched_type"), i18n("message.error"), MessageBox.ERROR_MESSAGE); - } catch (IOException e) { - region.get().fireEvent(new DialogCloseEvent()); - Controllers.dialog(i18n("modpack.invalid"), i18n("message.error"), MessageBox.ERROR_MESSAGE); - } - } - }); + item.setOnUpdateButtonClicked(event -> Versions.updateVersion(profile, id)); item.setOnMouseClicked(event -> { if (event.getButton() == MouseButton.SECONDARY) { JFXListView versionList = new JFXListView<>(); @@ -194,16 +141,16 @@ public final class MainPage extends StackPane implements DecoratorPage { versionPopup.hide(); switch (versionList.getSelectionModel().getSelectedIndex()) { case 0: - VersionPage.renameVersion(profile, id); + Versions.renameVersion(profile, id); break; case 1: - VersionPage.deleteVersion(profile, id); + Versions.deleteVersion(profile, id); break; case 2: - VersionPage.exportVersion(profile, id); + Versions.exportVersion(profile, id); break; case 3: - FXUtils.openFolder(repository.getRunDirectory(id)); + Versions.openFolder(profile, id); break; default: break; @@ -211,10 +158,7 @@ public final class MainPage extends StackPane implements DecoratorPage { }); versionPopup.show(item, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY()); } else if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) { - if (Accounts.getSelectedAccount() == null) - Controllers.dialog(i18n("login.empty_username")); - else - LauncherHelper.INSTANCE.launch(profile, Accounts.getSelectedAccount(), id, null); + Versions.launch(profile, id); } }); File iconFile = repository.getVersionIcon(id); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java new file mode 100644 index 000000000..391ab00a0 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -0,0 +1,133 @@ +/* + * 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.versions; + +import javafx.scene.layout.Region; +import javafx.stage.FileChooser; +import org.jackhuang.hmcl.game.GameRepository; +import org.jackhuang.hmcl.game.LauncherHelper; +import org.jackhuang.hmcl.game.ModpackHelper; +import org.jackhuang.hmcl.mod.MismatchedModpackTypeException; +import org.jackhuang.hmcl.mod.UnsupportedModpackException; +import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.setting.EnumGameDirectory; +import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.task.TaskExecutor; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; +import org.jackhuang.hmcl.ui.construct.MessageBox; +import org.jackhuang.hmcl.ui.export.ExportWizardProvider; +import org.jackhuang.hmcl.util.FileUtils; +import org.jackhuang.hmcl.util.OperatingSystem; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class Versions { + + public static void deleteVersion(Profile profile, String version) { + boolean isIndependent = profile.getVersionSetting(version).getGameDirType() == EnumGameDirectory.VERSION_FOLDER; + boolean isMovingToTrashSupported = FileUtils.isMovingToTrashSupported(); + String message = isIndependent ? i18n("version.manage.remove.confirm.independent", version) : + isMovingToTrashSupported ? i18n("version.manage.remove.confirm.trash", version, version + "_removed") : + i18n("version.manage.remove.confirm", version); + Controllers.confirmDialog(message, i18n("message.confirm"), () -> { + if (profile.getRepository().removeVersionFromDisk(version)) { + profile.getRepository().refreshVersionsAsync().start(); + Controllers.navigate(null); + } + }, null); + } + + public static void renameVersion(Profile profile, String version) { + Controllers.inputDialog(i18n("version.manage.rename.message"), (res, resolve, reject) -> { + if (profile.getRepository().renameVersion(version, res)) { + profile.getRepository().refreshVersionsAsync().start(); + Controllers.navigate(null); + resolve.run(); + } else { + reject.accept(i18n("version.manage.rename.fail")); + } + }).setInitialText(version); + } + + public static void exportVersion(Profile profile, String version) { + Controllers.getDecorator().startWizard(new ExportWizardProvider(profile, version), i18n("modpack.wizard")); + } + + public static void openFolder(Profile profile, String version) { + FXUtils.openFolder(profile.getRepository().getRunDirectory(version)); + } + + public static void updateVersion(Profile profile, String version) { + FileChooser chooser = new FileChooser(); + chooser.setTitle(i18n("modpack.choose")); + chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); + File selectedFile = chooser.showOpenDialog(Controllers.getStage()); + if (selectedFile != null) { + AtomicReference region = new AtomicReference<>(); + try { + TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, version, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(version))) + .then(Task.of(Schedulers.javafx(), () -> region.get().fireEvent(new DialogCloseEvent()))).executor(); + region.set(Controllers.taskDialog(executor, i18n("modpack.update"), "")); + executor.start(); + } catch (UnsupportedModpackException e) { + region.get().fireEvent(new DialogCloseEvent()); + Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + } catch (MismatchedModpackTypeException e) { + region.get().fireEvent(new DialogCloseEvent()); + Controllers.dialog(i18n("modpack.mismatched_type"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + } catch (IOException e) { + region.get().fireEvent(new DialogCloseEvent()); + Controllers.dialog(i18n("modpack.invalid"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + } + } + } + + public static void generateLaunchScript(Profile profile, String id) { + GameRepository repository = profile.getRepository(); + + if (Accounts.getSelectedAccount() == null) + Controllers.dialog(i18n("login.empty_username")); + else { + FileChooser chooser = new FileChooser(); + if (repository.getRunDirectory(id).isDirectory()) + chooser.setInitialDirectory(repository.getRunDirectory(id)); + chooser.setTitle(i18n("version.launch_script.save")); + chooser.getExtensionFilters().add(OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS + ? new FileChooser.ExtensionFilter(i18n("extension.bat"), "*.bat") + : new FileChooser.ExtensionFilter(i18n("extension.sh"), "*.sh")); + File file = chooser.showSaveDialog(Controllers.getStage()); + if (file != null) + LauncherHelper.INSTANCE.launch(profile, Accounts.getSelectedAccount(), id, file); + } + } + + public static void launch(Profile profile, String id) { + if (Accounts.getSelectedAccount() == null) + Controllers.getLeftPaneController().checkAccount(); + else + LauncherHelper.INSTANCE.launch(profile, Accounts.getSelectedAccount(), id, null); + } +} From f9127272b19f4ae6d1ba2a3262df64839062dcd1 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:11:49 +0800 Subject: [PATCH 04/28] Split download wizard into { new game, install modpack } --- .../java/org/jackhuang/hmcl/ui/MainPage.java | 2 +- .../ui/download/DownloadWizardProvider.java | 14 +++-- .../hmcl/ui/download/InstallTypePage.java | 57 ------------------- .../assets/fxml/download/dltype.fxml | 33 ----------- 4 files changed, 9 insertions(+), 97 deletions(-) delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallTypePage.java delete mode 100644 HMCL/src/main/resources/assets/fxml/download/dltype.fxml 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 43249550f..e9c052c3f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java @@ -90,7 +90,7 @@ public final class MainPage extends StackPane implements DecoratorPage { this.profile = event.getProfile(); }); - btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), i18n("install"))); + btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(0), i18n("install"))); FXUtils.installTooltip(btnAdd, i18n("install")); btnRefresh.setOnMouseClicked(e -> Settings.instance().getSelectedProfile().getRepository().refreshVersionsAsync().start()); FXUtils.installTooltip(btnRefresh, i18n("button.refresh")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java index 2c6721799..f5635bdc0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java @@ -38,6 +38,11 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class DownloadWizardProvider implements WizardProvider { private Profile profile; + private final int type; + + public DownloadWizardProvider(int type) { + this.type = type; + } @Override public void start(Map settings) { @@ -80,7 +85,7 @@ public final class DownloadWizardProvider implements WizardProvider { settings.put("success_message", i18n("install.success")); settings.put("failure_message", i18n("install.failed")); - switch (Lang.parseInt(settings.get(InstallTypePage.INSTALL_TYPE), -1)) { + switch (type) { case 0: return finishVersionDownloadingAsync(settings); case 1: return finishModpackInstallingAsync(settings); default: return null; @@ -92,16 +97,13 @@ public final class DownloadWizardProvider implements WizardProvider { DownloadProvider provider = profile.getDependency().getDownloadProvider(); switch (step) { case 0: - return new InstallTypePage(controller); - case 1: - int subStep = Lang.parseInt(settings.get(InstallTypePage.INSTALL_TYPE), -1); - switch (subStep) { + switch (type) { case 0: return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.game")), "", provider, "game", () -> controller.onNext(new InstallersPage(controller, profile.getRepository(), provider))); case 1: return new ModpackPage(controller); default: - throw new IllegalStateException("Error step " + step + ", subStep " + subStep + ", settings: " + settings + ", pages: " + controller.getPages()); + throw new IllegalStateException("Error step " + step + ", subStep " + type + ", settings: " + settings + ", pages: " + controller.getPages()); } default: throw new IllegalStateException("error step " + step + ", settings: " + settings + ", pages: " + controller.getPages()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallTypePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallTypePage.java deleted file mode 100644 index 00a119b5f..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallTypePage.java +++ /dev/null @@ -1,57 +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.ui.download; - -import com.jfoenix.controls.JFXListView; -import javafx.fxml.FXML; -import javafx.scene.layout.StackPane; - -import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.ui.wizard.WizardController; -import org.jackhuang.hmcl.ui.wizard.WizardPage; -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; - -import java.util.Map; - -public final class InstallTypePage extends StackPane implements WizardPage { - - @FXML private JFXListView list; - - public InstallTypePage(WizardController controller) { - - FXUtils.loadFXML(this, "/assets/fxml/download/dltype.fxml"); - list.setOnMouseClicked(e -> { - if (list.getSelectionModel().getSelectedIndex() < 0) - return; - controller.getSettings().put(INSTALL_TYPE, list.getSelectionModel().getSelectedIndex()); - controller.onNext(); - }); - } - - @Override - public void cleanup(Map settings) { - settings.remove(INSTALL_TYPE); - } - - @Override - public String getTitle() { - return i18n("install.select"); - } - - public static final String INSTALL_TYPE = "INSTALL_TYPE"; -} diff --git a/HMCL/src/main/resources/assets/fxml/download/dltype.fxml b/HMCL/src/main/resources/assets/fxml/download/dltype.fxml deleted file mode 100644 index c558bc756..000000000 --- a/HMCL/src/main/resources/assets/fxml/download/dltype.fxml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 40c7c729739528b7b907c1ccfa1904f66aa89841 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:11:58 +0800 Subject: [PATCH 05/28] Move operations on version to new class 2 --- .../org/jackhuang/hmcl/ui/VersionPage.java | 39 +++---------------- 1 file changed, 5 insertions(+), 34 deletions(-) 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 ee14e1281..28745ca8c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java @@ -30,6 +30,7 @@ import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask; import org.jackhuang.hmcl.setting.EnumGameDirectory; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.ui.export.ExportWizardProvider; +import org.jackhuang.hmcl.ui.versions.Versions; import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.util.FileUtils; @@ -114,12 +115,12 @@ public final class VersionPage extends StackPane implements DecoratorPage { @FXML private void onDelete() { - deleteVersion(profile, version); + Versions.deleteVersion(profile, version); } @FXML private void onExport() { - exportVersion(profile, version); + Versions.exportVersion(profile, version); } @FXML @@ -157,10 +158,10 @@ public final class VersionPage extends StackPane implements DecoratorPage { private void onManagement() { switch (managementList.getSelectionModel().getSelectedIndex()) { case 0: // rename a version - renameVersion(profile, version); + Versions.renameVersion(profile, version); break; case 1: // remove a version - deleteVersion(profile, version); + Versions.deleteVersion(profile, version); break; case 2: // redownload asset index new GameAssetIndexDownloadTask(profile.getDependency(), profile.getRepository().getResolvedVersion(version)).start(); @@ -185,34 +186,4 @@ public final class VersionPage extends StackPane implements DecoratorPage { public void setTitle(String title) { this.title.set(title); } - - public static void deleteVersion(Profile profile, String version) { - boolean isIndependent = profile.getVersionSetting(version).getGameDirType() == EnumGameDirectory.VERSION_FOLDER; - boolean isMovingToTrashSupported = FileUtils.isMovingToTrashSupported(); - String message = isIndependent ? i18n("version.manage.remove.confirm.independent", version) : - isMovingToTrashSupported ? i18n("version.manage.remove.confirm.trash", version, version + "_removed") : - i18n("version.manage.remove.confirm", version); - Controllers.confirmDialog(message, i18n("message.confirm"), () -> { - if (profile.getRepository().removeVersionFromDisk(version)) { - profile.getRepository().refreshVersionsAsync().start(); - Controllers.navigate(null); - } - }, null); - } - - public static void renameVersion(Profile profile, String version) { - Controllers.inputDialog(i18n("version.manage.rename.message"), (res, resolve, reject) -> { - if (profile.getRepository().renameVersion(version, res)) { - profile.getRepository().refreshVersionsAsync().start(); - Controllers.navigate(null); - resolve.run(); - } else { - reject.accept(i18n("version.manage.rename.fail")); - } - }).setInitialText(version); - } - - public static void exportVersion(Profile profile, String version) { - Controllers.getDecorator().startWizard(new ExportWizardProvider(profile, version), i18n("modpack.wizard")); - } } From 60454c6af392c0aab967f38e01b2b526a8dca5b1 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:12:39 +0800 Subject: [PATCH 06/28] New game list page --- .../org/jackhuang/hmcl/ui/Controllers.java | 10 ++ .../ui/GameAdvancedListItemViewModel.java | 1 + .../main/java/org/jackhuang/hmcl/ui/SVG.java | 6 + .../hmcl/ui/versions/GameListItemView.java | 127 ++++++++++++++++ .../ui/versions/GameListItemViewModel.java | 141 ++++++++++++++++++ .../hmcl/ui/versions/GameListView.java | 125 ++++++++++++++++ .../hmcl/ui/versions/GameListViewModel.java | 113 ++++++++++++++ HMCL/src/main/resources/assets/css/root.css | 32 +++- .../resources/assets/lang/I18N.properties | 8 +- .../resources/assets/lang/I18N_zh.properties | 8 +- .../assets/lang/I18N_zh_CN.properties | 10 +- 11 files changed, 570 insertions(+), 11 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemView.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemViewModel.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListView.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListViewModel.java 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 272921f0f..c0804cb9d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -31,6 +31,8 @@ import org.jackhuang.hmcl.ui.construct.InputDialogPane; import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; +import org.jackhuang.hmcl.ui.versions.GameListView; +import org.jackhuang.hmcl.ui.versions.GameListViewModel; import org.jackhuang.hmcl.util.FutureCallback; import org.jackhuang.hmcl.util.JavaVersion; @@ -45,6 +47,7 @@ public final class Controllers { private static MainPage mainPage = null; private static SettingsPage settingsPage = null; private static VersionPage versionPage = null; + private static GameListView gameListView = null; private static AuthlibInjectorServersPage serversPage = null; private static LeftPaneController leftPaneController; private static Decorator decorator; @@ -64,6 +67,13 @@ public final class Controllers { return settingsPage; } + // FXThread + public static GameListView getGameListView() { + if (gameListView == null) + gameListView = new GameListView(new GameListViewModel()); + return gameListView; + } + // FXThread public static VersionPage getVersionPage() { if (versionPage == null) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java index a65d2e50d..a484888fe 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java @@ -78,6 +78,7 @@ public class GameAdvancedListItemViewModel extends AdvancedListItemViewModel { @Override public void action() { + Controllers.navigate(Controllers.getGameListView()); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index 65f15b6f3..96928efb6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -33,6 +33,9 @@ public final class SVG { path.setContent(d); path.fillProperty().bind(fill); + if (width < 0 || height < 0) + return path; + Group svg = new Group(path); double scale = Math.min(width / svg.getBoundsInParent().getWidth(), height / svg.getBoundsInParent().getHeight()); svg.setScaleX(scale); @@ -127,4 +130,7 @@ public final class SVG { return createSVGPath("M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z", fill, width, height); } + public static Node importIcon(ObjectBinding fill, double width, double height) { + return createSVGPath("M14,12L10,8V11H2V13H10V16M20,18V6C20,4.89 19.1,4 18,4H6A2,2 0 0,0 4,6V9H6V6H18V18H6V15H4V18A2,2 0 0,0 6,20H18A2,2 0 0,0 20,18Z", fill, width, height); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemView.java new file mode 100644 index 000000000..42d6e26c2 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemView.java @@ -0,0 +1,127 @@ +/* + * 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.versions; + +import com.jfoenix.concurrency.JFXUtilities; +import com.jfoenix.controls.*; +import com.jfoenix.effects.JFXDepthManager; +import javafx.geometry.Pos; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.TwoLineListItem; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class GameListItemView extends BorderPane { + + public GameListItemView(GameListItemViewModel viewModel) { + JFXRadioButton chkSelected = new JFXRadioButton(); + BorderPane.setAlignment(chkSelected, Pos.CENTER); + chkSelected.setUserData(viewModel); + chkSelected.selectedProperty().bindBidirectional(viewModel.selectedProperty()); + chkSelected.setToggleGroup(viewModel.getToggleGroup()); + setLeft(chkSelected); + + HBox center = new HBox(); + center.setSpacing(8); + center.setAlignment(Pos.CENTER_LEFT); + + StackPane imageViewContainer = new StackPane(); + FXUtils.setLimitWidth(imageViewContainer, 32); + FXUtils.setLimitHeight(imageViewContainer, 32); + + ImageView imageView = new ImageView(); + FXUtils.limitSize(imageView, 32, 32); + imageView.imageProperty().bind(viewModel.imageProperty()); + imageViewContainer.getChildren().setAll(imageView); + + TwoLineListItem item = new TwoLineListItem(); + BorderPane.setAlignment(item, Pos.CENTER); + center.getChildren().setAll(imageView, item); + setCenter(center); + + JFXListView menu = new JFXListView<>(); + menu.getItems().setAll( + i18n("settings"), + i18n("version.manage.rename"), + i18n("version.manage.remove"), + i18n("modpack.export"), + i18n("folder.game"), + i18n("version.launch"), + i18n("version.launch_script")); + JFXPopup popup = new JFXPopup(menu); + menu.setOnMouseClicked(e -> { + popup.hide(); + switch (menu.getSelectionModel().getSelectedIndex()) { + case 0: + viewModel.modifyGameSettings(); + break; + case 1: + viewModel.rename(); + break; + case 2: + viewModel.remove(); + break; + case 3: + viewModel.export(); + break; + case 4: + viewModel.browse(); + break; + case 5: + viewModel.launch(); + break; + case 6: + viewModel.generateLaunchScript(); + break; + } + }); + + HBox right = new HBox(); + right.setAlignment(Pos.CENTER_RIGHT); + if (viewModel.canUpdate()) { + JFXButton btnUpgrade = new JFXButton(); + btnUpgrade.setOnMouseClicked(e -> viewModel.update()); + btnUpgrade.getStyleClass().add("toggle-icon4"); + btnUpgrade.setGraphic(SVG.update(Theme.blackFillBinding(), -1, -1)); + JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnUpgrade, i18n("version.update"))); + right.getChildren().add(btnUpgrade); + } + + JFXButton btnManage = new JFXButton(); + btnManage.setOnMouseClicked(e -> { + menu.getSelectionModel().select(-1); + popup.show(this, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 0, this.getHeight()); + }); + btnManage.getStyleClass().add("toggle-icon4"); + BorderPane.setAlignment(btnManage, Pos.CENTER); + btnManage.setGraphic(SVG.dotsVertical(Theme.blackFillBinding(), -1, -1)); + right.getChildren().add(btnManage); + setRight(right); + + setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;"); + JFXDepthManager.setDepth(this, 1); + item.titleProperty().bind(viewModel.titleProperty()); + item.subtitleProperty().bind(viewModel.subtitleProperty()); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemViewModel.java new file mode 100644 index 000000000..7cee5029a --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemViewModel.java @@ -0,0 +1,141 @@ +/* + * 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.versions; + +import javafx.beans.property.*; +import javafx.scene.control.ToggleGroup; +import javafx.scene.image.Image; +import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameVersion; +import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.ui.Controllers; + +import java.io.File; + +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 class GameListItemViewModel { + private final Profile profile; + private final String version; + private final boolean isModpack; + private final ToggleGroup toggleGroup; + private final StringProperty title = new SimpleStringProperty(); + private final StringProperty subtitle = new SimpleStringProperty(); + private final BooleanProperty selected = new SimpleBooleanProperty(); + private final ObjectProperty image = new SimpleObjectProperty<>(); + + public GameListItemViewModel(ToggleGroup toggleGroup, Profile profile, String id) { + this.profile = profile; + this.version = id; + this.toggleGroup = toggleGroup; + this.isModpack = profile.getRepository().isModpack(id); + + String game = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(id)).orElse("Unknown"); + + StringBuilder libraries = new StringBuilder(game); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(profile.getRepository().getVersion(id)); + analyzer.getForge().ifPresent(library -> libraries.append(", ").append(i18n("install.installer.forge")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)forge", "")))); + analyzer.getLiteLoader().ifPresent(library -> libraries.append(", ").append(i18n("install.installer.liteloader")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)liteloader", "")))); + analyzer.getOptiFine().ifPresent(library -> libraries.append(", ").append(i18n("install.installer.optifine")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)optifine", "")))); + + title.set(id); + subtitle.set(libraries.toString()); + selected.set(profile.getSelectedVersion().equals(id)); + + File iconFile = profile.getRepository().getVersionIcon(version); + if (iconFile.exists()) + image.set(new Image("file:" + iconFile.getAbsolutePath())); + else + image.set(new Image("/assets/img/grass.png")); + } + + public ToggleGroup getToggleGroup() { + return toggleGroup; + } + + public Profile getProfile() { + return profile; + } + + public String getVersion() { + return version; + } + + public StringProperty titleProperty() { + return title; + } + + public StringProperty subtitleProperty() { + return subtitle; + } + + public BooleanProperty selectedProperty() { + return selected; + } + + public ObjectProperty imageProperty() { + return image; + } + + public void checkSelection() { + selected.set(version.equals(profile.getSelectedVersion())); + } + + public void rename() { + Versions.renameVersion(profile, version); + } + + public void remove() { + Versions.deleteVersion(profile, version); + } + + public void export() { + Versions.exportVersion(profile, version); + } + + public void browse() { + Versions.openFolder(profile, version); + } + + public void launch() { + Versions.launch(profile, version); + } + + public void modifyGameSettings() { + Controllers.getVersionPage().load(version, profile); + Controllers.getDecorator().showPage(Controllers.getVersionPage()); + } + + public void generateLaunchScript() { + Versions.generateLaunchScript(profile, version); + } + + public boolean canUpdate() { + return isModpack; + } + + public void update() { + Versions.updateVersion(profile, version); + } + + private static String modifyVersion(String gameVersion, String version) { + return removeSuffix(removePrefix(removeSuffix(removePrefix(version.replace(gameVersion, "").trim(), "-"), "-"), "_"), "_"); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListView.java new file mode 100644 index 000000000..1bca1d9a8 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListView.java @@ -0,0 +1,125 @@ +/* + * 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.versions; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXSpinner; +import com.jfoenix.effects.JFXDepthManager; +import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.geometry.Insets; +import javafx.scene.Node; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.util.MappedObservableList; +import org.jackhuang.hmcl.util.i18n.I18n; + +public class GameListView extends BorderPane implements DecoratorPage { + private final StringProperty title = new SimpleStringProperty(I18n.i18n("version.manage")); + + private static Node wrap(Node node) { + StackPane stackPane = new StackPane(); + stackPane.setPadding(new Insets(0, 5, 0, 2)); + stackPane.getChildren().setAll(node); + return stackPane; + } + + public GameListView(GameListViewModel viewModel) { + { + HBox toolbar = new HBox(); + toolbar.getStyleClass().setAll("jfx-tool-bar-second"); + JFXDepthManager.setDepth(toolbar, 1); + toolbar.setPickOnBounds(false); + + JFXButton btnAddNewGame = new JFXButton(); + btnAddNewGame.getStyleClass().add("jfx-tool-bar-button"); + btnAddNewGame.textFillProperty().bind(Theme.foregroundFillBinding()); + btnAddNewGame.setGraphic(wrap(SVG.plus(Theme.foregroundFillBinding(), -1, -1))); + btnAddNewGame.setText(I18n.i18n("install.new_game")); + btnAddNewGame.setOnMouseClicked(e -> viewModel.addNewGame()); + toolbar.getChildren().add(btnAddNewGame); + + JFXButton btnImportModpack = new JFXButton(); + btnImportModpack.getStyleClass().add("jfx-tool-bar-button"); + btnImportModpack.textFillProperty().bind(Theme.foregroundFillBinding()); + btnImportModpack.setGraphic(wrap(SVG.importIcon(Theme.foregroundFillBinding(), -1, -1))); + btnImportModpack.setText(I18n.i18n("install.modpack")); + btnImportModpack.setOnMouseClicked(e -> viewModel.importModpack()); + toolbar.getChildren().add(btnImportModpack); + + JFXButton btnRefresh = new JFXButton(); + btnRefresh.getStyleClass().add("jfx-tool-bar-button"); + btnRefresh.textFillProperty().bind(Theme.foregroundFillBinding()); + btnRefresh.setGraphic(wrap(SVG.refresh(Theme.foregroundFillBinding(), -1, -1))); + btnRefresh.setText(I18n.i18n("button.refresh")); + btnRefresh.setOnMouseClicked(e -> viewModel.refresh()); + toolbar.getChildren().add(btnRefresh); + + JFXButton btnModify = new JFXButton(); + btnModify.getStyleClass().add("jfx-tool-bar-button"); + btnModify.textFillProperty().bind(Theme.foregroundFillBinding()); + btnModify.setGraphic(wrap(SVG.gear(Theme.foregroundFillBinding(), -1, -1))); + btnModify.setText(I18n.i18n("settings.type.global.manage")); + btnModify.setOnMouseClicked(e -> viewModel.modifyGlobalGameSettings()); + toolbar.getChildren().add(btnModify); + + setTop(toolbar); + } + + { + StackPane center = new StackPane(); + + JFXSpinner spinner = new JFXSpinner(); + spinner.getStyleClass().setAll("first-spinner"); + + ScrollPane scrollPane = new ScrollPane(); + scrollPane.setFitToWidth(true); + + VBox gameList = new VBox(); + gameList.maxWidthProperty().bind(scrollPane.widthProperty()); + gameList.setSpacing(10); + gameList.setStyle("-fx-padding: 10 10 10 10;"); + + Bindings.bindContent(gameList.getChildren(), + MappedObservableList.create(viewModel.itemsProperty(), model -> { + GameListItemView view = new GameListItemView(model); + return view; + })); + + scrollPane.setContent(gameList); + + FXUtils.onChangeAndOperate(viewModel.loadingProperty(), + loading -> center.getChildren().setAll(loading ? spinner : scrollPane)); + + setCenter(center); + } + } + + @Override + public StringProperty titleProperty() { + return title; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListViewModel.java new file mode 100644 index 000000000..c54d64bc7 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListViewModel.java @@ -0,0 +1,113 @@ +/* + * 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.versions; + +import com.jfoenix.concurrency.JFXUtilities; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ListProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.collections.FXCollections; +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.Settings; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; +import org.jackhuang.hmcl.util.VersionNumber; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class GameListViewModel { + private final BooleanProperty loading = new SimpleBooleanProperty(true); + private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()); + + private Profile profile; + private ToggleGroup toggleGroup; + + public GameListViewModel() { + 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()) + JFXUtilities.runInFX(() -> loading.set(true)); + }); + EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(event -> { + this.profile = event.getProfile(); + }); + + profile = Settings.instance().getSelectedProfile(); + if (profile.getRepository().isLoaded()) + loadVersions(profile.getRepository()); + else + profile.getRepository().refreshVersionsAsync().start(); + } + + private void loadVersions(HMCLGameRepository repository) { + toggleGroup = new ToggleGroup(); + 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 -> new GameListItemViewModel(toggleGroup, profile, version.getId())) + .collect(Collectors.toList()); + JFXUtilities.runInFX(() -> { + if (profile == repository.getProfile()) { + loading.set(false); + items.setAll(children); + children.forEach(GameListItemViewModel::checkSelection); + } + toggleGroup.selectedToggleProperty().addListener((o, a, toggle) -> { + GameListItemViewModel model = (GameListItemViewModel) toggle.getUserData(); + model.getProfile().setSelectedVersion(model.getVersion()); + }); + }); + } + + public void addNewGame() { + Controllers.getDecorator().startWizard(new DownloadWizardProvider(0), i18n("install.new_game")); + } + + public void importModpack() { + Controllers.getDecorator().startWizard(new DownloadWizardProvider(1), i18n("install.modpack")); + } + + public void refresh() { + profile.getRepository().refreshVersionsAsync().start(); + } + + public void modifyGlobalGameSettings() { + // Controllers.navigate(); + } + + public BooleanProperty loadingProperty() { + return loading; + } + + public ListProperty itemsProperty() { + return items; + } +} diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index c3e29e931..04ad657a9 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -294,9 +294,39 @@ -jfx-rippler-fill: WHITE; } +.jfx-tool-bar-second { + -fx-padding: 2 2 2 2; + -fx-background-color: -fx-base-check-color; +} + +.jfx-tool-bar-second .jfx-rippler { + -jfx-rippler-fill: WHITE; +} + +.jfx-tool-bar-button { + -fx-toggle-icon4-size: 35px; + -fx-pref-height: -fx-toggle-icon4-size; + -fx-max-height: -fx-toggle-icon4-size; + -fx-min-height: -fx-toggle-icon4-size; + -fx-background-radius: 5px; + -fx-background-color: transparent; + -jfx-toggle-color: white; + -jfx-untoggle-color: transparent; +} + +.jfx-tool-bar-button .icon { + -fx-fill: rgb(204.0, 204.0, 51.0); + -fx-padding: 10.0; +} + +.jfx-tool-bar-button .jfx-rippler { + -jfx-rippler-fill: white; + -jfx-mask-type: CIRCLE; +} + .jfx-decorator-button { -fx-max-width: 35px; - -fx-background-radius: 40px; + -fx-background-radius: 5px; -fx-max-height: 35px; -fx-background-color: transparent; -jfx-toggle-color: rgba(128, 128, 255, 0.2); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 1709e9db3..2302fd043 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -253,7 +253,7 @@ selector.choose=Choose selector.choose_file=Select a file selector.custom=Custom -settings=Settings +settings=Game Settings settings.advanced=Advanced Settings settings.advanced.dont_check_game_completeness=Don't check game completeness @@ -308,8 +308,9 @@ settings.max_memory=Max Memory/MB settings.physical_memory=Physical Memory Size settings.show_log=Show Logs settings.tabs.installers=Installers -settings.type.global=Global version settings(all shared) settings.type=Version setting type +settings.type.global=Global global settings(all shared) +settings.type.global.manage=Global Game Settings settings.type.special=Specialized version settings(will not affect other versions) update=Update @@ -334,6 +335,7 @@ version.launch_script=Make Launching Script version.launch_script.failed=Unable to make launch script. version.launch_script.save=Save the launch script version.launch_script.success=Finished script creation, %s. +version.manage=Game List version.manage.redownload_assets_index=Redownload Assets Index version.manage.remove=Delete this game version.manage.remove.confirm=Sure to remove game %s? You cannot restore this game again! @@ -344,7 +346,7 @@ version.manage.rename=Rename this game version.manage.rename.message=Please enter the new name version.manage.rename.fail=Failed to rename this game. version.settings=Settings -version.update=Update +version.update=Update modpack wizard.prev=< Prev wizard.failed=Failed diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 1695d20b1..baffe02dc 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -253,7 +253,7 @@ selector.choose=選擇 selector.choose_file=選擇檔案 selector.custom=自訂 -settings=普通設定 +settings=遊戲設定 settings.advanced=進階設定 settings.advanced.dont_check_game_completeness=不檢查遊戲完整性 @@ -308,8 +308,9 @@ settings.max_memory=最大記憶體(MB) settings.physical_memory=實體記憶體大小 settings.show_log=查看記錄 settings.tabs.installers=自動安裝 -settings.type.global=全域版本設定(使用該設定的版本共用一套設定) settings.type=版本設定類型 +settings.type.global=全域版本設定(使用該設定的版本共用一套設定) +settings.type.global.manage=全域遊戲設定 settings.type.special=單獨版本設定(不會影響到其他版本的設定) update=啟動器更新 @@ -334,6 +335,7 @@ version.launch_script=生成啟動腳本 version.launch_script.failed=生成啟動腳本失敗 version.launch_script.save=儲存啟動腳本 version.launch_script.success=啟動腳本已生成完畢:%s +version.manage=遊戲列表 version.manage.redownload_assets_index=重新下載資源設定(assets_index.json) version.manage.remove=刪除該版本 version.manage.remove.confirm=真的要刪除版本 %s 嗎?你將無法找回被刪除的檔案! @@ -344,7 +346,7 @@ version.manage.rename=重新命名該版本 version.manage.rename.message=請輸入新名稱 version.manage.rename.fail=重新命名版本失敗,可能檔案被佔用或者名字有特殊字元 version.settings=遊戲設定 -version.update=更新 +version.update=更新整合包 wizard.prev=< 上一步 wizard.failed=失敗 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 08cec0547..8eee5b0d6 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -253,7 +253,7 @@ selector.choose=选择 selector.choose_file=选择文件 selector.custom=自定义 -settings=普通设置 +settings=游戏设置 settings.advanced=高级设置 settings.advanced.dont_check_game_completeness=不检查游戏完整性 @@ -308,9 +308,10 @@ settings.max_memory=最大内存(MB) settings.physical_memory=物理内存大小 settings.show_log=查看日志 settings.tabs.installers=自动安装 -settings.type.global=全局版本设置(使用该设置的版本共用一套设定) settings.type=版本设置类型 -settings.type.special=单独版本设置(不会影响到其他版本的设定) +settings.type.global=全局版本设置(使用该设置的版本共用一套设定) +settings.type.global.manage=全局游戏设置 +settings.type.special=版本特定设置(不会影响到其他版本的设定) update=启动器更新 update.channel.dev=更新到开发版 @@ -334,6 +335,7 @@ version.launch_script=生成启动脚本 version.launch_script.failed=生成启动脚本失败 version.launch_script.save=保存启动脚本 version.launch_script.success=启动脚本已生成完毕:%s +version.manage=游戏列表 version.manage.redownload_assets_index=重新下载资源配置(assets_index.json) version.manage.remove=删除该版本 version.manage.remove.confirm=真的要删除版本 %s 吗?你将无法找回被删除的文件! @@ -344,7 +346,7 @@ version.manage.rename=重命名该版本 version.manage.rename.message=请输入要改成的名字 version.manage.rename.fail=重命名版本失败,可能文件被占用或者名字有特殊字符 version.settings=游戏设置 -version.update=更新 +version.update=更新整合包 wizard.prev=< 上一步 wizard.failed=失败 From 8f2f57c543afca2f64929f287c2b3a89f3c585ab Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:13:13 +0800 Subject: [PATCH 07/28] cut off the version name if too long --- .../org/jackhuang/hmcl/ui/AdvancedListItem2.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java index c09b96ff6..93506b15d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java @@ -25,6 +25,7 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import org.jackhuang.hmcl.setting.Theme; @@ -48,15 +49,16 @@ public class AdvancedListItem2 extends StackPane { imageView.imageProperty().bind(viewModel.imageProperty()); imageViewContainer.getChildren().setAll(imageView); - BorderPane borderPane = new BorderPane(); - borderPane.setPadding(new Insets(0, 0, 0, 10)); + VBox vbox = new VBox(); + vbox.setAlignment(Pos.CENTER_LEFT); + vbox.setPadding(new Insets(0, 0, 0, 10)); Label title = new Label(); title.textProperty().bind(viewModel.titleProperty()); title.setMaxWidth(90); title.setStyle("-fx-font-size: 15;"); title.setTextAlignment(TextAlignment.JUSTIFY); - borderPane.setTop(title); + vbox.getChildren().add(title); if (viewModel.subtitleProperty() != null) { Label subtitle = new Label(); @@ -64,12 +66,10 @@ public class AdvancedListItem2 extends StackPane { subtitle.setMaxWidth(90); subtitle.setStyle("-fx-font-size: 10;"); subtitle.setTextAlignment(TextAlignment.JUSTIFY); - borderPane.setBottom(subtitle); - } else { - title.setWrapText(true); + vbox.getChildren().add(subtitle); } - left.getChildren().setAll(imageViewContainer, borderPane); + left.getChildren().setAll(imageViewContainer, vbox); root.setLeft(left); HBox right = new HBox(); From 1695619ef563d60ee9a63440d035c769a44f549b Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:13:53 +0800 Subject: [PATCH 08/28] Move account ui class to a new package --- HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java | 1 + HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java | 1 + .../main/java/org/jackhuang/hmcl/ui/DialogController.java | 1 + .../main/java/org/jackhuang/hmcl/ui/LeftPaneController.java | 2 ++ .../jackhuang/hmcl/ui/{ => account}/AccountLoginPane.java | 3 ++- .../org/jackhuang/hmcl/ui/{ => account}/AccountPage.java | 4 +++- .../org/jackhuang/hmcl/ui/{ => account}/AddAccountPane.java | 5 ++++- .../hmcl/ui/{ => account}/AddAuthlibInjectorServerPane.java | 2 +- .../hmcl/ui/{ => account}/AuthlibInjectorServerItem.java | 3 ++- .../hmcl/ui/{ => account}/AuthlibInjectorServersPage.java | 3 ++- 10 files changed, 19 insertions(+), 6 deletions(-) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => account}/AccountLoginPane.java (97%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => account}/AccountPage.java (97%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => account}/AddAccountPane.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => account}/AddAuthlibInjectorServerPane.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => account}/AuthlibInjectorServerItem.java (97%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => account}/AuthlibInjectorServersPage.java (97%) 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 c0804cb9d..c27d2ee4a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -27,6 +27,7 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; +import org.jackhuang.hmcl.ui.account.AuthlibInjectorServersPage; import org.jackhuang.hmcl.ui.construct.InputDialogPane; import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.construct.MessageDialogPane; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java index ddb959e32..91ac7e21b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java @@ -58,6 +58,7 @@ import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDnD; import org.jackhuang.hmcl.setting.ConfigHolder; import org.jackhuang.hmcl.setting.EnumBackgroundImage; import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.account.AddAuthlibInjectorServerPane; import org.jackhuang.hmcl.ui.animation.AnimationProducer; import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.TransitionHandler; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogController.java index b0d4d5862..c47790a1a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogController.java @@ -22,6 +22,7 @@ import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.AuthInfo; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.task.SilentException; +import org.jackhuang.hmcl.ui.account.AccountLoginPane; import java.util.Optional; import java.util.concurrent.CountDownLatch; 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 5db94cebd..cec6a6dcb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -49,6 +49,8 @@ import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; +import org.jackhuang.hmcl.ui.account.AccountPage; +import org.jackhuang.hmcl.ui.account.AddAccountPane; import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.Lang; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountLoginPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java similarity index 97% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountLoginPane.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java index 89bd4c6bd..340cb8489 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountLoginPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.account; import com.jfoenix.controls.JFXPasswordField; import com.jfoenix.controls.JFXProgressBar; @@ -27,6 +27,7 @@ import org.jackhuang.hmcl.auth.AuthInfo; import org.jackhuang.hmcl.auth.NoSelectedCharacterException; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import java.util.function.Consumer; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java similarity index 97% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java index 9befda845..f0eff72d8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.account; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXProgressBar; @@ -38,6 +38,8 @@ import org.jackhuang.hmcl.game.AccountHelper; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.AdvancedListItem; import org.jackhuang.hmcl.ui.construct.ComponentList; import org.jackhuang.hmcl.ui.wizard.DecoratorPage; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java index ab2bbf599..b1a9daf70 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.account; import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.*; @@ -41,6 +41,9 @@ import org.jackhuang.hmcl.game.AccountHelper; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.TwoLineListItem; import org.jackhuang.hmcl.ui.construct.AdvancedListBox; import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.IconedItem; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAuthlibInjectorServerPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAuthlibInjectorServerPane.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java index 61dd9d52d..c875e1c01 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAuthlibInjectorServerPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.account; import static org.jackhuang.hmcl.ui.FXUtils.loadFXML; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AuthlibInjectorServerItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServerItem.java similarity index 97% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/AuthlibInjectorServerItem.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServerItem.java index f5c874dfc..570988b42 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AuthlibInjectorServerItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServerItem.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.account; import com.jfoenix.controls.JFXButton; import com.jfoenix.effects.JFXDepthManager; @@ -25,6 +25,7 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.SVG; import java.util.function.Consumer; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AuthlibInjectorServersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java similarity index 97% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/AuthlibInjectorServersPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java index 02e02ad30..29d5e33bd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AuthlibInjectorServersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java @@ -15,13 +15,14 @@ * 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; +package org.jackhuang.hmcl.ui.account; import static org.jackhuang.hmcl.ui.FXUtils.loadFXML; import static org.jackhuang.hmcl.ui.FXUtils.smoothScrolling; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; +import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.util.MappedObservableList; From 08564e5ea260de732b510aba5ed66fc7a45d0f9e Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:14:30 +0800 Subject: [PATCH 09/28] Add account list view --- .../jackhuang/hmcl/ui/AdvancedListItem2.java | 4 + .../hmcl/ui/AdvancedListItemViewModel.java | 5 + .../org/jackhuang/hmcl/ui/Controllers.java | 12 ++ .../jackhuang/hmcl/ui/LeftPaneController.java | 120 ++------------ .../main/java/org/jackhuang/hmcl/ui/SVG.java | 9 +- .../AccountAdvancedListItemViewModel.java | 100 +++++++++++ .../hmcl/ui/account/AccountListItemView.java | 88 ++++++++++ .../ui/account/AccountListItemViewModel.java | 116 +++++++++++++ .../hmcl/ui/account/AccountListView.java | 81 +++++++++ .../hmcl/ui/account/AccountListViewModel.java | 73 +++++++++ .../hmcl/ui/account/AccountPage.java | 155 ------------------ .../GameAdvancedListItemViewModel.java | 7 +- HMCL/src/main/resources/assets/css/root.css | 2 +- .../main/resources/assets/img/craft_table.png | Bin 0 -> 12058 bytes .../resources/assets/lang/I18N.properties | 5 +- .../resources/assets/lang/I18N_zh.properties | 5 +- .../assets/lang/I18N_zh_CN.properties | 5 +- 17 files changed, 513 insertions(+), 274 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => versions}/GameAdvancedListItemViewModel.java (94%) create mode 100644 HMCL/src/main/resources/assets/img/craft_table.png diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java index 93506b15d..bd091f404 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java @@ -29,6 +29,8 @@ import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import org.jackhuang.hmcl.setting.Theme; +import java.util.Optional; + public class AdvancedListItem2 extends StackPane { public AdvancedListItem2(AdvancedListItemViewModel viewModel) { @@ -47,6 +49,8 @@ public class AdvancedListItem2 extends StackPane { FXUtils.limitSize(imageView, 32, 32); imageView.setPreserveRatio(true); imageView.imageProperty().bind(viewModel.imageProperty()); + Optional.ofNullable(viewModel.viewportProperty()) + .ifPresent(imageView.viewportProperty()::bind); imageViewContainer.getChildren().setAll(imageView); VBox vbox = new VBox(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java index d902c6ae9..ffb1fa339 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui; import javafx.beans.property.ObjectProperty; import javafx.beans.property.StringProperty; +import javafx.geometry.Rectangle2D; import javafx.scene.image.Image; public abstract class AdvancedListItemViewModel { @@ -27,6 +28,10 @@ public abstract class AdvancedListItemViewModel { public abstract ObjectProperty imageProperty(); + public ObjectProperty viewportProperty() { + return null; + } + public abstract StringProperty titleProperty(); public abstract StringProperty subtitleProperty(); 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 c27d2ee4a..a70dc1b0f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -27,6 +27,8 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; +import org.jackhuang.hmcl.ui.account.AccountListView; +import org.jackhuang.hmcl.ui.account.AccountListViewModel; import org.jackhuang.hmcl.ui.account.AuthlibInjectorServersPage; import org.jackhuang.hmcl.ui.construct.InputDialogPane; import org.jackhuang.hmcl.ui.construct.MessageBox; @@ -49,6 +51,7 @@ public final class Controllers { private static SettingsPage settingsPage = null; private static VersionPage versionPage = null; private static GameListView gameListView = null; + private static AccountListView accountListView = null; private static AuthlibInjectorServersPage serversPage = null; private static LeftPaneController leftPaneController; private static Decorator decorator; @@ -75,6 +78,13 @@ public final class Controllers { return gameListView; } + // FXThread + public static AccountListView getAccountListView() { + if (accountListView == null) + accountListView = new AccountListView(new AccountListViewModel()); + return accountListView; + } + // FXThread public static VersionPage getVersionPage() { if (versionPage == null) @@ -192,5 +202,7 @@ public final class Controllers { decorator = null; stage = null; scene = null; + gameListView = null; + accountListView = null; } } 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 cec6a6dcb..562d41b37 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -19,28 +19,15 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXPopup; import javafx.application.Platform; -import javafx.beans.binding.Bindings; import javafx.beans.binding.When; -import javafx.beans.property.ListProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleListProperty; -import javafx.beans.property.SimpleObjectProperty; import javafx.scene.Node; -import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.input.MouseButton; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import org.jackhuang.hmcl.auth.Account; -import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; -import org.jackhuang.hmcl.auth.offline.OfflineAccount; -import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.event.*; -import org.jackhuang.hmcl.game.AccountHelper; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.mod.Modpack; @@ -49,12 +36,12 @@ import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; -import org.jackhuang.hmcl.ui.account.AccountPage; +import org.jackhuang.hmcl.ui.account.AccountAdvancedListItemViewModel; import org.jackhuang.hmcl.ui.account.AddAccountPane; import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.ui.versions.GameAdvancedListItemViewModel; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.MappedObservableList; import java.io.File; import java.util.LinkedList; @@ -62,37 +49,20 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; -import static javafx.collections.FXCollections.singletonObservableList; -import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class LeftPaneController { private final AdvancedListBox leftPane; private final VBox profilePane = new VBox(); private final VBox accountPane = new VBox(); - private final IconedItem launcherSettingsItem; - private final AdvancedListItem2 gameListItem; - - private ListProperty accountItems = new SimpleListProperty<>(); - private ObjectProperty selectedAccount = new SimpleObjectProperty() { - { - accountItems.addListener(onInvalidating(this::invalidated)); - } - - @Override - protected void invalidated() { - Account selected = get(); - accountItems.forEach(item -> item.setSelected( - getAccountFromItem(item) - .map(it -> it == selected) - .orElse(false))); - } - }; public LeftPaneController(AdvancedListBox leftPane) { this.leftPane = leftPane; - launcherSettingsItem = new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20)); + AdvancedListItem2 accountListItem = new AdvancedListItem2(new AccountAdvancedListItemViewModel()); + AdvancedListItem2 gameListItem = new AdvancedListItem2(new GameAdvancedListItemViewModel()); + + IconedItem launcherSettingsItem = new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20)); launcherSettingsItem.getLabel().textProperty().bind( new When(UpdateChecker.outdatedProperty()) @@ -107,17 +77,12 @@ public final class LeftPaneController { launcherSettingsItem.maxWidthProperty().bind(leftPane.widthProperty()); launcherSettingsItem.setOnMouseClicked(e -> Controllers.navigate(Controllers.getSettingsPage())); - gameListItem = new AdvancedListItem2(new GameAdvancedListItemViewModel()); - leftPane - .add(new ClassTitle(i18n("account").toUpperCase(), Lang.apply(new JFXButton(), button -> { - button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10)); - button.getStyleClass().add("toggle-icon-tiny"); - button.setOnMouseClicked(e -> addNewAccount()); - }))) - .add(accountPane) - .startCategory(i18n("launcher").toUpperCase()) + .startCategory(i18n("account").toUpperCase()) + .add(accountListItem) + .startCategory(i18n("version").toUpperCase()) .add(gameListItem) + .startCategory(i18n("launcher").toUpperCase()) .add(launcherSettingsItem) .add(new ClassTitle(i18n("profile.title").toUpperCase(), Lang.apply(new JFXButton(), button -> { button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10)); @@ -127,22 +92,6 @@ public final class LeftPaneController { }))) .add(profilePane); - // ==== Accounts ==== - // Missing account item - AdvancedListItem missingAccountItem = new AdvancedListItem(i18n("account.missing"), i18n("message.unknown")); - RipplerContainer missingAccountRippler = new RipplerContainer(missingAccountItem); - missingAccountItem.setOnSettingsButtonClicked(e -> addNewAccount()); - missingAccountRippler.setOnMouseClicked(e -> addNewAccount()); - - accountItems.bind( - new When(Accounts.accountsProperty().emptyProperty()) - .then(singletonObservableList(missingAccountRippler)) - .otherwise(MappedObservableList.create(Accounts.getAccounts(), this::createAccountItem))); - Bindings.bindContent(accountPane.getChildren(), accountItems); - - selectedAccount.bindBidirectional(Accounts.selectedAccountProperty()); - // ==== - 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); @@ -154,55 +103,6 @@ public final class LeftPaneController { .map(Account.class::cast); } - private static String accountSubtitle(Account account) { - if (account instanceof OfflineAccount) - return i18n("account.methods.offline"); - else if (account instanceof YggdrasilAccount) - return account.getUsername(); - else - return ""; - } - - private RipplerContainer createAccountItem(Account account) { - AdvancedListItem item = new AdvancedListItem(account.getCharacter(), accountSubtitle(account)); - RipplerContainer rippler = new RipplerContainer(item); - item.setOnSettingsButtonClicked(e -> { - AccountPage accountPage = new AccountPage(account, item); - JFXPopup popup = new JFXPopup(accountPage); - accountPage.setOnDelete(popup::hide); - popup.show((Node) e.getSource(), JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, e.getX(), e.getY()); - }); - rippler.setOnMouseClicked(e -> { - if (e.getButton() == MouseButton.PRIMARY) { - selectedAccount.set(account); - } - }); - rippler.getProperties().put("account", account); - rippler.maxWidthProperty().bind(leftPane.widthProperty()); - - if (account instanceof YggdrasilAccount) { - Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); - item.setImage(image, AccountHelper.getViewport(4)); - } else { - item.setImage(AccountHelper.getDefaultSkin(account.getUUID(), 4), AccountHelper.getViewport(4)); - } - - if (account instanceof AuthlibInjectorAccount) { - FXUtils.installTooltip(rippler, 500, 5000, 0, new Tooltip(((AuthlibInjectorAccount) account).getServer().getName())); - } - - // update skin - if (account instanceof YggdrasilAccount) { - AccountHelper.refreshSkinAsync((YggdrasilAccount) account) - .subscribe(Schedulers.javafx(), () -> { - Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); - item.setImage(image, AccountHelper.getViewport(4)); - }); - } - - return rippler; - } - public void checkAccount() { if (Accounts.getAccounts().isEmpty()) addNewAccount(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index 96928efb6..8925dd194 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -18,8 +18,10 @@ package org.jackhuang.hmcl.ui; import javafx.beans.binding.ObjectBinding; +import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.Node; +import javafx.scene.layout.StackPane; import javafx.scene.paint.Paint; import javafx.scene.shape.SVGPath; @@ -33,8 +35,11 @@ public final class SVG { path.setContent(d); path.fillProperty().bind(fill); - if (width < 0 || height < 0) - return path; + if (width < 0 || height < 0) { + StackPane pane = new StackPane(path); + pane.setAlignment(Pos.CENTER); + return pane; + } Group svg = new Group(path); double scale = Math.min(width / svg.getBoundsInParent().getWidth(), height / svg.getBoundsInParent().getHeight()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java new file mode 100644 index 000000000..8110af52b --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java @@ -0,0 +1,100 @@ +/* + * 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.account; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.geometry.Rectangle2D; +import javafx.scene.image.Image; +import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.auth.offline.OfflineAccount; +import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; +import org.jackhuang.hmcl.game.AccountHelper; +import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.ui.AdvancedListItemViewModel; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class AccountAdvancedListItemViewModel extends AdvancedListItemViewModel { + private final ObjectProperty image = new SimpleObjectProperty<>(); + private final ObjectProperty viewport = new SimpleObjectProperty<>(AccountHelper.getViewport(4)); + private final StringProperty title = new SimpleStringProperty(); + private final StringProperty subtitle = new SimpleStringProperty(); + + public AccountAdvancedListItemViewModel() { + + FXUtils.onChangeAndOperate(Accounts.selectedAccountProperty(), account -> { + if (account == null) { + title.set(i18n("account.missing")); + subtitle.set(i18n("account.missing.add")); + image.set(new Image("/assets/img/craft_table.png")); + } else { + title.set(account.getCharacter()); + subtitle.set(accountSubtitle(account)); + + this.image.set(AccountHelper.getDefaultSkin(account.getUUID(), 4)); + + if (account instanceof YggdrasilAccount) { + AccountHelper.loadSkinAsync((YggdrasilAccount) account).subscribe(Schedulers.javafx(), () -> { + Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); + this.image.set(image); + }); + } + } + }); + } + + @Override + public void action() { + Controllers.navigate(Controllers.getAccountListView()); + } + + @Override + public ObjectProperty imageProperty() { + return image; + } + + @Override + public ObjectProperty viewportProperty() { + return viewport; + } + + @Override + public StringProperty titleProperty() { + return title; + } + + @Override + public StringProperty subtitleProperty() { + return subtitle; + } + + private static String accountSubtitle(Account account) { + if (account instanceof OfflineAccount) + return i18n("account.methods.offline"); + else if (account instanceof YggdrasilAccount) + return account.getUsername(); + else + return ""; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java new file mode 100644 index 000000000..8728c20df --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java @@ -0,0 +1,88 @@ +/* + * 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.account; + +import com.jfoenix.concurrency.JFXUtilities; +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXRadioButton; +import com.jfoenix.effects.JFXDepthManager; +import javafx.geometry.Pos; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.TwoLineListItem; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class AccountListItemView extends BorderPane { + + public AccountListItemView(AccountListItemViewModel viewModel) { + JFXRadioButton chkSelected = new JFXRadioButton(); + BorderPane.setAlignment(chkSelected, Pos.CENTER); + chkSelected.setUserData(viewModel); + chkSelected.selectedProperty().bindBidirectional(viewModel.selectedProperty()); + chkSelected.setToggleGroup(viewModel.getToggleGroup()); + setLeft(chkSelected); + + HBox center = new HBox(); + center.setSpacing(8); + center.setAlignment(Pos.CENTER_LEFT); + + StackPane imageViewContainer = new StackPane(); + FXUtils.setLimitWidth(imageViewContainer, 32); + FXUtils.setLimitHeight(imageViewContainer, 32); + + ImageView imageView = new ImageView(); + FXUtils.limitSize(imageView, 32, 32); + imageView.imageProperty().bind(viewModel.imageProperty()); + imageView.viewportProperty().bind(viewModel.viewportProperty()); + imageViewContainer.getChildren().setAll(imageView); + + TwoLineListItem item = new TwoLineListItem(); + BorderPane.setAlignment(item, Pos.CENTER); + center.getChildren().setAll(imageView, item); + setCenter(center); + + HBox right = new HBox(); + right.setAlignment(Pos.CENTER_RIGHT); + JFXButton btnRefresh = new JFXButton(); + btnRefresh.setOnMouseClicked(e -> viewModel.refresh()); + btnRefresh.getStyleClass().add("toggle-icon4"); + btnRefresh.setGraphic(SVG.refresh(Theme.blackFillBinding(), -1, -1)); + JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnRefresh, i18n("button.refresh"))); + right.getChildren().add(btnRefresh); + + JFXButton btnRemove = new JFXButton(); + btnRemove.setOnMouseClicked(e -> viewModel.remove()); + btnRemove.getStyleClass().add("toggle-icon4"); + BorderPane.setAlignment(btnRemove, Pos.CENTER); + btnRemove.setGraphic(SVG.delete(Theme.blackFillBinding(), -1, -1)); + JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnRefresh, i18n("button.delete"))); + right.getChildren().add(btnRemove); + setRight(right); + + setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;"); + JFXDepthManager.setDepth(this, 1); + item.titleProperty().bind(viewModel.titleProperty()); + item.subtitleProperty().bind(viewModel.subtitleProperty()); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java new file mode 100644 index 000000000..c28e107df --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java @@ -0,0 +1,116 @@ +/* + * 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.account; + +import javafx.beans.property.*; +import javafx.geometry.Rectangle2D; +import javafx.scene.control.ToggleGroup; +import javafx.scene.image.Image; +import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; +import org.jackhuang.hmcl.auth.offline.OfflineAccount; +import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; +import org.jackhuang.hmcl.game.AccountHelper; +import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.task.Schedulers; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class AccountListItemViewModel { + private final Account account; + private final ToggleGroup toggleGroup; + private final StringProperty title = new SimpleStringProperty(); + private final StringProperty subtitle = new SimpleStringProperty(); + private final BooleanProperty selected = new SimpleBooleanProperty(); + private final ObjectProperty image = new SimpleObjectProperty<>(); + private final ObjectProperty viewport = new SimpleObjectProperty<>(); + + public AccountListItemViewModel(ToggleGroup toggleGroup, Account account) { + this.account = account; + this.toggleGroup = toggleGroup; + + StringBuilder subtitleString = new StringBuilder(Accounts.getAccountTypeName(account)); + if (account instanceof AuthlibInjectorAccount) { + AuthlibInjectorServer server = ((AuthlibInjectorAccount) account).getServer(); + subtitleString.append(", ").append(i18n("account.injector.server")).append(": ").append(server.getName()); + } + + if (account instanceof OfflineAccount) + title.set(account.getCharacter()); + else + title.set(account.getUsername() + " - " + account.getCharacter()); + subtitle.set(subtitleString.toString()); + selected.set(Accounts.selectedAccountProperty().get() == account); + + viewport.set(AccountHelper.getViewport(4)); + if (account instanceof YggdrasilAccount) { + Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); + this.image.set(image); + } else { + this.image.set(AccountHelper.getDefaultSkin(account.getUUID(), 4)); + } + } + + public ToggleGroup getToggleGroup() { + return toggleGroup; + } + + public Account getAccount() { + return account; + } + + public StringProperty titleProperty() { + return title; + } + + public StringProperty subtitleProperty() { + return subtitle; + } + + public BooleanProperty selectedProperty() { + return selected; + } + + public ObjectProperty imageProperty() { + return image; + } + + public ObjectProperty viewportProperty() { + return viewport; + } + + public void refresh() { + if (account instanceof YggdrasilAccount) { + // progressBar.setVisible(true); + AccountHelper.refreshSkinAsync((YggdrasilAccount) account) + .finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> { + // progressBar.setVisible(false); + + if (isDependentsSucceeded) { + Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); + this.image.set(image); + } + }).start(); + } + } + + public void remove() { + Accounts.getAccounts().remove(account); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java new file mode 100644 index 000000000..8e0cc566d --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java @@ -0,0 +1,81 @@ +/* + * 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.account; + +import com.jfoenix.controls.JFXButton; +import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.util.MappedObservableList; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class AccountListView extends StackPane implements DecoratorPage { + private final StringProperty title = new SimpleStringProperty(i18n("account.manage")); + + public AccountListView(AccountListViewModel viewModel) { + ScrollPane scrollPane = new ScrollPane(); + { + scrollPane.setFitToWidth(true); + + VBox accountList = new VBox(); + accountList.maxWidthProperty().bind(scrollPane.widthProperty()); + accountList.setSpacing(10); + accountList.setStyle("-fx-padding: 10 10 10 10;"); + + Bindings.bindContent(accountList.getChildren(), + MappedObservableList.create(viewModel.itemsProperty(), AccountListItemView::new)); + + scrollPane.setContent(accountList); + } + + VBox vBox = new VBox(); + { + vBox.setAlignment(Pos.BOTTOM_RIGHT); + vBox.setPickOnBounds(false); + vBox.setPadding(new Insets(15)); + vBox.setSpacing(15); + + JFXButton btnAdd = new JFXButton(); + FXUtils.setLimitWidth(btnAdd, 40); + FXUtils.setLimitHeight(btnAdd, 40); + btnAdd.getStyleClass().setAll("jfx-button-raised-round"); + btnAdd.setButtonType(JFXButton.ButtonType.RAISED); + btnAdd.setGraphic(SVG.plus(Theme.whiteFillBinding(), -1, -1)); + btnAdd.setOnMouseClicked(e -> viewModel.addNewAccount()); + + vBox.getChildren().setAll(btnAdd); + } + + getChildren().setAll(scrollPane, vBox); + } + + @Override + public StringProperty titleProperty() { + return title; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java new file mode 100644 index 000000000..d952e2772 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java @@ -0,0 +1,73 @@ +/* + * 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.account; + +import javafx.beans.property.ListProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.scene.control.ToggleGroup; +import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.util.MappedObservableList; + +import java.util.Comparator; + +import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; + +public class AccountListViewModel { + private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()); + private ObjectProperty selectedAccount = new SimpleObjectProperty() { + { + items.addListener(onInvalidating(this::invalidated)); + } + + @Override + protected void invalidated() { + Account selected = get(); + items.forEach(item -> item.selectedProperty().set(item.getAccount() == selected)); + } + }; + + private ToggleGroup toggleGroup; + + public AccountListViewModel() { + toggleGroup = new ToggleGroup(); + + items.bindContent(MappedObservableList.create( + Accounts.accountsProperty(), + account -> new AccountListItemViewModel(toggleGroup, account))); + + selectedAccount.bindBidirectional(Accounts.selectedAccountProperty()); + toggleGroup.selectedToggleProperty().addListener((o, a, toggle) -> { + if (toggle == null || toggle.getUserData() == null) return; + selectedAccount.set(((AccountListItemViewModel) toggle.getUserData()).getAccount()); + }); + } + + public void addNewAccount() { + Controllers.dialog(new AddAccountPane()); + } + + public ListProperty itemsProperty() { + return items; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java deleted file mode 100644 index f0eff72d8..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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.account; - -import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXProgressBar; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.image.Image; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.StackPane; - -import org.jackhuang.hmcl.auth.Account; -import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; -import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; -import org.jackhuang.hmcl.auth.offline.OfflineAccount; -import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; -import org.jackhuang.hmcl.game.AccountHelper; -import org.jackhuang.hmcl.setting.Accounts; -import org.jackhuang.hmcl.setting.Theme; -import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.construct.AdvancedListItem; -import org.jackhuang.hmcl.ui.construct.ComponentList; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; - -import static org.jackhuang.hmcl.ui.FXUtils.installTooltip; -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; - -import java.util.Optional; - -public class AccountPage extends StackPane implements DecoratorPage { - private final StringProperty title; - private final ObjectProperty onDelete = new SimpleObjectProperty<>(this, "onDelete"); - private final AdvancedListItem item; - private final Account account; - - @FXML - private Label lblType; - @FXML - private Label lblServer; - @FXML - private Label lblCharacter; - @FXML - private Label lblEmail; - @FXML - private BorderPane paneServer; - @FXML - private BorderPane paneEmail; - @FXML - private ComponentList componentList; - @FXML - private JFXButton btnDelete; - @FXML - private JFXButton btnRefresh; - @FXML - private JFXProgressBar progressBar; - - public AccountPage(Account account, AdvancedListItem item) { - this.account = account; - this.item = item; - - title = new SimpleStringProperty(this, "title", i18n("account") + " - " + account.getCharacter()); - - FXUtils.loadFXML(this, "/assets/fxml/account.fxml"); - - if (account instanceof AuthlibInjectorAccount) { - AuthlibInjectorServer server = ((AuthlibInjectorAccount) account).getServer(); - lblServer.setText(server.getName()); - installTooltip(lblServer, server.getUrl()); - } else { - componentList.removeChildren(paneServer); - - if (account instanceof OfflineAccount) { - componentList.removeChildren(paneEmail); - } - } - - btnDelete.setGraphic(SVG.delete(Theme.blackFillBinding(), 15, 15)); - btnRefresh.setGraphic(SVG.refresh(Theme.blackFillBinding(), 15, 15)); - - lblCharacter.setText(account.getCharacter()); - lblType.setText(Accounts.getAccountTypeName(account)); - lblEmail.setText(account.getUsername()); - - btnRefresh.setVisible(account instanceof YggdrasilAccount); - } - - @FXML - private void onDelete() { - Accounts.getAccounts().remove(account); - Optional.ofNullable(onDelete.get()).ifPresent(Runnable::run); - } - - @FXML - private void onRefresh() { - if (account instanceof YggdrasilAccount) { - progressBar.setVisible(true); - AccountHelper.refreshSkinAsync((YggdrasilAccount) account) - .finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> { - progressBar.setVisible(false); - - if (isDependentsSucceeded) { - Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); - item.setImage(image, AccountHelper.getViewport(4)); - } - }).start(); - } - } - - public String getTitle() { - return title.get(); - } - - @Override - public StringProperty titleProperty() { - return title; - } - - public void setTitle(String title) { - this.title.set(title); - } - - public Runnable getOnDelete() { - return onDelete.get(); - } - - public ObjectProperty onDeleteProperty() { - return onDelete; - } - - public void setOnDelete(Runnable onDelete) { - this.onDelete.set(onDelete); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItemViewModel.java similarity index 94% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItemViewModel.java index a484888fe..f4581c0f0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItemViewModel.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.versions; import com.jfoenix.concurrency.JFXUtilities; import javafx.beans.InvalidationListener; @@ -23,14 +23,15 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import javafx.beans.value.ChangeListener; 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.event.RefreshingVersionsEvent; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Settings; +import org.jackhuang.hmcl.ui.AdvancedListItemViewModel; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.WeakListenerHelper; import java.io.File; diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 04ad657a9..94fe55876 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -52,7 +52,7 @@ } .advanced-list-box-item { - -fx-padding: 10 16 10 16; + -fx-padding: 0 16 0 16; } .advanced-list-box-content { diff --git a/HMCL/src/main/resources/assets/img/craft_table.png b/HMCL/src/main/resources/assets/img/craft_table.png new file mode 100644 index 0000000000000000000000000000000000000000..45740510a49e45390674610b603169eaa4685d9f GIT binary patch literal 12058 zcmXY02{crH*dAt#!5I74iL#F+*_Z6QH1?aV8Pb9z``(PT2$d|AtjX91p{%ozD2a?U zL`jxxp{#xL|Gw|cxp&SzbI$#}@9%w{=Y8Ja+(X-0u(JxWfoMG30D+X|txS!aBB$17_Fdh_o0&IHMx+b#ud$p-dH}*ppE0!jkS};{NbFlw8jnoe zO+U>G-hDSlUflFrg%5sx6?k~yH6C-v>ZIcL-t7A&kxF-W?p~u;_~x$nnINkl@xkkF z4%b5V$@6{mkHy3DD^;08CS7s4`{E+;BJp}MXnFlq62f2)Zf$*Y@vO7s?H%`b5=3g& z67>ImXuJc;jk*h~nsKc4nq6^LzO_fth$w&ClQ6`f4cUwP_o?`lydr$`@~o%*otRsE ziFdA%PUa6e@Nsh&qmppUhR&7k6^9HXPocfruAp${PtBUHzHymVjb&9k6Kr#ZS_fq& zn7Pl|yx`qG92&HLaj!4z&vCSl&dtreGu)l)_Bgk{TIS6DAwlOPff2n-UvlFwp1FCg zCtZd7^0SDI$so&Ogljdk=36eF_jB=YO4b8pMt==@?JlLhhovp@yQb!}HW-svPn|&u zbUHcj4^th#ce4?$l_ihJDM#5esDKB))!-ghy}i;gUM)8BRHfK!a@lphMQ8D573u5T zIb3H*oKwc0T6N10`84qnz%_w{A~c>(4I$pivx@Rvet63!Q$kvdAE%@H-LdC1 zWc$IlUF`1Cv(qcf8C<(>9h}%9!t8|S*rJ2oNw3|>^7w)2ReSZxnHNU$b6aPRqYkEi zmAJ6Oi8^1kPyhY$S7^tr^(A9f?K0;?x0bA!!F{1|yYTC1)Sb4oHSsMXR^Z+co*>NM zB@|IJm%8b(rp^j=xfK3A=+QrA5G#kIs4ZLCQh&42?*8Hn*pKJCfni9y8*B|%;U}Wv z*dJq>FaiiwosESXSX>UdQ)O~<_a13;C+&C3jF;Kd@!05)`?zLxXA#EfU(B`? zHi#7M?)4-|TRjNeoIr&3CXkS2O5l}0Pb5q)7jC3-%I-z62gv=H|y_u!=*lEP>@BS8B zHPAM-S7~?MgBkh{k>yKim4TY|we?OFlDqMb&P`s+m)w_b^FK z_8GOjG&UzVg>@C(%Bmc2HPMXcs(b)N_AGxeg`w`fVKbM%1BYwbtDSAoiO1@XioX7j z(dvT1gq+CiHr*dS#II+MMB)ZbiwjHKGPG#!HrU#T3_EZk5>jokDG;5cU2Q z3=^Y|{{CERd0t0x;Xv{QcH2a6W;^ioT^?B%mE?b8^hl8GD0W^pM)wR2Zi0-fs*>!_ zZZ>A~Vt^Go2H53CqKF&EZnKW>@?0HOw)Vw#{~9Yl7yGXBIF=-KbMMrts6y!VtvBn+ z$)kSt&q|(Ts#QgSSjpW@L-l)aXmJ4U1pf6-l$1YnBE%mfG+F^Dw+Hy(xn%dI+L_+^W7`>X z5i6G~eV>k(5M#KJ_XG(CE8nUn?cYY{PKc~H7!+~~$iv1;Hb*?QPPMRhW$zf{&wca) zbL_}mi>B4*Ca$zj&rF&d$zKTAeLWiRNur)FoC=A>)opTe4@ub?}|(x^enh8-?FLJ>*B{bC)B9nBGA zhx>a%RuapVcHbx4W&h!CS5pD|>7erpZ80-?NC7;>Uioy95lG-fS6z1RJR9Vy;W2|@ zxf!@@y!c12l1#uRLE}cEND$`evFnXCwR7L9l%2;*djHfanE0Jpze+4#eblCWC&n&- zSeDp1`T50FlaDn9N6+KyqeC=`XP<01RUEocFg#0qIs)-H7$Wx^En@4Vhl(5b`>*Ia ze_1ll096)}r|!37wr4Kb=v{N@3V6M?>|gtgeDJ+IKJ1p9X~oAUmZ!Uvb3;H&uDUEO z$bS4jjXJ26$RrUYUbNw;YjU2pSUXIxHUBoaq6iSWyM(Ad)J%Rc6Rq4DTl*dD$IzEX z`f=Cnk9X%EWkRnS#z9V7{xHH61MJq|&&B6wl6JL(*+1Zf0&DF}X8p1!anP$qzLK}; zg?2hv=KWX5f_m$X+G{V?KU*jCqUx_9?8!wK5fsUh1D|xCeksN#uUhJ&oN+#rqE; zTQHN~1G1ud5I-Gy?&Sk-r&no`AV~qeq?Dnda07svpoEps#C1@wzMHoqUB58S@j z;fHTTW(<;TyHB)se$G)UNz-hwG^TLj`Lk4?yw*={aCCrz&W~=f+ABELLqB>m{&Gdt z58pW`w06BKi3BHT+8s=~&C0CK!_h^@)GJ3zbR;}wYv0Ck*PZzT`tVo|B^#dHs*73c z^v8sQVY~^Qb*!RaT5=Ka%nWUpy)g#&Ll`{#O)REeElLRE9Ur1!n3?uV=wvzhG3y}< zAKQ9#)V0&*%qfOjpBC0T<*@ijcK*BAeQLNW@yJ^pZYeCWE!#ZuxpbD#Tu!3qT^lIU z&dhT%Xt*+}t(u|aTG2(XBsbb}q?;yF->%qwltSmh=%7hOU+wFsY3=H=DXTI|*=quV z9-wu3TWr-pPmIIYC1U6nxe!Zs?pflTGVjIpmm%>7MSsS_Hg5|Bv*a@|{Z3@aW`082 zF`8FYMDmJ^VsG@P8rI5u@spgusEUSP1!b>ZX2O^HotUFSl(cw4Ptkb-NBqRt{7dL-sj>ljKo@Qv&EiW;Lrq00?O&p`jmWN$K23#cH zwX}IVf`i8Md_gfmr+pN3ylTJxKto;{VLSVju%8Z!K(+#&=RxSZzFpJ4s)-@OY^?Po z*X?AKS*(65VbA;Wx3d%4PkS|CjzPixq(l;2R{oCZh~G^X4#v;LK~5=s9iK$!tvJBG z|5AH`sTe5X^XYh+Z^VtyIrF;X=HX0!Dy-KsOTuYWPH%1zRfGfzr+PFe7ErGJLD?tF zjc{uD@y2CqRi`H;$te7D;T?Zll&)O;=#@Mc_+^vAiKQrx+7+?KIxw2PPh+yOrJ z6WacTo@KN_X>p^8gGmfFw@%NK1VB%pT|&oRlKHo%46Pxhy}mhdxO;UJ6tm$Z<8pLc z>0NF}u+vOa!a&C(+w#QH>VXf71+bI*lNr{mQ0G`c1aNP9&463qR^<*IaZzOKHNFtr z!jQ>q!$bM$E%Bw`CvR(e2qsqIU;udLg+toFT>GYXZsa7NFl&#e7Wdpd*0h6$<8zln z-nrte^ZiG-hqUf{@kucy%8S)Rl1l38%tGcDLR9>8%Ay1URm)$E8ik!)Nj>;6MP$gC zj3eRZ=aPqe6WkIS$-(t`!GQ$P*1(1(Bu;|+X`&+4W9Xdzi9=tPpU}9HGk?KM$j~ znMlw?^Hyd@+^^AD$)YaML-Dg)oWvFnDTt8sYyw*{A2{ur!enWS1WwL>?BL5^k+zMr z#f$q!`qM!rMh&Oj1WMGT3+pj-fmb3hDn(Jvn#v0BI+jG+^?*N>3e^ugNJ#TIpvuz1 z?3v8=tpeHD_#I4{`*ZO?8;j)A`JsIG?s(fjmL3fmmWI>bNTXzH60!QW-B6dohUh@U z3(H{!pD!4dbLvO*7%W$9hX$fIJFsi&)UQ8n>wS&N12*_NZ%ysdTLDXP{Q2qfF&*Ji zlt-8y`HAEL%8`9fBR|PU>xUf3VVaEF{njM6EC02vSx{L3&_7E29k4s~Th#mG_Z#5& zZ%(r8yhG;7IJdFl$XT!SPfw-s3lbOu_oVv%xPj9INE-9SV(<+c3WQbE+uolTK$r z8w(~+D?i(-JqOn7w<_-ab7zxio-H@pf|Djo-mE}El^-uc)HSagVi8I;(<*Bh$z%^P ze-n%(>e@|~6dcW2^76QXHFOLBb$%rg#YU7y0m2DE%}eK$5^hc$PtVr{{wfjRgeQZ4-POLBFJ) z^}8(k%f4q{yTS|-#u9frzH#t#ue1se!=1!YP#X0KV)vs{dvw^lY8PrI>JWZoI?oNE zPDHQ{Sl`_iI5}CoBrkCty*X7z^qQz`eP}N}9@`@oDt6ZwytZ|5F7hD-kZ>If^}$FuB8u&{?FS9w=tlY4quO?c-jJKJ%7h!274c7*J_raRpKrss@Q<+b<4Ezb znRCZ0sv*Rr7!OxfSp&hsX*dGo0cxmJLZG6M z)lrdRt>%JJQ(?EBvQ7z;g!Go;JG%u&>7xNd;B{ItphgTuKas%v6^SyGDiS7)4fqIf z<(9Q2xmdkDc*>Ol^B0IX&CpRg-q1EJJi2<#0qzp5MDaa%>x9GI21F*JtDFDn2-sXHHaOuNa5;b|W&8LBD+IE=&X3UbPi zW)+eAbkE6`+4-8`J>tPp7;JzfxI}~#x{4jmrdGac?MpeS&24K9bcX<=x71mfPri8M z|L?N3yIRKF*$Iq3%14GunJwwW=;yxq|F2QJun!3fa##B9Gxb_HFz!LnH_+ee(sr(6 zppWZUw9J##g@OoUDGmaj16-MKg=O_(O8otlUZaejr#DVj*6TDF5Y4{@!Wne+c_4-o zNoTHVvncFji>~-$zgs5@m#pDLIs0G}v9GNX%mi^#rJ@2|iN<#{!u1cH`!2*;{ql~G zXkFN2`=t=|=kKz%xCK*IXOjTl>W#^TRwss1Yp6e?%nwgHn!OFd6qs4JW^|5UU~-0T z{lKf!HfY5HD0aWUg0jc^aVfqb+_OhhrtPqR2Jxv&(evCLLl;Qv0ee>xeHUu=OAZPa z-(a46e#u0ij^HY@7aH6#k7qWlU;Z3Pg7(^l3FSk}FW!v8%pdU>8SO#R4cU#bn%}E? zzkcgY>c!U<2DmCKCO48*weM8fTsE^Ns!1|RQyXs^3YKPmqZF#F&Ae3YrETrK3S@1{ zO^w4&1l^4hy=~w%cGHPx*xdG@UKiWF2N7QTTMG_oHDWG!nMmwWov(Z(zbG{7g&lqo zr(>mF*+F!`@?%#5D&O)l=gv9byW0cr^Ki1u7xtr(v}*R1V!u3=d&G!B3g#+#(yPN; zpU9;mLgwUEr*6$mLO$U9Zs(R;fZreK>l}?SXhr*WMmWdO&Vuh7PFlYKrI$Ry%o`iv z8!I#fU5Gg<%Xf()>QaZFe?}n83mmEExm-TErk^F*Nqw2bJn={z*j2NYpJ6YLN%B4S zY+J6o+8XuBB}c5J&bpiF;jsA0<^k*%m2KZo02K*0NWGkRIem`0fPdIhS!deuZu9H! zzZVAO_)&;7YU!}{)GSC{K|XMR$gK!}3}Umf(aL(;G};yZzz@TS;%Q?PL}-u3UUUH> zAB55habX%cOG$jGbT7{`R$eZUV`4Xy1i`cf>G*Jkn5KP>?H0?%8Y2PpBGYmXf`)x4QQD)tPEQ%6>gL7|P_VQf3Ep?0>&wU%r z&U%_WQvAW$%f`$F4f)8h@us!uY{VZ5t{aVhHSAqFRA$7-gJjmwoT!qxr~PCT9k;p= z91gI7*p{sA?lQc^V&TL|BjxZaRw64jMHje&+3wQQU}aPRR}Cd_W3~OQzmjy+Wz&*- zjhyulyTllfnV2}wjDgjf34Ou>bs>r+*Y2ecI&~$06PvNC;bSJI@EZn?dEH1JVUUH@ zIQ0smcsw7_NCGu8jK4|#U)(mVz;8D~zWP@^UVmI6RCSS1SO?9pOei;cvfHN0kPG!S z*QDQRU(hK-84=lXyrcNka+zhJe8~GNfuERjjyR7sA`$7vW$4ooyqQS3h|ASp@z&oy zHd!DWNXoB~MO zocf?EysaC95gnun@uqY5p??iJ3-PeoPO(ANX%~5Xgb{2@v5~$hJn@inDyA1~2l7=| zytyvVg8X$qvxUabYTdoGK+=hkCm_v_RzAe$LD!l#3z~yH6n#yW#mR>KSR4r6s&M#&8Al?Uth96kYvop z&>@i58q@F)ofH<=`An?4flC=Sv3)&twqJ4fqs)kxD<`2)iCJ{?3c+5FaPGmqB#T6f z3h?+^ai;ijiB9Q!B&R9f#yY|50ya6l(@#!Vh4j>+21LGRkv(wfu%^L56=$MaK$RzHO5Bs zFBj4Y=RjAyBz3q(t%QCu+~$JjrpXFI?{`O4mE2a zY7x@7+l+gX1Grr3yWQBs)GhTlRR5#8p3s_6PnAOd*TLx z21&*w_qNnE&8X-@-in1H-Y58-D?S4Zt+V-ABe+qF>YziZRB#YYC{zVtB$QH0`Hs#t zNp$qgokl$yXGnmj2TrB9CQhj1NM`^6aDbB8Gc5ts^N`fck$L%qtokG+fSmKCTKAY66>FP?fCbbOiBlOoK^>Y9 zI-{1yWia{Z6_o9arGQUF7L!C6xD^ePjekD*zf?4)9P+W-jrq{ZI2+M(|8!~zD{T0%ZQR3x&8A)7VVE$Mel$pafRt#6Ta|H-L z>y|;j$Bi9!KAY;BUUjdS+67IO zN?afYWhRFxcKoAuFdJE^6oQ1Y*W0Szxh>e+r0GWEFSQ(Fw6 zm0B`mZZyq^qOe9*JG177yye+Bj!8Bvwj0Tj&3GhP^W??l-YHR`$GoP=#X;Pcd!8k% zow*&D9D-s&o`1Q7fdvc^J3tDxk1lhfBI4G$K5WR_)PLt^RfsFAIIkjE6aF*c!sIS_ ziBTcoJv$wjw3#u(ZftX<%bKiNkk%s!0@(4CnF{3E>**Z!T(MyRqCSZtfKfPJFCcpV z3ImbKr)CzpYwla0P=eg)=Z7ABbU2=m?B@EZ@pGf56{dxPxXGf9cut<>hMEHFDXIZQ z=IFK@D7L@Y;Uv*Ls=HwoU?iTXPeqQJm zKve~ip5>v6y3a7S!ilw~bggW_U24G6ou4*};0|o*Fn_J}kz$pYB(qlO(w2N&yfPyS zVf`_*im%@5PyO)5fZt<=Y>c8^IS9>WA_e+Pe1TbfuHh8~Xv8nwJI41KErS)wBRRn$ z+FaRmSre#!|C`$Qr_Fj=V+cDh$CdYhl_|N?w|DhaXG@M#{pCwgr^Icy`DTTcy;@;E zpW7Xrn9wk1cR32B_V3Qr_;?!^AVK7G2m~&a@BF>;PYHV)Em_lL_}nX+Y)O}W)$bJb zi)-uen9^6$_Pks#VI^d1UarmzCgbGe{kLg~`LDYVs@h_fq#4>fpS**zbwf=^S5tIC z<|#Je1qBN_f*_v%9L^v^7U+G_59j^|=lD_CPG<~i>~4kW$a1-W%&949r>h2?OYE(P zYO;CDlz>Loi)s5%5zviwViJO=Dfl-q2Q>q-@4ZPT=l6I!9Ih!x=}#Te9k8@o zaMw8?apuC`5DL?^9!WAKMxGazPA3?_A@h$A4}JPtv+OlV9?y--@9=rH+7uV953^06 zlXm?})TIQo$)^azjab+nM*&89QNQL$gyHGTtDoyEQ!1EoVJIp)1$1>%8jE-ExtQU~ zO7Q^=hKpfdz~X+)Y!NKo`-je$Kcm|V1=}$zg_@`U#nNa`LbHZU4aQ%T?m@hZA%M{1_>2EF zNMtbD$Y3WV?+db9vTE#| zMbk5qqF6(o3-P|CEEI2E!)-9!LU!Duo6Tzjri@(uZ{mJOp9i8LrkK~7Tw9jpG|ih2 zCJNU_=uSQ>e%CVY_n_uVf3^%#Ak{Z`pQ7=>!YwGiSr>>E&Vf}=+8Y&XXljn`?X|eM zZk?)VxM+pK`k=Ck;_9>)NwO( zLTikyh04w{IML#$5j~EF0k`r_E7zF5>o>d&gd@~H@k`Tf9oxkWD6d&c@@c`1z^~-z z%gdUVvN31T%uL7>hUwEaJC;OM+)Y~u=YJiLnArFU;c#}i<%9MpMv{WLfj(WC?{?W; z%9g&HX7j`sjd+;Cd_A7&AUcB(No;fT-wKqML?KWHH6g#}3y4BhS>GHGfq*aZ45nL& z8}+85fa>O@S8lZ27ffzsk5{d^ub;w;c#qrGeP6j+V$u~!vghY-_)|X2p&79tYx18w ztWnGJ)Ls4~GCRB0&t$`#C!lubtgxf~?q}EfO6Be&Zv#w!u@rdhjv$vLvvTNR zx*8L3l}qSW-POQvgC<%xUg@cGpTUU7?O^SIYvE(da~cn?&xoShLjZowUic1_i`R>9 zgoc$|pc`%vwjsns)~+6AUXM*?b4QHjF`Vt6BB*MjwjUB;=;^j%j^RdqvBXJWvMX_r zbX9~w@yZB)NmrinpX*0h+;ypd?8?zYWd`%Qz`RI#rxh&q}SAoH0 zvgn)&$RgSvtq*h%r=ATS>$9T}10PGSN~f3s)z#I0@$p`Xk1!H1kBC~bT)FLCy!^+z zOXX?#5cGF+3^K*WZZw z7*$)nU(F;OtUJ!b_?xIlT>ah2io7ica8J-46Qk{VsVjU$z%l;jjg+7>s024JFLPd0 z>~Tirxq5Sod{%fATFeFJ*Yi~0dhe7$!et(!eEIAT(NjS~ULCi3g8$XUI?Ojg2@=P-K96ds(Ph$e+TYiadDm$H}_+Rvb0?!4DV?ReUE=ctoz`UXS2jBn^OGxT@_|4 z$|i5{1#@!~aV^=S)s$SdN_=yJB#GJjm=vahae5Cx!hrpJ z%pRP?i-L;Jf5Ys5`>uydp!jKJlCq0~_!SVL+>cY58RY8%t|}!Z=m!01F}eYCIBIYr z9Ytd4;EhTimu{;n%4=srajeb%Vqsy^_I}SUno4B+g^|OsBB>XXSzWeS9cF&slQhH` zi=Y^+DbY8-1xg_;MP&=WtYECrIva}!uF;QVir7^1DyNB2o$Zv=}}lgg2n z47Mb0k{+tgM_nveiFg{tZcSw*5ON|oSnY|A5{-|~w|pVGPKyAsTcEYsR3?`6{r9P{ z?KaHm+P?4EuM4^tp3fG@PhE7Ky=LCt!e)baAo9vy;s}$yx)}F!&7Na`x&T#=zwCfM z@(PEKZLlH})ysxlpk~&q^5H4qSU6nFLFKq1j-f2 zg)Xj^yoF{bdh3tQw;-9`f1=r47$X{^pUwF=pzVqA64U)#5H$=Jl9TBrrxyQsKeaAh#?aT&od}&p*cl_^B}y& z05|VkU0v=ORIJb$d5-s*wT)0O3MJTTu3hx!yIjJ9kC~bh)LIQC4j`8^vqvS=PDqnVZANQ70bM(T1iDG;^L z7r-wuXKh-pNBiG6rH+CoU(KI03YSmY%#66dPs5eH5k zj`aF8X|LmYyE35_m@ZwMRPv(Bz{9KL=+z8ltg*BtlTE@@D~Y#e_#`Na zGyd*hvOEC8m*khp*G^Q@7v4l9~R{M9WSl93rxV$c>Uo1W? zlqfLP4$j9Va|-m?4Pag#b<8{w`_c`ji-|V+=d4kbI>I+CA-V458G2*A3taExbb-n* zS6DKnlNn`J$@#TYWUX;0iuXM@u6DM=!PAm~t!RCqOhUHn9V_zv6Q7Gfr(XQaAFI}Y zAmK&cbHKSl{Q425!R92T6kI#qVMR~;hM@+uw*!r=wmeQuvmncs~`)Ol+;9Q7$ zyizJlU(3bFQ>-qSJN>F?^#RO1&u8-O=$1Hkhjd4V5iaH&N?yH ze62BYlm)Yn*XFqp?;8S@2zCm9D~Lz0YWgTp<^YWDcbXwfgRnxOBVi~O{{;^j!!0FVx+YvHK7owGo3PtWEifGc3kb|GPMXEW};v|X?Z<4o}!da0*f~x4lEmbt1 z%~v|<#+MGhVX!$JVvkdE;sy?z7@F*sKzE%~_?yFHv*+O9%&^xJYK!pEqGLf@k}>Rl zF(+k!b*)iBEFae^AkC}*&hhNW-vraK(%y0?w{6M^X3LIHMNj5jjpf1m?xH7~!KD8l eJ=h~J!v6pOM>u`} literal 0 HcmV?d00001 diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 2302fd043..48b52fa7e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -48,11 +48,13 @@ account.injector.http=Warning: This server is using HTTP, which will cause your account.injector.server=Auth Server account.injector.server_url=Server URL account.injector.server_name=Server Name +account.manage=Account List account.methods=Login Type account.methods.authlib_injector=authlib-injector account.methods.offline=Offline account.methods.yggdrasil=Mojang -account.missing=None +account.missing=No Account +account.missing.add=Click the button on the right to add account.password=Password account.username=Name @@ -325,6 +327,7 @@ update.latest=This is latest Version. update.no_browser=Cannot open any browser. The link has been copied to the clipboard. Paste it to a browser address bar to update. update.tooltip=Update +version=Games version.cannot_read=Unable to gather the game version. Cannot continue auto-installing. version.forbidden_name=Forbidden name, do not use this. version.game.old=Old diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index baffe02dc..632f4d877 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -48,11 +48,13 @@ account.injector.http=警告:此伺服器使用不安全的 HTTP 協議,您 account.injector.server=認證伺服器 account.injector.server_url=伺服器位址 account.injector.server_name=伺服器名稱 +account.manage=帳戶列表 account.methods=登入方式 account.methods.authlib_injector=authlib-injector 登入 account.methods.offline=離線模式 account.methods.yggdrasil=正版登入 -account.missing=沒有帳戶 +account.missing=沒有遊戲帳戶 +account.missing.add=點擊右邊按鈕添加 account.password=密碼 account.username=使用者名稱 @@ -325,6 +327,7 @@ update.latest=目前版本為最新版本 update.no_browser=無法打開瀏覽器,網址已經複製到剪貼簿了,您可以手動複製網址打開頁面 update.tooltip=更新 +version=遊戲 version.cannot_read=讀取遊戲版本失敗,無法進行自動安裝 version.forbidden_name=此版本名稱不受支援,請換一個名字 version.game.old=老舊版本 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 8eee5b0d6..036e06861 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -48,11 +48,13 @@ account.injector.http=警告:此服务器使用不安全的 HTTP 协议,您 account.injector.server=认证服务器 account.injector.server_url=服务器地址 account.injector.server_name=服务器名称 +account.manage=账户列表 account.methods=登录方式 account.methods.authlib_injector=authlib-injector 登录 account.methods.offline=离线模式 account.methods.yggdrasil=正版登录 -account.missing=没有账户 +account.missing=没有游戏账户 +account.missing.add=点击右边按钮添加 account.password=密码 account.username=用户名 @@ -325,6 +327,7 @@ update.latest=当前版本为最新版本 update.no_browser=无法打开浏览器,网址已经复制到剪贴板了,您可以手动粘贴网址打开页面 update.tooltip=更新 +version=游戏 version.cannot_read=读取游戏版本失败,无法进行自动安装 version.forbidden_name=此版本名称不受支持,请换一个名字 version.game.old=远古版 From e9e8b8a1ad59ce5807ad03d554aa3f526c8c16c5 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:15:12 +0800 Subject: [PATCH 10/28] Change contact URL --- HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java b/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java index b9ce083ea..3018f51fa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java @@ -30,6 +30,6 @@ public final class Metadata { public static final String TITLE = NAME + " " + VERSION; public static final String UPDATE_URL = System.getProperty("hmcl.update_source.override", "https://hmcl.huangyuhui.net/api/update_link"); - public static final String CONTACT_URL = "https://www.huangyuhui.net/hmcl.php"; + public static final String CONTACT_URL = "https://hmcl.huangyuhui.net/contact"; public static final String PUBLISH_URL = "http://www.mcbbs.net/thread-142335-1-1.html"; } From aeb5eb91295ad3076ad64cf830051f61b446177b Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:19:49 +0800 Subject: [PATCH 11/28] clean up HMCL --- .../main/java/org/jackhuang/hmcl/game/AccountHelper.java | 4 ---- .../org/jackhuang/hmcl/game/HMCLGameDownloadTask.java | 2 -- .../java/org/jackhuang/hmcl/game/HMCLGameLauncher.java | 2 -- .../java/org/jackhuang/hmcl/game/HMCLGameRepository.java | 3 --- .../org/jackhuang/hmcl/game/HMCLModpackInstallTask.java | 1 - .../java/org/jackhuang/hmcl/ui/LeftPaneController.java | 8 -------- .../src/main/java/org/jackhuang/hmcl/ui/SettingsView.java | 2 -- HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java | 2 -- .../org/jackhuang/hmcl/ui/VersionSettingsController.java | 1 - .../jackhuang/hmcl/ui/account/AccountListViewModel.java | 3 --- .../jackhuang/hmcl/ui/animation/AnimationProducer.java | 1 - .../jackhuang/hmcl/ui/animation/ContainerAnimations.java | 3 --- .../org/jackhuang/hmcl/ui/construct/FontComboBox.java | 1 - .../hmcl/ui/download/DownloadWizardProvider.java | 1 - .../hmcl/ui/download/InstallerWizardProvider.java | 1 - .../java/org/jackhuang/hmcl/ui/download/VersionsPage.java | 2 -- .../jackhuang/hmcl/ui/export/ExportWizardProvider.java | 2 -- 17 files changed, 39 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java index faef60043..b576f0978 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java @@ -101,10 +101,6 @@ public final class AccountHelper { private final boolean refresh; private final List dependencies = new LinkedList<>(); - public SkinLoadTask(YggdrasilAccount account) { - this(account, false); - } - public SkinLoadTask(YggdrasilAccount account, boolean refresh) { this.account = account; this.refresh = refresh; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameDownloadTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameDownloadTask.java index e1e15a72d..83ad03709 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameDownloadTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameDownloadTask.java @@ -33,8 +33,6 @@ import java.util.List; import java.util.Optional; import java.util.logging.Level; -import static org.jackhuang.hmcl.setting.ConfigHolder.config; - /** * @author huangyuhui */ diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java index bf7bf4036..f586b2e29 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java @@ -21,9 +21,7 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.auth.AuthInfo; import org.jackhuang.hmcl.launch.DefaultLauncher; import org.jackhuang.hmcl.launch.ProcessListener; -import org.jackhuang.hmcl.util.CommandBuilder; -import java.util.List; import java.util.Map; /** diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 0033ae6bd..4d4ea3f04 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -34,8 +34,6 @@ import java.io.IOException; import java.util.*; import java.util.logging.Level; -import static org.jackhuang.hmcl.setting.ConfigHolder.config; - public class HMCLGameRepository extends DefaultGameRepository { private final Profile profile; private final Map versionSettings = new HashMap<>(); @@ -81,7 +79,6 @@ public class HMCLGameRepository extends DefaultGameRepository { @Override public File getLibraryFile(Version version, Library lib) { - VersionSetting vs = profile.getVersionSetting(version.getId()); File self = super.getLibraryFile(version, lib); if (Settings.instance().isCommonDirectoryDisabled() || self.exists()) return self; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java index b90162f04..f3a223794 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java @@ -35,7 +35,6 @@ import java.io.File; import java.io.IOException; import java.util.LinkedList; import java.util.List; -import java.util.Objects; public final class HMCLModpackInstallTask extends Task { private final File zipFile; 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 562d41b37..849f5ed63 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -26,7 +26,6 @@ import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; -import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.event.*; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ModpackHelper; @@ -46,7 +45,6 @@ import org.jackhuang.hmcl.util.Lang; import java.io.File; import java.util.LinkedList; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -54,7 +52,6 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class LeftPaneController { private final AdvancedListBox leftPane; private final VBox profilePane = new VBox(); - private final VBox accountPane = new VBox(); public LeftPaneController(AdvancedListBox leftPane) { this.leftPane = leftPane; @@ -98,11 +95,6 @@ public final class LeftPaneController { } // ==== Accounts ==== - private Optional getAccountFromItem(RipplerContainer accountItem) { - return Optional.ofNullable(accountItem.getProperties().get("account")) - .map(Account.class::cast); - } - public void checkAccount() { if (Accounts.getAccounts().isEmpty()) addNewAccount(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java index 9149bc294..810bf98ce 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.controls.*; -import javafx.fxml.FXML; import javafx.geometry.HPos; import javafx.geometry.Pos; import javafx.geometry.VPos; @@ -31,7 +30,6 @@ import org.jackhuang.hmcl.setting.EnumBackgroundImage; import org.jackhuang.hmcl.setting.EnumCommonDirectory; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.construct.*; -import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.i18n.I18n; public abstract class SettingsView extends StackPane { 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 28745ca8c..beab5bd96 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java @@ -27,9 +27,7 @@ import javafx.fxml.FXML; import javafx.scene.control.Tab; import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask; -import org.jackhuang.hmcl.setting.EnumGameDirectory; import org.jackhuang.hmcl.setting.Profile; -import org.jackhuang.hmcl.ui.export.ExportWizardProvider; import org.jackhuang.hmcl.ui.versions.Versions; import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.util.FileUtils; 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 aea799b94..62e3ae16e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java @@ -26,7 +26,6 @@ import javafx.beans.binding.Bindings; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; -import javafx.scene.control.Toggle; import javafx.scene.image.Image; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java index d952e2772..034cde399 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java @@ -26,11 +26,8 @@ import javafx.scene.control.ToggleGroup; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.ui.Controllers; -import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.util.MappedObservableList; -import java.util.Comparator; - import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; public class AccountListViewModel { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/AnimationProducer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/AnimationProducer.java index 5611ba16a..770f87810 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/AnimationProducer.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/AnimationProducer.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.ui.animation; import javafx.animation.KeyFrame; -import javafx.util.Duration; import java.util.List; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/ContainerAnimations.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/ContainerAnimations.java index 7a358f10e..8eaf67baa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/ContainerAnimations.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/ContainerAnimations.java @@ -20,14 +20,11 @@ package org.jackhuang.hmcl.ui.animation; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; -import javafx.scene.Node; import javafx.util.Duration; -import org.jackhuang.hmcl.util.Lang; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; 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 51d3fc115..2708b3f5a 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 @@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.construct; import com.jfoenix.controls.JFXComboBox; import javafx.beans.NamedArg; -import javafx.collections.FXCollections; import javafx.scene.control.ListCell; import javafx.scene.text.Font; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java index f5635bdc0..55eb136c2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java @@ -29,7 +29,6 @@ import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardProvider; -import org.jackhuang.hmcl.util.Lang; import java.io.File; import java.util.Map; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java index b5d08c8c5..c98451914 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java @@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.download; import javafx.scene.Node; -import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index dfc810060..6e4ff09d1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -27,7 +27,6 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.download.VersionList; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.FXUtils; @@ -39,7 +38,6 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.stream.Collectors; public final class VersionsPage extends StackPane implements WizardPage, Refreshable { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java index 4a9ee699e..93469c1e4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java @@ -79,8 +79,6 @@ public final class ExportWizardProvider implements WizardProvider { if (includeLauncher) { dependency = dependency.then(Task.of(() -> { - boolean flag = true; - try (Zipper zip = new Zipper(modpackFile.toPath())) { Config exported = new Config(); exported.setBackgroundImageType(config().getBackgroundImageType()); From 93cabde9f447ef29fc021b407eeb9a037d004acb Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:20:18 +0800 Subject: [PATCH 12/28] clean up HMCLCore --- .../java/org/jackhuang/hmcl/auth/AccountBuilder.java | 8 ++++---- .../hmcl/download/game/GameAssetDownloadTask.java | 4 ---- .../jackhuang/hmcl/download/game/GameVersionList.java | 1 - .../hmcl/download/game/LibraryDownloadTask.java | 2 -- .../org/jackhuang/hmcl/event/ProcessStoppedEvent.java | 1 - .../org/jackhuang/hmcl/game/LibrariesDownloadInfo.java | 2 +- .../src/main/java/org/jackhuang/hmcl/game/Version.java | 10 +++++----- .../org/jackhuang/hmcl/launch/DefaultLauncher.java | 4 ---- .../org/jackhuang/hmcl/mod/ModpackInstallTask.java | 1 - .../org/jackhuang/hmcl/util/ExceptionalRunnable.java | 2 +- .../java/org/jackhuang/hmcl/util/IntVersionNumber.java | 1 - .../src/main/java/org/jackhuang/hmcl/util/Zipper.java | 2 -- 12 files changed, 11 insertions(+), 27 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountBuilder.java index 2e2ecfd93..2b865e409 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountBuilder.java @@ -28,22 +28,22 @@ public final class AccountBuilder { public AccountBuilder() { } - public AccountBuilder setSelector(CharacterSelector selector) { + public AccountBuilder setSelector(CharacterSelector selector) { this.selector = Objects.requireNonNull(selector); return this; } - public AccountBuilder setUsername(String username) { + public AccountBuilder setUsername(String username) { this.username = Objects.requireNonNull(username); return this; } - public AccountBuilder setPassword(String password) { + public AccountBuilder setPassword(String password) { this.password = password; return this; } - public AccountBuilder setAdditionalData(Object additionalData) { + public AccountBuilder setAdditionalData(Object additionalData) { this.additionalData = additionalData; return this; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java index d8940ca0e..f43b482ce 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java @@ -28,16 +28,12 @@ import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.NetworkUtils; import java.io.File; -import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; -import static org.jackhuang.hmcl.util.DigestUtils.digest; -import static org.jackhuang.hmcl.util.Hex.encodeHex; - /** * * @author huangyuhui diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVersionList.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVersionList.java index ced1afb49..97c4b51f5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVersionList.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVersionList.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.download.game; import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.download.VersionList; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java index 9c4c4d868..7cb061812 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java @@ -14,8 +14,6 @@ import org.tukaani.xz.XZInputStream; import java.io.*; import java.net.URL; import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/ProcessStoppedEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/ProcessStoppedEvent.java index 36c70856f..7786c13d5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/ProcessStoppedEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/ProcessStoppedEvent.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.event; import org.jackhuang.hmcl.util.ManagedProcess; -import org.jackhuang.hmcl.util.ToStringBuilder; /** * This event gets fired when minecraft process exited successfully and the exit code is 0. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LibrariesDownloadInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LibrariesDownloadInfo.java index e3b0e64f4..e898be8ff 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LibrariesDownloadInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LibrariesDownloadInfo.java @@ -47,7 +47,7 @@ public final class LibrariesDownloadInfo { } public Map getClassifiers() { - return classifiers == null ? Collections.EMPTY_MAP : Collections.unmodifiableMap(classifiers); + return classifiers == null ? Collections.emptyMap() : Collections.unmodifiableMap(classifiers); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java index c1af9e157..cd62e2978 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java @@ -116,15 +116,15 @@ public class Version implements Comparable, Validation { } public Map getLogging() { - return logging == null ? Collections.EMPTY_MAP : Collections.unmodifiableMap(logging); + return logging == null ? Collections.emptyMap() : Collections.unmodifiableMap(logging); } public List getLibraries() { - return libraries == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(libraries); + return libraries == null ? Collections.emptyList() : Collections.unmodifiableList(libraries); } public List getCompatibilityRules() { - return compatibilityRules == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(compatibilityRules); + return compatibilityRules == null ? Collections.emptyList() : Collections.unmodifiableList(compatibilityRules); } public DownloadInfo getDownloadInfo() { @@ -247,14 +247,14 @@ public class Version implements Comparable, Validation { if (StringUtils.isBlank(id)) throw new JsonParseException("Version ID cannot be blank"); if (downloads != null) - for (Map.Entry entry : downloads.entrySet()) { + for (Map.Entry entry : downloads.entrySet()) { if (!(entry.getKey() instanceof DownloadType)) throw new JsonParseException("Version downloads key must be DownloadType"); if (!(entry.getValue() instanceof DownloadInfo)) throw new JsonParseException("Version downloads value must be DownloadInfo"); } if (logging != null) - for (Map.Entry entry : logging.entrySet()) { + for (Map.Entry entry : logging.entrySet()) { if (!(entry.getKey() instanceof DownloadType)) throw new JsonParseException("Version logging key must be DownloadType"); if (!(entry.getValue() instanceof LoggingInfo)) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 1448dc485..e061ecb0d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -300,10 +300,6 @@ public class DefaultLauncher extends Launcher { throw new PermissionException(); } - private void startMonitors(ManagedProcess managedProcess, ProcessListener processListener) { - startMonitors(managedProcess, processListener, true); - } - private void startMonitors(ManagedProcess managedProcess, ProcessListener processListener, boolean isDaemon) { processListener.setProcess(managedProcess); Thread stdout = Lang.thread(new StreamPump(managedProcess.getProcess().getInputStream(), it -> { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java index 92e6d639f..ab3e4f8e6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java @@ -19,7 +19,6 @@ package org.jackhuang.hmcl.mod; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.FileUtils; -import org.jackhuang.hmcl.util.IOUtils; import org.jackhuang.hmcl.util.Unzipper; import java.io.File; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ExceptionalRunnable.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ExceptionalRunnable.java index 941b8cbb2..ed693979a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ExceptionalRunnable.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ExceptionalRunnable.java @@ -34,7 +34,7 @@ public interface ExceptionalRunnable { }; } - static ExceptionalRunnable fromRunnable(Runnable r) { + static ExceptionalRunnable fromRunnable(Runnable r) { return r::run; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/IntVersionNumber.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/IntVersionNumber.java index b6a19590b..b1171bdde 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/IntVersionNumber.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/IntVersionNumber.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.util; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Zipper.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Zipper.java index cfb57c817..ac3c4b695 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Zipper.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Zipper.java @@ -67,8 +67,6 @@ public final class Zipper implements Closeable { * @param filter returns false if you do not want that file or directory */ public void putDirectory(Path source, String targetDir, Predicate filter) throws IOException { - File[] files = null; - Path root = fs.getPath(targetDir); Files.createDirectories(root); Files.walkFileTree(source, new SimpleFileVisitor() { From bb1654828f52c9aef890e75d658efc31869277c4 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Fri, 31 Aug 2018 10:44:01 +0800 Subject: [PATCH 13/28] Change "Run Directory" to "Working Directory" --- .../main/resources/assets/fxml/version/version-settings.fxml | 2 +- HMCL/src/main/resources/assets/lang/I18N.properties | 4 ++-- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 4 ++-- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml b/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml index 01479693b..0e9742998 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml @@ -25,7 +25,7 @@ - diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 48b52fa7e..afa37c731 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -284,9 +284,9 @@ settings.game.exploration=Explore settings.game.fullscreen=Fullscreen settings.game.java_directory=Java Directory settings.game.java_directory.choose=Choose Java Directory. -settings.game.run_directory=Run Directory -settings.game.run_directory.choose=Choose Run Directory settings.game.management=Manage +settings.game.working_directory=Working Directory +settings.game.working_directory.choose=Choose Working Directory settings.icon=Game Icon diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 632f4d877..36cbcb687 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -284,9 +284,9 @@ settings.game.exploration=瀏覽 settings.game.fullscreen=全螢幕 settings.game.java_directory=Java 路徑 settings.game.java_directory.choose=選擇 Java 路徑 -settings.game.run_directory=運行路徑(版本隔離) -settings.game.run_directory.choose=選擇運行路徑 settings.game.management=管理 +settings.game.working_directory=運行路徑(版本隔離) +settings.game.working_directory.choose=選擇運行路徑 settings.icon=遊戲圖示 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 036e06861..41037eed9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -284,9 +284,9 @@ settings.game.exploration=浏览 settings.game.fullscreen=全屏 settings.game.java_directory=Java 路径 settings.game.java_directory.choose=选择 Java 路径 -settings.game.run_directory=运行路径(版本隔离) -settings.game.run_directory.choose=选择运行路径 settings.game.management=管理 +settings.game.working_directory=运行路径(版本隔离) +settings.game.working_directory.choose=选择运行路径 settings.icon=游戏图标 From 5efc8d6fa9e53d729be7669252fab76d8eac6659 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Fri, 31 Aug 2018 12:13:47 +0800 Subject: [PATCH 14/28] Refactor View and ViewModel to Control and Skin --- .../jackhuang/hmcl/ui/AdvancedListItem2.java | 103 +++++++----------- .../hmcl/ui/AdvancedListItemSkin.java | 102 +++++++++++++++++ .../hmcl/ui/AdvancedListItemViewModel.java | 38 ------- .../org/jackhuang/hmcl/ui/Controllers.java | 30 +++-- .../jackhuang/hmcl/ui/LeftPaneController.java | 16 ++- ...odel.java => AccountAdvancedListItem.java} | 11 +- ...untListViewModel.java => AccountList.java} | 32 ++++-- ...temViewModel.java => AccountListItem.java} | 11 +- ...ItemView.java => AccountListItemSkin.java} | 39 ++++--- ...ountListView.java => AccountListSkin.java} | 26 ++--- .../hmcl/ui/construct/AdvancedListBox.java | 3 +- ...ewModel.java => GameAdvancedListItem.java} | 12 +- .../{GameListViewModel.java => GameList.java} | 36 ++++-- ...stItemViewModel.java => GameListItem.java} | 11 +- ...istItemView.java => GameListItemSkin.java} | 53 +++++---- .../{GameListView.java => GameListSkin.java} | 39 +++---- 16 files changed, 320 insertions(+), 242 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java rename HMCL/src/main/java/org/jackhuang/hmcl/ui/account/{AccountAdvancedListItemViewModel.java => AccountAdvancedListItem.java} (91%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/account/{AccountListViewModel.java => AccountList.java} (68%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/account/{AccountListItemViewModel.java => AccountListItem.java} (93%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/account/{AccountListItemView.java => AccountListItemSkin.java} (72%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/account/{AccountListView.java => AccountListSkin.java} (72%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/{GameAdvancedListItemViewModel.java => GameAdvancedListItem.java} (90%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/{GameListViewModel.java => GameList.java} (78%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/{GameListItemViewModel.java => GameListItem.java} (94%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/{GameListItemView.java => GameListItemSkin.java} (73%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/{GameListView.java => GameListSkin.java} (78%) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java index bd091f404..1933ba7b3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java @@ -17,80 +17,59 @@ */ package org.jackhuang.hmcl.ui; -import com.jfoenix.controls.JFXButton; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.control.Label; -import javafx.scene.image.ImageView; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import javafx.scene.text.TextAlignment; -import org.jackhuang.hmcl.setting.Theme; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ObjectPropertyBase; +import javafx.beans.property.StringProperty; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.Rectangle2D; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; +import javafx.scene.image.Image; -import java.util.Optional; +public abstract class AdvancedListItem2 extends Control { -public class AdvancedListItem2 extends StackPane { + public abstract ObjectProperty imageProperty(); - public AdvancedListItem2(AdvancedListItemViewModel viewModel) { - BorderPane root = new BorderPane(); - root.setPickOnBounds(false); + public ObjectProperty viewportProperty() { + return null; + } - HBox left = new HBox(); - left.setAlignment(Pos.CENTER); - left.setMouseTransparent(true); + public abstract StringProperty titleProperty(); - StackPane imageViewContainer = new StackPane(); - FXUtils.setLimitWidth(imageViewContainer, 32); - FXUtils.setLimitHeight(imageViewContainer, 32); + public abstract StringProperty subtitleProperty(); - ImageView imageView = new ImageView(); - FXUtils.limitSize(imageView, 32, 32); - imageView.setPreserveRatio(true); - imageView.imageProperty().bind(viewModel.imageProperty()); - Optional.ofNullable(viewModel.viewportProperty()) - .ifPresent(imageView.viewportProperty()::bind); - imageViewContainer.getChildren().setAll(imageView); + public final ObjectProperty> onActionProperty() { + return onAction; + } - VBox vbox = new VBox(); - vbox.setAlignment(Pos.CENTER_LEFT); - vbox.setPadding(new Insets(0, 0, 0, 10)); + public final void setOnAction(EventHandler value) { + onActionProperty().set(value); + } - Label title = new Label(); - title.textProperty().bind(viewModel.titleProperty()); - title.setMaxWidth(90); - title.setStyle("-fx-font-size: 15;"); - title.setTextAlignment(TextAlignment.JUSTIFY); - vbox.getChildren().add(title); + public final EventHandler getOnAction() { + return onActionProperty().get(); + } - if (viewModel.subtitleProperty() != null) { - Label subtitle = new Label(); - subtitle.textProperty().bind(viewModel.subtitleProperty()); - subtitle.setMaxWidth(90); - subtitle.setStyle("-fx-font-size: 10;"); - subtitle.setTextAlignment(TextAlignment.JUSTIFY); - vbox.getChildren().add(subtitle); + private ObjectProperty> onAction = new ObjectPropertyBase>() { + @Override + protected void invalidated() { + setEventHandler(ActionEvent.ACTION, get()); } - left.getChildren().setAll(imageViewContainer, vbox); - root.setLeft(left); + @Override + public Object getBean() { + return AdvancedListItem2.this; + } - HBox right = new HBox(); - right.setAlignment(Pos.CENTER); - right.setPickOnBounds(false); + @Override + public String getName() { + return "onAction"; + } + }; - JFXButton settings = new JFXButton(); - FXUtils.setLimitWidth(settings, 40); - settings.setOnMouseClicked(e -> viewModel.action()); - settings.getStyleClass().setAll("toggle-icon4"); - settings.setGraphic(SVG.gear(Theme.blackFillBinding(), -1, -1)); - right.getChildren().setAll(settings); - root.setRight(right); - - setStyle("-fx-padding: 10 16 10 16;"); - getStyleClass().setAll("transparent"); - setPickOnBounds(false); - getChildren().setAll(root); + @Override + protected Skin createDefaultSkin() { + return new AdvancedListItemSkin(this); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java new file mode 100644 index 000000000..3aa303374 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java @@ -0,0 +1,102 @@ +/* + * 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 com.jfoenix.controls.JFXButton; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.SkinBase; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.text.TextAlignment; +import org.jackhuang.hmcl.setting.Theme; + +import java.util.Optional; + +public class AdvancedListItemSkin extends SkinBase { + + public AdvancedListItemSkin(AdvancedListItem2 skinnable) { + super(skinnable); + + StackPane stackPane = new StackPane(); + + BorderPane root = new BorderPane(); + root.setPickOnBounds(false); + + HBox left = new HBox(); + left.setAlignment(Pos.CENTER); + left.setMouseTransparent(true); + + StackPane imageViewContainer = new StackPane(); + FXUtils.setLimitWidth(imageViewContainer, 32); + FXUtils.setLimitHeight(imageViewContainer, 32); + + ImageView imageView = new ImageView(); + FXUtils.limitSize(imageView, 32, 32); + imageView.setPreserveRatio(true); + imageView.imageProperty().bind(skinnable.imageProperty()); + Optional.ofNullable(skinnable.viewportProperty()) + .ifPresent(imageView.viewportProperty()::bind); + imageViewContainer.getChildren().setAll(imageView); + + VBox vbox = new VBox(); + vbox.setAlignment(Pos.CENTER_LEFT); + vbox.setPadding(new Insets(0, 0, 0, 10)); + + Label title = new Label(); + title.textProperty().bind(skinnable.titleProperty()); + title.setMaxWidth(90); + title.setStyle("-fx-font-size: 15;"); + title.setTextAlignment(TextAlignment.JUSTIFY); + vbox.getChildren().add(title); + + if (skinnable.subtitleProperty() != null) { + Label subtitle = new Label(); + subtitle.textProperty().bind(skinnable.subtitleProperty()); + subtitle.setMaxWidth(90); + subtitle.setStyle("-fx-font-size: 10;"); + subtitle.setTextAlignment(TextAlignment.JUSTIFY); + vbox.getChildren().add(subtitle); + } + + left.getChildren().setAll(imageViewContainer, vbox); + root.setLeft(left); + + HBox right = new HBox(); + right.setAlignment(Pos.CENTER); + right.setPickOnBounds(false); + + JFXButton settings = new JFXButton(); + FXUtils.setLimitWidth(settings, 40); + settings.getStyleClass().setAll("toggle-icon4"); + settings.setGraphic(SVG.gear(Theme.blackFillBinding(), -1, -1)); + right.getChildren().setAll(settings); + root.setRight(right); + + stackPane.setStyle("-fx-padding: 10 16 10 16;"); + stackPane.getStyleClass().setAll("transparent"); + stackPane.setPickOnBounds(false); + stackPane.getChildren().setAll(root); + + getChildren().setAll(stackPane); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java deleted file mode 100644 index ffb1fa339..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.property.ObjectProperty; -import javafx.beans.property.StringProperty; -import javafx.geometry.Rectangle2D; -import javafx.scene.image.Image; - -public abstract class AdvancedListItemViewModel { - - public abstract void action(); - - public abstract ObjectProperty imageProperty(); - - public ObjectProperty viewportProperty() { - return null; - } - - public abstract StringProperty titleProperty(); - - public abstract StringProperty subtitleProperty(); -} 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 a70dc1b0f..9834cb62f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -27,15 +27,13 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; -import org.jackhuang.hmcl.ui.account.AccountListView; -import org.jackhuang.hmcl.ui.account.AccountListViewModel; +import org.jackhuang.hmcl.ui.account.AccountList; import org.jackhuang.hmcl.ui.account.AuthlibInjectorServersPage; import org.jackhuang.hmcl.ui.construct.InputDialogPane; import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; -import org.jackhuang.hmcl.ui.versions.GameListView; -import org.jackhuang.hmcl.ui.versions.GameListViewModel; +import org.jackhuang.hmcl.ui.versions.GameList; import org.jackhuang.hmcl.util.FutureCallback; import org.jackhuang.hmcl.util.JavaVersion; @@ -50,8 +48,8 @@ public final class Controllers { private static MainPage mainPage = null; private static SettingsPage settingsPage = null; private static VersionPage versionPage = null; - private static GameListView gameListView = null; - private static AccountListView accountListView = null; + private static GameList gameListPage = null; + private static AccountList accountListPage = null; private static AuthlibInjectorServersPage serversPage = null; private static LeftPaneController leftPaneController; private static Decorator decorator; @@ -72,17 +70,17 @@ public final class Controllers { } // FXThread - public static GameListView getGameListView() { - if (gameListView == null) - gameListView = new GameListView(new GameListViewModel()); - return gameListView; + public static GameList getGameListPage() { + if (gameListPage == null) + gameListPage = new GameList(); + return gameListPage; } // FXThread - public static AccountListView getAccountListView() { - if (accountListView == null) - accountListView = new AccountListView(new AccountListViewModel()); - return accountListView; + public static AccountList getAccountListPage() { + if (accountListPage == null) + accountListPage = new AccountList(); + return accountListPage; } // FXThread @@ -202,7 +200,7 @@ public final class Controllers { decorator = null; stage = null; scene = null; - gameListView = null; - accountListView = null; + gameListPage = null; + accountListPage = null; } } 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 849f5ed63..9b2d143b2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -25,8 +25,10 @@ import javafx.scene.Node; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; - -import org.jackhuang.hmcl.event.*; +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; import org.jackhuang.hmcl.mod.Modpack; @@ -35,10 +37,10 @@ import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; -import org.jackhuang.hmcl.ui.account.AccountAdvancedListItemViewModel; +import org.jackhuang.hmcl.ui.account.AccountAdvancedListItem; import org.jackhuang.hmcl.ui.account.AddAccountPane; import org.jackhuang.hmcl.ui.construct.*; -import org.jackhuang.hmcl.ui.versions.GameAdvancedListItemViewModel; +import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.Lang; @@ -56,8 +58,10 @@ public final class LeftPaneController { public LeftPaneController(AdvancedListBox leftPane) { this.leftPane = leftPane; - AdvancedListItem2 accountListItem = new AdvancedListItem2(new AccountAdvancedListItemViewModel()); - AdvancedListItem2 gameListItem = new AdvancedListItem2(new GameAdvancedListItemViewModel()); + AdvancedListItem2 accountListItem = new AccountAdvancedListItem(); + accountListItem.setOnAction(e -> Controllers.navigate(Controllers.getAccountListPage())); + AdvancedListItem2 gameListItem = new GameAdvancedListItem(); + gameListItem.setOnAction(e -> Controllers.navigate(Controllers.getGameListPage())); IconedItem launcherSettingsItem = new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java similarity index 91% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java index 8110af52b..17dd802a3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java @@ -29,19 +29,18 @@ import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.game.AccountHelper; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.ui.AdvancedListItemViewModel; -import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.AdvancedListItem2; import org.jackhuang.hmcl.ui.FXUtils; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class AccountAdvancedListItemViewModel extends AdvancedListItemViewModel { +public class AccountAdvancedListItem extends AdvancedListItem2 { private final ObjectProperty image = new SimpleObjectProperty<>(); private final ObjectProperty viewport = new SimpleObjectProperty<>(AccountHelper.getViewport(4)); private final StringProperty title = new SimpleStringProperty(); private final StringProperty subtitle = new SimpleStringProperty(); - public AccountAdvancedListItemViewModel() { + public AccountAdvancedListItem() { FXUtils.onChangeAndOperate(Accounts.selectedAccountProperty(), account -> { if (account == null) { @@ -65,8 +64,8 @@ public class AccountAdvancedListItemViewModel extends AdvancedListItemViewModel } @Override - public void action() { - Controllers.navigate(Controllers.getAccountListView()); + protected void layoutChildren() { + super.layoutChildren(); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountList.java similarity index 68% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountList.java index 034cde399..e2293af2c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountList.java @@ -17,21 +17,23 @@ */ package org.jackhuang.hmcl.ui.account; -import javafx.beans.property.ListProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleListProperty; -import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.*; import javafx.collections.FXCollections; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; import javafx.scene.control.ToggleGroup; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.util.MappedObservableList; import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class AccountListViewModel { - private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()); +public class AccountList extends Control implements DecoratorPage { + private final StringProperty title = new SimpleStringProperty(i18n("account.manage")); + private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()); private ObjectProperty selectedAccount = new SimpleObjectProperty() { { items.addListener(onInvalidating(this::invalidated)); @@ -46,25 +48,35 @@ public class AccountListViewModel { private ToggleGroup toggleGroup; - public AccountListViewModel() { + public AccountList() { toggleGroup = new ToggleGroup(); items.bindContent(MappedObservableList.create( Accounts.accountsProperty(), - account -> new AccountListItemViewModel(toggleGroup, account))); + account -> new AccountListItem(toggleGroup, account))); selectedAccount.bindBidirectional(Accounts.selectedAccountProperty()); toggleGroup.selectedToggleProperty().addListener((o, a, toggle) -> { if (toggle == null || toggle.getUserData() == null) return; - selectedAccount.set(((AccountListItemViewModel) toggle.getUserData()).getAccount()); + selectedAccount.set(((AccountListItem) toggle.getUserData()).getAccount()); }); } + @Override + protected Skin createDefaultSkin() { + return new AccountListSkin(this); + } + public void addNewAccount() { Controllers.dialog(new AddAccountPane()); } - public ListProperty itemsProperty() { + public ListProperty itemsProperty() { return items; } + + @Override + public StringProperty titleProperty() { + return title; + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java similarity index 93% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java index c28e107df..fe46ed324 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java @@ -19,6 +19,8 @@ package org.jackhuang.hmcl.ui.account; import javafx.beans.property.*; import javafx.geometry.Rectangle2D; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; import javafx.scene.control.ToggleGroup; import javafx.scene.image.Image; import org.jackhuang.hmcl.auth.Account; @@ -32,7 +34,7 @@ import org.jackhuang.hmcl.task.Schedulers; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class AccountListItemViewModel { +public class AccountListItem extends Control { private final Account account; private final ToggleGroup toggleGroup; private final StringProperty title = new SimpleStringProperty(); @@ -41,7 +43,7 @@ public class AccountListItemViewModel { private final ObjectProperty image = new SimpleObjectProperty<>(); private final ObjectProperty viewport = new SimpleObjectProperty<>(); - public AccountListItemViewModel(ToggleGroup toggleGroup, Account account) { + public AccountListItem(ToggleGroup toggleGroup, Account account) { this.account = account; this.toggleGroup = toggleGroup; @@ -67,6 +69,11 @@ public class AccountListItemViewModel { } } + @Override + protected Skin createDefaultSkin() { + return new AccountListItemSkin(this); + } + public ToggleGroup getToggleGroup() { return toggleGroup; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java similarity index 72% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java index 8728c20df..148e16d7a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java @@ -22,6 +22,7 @@ import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXRadioButton; import com.jfoenix.effects.JFXDepthManager; import javafx.geometry.Pos; +import javafx.scene.control.SkinBase; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; @@ -33,15 +34,19 @@ import org.jackhuang.hmcl.ui.TwoLineListItem; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class AccountListItemView extends BorderPane { +public class AccountListItemSkin extends SkinBase { + + public AccountListItemSkin(AccountListItem skinnable) { + super(skinnable); + + BorderPane root = new BorderPane(); - public AccountListItemView(AccountListItemViewModel viewModel) { JFXRadioButton chkSelected = new JFXRadioButton(); BorderPane.setAlignment(chkSelected, Pos.CENTER); - chkSelected.setUserData(viewModel); - chkSelected.selectedProperty().bindBidirectional(viewModel.selectedProperty()); - chkSelected.setToggleGroup(viewModel.getToggleGroup()); - setLeft(chkSelected); + chkSelected.setUserData(skinnable); + chkSelected.selectedProperty().bindBidirectional(skinnable.selectedProperty()); + chkSelected.setToggleGroup(skinnable.getToggleGroup()); + root.setLeft(chkSelected); HBox center = new HBox(); center.setSpacing(8); @@ -53,36 +58,38 @@ public class AccountListItemView extends BorderPane { ImageView imageView = new ImageView(); FXUtils.limitSize(imageView, 32, 32); - imageView.imageProperty().bind(viewModel.imageProperty()); - imageView.viewportProperty().bind(viewModel.viewportProperty()); + imageView.imageProperty().bind(skinnable.imageProperty()); + imageView.viewportProperty().bind(skinnable.viewportProperty()); imageViewContainer.getChildren().setAll(imageView); TwoLineListItem item = new TwoLineListItem(); BorderPane.setAlignment(item, Pos.CENTER); center.getChildren().setAll(imageView, item); - setCenter(center); + root.setCenter(center); HBox right = new HBox(); right.setAlignment(Pos.CENTER_RIGHT); JFXButton btnRefresh = new JFXButton(); - btnRefresh.setOnMouseClicked(e -> viewModel.refresh()); + btnRefresh.setOnMouseClicked(e -> skinnable.refresh()); btnRefresh.getStyleClass().add("toggle-icon4"); btnRefresh.setGraphic(SVG.refresh(Theme.blackFillBinding(), -1, -1)); JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnRefresh, i18n("button.refresh"))); right.getChildren().add(btnRefresh); JFXButton btnRemove = new JFXButton(); - btnRemove.setOnMouseClicked(e -> viewModel.remove()); + btnRemove.setOnMouseClicked(e -> skinnable.remove()); btnRemove.getStyleClass().add("toggle-icon4"); BorderPane.setAlignment(btnRemove, Pos.CENTER); btnRemove.setGraphic(SVG.delete(Theme.blackFillBinding(), -1, -1)); JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnRefresh, i18n("button.delete"))); right.getChildren().add(btnRemove); - setRight(right); + root.setRight(right); - setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;"); - JFXDepthManager.setDepth(this, 1); - item.titleProperty().bind(viewModel.titleProperty()); - item.subtitleProperty().bind(viewModel.subtitleProperty()); + root.setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;"); + JFXDepthManager.setDepth(root, 1); + item.titleProperty().bind(skinnable.titleProperty()); + item.subtitleProperty().bind(skinnable.subtitleProperty()); + + getChildren().setAll(root); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListSkin.java similarity index 72% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListSkin.java index 8e0cc566d..8173b067e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListSkin.java @@ -19,25 +19,23 @@ package org.jackhuang.hmcl.ui.account; import com.jfoenix.controls.JFXButton; import javafx.beans.binding.Bindings; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.ScrollPane; +import javafx.scene.control.SkinBase; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; -import org.jackhuang.hmcl.util.MappedObservableList; -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; +public class AccountListSkin extends SkinBase { -public class AccountListView extends StackPane implements DecoratorPage { - private final StringProperty title = new SimpleStringProperty(i18n("account.manage")); + public AccountListSkin(AccountList skinnable) { + super(skinnable); + + StackPane root = new StackPane(); - public AccountListView(AccountListViewModel viewModel) { ScrollPane scrollPane = new ScrollPane(); { scrollPane.setFitToWidth(true); @@ -47,8 +45,7 @@ public class AccountListView extends StackPane implements DecoratorPage { accountList.setSpacing(10); accountList.setStyle("-fx-padding: 10 10 10 10;"); - Bindings.bindContent(accountList.getChildren(), - MappedObservableList.create(viewModel.itemsProperty(), AccountListItemView::new)); + Bindings.bindContent(accountList.getChildren(), skinnable.itemsProperty()); scrollPane.setContent(accountList); } @@ -66,16 +63,13 @@ public class AccountListView extends StackPane implements DecoratorPage { btnAdd.getStyleClass().setAll("jfx-button-raised-round"); btnAdd.setButtonType(JFXButton.ButtonType.RAISED); btnAdd.setGraphic(SVG.plus(Theme.whiteFillBinding(), -1, -1)); - btnAdd.setOnMouseClicked(e -> viewModel.addNewAccount()); + btnAdd.setOnMouseClicked(e -> skinnable.addNewAccount()); vBox.getChildren().setAll(btnAdd); } - getChildren().setAll(scrollPane, vBox); - } + root.getChildren().setAll(scrollPane, vBox); - @Override - public StringProperty titleProperty() { - return title; + getChildren().setAll(root); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListBox.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListBox.java index 2cad4d23c..3b1ea32a6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListBox.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListBox.java @@ -23,6 +23,7 @@ import javafx.scene.control.ScrollPane; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; +import org.jackhuang.hmcl.ui.AdvancedListItem2; import org.jackhuang.hmcl.ui.FXUtils; public class AdvancedListBox extends ScrollPane { @@ -42,7 +43,7 @@ public class AdvancedListBox extends ScrollPane { } public AdvancedListBox add(Node child) { - if (child instanceof Pane) + if (child instanceof Pane || child instanceof AdvancedListItem2) container.getChildren().add(child); else { StackPane pane = new StackPane(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java similarity index 90% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItemViewModel.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java index f4581c0f0..ddeb7bf1f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItemViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java @@ -29,13 +29,12 @@ import org.jackhuang.hmcl.event.ProfileChangedEvent; import org.jackhuang.hmcl.event.RefreshedVersionsEvent; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Settings; -import org.jackhuang.hmcl.ui.AdvancedListItemViewModel; -import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.AdvancedListItem2; import org.jackhuang.hmcl.ui.WeakListenerHelper; import java.io.File; -public class GameAdvancedListItemViewModel extends AdvancedListItemViewModel { +public class GameAdvancedListItem extends AdvancedListItem2 { private final ObjectProperty image = new SimpleObjectProperty<>(); private final StringProperty title = new SimpleStringProperty(); private final WeakListenerHelper helper = new WeakListenerHelper(); @@ -43,7 +42,7 @@ public class GameAdvancedListItemViewModel extends AdvancedListItemViewModel { private Profile profile; private InvalidationListener listener = o -> loadVersion(); - public GameAdvancedListItemViewModel() { + public GameAdvancedListItem() { helper.add(EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).registerWeak(event -> { JFXUtilities.runInFX(() -> loadProfile(event.getProfile())); })); @@ -77,11 +76,6 @@ public class GameAdvancedListItemViewModel extends AdvancedListItemViewModel { title.set(version); } - @Override - public void action() { - Controllers.navigate(Controllers.getGameListView()); - } - @Override public ObjectProperty imageProperty() { return image; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java similarity index 78% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListViewModel.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java index c54d64bc7..346f42c80 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java @@ -18,11 +18,10 @@ package org.jackhuang.hmcl.ui.versions; import com.jfoenix.concurrency.JFXUtilities; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.ListProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.*; import javafx.collections.FXCollections; +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; @@ -33,21 +32,24 @@ import org.jackhuang.hmcl.setting.Profile; 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; import org.jackhuang.hmcl.util.VersionNumber; +import org.jackhuang.hmcl.util.i18n.I18n; import java.util.List; import java.util.stream.Collectors; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class GameListViewModel { +public class GameList extends Control implements DecoratorPage { + private final StringProperty title = new SimpleStringProperty(I18n.i18n("version.manage")); private final BooleanProperty loading = new SimpleBooleanProperty(true); - private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()); + private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()); private Profile profile; private ToggleGroup toggleGroup; - public GameListViewModel() { + public GameList() { EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> { if (event.getSource() == profile.getRepository()) loadVersions((HMCLGameRepository) event.getSource()); @@ -69,24 +71,29 @@ public class GameListViewModel { private void loadVersions(HMCLGameRepository repository) { toggleGroup = new ToggleGroup(); - List children = repository.getVersions().parallelStream() + 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 -> new GameListItemViewModel(toggleGroup, profile, version.getId())) + .map(version -> new GameListItem(toggleGroup, profile, version.getId())) .collect(Collectors.toList()); JFXUtilities.runInFX(() -> { if (profile == repository.getProfile()) { loading.set(false); items.setAll(children); - children.forEach(GameListItemViewModel::checkSelection); + children.forEach(GameListItem::checkSelection); } toggleGroup.selectedToggleProperty().addListener((o, a, toggle) -> { - GameListItemViewModel model = (GameListItemViewModel) toggle.getUserData(); + GameListItem model = (GameListItem) toggle.getUserData(); model.getProfile().setSelectedVersion(model.getVersion()); }); }); } + @Override + protected Skin createDefaultSkin() { + return new GameListSkin(this); + } + public void addNewGame() { Controllers.getDecorator().startWizard(new DownloadWizardProvider(0), i18n("install.new_game")); } @@ -103,11 +110,16 @@ public class GameListViewModel { // Controllers.navigate(); } + @Override + public StringProperty titleProperty() { + return title; + } + public BooleanProperty loadingProperty() { return loading; } - public ListProperty itemsProperty() { + public ListProperty itemsProperty() { return items; } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java similarity index 94% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemViewModel.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java index 7cee5029a..e8f5f1c5b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java @@ -18,6 +18,8 @@ package org.jackhuang.hmcl.ui.versions; import javafx.beans.property.*; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; import javafx.scene.control.ToggleGroup; import javafx.scene.image.Image; import org.jackhuang.hmcl.download.LibraryAnalyzer; @@ -31,7 +33,7 @@ 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 class GameListItemViewModel { +public class GameListItem extends Control { private final Profile profile; private final String version; private final boolean isModpack; @@ -41,7 +43,7 @@ public class GameListItemViewModel { private final BooleanProperty selected = new SimpleBooleanProperty(); private final ObjectProperty image = new SimpleObjectProperty<>(); - public GameListItemViewModel(ToggleGroup toggleGroup, Profile profile, String id) { + public GameListItem(ToggleGroup toggleGroup, Profile profile, String id) { this.profile = profile; this.version = id; this.toggleGroup = toggleGroup; @@ -66,6 +68,11 @@ public class GameListItemViewModel { image.set(new Image("/assets/img/grass.png")); } + @Override + protected Skin createDefaultSkin() { + return new GameListItemSkin(this); + } + public ToggleGroup getToggleGroup() { return toggleGroup; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemSkin.java similarity index 73% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemView.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemSkin.java index 42d6e26c2..99b43a860 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemSkin.java @@ -21,6 +21,7 @@ import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.*; import com.jfoenix.effects.JFXDepthManager; import javafx.geometry.Pos; +import javafx.scene.control.SkinBase; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; @@ -32,15 +33,19 @@ import org.jackhuang.hmcl.ui.TwoLineListItem; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class GameListItemView extends BorderPane { +public class GameListItemSkin extends SkinBase { + + public GameListItemSkin(GameListItem skinnable) { + super(skinnable); + + BorderPane root = new BorderPane(); - public GameListItemView(GameListItemViewModel viewModel) { JFXRadioButton chkSelected = new JFXRadioButton(); BorderPane.setAlignment(chkSelected, Pos.CENTER); - chkSelected.setUserData(viewModel); - chkSelected.selectedProperty().bindBidirectional(viewModel.selectedProperty()); - chkSelected.setToggleGroup(viewModel.getToggleGroup()); - setLeft(chkSelected); + chkSelected.setUserData(skinnable); + chkSelected.selectedProperty().bindBidirectional(skinnable.selectedProperty()); + chkSelected.setToggleGroup(skinnable.getToggleGroup()); + root.setLeft(chkSelected); HBox center = new HBox(); center.setSpacing(8); @@ -52,13 +57,13 @@ public class GameListItemView extends BorderPane { ImageView imageView = new ImageView(); FXUtils.limitSize(imageView, 32, 32); - imageView.imageProperty().bind(viewModel.imageProperty()); + imageView.imageProperty().bind(skinnable.imageProperty()); imageViewContainer.getChildren().setAll(imageView); TwoLineListItem item = new TwoLineListItem(); BorderPane.setAlignment(item, Pos.CENTER); center.getChildren().setAll(imageView, item); - setCenter(center); + root.setCenter(center); JFXListView menu = new JFXListView<>(); menu.getItems().setAll( @@ -74,34 +79,34 @@ public class GameListItemView extends BorderPane { popup.hide(); switch (menu.getSelectionModel().getSelectedIndex()) { case 0: - viewModel.modifyGameSettings(); + skinnable.modifyGameSettings(); break; case 1: - viewModel.rename(); + skinnable.rename(); break; case 2: - viewModel.remove(); + skinnable.remove(); break; case 3: - viewModel.export(); + skinnable.export(); break; case 4: - viewModel.browse(); + skinnable.browse(); break; case 5: - viewModel.launch(); + skinnable.launch(); break; case 6: - viewModel.generateLaunchScript(); + skinnable.generateLaunchScript(); break; } }); HBox right = new HBox(); right.setAlignment(Pos.CENTER_RIGHT); - if (viewModel.canUpdate()) { + if (skinnable.canUpdate()) { JFXButton btnUpgrade = new JFXButton(); - btnUpgrade.setOnMouseClicked(e -> viewModel.update()); + btnUpgrade.setOnMouseClicked(e -> skinnable.update()); btnUpgrade.getStyleClass().add("toggle-icon4"); btnUpgrade.setGraphic(SVG.update(Theme.blackFillBinding(), -1, -1)); JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnUpgrade, i18n("version.update"))); @@ -111,17 +116,19 @@ public class GameListItemView extends BorderPane { JFXButton btnManage = new JFXButton(); btnManage.setOnMouseClicked(e -> { menu.getSelectionModel().select(-1); - popup.show(this, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 0, this.getHeight()); + popup.show(root, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 0, root.getHeight()); }); btnManage.getStyleClass().add("toggle-icon4"); BorderPane.setAlignment(btnManage, Pos.CENTER); btnManage.setGraphic(SVG.dotsVertical(Theme.blackFillBinding(), -1, -1)); right.getChildren().add(btnManage); - setRight(right); + root.setRight(right); - setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;"); - JFXDepthManager.setDepth(this, 1); - item.titleProperty().bind(viewModel.titleProperty()); - item.subtitleProperty().bind(viewModel.subtitleProperty()); + root.setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;"); + JFXDepthManager.setDepth(root, 1); + item.titleProperty().bind(skinnable.titleProperty()); + item.subtitleProperty().bind(skinnable.subtitleProperty()); + + getChildren().setAll(root); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java similarity index 78% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListView.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java index 1bca1d9a8..f5b76e594 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java @@ -21,11 +21,10 @@ import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXSpinner; import com.jfoenix.effects.JFXDepthManager; import javafx.beans.binding.Bindings; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.control.ScrollPane; +import javafx.scene.control.SkinBase; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; @@ -33,12 +32,9 @@ import javafx.scene.layout.VBox; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; -import org.jackhuang.hmcl.util.MappedObservableList; import org.jackhuang.hmcl.util.i18n.I18n; -public class GameListView extends BorderPane implements DecoratorPage { - private final StringProperty title = new SimpleStringProperty(I18n.i18n("version.manage")); +public class GameListSkin extends SkinBase { private static Node wrap(Node node) { StackPane stackPane = new StackPane(); @@ -47,7 +43,11 @@ public class GameListView extends BorderPane implements DecoratorPage { return stackPane; } - public GameListView(GameListViewModel viewModel) { + public GameListSkin(GameList skinnable) { + super(skinnable); + + BorderPane root = new BorderPane(); + { HBox toolbar = new HBox(); toolbar.getStyleClass().setAll("jfx-tool-bar-second"); @@ -59,7 +59,7 @@ public class GameListView extends BorderPane implements DecoratorPage { btnAddNewGame.textFillProperty().bind(Theme.foregroundFillBinding()); btnAddNewGame.setGraphic(wrap(SVG.plus(Theme.foregroundFillBinding(), -1, -1))); btnAddNewGame.setText(I18n.i18n("install.new_game")); - btnAddNewGame.setOnMouseClicked(e -> viewModel.addNewGame()); + btnAddNewGame.setOnMouseClicked(e -> skinnable.addNewGame()); toolbar.getChildren().add(btnAddNewGame); JFXButton btnImportModpack = new JFXButton(); @@ -67,7 +67,7 @@ public class GameListView extends BorderPane implements DecoratorPage { btnImportModpack.textFillProperty().bind(Theme.foregroundFillBinding()); btnImportModpack.setGraphic(wrap(SVG.importIcon(Theme.foregroundFillBinding(), -1, -1))); btnImportModpack.setText(I18n.i18n("install.modpack")); - btnImportModpack.setOnMouseClicked(e -> viewModel.importModpack()); + btnImportModpack.setOnMouseClicked(e -> skinnable.importModpack()); toolbar.getChildren().add(btnImportModpack); JFXButton btnRefresh = new JFXButton(); @@ -75,7 +75,7 @@ public class GameListView extends BorderPane implements DecoratorPage { btnRefresh.textFillProperty().bind(Theme.foregroundFillBinding()); btnRefresh.setGraphic(wrap(SVG.refresh(Theme.foregroundFillBinding(), -1, -1))); btnRefresh.setText(I18n.i18n("button.refresh")); - btnRefresh.setOnMouseClicked(e -> viewModel.refresh()); + btnRefresh.setOnMouseClicked(e -> skinnable.refresh()); toolbar.getChildren().add(btnRefresh); JFXButton btnModify = new JFXButton(); @@ -83,10 +83,10 @@ public class GameListView extends BorderPane implements DecoratorPage { btnModify.textFillProperty().bind(Theme.foregroundFillBinding()); btnModify.setGraphic(wrap(SVG.gear(Theme.foregroundFillBinding(), -1, -1))); btnModify.setText(I18n.i18n("settings.type.global.manage")); - btnModify.setOnMouseClicked(e -> viewModel.modifyGlobalGameSettings()); + btnModify.setOnMouseClicked(e -> skinnable.modifyGlobalGameSettings()); toolbar.getChildren().add(btnModify); - setTop(toolbar); + root.setTop(toolbar); } { @@ -103,23 +103,16 @@ public class GameListView extends BorderPane implements DecoratorPage { gameList.setSpacing(10); gameList.setStyle("-fx-padding: 10 10 10 10;"); - Bindings.bindContent(gameList.getChildren(), - MappedObservableList.create(viewModel.itemsProperty(), model -> { - GameListItemView view = new GameListItemView(model); - return view; - })); + Bindings.bindContent(gameList.getChildren(), skinnable.itemsProperty()); scrollPane.setContent(gameList); - FXUtils.onChangeAndOperate(viewModel.loadingProperty(), + FXUtils.onChangeAndOperate(skinnable.loadingProperty(), loading -> center.getChildren().setAll(loading ? spinner : scrollPane)); - setCenter(center); + root.setCenter(center); } - } - @Override - public StringProperty titleProperty() { - return title; + getChildren().setAll(root); } } From b3d84c4f9920e6a65cc6f6911b32f5d54737a12d Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Fri, 31 Aug 2018 22:21:15 +0800 Subject: [PATCH 15/28] Refactor --- .../jackhuang/hmcl/ui/AdvancedListItem2.java | 29 ++++++---- .../hmcl/ui/AdvancedListItemSkin.java | 22 +++---- .../jackhuang/hmcl/ui/LeftPaneController.java | 3 +- .../ui/account/AccountAdvancedListItem.java | 57 +++++++------------ .../ui/versions/GameAdvancedListItem.java | 23 +------- 5 files changed, 55 insertions(+), 79 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java index 1933ba7b3..449a5fd76 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java @@ -17,27 +17,36 @@ */ package org.jackhuang.hmcl.ui; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ObjectPropertyBase; -import javafx.beans.property.StringProperty; +import javafx.beans.property.*; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Rectangle2D; import javafx.scene.control.Control; import javafx.scene.control.Skin; import javafx.scene.image.Image; +import org.jackhuang.hmcl.game.AccountHelper; -public abstract class AdvancedListItem2 extends Control { +public class AdvancedListItem2 extends Control { + private final ObjectProperty image = new SimpleObjectProperty<>(); + private final ObjectProperty viewport = new SimpleObjectProperty<>(); + private final StringProperty title = new SimpleStringProperty(); + private final StringProperty subtitle = new SimpleStringProperty(); - public abstract ObjectProperty imageProperty(); - - public ObjectProperty viewportProperty() { - return null; + public ObjectProperty imageProperty() { + return image; } - public abstract StringProperty titleProperty(); + public ObjectProperty viewportProperty() { + return viewport; + } - public abstract StringProperty subtitleProperty(); + public StringProperty titleProperty() { + return title; + } + + public StringProperty subtitleProperty() { + return subtitle; + } public final ObjectProperty> onActionProperty() { return onAction; 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 3aa303374..58747a4c6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java @@ -54,8 +54,7 @@ public class AdvancedListItemSkin extends SkinBase { FXUtils.limitSize(imageView, 32, 32); imageView.setPreserveRatio(true); imageView.imageProperty().bind(skinnable.imageProperty()); - Optional.ofNullable(skinnable.viewportProperty()) - .ifPresent(imageView.viewportProperty()::bind); + imageView.viewportProperty().bind(skinnable.viewportProperty()); imageViewContainer.getChildren().setAll(imageView); VBox vbox = new VBox(); @@ -69,14 +68,17 @@ public class AdvancedListItemSkin extends SkinBase { title.setTextAlignment(TextAlignment.JUSTIFY); vbox.getChildren().add(title); - if (skinnable.subtitleProperty() != null) { - Label subtitle = new Label(); - subtitle.textProperty().bind(skinnable.subtitleProperty()); - subtitle.setMaxWidth(90); - subtitle.setStyle("-fx-font-size: 10;"); - subtitle.setTextAlignment(TextAlignment.JUSTIFY); - vbox.getChildren().add(subtitle); - } + Label subtitle = new Label(); + subtitle.textProperty().bind(skinnable.subtitleProperty()); + subtitle.setMaxWidth(90); + subtitle.setStyle("-fx-font-size: 10;"); + subtitle.setTextAlignment(TextAlignment.JUSTIFY); + vbox.getChildren().add(subtitle); + + FXUtils.onChangeAndOperate(skinnable.subtitleProperty(), subtitleString -> { + if (subtitleString == null) vbox.getChildren().setAll(title); + else vbox.getChildren().setAll(title, subtitle); + }); left.getChildren().setAll(imageViewContainer, vbox); root.setLeft(left); 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 9b2d143b2..cb45f358c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -58,8 +58,9 @@ public final class LeftPaneController { public LeftPaneController(AdvancedListBox leftPane) { this.leftPane = leftPane; - AdvancedListItem2 accountListItem = new AccountAdvancedListItem(); + AccountAdvancedListItem accountListItem = new AccountAdvancedListItem(); accountListItem.setOnAction(e -> Controllers.navigate(Controllers.getAccountListPage())); + accountListItem.accountProperty().bind(Accounts.selectedAccountProperty()); AdvancedListItem2 gameListItem = new GameAdvancedListItem(); gameListItem.setOnAction(e -> Controllers.navigate(Controllers.getGameListPage())); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java index 17dd802a3..4aaf5c32c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java @@ -32,60 +32,41 @@ import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.AdvancedListItem2; import org.jackhuang.hmcl.ui.FXUtils; +import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public class AccountAdvancedListItem extends AdvancedListItem2 { - private final ObjectProperty image = new SimpleObjectProperty<>(); - private final ObjectProperty viewport = new SimpleObjectProperty<>(AccountHelper.getViewport(4)); - private final StringProperty title = new SimpleStringProperty(); - private final StringProperty subtitle = new SimpleStringProperty(); + private ObjectProperty account = new SimpleObjectProperty() { - public AccountAdvancedListItem() { - - FXUtils.onChangeAndOperate(Accounts.selectedAccountProperty(), account -> { + @Override + protected void invalidated() { + Account account = get(); if (account == null) { - title.set(i18n("account.missing")); - subtitle.set(i18n("account.missing.add")); - image.set(new Image("/assets/img/craft_table.png")); + titleProperty().set(i18n("account.missing")); + subtitleProperty().set(i18n("account.missing.add")); + imageProperty().set(new Image("/assets/img/craft_table.png")); } else { - title.set(account.getCharacter()); - subtitle.set(accountSubtitle(account)); + titleProperty().set(account.getCharacter()); + subtitleProperty().set(accountSubtitle(account)); - this.image.set(AccountHelper.getDefaultSkin(account.getUUID(), 4)); + imageProperty().set(AccountHelper.getDefaultSkin(account.getUUID(), 4)); if (account instanceof YggdrasilAccount) { AccountHelper.loadSkinAsync((YggdrasilAccount) account).subscribe(Schedulers.javafx(), () -> { Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); - this.image.set(image); + imageProperty().set(image); }); } } - }); + } + }; + + public AccountAdvancedListItem() { + viewportProperty().set(AccountHelper.getViewport(4)); } - @Override - protected void layoutChildren() { - super.layoutChildren(); - } - - @Override - public ObjectProperty imageProperty() { - return image; - } - - @Override - public ObjectProperty viewportProperty() { - return viewport; - } - - @Override - public StringProperty titleProperty() { - return title; - } - - @Override - public StringProperty subtitleProperty() { - return subtitle; + public ObjectProperty accountProperty() { + return account; } private static String accountSubtitle(Account account) { 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 ddeb7bf1f..db0961124 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 @@ -35,8 +35,6 @@ import org.jackhuang.hmcl.ui.WeakListenerHelper; import java.io.File; public class GameAdvancedListItem extends AdvancedListItem2 { - private final ObjectProperty image = new SimpleObjectProperty<>(); - private final StringProperty title = new SimpleStringProperty(); private final WeakListenerHelper helper = new WeakListenerHelper(); private Profile profile; @@ -69,25 +67,10 @@ public class GameAdvancedListItem extends AdvancedListItem2 { String version = profile.getSelectedVersion(); File iconFile = profile.getRepository().getVersionIcon(version); if (iconFile.exists()) - image.set(new Image("file:" + iconFile.getAbsolutePath())); + imageProperty().set(new Image("file:" + iconFile.getAbsolutePath())); else - image.set(new Image("/assets/img/grass.png")); + imageProperty().set(new Image("/assets/img/grass.png")); - title.set(version); - } - - @Override - public ObjectProperty imageProperty() { - return image; - } - - @Override - public StringProperty titleProperty() { - return title; - } - - @Override - public StringProperty subtitleProperty() { - return null; + titleProperty().set(version); } } From efa280b839676bdf77da15b63a10e4e36472c1f8 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Fri, 31 Aug 2018 22:28:06 +0800 Subject: [PATCH 16/28] Move profile management to Profiles --- .../org/jackhuang/hmcl/setting/Profiles.java | 116 ++++++++++++++++++ .../org/jackhuang/hmcl/setting/Settings.java | 100 +-------------- .../org/jackhuang/hmcl/ui/Controllers.java | 3 +- .../jackhuang/hmcl/ui/LeftPaneController.java | 4 +- .../java/org/jackhuang/hmcl/ui/MainPage.java | 3 +- .../org/jackhuang/hmcl/ui/ProfilePage.java | 6 +- .../ui/download/DownloadWizardProvider.java | 3 +- .../ui/versions/GameAdvancedListItem.java | 16 ++- .../jackhuang/hmcl/ui/versions/GameList.java | 3 +- 9 files changed, 140 insertions(+), 114 deletions(-) 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 5b6d69b7f..5b770d2c9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profiles.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profiles.java @@ -17,6 +17,21 @@ */ package org.jackhuang.hmcl.setting; +import javafx.beans.value.ObservableValue; +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 static org.jackhuang.hmcl.setting.ConfigHolder.config; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class Profiles { @@ -37,4 +52,105 @@ public final class Profiles { return profile.getName(); } } + + /**************************************** + * PROFILES * + ****************************************/ + + public static Profile getSelectedProfile() { + checkProfileMap(); + + if (!hasProfile(config().getSelectedProfile())) { + getProfileMap().keySet().stream().findFirst().ifPresent(selectedProfile -> { + config().setSelectedProfile(selectedProfile); + }); + Schedulers.computation().schedule(Profiles::onProfileChanged); + } + 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); + } + } + + 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()) { + 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); + } + } + + 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. + */ + public static void onProfileLoading() { + EventBus.EVENT_BUS.fireEvent(new ProfileLoadingEvent(new Object(), getProfiles())); + onProfileChanged(); + } + + static void init() { + checkProfileMap(); + + for (Map.Entry profileEntry : getProfileMap().entrySet()) { + profileEntry.getValue().setName(profileEntry.getKey()); + profileEntry.getValue().nameProperty().setChangedListener(Profiles::profileNameChanged); + profileEntry.getValue().addPropertyChangedListener(e -> ConfigHolder.markConfigDirty()); + } + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java index 1d987604b..ca227baa0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java @@ -52,14 +52,7 @@ public class Settings { private Settings() { ProxyManager.init(); Accounts.init(); - - checkProfileMap(); - - for (Map.Entry profileEntry : getProfileMap().entrySet()) { - profileEntry.getValue().setName(profileEntry.getKey()); - profileEntry.getValue().nameProperty().setChangedListener(this::profileNameChanged); - profileEntry.getValue().addPropertyChangedListener(e -> ConfigHolder.markConfigDirty()); - } + Profiles.init(); } public Font getFont() { @@ -114,95 +107,4 @@ public class Settings { throw new IllegalArgumentException("Unknown download provider: " + downloadProvider); config().setDownloadType(index); } - - /**************************************** - * PROFILES * - ****************************************/ - - public Profile getSelectedProfile() { - checkProfileMap(); - - if (!hasProfile(config().getSelectedProfile())) { - getProfileMap().keySet().stream().findFirst().ifPresent(selectedProfile -> { - config().setSelectedProfile(selectedProfile); - }); - Schedulers.computation().schedule(this::onProfileChanged); - } - return getProfile(config().getSelectedProfile()); - } - - public void setSelectedProfile(Profile selectedProfile) { - if (hasProfile(selectedProfile.getName()) && !Objects.equals(selectedProfile.getName(), config().getSelectedProfile())) { - config().setSelectedProfile(selectedProfile.getName()); - Schedulers.computation().schedule(this::onProfileChanged); - } - } - - public Profile getProfile(String name) { - checkProfileMap(); - - Optional p = name == null ? getProfileMap().values().stream().findFirst() : Optional.ofNullable(getProfileMap().get(name)); - return p.orElse(null); - } - - public boolean hasProfile(String name) { - return getProfileMap().containsKey(name); - } - - public Map getProfileMap() { - return config().getConfigurations(); - } - - public Collection getProfiles() { - return getProfileMap().values().stream().filter(t -> StringUtils.isNotBlank(t.getName())).collect(Collectors.toList()); - } - - public 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(this::onProfileLoading); - - ver.nameProperty().setChangedListener(this::profileNameChanged); - } - - public void deleteProfile(Profile profile) { - deleteProfile(profile.getName()); - } - - public void deleteProfile(String profileName) { - getProfileMap().remove(profileName); - checkProfileMap(); - Schedulers.computation().schedule(this::onProfileLoading); - } - - private void checkProfileMap() { - if (getProfileMap().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); - } - } - - private void onProfileChanged() { - EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(this, getSelectedProfile())); - getSelectedProfile().getRepository().refreshVersionsAsync().start(); - } - - private void profileNameChanged(ObservableValue observableValue, String oldValue, String newValue) { - getProfileMap().put(newValue, getProfileMap().remove(oldValue)); - } - - /** - * Start profiles loading process. - * Invoked by loading GUI phase. - */ - public void onProfileLoading() { - EventBus.EVENT_BUS.fireEvent(new ProfileLoadingEvent(this, getProfiles())); - onProfileChanged(); - } } 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 9834cb62f..62ef628bb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -24,6 +24,7 @@ import javafx.scene.layout.Region; import javafx.stage.Stage; import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.Metadata; +import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; @@ -121,7 +122,7 @@ public final class Controllers { decorator.showPage(null); leftPaneController = new LeftPaneController(decorator.getLeftPane()); - Settings.instance().onProfileLoading(); + 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 cb45f358c..e45e413b2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -126,11 +126,11 @@ public final class LeftPaneController { private void onProfilesLoading() { LinkedList list = new LinkedList<>(); - for (Profile profile : Settings.instance().getProfiles()) { + for (Profile profile : Profiles.getProfiles()) { AdvancedListItem item = new AdvancedListItem(Profiles.getProfileDisplayName(profile)); RipplerContainer ripplerContainer = new RipplerContainer(item); item.setOnSettingsButtonClicked(e -> Controllers.getDecorator().showPage(new ProfilePage(profile))); - ripplerContainer.setOnMouseClicked(e -> Settings.instance().setSelectedProfile(profile)); + ripplerContainer.setOnMouseClicked(e -> Profiles.setSelectedProfile(profile)); ripplerContainer.getProperties().put("profile", profile.getName()); ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty()); list.add(ripplerContainer); 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 e9c052c3f..7e384fa8b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java @@ -35,6 +35,7 @@ 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; @@ -92,7 +93,7 @@ public final class MainPage extends StackPane implements DecoratorPage { btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(0), i18n("install"))); FXUtils.installTooltip(btnAdd, i18n("install")); - btnRefresh.setOnMouseClicked(e -> Settings.instance().getSelectedProfile().getRepository().refreshVersionsAsync().start()); + btnRefresh.setOnMouseClicked(e -> Profiles.getSelectedProfile().getRepository().refreshVersionsAsync().start()); FXUtils.installTooltip(btnRefresh, i18n("button.refresh")); } 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 c50b96c08..39a42c104 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) { - Settings.instance().deleteProfile(profile); + Profiles.deleteProfile(profile); Controllers.navigate(null); } } @@ -99,10 +99,10 @@ public final class ProfilePage extends StackPane implements DecoratorPage { } Profile newProfile = new Profile(txtProfileName.getText(), new File(getLocation())); newProfile.setUseRelativePath(toggleUseRelativePath.isSelected()); - Settings.instance().putProfile(newProfile); + Profiles.putProfile(newProfile); } - Settings.instance().onProfileLoading(); + Profiles.onProfileLoading(); Controllers.navigate(null); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java index 55eb136c2..b1121f6b7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java @@ -25,6 +25,7 @@ import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.wizard.WizardController; @@ -45,7 +46,7 @@ public final class DownloadWizardProvider implements WizardProvider { @Override public void start(Map settings) { - profile = Settings.instance().getSelectedProfile(); + profile = Profiles.getSelectedProfile(); settings.put(PROFILE, profile); } 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 db0961124..e813c937c 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 @@ -28,6 +28,7 @@ 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; @@ -50,7 +51,7 @@ public class GameAdvancedListItem extends AdvancedListItem2 { loadVersion(); }); })); - loadProfile(Settings.instance().getSelectedProfile()); + loadProfile(Profiles.getSelectedProfile()); } private void loadProfile(Profile newProfile) { @@ -66,11 +67,14 @@ public class GameAdvancedListItem extends AdvancedListItem2 { if (profile == null || !profile.getRepository().isLoaded()) return; String version = profile.getSelectedVersion(); File iconFile = profile.getRepository().getVersionIcon(version); - if (iconFile.exists()) - imageProperty().set(new Image("file:" + iconFile.getAbsolutePath())); - else - imageProperty().set(new Image("/assets/img/grass.png")); + + JFXUtilities.runInFX(() -> { + if (iconFile.exists()) + imageProperty().set(new Image("file:" + iconFile.getAbsolutePath())); + else + imageProperty().set(new Image("/assets/img/grass.png")); - titleProperty().set(version); + titleProperty().set(version); + }); } } 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 346f42c80..3e87173c3 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 @@ -29,6 +29,7 @@ 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; @@ -62,7 +63,7 @@ public class GameList extends Control implements DecoratorPage { this.profile = event.getProfile(); }); - profile = Settings.instance().getSelectedProfile(); + profile = Profiles.getSelectedProfile(); if (profile.getRepository().isLoaded()) loadVersions(profile.getRepository()); else From 531fd87ff37eb31e19430b24f8244711d423c844 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sat, 1 Sep 2018 00:19:25 +0800 Subject: [PATCH 17/28] Profiles --- .../hmcl/event/ProfileChangedEvent.java | 55 ------ .../hmcl/event/ProfileLoadingEvent.java | 60 ------ .../org/jackhuang/hmcl/setting/Accounts.java | 4 +- .../org/jackhuang/hmcl/setting/Profile.java | 31 ++- .../org/jackhuang/hmcl/setting/Profiles.java | 178 +++++++++--------- .../hmcl/ui/AdvancedListItemSkin.java | 2 +- .../org/jackhuang/hmcl/ui/Controllers.java | 1 - .../jackhuang/hmcl/ui/LeftPaneController.java | 18 +- .../java/org/jackhuang/hmcl/ui/MainPage.java | 152 +-------------- .../org/jackhuang/hmcl/ui/ProfilePage.java | 5 +- .../org/jackhuang/hmcl/ui/VersionPage.java | 10 - .../hmcl/ui/VersionSettingsController.java | 2 +- .../hmcl/ui/account/AccountListSkin.java | 2 + .../ui/versions/GameAdvancedListItem.java | 12 +- .../jackhuang/hmcl/ui/versions/GameList.java | 6 +- .../hmcl/ui/versions/GameListSkin.java | 2 + HMCL/src/main/resources/assets/fxml/main.fxml | 28 +-- .../assets/fxml/version/version.fxml | 12 -- .../java/org/jackhuang/hmcl/auth/Account.java | 2 +- 19 files changed, 142 insertions(+), 440 deletions(-) delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/event/ProfileChangedEvent.java delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/event/ProfileLoadingEvent.java 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. From 64e7cc74b29087fec479bfc16539b23e10eae872 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sat, 1 Sep 2018 00:36:18 +0800 Subject: [PATCH 18/28] ProfileList --- .../org/jackhuang/hmcl/ui/Controllers.java | 12 ++- .../jackhuang/hmcl/ui/LeftPaneController.java | 57 ++----------- .../hmcl/ui/account/AccountListItemSkin.java | 2 +- .../ui/profile/ProfileAdvancedListItem.java | 60 +++++++++++++ .../hmcl/ui/profile/ProfileList.java | 82 ++++++++++++++++++ .../hmcl/ui/profile/ProfileListItem.java | 83 ++++++++++++++++++ .../hmcl/ui/profile/ProfileListItemSkin.java | 85 +++++++++++++++++++ .../hmcl/ui/profile/ProfileListSkin.java | 77 +++++++++++++++++ .../hmcl/ui/{ => profile}/ProfilePage.java | 4 +- .../hmcl/ui/versions/GameListSkin.java | 14 +-- .../resources/assets/lang/I18N.properties | 4 +- .../resources/assets/lang/I18N_zh.properties | 4 +- .../assets/lang/I18N_zh_CN.properties | 4 +- 13 files changed, 422 insertions(+), 66 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileList.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItem.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItemSkin.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListSkin.java rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => profile}/ProfilePage.java (97%) 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 31533d60a..a4a7314d4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -24,8 +24,6 @@ import javafx.scene.layout.Region; import javafx.stage.Stage; import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.setting.Profiles; -import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.account.AccountList; @@ -34,6 +32,7 @@ import org.jackhuang.hmcl.ui.construct.InputDialogPane; import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; +import org.jackhuang.hmcl.ui.profile.ProfileList; import org.jackhuang.hmcl.ui.versions.GameList; import org.jackhuang.hmcl.util.FutureCallback; import org.jackhuang.hmcl.util.JavaVersion; @@ -51,6 +50,7 @@ public final class Controllers { private static VersionPage versionPage = null; private static GameList gameListPage = null; private static AccountList accountListPage = null; + private static ProfileList profileListPage = null; private static AuthlibInjectorServersPage serversPage = null; private static LeftPaneController leftPaneController; private static Decorator decorator; @@ -84,6 +84,13 @@ public final class Controllers { return accountListPage; } + // FXThread + public static ProfileList getProfileListPage() { + if (profileListPage == null) + profileListPage = new ProfileList(); + return profileListPage; + } + // FXThread public static VersionPage getVersionPage() { if (versionPage == null) @@ -202,5 +209,6 @@ public final class Controllers { scene = null; gameListPage = null; accountListPage = null; + profileListPage = null; } } 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 e8228c024..cb2eb266c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -18,12 +18,8 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.concurrency.JFXUtilities; -import com.jfoenix.controls.JFXButton; -import javafx.application.Platform; import javafx.beans.binding.When; -import javafx.scene.Node; 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.RefreshedVersionsEvent; @@ -38,30 +34,26 @@ import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.account.AccountAdvancedListItem; import org.jackhuang.hmcl.ui.account.AddAccountPane; import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.ui.profile.ProfileAdvancedListItem; import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem; import org.jackhuang.hmcl.upgrade.UpdateChecker; -import org.jackhuang.hmcl.util.Lang; import java.io.File; -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 { - private final AdvancedListBox leftPane; - private final VBox profilePane = new VBox(); public LeftPaneController(AdvancedListBox leftPane) { - this.leftPane = leftPane; - AccountAdvancedListItem accountListItem = new AccountAdvancedListItem(); accountListItem.setOnAction(e -> Controllers.navigate(Controllers.getAccountListPage())); accountListItem.accountProperty().bind(Accounts.selectedAccountProperty()); - AdvancedListItem2 gameListItem = new GameAdvancedListItem(); + GameAdvancedListItem gameListItem = new GameAdvancedListItem(); gameListItem.setOnAction(e -> Controllers.navigate(Controllers.getGameListPage())); + ProfileAdvancedListItem profileListItem = new ProfileAdvancedListItem(); + profileListItem.setOnAction(e -> Controllers.navigate(Controllers.getProfileListPage())); + profileListItem.profileProperty().bind(Profiles.selectedProfileProperty()); IconedItem launcherSettingsItem = new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20)); @@ -83,18 +75,11 @@ public final class LeftPaneController { .add(accountListItem) .startCategory(i18n("version").toUpperCase()) .add(gameListItem) + .startCategory(i18n("profile.title").toUpperCase()) + .add(profileListItem) .startCategory(i18n("launcher").toUpperCase()) - .add(launcherSettingsItem) - .add(new ClassTitle(i18n("profile.title").toUpperCase(), Lang.apply(new JFXButton(), button -> { - button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10)); - button.getStyleClass().add("toggle-icon-tiny"); - button.setOnMouseClicked(e -> - Controllers.getDecorator().showPage(new ProfilePage(null))); - }))) - .add(profilePane); + .add(launcherSettingsItem); - 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()); @@ -111,32 +96,6 @@ public final class LeftPaneController { } // ==== - private void onProfileChanged(Profile profile) { - Platform.runLater(() -> { - for (Node node : profilePane.getChildren()) { - if (node instanceof RipplerContainer && node.getProperties().get("profile") instanceof String) { - boolean current = Objects.equals(node.getProperties().get("profile"), profile.getName()); - ((RipplerContainer) node).setSelected(current); - ((AdvancedListItem) ((RipplerContainer) node).getContainer()).setSubtitle(current ? i18n("profile.selected") : ""); - } - } - }); - } - - private void onProfilesLoading() { - LinkedList list = new LinkedList<>(); - for (Profile profile : Profiles.getProfiles()) { - AdvancedListItem item = new AdvancedListItem(Profiles.getProfileDisplayName(profile)); - RipplerContainer ripplerContainer = new RipplerContainer(item); - item.setOnSettingsButtonClicked(e -> Controllers.getDecorator().showPage(new ProfilePage(profile))); - ripplerContainer.setOnMouseClicked(e -> Profiles.setSelectedProfile(profile)); - ripplerContainer.getProperties().put("profile", profile.getName()); - ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty()); - list.add(ripplerContainer); - } - Platform.runLater(() -> profilePane.getChildren().setAll(list)); - } - private boolean checkedModpack = false; private static boolean showNewAccount = true; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java index 148e16d7a..2b50d2f84 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java @@ -81,7 +81,7 @@ public class AccountListItemSkin extends SkinBase { btnRemove.getStyleClass().add("toggle-icon4"); BorderPane.setAlignment(btnRemove, Pos.CENTER); btnRemove.setGraphic(SVG.delete(Theme.blackFillBinding(), -1, -1)); - JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnRefresh, i18n("button.delete"))); + JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnRemove, i18n("button.delete"))); right.getChildren().add(btnRemove); root.setRight(right); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java new file mode 100644 index 000000000..19d7b1ff9 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java @@ -0,0 +1,60 @@ +/* + * 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.profile; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.geometry.Rectangle2D; +import javafx.scene.image.Image; +import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.auth.offline.OfflineAccount; +import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; +import org.jackhuang.hmcl.game.AccountHelper; +import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.ui.AdvancedListItem2; +import org.jackhuang.hmcl.ui.FXUtils; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class ProfileAdvancedListItem extends AdvancedListItem2 { + private ObjectProperty profile = new SimpleObjectProperty() { + + @Override + protected void invalidated() { + Profile profile = get(); + if (profile == null) { + } else { + titleProperty().set(Profiles.getProfileDisplayName(profile)); + subtitleProperty().set(profile.getGameDir().toString()); + } + } + }; + + public ProfileAdvancedListItem() { + imageProperty().set(new Image("/assets/img/craft_table.png")); + } + + public ObjectProperty profileProperty() { + return profile; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileList.java new file mode 100644 index 000000000..70ca881bb --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileList.java @@ -0,0 +1,82 @@ +/* + * 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.profile; + +import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; +import javafx.scene.control.ToggleGroup; +import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.util.MappedObservableList; + +import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class ProfileList extends Control implements DecoratorPage { + private final StringProperty title = new SimpleStringProperty(i18n("profile.manage")); + private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()); + private ObjectProperty selectedProfile = new SimpleObjectProperty() { + { + items.addListener(onInvalidating(this::invalidated)); + } + + @Override + protected void invalidated() { + Profile selected = get(); + items.forEach(item -> item.selectedProperty().set(item.getProfile() == selected)); + } + }; + + private ToggleGroup toggleGroup; + + public ProfileList() { + toggleGroup = new ToggleGroup(); + + items.bindContent(MappedObservableList.create( + Profiles.profilesProperty(), + profile -> new ProfileListItem(toggleGroup, profile))); + + selectedProfile.bindBidirectional(Profiles.selectedProfileProperty()); + toggleGroup.selectedToggleProperty().addListener((o, a, toggle) -> { + if (toggle == null || toggle.getUserData() == null) return; + selectedProfile.set(((ProfileListItem) toggle.getUserData()).getProfile()); + }); + } + + @Override + protected Skin createDefaultSkin() { + return new ProfileListSkin(this); + } + + public void addNewProfile() { + Controllers.navigate(new ProfilePage(null)); + } + + public ListProperty itemsProperty() { + return items; + } + + @Override + public StringProperty titleProperty() { + return title; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItem.java new file mode 100644 index 000000000..e49db6441 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItem.java @@ -0,0 +1,83 @@ +/* + * 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.profile; + +import javafx.beans.property.*; +import javafx.geometry.Rectangle2D; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; +import javafx.scene.control.ToggleGroup; +import javafx.scene.image.Image; +import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; +import org.jackhuang.hmcl.auth.offline.OfflineAccount; +import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; +import org.jackhuang.hmcl.game.AccountHelper; +import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.task.Schedulers; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class ProfileListItem extends Control { + private final Profile profile; + private final ToggleGroup toggleGroup; + private final StringProperty title = new SimpleStringProperty(); + private final StringProperty subtitle = new SimpleStringProperty(); + private final BooleanProperty selected = new SimpleBooleanProperty(); + + public ProfileListItem(ToggleGroup toggleGroup, Profile profile) { + this.profile = profile; + this.toggleGroup = toggleGroup; + + title.set(Profiles.getProfileDisplayName(profile)); + subtitle.set(profile.getGameDir().toString()); + selected.set(Profiles.selectedProfileProperty().get() == profile); + } + + @Override + protected Skin createDefaultSkin() { + return new ProfileListItemSkin(this); + } + + public ToggleGroup getToggleGroup() { + return toggleGroup; + } + + public Profile getProfile() { + return profile; + } + + public StringProperty titleProperty() { + return title; + } + + public StringProperty subtitleProperty() { + return subtitle; + } + + public BooleanProperty selectedProperty() { + return selected; + } + + public void remove() { + Profiles.getProfiles().remove(profile); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItemSkin.java new file mode 100644 index 000000000..9ec213446 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItemSkin.java @@ -0,0 +1,85 @@ +/* + * 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.profile; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXRadioButton; +import com.jfoenix.effects.JFXDepthManager; +import javafx.geometry.Pos; +import javafx.scene.control.SkinBase; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.TwoLineListItem; + +public class ProfileListItemSkin extends SkinBase { + + public ProfileListItemSkin(ProfileListItem skinnable) { + super(skinnable); + + BorderPane root = new BorderPane(); + + JFXRadioButton chkSelected = new JFXRadioButton(); + BorderPane.setAlignment(chkSelected, Pos.CENTER); + chkSelected.setUserData(skinnable); + chkSelected.selectedProperty().bindBidirectional(skinnable.selectedProperty()); + chkSelected.setToggleGroup(skinnable.getToggleGroup()); + root.setLeft(chkSelected); + + HBox center = new HBox(); + center.setSpacing(8); + center.setAlignment(Pos.CENTER_LEFT); + + StackPane imageViewContainer = new StackPane(); + FXUtils.setLimitWidth(imageViewContainer, 32); + FXUtils.setLimitHeight(imageViewContainer, 32); + + ImageView imageView = new ImageView(); + FXUtils.limitSize(imageView, 32, 32); + imageView.imageProperty().set(new Image("/assets/img/craft_table.png")); + imageViewContainer.getChildren().setAll(imageView); + + TwoLineListItem item = new TwoLineListItem(); + BorderPane.setAlignment(item, Pos.CENTER); + center.getChildren().setAll(imageView, item); + root.setCenter(center); + + HBox right = new HBox(); + right.setAlignment(Pos.CENTER_RIGHT); + + JFXButton btnRemove = new JFXButton(); + btnRemove.setOnMouseClicked(e -> skinnable.remove()); + btnRemove.getStyleClass().add("toggle-icon4"); + BorderPane.setAlignment(btnRemove, Pos.CENTER); + btnRemove.setGraphic(SVG.delete(Theme.blackFillBinding(), -1, -1)); + right.getChildren().add(btnRemove); + root.setRight(right); + + root.setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;"); + JFXDepthManager.setDepth(root, 1); + item.titleProperty().bind(skinnable.titleProperty()); + item.subtitleProperty().bind(skinnable.subtitleProperty()); + + getChildren().setAll(root); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListSkin.java new file mode 100644 index 000000000..588fda41f --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListSkin.java @@ -0,0 +1,77 @@ +/* + * 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.profile; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXScrollPane; +import javafx.beans.binding.Bindings; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.SkinBase; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; + +public class ProfileListSkin extends SkinBase { + + public ProfileListSkin(ProfileList skinnable) { + super(skinnable); + + StackPane root = new StackPane(); + + ScrollPane scrollPane = new ScrollPane(); + { + scrollPane.setFitToWidth(true); + + VBox accountList = new VBox(); + accountList.maxWidthProperty().bind(scrollPane.widthProperty()); + accountList.setSpacing(10); + accountList.setStyle("-fx-padding: 10 10 10 10;"); + + Bindings.bindContent(accountList.getChildren(), skinnable.itemsProperty()); + + scrollPane.setContent(accountList); + JFXScrollPane.smoothScrolling(scrollPane); + } + + VBox vBox = new VBox(); + { + vBox.setAlignment(Pos.BOTTOM_RIGHT); + vBox.setPickOnBounds(false); + vBox.setPadding(new Insets(15)); + vBox.setSpacing(15); + + JFXButton btnAdd = new JFXButton(); + FXUtils.setLimitWidth(btnAdd, 40); + FXUtils.setLimitHeight(btnAdd, 40); + btnAdd.getStyleClass().setAll("jfx-button-raised-round"); + btnAdd.setButtonType(JFXButton.ButtonType.RAISED); + btnAdd.setGraphic(SVG.plus(Theme.whiteFillBinding(), -1, -1)); + btnAdd.setOnMouseClicked(e -> skinnable.addNewProfile()); + + vBox.getChildren().setAll(btnAdd); + } + + root.getChildren().setAll(scrollPane, vBox); + + getChildren().setAll(root); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ProfilePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java similarity index 97% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/ProfilePage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java index 344a6bd24..cd7961387 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ProfilePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.profile; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXCheckBox; @@ -28,6 +28,8 @@ import javafx.scene.layout.StackPane; 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.FXUtils; import org.jackhuang.hmcl.ui.construct.FileItem; import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.util.StringUtils; 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 b7cf3bb13..da6fbdb73 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 @@ -79,13 +79,13 @@ public class GameListSkin extends SkinBase { btnRefresh.setOnMouseClicked(e -> skinnable.refresh()); toolbar.getChildren().add(btnRefresh); - JFXButton btnModify = new JFXButton(); - btnModify.getStyleClass().add("jfx-tool-bar-button"); - btnModify.textFillProperty().bind(Theme.foregroundFillBinding()); - btnModify.setGraphic(wrap(SVG.gear(Theme.foregroundFillBinding(), -1, -1))); - btnModify.setText(I18n.i18n("settings.type.global.manage")); - btnModify.setOnMouseClicked(e -> skinnable.modifyGlobalGameSettings()); - toolbar.getChildren().add(btnModify); +// JFXButton btnModify = new JFXButton(); +// btnModify.getStyleClass().add("jfx-tool-bar-button"); +// btnModify.textFillProperty().bind(Theme.foregroundFillBinding()); +// btnModify.setGraphic(wrap(SVG.gear(Theme.foregroundFillBinding(), -1, -1))); +// btnModify.setText(I18n.i18n("settings.type.global.manage")); +// btnModify.setOnMouseClicked(e -> skinnable.modifyGlobalGameSettings()); +// toolbar.getChildren().add(btnModify); root.setTop(toolbar); } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index afa37c731..eaf4b37a7 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -240,12 +240,12 @@ mods.add.success=Successfully added mods %s. mods.choose_mod=Choose your mods mods.remove=Remove - -profile=Profile +profile=Game Directories profile.default=Current directory profile.home=User home profile.instance_directory=Game Directory profile.instance_directory.choose=Choose Game Directory +profile.manage=Game Directory List profile.new=New Config profile.title=Game Directories profile.selected=Selected diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 36cbcb687..a2145257b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -240,12 +240,12 @@ mods.add.success=成功新增模組 %s。 mods.choose_mod=選擇模組 mods.remove=刪除 - -profile=設定 +profile=遊戲目錄 profile.default=目前目錄 profile.home=主資料夾 profile.instance_directory=遊戲路徑 profile.instance_directory.choose=選擇遊戲路徑 +profile.manage=遊戲目錄列表 profile.new=建立設定 profile.title=遊戲目錄 profile.selected=已選取 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 41037eed9..4397f57b3 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -240,12 +240,12 @@ mods.add.success=成功添加模组 %s。 mods.choose_mod=选择模组 mods.remove=删除 - -profile=配置 +profile=游戏目录 profile.default=当前目录 profile.home=主文件夹 profile.instance_directory=游戏路径 profile.instance_directory.choose=选择游戏路径 +profile.manage=游戏目录列表 profile.new=新建配置 profile.title=游戏目录 profile.selected=已选中 From f2f0c96e62f5e218e0ab58dffd28a84988ed86b8 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sat, 1 Sep 2018 23:38:05 +0800 Subject: [PATCH 19/28] Notice user to add authentication server --- .../org/jackhuang/hmcl/ui/account/AddAccountPane.java | 11 +++++++++++ HMCL/src/main/resources/assets/css/root.css | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java index b1a9daf70..7aab8b9e4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java @@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui.account; import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.*; +import javafx.beans.InvalidationListener; import javafx.beans.binding.Bindings; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.fxml.FXML; @@ -90,6 +91,9 @@ public class AddAccountPane extends StackPane { cboType.setConverter(stringConverter(Accounts::getAccountTypeName)); cboType.getSelectionModel().select(0); + cboServers.getItems().addListener(onInvalidating(this::checkIfNoServer)); + checkIfNoServer(); + ReadOnlyObjectProperty> loginType = cboType.getSelectionModel().selectedItemProperty(); txtPassword.visibleProperty().bind(loginType.isNotEqualTo(Accounts.FACTORY_OFFLINE)); @@ -122,6 +126,13 @@ public class AddAccountPane extends StackPane { } } + private void checkIfNoServer() { + if (cboServers.getItems().isEmpty()) + cboServers.getStyleClass().setAll("jfx-combo-box-warning"); + else + cboServers.getStyleClass().setAll("jfx-combo-box"); + } + /** * Gets the additional data that needs to be passed into {@link AccountFactory#create(CharacterSelector, String, String, Object)}. */ diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 94fe55876..57e7fde41 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -1007,6 +1007,15 @@ -jfx-focus-color: -fx-base-check-color; } +.jfx-combo-box-warning { + -jfx-focus-color: #D34336; + -jfx-unfocus-color: #D34336; +} + +.jfx-combo-box-warning .text { + -fx-fill: #D34336; +} + .combo-box-popup .list-view .jfx-list-cell .label, .combo-box-popup .list-view .jfx-list-cell:filled:hover .label { -fx-text-fill: BLACK; From cf6485aea84aad4b94eaf1f366a613412bd30c33 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sat, 1 Sep 2018 23:38:32 +0800 Subject: [PATCH 20/28] Make exception message more understandable in FileDownloadTask --- .../src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java index 08a8cfa41..056f7d08d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java @@ -260,7 +260,7 @@ public class FileDownloadTask extends Task { } if (exception != null) - throw new IOException("Unable to download file " + currentURL, exception); + throw new IOException("Unable to download file " + currentURL + ". " + exception.getMessage(), exception); } } From 5c5f091769e0087d48df142a275819a1163a98fe Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sat, 1 Sep 2018 23:41:13 +0800 Subject: [PATCH 21/28] Fails game installing when unable to download game jar --- .../java/org/jackhuang/hmcl/download/DefaultGameBuilder.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java index 08559af84..b59b444a1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java @@ -53,11 +53,10 @@ public class DefaultGameBuilder extends GameBuilder { Version version = Constants.GSON.fromJson(variables.get(VersionJsonDownloadTask.ID), Version.class); version = version.setId(name).setJar(null); variables.set("version", version); - Task result = new ParallelTask( + Task result = downloadGameAsync(gameVersion, version).then(new ParallelTask( new GameAssetDownloadTask(dependencyManager, version), - downloadGameAsync(gameVersion, version), new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries. - ).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version)); // using [with] because download failure here are tolerant. + ).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant. if (toolVersions.containsKey("forge")) result = result.then(libraryTaskHelper(gameVersion, "forge")); From b8793f7575037ff123780895222d77c10f8f40f2 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sun, 2 Sep 2018 21:52:45 +0800 Subject: [PATCH 22/28] Prevent horizontal scroll bar in settings page --- HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java index 810bf98ce..8f9cdc677 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java @@ -62,13 +62,12 @@ public abstract class SettingsView extends StackPane { public SettingsView() { scroll = new ScrollPane(); getChildren().setAll(scroll); - scroll.setStyle("-fx-font-size: 14; -fx-pref-width: 100%;"); - scroll.setFitToHeight(true); + scroll.setStyle("-fx-font-size: 14;"); scroll.setFitToWidth(true); { VBox rootPane = new VBox(); - rootPane.setStyle("-fx-padding: 20;"); + rootPane.setStyle("-fx-padding: 18;"); { ComponentList settingsPane = new ComponentList(); { From 5fb0035b2f2cbe40bee7badb407c37a0d4d7bda4 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sun, 2 Sep 2018 21:53:34 +0800 Subject: [PATCH 23/28] Refactor decorator as well as navigation --- .../jackhuang/hmcl/ui/AdvancedListItem2.java | 12 +- .../org/jackhuang/hmcl/ui/Controllers.java | 24 +- .../java/org/jackhuang/hmcl/ui/Decorator.java | 733 ------------------ .../jackhuang/hmcl/ui/LeftPaneController.java | 9 +- .../java/org/jackhuang/hmcl/ui/MainPage.java | 5 +- .../org/jackhuang/hmcl/ui/SettingsPage.java | 2 +- .../org/jackhuang/hmcl/ui/VersionPage.java | 2 +- .../hmcl/ui/account/AccountList.java | 2 +- .../account/AuthlibInjectorServersPage.java | 2 +- .../hmcl/ui/construct/DialogCloseEvent.java | 2 +- .../hmcl/ui/construct/Navigator.java | 155 ++++ .../hmcl/ui/construct/PageCloseEvent.java | 41 + .../hmcl/ui/decorator/DecoratorControl.java | 201 +++++ .../ui/decorator/DecoratorController.java | 374 +++++++++ .../{wizard => decorator}/DecoratorPage.java | 12 +- .../hmcl/ui/decorator/DecoratorSkin.java | 422 ++++++++++ .../decorator/DecoratorWizardDisplayer.java | 126 +++ .../ui/download/DownloadWizardProvider.java | 10 +- .../hmcl/ui/profile/ProfileList.java | 2 +- .../hmcl/ui/profile/ProfilePage.java | 3 +- .../jackhuang/hmcl/ui/versions/GameList.java | 9 +- .../hmcl/ui/versions/GameListItem.java | 2 +- .../jackhuang/hmcl/ui/wizard/Refreshable.java | 7 + .../TaskExecutorDialogWizardDisplayer.java | 0 HMCL/src/main/resources/assets/css/root.css | 4 + .../main/resources/assets/fxml/decorator.fxml | 2 +- HMCL/src/main/resources/assets/fxml/main.fxml | 4 +- 27 files changed, 1381 insertions(+), 786 deletions(-) delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/Navigator.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/PageCloseEvent.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorControl.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{wizard => decorator}/DecoratorPage.java (80%) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorSkin.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorWizardDisplayer.java rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{construct => wizard}/TaskExecutorDialogWizardDisplayer.java (100%) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java index 449a5fd76..8c660746f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java @@ -60,21 +60,11 @@ public class AdvancedListItem2 extends Control { return onActionProperty().get(); } - private ObjectProperty> onAction = new ObjectPropertyBase>() { + private ObjectProperty> onAction = new SimpleObjectProperty>(this, "onAction") { @Override protected void invalidated() { setEventHandler(ActionEvent.ACTION, get()); } - - @Override - public Object getBean() { - return AdvancedListItem2.this; - } - - @Override - public String getName() { - return "onAction"; - } }; @Override 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 a4a7314d4..69c3f39a7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -32,6 +32,7 @@ import org.jackhuang.hmcl.ui.construct.InputDialogPane; import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; +import org.jackhuang.hmcl.ui.decorator.DecoratorController; import org.jackhuang.hmcl.ui.profile.ProfileList; import org.jackhuang.hmcl.ui.versions.GameList; import org.jackhuang.hmcl.util.FutureCallback; @@ -53,7 +54,7 @@ public final class Controllers { private static ProfileList profileListPage = null; private static AuthlibInjectorServersPage serversPage = null; private static LeftPaneController leftPaneController; - private static Decorator decorator; + private static DecoratorController decorator; public static Scene getScene() { return scene; @@ -106,7 +107,7 @@ public final class Controllers { } // FXThread - public static Decorator getDecorator() { + public static DecoratorController getDecorator() { return decorator; } @@ -125,20 +126,14 @@ public final class Controllers { stage.setOnCloseRequest(e -> Launcher.stopApplication()); - decorator = new Decorator(stage, getMainPage(), Metadata.TITLE, false, true); - decorator.showPage(null); - leftPaneController = new LeftPaneController(decorator.getLeftPane()); + decorator = new DecoratorController(stage, getMainPage()); + leftPaneController = new LeftPaneController(); + decorator.getDecorator().drawerProperty().setAll(leftPaneController); Task.of(JavaVersion::initialize).start(); - decorator.setCustomMaximize(false); - - scene = new Scene(decorator, 804, 521); + scene = new Scene(decorator.getDecorator(), 800, 519); scene.getStylesheets().setAll(config().getTheme().getStylesheets()); - stage.setMinWidth(804); - stage.setMaxWidth(804); - stage.setMinHeight(521); - stage.setMaxHeight(521); stage.getIcons().add(new Image("/assets/img/icon.png")); stage.setTitle(Metadata.TITLE); @@ -189,10 +184,7 @@ public final class Controllers { } public static void navigate(Node node) { - if (decorator.getNowPage() == node) - decorator.showPage(null); - else - decorator.showPage(node); + decorator.getNavigator().navigate(node); } public static boolean isStopped() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java deleted file mode 100644 index 91ac7e21b..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java +++ /dev/null @@ -1,733 +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.ui; - -import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXDialog; -import com.jfoenix.controls.JFXDrawer; -import com.jfoenix.controls.JFXHamburger; -import com.jfoenix.svg.SVGGlyph; -import javafx.animation.Interpolator; -import javafx.animation.KeyFrame; -import javafx.animation.KeyValue; -import javafx.animation.Timeline; -import javafx.application.Platform; -import javafx.beans.binding.Bindings; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.geometry.BoundingBox; -import javafx.geometry.Bounds; -import javafx.geometry.Insets; -import javafx.geometry.Rectangle2D; -import javafx.scene.Cursor; -import javafx.scene.Node; -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.DragEvent; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.*; -import javafx.scene.paint.Color; -import javafx.scene.shape.Rectangle; -import javafx.stage.Screen; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import javafx.util.Duration; -import org.jackhuang.hmcl.Launcher; -import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDnD; -import org.jackhuang.hmcl.setting.ConfigHolder; -import org.jackhuang.hmcl.setting.EnumBackgroundImage; -import org.jackhuang.hmcl.setting.Theme; -import org.jackhuang.hmcl.ui.account.AddAuthlibInjectorServerPane; -import org.jackhuang.hmcl.ui.animation.AnimationProducer; -import org.jackhuang.hmcl.ui.animation.ContainerAnimations; -import org.jackhuang.hmcl.ui.animation.TransitionHandler; -import org.jackhuang.hmcl.ui.construct.AdvancedListBox; -import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; -import org.jackhuang.hmcl.ui.construct.StackContainerPane; -import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogWizardDisplayer; -import org.jackhuang.hmcl.ui.wizard.*; -import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.OperatingSystem; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.Queue; -import java.util.Random; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.logging.Level; - -import static java.util.stream.Collectors.toList; -import static org.jackhuang.hmcl.setting.ConfigHolder.config; -import static org.jackhuang.hmcl.util.Logging.LOG; - -public final class Decorator extends StackPane implements TaskExecutorDialogWizardDisplayer { - private static final SVGGlyph minus = Lang.apply(new SVGGlyph(0, "MINUS", "M804.571 420.571v109.714q0 22.857-16 38.857t-38.857 16h-694.857q-22.857 0-38.857-16t-16-38.857v-109.714q0-22.857 16-38.857t38.857-16h694.857q22.857 0 38.857 16t16 38.857z", Color.WHITE), - glyph -> { glyph.setSize(12, 2); glyph.setTranslateY(4); }); - private static final SVGGlyph resizeMax = Lang.apply(new SVGGlyph(0, "RESIZE_MAX", "M726 810v-596h-428v596h428zM726 44q34 0 59 25t25 59v768q0 34-25 60t-59 26h-428q-34 0-59-26t-25-60v-768q0-34 25-60t59-26z", Color.WHITE), - glyph -> { glyph.setPrefSize(12, 12); glyph.setSize(12, 12); }); - private static final SVGGlyph resizeMin = Lang.apply(new SVGGlyph(0, "RESIZE_MIN", "M80.842 943.158v-377.264h565.894v377.264h-565.894zM0 404.21v619.79h727.578v-619.79h-727.578zM377.264 161.684h565.894v377.264h-134.736v80.842h215.578v-619.79h-727.578v323.37h80.842v-161.686z", Color.WHITE), - glyph -> { glyph.setPrefSize(12, 12); glyph.setSize(12, 12); }); - private static final SVGGlyph close = Lang.apply(new SVGGlyph(0, "CLOSE", "M810 274l-238 238 238 238-60 60-238-238-238 238-60-60 238-238-238-238 60-60 238 238 238-238z", Color.WHITE), - glyph -> { glyph.setPrefSize(12, 12); glyph.setSize(12, 12); }); - - private static final String PROPERTY_DIALOG_CLOSE_HANDLER = Decorator.class.getName() + ".dialog.closeListener"; - - private final ObjectProperty onCloseButtonAction; - private final BooleanProperty customMaximize = new SimpleBooleanProperty(false); - - private final Stage primaryStage; - private final Node mainPage; - private final boolean max, min; - private final WizardController wizardController = new WizardController(this); - private final Queue cancelQueue = new ConcurrentLinkedQueue<>(); - - private double xOffset, yOffset, newX, newY, initX, initY; - private boolean allowMove, isDragging, maximized; - private BoundingBox originalBox, maximizedBox; - private final TransitionHandler animationHandler; - - private JFXDialog dialog; - private StackContainerPane dialogPane; - - @FXML - private StackPane contentPlaceHolder; - @FXML - private StackPane drawerWrapper; - @FXML - private BorderPane titleContainer; - @FXML - private BorderPane leftRootPane; - @FXML - private HBox buttonsContainer; - @FXML - private JFXButton backNavButton; - @FXML - private JFXButton refreshNavButton; - @FXML - private JFXButton closeNavButton; - @FXML - private JFXButton refreshMenuButton; - @FXML - private Label titleLabel; - @FXML - private Label lblTitle; - @FXML - private AdvancedListBox leftPane; - @FXML - private JFXDrawer drawer; - @FXML - private StackPane titleBurgerContainer; - @FXML - private JFXHamburger titleBurger; - @FXML - private JFXButton btnMin; - @FXML - private JFXButton btnMax; - @FXML - private JFXButton btnClose; - @FXML - private HBox navLeft; - @FXML - private ImageView welcomeView; - @FXML - private Rectangle separator; - - public Decorator(Stage primaryStage, Node mainPage, String title) { - this(primaryStage, mainPage, title, true, true); - } - - public Decorator(Stage primaryStage, Node mainPage, String title, boolean max, boolean min) { - this.primaryStage = primaryStage; - this.mainPage = mainPage; - this.max = max; - this.min = min; - - FXUtils.loadFXML(this, "/assets/fxml/decorator.fxml"); - - onCloseButtonAction = new SimpleObjectProperty<>(this, "onCloseButtonAction", Launcher::stopApplication); - - primaryStage.initStyle(StageStyle.UNDECORATED); - btnClose.setGraphic(close); - btnMin.setGraphic(minus); - btnMax.setGraphic(resizeMax); - - close.fillProperty().bind(Theme.foregroundFillBinding()); - minus.fillProperty().bind(Theme.foregroundFillBinding()); - resizeMax.fillProperty().bind(Theme.foregroundFillBinding()); - resizeMin.fillProperty().bind(Theme.foregroundFillBinding()); - - refreshNavButton.setGraphic(SVG.refresh(Theme.foregroundFillBinding(), 15, 15)); - closeNavButton.setGraphic(SVG.close(Theme.foregroundFillBinding(), 15, 15)); - backNavButton.setGraphic(SVG.back(Theme.foregroundFillBinding(), 15, 15)); - - separator.visibleProperty().bind(refreshNavButton.visibleProperty()); - - lblTitle.setText(title); - - buttonsContainer.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY))); - titleContainer.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { - if (event.getClickCount() == 2) - btnMax.fire(); - }); - - welcomeView.setCursor(Cursor.HAND); - welcomeView.setOnMouseClicked(e -> { - Timeline nowAnimation = new Timeline(); - nowAnimation.getKeyFrames().addAll( - new KeyFrame(Duration.ZERO, new KeyValue(welcomeView.opacityProperty(), 1.0D, Interpolator.EASE_BOTH)), - new KeyFrame(new Duration(300), new KeyValue(welcomeView.opacityProperty(), 0.0D, Interpolator.EASE_BOTH)), - new KeyFrame(new Duration(300), e2 -> drawerWrapper.getChildren().remove(welcomeView)) - ); - nowAnimation.play(); - }); - if (!ConfigHolder.isNewlyCreated() || config().getLocalization().getLocale() != Locale.CHINA) - drawerWrapper.getChildren().remove(welcomeView); - - if (!min) buttonsContainer.getChildren().remove(btnMin); - if (!max) buttonsContainer.getChildren().remove(btnMax); - - //JFXDepthManager.setDepth(titleContainer, 1); - titleContainer.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> allowMove = true); - titleContainer.addEventHandler(MouseEvent.MOUSE_EXITED, e -> { - if (!isDragging) allowMove = false; - }); - Rectangle rectangle = new Rectangle(0, 0, 0, 0); - rectangle.widthProperty().bind(titleContainer.widthProperty()); - rectangle.heightProperty().bind(Bindings.createDoubleBinding(() -> titleContainer.getHeight() + 100, titleContainer.heightProperty())); - titleContainer.setClip(rectangle); - - animationHandler = new TransitionHandler(contentPlaceHolder); - - setupBackground(); - setupAuthlibInjectorDnD(); - } - - // ==== Background ==== - private void setupBackground() { - drawerWrapper.backgroundProperty().bind( - Bindings.createObjectBinding( - () -> { - Image image = null; - if (config().getBackgroundImageType() == EnumBackgroundImage.CUSTOM && config().getBackgroundImage() != null) { - image = tryLoadImage(Paths.get(config().getBackgroundImage())) - .orElse(null); - } - if (image == null) { - image = loadDefaultBackgroundImage(); - } - return new Background(new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(800, 480, false, false, true, true))); - }, - config().backgroundImageTypeProperty(), - config().backgroundImageProperty())); - } - - private Image defaultBackground = new Image("/assets/img/background.jpg"); - - /** - * Load background image from bg/, background.png, background.jpg - */ - private Image loadDefaultBackgroundImage() { - Optional image = randomImageIn(Paths.get("bg")); - if (!image.isPresent()) { - image = tryLoadImage(Paths.get("background.png")); - } - if (!image.isPresent()) { - image = tryLoadImage(Paths.get("background.jpg")); - } - return image.orElse(defaultBackground); - } - - private Optional randomImageIn(Path imageDir) { - if (!Files.isDirectory(imageDir)) { - return Optional.empty(); - } - - List candidates; - try { - candidates = Files.list(imageDir) - .filter(Files::isRegularFile) - .filter(it -> { - String filename = it.getFileName().toString(); - return filename.endsWith(".png") || filename.endsWith(".jpg"); - }) - .collect(toList()); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to list files in ./bg", e); - return Optional.empty(); - } - - Random rnd = new Random(); - while (candidates.size() > 0) { - int selected = rnd.nextInt(candidates.size()); - Optional loaded = tryLoadImage(candidates.get(selected)); - if (loaded.isPresent()) { - return Optional.of(loaded.get()); - } else { - candidates.remove(selected); - } - } - return Optional.empty(); - } - - private Optional tryLoadImage(Path path) { - if (Files.isRegularFile(path)) { - try { - return Optional.of(new Image(path.toAbsolutePath().toUri().toString())); - } catch (IllegalArgumentException ignored) { - } - } - return Optional.empty(); - } - - private boolean isMaximized() { - switch (OperatingSystem.CURRENT_OS) { - case OSX: - Rectangle2D bounds = Screen.getPrimary().getVisualBounds(); - return primaryStage.getWidth() >= bounds.getWidth() && primaryStage.getHeight() >= bounds.getHeight(); - default: - return primaryStage.isMaximized(); - } - } - - // ==== - - @FXML - private void onMouseMoved(MouseEvent mouseEvent) { - if (!isMaximized() && !primaryStage.isFullScreen() && !maximized) { - if (!primaryStage.isResizable()) - updateInitMouseValues(mouseEvent); - else { - double x = mouseEvent.getX(), y = mouseEvent.getY(); - Bounds boundsInParent = getBoundsInParent(); - if (getBorder() != null && getBorder().getStrokes().size() > 0) { - double borderWidth = this.contentPlaceHolder.snappedLeftInset(); - if (this.isRightEdge(x, y, boundsInParent)) { - if (y < borderWidth) { - setCursor(Cursor.NE_RESIZE); - } else if (y > this.getHeight() - borderWidth) { - setCursor(Cursor.SE_RESIZE); - } else { - setCursor(Cursor.E_RESIZE); - } - } else if (this.isLeftEdge(x, y, boundsInParent)) { - if (y < borderWidth) { - setCursor(Cursor.NW_RESIZE); - } else if (y > this.getHeight() - borderWidth) { - setCursor(Cursor.SW_RESIZE); - } else { - setCursor(Cursor.W_RESIZE); - } - } else if (this.isTopEdge(x, y, boundsInParent)) { - setCursor(Cursor.N_RESIZE); - } else if (this.isBottomEdge(x, y, boundsInParent)) { - setCursor(Cursor.S_RESIZE); - } else { - setCursor(Cursor.DEFAULT); - } - - this.updateInitMouseValues(mouseEvent); - } - } - } else { - setCursor(Cursor.DEFAULT); - } - } - - @FXML - private void onMouseReleased() { - isDragging = false; - } - - @FXML - private void onMouseDragged(MouseEvent mouseEvent) { - this.isDragging = true; - if (mouseEvent.isPrimaryButtonDown() && (this.xOffset != -1.0 || this.yOffset != -1.0)) { - if (!this.primaryStage.isFullScreen() && !mouseEvent.isStillSincePress() && !isMaximized() && !this.maximized) { - this.newX = mouseEvent.getScreenX(); - this.newY = mouseEvent.getScreenY(); - double deltaX = this.newX - this.initX; - double deltaY = this.newY - this.initY; - Cursor cursor = this.getCursor(); - if (Cursor.E_RESIZE == cursor) { - this.setStageWidth(this.primaryStage.getWidth() + deltaX); - mouseEvent.consume(); - } else if (Cursor.NE_RESIZE == cursor) { - if (this.setStageHeight(this.primaryStage.getHeight() - deltaY)) { - this.primaryStage.setY(this.primaryStage.getY() + deltaY); - } - - this.setStageWidth(this.primaryStage.getWidth() + deltaX); - mouseEvent.consume(); - } else if (Cursor.SE_RESIZE == cursor) { - this.setStageWidth(this.primaryStage.getWidth() + deltaX); - this.setStageHeight(this.primaryStage.getHeight() + deltaY); - mouseEvent.consume(); - } else if (Cursor.S_RESIZE == cursor) { - this.setStageHeight(this.primaryStage.getHeight() + deltaY); - mouseEvent.consume(); - } else if (Cursor.W_RESIZE == cursor) { - if (this.setStageWidth(this.primaryStage.getWidth() - deltaX)) { - this.primaryStage.setX(this.primaryStage.getX() + deltaX); - } - - mouseEvent.consume(); - } else if (Cursor.SW_RESIZE == cursor) { - if (this.setStageWidth(this.primaryStage.getWidth() - deltaX)) { - this.primaryStage.setX(this.primaryStage.getX() + deltaX); - } - - this.setStageHeight(this.primaryStage.getHeight() + deltaY); - mouseEvent.consume(); - } else if (Cursor.NW_RESIZE == cursor) { - if (this.setStageWidth(this.primaryStage.getWidth() - deltaX)) { - this.primaryStage.setX(this.primaryStage.getX() + deltaX); - } - - if (this.setStageHeight(this.primaryStage.getHeight() - deltaY)) { - this.primaryStage.setY(this.primaryStage.getY() + deltaY); - } - - mouseEvent.consume(); - } else if (Cursor.N_RESIZE == cursor) { - if (this.setStageHeight(this.primaryStage.getHeight() - deltaY)) { - this.primaryStage.setY(this.primaryStage.getY() + deltaY); - } - - mouseEvent.consume(); - } else if (this.allowMove) { - this.primaryStage.setX(mouseEvent.getScreenX() - this.xOffset); - this.primaryStage.setY(mouseEvent.getScreenY() - this.yOffset); - mouseEvent.consume(); - } - } - } - } - - @FXML - private void onMin() { - primaryStage.setIconified(true); - } - - @FXML - private void onMax() { - if (!max) return; - if (!this.isCustomMaximize()) { - this.primaryStage.setMaximized(!this.primaryStage.isMaximized()); - this.maximized = this.primaryStage.isMaximized(); - if (this.primaryStage.isMaximized()) { - this.btnMax.setGraphic(resizeMin); - this.btnMax.setTooltip(new Tooltip("Restore Down")); - } else { - this.btnMax.setGraphic(resizeMax); - this.btnMax.setTooltip(new Tooltip("Maximize")); - } - } else { - if (!this.maximized) { - this.originalBox = new BoundingBox(primaryStage.getX(), primaryStage.getY(), primaryStage.getWidth(), primaryStage.getHeight()); - Screen screen = Screen.getScreensForRectangle(primaryStage.getX(), primaryStage.getY(), primaryStage.getWidth(), primaryStage.getHeight()).get(0); - Rectangle2D bounds = screen.getVisualBounds(); - this.maximizedBox = new BoundingBox(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight()); - primaryStage.setX(this.maximizedBox.getMinX()); - primaryStage.setY(this.maximizedBox.getMinY()); - primaryStage.setWidth(this.maximizedBox.getWidth()); - primaryStage.setHeight(this.maximizedBox.getHeight()); - this.btnMax.setGraphic(resizeMin); - this.btnMax.setTooltip(new Tooltip("Restore Down")); - } else { - primaryStage.setX(this.originalBox.getMinX()); - primaryStage.setY(this.originalBox.getMinY()); - primaryStage.setWidth(this.originalBox.getWidth()); - primaryStage.setHeight(this.originalBox.getHeight()); - this.originalBox = null; - this.btnMax.setGraphic(resizeMax); - this.btnMax.setTooltip(new Tooltip("Maximize")); - } - - this.maximized = !this.maximized; - } - } - - @FXML - private void onClose() { - onCloseButtonAction.get().run(); - } - - private void updateInitMouseValues(MouseEvent mouseEvent) { - initX = mouseEvent.getScreenX(); - initY = mouseEvent.getScreenY(); - xOffset = mouseEvent.getSceneX(); - yOffset = mouseEvent.getSceneY(); - } - - private boolean isRightEdge(double x, double y, Bounds boundsInParent) { - return x < getWidth() && x > getWidth() - contentPlaceHolder.snappedLeftInset(); - } - - private boolean isTopEdge(double x, double y, Bounds boundsInParent) { - return y >= 0 && y < contentPlaceHolder.snappedLeftInset(); - } - - private boolean isBottomEdge(double x, double y, Bounds boundsInParent) { - return y < getHeight() && y > getHeight() - contentPlaceHolder.snappedLeftInset(); - } - - private boolean isLeftEdge(double x, double y, Bounds boundsInParent) { - return x >= 0 && x < contentPlaceHolder.snappedLeftInset(); - } - - private boolean setStageWidth(double width) { - if (width >= primaryStage.getMinWidth() && width >= titleContainer.getMinWidth()) { - primaryStage.setWidth(width); - initX = newX; - return true; - } else { - if (width >= primaryStage.getMinWidth() && width <= titleContainer.getMinWidth()) - primaryStage.setWidth(titleContainer.getMinWidth()); - - return false; - } - } - - private boolean setStageHeight(double height) { - if (height >= primaryStage.getMinHeight() && height >= titleContainer.getHeight()) { - primaryStage.setHeight(height); - initY = newY; - return true; - } else { - if (height >= primaryStage.getMinHeight() && height <= titleContainer.getHeight()) - primaryStage.setHeight(titleContainer.getHeight()); - - return false; - } - } - - public void setMaximized(boolean maximized) { - if (this.maximized != maximized) { - Platform.runLater(btnMax::fire); - } - } - - private void showCloseNavButton() { - navLeft.getChildren().add(closeNavButton); - } - - private void hideCloseNavButton() { - navLeft.getChildren().remove(closeNavButton); - } - - private void setContent(Node content, AnimationProducer animation) { - isWizardPageNow = false; - animationHandler.setContent(content, animation); - - if (content instanceof Region) { - ((Region) content).setMinSize(0, 0); - FXUtils.setOverflowHidden((Region) content, true); - } - - refreshNavButton.setVisible(content instanceof Refreshable); - backNavButton.setVisible(content != mainPage); - - String prefix = category == null ? "" : category + " - "; - - titleLabel.textProperty().unbind(); - - if (content instanceof WizardPage) - titleLabel.setText(prefix + ((WizardPage) content).getTitle()); - - if (content instanceof DecoratorPage) - titleLabel.textProperty().bind(((DecoratorPage) content).titleProperty()); - } - - private String category; - private Node nowPage; - private boolean isWizardPageNow; - - public Node getNowPage() { - return nowPage; - } - - public void showPage(Node content) { - FXUtils.checkFxUserThread(); - - contentPlaceHolder.getStyleClass().removeAll("gray-background", "white-background"); - if (content != null) - contentPlaceHolder.getStyleClass().add("gray-background"); - - Node c = content == null ? mainPage : content; - onEnd(); - if (nowPage instanceof DecoratorPage) - ((DecoratorPage) nowPage).onClose(); - nowPage = content; - - setContent(c, ContainerAnimations.FADE.getAnimationProducer()); - - if (c instanceof Region) { - // Let root pane fix window size. - StackPane parent = (StackPane) c.getParent(); - ((Region) c).prefWidthProperty().bind(parent.widthProperty()); - ((Region) c).prefHeightProperty().bind(parent.heightProperty()); - } - } - - public void showDialog(Node node) { - FXUtils.checkFxUserThread(); - - if (dialog == null) { - dialog = new JFXDialog(); - dialogPane = new StackContainerPane(); - - dialog.setContent(dialogPane); - dialog.setDialogContainer(drawerWrapper); - dialog.setOverlayClose(false); - dialog.show(); - } - - dialogPane.push(node); - - EventHandler handler = event -> closeDialog(node); - node.getProperties().put(PROPERTY_DIALOG_CLOSE_HANDLER, handler); - node.addEventHandler(DialogCloseEvent.CLOSE, handler); - } - - @SuppressWarnings("unchecked") - private void closeDialog(Node node) { - FXUtils.checkFxUserThread(); - - Optional.ofNullable(node.getProperties().get(PROPERTY_DIALOG_CLOSE_HANDLER)) - .ifPresent(handler -> node.removeEventHandler(DialogCloseEvent.CLOSE, (EventHandler) handler)); - - if (dialog != null) { - dialogPane.pop(node); - - if (dialogPane.getChildren().isEmpty()) { - dialog.close(); - dialog = null; - dialogPane = null; - } - } - } - - public void startWizard(WizardProvider wizardProvider) { - startWizard(wizardProvider, null); - } - - public void startWizard(WizardProvider wizardProvider, String category) { - FXUtils.checkFxUserThread(); - - this.category = category; - wizardController.setProvider(wizardProvider); - wizardController.onStart(); - } - - @Override - public void onStart() { - backNavButton.setVisible(true); - backNavButton.setDisable(false); - showCloseNavButton(); - refreshNavButton.setVisible(false); - } - - @Override - public void onEnd() { - backNavButton.setVisible(false); - hideCloseNavButton(); - refreshNavButton.setVisible(false); - } - - @Override - public void navigateTo(Node page, Navigation.NavigationDirection nav) { - contentPlaceHolder.getStyleClass().removeAll("gray-background", "white-background"); - contentPlaceHolder.getStyleClass().add("white-background"); - setContent(page, nav.getAnimation().getAnimationProducer()); - isWizardPageNow = true; - } - - @FXML - private void onRefresh() { - ((Refreshable) contentPlaceHolder.getChildren().get(0)).refresh(); - } - - @FXML - private void onCloseNav() { - wizardController.onCancel(); - showPage(null); - } - - @FXML - private void onBack() { - if (isWizardPageNow && wizardController.canPrev()) - wizardController.onPrev(true); - else - onCloseNav(); - } - - @Override - public Queue getCancelQueue() { - return cancelQueue; - } - - public Runnable getOnCloseButtonAction() { - return onCloseButtonAction.get(); - } - - public ObjectProperty onCloseButtonActionProperty() { - return onCloseButtonAction; - } - - public void setOnCloseButtonAction(Runnable onCloseButtonAction) { - this.onCloseButtonAction.set(onCloseButtonAction); - } - - public boolean isCustomMaximize() { - return customMaximize.get(); - } - - public BooleanProperty customMaximizeProperty() { - return customMaximize; - } - - public void setCustomMaximize(boolean customMaximize) { - this.customMaximize.set(customMaximize); - } - - @Override - public WizardController getWizardController() { - return wizardController; - } - - public AdvancedListBox getLeftPane() { - return leftPane; - } - - private void setupAuthlibInjectorDnD() { - addEventFilter(DragEvent.DRAG_OVER, AuthlibInjectorDnD.dragOverHandler()); - addEventFilter(DragEvent.DRAG_DROPPED, AuthlibInjectorDnD.dragDroppedHandler( - url -> Controllers.dialog(new AddAuthlibInjectorServerPane(url)))); - } -} 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 cb2eb266c..07c3acfae 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -43,9 +43,10 @@ import java.util.concurrent.atomic.AtomicReference; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public final class LeftPaneController { +public final class LeftPaneController extends AdvancedListBox { + + public LeftPaneController() { - public LeftPaneController(AdvancedListBox leftPane) { AccountAdvancedListItem accountListItem = new AccountAdvancedListItem(); accountListItem.setOnAction(e -> Controllers.navigate(Controllers.getAccountListPage())); accountListItem.accountProperty().bind(Accounts.selectedAccountProperty()); @@ -67,10 +68,10 @@ public final class LeftPaneController { .then(Color.RED) .otherwise(Color.BLACK)); - launcherSettingsItem.maxWidthProperty().bind(leftPane.widthProperty()); + launcherSettingsItem.maxWidthProperty().bind(widthProperty()); launcherSettingsItem.setOnMouseClicked(e -> Controllers.navigate(Controllers.getSettingsPage())); - leftPane + this .startCategory(i18n("account").toUpperCase()) .add(accountListItem) .startCategory(i18n("version").toUpperCase()) 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 22e0ac691..af12d5e23 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java @@ -23,18 +23,17 @@ import javafx.fxml.FXML; import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.versions.Versions; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; + 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")); - { FXUtils.loadFXML(this, "/assets/fxml/main.fxml"); - } @FXML diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java index 8b35d9eb7..b3588e87c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java @@ -36,7 +36,7 @@ import javafx.scene.paint.Color; import javafx.scene.text.Font; import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.ui.construct.Validator; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.upgrade.UpdateChannel; import org.jackhuang.hmcl.upgrade.RemoteVersion; import org.jackhuang.hmcl.upgrade.UpdateChecker; 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 47a938fd9..35e924311 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java @@ -28,8 +28,8 @@ import javafx.scene.control.Tab; import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask; import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.versions.Versions; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.util.FileUtils; import java.io.File; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountList.java index e2293af2c..9164e908c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountList.java @@ -25,7 +25,7 @@ import javafx.scene.control.ToggleGroup; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.ui.Controllers; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.MappedObservableList; import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java index 29d5e33bd..70c20d4c4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java @@ -23,7 +23,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; import org.jackhuang.hmcl.ui.Controllers; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.MappedObservableList; import javafx.beans.binding.Bindings; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/DialogCloseEvent.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/DialogCloseEvent.java index 6b7d7656b..b4282dacd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/DialogCloseEvent.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/DialogCloseEvent.java @@ -32,7 +32,7 @@ import javafx.scene.layout.Region; */ public class DialogCloseEvent extends Event { - public static final EventType CLOSE = new EventType<>("CLOSE"); + public static final EventType CLOSE = new EventType<>("DIALOG_CLOSE"); public DialogCloseEvent() { super(CLOSE); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/Navigator.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/Navigator.java new file mode 100644 index 000000000..15e50c090 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/Navigator.java @@ -0,0 +1,155 @@ +/* + * 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.construct; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ReadOnlyBooleanWrapper; +import javafx.beans.property.SimpleObjectProperty; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.event.EventTarget; +import javafx.event.EventType; +import javafx.scene.Node; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.animation.ContainerAnimations; +import org.jackhuang.hmcl.ui.animation.TransitionHandler; + +import java.util.Optional; +import java.util.Stack; + +public class Navigator extends StackPane { + private static final String PROPERTY_DIALOG_CLOSE_HANDLER = Navigator.class.getName() + ".closeListener"; + + private final Stack stack = new Stack<>(); + private final TransitionHandler animationHandler = new TransitionHandler(this); + private final ReadOnlyBooleanWrapper canGoBack = new ReadOnlyBooleanWrapper(); + + public Navigator(Node init) { + stack.push(init); + getChildren().setAll(init); + } + + public void navigate(Node node) { + FXUtils.checkFxUserThread(); + + Node from = stack.peek(); + if (from == node) + return; + + stack.push(node); + fireEvent(new NavigationEvent(this, from, NavigationEvent.NAVIGATING)); + setContent(node); + fireEvent(new NavigationEvent(this, node, NavigationEvent.NAVIGATED)); + + EventHandler handler = event -> close(node); + node.getProperties().put(PROPERTY_DIALOG_CLOSE_HANDLER, handler); + node.addEventHandler(PageCloseEvent.CLOSE, handler); + } + + public void close() { + close(stack.peek()); + } + + @SuppressWarnings("unchecked") + public void close(Node from) { + FXUtils.checkFxUserThread(); + + stack.remove(from); + Node node = stack.peek(); + fireEvent(new NavigationEvent(this, from, NavigationEvent.NAVIGATING)); + setContent(node); + fireEvent(new NavigationEvent(this, node, NavigationEvent.NAVIGATED)); + + Optional.ofNullable(from.getProperties().get(PROPERTY_DIALOG_CLOSE_HANDLER)) + .ifPresent(handler -> from.removeEventHandler(PageCloseEvent.CLOSE, (EventHandler) handler)); + } + + public Node getCurrentPage() { + return stack.peek(); + } + + public boolean canGoBack() { + return stack.size() > 1; + } + + private void setContent(Node content) { + animationHandler.setContent(content, ContainerAnimations.FADE.getAnimationProducer()); + + if (content instanceof Region) { + ((Region) content).setMinSize(0, 0); + FXUtils.setOverflowHidden((Region) content, true); + } + } + + public EventHandler getOnNavigated() { + return onNavigated.get(); + } + + public ObjectProperty> onNavigatedProperty() { + return onNavigated; + } + + public void setOnNavigated(EventHandler onNavigated) { + this.onNavigated.set(onNavigated); + } + + private ObjectProperty> onNavigated = new SimpleObjectProperty>(this, "onNavigated") { + @Override + protected void invalidated() { + setEventHandler(NavigationEvent.NAVIGATED, get()); + } + }; + + public EventHandler getOnNavigating() { + return onNavigating.get(); + } + + public ObjectProperty> onNavigatingProperty() { + return onNavigating; + } + + public void setOnNavigating(EventHandler onNavigating) { + this.onNavigating.set(onNavigating); + } + + private ObjectProperty> onNavigating = new SimpleObjectProperty>(this, "onNavigating") { + @Override + protected void invalidated() { + setEventHandler(NavigationEvent.NAVIGATING, get()); + } + }; + + public static class NavigationEvent extends Event { + public static final EventType NAVIGATED = new EventType<>("NAVIGATED"); + public static final EventType NAVIGATING = new EventType<>("NAVIGATING"); + + private final Node node; + + public NavigationEvent(Object source, Node target, EventType eventType) { + super(source, target, eventType); + + this.node = target; + } + + public Node getNode() { + return node; + } + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/PageCloseEvent.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/PageCloseEvent.java new file mode 100644 index 000000000..55601ded6 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/PageCloseEvent.java @@ -0,0 +1,41 @@ +/* + * 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.construct; + +import javafx.event.Event; +import javafx.event.EventTarget; +import javafx.event.EventType; + +/** + * Indicates a close operation on the navigator page. + * + * @author huangyuhui + */ +public class PageCloseEvent extends Event { + + public static final EventType CLOSE = new EventType<>("PAGE_CLOSE"); + + public PageCloseEvent() { + super(CLOSE); + } + + public PageCloseEvent(Object source, EventTarget target) { + super(source, target, CLOSE); + } + +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorControl.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorControl.java new file mode 100644 index 000000000..6725fe552 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorControl.java @@ -0,0 +1,201 @@ +/* + * 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.decorator; + +import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.Node; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; +import javafx.scene.layout.Background; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; +import javafx.stage.StageStyle; + +public class DecoratorControl extends Control { + private final ListProperty drawer = new SimpleListProperty<>(FXCollections.observableArrayList()); + private final ListProperty content = new SimpleListProperty<>(FXCollections.observableArrayList()); + private final ListProperty container = new SimpleListProperty<>(FXCollections.observableArrayList()); + private final ObjectProperty contentBackground = new SimpleObjectProperty<>(); + private final StringProperty title = new SimpleStringProperty(); + private final StringProperty drawerTitle = new SimpleStringProperty(); + private final ObjectProperty onCloseButtonAction = new SimpleObjectProperty<>(); + private final ObjectProperty> onCloseNavButtonAction = new SimpleObjectProperty<>(); + private final ObjectProperty> onBackNavButtonAction = new SimpleObjectProperty<>(); + private final ObjectProperty> onRefreshNavButtonAction = new SimpleObjectProperty<>(); + private final BooleanProperty closeNavButtonVisible = new SimpleBooleanProperty(true); + private final BooleanProperty canRefresh = new SimpleBooleanProperty(false); + private final BooleanProperty canBack = new SimpleBooleanProperty(false); + private final BooleanProperty canClose = new SimpleBooleanProperty(false); + private final Stage primaryStage; + private StackPane drawerWrapper; + + public DecoratorControl(Stage primaryStage) { + this.primaryStage = primaryStage; + + primaryStage.initStyle(StageStyle.UNDECORATED); + } + + public Stage getPrimaryStage() { + return primaryStage; + } + + public StackPane getDrawerWrapper() { + return drawerWrapper; + } + + void setDrawerWrapper(StackPane drawerWrapper) { + this.drawerWrapper = drawerWrapper; + } + + public ObservableList getDrawer() { + return drawer.get(); + } + + public ListProperty drawerProperty() { + return drawer; + } + + public void setDrawer(ObservableList drawer) { + this.drawer.set(drawer); + } + + public ObservableList getContent() { + return content.get(); + } + + public ListProperty contentProperty() { + return content; + } + + public void setContent(ObservableList content) { + this.content.set(content); + } + + public String getTitle() { + return title.get(); + } + + public StringProperty titleProperty() { + return title; + } + + public void setTitle(String title) { + this.title.set(title); + } + + public String getDrawerTitle() { + return drawerTitle.get(); + } + + public StringProperty drawerTitleProperty() { + return drawerTitle; + } + + public void setDrawerTitle(String drawerTitle) { + this.drawerTitle.set(drawerTitle); + } + + public Runnable getOnCloseButtonAction() { + return onCloseButtonAction.get(); + } + + public ObjectProperty onCloseButtonActionProperty() { + return onCloseButtonAction; + } + + public void setOnCloseButtonAction(Runnable onCloseButtonAction) { + this.onCloseButtonAction.set(onCloseButtonAction); + } + + public boolean isCloseNavButtonVisible() { + return closeNavButtonVisible.get(); + } + + public BooleanProperty closeNavButtonVisibleProperty() { + return closeNavButtonVisible; + } + + public void setCloseNavButtonVisible(boolean closeNavButtonVisible) { + this.closeNavButtonVisible.set(closeNavButtonVisible); + } + + public ObservableList getContainer() { + return container.get(); + } + + public ListProperty containerProperty() { + return container; + } + + public void setContainer(ObservableList container) { + this.container.set(container); + } + + public Background getContentBackground() { + return contentBackground.get(); + } + + public ObjectProperty contentBackgroundProperty() { + return contentBackground; + } + + public void setContentBackground(Background contentBackground) { + this.contentBackground.set(contentBackground); + } + + public BooleanProperty canRefreshProperty() { + return canRefresh; + } + + public BooleanProperty canBackProperty() { + return canBack; + } + + public BooleanProperty canCloseProperty() { + return canClose; + } + + public ObjectProperty> onBackNavButtonActionProperty() { + return onBackNavButtonAction; + } + + public ObjectProperty> onCloseNavButtonActionProperty() { + return onCloseNavButtonAction; + } + + public ObjectProperty> onRefreshNavButtonActionProperty() { + return onRefreshNavButtonAction; + } + + @Override + protected Skin createDefaultSkin() { + return new DecoratorSkin(this); + } + + public void minimize() { + primaryStage.setIconified(true); + } + + public void close() { + onCloseButtonAction.get().run(); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java new file mode 100644 index 000000000..469b4b4ba --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java @@ -0,0 +1,374 @@ +/* + * 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.decorator; + +import com.jfoenix.controls.JFXDialog; +import javafx.animation.Interpolator; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; +import javafx.beans.binding.Bindings; +import javafx.event.EventHandler; +import javafx.event.EventTarget; +import javafx.geometry.Insets; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.DragEvent; +import javafx.scene.layout.*; +import javafx.scene.paint.Color; +import javafx.stage.Stage; +import javafx.util.Duration; +import org.jackhuang.hmcl.Launcher; +import org.jackhuang.hmcl.Metadata; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDnD; +import org.jackhuang.hmcl.setting.ConfigHolder; +import org.jackhuang.hmcl.setting.EnumBackgroundImage; +import org.jackhuang.hmcl.task.TaskExecutor; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.construct.Navigator; +import org.jackhuang.hmcl.ui.account.AddAuthlibInjectorServerPane; +import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.ui.wizard.Refreshable; +import org.jackhuang.hmcl.ui.wizard.WizardProvider; +import org.jackhuang.hmcl.util.FutureCallback; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import java.util.function.Consumer; +import java.util.logging.Level; + +import static java.util.stream.Collectors.toList; +import static org.jackhuang.hmcl.setting.ConfigHolder.config; +import static org.jackhuang.hmcl.util.Logging.LOG; + +public class DecoratorController { + private static final String PROPERTY_DIALOG_CLOSE_HANDLER = DecoratorController.class.getName() + ".dialog.closeListener"; + + private final DecoratorControl decorator; + private final ImageView welcomeView; + private final Navigator navigator; + private final Node mainPage; + + private JFXDialog dialog; + private StackContainerPane dialogPane; + + public DecoratorController(Stage stage, Node mainPage) { + this.mainPage = mainPage; + + decorator = new DecoratorControl(stage); + decorator.titleProperty().set(Metadata.TITLE); + decorator.setOnCloseButtonAction(Launcher::stopApplication); + + navigator = new Navigator(mainPage); + navigator.setOnNavigating(this::onNavigating); + navigator.setOnNavigated(this::onNavigated); + + decorator.getContent().setAll(navigator); + decorator.onCloseNavButtonActionProperty().set(e -> close()); + decorator.onBackNavButtonActionProperty().set(e -> back()); + decorator.onRefreshNavButtonActionProperty().set(e -> refresh()); + + welcomeView = new ImageView(); + welcomeView.setImage(new Image("/assets/img/welcome.png")); + welcomeView.setCursor(Cursor.HAND); + welcomeView.setOnMouseClicked(e -> { + Timeline nowAnimation = new Timeline(); + nowAnimation.getKeyFrames().addAll( + new KeyFrame(Duration.ZERO, new KeyValue(welcomeView.opacityProperty(), 1.0D, Interpolator.EASE_BOTH)), + new KeyFrame(new Duration(300), new KeyValue(welcomeView.opacityProperty(), 0.0D, Interpolator.EASE_BOTH)), + new KeyFrame(new Duration(300), e2 -> decorator.getContainer().remove(welcomeView)) + ); + nowAnimation.play(); + }); + if (ConfigHolder.isNewlyCreated() && config().getLocalization().getLocale() == Locale.CHINA) + decorator.getContainer().setAll(welcomeView); + + setupBackground(); + + setupAuthlibInjectorDnD(); + } + + public DecoratorControl getDecorator() { + return decorator; + } + + // ==== Background ==== + + private void setupBackground() { + decorator.backgroundProperty().bind( + Bindings.createObjectBinding( + () -> { + Image image = null; + if (config().getBackgroundImageType() == EnumBackgroundImage.CUSTOM && config().getBackgroundImage() != null) { + image = tryLoadImage(Paths.get(config().getBackgroundImage())) + .orElse(null); + } + if (image == null) { + image = loadDefaultBackgroundImage(); + } + return new Background(new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(800, 480, false, false, true, true))); + }, + config().backgroundImageTypeProperty(), + config().backgroundImageProperty())); + } + + private Image defaultBackground = new Image("/assets/img/background.jpg"); + + /** + * Load background image from bg/, background.png, background.jpg + */ + private Image loadDefaultBackgroundImage() { + Optional image = randomImageIn(Paths.get("bg")); + if (!image.isPresent()) { + image = tryLoadImage(Paths.get("background.png")); + } + if (!image.isPresent()) { + image = tryLoadImage(Paths.get("background.jpg")); + } + return image.orElse(defaultBackground); + } + + private Optional randomImageIn(Path imageDir) { + if (!Files.isDirectory(imageDir)) { + return Optional.empty(); + } + + List candidates; + try { + candidates = Files.list(imageDir) + .filter(Files::isRegularFile) + .filter(it -> { + String filename = it.getFileName().toString(); + return filename.endsWith(".png") || filename.endsWith(".jpg"); + }) + .collect(toList()); + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to list files in ./bg", e); + return Optional.empty(); + } + + Random rnd = new Random(); + while (candidates.size() > 0) { + int selected = rnd.nextInt(candidates.size()); + Optional loaded = tryLoadImage(candidates.get(selected)); + if (loaded.isPresent()) { + return loaded; + } else { + candidates.remove(selected); + } + } + return Optional.empty(); + } + + private Optional tryLoadImage(Path path) { + if (Files.isRegularFile(path)) { + try { + return Optional.of(new Image(path.toAbsolutePath().toUri().toString())); + } catch (IllegalArgumentException ignored) { + } + } + return Optional.empty(); + } + + // ==== Navigation ==== + + public Navigator getNavigator() { + return navigator; + } + + private void close() { + if (navigator.getCurrentPage() instanceof DecoratorPage) { + DecoratorPage page = (DecoratorPage) navigator.getCurrentPage(); + + page.onForceToClose(); + } else { + navigator.close(); + } + } + + private void back() { + if (navigator.getCurrentPage() instanceof DecoratorPage) { + DecoratorPage page = (DecoratorPage) navigator.getCurrentPage(); + + if (page.onClose()) + navigator.close(); + } else { + navigator.close(); + } + } + + private void refresh() { + if (navigator.getCurrentPage() instanceof Refreshable) { + Refreshable refreshable = (Refreshable) navigator.getCurrentPage(); + + if (refreshable.canRefreshProperty().get()) + refreshable.refresh(); + } + } + + private void onNavigating(Navigator.NavigationEvent event) { + Node from = event.getNode(); + + if (from instanceof DecoratorPage) + ((DecoratorPage) from).onClose(); + } + + private void onNavigated(Navigator.NavigationEvent event) { + Node to = event.getNode(); + + if (to instanceof Refreshable) { + decorator.canRefreshProperty().bind(((Refreshable) to).canRefreshProperty()); + } else { + decorator.canRefreshProperty().unbind(); + decorator.canRefreshProperty().set(false); + } + + if (to instanceof DecoratorPage) { + decorator.drawerTitleProperty().bind(((DecoratorPage) to).titleProperty()); + decorator.canCloseProperty().set(((DecoratorPage) to).canForceToClose()); + } else { + decorator.drawerTitleProperty().unbind(); + decorator.drawerTitleProperty().set(""); + decorator.canCloseProperty().set(false); + } + + decorator.canBackProperty().set(navigator.canGoBack()); + + if (navigator.canGoBack()) { + decorator.setContentBackground(new Background(new BackgroundFill(Color.rgb(244, 244, 244, 0.5), CornerRadii.EMPTY, Insets.EMPTY))); + } else { + decorator.setContentBackground(null); + } + + if (to instanceof Region) { + Region region = (Region) to; + // Let root pane fix window size. + StackPane parent = (StackPane) region.getParent(); + region.prefWidthProperty().bind(parent.widthProperty()); + region.prefHeightProperty().bind(parent.heightProperty()); + } + } + + // ==== Dialog ==== + + public void showDialog(Node node) { + FXUtils.checkFxUserThread(); + + if (dialog == null) { + dialog = new JFXDialog(); + dialogPane = new StackContainerPane(); + + dialog.setContent(dialogPane); + dialog.setDialogContainer(decorator.getDrawerWrapper()); + dialog.setOverlayClose(false); + dialog.show(); + } + + dialogPane.push(node); + + EventHandler handler = event -> closeDialog(node); + node.getProperties().put(PROPERTY_DIALOG_CLOSE_HANDLER, handler); + node.addEventHandler(DialogCloseEvent.CLOSE, handler); + } + + @SuppressWarnings("unchecked") + private void closeDialog(Node node) { + FXUtils.checkFxUserThread(); + + Optional.ofNullable(node.getProperties().get(PROPERTY_DIALOG_CLOSE_HANDLER)) + .ifPresent(handler -> node.removeEventHandler(DialogCloseEvent.CLOSE, (EventHandler) handler)); + + if (dialog != null) { + dialogPane.pop(node); + + if (dialogPane.getChildren().isEmpty()) { + dialog.close(); + dialog = null; + dialogPane = null; + } + } + } + + public void showDialog(String text) { + showDialog(text, null); + } + + public void showDialog(String text, String title) { + showDialog(text, title, MessageBox.INFORMATION_MESSAGE); + } + + public void showDialog(String text, String title, int type) { + showDialog(text, title, type, null); + } + + public void showDialog(String text, String title, int type, Runnable onAccept) { + showDialog(new MessageDialogPane(text, title, type, onAccept)); + } + + public void showConfirmDialog(String text, String title, Runnable onAccept, Runnable onCancel) { + showDialog(new MessageDialogPane(text, title, onAccept, onCancel)); + } + + public InputDialogPane showInputDialog(String text, FutureCallback onResult) { + InputDialogPane pane = new InputDialogPane(text, onResult); + showDialog(pane); + return pane; + } + + public Region showTaskDialog(TaskExecutor executor, String title, String subtitle) { + return showTaskDialog(executor, title, subtitle, null); + } + + public Region showTaskDialog(TaskExecutor executor, String title, String subtitle, Consumer onCancel) { + TaskExecutorDialogPane pane = new TaskExecutorDialogPane(onCancel); + pane.setTitle(title); + pane.setSubtitle(subtitle); + pane.setExecutor(executor); + showDialog(pane); + return pane; + } + + // ==== Wizard ==== + + public void startWizard(WizardProvider wizardProvider) { + startWizard(wizardProvider, null); + } + + public void startWizard(WizardProvider wizardProvider, String category) { + FXUtils.checkFxUserThread(); + + getNavigator().navigate(new DecoratorWizardDisplayer(wizardProvider, category)); + } + + // ==== Authlib Injector DnD ==== + + private void setupAuthlibInjectorDnD() { + decorator.addEventFilter(DragEvent.DRAG_OVER, AuthlibInjectorDnD.dragOverHandler()); + decorator.addEventFilter(DragEvent.DRAG_DROPPED, AuthlibInjectorDnD.dragDroppedHandler( + url -> Controllers.dialog(new AddAuthlibInjectorServerPane(url)))); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/DecoratorPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorPage.java similarity index 80% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/DecoratorPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorPage.java index 239e3ee5e..658c73e3f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/DecoratorPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorPage.java @@ -15,13 +15,21 @@ * 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.wizard; +package org.jackhuang.hmcl.ui.decorator; import javafx.beans.property.StringProperty; public interface DecoratorPage { StringProperty titleProperty(); - default void onClose() { + default boolean canForceToClose() { + return false; + } + + default boolean onClose() { + return true; + } + + default void onForceToClose() { } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorSkin.java new file mode 100644 index 000000000..f43925753 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorSkin.java @@ -0,0 +1,422 @@ +/* + * 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.decorator; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.svg.SVGGlyph; +import javafx.beans.binding.Bindings; +import javafx.collections.ListChangeListener; +import javafx.geometry.BoundingBox; +import javafx.geometry.Bounds; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.SkinBase; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; +import javafx.stage.Stage; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.util.Lang; + +public class DecoratorSkin extends SkinBase { + private static final SVGGlyph minus = Lang.apply(new SVGGlyph(0, "MINUS", "M804.571 420.571v109.714q0 22.857-16 38.857t-38.857 16h-694.857q-22.857 0-38.857-16t-16-38.857v-109.714q0-22.857 16-38.857t38.857-16h694.857q22.857 0 38.857 16t16 38.857z", Color.WHITE), + glyph -> { glyph.setSize(12, 2); glyph.setTranslateY(4); }); + + private final BorderPane titleContainer; + private final StackPane contentPlaceHolder; + private final JFXButton refreshNavButton; + private final JFXButton closeNavButton; + private final HBox navLeft; + private final Stage primaryStage; + + private double xOffset, yOffset, newX, newY, initX, initY; + private boolean allowMove, isDragging; + private BoundingBox originalBox, maximizedBox; + + /** + * Constructor for all SkinBase instances. + * + * @param control The control for which this Skin should attach to. + */ + public DecoratorSkin(DecoratorControl control) { + super(control); + + primaryStage = control.getPrimaryStage(); + + minus.fillProperty().bind(Theme.foregroundFillBinding()); + + DecoratorControl skinnable = getSkinnable(); + + BorderPane root = new BorderPane(); + root.getStyleClass().setAll("jfx-decorator", "resize-border"); + root.setMaxHeight(519); + root.setMaxWidth(800); + + StackPane drawerWrapper = new StackPane(); + skinnable.setDrawerWrapper(drawerWrapper); + drawerWrapper.getStyleClass().setAll("jfx-decorator-drawer"); + drawerWrapper.backgroundProperty().bind(skinnable.backgroundProperty()); + FXUtils.setOverflowHidden(drawerWrapper, true); + { + BorderPane drawer = new BorderPane(); + + { + BorderPane leftRootPane = new BorderPane(); + FXUtils.setLimitWidth(leftRootPane, 200); + leftRootPane.getStyleClass().setAll("jfx-decorator-content-container"); + + StackPane drawerContainer = new StackPane(); + drawerContainer.getStyleClass().setAll("gray-background"); + Bindings.bindContent(drawerContainer.getChildren(), skinnable.drawerProperty()); + leftRootPane.setCenter(drawerContainer); + + Rectangle separator = new Rectangle(); + separator.heightProperty().bind(drawer.heightProperty()); + separator.setWidth(1); + separator.setFill(Color.GRAY); + + leftRootPane.setRight(separator); + + drawer.setLeft(leftRootPane); + } + + { + contentPlaceHolder = new StackPane(); + contentPlaceHolder.getStyleClass().setAll("jfx-decorator-content-container"); + contentPlaceHolder.backgroundProperty().bind(skinnable.contentBackgroundProperty()); + FXUtils.setOverflowHidden(contentPlaceHolder, true); + Bindings.bindContent(contentPlaceHolder.getChildren(), skinnable.contentProperty()); + + drawer.setCenter(contentPlaceHolder); + } + + drawerWrapper.getChildren().add(drawer); + } + + { + StackPane container = new StackPane(); + Bindings.bindContent(container.getChildren(), skinnable.containerProperty()); + ListChangeListener listener = new ListChangeListener() { + @Override + public void onChanged(Change c) { + if (skinnable.getContainer().isEmpty()) { + container.setMouseTransparent(true); + container.setVisible(false); + } else { + container.setMouseTransparent(false); + container.setVisible(true); + } + } + }; + skinnable.containerProperty().addListener(listener); + listener.onChanged(null); + + drawerWrapper.getChildren().add(container); + } + + root.setCenter(drawerWrapper); + + titleContainer = new BorderPane(); + titleContainer.setOnMouseReleased(this::onMouseReleased); + titleContainer.setOnMouseDragged(this::onMouseDragged); + titleContainer.setOnMouseMoved(this::onMouseMoved); + titleContainer.setPickOnBounds(false); + titleContainer.setMinHeight(40); + titleContainer.getStyleClass().setAll("jfx-tool-bar"); + titleContainer.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> allowMove = true); + titleContainer.addEventHandler(MouseEvent.MOUSE_EXITED, e -> { + if (!isDragging) allowMove = false; + }); + + Rectangle rectangle = new Rectangle(0, 0, 0, 0); + rectangle.widthProperty().bind(titleContainer.widthProperty()); + rectangle.heightProperty().bind(Bindings.createDoubleBinding(() -> titleContainer.getHeight() + 100, titleContainer.heightProperty())); + titleContainer.setClip(rectangle); + { + BorderPane titleWrapper = new BorderPane(); + FXUtils.setLimitWidth(titleWrapper, 200); + { + Label lblTitle = new Label(); + BorderPane.setMargin(lblTitle, new Insets(0, 0, 0, 3)); + lblTitle.setStyle("-fx-background-color: transparent; -fx-text-fill: -fx-base-text-fill; -fx-font-size: 15px;"); + lblTitle.setMouseTransparent(true); + lblTitle.textProperty().bind(skinnable.titleProperty()); + BorderPane.setAlignment(lblTitle, Pos.CENTER); + titleWrapper.setCenter(lblTitle); + + Rectangle separator = new Rectangle(); + separator.heightProperty().bind(titleWrapper.heightProperty()); + separator.setWidth(1); + separator.setFill(Color.GRAY); + titleWrapper.setRight(separator); + } + titleContainer.setLeft(titleWrapper); + + BorderPane navBar = new BorderPane(); + { + navLeft = new HBox(); + navLeft.setAlignment(Pos.CENTER_LEFT); + navLeft.setPadding(new Insets(0, 5, 0, 5)); + { + JFXButton backNavButton = new JFXButton(); + backNavButton.setGraphic(SVG.back(Theme.foregroundFillBinding(), -1, -1)); + backNavButton.getStyleClass().setAll("jfx-decorator-button"); + backNavButton.ripplerFillProperty().bind(Theme.whiteFillBinding()); + backNavButton.onActionProperty().bind(skinnable.onBackNavButtonActionProperty()); + backNavButton.visibleProperty().bind(skinnable.canBackProperty()); + + closeNavButton = new JFXButton(); + closeNavButton.setGraphic(SVG.close(Theme.foregroundFillBinding(), -1, -1)); + closeNavButton.getStyleClass().setAll("jfx-decorator-button"); + closeNavButton.ripplerFillProperty().bind(Theme.whiteFillBinding()); + closeNavButton.onActionProperty().bind(skinnable.onCloseNavButtonActionProperty()); + + navLeft.getChildren().setAll(backNavButton); + + skinnable.canCloseProperty().addListener((a, b, newValue) -> { + if (newValue) navLeft.getChildren().setAll(backNavButton, closeNavButton); + else navLeft.getChildren().setAll(backNavButton); + }); + } + navBar.setLeft(navLeft); + + VBox navCenter = new VBox(); + navCenter.setAlignment(Pos.CENTER_LEFT); + Label titleLabel = new Label(); + titleLabel.getStyleClass().setAll("jfx-decorator-title"); + titleLabel.textProperty().bind(skinnable.drawerTitleProperty()); + navCenter.getChildren().setAll(titleLabel); + navBar.setCenter(navCenter); + + HBox navRight = new HBox(); + navRight.setAlignment(Pos.CENTER_RIGHT); + refreshNavButton = new JFXButton(); + refreshNavButton.setGraphic(SVG.refresh(Theme.foregroundFillBinding(), -1, -1)); + refreshNavButton.getStyleClass().setAll("jfx-decorator-button"); + refreshNavButton.ripplerFillProperty().bind(Theme.whiteFillBinding()); + refreshNavButton.onActionProperty().bind(skinnable.onRefreshNavButtonActionProperty()); + refreshNavButton.visibleProperty().bind(skinnable.canRefreshProperty()); + navRight.getChildren().setAll(refreshNavButton); + navBar.setRight(navRight); + } + titleContainer.setCenter(navBar); + + HBox buttonsContainer = new HBox(); + buttonsContainer.setStyle("-fx-background-color: transparent;"); + buttonsContainer.setAlignment(Pos.CENTER_RIGHT); + buttonsContainer.setPadding(new Insets(4)); + { + Rectangle separator = new Rectangle(); + separator.visibleProperty().bind(refreshNavButton.visibleProperty()); + separator.heightProperty().bind(navBar.heightProperty()); + + JFXButton btnMin = new JFXButton(); + StackPane pane = new StackPane(minus); + pane.setAlignment(Pos.CENTER); + btnMin.setGraphic(pane); + btnMin.getStyleClass().setAll("jfx-decorator-button"); + btnMin.setOnAction(e -> skinnable.minimize()); + + JFXButton btnClose = new JFXButton(); + btnClose.setGraphic(SVG.close(Theme.foregroundFillBinding(), -1, -1)); + btnClose.getStyleClass().setAll("jfx-decorator-button"); + btnClose.setOnAction(e -> skinnable.close()); + + buttonsContainer.getChildren().setAll(separator, btnMin, btnClose); + } + titleContainer.setRight(buttonsContainer); + } + root.setTop(titleContainer); + + getChildren().setAll(root); + + getSkinnable().closeNavButtonVisibleProperty().addListener((a, b, newValue) -> { + if (newValue) navLeft.getChildren().add(closeNavButton); + else navLeft.getChildren().remove(closeNavButton); + }); + } + + private void updateInitMouseValues(MouseEvent mouseEvent) { + initX = mouseEvent.getScreenX(); + initY = mouseEvent.getScreenY(); + xOffset = mouseEvent.getSceneX(); + yOffset = mouseEvent.getSceneY(); + } + + private boolean isRightEdge(double x, double y, Bounds boundsInParent) { + return x < getSkinnable().getWidth() && x > getSkinnable().getWidth() - contentPlaceHolder.snappedLeftInset(); + } + + private boolean isTopEdge(double x, double y, Bounds boundsInParent) { + return y >= 0 && y < contentPlaceHolder.snappedLeftInset(); + } + + private boolean isBottomEdge(double x, double y, Bounds boundsInParent) { + return y < getSkinnable().getHeight() && y > getSkinnable().getHeight() - contentPlaceHolder.snappedLeftInset(); + } + + private boolean isLeftEdge(double x, double y, Bounds boundsInParent) { + return x >= 0 && x < contentPlaceHolder.snappedLeftInset(); + } + + private boolean setStageWidth(double width) { + if (width >= primaryStage.getMinWidth() && width >= titleContainer.getMinWidth()) { + primaryStage.setWidth(width); + initX = newX; + return true; + } else { + if (width >= primaryStage.getMinWidth() && width <= titleContainer.getMinWidth()) + primaryStage.setWidth(titleContainer.getMinWidth()); + + return false; + } + } + + private boolean setStageHeight(double height) { + if (height >= primaryStage.getMinHeight() && height >= titleContainer.getHeight()) { + primaryStage.setHeight(height); + initY = newY; + return true; + } else { + if (height >= primaryStage.getMinHeight() && height <= titleContainer.getHeight()) + primaryStage.setHeight(titleContainer.getHeight()); + + return false; + } + } + + // ==== + + protected void onMouseMoved(MouseEvent mouseEvent) { + if (!primaryStage.isFullScreen()) { + if (!primaryStage.isResizable()) + updateInitMouseValues(mouseEvent); + else { + double x = mouseEvent.getX(), y = mouseEvent.getY(); + Bounds boundsInParent = getSkinnable().getBoundsInParent(); + if (getSkinnable().getBorder() != null && getSkinnable().getBorder().getStrokes().size() > 0) { + double borderWidth = contentPlaceHolder.snappedLeftInset(); + if (this.isRightEdge(x, y, boundsInParent)) { + if (y < borderWidth) { + getSkinnable().setCursor(Cursor.NE_RESIZE); + } else if (y > getSkinnable().getHeight() - borderWidth) { + getSkinnable().setCursor(Cursor.SE_RESIZE); + } else { + getSkinnable().setCursor(Cursor.E_RESIZE); + } + } else if (this.isLeftEdge(x, y, boundsInParent)) { + if (y < borderWidth) { + getSkinnable().setCursor(Cursor.NW_RESIZE); + } else if (y > getSkinnable().getHeight() - borderWidth) { + getSkinnable().setCursor(Cursor.SW_RESIZE); + } else { + getSkinnable().setCursor(Cursor.W_RESIZE); + } + } else if (this.isTopEdge(x, y, boundsInParent)) { + getSkinnable().setCursor(Cursor.N_RESIZE); + } else if (this.isBottomEdge(x, y, boundsInParent)) { + getSkinnable().setCursor(Cursor.S_RESIZE); + } else { + getSkinnable().setCursor(Cursor.DEFAULT); + } + + this.updateInitMouseValues(mouseEvent); + } + } + } else { + getSkinnable().setCursor(Cursor.DEFAULT); + } + } + + protected void onMouseReleased(MouseEvent mouseEvent) { + isDragging = false; + } + + protected void onMouseDragged(MouseEvent mouseEvent) { + this.isDragging = true; + if (mouseEvent.isPrimaryButtonDown() && (this.xOffset != -1.0 || this.yOffset != -1.0)) { + if (!this.primaryStage.isFullScreen() && !mouseEvent.isStillSincePress()) { + this.newX = mouseEvent.getScreenX(); + this.newY = mouseEvent.getScreenY(); + double deltaX = this.newX - this.initX; + double deltaY = this.newY - this.initY; + Cursor cursor = getSkinnable().getCursor(); + if (Cursor.E_RESIZE == cursor) { + this.setStageWidth(this.primaryStage.getWidth() + deltaX); + mouseEvent.consume(); + } else if (Cursor.NE_RESIZE == cursor) { + if (this.setStageHeight(this.primaryStage.getHeight() - deltaY)) { + this.primaryStage.setY(this.primaryStage.getY() + deltaY); + } + + this.setStageWidth(this.primaryStage.getWidth() + deltaX); + mouseEvent.consume(); + } else if (Cursor.SE_RESIZE == cursor) { + this.setStageWidth(this.primaryStage.getWidth() + deltaX); + this.setStageHeight(this.primaryStage.getHeight() + deltaY); + mouseEvent.consume(); + } else if (Cursor.S_RESIZE == cursor) { + this.setStageHeight(this.primaryStage.getHeight() + deltaY); + mouseEvent.consume(); + } else if (Cursor.W_RESIZE == cursor) { + if (this.setStageWidth(this.primaryStage.getWidth() - deltaX)) { + this.primaryStage.setX(this.primaryStage.getX() + deltaX); + } + + mouseEvent.consume(); + } else if (Cursor.SW_RESIZE == cursor) { + if (this.setStageWidth(this.primaryStage.getWidth() - deltaX)) { + this.primaryStage.setX(this.primaryStage.getX() + deltaX); + } + + this.setStageHeight(this.primaryStage.getHeight() + deltaY); + mouseEvent.consume(); + } else if (Cursor.NW_RESIZE == cursor) { + if (this.setStageWidth(this.primaryStage.getWidth() - deltaX)) { + this.primaryStage.setX(this.primaryStage.getX() + deltaX); + } + + if (this.setStageHeight(this.primaryStage.getHeight() - deltaY)) { + this.primaryStage.setY(this.primaryStage.getY() + deltaY); + } + + mouseEvent.consume(); + } else if (Cursor.N_RESIZE == cursor) { + if (this.setStageHeight(this.primaryStage.getHeight() - deltaY)) { + this.primaryStage.setY(this.primaryStage.getY() + deltaY); + } + + mouseEvent.consume(); + } else if (this.allowMove) { + this.primaryStage.setX(mouseEvent.getScreenX() - this.xOffset); + this.primaryStage.setY(mouseEvent.getScreenY() - this.yOffset); + mouseEvent.consume(); + } + } + } + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorWizardDisplayer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorWizardDisplayer.java new file mode 100644 index 000000000..7e16b0c62 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorWizardDisplayer.java @@ -0,0 +1,126 @@ +/* + * 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.decorator; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.scene.Node; +import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.ui.animation.TransitionHandler; +import org.jackhuang.hmcl.ui.construct.PageCloseEvent; +import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogWizardDisplayer; +import org.jackhuang.hmcl.ui.wizard.*; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class DecoratorWizardDisplayer extends StackPane implements TaskExecutorDialogWizardDisplayer, Refreshable, DecoratorPage { + private final StringProperty title = new SimpleStringProperty(); + private final BooleanProperty canRefresh = new SimpleBooleanProperty(); + + private final TransitionHandler transitionHandler = new TransitionHandler(this); + private final WizardController wizardController = new WizardController(this); + private final Queue cancelQueue = new ConcurrentLinkedQueue<>(); + + private final String category; + + private Node nowPage; + + public DecoratorWizardDisplayer(WizardProvider provider) { + this(provider, null); + } + + public DecoratorWizardDisplayer(WizardProvider provider, String category) { + this.category = category; + + wizardController.setProvider(provider); + wizardController.onStart(); + + getStyleClass().setAll("white-background"); + } + + @Override + public StringProperty titleProperty() { + return title; + } + + @Override + public BooleanProperty canRefreshProperty() { + return canRefresh; + } + + @Override + public WizardController getWizardController() { + return wizardController; + } + + @Override + public Queue getCancelQueue() { + return cancelQueue; + } + + @Override + public void onStart() { + + } + + @Override + public void onEnd() { + fireEvent(new PageCloseEvent()); + } + + @Override + public void navigateTo(Node page, Navigation.NavigationDirection nav) { + nowPage = page; + + transitionHandler.setContent(page, nav.getAnimation().getAnimationProducer()); + + canRefresh.set(page instanceof Refreshable); + + String prefix = category == null ? "" : category + " - "; + + if (page instanceof WizardPage) + title.set(prefix + ((WizardPage) page).getTitle()); + } + + @Override + public boolean canForceToClose() { + return true; + } + + @Override + public void onForceToClose() { + wizardController.onCancel(); + } + + @Override + public boolean onClose() { + if (wizardController.canPrev()) { + wizardController.onPrev(true); + return false; + } else + return true; + } + + @Override + public void refresh() { + ((Refreshable) nowPage).refresh(); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java index b1121f6b7..e11925936 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java @@ -27,6 +27,7 @@ import org.jackhuang.hmcl.mod.Modpack; 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.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardProvider; @@ -53,7 +54,8 @@ public final class DownloadWizardProvider implements WizardProvider { private Task finishVersionDownloadingAsync(Map settings) { GameBuilder builder = profile.getDependency().gameBuilder(); - builder.name((String) settings.get("name")); + String name = (String) settings.get("name"); + builder.name(name); builder.gameVersion(((RemoteVersion) settings.get("game")).getGameVersion()); if (settings.containsKey("forge")) @@ -65,7 +67,8 @@ public final class DownloadWizardProvider implements WizardProvider { if (settings.containsKey("optifine")) builder.version((RemoteVersion) settings.get("optifine")); - return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions()); + return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions()) + .then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name))); } private Task finishModpackInstallingAsync(Map settings) { @@ -77,7 +80,8 @@ public final class DownloadWizardProvider implements WizardProvider { String name = tryCast(settings.get(ModpackPage.MODPACK_NAME), String.class).orElse(null); if (selected == null || modpack == null || name == null) return null; - return ModpackHelper.getInstallTask(profile, selected, name, modpack); + return ModpackHelper.getInstallTask(profile, selected, name, modpack) + .then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name))); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileList.java index 70ca881bb..fec55ec7b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileList.java @@ -25,7 +25,7 @@ import javafx.scene.control.ToggleGroup; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.ui.Controllers; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.MappedObservableList; import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java index cd7961387..b85c904ff 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java @@ -27,11 +27,10 @@ import javafx.scene.layout.StackPane; 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.FXUtils; import org.jackhuang.hmcl.ui.construct.FileItem; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.StringUtils; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; 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 f048e493a..ec7bfc4d3 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 @@ -31,7 +31,7 @@ import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.VersionNumber; import org.jackhuang.hmcl.util.i18n.I18n; @@ -78,6 +78,13 @@ public class GameList extends Control implements DecoratorPage { loading.set(false); items.setAll(children); children.forEach(GameListItem::checkSelection); + + profile.selectedVersionProperty().addListener((a, b, newValue) -> { + toggleGroup.getToggles().stream() + .filter(it -> ((GameListItem) it.getUserData()).getVersion().equals(newValue)) + .findFirst() + .ifPresent(it -> it.setSelected(true)); + }); } toggleGroup.selectedToggleProperty().addListener((o, a, toggle) -> { GameListItem model = (GameListItem) toggle.getUserData(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java index e8f5f1c5b..daf47bcee 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java @@ -127,7 +127,7 @@ public class GameListItem extends Control { public void modifyGameSettings() { Controllers.getVersionPage().load(version, profile); - Controllers.getDecorator().showPage(Controllers.getVersionPage()); + Controllers.navigate(Controllers.getVersionPage()); } public void generateLaunchScript() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/Refreshable.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/Refreshable.java index 0c8fd8320..e4a70aa0b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/Refreshable.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/Refreshable.java @@ -17,6 +17,13 @@ */ package org.jackhuang.hmcl.ui.wizard; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; + public interface Refreshable { void refresh(); + + default BooleanProperty canRefreshProperty() { + return new SimpleBooleanProperty(false); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskExecutorDialogWizardDisplayer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java similarity index 100% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskExecutorDialogWizardDisplayer.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 57e7fde41..486a6deaa 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -1062,6 +1062,10 @@ .jfx-decorator-drawer { } +.jfx-decorator-title { + -fx-text-fill: -fx-base-text-fill; -fx-font-size: 15; +} + .resize-border { -fx-border-color: -fx-base-color; -fx-border-width: 0 2 2 2; diff --git a/HMCL/src/main/resources/assets/fxml/decorator.fxml b/HMCL/src/main/resources/assets/fxml/decorator.fxml index fa26b7b10..4c2d9a52b 100644 --- a/HMCL/src/main/resources/assets/fxml/decorator.fxml +++ b/HMCL/src/main/resources/assets/fxml/decorator.fxml @@ -22,7 +22,7 @@ maxWidth="800">
- + diff --git a/HMCL/src/main/resources/assets/fxml/main.fxml b/HMCL/src/main/resources/assets/fxml/main.fxml index 3345f3fbc..11cf40672 100644 --- a/HMCL/src/main/resources/assets/fxml/main.fxml +++ b/HMCL/src/main/resources/assets/fxml/main.fxml @@ -2,9 +2,7 @@ - Date: Sun, 2 Sep 2018 21:57:25 +0800 Subject: [PATCH 24/28] Move AdvancedListItem & Skin to new package --- .../jackhuang/hmcl/ui/AdvancedListItem2.java | 74 ------------------ .../ui/account/AccountAdvancedListItem.java | 10 +-- .../hmcl/ui/construct/AdvancedListBox.java | 3 +- .../hmcl/ui/construct/AdvancedListItem.java | 76 ++++++++++--------- .../{ => construct}/AdvancedListItemSkin.java | 10 +-- .../ui/profile/ProfileAdvancedListItem.java | 14 +--- .../ui/versions/GameAdvancedListItem.java | 4 +- 7 files changed, 51 insertions(+), 140 deletions(-) delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => construct}/AdvancedListItemSkin.java (95%) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java deleted file mode 100644 index 8c660746f..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.property.*; -import javafx.event.ActionEvent; -import javafx.event.EventHandler; -import javafx.geometry.Rectangle2D; -import javafx.scene.control.Control; -import javafx.scene.control.Skin; -import javafx.scene.image.Image; -import org.jackhuang.hmcl.game.AccountHelper; - -public class AdvancedListItem2 extends Control { - private final ObjectProperty image = new SimpleObjectProperty<>(); - private final ObjectProperty viewport = new SimpleObjectProperty<>(); - private final StringProperty title = new SimpleStringProperty(); - private final StringProperty subtitle = new SimpleStringProperty(); - - public ObjectProperty imageProperty() { - return image; - } - - public ObjectProperty viewportProperty() { - return viewport; - } - - public StringProperty titleProperty() { - return title; - } - - public StringProperty subtitleProperty() { - return subtitle; - } - - public final ObjectProperty> onActionProperty() { - return onAction; - } - - public final void setOnAction(EventHandler value) { - onActionProperty().set(value); - } - - public final EventHandler getOnAction() { - return onActionProperty().get(); - } - - private ObjectProperty> onAction = new SimpleObjectProperty>(this, "onAction") { - @Override - protected void invalidated() { - setEventHandler(ActionEvent.ACTION, get()); - } - }; - - @Override - protected Skin createDefaultSkin() { - return new AdvancedListItemSkin(this); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java index 4aaf5c32c..ce968e09b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java @@ -19,23 +19,17 @@ package org.jackhuang.hmcl.ui.account; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.geometry.Rectangle2D; import javafx.scene.image.Image; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.offline.OfflineAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.game.AccountHelper; -import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.ui.AdvancedListItem2; -import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.construct.AdvancedListItem; -import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class AccountAdvancedListItem extends AdvancedListItem2 { +public class AccountAdvancedListItem extends AdvancedListItem { private ObjectProperty account = new SimpleObjectProperty() { @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListBox.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListBox.java index 3b1ea32a6..0695bc013 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListBox.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListBox.java @@ -23,7 +23,6 @@ import javafx.scene.control.ScrollPane; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; -import org.jackhuang.hmcl.ui.AdvancedListItem2; import org.jackhuang.hmcl.ui.FXUtils; public class AdvancedListBox extends ScrollPane { @@ -43,7 +42,7 @@ public class AdvancedListBox extends ScrollPane { } public AdvancedListBox add(Node child) { - if (child instanceof Pane || child instanceof AdvancedListItem2) + if (child instanceof Pane || child instanceof AdvancedListItem) container.getChildren().add(child); else { StackPane pane = new StackPane(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItem.java index e740fbce9..356d7d7cd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItem.java @@ -1,7 +1,7 @@ /* * Hello Minecraft! Launcher. - * Copyright (C) 2018 huangyuhui - * + * 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 @@ -17,55 +17,57 @@ */ package org.jackhuang.hmcl.ui.construct; -import com.jfoenix.controls.JFXButton; +import javafx.beans.property.*; +import javafx.event.ActionEvent; import javafx.event.EventHandler; -import javafx.fxml.FXML; import javafx.geometry.Rectangle2D; -import javafx.scene.control.Label; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.StackPane; -import org.jackhuang.hmcl.ui.FXUtils; -public final class AdvancedListItem extends StackPane { - @FXML - private StackPane imageViewContainer; - @FXML - private Label lblTitle; - @FXML - private Label lblSubtitle; - @FXML - private ImageView imageView; - @FXML private JFXButton btnSettings; +public class AdvancedListItem extends Control { + private final ObjectProperty image = new SimpleObjectProperty<>(); + private final ObjectProperty viewport = new SimpleObjectProperty<>(); + private final StringProperty title = new SimpleStringProperty(); + private final StringProperty subtitle = new SimpleStringProperty(); - public AdvancedListItem(String title) { - this(title, ""); + public ObjectProperty imageProperty() { + return image; } - public AdvancedListItem(String title, String subtitle) { - FXUtils.loadFXML(this, "/assets/fxml/advanced-list-item.fxml"); - - lblTitle.setText(title); - lblSubtitle.setText(subtitle); - - FXUtils.limitSize(imageView, 32, 32); + public ObjectProperty viewportProperty() { + return viewport; } - public void setOnSettingsButtonClicked(EventHandler handler) { - btnSettings.setOnMouseClicked(handler); + public StringProperty titleProperty() { + return title; } - public void setTitle(String title) { - lblTitle.setText(title); + public StringProperty subtitleProperty() { + return subtitle; } - public void setSubtitle(String subtitle) { - lblSubtitle.setText(subtitle); + public final ObjectProperty> onActionProperty() { + return onAction; } - public void setImage(Image image, Rectangle2D viewport) { - imageView.setImage(image); - imageView.setViewport(viewport); + public final void setOnAction(EventHandler value) { + onActionProperty().set(value); + } + + public final EventHandler getOnAction() { + return onActionProperty().get(); + } + + private ObjectProperty> onAction = new SimpleObjectProperty>(this, "onAction") { + @Override + protected void invalidated() { + setEventHandler(ActionEvent.ACTION, get()); + } + }; + + @Override + protected Skin createDefaultSkin() { + return new AdvancedListItemSkin(this); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java similarity index 95% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java index 1b94cf304..901693879 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.construct; import com.jfoenix.controls.JFXButton; import javafx.geometry.Insets; @@ -29,12 +29,12 @@ import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; -import java.util.Optional; +public class AdvancedListItemSkin extends SkinBase { -public class AdvancedListItemSkin extends SkinBase { - - public AdvancedListItemSkin(AdvancedListItem2 skinnable) { + public AdvancedListItemSkin(AdvancedListItem skinnable) { super(skinnable); StackPane stackPane = new StackPane(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java index 19d7b1ff9..47bd4b01a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java @@ -19,24 +19,14 @@ package org.jackhuang.hmcl.ui.profile; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.geometry.Rectangle2D; import javafx.scene.image.Image; -import org.jackhuang.hmcl.auth.Account; -import org.jackhuang.hmcl.auth.offline.OfflineAccount; -import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; -import org.jackhuang.hmcl.game.AccountHelper; -import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; -import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.ui.AdvancedListItem2; -import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.construct.AdvancedListItem; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class ProfileAdvancedListItem extends AdvancedListItem2 { +public class ProfileAdvancedListItem extends AdvancedListItem { private ObjectProperty profile = new SimpleObjectProperty() { @Override 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 34a359e2d..58fbb22c2 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 @@ -24,12 +24,12 @@ 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.ui.AdvancedListItem2; +import org.jackhuang.hmcl.ui.construct.AdvancedListItem; import org.jackhuang.hmcl.ui.WeakListenerHelper; import java.io.File; -public class GameAdvancedListItem extends AdvancedListItem2 { +public class GameAdvancedListItem extends AdvancedListItem { private final WeakListenerHelper helper = new WeakListenerHelper(); private Profile profile; From 222e39dc48287ca571e67a9c829968b668265074 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sun, 2 Sep 2018 21:58:03 +0800 Subject: [PATCH 25/28] Remove decorator.fxml --- .../main/resources/assets/fxml/decorator.fxml | 118 ------------------ 1 file changed, 118 deletions(-) delete mode 100644 HMCL/src/main/resources/assets/fxml/decorator.fxml diff --git a/HMCL/src/main/resources/assets/fxml/decorator.fxml b/HMCL/src/main/resources/assets/fxml/decorator.fxml deleted file mode 100644 index 4c2d9a52b..000000000 --- a/HMCL/src/main/resources/assets/fxml/decorator.fxml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - -
- - - - - -
- - - -
- - - -
-
-
-
- - - - - -
-
- - - -
-
- - - - -
- -
- - - -
-
-
- - - - - - - -
- - -
- - - - - -
-
- - - - - - - - - - - - - - -
-
-
-
\ No newline at end of file From bbc2b251dd1b0b22192b1a34dafdb9d22d058ef7 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sun, 2 Sep 2018 23:24:32 +0800 Subject: [PATCH 26/28] Change the way to determine whether use global settings or the specific one --- .../org/jackhuang/hmcl/setting/Profile.java | 22 ++--- .../org/jackhuang/hmcl/ui/VersionPage.java | 4 +- ...ntroller.java => VersionSettingsPage.java} | 99 +++++++++++++------ .../hmcl/ui/construct/ComponentList.java | 2 +- .../jackhuang/hmcl/ui/versions/GameList.java | 3 +- .../hmcl/ui/versions/GameListSkin.java | 14 +-- .../jackhuang/hmcl/ui/versions/Versions.java | 9 ++ .../assets/fxml/version/version-settings.fxml | 25 +++-- .../assets/fxml/version/version.fxml | 3 +- .../resources/assets/lang/I18N.properties | 3 +- .../resources/assets/lang/I18N_zh.properties | 3 +- .../assets/lang/I18N_zh_CN.properties | 3 +- 12 files changed, 126 insertions(+), 64 deletions(-) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{VersionSettingsController.java => VersionSettingsPage.java} (82%) 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 1230a9f25..78a6ca28a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java @@ -75,22 +75,16 @@ public final class Profile implements Observable { this.gameDir.set(gameDir); } - private final ObjectProperty global = new ImmediateObjectProperty<>(this, "global", new VersionSetting()); + private final ReadOnlyObjectWrapper global = new ReadOnlyObjectWrapper<>(this, "global"); - public ObjectProperty globalProperty() { - return global; + public ReadOnlyObjectProperty globalProperty() { + return global.getReadOnlyProperty(); } public VersionSetting getGlobal() { return global.get(); } - private void setGlobal(VersionSetting global) { - if (global == null) - global = new VersionSetting(); - this.global.set(global); - } - private final ImmediateStringProperty name; public ImmediateStringProperty nameProperty() { @@ -124,10 +118,15 @@ public final class Profile implements Observable { } public Profile(String name, File initialGameDir) { + this(name, initialGameDir, new VersionSetting()); + } + + public Profile(String name, File initialGameDir, VersionSetting global) { this.name = new ImmediateStringProperty(this, "name", name); gameDir = new ImmediateObjectProperty<>(this, "gameDir", initialGameDir); repository = new HMCLGameRepository(this, initialGameDir); modManager = new ModManager(repository); + this.global.set(global == null ? new VersionSetting() : global); gameDir.addListener((a, b, newValue) -> repository.changeDirectory(newValue)); selectedVersion.addListener(o -> checkSelectedVersion()); @@ -227,7 +226,7 @@ public final class Profile implements Observable { } protected void invalidate() { - JFXUtilities.runInFX(observableHelper::invalidate); + observableHelper.invalidate(); } public static final class Serializer implements JsonSerializer, JsonDeserializer { @@ -256,8 +255,7 @@ public final class Profile implements Observable { JsonObject obj = (JsonObject) json; String gameDir = Optional.ofNullable(obj.get("gameDir")).map(JsonElement::getAsString).orElse(""); - Profile profile = new Profile("Default", new File(gameDir)); - profile.setGlobal(context.deserialize(obj.get("global"), VersionSetting.class)); + Profile profile = new Profile("Default", new File(gameDir), 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/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java index 35e924311..16134dd9d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java @@ -40,7 +40,7 @@ public final class VersionPage extends StackPane implements DecoratorPage { private final StringProperty title = new SimpleStringProperty(this, "title", null); @FXML - private VersionSettingsController versionSettingsController; + private VersionSettingsPage versionSettings; @FXML private Tab modTab; @FXML @@ -92,7 +92,7 @@ public final class VersionPage extends StackPane implements DecoratorPage { title.set(i18n("settings.game") + " - " + id); - versionSettingsController.loadVersionSetting(profile, id); + versionSettings.loadVersionSetting(profile, id); modController.setParentTab(tabPane); modTab.setUserData(modController); modController.loadMods(profile.getModManager(), id); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsPage.java similarity index 82% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsPage.java index d48a671f2..9bd1745b2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsPage.java @@ -17,16 +17,18 @@ */ package org.jackhuang.hmcl.ui; -import com.jfoenix.controls.JFXCheckBox; -import com.jfoenix.controls.JFXComboBox; -import com.jfoenix.controls.JFXTextField; -import com.jfoenix.controls.JFXToggleButton; +import com.jfoenix.controls.*; import javafx.application.Platform; +import javafx.beans.InvalidationListener; import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.image.Image; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; @@ -38,6 +40,8 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.construct.ComponentList; import org.jackhuang.hmcl.ui.construct.ImagePickerItem; import org.jackhuang.hmcl.ui.construct.MultiFileItem; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; +import org.jackhuang.hmcl.ui.versions.Versions; import org.jackhuang.hmcl.util.*; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -49,11 +53,14 @@ import java.util.List; import java.util.logging.Level; import java.util.stream.Collectors; -public final class VersionSettingsController { +public final class VersionSettingsPage extends StackPane implements DecoratorPage { + private final StringProperty title = new SimpleStringProperty(); + private VersionSetting lastVersionSetting = null; private Profile profile; private String versionId; private boolean javaItemsLoaded; + private InvalidationListener specificSettingsListener; @FXML private VBox rootPane; @FXML private ScrollPane scroll; @@ -67,16 +74,22 @@ public final class VersionSettingsController { @FXML private JFXTextField txtPrecallingCommand; @FXML private JFXTextField txtServerIP; @FXML private ComponentList advancedSettingsPane; + @FXML private ComponentList componentList; @FXML private JFXComboBox cboLauncherVisibility; @FXML private JFXCheckBox chkFullscreen; @FXML private Label lblPhysicalMemory; @FXML private JFXToggleButton chkNoJVMArgs; @FXML private JFXToggleButton chkNoGameCheck; - @FXML private MultiFileItem globalItem; @FXML private MultiFileItem javaItem; @FXML private MultiFileItem gameDirItem; @FXML private JFXToggleButton chkShowLogs; @FXML private ImagePickerItem iconPickerItem; + @FXML private JFXCheckBox chkEnableSpecificSettings; + @FXML private BorderPane settingsTypePane; + + public VersionSettingsPage() { + FXUtils.loadFXML(this, "/assets/fxml/version/version-settings.fxml"); + } @FXML private void initialize() { @@ -105,20 +118,42 @@ public final class VersionSettingsController { gameDirItem.createChildren(i18n("settings.advanced.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER) )); - globalItem.loadChildren(Arrays.asList( - globalItem.createChildren(i18n("settings.type.global"), true), - globalItem.createChildren(i18n("settings.type.special"), false) - )); + chkEnableSpecificSettings.selectedProperty().addListener((a, b, newValue) -> { + if (versionId == null) return; + + // do not call versionSettings.setUsesGlobal(true/false) + // because versionSettings can be the global one. + // global versionSettings.usesGlobal is always true. + if (newValue) + profile.specializeVersionSetting(versionId); + else + profile.globalizeVersionSetting(versionId); + + Platform.runLater(() -> loadVersionSetting(profile, versionId)); + }); + + specificSettingsListener = o -> { + chkEnableSpecificSettings.setSelected(!lastVersionSetting.isUsesGlobal()); + }; + + componentList.disableProperty().bind(chkEnableSpecificSettings.selectedProperty().not()); + advancedSettingsPane.disableProperty().bind(chkEnableSpecificSettings.selectedProperty().not()); } public void loadVersionSetting(Profile profile, String versionId) { this.profile = profile; this.versionId = versionId; + if (versionId == null) { + componentList.removeChild(iconPickerItem); + rootPane.getChildren().remove(settingsTypePane); + chkEnableSpecificSettings.setSelected(true); + } + VersionSetting versionSetting = profile.getVersionSetting(versionId); - gameDirItem.setDisable(profile.getRepository().isModpack(versionId)); - globalItem.setDisable(profile.getRepository().isModpack(versionId)); + gameDirItem.setDisable(versionId != null && profile.getRepository().isModpack(versionId)); + settingsTypePane.setDisable(versionId != null && profile.getRepository().isModpack(versionId)); // unbind data fields if (lastVersionSetting != null) { @@ -139,14 +174,13 @@ public final class VersionSettingsController { FXUtils.unbindBoolean(chkShowLogs, lastVersionSetting.showLogsProperty()); FXUtils.unbindEnum(cboLauncherVisibility); - globalItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.usesGlobalProperty()); + lastVersionSetting.usesGlobalProperty().removeListener(specificSettingsListener); gameDirItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.gameDirTypeProperty()); gameDirItem.subtitleProperty().unbind(); } // unbind data fields - globalItem.setToggleSelectedListener(null); javaItem.setToggleSelectedListener(null); // bind new data fields @@ -167,6 +201,10 @@ public final class VersionSettingsController { FXUtils.bindBoolean(chkShowLogs, versionSetting.showLogsProperty()); FXUtils.bindEnum(cboLauncherVisibility, versionSetting.launcherVisibilityProperty()); + versionSetting.usesGlobalProperty().addListener(specificSettingsListener); + if (versionId != null) + chkEnableSpecificSettings.setSelected(!versionSetting.isUsesGlobal()); + javaItem.setToggleSelectedListener(newValue -> { if (javaItem.isCustomToggle(newValue)) { versionSetting.setUsesCustomJavaDir(); @@ -179,21 +217,6 @@ public final class VersionSettingsController { versionSetting.javaProperty().setChangedListener(it -> initJavaSubtitle(versionSetting)); initJavaSubtitle(versionSetting); - globalItem.selectedDataProperty().bindBidirectional(versionSetting.usesGlobalProperty()); - globalItem.subtitleProperty().bind(Bindings.createStringBinding(() -> i18n(versionSetting.isUsesGlobal() ? "settings.type.global" : "settings.type.special"), - versionSetting.usesGlobalProperty())); - globalItem.setToggleSelectedListener(newValue -> { - // do not call versionSettings.setUsesGlobal(true/false) - // because versionSettings can be the global one. - // global versionSettings.usesGlobal is always true. - if ((Boolean) newValue.getUserData()) - profile.globalizeVersionSetting(versionId); - else - profile.specializeVersionSetting(versionId); - - Platform.runLater(() -> loadVersionSetting(profile, versionId)); - }); - gameDirItem.selectedDataProperty().bindBidirectional(versionSetting.gameDirTypeProperty()); gameDirItem.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath()).normalize().toString(), versionSetting.gameDirProperty(), versionSetting.gameDirTypeProperty())); @@ -229,8 +252,16 @@ public final class VersionSettingsController { .map(JavaVersion::getBinary).map(File::getAbsolutePath).orElse("Invalid Java Directory")))); } + @FXML + private void editGlobalSettings() { + Versions.modifyGlobalSettings(profile); + } + @FXML private void onExploreIcon() { + if (versionId == null) + return; + FileChooser chooser = new FileChooser(); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("extension.png"), "*.png")); File selectedFile = chooser.showOpenDialog(Controllers.getStage()); @@ -246,6 +277,11 @@ public final class VersionSettingsController { } private void loadIcon() { + if (versionId == null) { + iconPickerItem.setImage(new Image("/assets/img/grass.png")); + return; + } + File iconFile = profile.getRepository().getVersionIcon(versionId); if (iconFile.exists()) iconPickerItem.setImage(new Image("file:" + iconFile.getAbsolutePath())); @@ -253,4 +289,9 @@ public final class VersionSettingsController { iconPickerItem.setImage(new Image("/assets/img/grass.png")); FXUtils.limitSize(iconPickerItem.getImageView(), 32, 32); } + + @Override + public StringProperty titleProperty() { + return title; + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java index 67d93a3f4..ffe131359 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java @@ -60,7 +60,7 @@ public class ComponentList extends StackPane { vbox.getChildren().add(child); } - public void removeChildren(Node node) { + public void removeChild(Node node) { vbox.getChildren().removeIf(node1 -> node1.getProperties().get("node") == node); } 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 ec7bfc4d3..f07750fa9 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 @@ -30,6 +30,7 @@ import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.VersionSettingsPage; import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.VersionNumber; @@ -111,7 +112,7 @@ public class GameList extends Control implements DecoratorPage { } public void modifyGlobalGameSettings() { - // Controllers.navigate(); + Versions.modifyGlobalSettings(profile); } @Override 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 da6fbdb73..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 @@ -79,13 +79,13 @@ public class GameListSkin extends SkinBase { btnRefresh.setOnMouseClicked(e -> skinnable.refresh()); toolbar.getChildren().add(btnRefresh); -// JFXButton btnModify = new JFXButton(); -// btnModify.getStyleClass().add("jfx-tool-bar-button"); -// btnModify.textFillProperty().bind(Theme.foregroundFillBinding()); -// btnModify.setGraphic(wrap(SVG.gear(Theme.foregroundFillBinding(), -1, -1))); -// btnModify.setText(I18n.i18n("settings.type.global.manage")); -// btnModify.setOnMouseClicked(e -> skinnable.modifyGlobalGameSettings()); -// toolbar.getChildren().add(btnModify); + JFXButton btnModify = new JFXButton(); + btnModify.getStyleClass().add("jfx-tool-bar-button"); + btnModify.textFillProperty().bind(Theme.foregroundFillBinding()); + btnModify.setGraphic(wrap(SVG.gear(Theme.foregroundFillBinding(), -1, -1))); + btnModify.setText(I18n.i18n("settings.type.global.manage")); + btnModify.setOnMouseClicked(e -> skinnable.modifyGlobalGameSettings()); + toolbar.getChildren().add(btnModify); root.setTop(toolbar); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 391ab00a0..949d3b0c1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -27,11 +27,13 @@ import org.jackhuang.hmcl.mod.UnsupportedModpackException; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.EnumGameDirectory; import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.VersionSettingsPage; import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.export.ExportWizardProvider; @@ -130,4 +132,11 @@ public class Versions { else LauncherHelper.INSTANCE.launch(profile, Accounts.getSelectedAccount(), id, null); } + + public static void modifyGlobalSettings(Profile profile) { + VersionSettingsPage page = new VersionSettingsPage(); + page.loadVersionSetting(profile, null); + page.titleProperty().set(Profiles.getProfileDisplayName(profile) + " - " + i18n("settings.type.global.manage")); + Controllers.navigate(page); + } } diff --git a/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml b/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml index 0e9742998..d5524de68 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml @@ -8,15 +8,24 @@ - + - + - + + + + + + + + - + @@ -104,7 +113,7 @@
- + @@ -150,4 +159,4 @@ -
+ diff --git a/HMCL/src/main/resources/assets/fxml/version/version.fxml b/HMCL/src/main/resources/assets/fxml/version/version.fxml index e7923dae9..54f3eea7f 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version.fxml @@ -4,6 +4,7 @@ + - + diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index eaf4b37a7..bb2ec3aa5 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -313,7 +313,8 @@ settings.tabs.installers=Installers settings.type=Version setting type settings.type.global=Global global settings(all shared) settings.type.global.manage=Global Game Settings -settings.type.special=Specialized version settings(will not affect other versions) +settings.type.global.edit=Configure global game settings +settings.type.special.enable=Enable specialized settings for this game update=Update update.channel.dev=Update to development version diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index a2145257b..d4f1f479f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -313,7 +313,8 @@ settings.tabs.installers=自動安裝 settings.type=版本設定類型 settings.type.global=全域版本設定(使用該設定的版本共用一套設定) settings.type.global.manage=全域遊戲設定 -settings.type.special=單獨版本設定(不會影響到其他版本的設定) +settings.type.global.edit=編輯全域遊戲設定 +settings.type.special.enable=啟用遊戲特別設定(不影響其他遊戲版本) update=啟動器更新 update.channel.dev=更新到開發版 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 4397f57b3..efe44fdca 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -313,7 +313,8 @@ settings.tabs.installers=自动安装 settings.type=版本设置类型 settings.type.global=全局版本设置(使用该设置的版本共用一套设定) settings.type.global.manage=全局游戏设置 -settings.type.special=版本特定设置(不会影响到其他版本的设定) +settings.type.global.edit=编辑全局版本设置 +settings.type.special.enable=启用游戏特定设置(不影响其他游戏版本) update=启动器更新 update.channel.dev=更新到开发版 From d74ab35279582bac3264c486b6127f797db30543 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sun, 2 Sep 2018 23:30:27 +0800 Subject: [PATCH 27/28] Move package --- HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java | 1 + HMCL/src/main/java/org/jackhuang/hmcl/ui/ModItem.java | 1 + .../org/jackhuang/hmcl/ui/account/AccountListItemSkin.java | 2 +- .../java/org/jackhuang/hmcl/ui/account/AddAccountPane.java | 3 +-- .../jackhuang/hmcl/ui/{ => construct}/TwoLineListItem.java | 2 +- .../org/jackhuang/hmcl/ui/profile/ProfileListItemSkin.java | 2 +- .../main/java/org/jackhuang/hmcl/ui/versions/GameList.java | 1 - .../org/jackhuang/hmcl/ui/versions/GameListItemSkin.java | 2 +- .../hmcl/ui/{ => versions}/InstallerController.java | 5 ++++- .../org/jackhuang/hmcl/ui/{ => versions}/ModController.java | 5 ++++- .../org/jackhuang/hmcl/ui/{ => versions}/VersionPage.java | 4 ++-- .../hmcl/ui/{ => versions}/VersionSettingsPage.java | 4 +++- .../main/java/org/jackhuang/hmcl/ui/versions/Versions.java | 1 - HMCL/src/main/resources/assets/fxml/version/installer.fxml | 2 +- HMCL/src/main/resources/assets/fxml/version/mod.fxml | 2 +- HMCL/src/main/resources/assets/fxml/version/version.fxml | 2 +- 16 files changed, 23 insertions(+), 16 deletions(-) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => construct}/TwoLineListItem.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => versions}/InstallerController.java (95%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => versions}/ModController.java (97%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => versions}/VersionPage.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => versions}/VersionSettingsPage.java (99%) 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 69c3f39a7..a12b7ce7d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -35,6 +35,7 @@ import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; import org.jackhuang.hmcl.ui.decorator.DecoratorController; import org.jackhuang.hmcl.ui.profile.ProfileList; import org.jackhuang.hmcl.ui.versions.GameList; +import org.jackhuang.hmcl.ui.versions.VersionPage; import org.jackhuang.hmcl.util.FutureCallback; import org.jackhuang.hmcl.util.JavaVersion; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ModItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/ModItem.java index ec7362e44..27f131b00 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ModItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/ModItem.java @@ -26,6 +26,7 @@ import javafx.scene.layout.BorderPane; import org.jackhuang.hmcl.mod.ModInfo; import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.util.StringUtils; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java index 2b50d2f84..26911fce3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java @@ -30,7 +30,7 @@ import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.TwoLineListItem; +import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java index 7aab8b9e4..92436bbca 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java @@ -20,7 +20,6 @@ package org.jackhuang.hmcl.ui.account; import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.*; -import javafx.beans.InvalidationListener; import javafx.beans.binding.Bindings; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.fxml.FXML; @@ -44,7 +43,7 @@ import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.ui.TwoLineListItem; +import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.construct.AdvancedListBox; import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.IconedItem; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/TwoLineListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TwoLineListItem.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/TwoLineListItem.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TwoLineListItem.java index 2cbcc6a79..1ad0406c5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/TwoLineListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TwoLineListItem.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.construct; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItemSkin.java index 9ec213446..841c3adaf 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileListItemSkin.java @@ -30,7 +30,7 @@ import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.TwoLineListItem; +import org.jackhuang.hmcl.ui.construct.TwoLineListItem; public class ProfileListItemSkin extends SkinBase { 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 f07750fa9..5655cefd3 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 @@ -30,7 +30,6 @@ import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.ui.Controllers; -import org.jackhuang.hmcl.ui.VersionSettingsPage; import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.VersionNumber; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemSkin.java index 99b43a860..4835e544d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItemSkin.java @@ -29,7 +29,7 @@ import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.TwoLineListItem; +import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerController.java similarity index 95% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerController.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerController.java index de0082e72..e4eae6aed 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerController.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.versions; import javafx.fxml.FXML; import javafx.scene.control.ScrollPane; @@ -30,6 +30,9 @@ import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.InstallerItem; import org.jackhuang.hmcl.ui.download.InstallerWizardProvider; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ModController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModController.java similarity index 97% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/ModController.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModController.java index 88d1255a7..5f8425bba 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ModController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModController.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.versions; import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.JFXSpinner; @@ -31,6 +31,9 @@ import org.jackhuang.hmcl.mod.ModInfo; import org.jackhuang.hmcl.mod.ModManager; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.ModItem; import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.Logging; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java index 16134dd9d..b8741055b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.versions; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXListView; @@ -28,8 +28,8 @@ import javafx.scene.control.Tab; import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask; import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.ui.versions.Versions; import org.jackhuang.hmcl.util.FileUtils; import java.io.File; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java index 9bd1745b2..582c5d360 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.versions; import com.jfoenix.controls.*; import javafx.application.Platform; @@ -37,6 +37,8 @@ import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.VersionSetting; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.ComponentList; import org.jackhuang.hmcl.ui.construct.ImagePickerItem; import org.jackhuang.hmcl.ui.construct.MultiFileItem; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 949d3b0c1..3317e6ce0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -33,7 +33,6 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.ui.VersionSettingsPage; import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.export.ExportWizardProvider; diff --git a/HMCL/src/main/resources/assets/fxml/version/installer.fxml b/HMCL/src/main/resources/assets/fxml/version/installer.fxml index 02df43515..e29ff41fa 100644 --- a/HMCL/src/main/resources/assets/fxml/version/installer.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/installer.fxml @@ -6,7 +6,7 @@ + fx:controller="org.jackhuang.hmcl.ui.versions.InstallerController"> diff --git a/HMCL/src/main/resources/assets/fxml/version/mod.fxml b/HMCL/src/main/resources/assets/fxml/version/mod.fxml index bfc7cc8b0..aaa8d0828 100644 --- a/HMCL/src/main/resources/assets/fxml/version/mod.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/mod.fxml @@ -8,7 +8,7 @@ + fx:controller="org.jackhuang.hmcl.ui.versions.ModController"> diff --git a/HMCL/src/main/resources/assets/fxml/version/version.fxml b/HMCL/src/main/resources/assets/fxml/version/version.fxml index 54f3eea7f..bddb50096 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version.fxml @@ -4,7 +4,7 @@ - + Date: Mon, 3 Sep 2018 00:10:21 +0800 Subject: [PATCH 28/28] allow displaying game list in main page --- .../org/jackhuang/hmcl/setting/Config.java | 15 ++ .../hmcl/ui/GameVersionListPage.java | 241 ++++++++++++++++++ .../java/org/jackhuang/hmcl/ui/MainPage.java | 12 + .../org/jackhuang/hmcl/ui/SettingsPage.java | 2 + .../org/jackhuang/hmcl/ui/SettingsView.java | 16 ++ HMCL/src/main/resources/assets/fxml/main.fxml | 12 +- .../assets/fxml/version/version-settings.fxml | 2 +- .../resources/assets/lang/I18N.properties | 1 + .../resources/assets/lang/I18N_zh.properties | 1 + .../assets/lang/I18N_zh_CN.properties | 1 + 10 files changed, 297 insertions(+), 6 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/GameVersionListPage.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java index 355cd3b8f..69d12c3f3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java @@ -153,6 +153,9 @@ public final class Config implements Cloneable, Observable { @SerializedName("updateChannel") private ObjectProperty updateChannel = new SimpleObjectProperty<>(UpdateChannel.STABLE); + @SerializedName("enableMainPageGameList") + private BooleanProperty enableMainPageGameList = new SimpleBooleanProperty(false); + @SerializedName("_version") private IntegerProperty configVersion = new SimpleIntegerProperty(0); @@ -450,4 +453,16 @@ public final class Config implements Cloneable, Observable { public void setUpdateChannel(UpdateChannel updateChannel) { this.updateChannel.set(updateChannel); } + + public boolean isEnableMainPageGameList() { + return enableMainPageGameList.get(); + } + + public BooleanProperty enableMainPageGameListProperty() { + return enableMainPageGameList; + } + + public void setEnableMainPageGameList(boolean enableMainPageGameList) { + this.enableMainPageGameList.set(enableMainPageGameList); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameVersionListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameVersionListPage.java new file mode 100644 index 000000000..0b2df4cc1 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameVersionListPage.java @@ -0,0 +1,241 @@ +/* + * 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 com.jfoenix.concurrency.JFXUtilities; +import com.jfoenix.controls.*; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.ScrollPane; +import javafx.scene.image.Image; +import javafx.scene.input.MouseButton; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.event.EventBus; +import org.jackhuang.hmcl.event.RefreshedVersionsEvent; +import org.jackhuang.hmcl.event.RefreshingVersionsEvent; +import org.jackhuang.hmcl.game.GameVersion; +import org.jackhuang.hmcl.game.HMCLGameRepository; +import org.jackhuang.hmcl.game.ModpackHelper; +import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.mod.MismatchedModpackTypeException; +import org.jackhuang.hmcl.mod.UnsupportedModpackException; +import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.task.TaskExecutor; +import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; +import org.jackhuang.hmcl.ui.construct.MessageBox; +import org.jackhuang.hmcl.ui.versions.Versions; +import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.VersionNumber; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; +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 class GameVersionListPage extends StackPane { + private final JFXSpinner spinner; + private final StackPane contentPane; + private final JFXMasonryPane masonryPane; + + private Profile profile; + + public GameVersionListPage() { + spinner = new JFXSpinner(); + spinner.getStyleClass().setAll("first-spinner"); + + contentPane = new StackPane(); + + ScrollPane scrollPane = new ScrollPane(); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setFitToWidth(true); + + masonryPane = new JFXMasonryPane(); + masonryPane.setHSpacing(3); + masonryPane.setVSpacing(3); + masonryPane.setCellWidth(182); + masonryPane.setCellHeight(153); + + scrollPane.setContent(masonryPane); + + VBox vBox = new VBox(); + vBox.setPadding(new Insets(15)); + vBox.setPickOnBounds(false); + vBox.setAlignment(Pos.BOTTOM_RIGHT); + vBox.setSpacing(15); + + JFXButton btnRefresh = new JFXButton(); + btnRefresh.setPrefWidth(40); + btnRefresh.setPrefHeight(40); + btnRefresh.setButtonType(JFXButton.ButtonType.RAISED); + btnRefresh.getStyleClass().setAll("jfx-button-raised-round"); + btnRefresh.setGraphic(SVG.refresh(Theme.foregroundFillBinding(), -1, -1)); + btnRefresh.setOnMouseClicked(e -> profile.getRepository().refreshVersionsAsync().start()); + FXUtils.installTooltip(btnRefresh, i18n("button.refresh")); + + vBox.getChildren().setAll(btnRefresh); + + contentPane.getChildren().setAll(scrollPane, vBox); + + getChildren().setAll(spinner); + + Profiles.selectedProfileProperty().addListener((o, a, b) -> this.profile = b); + profile = Profiles.getSelectedProfile(); + + 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); + }); + if (profile.getRepository().isLoaded()) + loadVersions(profile.getRepository()); + else + profile.getRepository().refreshVersionsAsync().start(); + } + + private 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.getVersionPage().load(id, profile); + Controllers.navigate(Controllers.getVersionPage()); + }); + item.setOnUpdateButtonClicked(event -> { + FileChooser chooser = new FileChooser(); + chooser.setTitle(i18n("modpack.choose")); + chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); + File selectedFile = chooser.showOpenDialog(Controllers.getStage()); + if (selectedFile != null) { + AtomicReference region = new AtomicReference<>(); + try { + TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, id, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(id))) + .then(Task.of(Schedulers.javafx(), () -> region.get().fireEvent(new DialogCloseEvent()))).executor(); + region.set(Controllers.taskDialog(executor, i18n("modpack.update"), "")); + executor.start(); + } catch (UnsupportedModpackException e) { + region.get().fireEvent(new DialogCloseEvent()); + Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + } catch (MismatchedModpackTypeException e) { + region.get().fireEvent(new DialogCloseEvent()); + Controllers.dialog(i18n("modpack.mismatched_type"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + } catch (IOException e) { + region.get().fireEvent(new DialogCloseEvent()); + Controllers.dialog(i18n("modpack.invalid"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + } + } + }); + 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: + FXUtils.openFolder(repository.getRunDirectory(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); + } + }); + } +} 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 af12d5e23..263ca1af5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java @@ -17,10 +17,12 @@ */ package org.jackhuang.hmcl.ui; +import com.jfoenix.controls.JFXButton; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.fxml.FXML; import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.setting.ConfigHolder; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; @@ -32,8 +34,18 @@ public final class MainPage extends StackPane implements DecoratorPage { private final StringProperty title = new SimpleStringProperty(this, "title", i18n("main_page")); + @FXML + private StackPane main; + { FXUtils.loadFXML(this, "/assets/fxml/main.fxml"); + + FXUtils.onChangeAndOperate(ConfigHolder.config().enableMainPageGameListProperty(), newValue -> { + if (newValue) + getChildren().setAll(new GameVersionListPage()); + else + getChildren().setAll(main); + }); } @FXML diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java index b3588e87c..803984150 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java @@ -63,6 +63,8 @@ public final class SettingsPage extends SettingsView implements DecoratorPage { public SettingsPage() { FXUtils.smoothScrolling(scroll); + chkEnableGameList.selectedProperty().bindBidirectional(config().enableMainPageGameListProperty()); + cboDownloadSource.getSelectionModel().select(DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(Settings.instance().getDownloadProvider())); cboDownloadSource.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> Settings.instance().setDownloadProvider(DownloadProviders.getDownloadProvider(newValue.intValue()))); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java index 8f9cdc677..aa4d913ca 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsView.java @@ -58,6 +58,7 @@ public abstract class SettingsView extends StackPane { protected final JFXCheckBox chkProxyAuthentication; protected final GridPane authPane; protected final Pane proxyPane; + protected final JFXToggleButton chkEnableGameList; public SettingsView() { scroll = new ScrollPane(); @@ -134,6 +135,21 @@ public abstract class SettingsView extends StackPane { settingsPane.addChildren(backgroundItem); } + { + BorderPane borderPane = new BorderPane(); + + Label left = new Label(I18n.i18n("settings.launcher.enable_game_list")); + BorderPane.setAlignment(left, Pos.CENTER_LEFT); + borderPane.setLeft(left); + + chkEnableGameList = new JFXToggleButton(); + chkEnableGameList.setSize(8); + FXUtils.setLimitHeight(chkEnableGameList, 20); + borderPane.setRight(chkEnableGameList); + + settingsPane.addChildren(borderPane); + } + { BorderPane downloadSourcePane = new BorderPane(); { diff --git a/HMCL/src/main/resources/assets/fxml/main.fxml b/HMCL/src/main/resources/assets/fxml/main.fxml index 11cf40672..d5b4908a9 100644 --- a/HMCL/src/main/resources/assets/fxml/main.fxml +++ b/HMCL/src/main/resources/assets/fxml/main.fxml @@ -2,9 +2,11 @@ - - + + + + diff --git a/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml b/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml index d5524de68..6591599fd 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml @@ -109,7 +109,7 @@
- +
diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index bb2ec3aa5..38c50db4f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -293,6 +293,7 @@ settings.icon=Game Icon settings.launcher=Settings settings.launcher.common_path.tooltip=This app will save all game libraries and assets here unless there are existant files in game folder. settings.launcher.download_source=Download Source +settings.launcher.enable_game_list=Display game list in main page settings.launcher.language=Language settings.launcher.log_font=Log Font settings.launcher.proxy=Proxy diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index d4f1f479f..3f6a216b7 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -293,6 +293,7 @@ settings.icon=遊戲圖示 settings.launcher=啟動器設定 settings.launcher.common_path.tooltip=啟動器將所有遊戲資源及依賴庫檔案放於此集中管理,如果遊戲資料夾內有現成的將不會使用公共庫檔案 settings.launcher.download_source=下載來源 +settings.launcher.enable_game_list=在首頁內顯示遊戲列表 settings.launcher.language=語言 settings.launcher.log_font=記錄字體 settings.launcher.proxy=代理 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index efe44fdca..830a4ecc1 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -293,6 +293,7 @@ settings.icon=游戏图标 settings.launcher=启动器设置 settings.launcher.common_path.tooltip=启动器将所有游戏资源及依赖库文件放于此集中管理,如果游戏文件夹内有现成的将不会使用公共库文件 settings.launcher.download_source=下载源 +settings.launcher.enable_game_list=在主页内显示游戏列表 settings.launcher.language=语言 settings.launcher.log_font=日志字体 settings.launcher.proxy=代理