Add update channel selection
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -149,6 +149,9 @@ public final class Config implements Cloneable, Observable {
|
||||
@SerializedName("authlibInjectorServers")
|
||||
private ObservableList<AuthlibInjectorServer> authlibInjectorServers = FXCollections.observableArrayList();
|
||||
|
||||
@SerializedName("updateChannel")
|
||||
private ObjectProperty<EnumUpdateChannel> 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<EnumUpdateChannel> updateChannelProperty() {
|
||||
return updateChannel;
|
||||
}
|
||||
|
||||
public void setUpdateChannel(EnumUpdateChannel updateChannel) {
|
||||
this.updateChannel.set(updateChannel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* 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
|
||||
}
|
||||
@@ -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<EnumUpdateChannel> updateChannel = new SimpleObjectProperty<EnumUpdateChannel>() {
|
||||
@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 ====
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;");
|
||||
|
||||
@@ -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<Node> headerLeft = new SimpleObjectProperty<>(this, "headerLeft");
|
||||
private final ObjectProperty<Node> headerRight = new SimpleObjectProperty<>(this, "headerRight");
|
||||
|
||||
public ComponentSublist() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Node getHeaderLeft() {
|
||||
return headerLeft.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<Node> headerLeftProperty() {
|
||||
return headerLeft;
|
||||
}
|
||||
|
||||
public void setHeaderLeft(Node headerLeft) {
|
||||
this.headerLeft.set(headerLeft);
|
||||
}
|
||||
|
||||
public Node getHeaderRight() {
|
||||
return headerRight.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<Node> headerRightProperty() {
|
||||
return headerRight;
|
||||
}
|
||||
|
||||
public void setHeaderRight(Node headerRight) {
|
||||
this.headerRight.set(headerRight);
|
||||
}
|
||||
}
|
||||
@@ -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<RemoteVersion> 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) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.*?>
|
||||
<?import org.jackhuang.hmcl.ui.FXUtils?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
@@ -15,21 +16,28 @@
|
||||
<VBox fx:id="rootPane" style="-fx-padding: 20;">
|
||||
<ComponentList fx:id="settingsPane">
|
||||
|
||||
<BorderPane> <!-- Update -->
|
||||
<left>
|
||||
<ComponentSublist fx:id="updatePane" title="%update" hasSubtitle="true">
|
||||
<headerLeft>
|
||||
<VBox>
|
||||
<Label fx:id="lblUpdate" text="%update" BorderPane.alignment="CENTER_LEFT"/>
|
||||
<Label fx:id="lblUpdateSub" styleClass="subtitle-label"/>
|
||||
</VBox>
|
||||
</left>
|
||||
<right>
|
||||
</headerLeft>
|
||||
<headerRight>
|
||||
<JFXButton fx:id="btnUpdate" onMouseClicked="#onUpdate" styleClass="toggle-icon4">
|
||||
<graphic>
|
||||
<fx:include source="/assets/svg/update.fxml" />
|
||||
</graphic>
|
||||
</JFXButton>
|
||||
</right>
|
||||
</BorderPane>
|
||||
</headerRight>
|
||||
<VBox spacing="8">
|
||||
<JFXRadioButton fx:id="chkUpdateStable" text="%update.channel.stable" />
|
||||
<JFXRadioButton fx:id="chkUpdateDev" text="%update.channel.dev" />
|
||||
<VBox style="-fx-padding: 10 0 0 0;">
|
||||
<Text fx:id="lblUpdateNote" text="%update.note" />
|
||||
</VBox>
|
||||
</VBox>
|
||||
</ComponentSublist>
|
||||
|
||||
<MultiFileItem fx:id="fileCommonLocation" title="%launcher.common_directory" directory="true" chooserTitle="%launcher.common_directory.choose" hasSubtitle="true" customText="%settings.custom" />
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=更新
|
||||
|
||||
@@ -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=更新
|
||||
|
||||
Reference in New Issue
Block a user