From dad7ac706feac47c6a619a1d3c0d5d412bf43449 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Fri, 10 Aug 2018 12:53:49 +0800 Subject: [PATCH] Add update channel selection --- .../java/org/jackhuang/hmcl/Launcher.java | 25 +++++++--- .../org/jackhuang/hmcl/setting/Config.java | 14 ++++++ .../hmcl/setting/EnumUpdateChannel.java | 23 +++++++++ .../org/jackhuang/hmcl/ui/SettingsPage.java | 38 +++++++++++++++ .../hmcl/ui/construct/ComponentList.java | 4 -- .../hmcl/ui/construct/ComponentListCell.java | 46 ++++++++++++------ .../hmcl/ui/construct/ComponentSublist.java | 41 ++++++++++++++++ .../jackhuang/hmcl/upgrade/UpdateChecker.java | 48 ++++++++++++------- .../main/resources/assets/fxml/setting.fxml | 20 +++++--- .../resources/assets/lang/I18N.properties | 4 ++ .../resources/assets/lang/I18N_zh.properties | 4 ++ .../assets/lang/I18N_zh_CN.properties | 4 ++ 12 files changed, 225 insertions(+), 46 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateChannel.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentSublist.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java index 0fc129d41..be048359e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java @@ -23,8 +23,10 @@ import static org.jackhuang.hmcl.util.Logging.LOG; import com.jfoenix.concurrency.JFXUtilities; import javafx.application.Application; import javafx.application.Platform; +import javafx.beans.binding.Bindings; import javafx.stage.Stage; +import org.jackhuang.hmcl.setting.ConfigHolder; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.upgrade.UpdateChecker; @@ -55,14 +57,25 @@ public final class Launcher extends Application { primaryStage.setResizable(false); primaryStage.setScene(Controllers.getScene()); - thread(() -> { - try { - UpdateChecker.checkUpdate(); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to check for update", e); - } + UpdateChecker.updateChannelProperty().addListener(observable -> { + thread(() -> { + try { + UpdateChecker.checkUpdate(); + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to check for update", e); + } + }); }); + UpdateChecker.updateChannelProperty().bind(Bindings.createStringBinding(() -> { + switch (ConfigHolder.config().getUpdateChannel()) { + case DEVELOPMENT: + return UpdateChecker.CHANNEL_DEV; + default: + return UpdateChecker.CHANNEL_STABLE; + } + }, ConfigHolder.config().updateChannelProperty())); + primaryStage.show(); } catch (Throwable e) { CRASH_REPORTER.uncaughtException(Thread.currentThread(), e); 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 7746dabf5..ead049122 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java @@ -149,6 +149,9 @@ public final class Config implements Cloneable, Observable { @SerializedName("authlibInjectorServers") private ObservableList authlibInjectorServers = FXCollections.observableArrayList(); + @SerializedName("updateChannel") + private ObjectProperty updateChannel = new SimpleObjectProperty<>(EnumUpdateChannel.STABLE); + @SerializedName("_version") private IntegerProperty configVersion = new SimpleIntegerProperty(0); @@ -435,4 +438,15 @@ public final class Config implements Cloneable, Observable { return authlibInjectorServers; } + public EnumUpdateChannel getUpdateChannel() { + return updateChannel.get(); + } + + public ObjectProperty updateChannelProperty() { + return updateChannel; + } + + public void setUpdateChannel(EnumUpdateChannel updateChannel) { + this.updateChannel.set(updateChannel); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateChannel.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateChannel.java new file mode 100644 index 000000000..fd117839c --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateChannel.java @@ -0,0 +1,23 @@ +/* + * 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.setting; + +public enum EnumUpdateChannel { + STABLE, + DEVELOPMENT +} 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 bc13aaaaf..dd61d13a4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java @@ -39,6 +39,7 @@ import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.text.Font; +import javafx.scene.text.Text; import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.construct.FontComboBox; @@ -88,6 +89,12 @@ public final class SettingsPage extends StackPane implements DecoratorPage { @FXML private Label lblUpdateSub; @FXML + private Text lblUpdateNote; + @FXML + private JFXRadioButton chkUpdateStable; + @FXML + private JFXRadioButton chkUpdateDev; + @FXML private JFXButton btnUpdate; @FXML private ScrollPane scroll; @@ -212,6 +219,12 @@ public final class SettingsPage extends StackPane implements DecoratorPage { lblUpdate.setText(i18n("update.found")); lblUpdate.getStyleClass().setAll("update-label"); + } else if (UpdateChecker.isCheckingUpdate()) { + lblUpdateSub.setText(i18n("update.checking")); + lblUpdateSub.getStyleClass().setAll("subtitle-label"); + + lblUpdate.setText(i18n("update")); + lblUpdate.getStyleClass().setAll(); } else { lblUpdateSub.setText(i18n("update.latest")); lblUpdateSub.getStyleClass().setAll("subtitle-label"); @@ -222,7 +235,32 @@ public final class SettingsPage extends StackPane implements DecoratorPage { }; UpdateChecker.latestVersionProperty().addListener(new WeakInvalidationListener(updateListener)); UpdateChecker.outdatedProperty().addListener(new WeakInvalidationListener(updateListener)); + UpdateChecker.checkingUpdateProperty().addListener(new WeakInvalidationListener(updateListener)); updateListener.invalidated(null); + + lblUpdateNote.setWrappingWidth(470); + + ObjectProperty updateChannel = new SimpleObjectProperty() { + @Override + protected void invalidated() { + EnumUpdateChannel updateChannel = Objects.requireNonNull(get()); + chkUpdateDev.setSelected(updateChannel == EnumUpdateChannel.DEVELOPMENT); + chkUpdateStable.setSelected(updateChannel == EnumUpdateChannel.STABLE); + } + }; + + ToggleGroup updateChannelGroup = new ToggleGroup(); + chkUpdateDev.setToggleGroup(updateChannelGroup); + chkUpdateDev.setUserData(EnumUpdateChannel.DEVELOPMENT); + chkUpdateStable.setToggleGroup(updateChannelGroup); + chkUpdateStable.setUserData(EnumUpdateChannel.STABLE); + updateChannelGroup.getToggles().forEach( + toggle -> toggle.selectedProperty().addListener((observable, oldValue, newValue) -> { + if (newValue) { + updateChannel.set((EnumUpdateChannel) toggle.getUserData()); + } + })); + updateChannel.bindBidirectional(ConfigHolder.config().updateChannelProperty()); // ==== // ==== Background ==== 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 6a3439202..67d93a3f4 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 @@ -50,10 +50,6 @@ public class ComponentList extends StackPane { } public void addChildren(Node node) { - if (node instanceof ComponentList) { - node.getProperties().put("title", ((ComponentList) node).getTitle()); - node.getProperties().put("subtitle", ((ComponentList) node).getSubtitle()); - } StackPane child = new StackPane(); child.getChildren().add(new ComponentListCell(node)); if (vbox.getChildren().isEmpty()) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentListCell.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentListCell.java index 71d39a077..7b80abf61 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentListCell.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentListCell.java @@ -27,6 +27,8 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.shape.Rectangle; @@ -75,31 +77,47 @@ public class ComponentListCell extends StackPane { content.getStyleClass().remove("options-list"); content.getStyleClass().add("options-sublist"); - StackPane groupNode = new StackPane(); + BorderPane groupNode = new BorderPane(); groupNode.getStyleClass().add("options-list-item-header"); Node expandIcon = SVG.expand(Theme.blackFillBinding(), 10, 10); JFXButton expandButton = new JFXButton(); expandButton.setGraphic(expandIcon); expandButton.getStyleClass().add("options-list-item-expand-button"); - StackPane.setAlignment(expandButton, Pos.CENTER_RIGHT); VBox labelVBox = new VBox(); - Label label = new Label(); - label.textProperty().bind(list.titleProperty()); - label.setMouseTransparent(true); - labelVBox.getChildren().add(label); - if (list.isHasSubtitle()) { - Label subtitleLabel = new Label(); - subtitleLabel.textProperty().bind(list.subtitleProperty()); - subtitleLabel.setMouseTransparent(true); - subtitleLabel.getStyleClass().add("subtitle-label"); - labelVBox.getChildren().add(subtitleLabel); + if (list instanceof ComponentSublist) { + Node leftNode = ((ComponentSublist) list).getHeaderLeft(); + if (leftNode != null) + labelVBox.getChildren().setAll(leftNode); + } else { + Label label = new Label(); + label.textProperty().bind(list.titleProperty()); + label.setMouseTransparent(true); + labelVBox.getChildren().add(label); + + if (list.isHasSubtitle()) { + Label subtitleLabel = new Label(); + subtitleLabel.textProperty().bind(list.subtitleProperty()); + subtitleLabel.setMouseTransparent(true); + subtitleLabel.getStyleClass().add("subtitle-label"); + labelVBox.getChildren().add(subtitleLabel); + } } - StackPane.setAlignment(labelVBox, Pos.CENTER_LEFT); - groupNode.getChildren().setAll(labelVBox, expandButton); + groupNode.setLeft(labelVBox); + + HBox right = new HBox(); + if (list instanceof ComponentSublist) { + Node rightNode = ((ComponentSublist) list).getHeaderRight(); + if (rightNode != null) + right.getChildren().add(rightNode); + } + right.getChildren().add(expandButton); + groupNode.setRight(right); + labelVBox.setAlignment(Pos.CENTER_LEFT); + right.setAlignment(Pos.CENTER_RIGHT); VBox container = new VBox(); container.setStyle("-fx-padding: 8 0 0 0;"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentSublist.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentSublist.java new file mode 100644 index 000000000..d4a6dfc9b --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentSublist.java @@ -0,0 +1,41 @@ +package org.jackhuang.hmcl.ui.construct; + +import javafx.beans.DefaultProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.scene.Node; + +@DefaultProperty("content") +public final class ComponentSublist extends ComponentList { + + private final ObjectProperty headerLeft = new SimpleObjectProperty<>(this, "headerLeft"); + private final ObjectProperty headerRight = new SimpleObjectProperty<>(this, "headerRight"); + + public ComponentSublist() { + super(); + } + + public Node getHeaderLeft() { + return headerLeft.get(); + } + + public ObjectProperty headerLeftProperty() { + return headerLeft; + } + + public void setHeaderLeft(Node headerLeft) { + this.headerLeft.set(headerLeft); + } + + public Node getHeaderRight() { + return headerRight.get(); + } + + public ObjectProperty headerRightProperty() { + return headerRight; + } + + public void setHeaderRight(Node headerRight) { + this.headerRight.set(headerRight); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index 7d0497c9a..d7fe0ee43 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -23,17 +23,15 @@ import static org.jackhuang.hmcl.util.VersionNumber.asVersion; import java.io.IOException; +import com.jfoenix.concurrency.JFXUtilities; +import javafx.beans.property.*; import org.jackhuang.hmcl.Metadata; +import org.jackhuang.hmcl.util.ImmediateStringProperty; import org.jackhuang.hmcl.util.NetworkUtils; import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; import javafx.beans.value.ObservableBooleanValue; public final class UpdateChecker { @@ -42,7 +40,7 @@ public final class UpdateChecker { public static final String CHANNEL_STABLE = "stable"; public static final String CHANNEL_DEV = "dev"; - private static StringProperty updateChannel = new SimpleStringProperty(CHANNEL_STABLE); + private static StringProperty updateChannel = new ImmediateStringProperty(null, "updateChannel", CHANNEL_STABLE); private static ObjectProperty latestVersion = new SimpleObjectProperty<>(); private static BooleanBinding outdated = Bindings.createBooleanBinding( @@ -55,6 +53,7 @@ public final class UpdateChecker { } }, latestVersion); + private static ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false); public static String getUpdateChannel() { return updateChannel.get(); @@ -84,22 +83,39 @@ public final class UpdateChecker { return outdated; } + public static boolean isCheckingUpdate() { + return checkingUpdate.get(); + } + + public static ReadOnlyBooleanProperty checkingUpdateProperty() { + return checkingUpdate.getReadOnlyProperty(); + } + public static void checkUpdate() throws IOException { if (!IntegrityChecker.isSelfVerified()) { return; } - String channel = getUpdateChannel(); - String url = NetworkUtils.withQuery(Metadata.UPDATE_URL, mapOf( - pair("version", Metadata.VERSION), - pair("channel", channel))); - - RemoteVersion fetched = RemoteVersion.fetch(url); - Platform.runLater(() -> { - if (channel.equals(getUpdateChannel())) { - latestVersion.set(fetched); - } + JFXUtilities.runInFXAndWait(() -> { + checkingUpdate.set(true); }); + + try { + + String channel = getUpdateChannel(); + String url = NetworkUtils.withQuery(Metadata.UPDATE_URL, mapOf( + pair("version", Metadata.VERSION), + pair("channel", channel))); + + RemoteVersion fetched = RemoteVersion.fetch(url); + Platform.runLater(() -> { + if (channel.equals(getUpdateChannel())) { + latestVersion.set(fetched); + } + }); + } finally { + Platform.runLater(() -> checkingUpdate.set(false)); + } } private static boolean isDevelopmentVersion(String version) { diff --git a/HMCL/src/main/resources/assets/fxml/setting.fxml b/HMCL/src/main/resources/assets/fxml/setting.fxml index ea0bebb0e..19cf76d20 100644 --- a/HMCL/src/main/resources/assets/fxml/setting.fxml +++ b/HMCL/src/main/resources/assets/fxml/setting.fxml @@ -6,6 +6,7 @@ + @@ -15,21 +16,28 @@ - - + + - - + + - - + + + + + + + + + diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 675f9b272..394778f5a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -311,9 +311,13 @@ settings.type=Version setting type settings.type.special=Specialized version settings(will not affect other versions) update=Update +update.channel.dev=Update to development version +update.channel.stable=Update to stable version +update.checking=Checking for updates update.failed=Failed to perform upgrade update.found=Update Available! update.newest_version=Latest version: %s +update.note=Development version contains more functionality and bug fixes as well as more possible bugs. And this will affect all HMCL installations in your computer. 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 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index d97cdb947..12a38b6df 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -311,9 +311,13 @@ settings.type=版本設置類型 settings.type.special=單獨版本設置(不會影響到其他版本的設定) update=啓動器更新 +update.channel.dev=更新到開發版 +update.channel.stable=更新到推薦版本 +update.checking=正在檢查更新 update.failed=更新失敗 update.found=發現更新 update.newest_version=最新版本爲:%s +update.note=開發版包含更多的功能以及錯誤修復,但也可能會包含其他的問題。選擇更新到開發版導致你電腦中所有的HMCL更新至開發版 update.latest=當前版本爲最新版本 update.no_browser=無法打開瀏覽器,網址已經複製到剪貼板了,您可以手動粘貼網址打開頁面 update.tooltip=更新 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 1048dc7aa..cf1ac1e00 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -311,9 +311,13 @@ settings.type=版本设置类型 settings.type.special=单独版本设置(不会影响到其他版本的设定) update=启动器更新 +update.channel.dev=更新到开发版 +update.channel.stable=更新到推荐版本 +update.checking=正在检查更新 update.failed=更新失败 update.found=发现更新 update.newest_version=最新版本为:%s +update.note=开发版包含更多的功能以及错误修复,但也可能会包含其他的问题。选择更新到开发版导致你电脑中所有的HMCL更新至开发版 update.latest=当前版本为最新版本 update.no_browser=无法打开浏览器,网址已经复制到剪贴板了,您可以手动粘贴网址打开页面 update.tooltip=更新