diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java index a558c4b748..45f9ffbbec 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java @@ -42,6 +42,7 @@ import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.CommandBuilder; import org.jackhuang.hmcl.util.platform.NativeUtils; +import org.jackhuang.hmcl.ui.animation.AnimationUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.platform.SystemInfo; @@ -309,4 +310,52 @@ public static void stopWithoutPlatform() { } public static final CrashReporter CRASH_REPORTER = new CrashReporter(true); + + private static Runnable onApplicationReloadedCallback; + + public static void setOnApplicationReloaded(Runnable callback) { + onApplicationReloadedCallback = callback; + } + + /** + * Reload the application + */ + public static void reloadApplication() { + LOG.info("Reloading application...\n"); + + try { + // Reload configuration + try { + ConfigHolder.reload(); + LOG.info("Configuration reloaded successfully\n"); + } catch (SambaException e) { + showAlert(AlertType.WARNING, i18n("fatal.samba")); + } + + // Update UI in FX thread + Platform.runLater(() -> { + try { + // Re-initialize controllers + Controllers.reload(); + + // Update animation settings based on new configuration + AnimationUtils.updateAnimationSettings(); + + // 执行重新加载后的回调函数 + if (onApplicationReloadedCallback != null) { + onApplicationReloadedCallback.run(); + } + + LOG.info("Application reloaded successfully\n"); + } catch (Exception e) { + LOG.error("Failed to reload UI", e); + showAlert(AlertType.ERROR, i18n("launcher.reload.failed")); + } + }); + } catch (Exception e) { + LOG.error("Error during application reload\n", e); + showAlert(AlertType.ERROR, i18n("launcher.reload.failed")); + } + } + } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java index 2854207c33..c11d6be5d5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java @@ -209,4 +209,36 @@ private static GlobalConfig loadGlobalConfig() throws IOException { return new GlobalConfig(); } + public static void reload() { + try { + // Save current configuration state + boolean wasNewlyCreated = newlyCreated; + boolean wasOwnerChanged = ownerChanged; + boolean wasUnsupportedVersion = unsupportedVersion; + + // Reload configuration + configInstance = loadConfig(); + if (!unsupportedVersion) + configInstance.addListener(source -> FileSaver.save(configLocation, configInstance.toJson())); + + globalConfigInstance = loadGlobalConfig(); + globalConfigInstance.addListener(source -> FileSaver.save(GLOBAL_CONFIG_PATH, globalConfigInstance.toJson())); + + // Force update language settings + Locale.setDefault(config().getLocalization().getLocale()); + I18n.setLocale(configInstance.getLocalization()); + + // Update log retention policy (do not reinitialize Settings to avoid duplicate initialization) + LOG.setLogRetention(globalConfig().getLogRetention()); + + // Restore state flags + newlyCreated = wasNewlyCreated; + ownerChanged = wasOwnerChanged; + unsupportedVersion = wasUnsupportedVersion; + + LOG.info("Configuration reloaded and UI refreshed"); + } catch (IOException e) { + LOG.warning("Failed to reload configuration", e); + } + } } 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 346745edd9..034bac27f7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -116,6 +116,20 @@ public final class Controllers { private Controllers() { } + private static void setupFontAntialiasing() { + if (System.getProperty("prism.lcdtext") == null) { + String fontAntiAliasing = globalConfig().getFontAntiAliasing(); + if ("lcd".equalsIgnoreCase(fontAntiAliasing)) { + LOG.info("Enable sub-pixel antialiasing"); + System.getProperties().put("prism.lcdtext", "true"); + } else if ("gray".equalsIgnoreCase(fontAntiAliasing) + || OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && SCREEN.getOutputScaleX() > 1) { + LOG.info("Disable sub-pixel antialiasing"); + System.getProperties().put("prism.lcdtext", "false"); + } + } + } + public static Scene getScene() { return scene; } @@ -187,17 +201,7 @@ public static void onApplicationStop() { public static void initialize(Stage stage) { LOG.info("Start initializing application"); - if (System.getProperty("prism.lcdtext") == null) { - String fontAntiAliasing = globalConfig().getFontAntiAliasing(); - if ("lcd".equalsIgnoreCase(fontAntiAliasing)) { - LOG.info("Enable sub-pixel antialiasing"); - System.getProperties().put("prism.lcdtext", "true"); - } else if ("gray".equalsIgnoreCase(fontAntiAliasing) - || OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && SCREEN.getOutputScaleX() > 1) { - LOG.info("Disable sub-pixel antialiasing"); - System.getProperties().put("prism.lcdtext", "false"); - } - } + setupFontAntialiasing(); Controllers.stage = stage; @@ -517,6 +521,61 @@ public static boolean isStopped() { return decorator == null; } + /** + * Reload UI controllers + */ + public static void reload() { + if (isStopped()) { + throw new IllegalStateException("Application has been stopped"); + } + + // Reinitialize all pages + rootPage = new Lazy<>(RootPage::new); + versionPage = new Lazy<>(VersionPage::new); + gameListPage = new Lazy<>(() -> { + GameListPage gameListPage = new GameListPage(); + gameListPage.selectedProfileProperty().bindBidirectional(Profiles.selectedProfileProperty()); + gameListPage.profilesProperty().bindContent(Profiles.profilesProperty()); + FXUtils.applyDragListener(gameListPage, ModpackHelper::isFileModpackByExtension, modpacks -> { + Path modpack = modpacks.get(0); + Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(Profiles.getSelectedProfile(), modpack), i18n("install.modpack")); + }); + return gameListPage; + }); + downloadPage = new Lazy<>(DownloadPage::new); + accountListPage = new Lazy<>(() -> { + AccountListPage accountListPage = new AccountListPage(); + accountListPage.selectedAccountProperty().bindBidirectional(Accounts.selectedAccountProperty()); + accountListPage.accountsProperty().bindContent(Accounts.getAccounts()); + accountListPage.authServersProperty().bindContentBidirectional(config().getAuthlibInjectorServers()); + return accountListPage; + }); + settingsPage = new Lazy<>(LauncherSettingsPage::new); + + // Apply antialiasing settings when reloading + setupFontAntialiasing(); + + // Update window title + if (stage != null) { + stage.setTitle(Metadata.FULL_TITLE); + } + + // Reset scene and navigate back to home page + if (scene != null && decorator != null) { + scene.setRoot(decorator.getDecorator()); + decorator.getDecorator().prefWidthProperty().bind(scene.widthProperty()); + decorator.getDecorator().prefHeightProperty().bind(scene.heightProperty()); + + // Clear navigation history and navigate back to home page + if (decorator.getNavigator() != null) { + decorator.getNavigator().clear(); + decorator.navigate(getRootPage()); + } + } + + LOG.info("UI controllers have been successfully reloaded"); + } + public static void shutdown() { rootPage = null; versionPage = null; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/AnimationUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/AnimationUtils.java index 7dca68b049..caceb96ff7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/AnimationUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/animation/AnimationUtils.java @@ -36,14 +36,23 @@ private AnimationUtils() { public static void init() { } - private static final boolean ENABLED = !ConfigHolder.config().isAnimationDisabled(); - private static final boolean PLAY_WINDOW_ANIMATION = ENABLED && !OperatingSystem.CURRENT_OS.isLinuxOrBSD(); + private static boolean enabled; + private static boolean playWindowAnimation; + + static { + updateAnimationSettings(); + } + + public static void updateAnimationSettings() { + enabled = !ConfigHolder.config().isAnimationDisabled(); + playWindowAnimation = enabled && !OperatingSystem.CURRENT_OS.isLinuxOrBSD(); + } public static boolean isAnimationEnabled() { - return ENABLED; + return enabled; } public static boolean playWindowAnimation() { - return PLAY_WINDOW_ANIMATION; + return playWindowAnimation; } } 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 index 27ff5bccf0..eafdeb6c7e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java @@ -188,6 +188,10 @@ public Decorator getDecorator() { return decorator; } + public Navigator getNavigator() { + return navigator; + } + // ==== Background ==== //FXThread 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 index a4a5be60f6..5c1e542b34 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorSkin.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.ui.decorator; import com.jfoenix.controls.JFXButton; +import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.WeakInvalidationListener; import javafx.beans.binding.Bindings; @@ -30,6 +31,7 @@ import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.SkinBase; +import javafx.scene.control.Tooltip; import javafx.scene.effect.BlurType; import javafx.scene.effect.DropShadow; import javafx.scene.input.MouseButton; @@ -39,16 +41,22 @@ import javafx.scene.shape.Rectangle; import javafx.stage.Stage; +import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; +import java.util.Objects; import org.jackhuang.hmcl.ui.animation.AnimationProducer; import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.TransitionPane; import org.jackhuang.hmcl.ui.wizard.Navigation; import org.jackhuang.hmcl.util.platform.OperatingSystem; +import static org.jackhuang.hmcl.setting.ConfigHolder.config; +import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + public class DecoratorSkin extends SkinBase { private final StackPane root, parent; private final StackPane titleContainer; @@ -229,6 +237,41 @@ public DecoratorSkin(Decorator control) { buttonsContainer.setAlignment(Pos.TOP_RIGHT); buttonsContainer.setMaxHeight(40); { + + JFXButton btnReload = new JFXButton(); + btnReload.setGraphic(SVG.REFRESH.createIcon(Theme.foregroundFillBinding(), 20)); + btnReload.getStyleClass().add("jfx-decorator-button"); + btnReload.setTooltip(new Tooltip(i18n("button.reload"))); + btnReload.setVisible(false); + btnReload.setOnMouseClicked(e -> { + Launcher.reloadApplication(); + btnReload.setVisible(false); + }); + + // Create a generic listener to show reload button when property value changes + javafx.beans.value.ChangeListener restartButtonListener = (observable, oldValue, newValue) -> { + if (!Objects.equals(oldValue, newValue)) { + btnReload.setVisible(true); + } + }; + + // Listen to settings that require reload to take effect + config().localizationProperty().addListener(restartButtonListener); + globalConfig().fontAntiAliasingProperty().addListener(restartButtonListener); + config().animationDisabledProperty().addListener(restartButtonListener); + + Launcher.setOnApplicationReloaded(() -> { + Platform.runLater(() -> { + config().localizationProperty().removeListener(restartButtonListener); + globalConfig().fontAntiAliasingProperty().removeListener(restartButtonListener); + config().animationDisabledProperty().removeListener(restartButtonListener); + + config().localizationProperty().addListener(restartButtonListener); + globalConfig().fontAntiAliasingProperty().addListener(restartButtonListener); + config().animationDisabledProperty().addListener(restartButtonListener); + }); + }); + JFXButton btnHelp = new JFXButton(); btnHelp.setFocusTraversable(false); btnHelp.setGraphic(SVG.HELP.createIcon(Theme.foregroundFillBinding(), -1)); @@ -247,7 +290,7 @@ public DecoratorSkin(Decorator control) { btnClose.getStyleClass().add("jfx-decorator-button"); btnClose.setOnAction(e -> skinnable.close()); - buttonsContainer.getChildren().setAll(btnHelp, btnMin, btnClose); + buttonsContainer.getChildren().setAll(btnReload, btnHelp, btnMin, btnClose); } AnchorPane layer = new AnchorPane(); layer.setPickOnBounds(false); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index f2680d2710..b88081f027 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -195,6 +195,7 @@ button.save_as=Save As button.select_all=Select All button.view=View button.yes=Yes +button.reload=Reload chat=Join Group Chat @@ -840,6 +841,7 @@ launcher.contact=Contact Us launcher.crash=Hello Minecraft! Launcher has encountered a fatal error! Please copy the following log and ask for help on our Discord, QQ group, GitHub, or other Minecraft forum. launcher.crash.java_internal_error=Hello Minecraft! Launcher has encountered a fatal error because your Java is corrupted. Please uninstall your Java and download a suitable Java here. launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher has encountered a fatal error! Your launcher is outdated. Please update your launcher! +launcher.reload.failed=Reload Failed launcher.update_java=Please update your Java version. libraries.download=Downloading Libraries diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 1c3b800d16..2e1ba2a9ab 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -196,6 +196,7 @@ button.save_as=Guardar como button.select_all=Seleccionar todo button.view=Vista button.yes=Sí +button.reload=Recargar chat=Chat de grupo @@ -846,6 +847,7 @@ launcher.contact=Contacta con nosotros launcher.crash=Hello Minecraft! Launcher ha encontrado un error fatal. Por favor, copie el siguiente registro y pida ayuda en nuestra comunidad en Discord, GitHub o Minecraft Forums. launcher.crash.java_internal_error=Hello Minecraft! Launcher ha encontrado un error fatal porque su Java está dañado. Por favor, desinstala tu Java y descarga un Java adecuado aquí. launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher ha encontrado un error fatal. Su launcher está desactualizado. Por favor, ¡actualícelo! +launcher.reload.failed=Error de recarga launcher.update_java=Por favor, actualice su versión de Java. libraries.download=Descargando bibliotecas diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index e5c9e5e784..0965d9e035 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -156,6 +156,7 @@ button.save_as=名前を付けて保存 button.select_all=すべて選択 button.view=読む button.yes=はい +button.reload=再読み込み chat=グループチャット @@ -548,6 +549,7 @@ launcher.cache_directory.invalid=無効なディレクトリ。デフォルト launcher.contact=お問い合わせ launcher.crash=Hello Minecraft!ランチャーがクラッシュしました!次のコンテンツをコピーして、MCBBS、Baidu Tieba、GitHub、またはMinecraftForumを介してフィードバックを送信してください。 launcher.crash.hmcl_out_dated=Hello Minecraft!ランチャーがクラッシュしました!ランチャーが古くなっています。ランチャーを更新してください! +launcher.reload.failed=再読み込み失敗 launcher.update_java=Javaを更新してください。 login.empty_username=ユーザー名を設定していません! @@ -870,6 +872,7 @@ settings.launcher.proxy.socks=SOCKS settings.launcher.proxy.username=アカウント settings.launcher.theme=テーマ settings.launcher.title_transparent=タイトルの透明性 +settings.launcher.turn_off_animations=アニメーションをオフにする(再起動後に有効になります) settings.launcher.version_list_source=バージョンリストソース settings.memory=ゲームメモリ diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index b117bf296e..db1e976bd5 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -205,6 +205,7 @@ button.save_as=另存 button.select_all=悉擇之 button.view=覽 button.yes=然 +button.reload=重載 chat=會集 @@ -655,6 +656,7 @@ launcher.contact=伏惟候告 launcher.crash=HMCL 有謬而弗能正。宜鈔下文而報謬于右下之鈕。 launcher.crash.java_internal_error=HMCL 不能行,以爪哇壞也。宜去是爪哇,而擊以置爪哇之適者。 launcher.crash.hmcl_out_dated=HMCL 有謬而弗能正之。余識君之啟者舊矣,宜新之。 +launcher.reload.failed=重載失敗 launcher.update_java=宜迭更爪哇。\n君可求助於 https://docs.hmcl.net/help.html。 libraries.download=引之所依 diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index 310a52a466..b150b4bf6a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -196,6 +196,7 @@ button.save_as=Сохранить как button.select_all=Выбрать все button.view=Просмотреть button.yes=Да +button.reload=Перезагрузить chat=Групповой чат @@ -839,6 +840,7 @@ launcher.contact=Связаться с нами launcher.crash=Лаунчер столкнулся с фатальной ошибкой! Скопируйте следующий журнал и попросите помощи в нашем Discord, группе QQ, GitHub или на другом форуме Minecraft. launcher.crash.java_internal_error=Лаунчер столкнулся с фатальной ошибкой! Пожалуйста, удалите Java и скачайте подходящую Java здесь. launcher.crash.hmcl_out_dated=Лаунчер столкнулся с фатальной ошибкой! Ваш лаунчер устарел. Обновите его! +launcher.reload.failed=Ошибка перезагрузки launcher.update_java=Пожалуйста, обновите версию Java. libraries.download=Скачивание библиотек diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index bf2a920e32..3961f020bd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -193,6 +193,7 @@ button.save_as=Зберегти як button.select_all=Вибрати все button.view=Переглянути button.yes=Так +button.reload=Перезавантажити chat=Приєднатися до групового чату @@ -781,6 +782,7 @@ launcher.contact=Зв'яжіться з нами launcher.crash=Hello Minecraft! Лаунчер зіткнувся з фатальною помилкою! Скопіюйте наступний журнал та попросіть допомоги на нашому Discord, групі QQ, GitHub або іншому форумі Minecraft. launcher.crash.java_internal_error=Hello Minecraft! Лаунчер зіткнувся з фатальною помилкою, оскільки ваша Java пошкоджена. Видаліть вашу Java та завантажте відповідну Java тут. launcher.crash.hmcl_out_dated=Hello Minecraft! Лаунчер зіткнувся з фатальною помилкою! Ваш лаунчер застарів. Оновіть ваш лаунчер! +launcher.reload.failed=Помилка перезавантаження launcher.update_java=Оновіть вашу версію Java. libraries.download=Завантаження бібліотек diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 6d1270efcf..a99c317dcb 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -198,6 +198,7 @@ button.save_as=另存新檔 button.select_all=全選 button.view=查看 button.yes=是 +button.reload=重新加載 chat=官方群組 @@ -647,6 +648,7 @@ launcher.contact=聯絡我們 launcher.crash=Hello Minecraft! Launcher 遇到了無法處理的錯誤。請複製下列內容並透過 GitHub、Discord 或 HMCL QQ 群回報問題。 launcher.crash.java_internal_error=Hello Minecraft! Launcher 由於目前 Java 損壞而無法繼續執行。請移除目前 Java,點擊 此處 安裝合適的 Java 版本。 launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher 遇到了無法處理的錯誤。已偵測到你的啟動器不是最新版本,請更新後重試! +launcher.reload.failed=重新加載失敗 launcher.update_java=請更新你的 Java libraries.download=下載依賴庫 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 b3800bf5e7..b7260bd816 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -206,6 +206,7 @@ button.save_as=另存为 button.select_all=全选 button.view=查看 button.yes=是 +button.reload=重新加载 chat=官方群组 @@ -657,6 +658,7 @@ launcher.contact=联系我们 launcher.crash=Hello Minecraft! Launcher 遇到了无法处理的错误。请复制下列内容并点击右下角的按钮反馈问题。 launcher.crash.java_internal_error=Hello Minecraft! Launcher 由于当前 Java 损坏而无法继续运行。请卸载当前 Java,点击 此处 安装合适的 Java 版本。 launcher.crash.hmcl_out_dated=Hello Minecraft! Launcher 遇到了无法处理的错误。已检测到你的启动器不是最新版本,请更新后再试。 +launcher.reload.failed=重新加载失败 launcher.update_java=请更新你的 Java。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。 libraries.download=下载依赖库