From e476460ab4aa1066c1314fb1475ea4aa839d9089 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Tue, 28 Sep 2021 12:54:54 +0800 Subject: [PATCH] feat: notification of dev/nightly version. --- .../java/org/jackhuang/hmcl/Metadata.java | 14 +++++ .../java/org/jackhuang/hmcl/ui/FXUtils.java | 44 ++++++++++++++ .../hmcl/ui/construct/AnnouncementCard.java | 60 +++++++++++++++++++ .../hmcl/ui/main/LauncherSettingsPage.java | 4 ++ .../org/jackhuang/hmcl/ui/main/MainPage.java | 18 +++++- HMCL/src/main/resources/assets/css/root.css | 13 ++++ .../resources/assets/lang/I18N.properties | 16 ++++- .../resources/assets/lang/I18N_zh.properties | 16 ++++- .../assets/lang/I18N_zh_CN.properties | 16 ++++- 9 files changed, 192 insertions(+), 9 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AnnouncementCard.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java b/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java index 2e3cf00be..8d91cfcea 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java @@ -43,6 +43,8 @@ public final class Metadata { public static final String PUBLISH_URL = "http://www.mcbbs.net/thread-142335-1-1.html"; public static final String EULA_URL = "https://hmcl.huangyuhui.net/eula"; + public static final String BUILD_CHANNEL = JarUtils.thisJar().flatMap(JarUtils::getManifest).map(manifest -> manifest.getMainAttributes().getValue("Build-Channel")).orElse("nightly"); + public static final Path MINECRAFT_DIRECTORY = OperatingSystem.getWorkingDirectory("minecraft"); public static final Path HMCL_DIRECTORY = getHMCLDirectory(); @@ -54,4 +56,16 @@ public final class Metadata { } return OperatingSystem.getWorkingDirectory("hmcl"); } + + public static boolean isStable() { + return "stable".equals(BUILD_CHANNEL); + } + + public static boolean isDev() { + return "dev".equals(BUILD_CHANNEL); + } + + public static boolean isNightly() { + return !isStable() && !isDev(); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index 718ef01a3..5a1553218 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -44,10 +44,13 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import javafx.scene.shape.Rectangle; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; import javafx.util.Callback; import javafx.util.Duration; import javafx.util.StringConverter; import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.ui.construct.JFXHyperlink; import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.ResourceNotFoundError; import org.jackhuang.hmcl.util.i18n.I18n; @@ -55,9 +58,17 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.javafx.ExtendedProperties; import org.jackhuang.hmcl.util.javafx.SafeStringConverter; import org.jackhuang.hmcl.util.platform.OperatingSystem; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import javax.swing.*; import javax.swing.event.HyperlinkEvent; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; @@ -65,6 +76,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URI; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.BooleanSupplier; @@ -687,4 +699,36 @@ public final class FXUtils { Controllers.showToast(i18n("message.copied")); } + + public static TextFlow segmentToTextFlow(final String segment, Consumer hyperlinkAction) throws ParserConfigurationException, IOException, SAXException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new InputSource(new StringReader("" + segment + ""))); + Element r = doc.getDocumentElement(); + + NodeList children = r.getChildNodes(); + List texts = new ArrayList<>(); + for (int i = 0; i < children.getLength(); i++) { + org.w3c.dom.Node node = children.item(i); + + if (node instanceof Element) { + Element element = (Element) node; + if ("a".equals(element.getTagName())) { + String href = element.getAttribute("href"); + JFXHyperlink hyperlink = new JFXHyperlink(element.getTextContent()); + hyperlink.setOnAction(e -> hyperlinkAction.accept(href)); + texts.add(hyperlink); + } else if ("br".equals(element.getTagName())) { + texts.add(new Text("\n")); + } else { + throw new IllegalArgumentException("unsupported tag " + element.getTagName()); + } + } else { + texts.add(new Text(node.getTextContent())); + } + } + final TextFlow tf = new TextFlow(texts.toArray(new javafx.scene.Node[0])); + return tf; + } + } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AnnouncementCard.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AnnouncementCard.java new file mode 100644 index 000000000..e6536e749 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AnnouncementCard.java @@ -0,0 +1,60 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2021 huangyuhui and contributors + * + * 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 . + */ +package org.jackhuang.hmcl.ui.construct; + +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; + +import java.util.logging.Level; + +import static org.jackhuang.hmcl.util.Logging.LOG; + +public class AnnouncementCard extends VBox { + + public AnnouncementCard(String title, String content) { + TextFlow tf; + try { + tf = FXUtils.segmentToTextFlow(content, AnnouncementCard::onAction); + } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to parse announcement content", e); + tf = new TextFlow(); + tf.getChildren().setAll(new Text(content)); + } + + Label label = new Label(title); + label.getStyleClass().add("title"); + getChildren().setAll(label, tf); + setSpacing(14); + getStyleClass().addAll("card", "announcement"); + } + + private static void onAction(String href) { + if (href.startsWith("hmcl://")) { + if ("hmcl://settings/feedback".equals(href)) { + Controllers.getSettingsPage().showFeedback(); + Controllers.navigate(Controllers.getSettingsPage()); + } + } else { + FXUtils.openLink(href); + } + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java index dad01f65e..1f635b0c9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java @@ -130,6 +130,10 @@ public class LauncherSettingsPage extends BorderPane implements DecoratorPage { tab.getSelectionModel().select(gameTab); } + public void showFeedback() { + tab.getSelectionModel().select(feedbackTab); + } + @Override public ReadOnlyObjectProperty stateProperty() { return state.getReadOnlyProperty(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 6f23e44e7..34757369d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -44,6 +44,7 @@ import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.construct.AnnouncementCard; import org.jackhuang.hmcl.ui.construct.PopupMenu; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; @@ -73,8 +74,9 @@ public final class MainPage extends StackPane implements DecoratorPage { private final ObservableList versionNodes; private Profile profile; - private StackPane updatePane; - private JFXButton menuButton; + private final VBox announcementPane; + private final StackPane updatePane; + private final JFXButton menuButton; { HBox titleNode = new HBox(8); @@ -92,6 +94,8 @@ public final class MainPage extends StackPane implements DecoratorPage { setPadding(new Insets(20)); + announcementPane = new VBox(16); + updatePane = new StackPane(); updatePane.setVisible(false); updatePane.getStyleClass().add("bubble"); @@ -198,7 +202,7 @@ public final class MainPage extends StackPane implements DecoratorPage { launchPane.getChildren().setAll(launchButton, separator, menuButton); } - getChildren().setAll(updatePane, launchPane); + getChildren().setAll(announcementPane, updatePane, launchPane); menu.setMaxHeight(365); menu.setMaxWidth(545); @@ -212,6 +216,14 @@ public final class MainPage extends StackPane implements DecoratorPage { Bindings.bindContent(menu.getContent(), versionNodes); } + public MainPage() { + if (Metadata.isNightly()) { + announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint"))); + } else if (Metadata.isDev()) { + announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint"))); + } + } + private void doAnimation(boolean show) { Duration duration = Duration.millis(320); Timeline nowAnimation = new Timeline(); diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 41e8058e6..f15a91e38 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -120,6 +120,19 @@ -fx-text-fill: rgba(0.0, 0.0, 0.0, 0.87); } +.announcement { + +} + +.announcement .title { + -fx-font-size: 14px; + -fx-font-weight: bold; +} + +.announcement JFXHyperlink, .announcement Text { + -fx-font-size: 12px; +} + .rippler-container { -jfx-rippler-fill: #a2a2a2; } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 6abad1c08..d37d3c79d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -839,8 +839,20 @@ system.operating_system=OS update=Update update.accept=Update update.changelog=Changes -update.channel.dev=Update to beta version -update.channel.stable=Update to stable version +update.channel.dev=Beta +update.channel.dev.hint=You are using the beta version, which may include some extra functionalities compared to the release version, only used for testing.\n\ + Beta version should be unstable!\n\ + If you met some problems, you can go to the feedback page to report them, or join chat Discord or KaiHeiLa to report problems.\n\n\ + To hide this hint, you need to donate or make impressive contribution in feedback. +update.channel.dev.title=Hints for beta version +update.channel.dev.update=Update to beta version +update.channel.nightly=Alpha +update.channel.nightly.hint=You are using the alpha version, which may include some extra functionalities compared to the beta and release version, only used for testing.\n\ + Alpha version should be unstable!\n\ + If you met some problems, you can go to the feedback page to report them, or join chat Discord or KaiHeiLa to report problems.\n\n\ + To hide this hint, you need to donate or make impressive contribution in feedback. +update.channel.nightly.title=Hints for alpha version +update.channel.stable.update=Update to release version update.checking=Checking for updates update.failed=Failed to perform update update.found=Update Available! diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 4d73f3014..df83b0edc 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -838,8 +838,20 @@ system.operating_system=操作系統 update=啟動器更新 update.accept=更新 update.changelog=更新日誌 -update.channel.dev=更新到開發版 -update.channel.stable=更新到建議版本 +update.channel.dev=測試版 +update.channel.dev.hint=你正在使用測試版。測試版包含一些未在正式版中包含的測試性功能,僅用於體驗新功能。\n\ + 測試版功能未受充分驗證,使用起來可能不穩定!\n\ + 如果你遇到了使用問題,可以在設置的 回饋頁面 中進行回饋,或加入回饋頁面中提供的 Discord開黑啦群以回饋問題。\n\n\ + 你需要 贊助 或在回饋過程中作出貢獻以隱藏本提示。 +update.channel.dev.title=測試版提示 +update.channel.dev.update=更新到開發版 +update.channel.nightly=預覽版 +update.channel.nightly.hint=你正在使用預覽版。預覽版可能會每天更新一次,包含一些未在正式版和測試版中包含的測試性功能,僅用於體驗新功能\n\ + 測試版功能未受充分驗證,使用起來可能不穩定!\n\ + 如果你遇到了使用問題,可以在設置的 回饋頁面 中進行回饋,或加入回饋頁面中提供的 Discord開黑啦群以回饋問題。\n\n\ + 你需要 贊助 或在回饋過程中作出貢獻以隱藏本提示。 +update.channel.nightly.title=預覽版提示 +update.channel.stable.update=更新到建議版本 update.checking=正在檢查更新 update.failed=更新失敗 update.found=發現到更新 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 bb9c600cf..f2a8de418 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -838,8 +838,20 @@ system.operating_system=操作系统 update=启动器更新 update.accept=更新 update.changelog=更新日志 -update.channel.dev=更新到开发版 -update.channel.stable=更新到推荐版本 +update.channel.dev=测试版 +update.channel.dev.hint=你正在使用测试版。测试版包含一些未在正式版中包含的测试性功能,仅用于体验新功能。\n\ + 测试版功能未受充分验证,使用起来可能不稳定!\n\ + 如果你遇到了使用问题,可以在设置的 反馈页面 中进行反馈,或加入反馈页面中提供的 Discord开黑啦群以反馈问题。\n\n\ + 你需要 赞助 或在反馈过程中作出贡献以隐藏本提示。 +update.channel.dev.title=测试版提示 +update.channel.dev.update=更新到开发版 +update.channel.nightly=预览版 +update.channel.nightly.hint=你正在使用预览版。预览版可能会每天更新一次,包含一些未在正式版和测试版中包含的测试性功能,仅用于体验新功能。\n\ + 测试版功能未受充分验证,使用起来可能不稳定!\n\ + 如果你遇到了使用问题,可以在设置的 反馈页面 中进行反馈,或加入反馈页面中提供的 Discord开黑啦群以反馈问题。\n\n\ + 你需要 赞助 或在反馈过程中作出贡献以隐藏本提示。 +update.channel.nightly.title=预览版提示 +update.channel.stable.update=更新到推荐版本 update.checking=正在检查更新 update.failed=更新失败 update.found=发现更新