From 68ee3affb3bc74cd3cef1dc5c6f29f42d46ec656 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Tue, 29 Sep 2026 20:03:08 +0800 Subject: [PATCH 01/16] feat(april-fools): The Copper Age --- .../hmcl/setting/ThemeColorType.java | 5 +- .../hmcl/theme/ThemeColorSource.java | 35 ++- .../hmcl/theme/ThemePackManager.java | 3 + .../java/org/jackhuang/hmcl/theme/Themes.java | 16 +- .../org/jackhuang/hmcl/ui/Controllers.java | 45 ++-- .../org/jackhuang/hmcl/ui/main/MainPage.java | 209 ++++++++++-------- .../hmcl/ui/main/PersonalizationPage.java | 8 +- .../util/{ => aprilfools}/AprilFools.java | 52 ++++- .../jackhuang/hmcl/util/aprilfools/Lzh.java | 51 +++++ .../hmcl/util/aprilfools/TheCopperAge.java | 118 ++++++++++ .../main/resources/assets/img/honeycomb.png | Bin 0 -> 187 bytes .../main/resources/assets/img/iron_axe.png | Bin 0 -> 178 bytes .../resources/assets/lang/I18N.properties | 4 + .../assets/lang/I18N_zh_Hans.properties | 4 + .../assets/lang/I18N_zh_Hant.properties | 6 +- 15 files changed, 425 insertions(+), 131 deletions(-) rename HMCL/src/main/java/org/jackhuang/hmcl/util/{ => aprilfools}/AprilFools.java (56%) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/Lzh.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java create mode 100644 HMCL/src/main/resources/assets/img/honeycomb.png create mode 100644 HMCL/src/main/resources/assets/img/iron_axe.png diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ThemeColorType.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ThemeColorType.java index ae15616d951..30cf619e729 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ThemeColorType.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ThemeColorType.java @@ -32,5 +32,8 @@ public enum ThemeColorType { CUSTOM, /// Extracts the launcher theme color from the current background when possible. - BACKGROUND + BACKGROUND, + + /// Uses time-varying color of oxidizing copper provided by Minecraft + COPPER } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java index fdbcb4ce5da..a1ad15be357 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java @@ -21,6 +21,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; +import org.jackhuang.hmcl.util.aprilfools.TheCopperAge; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; @@ -29,7 +30,7 @@ /// Describes how a theme-pack appearance chooses its Monet seed color. @NotNullByDefault -public sealed interface ThemeColorSource permits ThemeColorSource.Default, ThemeColorSource.Custom, ThemeColorSource.Wallpaper { +public sealed interface ThemeColorSource permits ThemeColorSource.Copper, ThemeColorSource.Custom, ThemeColorSource.Default, ThemeColorSource.Wallpaper { /// JSON member name for the source type. String FIELD_SOURCE = "source"; @@ -51,6 +52,13 @@ static ThemeColorSource wallpaper() { return new Wallpaper(); } + /// Creates a copper color source. + /// + /// @return the copper color source + static ThemeColorSource copper() { + return new Copper(); + } + /// Parses a color source from a manifest JSON value. /// /// @param element the JSON value @@ -88,6 +96,9 @@ static ThemeColorSource fromJson(JsonElement element) throws JsonParseException if ("WALLPAPER".equals(normalized)) { return wallpaper(); } + if ("COPPER".equals(normalized)) { + return copper(); + } throw new JsonParseException("Unsupported theme color source: " + source); } @@ -169,4 +180,26 @@ public ThemeColor resolveFallback() { return ThemeColor.DEFAULT; } } + + /// A time-varying seed color of oxidizing copper provided by Minecraft. + @NotNullByDefault + record Copper() implements ThemeColorSource { + /// Creates a copper color source. + public Copper { + } + + /// Converts this color source to its JSON representation. + @Override + public JsonElement toJsonElement() { + JsonObject object = new JsonObject(); + object.addProperty(FIELD_SOURCE, "copper"); + return object; + } + + /// Returns the default color or copper used. + @Override + public ThemeColor resolveFallback() { + return ThemeColor.of(TheCopperAge.Degree.UNOXIDIZED.getColor()); + } + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java index c800b1041d1..b4693e051ff 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java @@ -721,6 +721,9 @@ private static ThemeColorSource currentThemeColorSource() throws IOException { } return ThemeColorSource.wallpaper(); } + if (themeColorType == ThemeColorType.COPPER) { + return ThemeColorSource.copper(); + } ThemeColor color = Objects.requireNonNullElse(settings().customThemeColorProperty().get(), ThemeColor.DEFAULT); return ThemeColorSource.custom(color); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/theme/Themes.java b/HMCL/src/main/java/org/jackhuang/hmcl/theme/Themes.java index df9cfe34b68..21bd505d1d3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/theme/Themes.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/theme/Themes.java @@ -62,6 +62,7 @@ import org.jackhuang.hmcl.ui.MacOSNativeUtils; import org.jackhuang.hmcl.ui.WindowsNativeUtils; import org.jackhuang.hmcl.util.MathUtils; +import org.jackhuang.hmcl.util.aprilfools.TheCopperAge; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.NativeUtils; import org.jackhuang.hmcl.util.platform.OSVersion; @@ -118,6 +119,7 @@ public final class Themes { if (FXUtils.ACCENT_COLOR != null) { observables.add(FXUtils.ACCENT_COLOR); } + observables.add(TheCopperAge.colorProperty()); bind(observables.toArray(new Observable[0])); } @@ -196,11 +198,17 @@ static ThemeColor resolveThemeColor( return fallback; } } + if (themeColorType == ThemeColorType.COPPER) { + TheCopperAge.startOxidation(); + return ThemeColor.of(TheCopperAge.getColor()); + } else { + TheCopperAge.stopAndClearState(); + } return switch (themeColorType) { - case DEFAULT -> ThemeColor.DEFAULT; case SYSTEM -> getSystemThemeColor(); case CUSTOM -> fallback; case BACKGROUND -> resolveWallpaperThemeColor(fallback, backgroundType); + default -> ThemeColor.DEFAULT; }; } @@ -209,6 +217,12 @@ private static ThemeColor resolveThemeColorSource( ThemeColor fallback, @Nullable ThemeColorSource source, BackgroundType backgroundType) { + if (source instanceof ThemeColorSource.Copper) { + TheCopperAge.startOxidation(); + return ThemeColor.of(TheCopperAge.getColor()); + } else { + TheCopperAge.stopAndClearState(); + } if (source == null) { return fallback; } 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 2e1b895065c..bf5c10ea6bd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -59,7 +59,8 @@ import org.jackhuang.hmcl.ui.instances.Instances; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.*; -import org.jackhuang.hmcl.util.i18n.I18n; +import org.jackhuang.hmcl.util.aprilfools.AprilFools; +import org.jackhuang.hmcl.util.aprilfools.Lzh; import org.jackhuang.hmcl.util.i18n.SupportedLocale; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; @@ -73,7 +74,6 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; -import java.time.LocalDate; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -89,7 +89,6 @@ public final class Controllers { public static final String JAVA_VERSION_TIP = "javaVersion"; public static final String JAVA_INTERPRETED_MODE_TIP = "javaInterpretedMode"; public static final String SOFTWARE_RENDERING = "softwareRendering"; - public static final String APRIL_FOOLS = "aprilFools"; private static GameInstancePage gameInstancePage; private static Lazy gameListPage = new Lazy<>(GameListPage::new); @@ -224,6 +223,7 @@ private static void configureMainStage(Stage stage) { /// Initializes the main application stage, scene graph, and background services. /// /// @param stage the primary application stage, which must not have been shown + @FXThread public static void initialize(Stage stage) { LOG.info("Start initializing application"); @@ -341,32 +341,16 @@ public static void initialize(Stage stage) { Controllers.dialog(agreementPane); } - aprilFools: - if (AprilFools.isEnabled()) { - int currentYear = LocalDate.now().getYear(); - if (state().getShownTips().get(APRIL_FOOLS) instanceof Number year && year.intValue() >= currentYear) - break aprilFools; - - if (!I18n.getLocale().getLocale().getLanguage().equals("zh")) - break aprilFools; - - SupportedLocale lzh = SupportedLocale.getSupportedLocales().stream() - .filter(locale -> "lzh".equals(locale.getName())) - .findFirst().orElse(null); - - if (lzh == null) { - LOG.warning("No supported locale found for lzh"); - break aprilFools; - } - - Runnable updateShowTips = () -> state().getShownTips().put(APRIL_FOOLS, currentYear); - + var aprilFoolsType = AprilFools.getRandomTypeToPrompt(); + if (aprilFoolsType == AprilFools.Type.LZH) { Controllers.confirmWithCountdown(i18n("launcher.april_fools.switch_lzh"), null, 10, MessageType.QUESTION, () -> { Controllers.confirm(i18n("launcher.april_fools.switch_lzh.confirm"), null, MessageType.QUESTION, () -> { + SupportedLocale lzh = Lzh.getLzhLocale(); + LOG.info("Switching locale to " + lzh); - updateShowTips.run(); + AprilFools.updateShownTips(); settings().languageProperty().set(lzh); Controllers.onApplicationStop(); @@ -384,8 +368,17 @@ public static void initialize(Stage stage) { } Platform.exit(); - }, updateShowTips); - }, updateShowTips); + }, AprilFools::updateShownTips); + }, AprilFools::updateShownTips); + } else if (aprilFoolsType == AprilFools.Type.THE_COPPER_AGE) { + Controllers.confirmWithCountdown(i18n("launcher.april_fools.copper"), null, 10, + MessageType.QUESTION, () -> { + Controllers.confirm(i18n("launcher.april_fools.copper.confirm"), null, MessageType.QUESTION, () -> { + AprilFools.updateShownTips(); + settings().getThemeAppearanceOverrides().add(LauncherSettings.THEME_APPEARANCE_COLOR); + settings().themeColorTypeProperty().set(ThemeColorType.COPPER); + }, AprilFools::updateShownTips); + }, AprilFools::updateShownTips); } tryInstallBundledModpack(GameDirectoryManager.getSelectedRepository()); 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 52c32536af8..1de0cb5e0a5 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 @@ -37,10 +37,7 @@ import javafx.scene.image.ImageView; import javafx.scene.input.MouseButton; 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.layout.*; import javafx.scene.text.TextFlow; import javafx.util.Duration; import org.jackhuang.hmcl.Metadata; @@ -67,6 +64,7 @@ import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.upgrade.UpdateHandler; import org.jackhuang.hmcl.util.*; +import org.jackhuang.hmcl.util.aprilfools.TheCopperAge; import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.javafx.BindingMapping; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -215,108 +213,135 @@ public final class MainPage extends StackPane implements DecoratorPage { updatePane.getChildren().setAll(hBox, closeUpdateButton); } + getChildren().add(updatePane); - HBox launchPane = new HBox(); - launchPane.getStyleClass().add("launch-pane"); - FXUtils.onChangeAndOperate(selectedRepositorySnapshot, ignored -> mutableInstances.setAll(GameDirectoryManager.getSelectedRepository().getDisplayInstances().toList())); - FXUtils.onScroll(launchPane, instances, list -> { - @Nullable HMCLGameInstance currentGame = getCurrentGame(); - @Nullable GameInstanceID currentId = currentGame != null ? currentGame.getId() : null; - return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); - }, instance -> instance.getRepository().setSelectedInstance(instance)); + HBox buttonsPane = new HBox(16); + { + HBox copperUtilities = new HBox(16); + copperUtilities.getStyleClass().add("card"); + copperUtilities.setMaxHeight(Region.USE_PREF_SIZE); + + JFXButton btnAxe = new JFXButton(); + btnAxe.getStyleClass().add("toggle-icon4"); + btnAxe.setGraphic(new ImageView(FXUtils.newBuiltinImage("/assets/img/iron_axe.png", 32D, 32D, true, true))); + btnAxe.setOnAction(e -> TheCopperAge.useAxe()); + + JFXButton btnWax = new JFXButton(); + btnWax.getStyleClass().add("toggle-icon4"); + btnWax.setGraphic(new ImageView(FXUtils.newBuiltinImage("/assets/img/honeycomb.png", 32D, 32D, true, true))); + btnWax.setOnAction(e -> TheCopperAge.wax()); - StackPane.setAlignment(launchPane, Pos.BOTTOM_RIGHT); + copperUtilities.getChildren().setAll(btnAxe, btnWax); + copperUtilities.visibleProperty().bind(TheCopperAge.oxidizingProperty()); + + buttonsPane.getChildren().add(copperUtilities); + } { - JFXButton launchButton = new JFXButton(); - launchButton.getStyleClass().add("launch-button"); - launchButton.setDefaultButton(true); + HBox launchPane = new HBox(); + launchPane.getStyleClass().add("launch-pane"); + FXUtils.onChangeAndOperate(selectedRepositorySnapshot, ignored -> mutableInstances.setAll(GameDirectoryManager.getSelectedRepository().getDisplayInstances().toList())); + FXUtils.onScroll(launchPane, instances, list -> { + @Nullable HMCLGameInstance currentGame = getCurrentGame(); + @Nullable GameInstanceID currentId = currentGame != null ? currentGame.getId() : null; + return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); + }, instance -> instance.getRepository().setSelectedInstance(instance)); + { - VBox graphic = new VBox(); - graphic.setAlignment(Pos.CENTER); - Label launchLabel = new Label(); - launchLabel.setStyle("-fx-font-size: 16px;"); - Label currentLabel = new Label(); - currentLabel.setStyle("-fx-font-size: 12px;"); - - FXUtils.onChangeAndOperate(currentGameProperty(), new Consumer<>() { - private Tooltip tooltip; - - @Override - public void accept(@Nullable HMCLGameInstance currentGame) { - if (currentGame == null) { - launchLabel.setText(i18n("instance.launch.empty")); - currentLabel.setText(null); - graphic.getChildren().setAll(launchLabel); - FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launchNoGame); - if (tooltip == null) - tooltip = new Tooltip(i18n("instance.launch.empty.tooltip")); - FXUtils.installFastTooltip(launchButton, tooltip); + JFXButton launchButton = new JFXButton(); + launchButton.getStyleClass().add("launch-button"); + launchButton.setDefaultButton(true); + { + VBox graphic = new VBox(); + graphic.setAlignment(Pos.CENTER); + Label launchLabel = new Label(); + launchLabel.setStyle("-fx-font-size: 16px;"); + Label currentLabel = new Label(); + currentLabel.setStyle("-fx-font-size: 12px;"); + + FXUtils.onChangeAndOperate(currentGameProperty(), new Consumer<>() { + private Tooltip tooltip; + + @Override + public void accept(@Nullable HMCLGameInstance currentGame) { + if (currentGame == null) { + launchLabel.setText(i18n("instance.launch.empty")); + currentLabel.setText(null); + graphic.getChildren().setAll(launchLabel); + FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launchNoGame); + if (tooltip == null) + tooltip = new Tooltip(i18n("instance.launch.empty.tooltip")); + FXUtils.installFastTooltip(launchButton, tooltip); + } else { + launchLabel.setText(i18n("instance.launch")); + currentLabel.setText(currentGame.getId().toString()); + graphic.getChildren().setAll(launchLabel, currentLabel); + FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launch); + if (tooltip != null) + Tooltip.uninstall(launchButton, tooltip); + } + } + }); + + launchButton.setGraphic(graphic); + } + + menuButton = new JFXButton(); + menuButton.getStyleClass().add("menu-button"); + menuButton.setOnAction(e -> { + if (GameListPopupMenu.hideShowing(menuButton)) { + return; + } + + JFXPopup popup = GameListPopupMenu.showAndGetPopup( + menuButton, + JFXPopup.PopupVPosition.BOTTOM, + JFXPopup.PopupHPosition.RIGHT, + 0, + -menuButton.getHeight(), + instances + ); + + Node graphic = menuButton.getGraphic(); + if (graphic != null) { + if (AnimationUtils.isAnimationEnabled()) { + Duration duration = Duration.millis(200); + RotateTransition rotateOpen = new RotateTransition(duration, graphic); + rotateOpen.setToAngle(-180); + FXUtils.playAnimation(graphic, "arrow-rotation", rotateOpen); + + popup.setOnHidden(windowEvent -> { + RotateTransition rotateClose = new RotateTransition(duration, graphic); + rotateClose.setToAngle(0); + FXUtils.playAnimation(graphic, "arrow-rotation", rotateClose); + }); } else { - launchLabel.setText(i18n("instance.launch")); - currentLabel.setText(currentGame.getId().toString()); - graphic.getChildren().setAll(launchLabel, currentLabel); - FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launch); - if (tooltip != null) - Tooltip.uninstall(launchButton, tooltip); + graphic.setRotate(-180); + popup.setOnHidden(windowEvent -> graphic.setRotate(0)); } } }); + FXUtils.installFastTooltip(menuButton, i18n("instance.switch")); + menuButton.setGraphic(SVG.ARROW_DROP_UP.createIcon(30)); - launchButton.setGraphic(graphic); - } - - menuButton = new JFXButton(); - menuButton.getStyleClass().add("menu-button"); - menuButton.setOnAction(e -> { - if (GameListPopupMenu.hideShowing(menuButton)) { - return; - } - - JFXPopup popup = GameListPopupMenu.showAndGetPopup( - menuButton, - JFXPopup.PopupVPosition.BOTTOM, - JFXPopup.PopupHPosition.RIGHT, - 0, - -menuButton.getHeight(), - instances - ); - - Node graphic = menuButton.getGraphic(); - if (graphic != null) { - if (AnimationUtils.isAnimationEnabled()) { - Duration duration = Duration.millis(200); - RotateTransition rotateOpen = new RotateTransition(duration, graphic); - rotateOpen.setToAngle(-180); - FXUtils.playAnimation(graphic, "arrow-rotation", rotateOpen); - - popup.setOnHidden(windowEvent -> { - RotateTransition rotateClose = new RotateTransition(duration, graphic); - rotateClose.setToAngle(0); - FXUtils.playAnimation(graphic, "arrow-rotation", rotateClose); - }); - } else { - graphic.setRotate(-180); - popup.setOnHidden(windowEvent -> graphic.setRotate(0)); + EventHandler secondaryClickHandle = event -> { + if (event.getButton() == MouseButton.SECONDARY && event.getClickCount() == 1) { + menuButton.fire(); + event.consume(); } - } - }); - FXUtils.installFastTooltip(menuButton, i18n("instance.switch")); - menuButton.setGraphic(SVG.ARROW_DROP_UP.createIcon(30)); + }; + launchButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle); + menuButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle); - EventHandler secondaryClickHandle = event -> { - if (event.getButton() == MouseButton.SECONDARY && event.getClickCount() == 1) { - menuButton.fire(); - event.consume(); - } - }; - launchButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle); - menuButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle); + launchPane.getChildren().setAll(launchButton, menuButton); + } - launchPane.getChildren().setAll(launchButton, menuButton); + buttonsPane.getChildren().add(launchPane); } - getChildren().addAll(updatePane, launchPane); - + buttonsPane.setAlignment(Pos.BOTTOM_RIGHT); + buttonsPane.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); + StackPane.setAlignment(buttonsPane, Pos.BOTTOM_RIGHT); + getChildren().add(buttonsPane); } private void showUpdateDialog(boolean show) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java index 003ea3f048c..b1ebc96f63d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java @@ -504,13 +504,19 @@ protected Node createRightNode() { i18n("settings.launcher.theme_color_type.background"), ThemeColorType.BACKGROUND); + var copperColorChoice = new RadioChoiceList.Choice( + i18n("settings.launcher.theme_color_type.copper"), + ThemeColorType.COPPER); + copperColorChoice.setSubtitle(i18n("settings.launcher.theme_color_type.copper.description")); + RadioChoiceList themeColorChoiceList = new RadioChoiceList<>(); themeColorChoiceList.setFallbackValue(ThemeColorType.DEFAULT); themeColorChoiceList.setChoices(Arrays.asList( defaultColorChoice, systemColorChoice, customColorChoice, - backgroundColorChoice)); + backgroundColorChoice, + copperColorChoice)); JFXButton themeColorOverrideButton = createThemeAppearanceOverrideButton(); themeColorSublist.setTitleRight(themeColorOverrideButton); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/AprilFools.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/AprilFools.java similarity index 56% rename from HMCL/src/main/java/org/jackhuang/hmcl/util/AprilFools.java rename to HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/AprilFools.java index 506f3f3bf01..f9e2e44e1e1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/AprilFools.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/AprilFools.java @@ -15,28 +15,38 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.util; +package org.jackhuang.hmcl.util.aprilfools; import org.jackhuang.hmcl.util.i18n.LocaleUtils; +import org.jetbrains.annotations.Nullable; import java.time.LocalDate; import java.time.Month; +import java.util.Arrays; import java.util.List; +import java.util.Random; +import java.util.function.Supplier; import static org.jackhuang.hmcl.setting.SettingsManager.settings; +import static org.jackhuang.hmcl.setting.SettingsManager.state; /// April Fools' Day utilities. /// -/// This class provides methods to check if it is April Fools' Day or near April Fools' Day. -/// It also provides a method to check if April Fools is enabled. +/// This class provides methods to check if it is April Fools' Day or if April Fools is enabled. +/// It also provides an enum of different April Fools effect types. /// /// @author Glavo public final class AprilFools { + public static final String APRIL_FOOLS_TIP = "aprilFools"; + private static final boolean ENABLED; + private static final int currentYear; + static { var date = LocalDate.now(); + currentYear = date.getYear(); // Some countries/regions may oppose April Fools' Day for various reasons. // Therefore, we use a regional whitelist to avoid risks. @@ -47,16 +57,20 @@ public final class AprilFools { "ES", "DE", "FR", "GB", "RU", "UA", "US" ).contains(LocaleUtils.SYSTEM_DEFAULT.getCountry()); - boolean aprilFoolsMode; + boolean aprilFoolsMode, forceAprilFools = false; String value = System.getProperty("hmcl.april_fools", System.getenv("HMCL_APRIL_FOOLS")); - if ("true".equalsIgnoreCase(value)) + if ("true".equalsIgnoreCase(value)) { aprilFoolsMode = true; - else if ("false".equalsIgnoreCase(value) || !supportedRegion) + forceAprilFools = true; + } else if ("false".equalsIgnoreCase(value) || !supportedRegion) { aprilFoolsMode = false; - else + } else { aprilFoolsMode = date.getMonth() == Month.APRIL && date.getDayOfMonth() == 1; + } - ENABLED = aprilFoolsMode && !settings().disableAprilFoolsProperty().get(); + ENABLED = aprilFoolsMode + && !settings().disableAprilFoolsProperty().get() + && (forceAprilFools || !(state().getShownTips().get(APRIL_FOOLS_TIP) instanceof Number year && year.intValue() >= currentYear)); } /// Whether April Fools is enabled. @@ -66,6 +80,28 @@ public static boolean isEnabled() { return ENABLED; } + public static @Nullable Type getRandomTypeToPrompt() { + if (!isEnabled()) return null; + Object[] supportedTypes = Arrays.stream(Type.values()).filter(t -> t.shouldPromptSupplier.get()).toArray(); + if (supportedTypes.length == 0) return null; + return (Type) supportedTypes[new Random().nextInt(supportedTypes.length)]; + } + + public static void updateShownTips() { + state().getShownTips().put(APRIL_FOOLS_TIP, currentYear); + } + private AprilFools() { } + + public enum Type { + LZH(Lzh::shouldPrompt), + THE_COPPER_AGE(() -> !TheCopperAge.isOxidizing()); + + private final Supplier shouldPromptSupplier; + + Type(Supplier supportedLazy) { + this.shouldPromptSupplier = supportedLazy; + } + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/Lzh.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/Lzh.java new file mode 100644 index 00000000000..7f7fa033799 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/Lzh.java @@ -0,0 +1,51 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 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.util.aprilfools; + +import org.jackhuang.hmcl.util.Lazy; +import org.jackhuang.hmcl.util.i18n.I18n; +import org.jackhuang.hmcl.util.i18n.SupportedLocale; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +public final class Lzh { + + private static final Lazy lzhLocale = new Lazy<>(() -> { + if (!I18n.getLocale().getLocale().getLanguage().equals("zh")) + return null; + + SupportedLocale lzh = SupportedLocale.getSupportedLocales().stream() + .filter(locale -> "lzh".equals(locale.getName())) + .findFirst().orElse(null); + + if (lzh == null) LOG.warning("No supported locale found for lzh"); + + return lzh; + }); + + public static boolean shouldPrompt() { + return lzhLocale.get() != null; + } + + public static SupportedLocale getLzhLocale() { + return lzhLocale.get(); + } + + private Lzh() { + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java new file mode 100644 index 00000000000..ec4b6a3cd12 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java @@ -0,0 +1,118 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 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.util.aprilfools; + +import javafx.beans.property.*; +import javafx.beans.value.ObservableValue; +import javafx.scene.paint.Color; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.util.FXThread; + +import java.util.Random; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public final class TheCopperAge { + + private static final ReadOnlyObjectWrapper oxidationDegree = new ReadOnlyObjectWrapper<>(Degree.UNOXIDIZED); + private static final ObservableValue color = oxidationDegree.map(Degree::getColor); + private static final BooleanProperty waxed = new SimpleBooleanProperty(false); + + private static final ReadOnlyBooleanWrapper oxidizing = new ReadOnlyBooleanWrapper(false); + + private static ScheduledExecutorService oxidationScheduler = null; + + public static ObservableValue colorProperty() { + return color; + } + + public static Color getColor() { + return colorProperty().getValue(); + } + + public static ReadOnlyBooleanProperty oxidizingProperty() { + return oxidizing.getReadOnlyProperty(); + } + + public static boolean isOxidizing() { + return oxidizing.get(); + } + + @FXThread + public static void startOxidation() { + if (isOxidizing()) return; + oxidizing.set(true); + oxidationScheduler = Executors.newScheduledThreadPool(1); + var random = new Random(); + + oxidationScheduler.scheduleAtFixedRate(() -> { + if (random.nextDouble() < 0.1) tryOxidize(); + }, 2, 5, TimeUnit.SECONDS); + } + + @FXThread + public static void stopAndClearState() { + if (!isOxidizing()) return; + oxidizing.set(false); + oxidationScheduler.shutdownNow(); + oxidationScheduler = null; + oxidationDegree.set(Degree.UNOXIDIZED); + waxed.set(false); + } + + private static void tryOxidize() { + FXUtils.runInFX(() -> { + if (!waxed.get()) oxidationDegree.set(Degree.values()[Math.min(oxidationDegree.get().ordinal() + 1, Degree.values().length - 1)]); + }); + } + + @FXThread + public static void wax() { + waxed.set(true); + } + + @FXThread + public static void useAxe() { + if (waxed.get()) { + waxed.set(false); + } else { + oxidationDegree.set(Degree.values()[Math.max(oxidationDegree.get().ordinal() - 1, 0)]); + } + } + + private TheCopperAge() { + } + + public enum Degree { + UNOXIDIZED("#D87F33"), + EXPOSED("#876B62"), + WEATHERED("#3A8E8C"), + OXIDIZED("#167E86"); + + private final Color color; + + Degree(String color) { + this.color = Color.web(color); + } + + public Color getColor() { + return color; + } + } +} diff --git a/HMCL/src/main/resources/assets/img/honeycomb.png b/HMCL/src/main/resources/assets/img/honeycomb.png new file mode 100644 index 0000000000000000000000000000000000000000..561b4ab9a9f43ac348f8256a19e520dd0aec60bd GIT binary patch literal 187 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!P=HT}E0F$mPxIG)%~yS5Ppeg~ zW^2FuYfuanWh@Eu3ubV5b|VeQ3HNky45_$P+Ov_j!9n1#-N(xRQ#JWiGz!wSPerDk zYxjKidcw`C@0^n#zt!bz-Jv(r@C&CvV+*fJv)CPeN0V*-nYkJjyIJlrCVAU-bEeh? gcF!}fs@uWv{0rl{-(F810Igu~boFyt=akR{0QyvGUjM7nY4DM b5CcQu(?;ugyZ?&<9l_w~>gTe~DWM4fVBkH8 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 9a56febc90e..0a382dab8d8 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -927,6 +927,8 @@ launcher.agreement.decline=Decline launcher.agreement.hint=You must agree to the EULA to use this software. launcher.april_fools.switch_lzh=HMCL Launcher now supports Classical Chinese, do you want to switch to Classical Chinese mode?\nYou can switch back to English in "置設 → 貫用 → 文". launcher.april_fools.switch_lzh.confirm=Confirm switching to Classical Chinese mode?\nClick "OK" to exit HMCL and restart it in Classical Chinese mode; click "Cancel" to keep the current language and enter HMCL homepage. +launcher.april_fools.copper=HMCL Launcher now supports "The Copper Age" theme, do you want to switch to "The Copper Age" theme color type?\nYou can switch it back in "Settings → Appearance → Theme Color" +launcher.april_fools.copper.confirm=Confirm switching theme color type to "The Copper Age"?\nClick "OK" to change the theme color; click "Cancel" to enter HMCL homepage directly. launcher.background=Background Image launcher.background.choose=Choose background image launcher.background.builtin=Built-in @@ -1607,6 +1609,8 @@ settings.launcher.theme_color_style.tonal_spot.desc=Reduces color saturation for settings.launcher.theme_color_style.vibrant=Vibrant settings.launcher.theme_color_style.vibrant.desc=Increases color saturation for a more vivid and impactful look. settings.launcher.theme_color_type.background=Follow Background Wallpaper +settings.launcher.theme_color_type.copper=The Copper Age +settings.launcher.theme_color_type.copper.description=Use a time-varying color of oxidizing copper settings.launcher.theme_color_type.custom=Custom Color settings.launcher.theme_color_type.custom.description=Use the color selected above. settings.launcher.theme_color_type.default=Default diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties index be22b30fc38..c5791ddab65 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties @@ -745,6 +745,8 @@ launcher.agreement.decline=拒绝 launcher.agreement.hint=同意本软件的用户协议与免责声明以使用本软件。 launcher.april_fools.switch_lzh=HMCL 启动器现已支持文言文,我们诚邀你参与体验。是否要将界面语言切换至中文 (文言)?\n在切换语言后,你可以在“置設 → 貫用 → 文”中将语言切换回中文 (简体)。 launcher.april_fools.switch_lzh.confirm=确认切换语言?\n点击“确定”后 HMCL 将自动重启并将界面语言切换至中文 (文言);点击“取消”将继续使用中文 (简体) 进入 HMCL 主页。 +launcher.april_fools.copper=HMCL 启动器现已支持“铜器时代”主题风格,我们诚邀你参与体验。是否要将主题色切换至“铜器时代”?\n切换后,你可以在“设置 → 外观 → 主题色”中切换回原来的主题色 +launcher.april_fools.copper.confirm=确认切换主题色?\n点击“确定”后 HMCL 将会变更主题色至随时间氧化的铜制品颜色;点击“取消”将直接进入 HMCL 主页 launcher.background=背景图片 launcher.background.choose=选择背景图片 launcher.background.builtin=内置 @@ -1419,6 +1421,8 @@ settings.launcher.theme_color_style.tonal_spot.desc=降低色彩饱和度,使 settings.launcher.theme_color_style.vibrant=鲜艳 settings.launcher.theme_color_style.vibrant.desc=提高色彩饱和度,使色彩更具冲击力 settings.launcher.theme_color_type.background=跟随背景壁纸 +settings.launcher.theme_color_type.copper=铜器时代 +settings.launcher.theme_color_type.copper.description=使用随时间氧化的铜制品颜色。 settings.launcher.theme_color_type.custom=自定义颜色 settings.launcher.theme_color_type.custom.description=使用上方选择的颜色。 settings.launcher.theme_color_type.default=默认 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties index 458f32c556f..17ecb037ad7 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties @@ -739,6 +739,8 @@ launcher.agreement.decline=拒絕 launcher.agreement.hint=同意本軟體的使用者協議與免責宣告以使用本軟體。 launcher.april_fools.switch_lzh=HMCL 啟動器現已支援文言文,我們誠邀你參與體驗。是否要將介面語言切換至中文 (文言)?\n在切換語言後,你可以在「置設 → 貫用 → 文」中將語言切換回中文 (繁體)。 launcher.april_fools.switch_lzh.confirm=確認切換語言?\n點擊「確定」後 HMCL 將自動重新啟動並將介面語言切換至中文 (文言);點擊「取消」將繼續使用中文 (繁體) 進入 HMCL 主頁。 +launcher.april_fools.copper=HMCL 啟動器現已支援「銅器時代」主題風格,我們誠邀你參與體驗。是否要將主題色切換至“銅器時代”?\n切換後,你可以在「設定 → 外觀 → 主題色」中切換回原來的主題色 +launcher.april_fools.copper.confirm=確認切換主題色?\n點選「確定」後 HMCL 將會變更主題色至隨時間氧化的銅製品顏色;點擊「取消」將直接進入 HMCL 主頁。 launcher.background=背景圖片 launcher.background.choose=選取背景圖片 launcher.background.builtin=內建 @@ -1412,7 +1414,9 @@ settings.launcher.theme_color_style.tonal_spot=點綴 settings.launcher.theme_color_style.tonal_spot.desc=降低色彩飽和度,使色彩更加柔和 settings.launcher.theme_color_style.vibrant=鮮豔 settings.launcher.theme_color_style.vibrant.desc=提高色彩飽和度,使色彩更具衝擊力 -settings.launcher.theme_color_type.background=跟隨背景圖片 +settings.launcher.theme_color_type.background=跟隨背景圖片转换结果 +settings.launcher.theme_color_type.copper=銅器時代 +settings.launcher.theme_color_type.copper.description=使用隨時間氧化的銅製品顏色。 settings.launcher.theme_color_type.custom=自訂顏色 settings.launcher.theme_color_type.custom.description=使用上方選擇的顏色。 settings.launcher.theme_color_type.default=預設 From 0aba5c8a41d36ef87016ce283df8d22a909fc0e0 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Tue, 29 Sep 2026 21:00:29 +0800 Subject: [PATCH 02/16] display current oxidation state --- .../org/jackhuang/hmcl/ui/main/MainPage.java | 14 ++++++++++ .../hmcl/util/aprilfools/TheCopperAge.java | 28 +++++++++++++++++++ .../resources/assets/lang/I18N.properties | 6 +++- .../assets/lang/I18N_zh_Hans.properties | 4 +++ .../assets/lang/I18N_zh_Hant.properties | 4 +++ 5 files changed, 55 insertions(+), 1 deletion(-) 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 1de0cb5e0a5..efad5b640b1 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 @@ -23,6 +23,7 @@ import javafx.animation.KeyValue; import javafx.animation.RotateTransition; import javafx.animation.Timeline; +import javafx.beans.binding.StringBinding; import javafx.beans.property.*; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; @@ -119,6 +120,19 @@ public final class MainPage extends StackPane implements DecoratorPage { ImageView titleIcon = new ImageView(FXUtils.newBuiltinImage("/assets/img/icon-title.png")); Label titleLabel = new Label(Metadata.FULL_TITLE); + titleLabel.textProperty().bind(new StringBinding() { + { + bind(TheCopperAge.displayedState()); + } + + @Override + protected String computeValue() { + var copperState = TheCopperAge.displayedState().get(); + if (copperState.isEmpty()) return Metadata.FULL_TITLE; + if (!copperState.endsWith(" ")) copperState += " "; + return Metadata.FULL_TITLE + " (%sHMCL)".formatted(copperState); + } + }); if (I18n.isUpsideDown()) { titleIcon.setRotate(180); titleLabel.setRotate(180); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java index ec4b6a3cd12..127966f679a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java @@ -17,17 +17,21 @@ */ package org.jackhuang.hmcl.util.aprilfools; +import javafx.beans.binding.StringBinding; import javafx.beans.property.*; import javafx.beans.value.ObservableValue; import javafx.scene.paint.Color; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.util.FXThread; +import java.util.Locale; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + public final class TheCopperAge { private static final ReadOnlyObjectWrapper oxidationDegree = new ReadOnlyObjectWrapper<>(Degree.UNOXIDIZED); @@ -36,6 +40,26 @@ public final class TheCopperAge { private static final ReadOnlyBooleanWrapper oxidizing = new ReadOnlyBooleanWrapper(false); + private static final StringBinding displayedState = new StringBinding() { + { + bind(oxidationDegree, waxed, oxidizing); + } + + @Override + protected String computeValue() { + if (!oxidizing.get()) return ""; + String state; + var degree = oxidationDegree.get(); + if (degree == Degree.UNOXIDIZED) { + state = ""; + } else { + state = i18n("launcher.april_fools.copper." + degree.name().toLowerCase(Locale.ROOT)); + } + if (waxed.get()) state = i18n("launcher.april_fools.copper.waxed") + state; + return state; + } + }; + private static ScheduledExecutorService oxidationScheduler = null; public static ObservableValue colorProperty() { @@ -54,6 +78,10 @@ public static boolean isOxidizing() { return oxidizing.get(); } + public static StringBinding displayedState() { + return displayedState; + } + @FXThread public static void startOxidation() { if (isOxidizing()) return; diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 0a382dab8d8..827f059c3e9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -928,7 +928,11 @@ launcher.agreement.hint=You must agree to the EULA to use this software. launcher.april_fools.switch_lzh=HMCL Launcher now supports Classical Chinese, do you want to switch to Classical Chinese mode?\nYou can switch back to English in "置設 → 貫用 → 文". launcher.april_fools.switch_lzh.confirm=Confirm switching to Classical Chinese mode?\nClick "OK" to exit HMCL and restart it in Classical Chinese mode; click "Cancel" to keep the current language and enter HMCL homepage. launcher.april_fools.copper=HMCL Launcher now supports "The Copper Age" theme, do you want to switch to "The Copper Age" theme color type?\nYou can switch it back in "Settings → Appearance → Theme Color" -launcher.april_fools.copper.confirm=Confirm switching theme color type to "The Copper Age"?\nClick "OK" to change the theme color; click "Cancel" to enter HMCL homepage directly. +launcher.april_fools.copper.confirm=Confirm switching theme color type to "The Copper Age"?\nClick "OK" to change the theme color; click "Cancel" to enter HMCL homepage directly.ing theme color type to "The Copper Age"?\nClick "OK" to change the theme color; click "Cancel" to enter HMCL homepage directly. +launcher.april_fools.copper.exposed=Exposed +launcher.april_fools.copper.oxidized=Oxidized +launcher.april_fools.copper.waxed=Waxed +launcher.april_fools.copper.weathered=Weathered launcher.background=Background Image launcher.background.choose=Choose background image launcher.background.builtin=Built-in diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties index c5791ddab65..36921e22fa9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties @@ -747,6 +747,10 @@ launcher.april_fools.switch_lzh=HMCL 启动器现已支持文言文,我们诚 launcher.april_fools.switch_lzh.confirm=确认切换语言?\n点击“确定”后 HMCL 将自动重启并将界面语言切换至中文 (文言);点击“取消”将继续使用中文 (简体) 进入 HMCL 主页。 launcher.april_fools.copper=HMCL 启动器现已支持“铜器时代”主题风格,我们诚邀你参与体验。是否要将主题色切换至“铜器时代”?\n切换后,你可以在“设置 → 外观 → 主题色”中切换回原来的主题色 launcher.april_fools.copper.confirm=确认切换主题色?\n点击“确定”后 HMCL 将会变更主题色至随时间氧化的铜制品颜色;点击“取消”将直接进入 HMCL 主页 +launcher.april_fools.copper.exposed=斑驳的 +launcher.april_fools.copper.oxidized=氧化的 +launcher.april_fools.copper.waxed=涂蜡的 +launcher.april_fools.copper.weathered=锈蚀的 launcher.background=背景图片 launcher.background.choose=选择背景图片 launcher.background.builtin=内置 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties index 17ecb037ad7..6e79983fbc0 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties @@ -741,6 +741,10 @@ launcher.april_fools.switch_lzh=HMCL 啟動器現已支援文言文,我們誠 launcher.april_fools.switch_lzh.confirm=確認切換語言?\n點擊「確定」後 HMCL 將自動重新啟動並將介面語言切換至中文 (文言);點擊「取消」將繼續使用中文 (繁體) 進入 HMCL 主頁。 launcher.april_fools.copper=HMCL 啟動器現已支援「銅器時代」主題風格,我們誠邀你參與體驗。是否要將主題色切換至“銅器時代”?\n切換後,你可以在「設定 → 外觀 → 主題色」中切換回原來的主題色 launcher.april_fools.copper.confirm=確認切換主題色?\n點選「確定」後 HMCL 將會變更主題色至隨時間氧化的銅製品顏色;點擊「取消」將直接進入 HMCL 主頁。 +launcher.april_fools.copper.exposed=斑駁的 +launcher.april_fools.copper.oxidized=氧化的 +launcher.april_fools.copper.waxed=上蠟的 +launcher.april_fools.copper.weathered=風化的 launcher.background=背景圖片 launcher.background.choose=選取背景圖片 launcher.background.builtin=內建 From 13dfacc3173d72f8357a790333b6de83b142dd74 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Tue, 29 Sep 2026 21:01:07 +0800 Subject: [PATCH 03/16] sync i18n --- HMCL/src/main/resources/assets/lang/I18N_ar.properties | 8 ++++++++ HMCL/src/main/resources/assets/lang/I18N_de.properties | 8 ++++++++ HMCL/src/main/resources/assets/lang/I18N_es.properties | 8 ++++++++ HMCL/src/main/resources/assets/lang/I18N_ja.properties | 8 ++++++++ HMCL/src/main/resources/assets/lang/I18N_lzh.properties | 8 ++++++++ HMCL/src/main/resources/assets/lang/I18N_ru.properties | 8 ++++++++ HMCL/src/main/resources/assets/lang/I18N_uk.properties | 8 ++++++++ 7 files changed, 56 insertions(+) diff --git a/HMCL/src/main/resources/assets/lang/I18N_ar.properties b/HMCL/src/main/resources/assets/lang/I18N_ar.properties index 635db716b65..3d41a9a3e24 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ar.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ar.properties @@ -928,6 +928,12 @@ launcher.agreement.decline=رفض launcher.agreement.hint=يجب الموافقة على اتفاقية الترخيص لاستخدام هذا البرنامج. # launcher.april_fools.switch_lzh= # launcher.april_fools.switch_lzh.confirm= +# launcher.april_fools.copper= +# launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.exposed= +# launcher.april_fools.copper.oxidized= +# launcher.april_fools.copper.waxed= +# launcher.april_fools.copper.weathered= launcher.background=صورة الخلفية launcher.background.choose=اختر صورة الخلفية # launcher.background.builtin= @@ -1605,6 +1611,8 @@ settings.launcher.theme=لون السمة # settings.launcher.theme_color_style.vibrant= # settings.launcher.theme_color_style.vibrant.desc= # settings.launcher.theme_color_type.background= +# settings.launcher.theme_color_type.copper= +# settings.launcher.theme_color_type.copper.description= # settings.launcher.theme_color_type.custom= # settings.launcher.theme_color_type.custom.description= # settings.launcher.theme_color_type.default= diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index 0e3534cb791..d0786d16dcd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -927,6 +927,12 @@ launcher.agreement.decline=Ablehnen launcher.agreement.hint=Sie müssen der EULA zustimmen, um diese Software nutzen zu können. launcher.april_fools.switch_lzh=HMCL Launcher unterstützt nun Klassisches Chinesisch. Möchten Sie in den Modus für Klassisches Chinesisch wechseln?\nSie können unter "置設 → 貫用 → 文" zu Englisch zurückkehren. launcher.april_fools.switch_lzh.confirm=Wechsel in den Modus für Klassisches Chinesisch bestätigen?\nKlicken Sie auf "OK", um HMCL zu beenden und im Modus für Klassisches Chinesisch neu zu starten; klicken Sie auf "Abbrechen", um die aktuelle Sprache beizubehalten und zur HMCL-Startseite zu gelangen. +# launcher.april_fools.copper= +# launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.exposed= +# launcher.april_fools.copper.oxidized= +# launcher.april_fools.copper.waxed= +# launcher.april_fools.copper.weathered= launcher.background=Hintergrundbild launcher.background.choose=Hintergrundbild auswählen launcher.background.builtin=Eingebaut @@ -1606,6 +1612,8 @@ settings.launcher.theme_color_style.tonal_spot.desc=Reduziert die Farbsättigung settings.launcher.theme_color_style.vibrant=Lebhaft settings.launcher.theme_color_style.vibrant.desc=Erhöht die Farbsättigung für ein lebendigeres und wirkungsvolleres Erscheinungsbild. settings.launcher.theme_color_type.background=Hintergrundbild folgen +# settings.launcher.theme_color_type.copper= +# settings.launcher.theme_color_type.copper.description= settings.launcher.theme_color_type.custom=Benutzerdefinierte Farbe settings.launcher.theme_color_type.custom.description=Die oben ausgewählte Farbe verwenden. settings.launcher.theme_color_type.default=Standard diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 38d32da099e..34d03321ef9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -920,6 +920,12 @@ launcher.agreement.decline=Rechazar launcher.agreement.hint=Debe aceptar el EULA para utilizar este software. # launcher.april_fools.switch_lzh= # launcher.april_fools.switch_lzh.confirm= +# launcher.april_fools.copper= +# launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.exposed= +# launcher.april_fools.copper.oxidized= +# launcher.april_fools.copper.waxed= +# launcher.april_fools.copper.weathered= launcher.background=Imagen de fondo launcher.background.choose=Elige una imagen de fondo # launcher.background.builtin= @@ -1597,6 +1603,8 @@ settings.launcher.theme=Tema # settings.launcher.theme_color_style.vibrant= # settings.launcher.theme_color_style.vibrant.desc= # settings.launcher.theme_color_type.background= +# settings.launcher.theme_color_type.copper= +# settings.launcher.theme_color_type.copper.description= # settings.launcher.theme_color_type.custom= # settings.launcher.theme_color_type.custom.description= # settings.launcher.theme_color_type.default= diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index d6f3e2fc5df..f111f742f4b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -730,6 +730,12 @@ launcher.agreement.decline=同意しません launcher.agreement.hint=このソフトウェアを使用するには、EULAに同意する必要があります。 # launcher.april_fools.switch_lzh= # launcher.april_fools.switch_lzh.confirm= +# launcher.april_fools.copper= +# launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.exposed= +# launcher.april_fools.copper.oxidized= +# launcher.april_fools.copper.waxed= +# launcher.april_fools.copper.weathered= launcher.background=背景画像 launcher.background.choose=背景画像ファイルを選択してください # launcher.background.builtin= @@ -1401,6 +1407,8 @@ settings.launcher.theme=テーマ # settings.launcher.theme_color_style.vibrant= # settings.launcher.theme_color_style.vibrant.desc= # settings.launcher.theme_color_type.background= +# settings.launcher.theme_color_type.copper= +# settings.launcher.theme_color_type.copper.description= # settings.launcher.theme_color_type.custom= # settings.launcher.theme_color_type.custom.description= # settings.launcher.theme_color_type.default= diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 0abaf77bbf6..61c6fe0de7e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -742,6 +742,12 @@ launcher.agreement.decline=拒 launcher.agreement.hint=將用而須許其客契與免責告示。 # launcher.april_fools.switch_lzh= # launcher.april_fools.switch_lzh.confirm= +# launcher.april_fools.copper= +# launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.exposed= +# launcher.april_fools.copper.oxidized= +# launcher.april_fools.copper.waxed= +# launcher.april_fools.copper.weathered= launcher.background=繪圖 launcher.background.choose=擇繪圖 # launcher.background.builtin= @@ -1413,6 +1419,8 @@ settings.launcher.theme=主題 # settings.launcher.theme_color_style.vibrant= # settings.launcher.theme_color_style.vibrant.desc= # settings.launcher.theme_color_type.background= +# settings.launcher.theme_color_type.copper= +# settings.launcher.theme_color_type.copper.description= # settings.launcher.theme_color_type.custom= # settings.launcher.theme_color_type.custom.description= # settings.launcher.theme_color_type.default= diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index 8b75ae658b8..a4e614fa753 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -859,6 +859,12 @@ launcher.agreement.decline=Отклонить launcher.agreement.hint=Нужно принять пользовательское соглашение, чтобы использовать это программное обеспечение. launcher.april_fools.switch_lzh=Лаунчер HMCL теперь поддерживает классический китайский. Переключиться на классический китайский?\nВернуть прежний язык можно в «置設 → 貫用 → 文». launcher.april_fools.switch_lzh.confirm=Переключиться на классический китайский?\nНажмите «ОК», чтобы закрыть HMCL и перезапустить его на классическом китайском; нажмите «Отмена», чтобы оставить текущий язык и перейти на главную страницу HMCL. +# launcher.april_fools.copper= +# launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.exposed= +# launcher.april_fools.copper.oxidized= +# launcher.april_fools.copper.waxed= +# launcher.april_fools.copper.weathered= launcher.background=Фоновое изображение launcher.background.choose=Выберите фоновое изображение launcher.background.builtin=Встроенный @@ -1537,6 +1543,8 @@ settings.launcher.theme_color_style.tonal_spot.desc=Сниженная насы settings.launcher.theme_color_style.vibrant=Яркий settings.launcher.theme_color_style.vibrant.desc=Повышенная насыщенность для более яркого и выразительного вида. settings.launcher.theme_color_type.background=Как у фонового изображения +# settings.launcher.theme_color_type.copper= +# settings.launcher.theme_color_type.copper.description= settings.launcher.theme_color_type.custom=Свой цвет settings.launcher.theme_color_type.custom.description=Использовать цвет, выбранный выше. settings.launcher.theme_color_type.default=По умолчанию diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 6f1985bff05..0af90012c2e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -882,6 +882,12 @@ launcher.agreement.decline=Відхилити launcher.agreement.hint=Ви повинні погодитися з EULA, щоб використовувати це програмне забезпечення. launcher.april_fools.switch_lzh=HMCL Launcher тепер підтримує класичну китайську, ви хочете перемкнутися на класичний китайський режим?\nВи можете повернутися до української у "置設 → 貫用 → 文". launcher.april_fools.switch_lzh.confirm=Підтвердити перемикання на класичний китайський режим?\nНатисніть "ОК", щоб вийти з HMCL і перезапустити його у класичному китайському режимі; натисніть "Скасувати", щоб зберегти поточну мову та увійти на головну сторінку HMCL. +# launcher.april_fools.copper= +# launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.exposed= +# launcher.april_fools.copper.oxidized= +# launcher.april_fools.copper.waxed= +# launcher.april_fools.copper.weathered= launcher.background=Фонове зображення launcher.background.choose=Вибрати фонове зображення launcher.background.builtin=Вбудоване @@ -1560,6 +1566,8 @@ settings.launcher.theme_color_style.tonal_spot.desc=Зменшує насиче settings.launcher.theme_color_style.vibrant=Яскравий settings.launcher.theme_color_style.vibrant.desc=Збільшує насиченість кольорів для більш яскравого та ефектного вигляду. settings.launcher.theme_color_type.background=Відповідно до фонових шпалер +# settings.launcher.theme_color_type.copper= +# settings.launcher.theme_color_type.copper.description= settings.launcher.theme_color_type.custom=Власний колір settings.launcher.theme_color_type.custom.description=Використовуйте колір, вибраний вище. settings.launcher.theme_color_type.default=Типово From b562efdea42b30d60aaaebcf4fefa1410f88ba2d Mon Sep 17 00:00:00 2001 From: ToobLac Date: Tue, 29 Sep 2026 21:08:23 +0800 Subject: [PATCH 04/16] update --- HMCL/src/main/resources/assets/lang/I18N_de.properties | 8 ++++---- HMCL/src/main/resources/assets/lang/I18N_lzh.properties | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index d0786d16dcd..b0c43ccad00 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -929,10 +929,10 @@ launcher.april_fools.switch_lzh=HMCL Launcher unterstützt nun Klassisches Chine launcher.april_fools.switch_lzh.confirm=Wechsel in den Modus für Klassisches Chinesisch bestätigen?\nKlicken Sie auf "OK", um HMCL zu beenden und im Modus für Klassisches Chinesisch neu zu starten; klicken Sie auf "Abbrechen", um die aktuelle Sprache beizubehalten und zur HMCL-Startseite zu gelangen. # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -# launcher.april_fools.copper.exposed= -# launcher.april_fools.copper.oxidized= -# launcher.april_fools.copper.waxed= -# launcher.april_fools.copper.weathered= +launcher.april_fools.copper.exposed=Angelaufener +launcher.april_fools.copper.oxidized=Oxidierter +launcher.april_fools.copper.waxed=Gewachster +launcher.april_fools.copper.weathered=Verwitterter launcher.background=Hintergrundbild launcher.background.choose=Hintergrundbild auswählen launcher.background.builtin=Eingebaut diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 61c6fe0de7e..cbd8bc5b7b3 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -744,10 +744,10 @@ launcher.agreement.hint=將用而須許其客契與免責告示。 # launcher.april_fools.switch_lzh.confirm= # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -# launcher.april_fools.copper.exposed= -# launcher.april_fools.copper.oxidized= -# launcher.april_fools.copper.waxed= -# launcher.april_fools.copper.weathered= +launcher.april_fools.copper.exposed=渡濕 +launcher.april_fools.copper.oxidized=風蝕 +launcher.april_fools.copper.waxed=蠟引 +launcher.april_fools.copper.weathered=鏽刻 launcher.background=繪圖 launcher.background.choose=擇繪圖 # launcher.background.builtin= From 5436c3a475687e9a8087129d4991e99f52724c6d Mon Sep 17 00:00:00 2001 From: ToobLac Date: Tue, 29 Sep 2026 22:23:57 +0800 Subject: [PATCH 05/16] update --- .../hmcl/theme/ThemeColorSource.java | 34 +------------------ .../hmcl/theme/ThemePackManager.java | 3 +- .../java/org/jackhuang/hmcl/theme/Themes.java | 7 +--- 3 files changed, 4 insertions(+), 40 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java index a1ad15be357..d1e7d242d81 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java @@ -21,7 +21,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; -import org.jackhuang.hmcl.util.aprilfools.TheCopperAge; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; @@ -30,7 +29,7 @@ /// Describes how a theme-pack appearance chooses its Monet seed color. @NotNullByDefault -public sealed interface ThemeColorSource permits ThemeColorSource.Copper, ThemeColorSource.Custom, ThemeColorSource.Default, ThemeColorSource.Wallpaper { +public sealed interface ThemeColorSource permits ThemeColorSource.Custom, ThemeColorSource.Default, ThemeColorSource.Wallpaper { /// JSON member name for the source type. String FIELD_SOURCE = "source"; @@ -52,13 +51,6 @@ static ThemeColorSource wallpaper() { return new Wallpaper(); } - /// Creates a copper color source. - /// - /// @return the copper color source - static ThemeColorSource copper() { - return new Copper(); - } - /// Parses a color source from a manifest JSON value. /// /// @param element the JSON value @@ -96,9 +88,6 @@ static ThemeColorSource fromJson(JsonElement element) throws JsonParseException if ("WALLPAPER".equals(normalized)) { return wallpaper(); } - if ("COPPER".equals(normalized)) { - return copper(); - } throw new JsonParseException("Unsupported theme color source: " + source); } @@ -181,25 +170,4 @@ public ThemeColor resolveFallback() { } } - /// A time-varying seed color of oxidizing copper provided by Minecraft. - @NotNullByDefault - record Copper() implements ThemeColorSource { - /// Creates a copper color source. - public Copper { - } - - /// Converts this color source to its JSON representation. - @Override - public JsonElement toJsonElement() { - JsonObject object = new JsonObject(); - object.addProperty(FIELD_SOURCE, "copper"); - return object; - } - - /// Returns the default color or copper used. - @Override - public ThemeColor resolveFallback() { - return ThemeColor.of(TheCopperAge.Degree.UNOXIDIZED.getColor()); - } - } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java index b4693e051ff..f4d6a54ebe2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java @@ -35,6 +35,7 @@ import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.util.MathUtils; import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.aprilfools.TheCopperAge; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.i18n.LocalizedText; import org.jackhuang.hmcl.util.io.ContentEncoding; @@ -722,7 +723,7 @@ private static ThemeColorSource currentThemeColorSource() throws IOException { return ThemeColorSource.wallpaper(); } if (themeColorType == ThemeColorType.COPPER) { - return ThemeColorSource.copper(); + return ThemeColorSource.custom(ThemeColor.of(TheCopperAge.getColor())); } ThemeColor color = Objects.requireNonNullElse(settings().customThemeColorProperty().get(), ThemeColor.DEFAULT); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/theme/Themes.java b/HMCL/src/main/java/org/jackhuang/hmcl/theme/Themes.java index 21bd505d1d3..845f4702124 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/theme/Themes.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/theme/Themes.java @@ -217,12 +217,7 @@ private static ThemeColor resolveThemeColorSource( ThemeColor fallback, @Nullable ThemeColorSource source, BackgroundType backgroundType) { - if (source instanceof ThemeColorSource.Copper) { - TheCopperAge.startOxidation(); - return ThemeColor.of(TheCopperAge.getColor()); - } else { - TheCopperAge.stopAndClearState(); - } + TheCopperAge.stopAndClearState(); if (source == null) { return fallback; } From 2786f81de299a878645d3cd141cae401a36874b6 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Tue, 29 Sep 2026 22:25:07 +0800 Subject: [PATCH 06/16] update --- .../main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java index d1e7d242d81..fdbcb4ce5da 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemeColorSource.java @@ -29,7 +29,7 @@ /// Describes how a theme-pack appearance chooses its Monet seed color. @NotNullByDefault -public sealed interface ThemeColorSource permits ThemeColorSource.Custom, ThemeColorSource.Default, ThemeColorSource.Wallpaper { +public sealed interface ThemeColorSource permits ThemeColorSource.Default, ThemeColorSource.Custom, ThemeColorSource.Wallpaper { /// JSON member name for the source type. String FIELD_SOURCE = "source"; @@ -169,5 +169,4 @@ public ThemeColor resolveFallback() { return ThemeColor.DEFAULT; } } - } From 3fd14df85dbd981089e4deeff22f9fc3e06d65a6 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Wed, 30 Sep 2026 17:27:10 +0800 Subject: [PATCH 07/16] Update i18n Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com> --- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties | 4 ++-- HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 827f059c3e9..0cd53fc7a1f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -928,7 +928,7 @@ launcher.agreement.hint=You must agree to the EULA to use this software. launcher.april_fools.switch_lzh=HMCL Launcher now supports Classical Chinese, do you want to switch to Classical Chinese mode?\nYou can switch back to English in "置設 → 貫用 → 文". launcher.april_fools.switch_lzh.confirm=Confirm switching to Classical Chinese mode?\nClick "OK" to exit HMCL and restart it in Classical Chinese mode; click "Cancel" to keep the current language and enter HMCL homepage. launcher.april_fools.copper=HMCL Launcher now supports "The Copper Age" theme, do you want to switch to "The Copper Age" theme color type?\nYou can switch it back in "Settings → Appearance → Theme Color" -launcher.april_fools.copper.confirm=Confirm switching theme color type to "The Copper Age"?\nClick "OK" to change the theme color; click "Cancel" to enter HMCL homepage directly.ing theme color type to "The Copper Age"?\nClick "OK" to change the theme color; click "Cancel" to enter HMCL homepage directly. +launcher.april_fools.copper.confirm=Confirm switching theme color type to "The Copper Age"?\nClick "OK" to change the theme color, or click "Cancel" to keep the currently selected color. launcher.april_fools.copper.exposed=Exposed launcher.april_fools.copper.oxidized=Oxidized launcher.april_fools.copper.waxed=Waxed diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties index 36921e22fa9..ec6228466a6 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties @@ -745,8 +745,8 @@ launcher.agreement.decline=拒绝 launcher.agreement.hint=同意本软件的用户协议与免责声明以使用本软件。 launcher.april_fools.switch_lzh=HMCL 启动器现已支持文言文,我们诚邀你参与体验。是否要将界面语言切换至中文 (文言)?\n在切换语言后,你可以在“置設 → 貫用 → 文”中将语言切换回中文 (简体)。 launcher.april_fools.switch_lzh.confirm=确认切换语言?\n点击“确定”后 HMCL 将自动重启并将界面语言切换至中文 (文言);点击“取消”将继续使用中文 (简体) 进入 HMCL 主页。 -launcher.april_fools.copper=HMCL 启动器现已支持“铜器时代”主题风格,我们诚邀你参与体验。是否要将主题色切换至“铜器时代”?\n切换后,你可以在“设置 → 外观 → 主题色”中切换回原来的主题色 -launcher.april_fools.copper.confirm=确认切换主题色?\n点击“确定”后 HMCL 将会变更主题色至随时间氧化的铜制品颜色;点击“取消”将直接进入 HMCL 主页 +launcher.april_fools.copper=HMCL 启动器现已支持“铜器时代”主题风格,我们诚邀你参与体验。是否要将主题色切换至“铜器时代”?\n切换后,你可以在“设置 → 外观 → 主题色”中切换回原来的主题色。 +launcher.april_fools.copper.confirm=确认切换主题色?\n点击“确定”后 HMCL 将会变更主题色至随时间氧化的铜制品颜色;点击“取消”继续使用当前主题色。 launcher.april_fools.copper.exposed=斑驳的 launcher.april_fools.copper.oxidized=氧化的 launcher.april_fools.copper.waxed=涂蜡的 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties index 6e79983fbc0..d9b08b53029 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties @@ -739,8 +739,8 @@ launcher.agreement.decline=拒絕 launcher.agreement.hint=同意本軟體的使用者協議與免責宣告以使用本軟體。 launcher.april_fools.switch_lzh=HMCL 啟動器現已支援文言文,我們誠邀你參與體驗。是否要將介面語言切換至中文 (文言)?\n在切換語言後,你可以在「置設 → 貫用 → 文」中將語言切換回中文 (繁體)。 launcher.april_fools.switch_lzh.confirm=確認切換語言?\n點擊「確定」後 HMCL 將自動重新啟動並將介面語言切換至中文 (文言);點擊「取消」將繼續使用中文 (繁體) 進入 HMCL 主頁。 -launcher.april_fools.copper=HMCL 啟動器現已支援「銅器時代」主題風格,我們誠邀你參與體驗。是否要將主題色切換至“銅器時代”?\n切換後,你可以在「設定 → 外觀 → 主題色」中切換回原來的主題色 -launcher.april_fools.copper.confirm=確認切換主題色?\n點選「確定」後 HMCL 將會變更主題色至隨時間氧化的銅製品顏色;點擊「取消」將直接進入 HMCL 主頁。 +launcher.april_fools.copper=HMCL 啟動器現已支援「銅器時代」主題風格,我們誠邀你參與體驗。是否要將主題色切換至「銅器時代」?\n切換後,你可以在「設定 → 外觀 → 主題色」中切換回原來的主題色。 +launcher.april_fools.copper.confirm=確認切換主題色?\n點選「確定」後 HMCL 將會變更主題色至隨時間氧化的銅製品顏色;點選「取消」繼續使用目前主題色。 launcher.april_fools.copper.exposed=斑駁的 launcher.april_fools.copper.oxidized=氧化的 launcher.april_fools.copper.waxed=上蠟的 @@ -1418,7 +1418,7 @@ settings.launcher.theme_color_style.tonal_spot=點綴 settings.launcher.theme_color_style.tonal_spot.desc=降低色彩飽和度,使色彩更加柔和 settings.launcher.theme_color_style.vibrant=鮮豔 settings.launcher.theme_color_style.vibrant.desc=提高色彩飽和度,使色彩更具衝擊力 -settings.launcher.theme_color_type.background=跟隨背景圖片转换结果 +settings.launcher.theme_color_type.background=跟隨背景圖片 settings.launcher.theme_color_type.copper=銅器時代 settings.launcher.theme_color_type.copper.description=使用隨時間氧化的銅製品顏色。 settings.launcher.theme_color_type.custom=自訂顏色 From d61fccb6ed30d1fa50cee660863357232cecd7c8 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Wed, 30 Sep 2026 17:51:24 +0800 Subject: [PATCH 08/16] fix scheduler not stopping --- .../org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java | 3 ++- HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java index 127966f679a..feed43801f7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java @@ -23,6 +23,7 @@ import javafx.scene.paint.Color; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.util.FXThread; +import org.jackhuang.hmcl.util.Lang; import java.util.Locale; import java.util.Random; @@ -86,7 +87,7 @@ public static StringBinding displayedState() { public static void startOxidation() { if (isOxidizing()) return; oxidizing.set(true); - oxidationScheduler = Executors.newScheduledThreadPool(1); + oxidationScheduler = Executors.newScheduledThreadPool(1, Lang.daemonThreadFactory("The Copper Age Oxidizer")); var random = new Random(); oxidationScheduler.scheduleAtFixedRate(() -> { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java index 586ab45672e..e4d366e53d8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java @@ -208,6 +208,14 @@ public static ThreadPoolExecutor threadPool(String name, boolean daemon, int thr return pool; } + public static ThreadFactory daemonThreadFactory(String name) { + return r -> { + Thread t = new Thread(r, name); + t.setDaemon(true); + return t; + }; + } + public static ThreadFactory counterThreadFactory(String name, boolean daemon) { AtomicInteger counter = new AtomicInteger(1); return r -> { From d7c7755f8423ac62a4f413c4bf39f20b35e310c1 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Wed, 30 Sep 2026 18:06:24 +0800 Subject: [PATCH 09/16] update i18n --- .../org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java | 4 ++-- HMCL/src/main/resources/assets/lang/I18N.properties | 8 ++++---- HMCL/src/main/resources/assets/lang/I18N_ar.properties | 8 ++++---- HMCL/src/main/resources/assets/lang/I18N_de.properties | 8 ++++---- HMCL/src/main/resources/assets/lang/I18N_es.properties | 8 ++++---- HMCL/src/main/resources/assets/lang/I18N_ja.properties | 8 ++++---- HMCL/src/main/resources/assets/lang/I18N_lzh.properties | 8 ++++---- HMCL/src/main/resources/assets/lang/I18N_ru.properties | 8 ++++---- HMCL/src/main/resources/assets/lang/I18N_uk.properties | 8 ++++---- .../main/resources/assets/lang/I18N_zh_Hans.properties | 8 ++++---- .../main/resources/assets/lang/I18N_zh_Hant.properties | 8 ++++---- 11 files changed, 42 insertions(+), 42 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java index feed43801f7..b49875a17d5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java @@ -54,9 +54,9 @@ protected String computeValue() { if (degree == Degree.UNOXIDIZED) { state = ""; } else { - state = i18n("launcher.april_fools.copper." + degree.name().toLowerCase(Locale.ROOT)); + state = i18n("launcher.april_fools.copper.state." + degree.name().toLowerCase(Locale.ROOT)); } - if (waxed.get()) state = i18n("launcher.april_fools.copper.waxed") + state; + if (waxed.get()) state = i18n("launcher.april_fools.copper.state.waxed") + state; return state; } }; diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 0cd53fc7a1f..72f7a779786 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -929,10 +929,10 @@ launcher.april_fools.switch_lzh=HMCL Launcher now supports Classical Chinese, do launcher.april_fools.switch_lzh.confirm=Confirm switching to Classical Chinese mode?\nClick "OK" to exit HMCL and restart it in Classical Chinese mode; click "Cancel" to keep the current language and enter HMCL homepage. launcher.april_fools.copper=HMCL Launcher now supports "The Copper Age" theme, do you want to switch to "The Copper Age" theme color type?\nYou can switch it back in "Settings → Appearance → Theme Color" launcher.april_fools.copper.confirm=Confirm switching theme color type to "The Copper Age"?\nClick "OK" to change the theme color, or click "Cancel" to keep the currently selected color. -launcher.april_fools.copper.exposed=Exposed -launcher.april_fools.copper.oxidized=Oxidized -launcher.april_fools.copper.waxed=Waxed -launcher.april_fools.copper.weathered=Weathered +launcher.april_fools.copper.state.exposed=Exposed +launcher.april_fools.copper.state.oxidized=Oxidized +launcher.april_fools.copper.state.waxed=Waxed +launcher.april_fools.copper.state.weathered=Weathered launcher.background=Background Image launcher.background.choose=Choose background image launcher.background.builtin=Built-in diff --git a/HMCL/src/main/resources/assets/lang/I18N_ar.properties b/HMCL/src/main/resources/assets/lang/I18N_ar.properties index 3d41a9a3e24..90bd05cae01 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ar.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ar.properties @@ -930,10 +930,10 @@ launcher.agreement.hint=يجب الموافقة على اتفاقية الترخ # launcher.april_fools.switch_lzh.confirm= # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -# launcher.april_fools.copper.exposed= -# launcher.april_fools.copper.oxidized= -# launcher.april_fools.copper.waxed= -# launcher.april_fools.copper.weathered= +# launcher.april_fools.copper.state.exposed= +# launcher.april_fools.copper.state.oxidized= +# launcher.april_fools.copper.state.waxed= +# launcher.april_fools.copper.state.weathered= launcher.background=صورة الخلفية launcher.background.choose=اختر صورة الخلفية # launcher.background.builtin= diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index b0c43ccad00..2a775fe4fff 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -929,10 +929,10 @@ launcher.april_fools.switch_lzh=HMCL Launcher unterstützt nun Klassisches Chine launcher.april_fools.switch_lzh.confirm=Wechsel in den Modus für Klassisches Chinesisch bestätigen?\nKlicken Sie auf "OK", um HMCL zu beenden und im Modus für Klassisches Chinesisch neu zu starten; klicken Sie auf "Abbrechen", um die aktuelle Sprache beizubehalten und zur HMCL-Startseite zu gelangen. # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -launcher.april_fools.copper.exposed=Angelaufener -launcher.april_fools.copper.oxidized=Oxidierter -launcher.april_fools.copper.waxed=Gewachster -launcher.april_fools.copper.weathered=Verwitterter +launcher.april_fools.copper.state.exposed=Angelaufener +launcher.april_fools.copper.state.oxidized=Oxidierter +launcher.april_fools.copper.state.waxed=Gewachster +launcher.april_fools.copper.state.weathered=Verwitterter launcher.background=Hintergrundbild launcher.background.choose=Hintergrundbild auswählen launcher.background.builtin=Eingebaut diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 34d03321ef9..7731e98e00a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -922,10 +922,10 @@ launcher.agreement.hint=Debe aceptar el EULA para utilizar este software. # launcher.april_fools.switch_lzh.confirm= # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -# launcher.april_fools.copper.exposed= -# launcher.april_fools.copper.oxidized= -# launcher.april_fools.copper.waxed= -# launcher.april_fools.copper.weathered= +# launcher.april_fools.copper.state.exposed= +# launcher.april_fools.copper.state.oxidized= +# launcher.april_fools.copper.state.waxed= +# launcher.april_fools.copper.state.weathered= launcher.background=Imagen de fondo launcher.background.choose=Elige una imagen de fondo # launcher.background.builtin= diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index f111f742f4b..6ca20567f73 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -732,10 +732,10 @@ launcher.agreement.hint=このソフトウェアを使用するには、EULAに # launcher.april_fools.switch_lzh.confirm= # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -# launcher.april_fools.copper.exposed= -# launcher.april_fools.copper.oxidized= -# launcher.april_fools.copper.waxed= -# launcher.april_fools.copper.weathered= +# launcher.april_fools.copper.state.exposed= +# launcher.april_fools.copper.state.oxidized= +# launcher.april_fools.copper.state.waxed= +# launcher.april_fools.copper.state.weathered= launcher.background=背景画像 launcher.background.choose=背景画像ファイルを選択してください # launcher.background.builtin= diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index cbd8bc5b7b3..1292446df2d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -744,10 +744,10 @@ launcher.agreement.hint=將用而須許其客契與免責告示。 # launcher.april_fools.switch_lzh.confirm= # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -launcher.april_fools.copper.exposed=渡濕 -launcher.april_fools.copper.oxidized=風蝕 -launcher.april_fools.copper.waxed=蠟引 -launcher.april_fools.copper.weathered=鏽刻 +launcher.april_fools.copper.state.exposed=渡濕 +launcher.april_fools.copper.state.oxidized=風蝕 +launcher.april_fools.copper.state.waxed=蠟引 +launcher.april_fools.copper.state.weathered=鏽刻 launcher.background=繪圖 launcher.background.choose=擇繪圖 # launcher.background.builtin= diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index a4e614fa753..15844ce50b5 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -861,10 +861,10 @@ launcher.april_fools.switch_lzh=Лаунчер HMCL теперь поддерж launcher.april_fools.switch_lzh.confirm=Переключиться на классический китайский?\nНажмите «ОК», чтобы закрыть HMCL и перезапустить его на классическом китайском; нажмите «Отмена», чтобы оставить текущий язык и перейти на главную страницу HMCL. # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -# launcher.april_fools.copper.exposed= -# launcher.april_fools.copper.oxidized= -# launcher.april_fools.copper.waxed= -# launcher.april_fools.copper.weathered= +# launcher.april_fools.copper.state.exposed= +# launcher.april_fools.copper.state.oxidized= +# launcher.april_fools.copper.state.waxed= +# launcher.april_fools.copper.state.weathered= launcher.background=Фоновое изображение launcher.background.choose=Выберите фоновое изображение launcher.background.builtin=Встроенный diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 0af90012c2e..252807eb265 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -884,10 +884,10 @@ launcher.april_fools.switch_lzh=HMCL Launcher тепер підтримує кл launcher.april_fools.switch_lzh.confirm=Підтвердити перемикання на класичний китайський режим?\nНатисніть "ОК", щоб вийти з HMCL і перезапустити його у класичному китайському режимі; натисніть "Скасувати", щоб зберегти поточну мову та увійти на головну сторінку HMCL. # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -# launcher.april_fools.copper.exposed= -# launcher.april_fools.copper.oxidized= -# launcher.april_fools.copper.waxed= -# launcher.april_fools.copper.weathered= +# launcher.april_fools.copper.state.exposed= +# launcher.april_fools.copper.state.oxidized= +# launcher.april_fools.copper.state.waxed= +# launcher.april_fools.copper.state.weathered= launcher.background=Фонове зображення launcher.background.choose=Вибрати фонове зображення launcher.background.builtin=Вбудоване diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties index ec6228466a6..926265288a6 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties @@ -747,10 +747,10 @@ launcher.april_fools.switch_lzh=HMCL 启动器现已支持文言文,我们诚 launcher.april_fools.switch_lzh.confirm=确认切换语言?\n点击“确定”后 HMCL 将自动重启并将界面语言切换至中文 (文言);点击“取消”将继续使用中文 (简体) 进入 HMCL 主页。 launcher.april_fools.copper=HMCL 启动器现已支持“铜器时代”主题风格,我们诚邀你参与体验。是否要将主题色切换至“铜器时代”?\n切换后,你可以在“设置 → 外观 → 主题色”中切换回原来的主题色。 launcher.april_fools.copper.confirm=确认切换主题色?\n点击“确定”后 HMCL 将会变更主题色至随时间氧化的铜制品颜色;点击“取消”继续使用当前主题色。 -launcher.april_fools.copper.exposed=斑驳的 -launcher.april_fools.copper.oxidized=氧化的 -launcher.april_fools.copper.waxed=涂蜡的 -launcher.april_fools.copper.weathered=锈蚀的 +launcher.april_fools.copper.state.exposed=斑驳的 +launcher.april_fools.copper.state.oxidized=氧化的 +launcher.april_fools.copper.state.waxed=涂蜡的 +launcher.april_fools.copper.state.weathered=锈蚀的 launcher.background=背景图片 launcher.background.choose=选择背景图片 launcher.background.builtin=内置 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties index d9b08b53029..9573a8d66f5 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties @@ -741,10 +741,10 @@ launcher.april_fools.switch_lzh=HMCL 啟動器現已支援文言文,我們誠 launcher.april_fools.switch_lzh.confirm=確認切換語言?\n點擊「確定」後 HMCL 將自動重新啟動並將介面語言切換至中文 (文言);點擊「取消」將繼續使用中文 (繁體) 進入 HMCL 主頁。 launcher.april_fools.copper=HMCL 啟動器現已支援「銅器時代」主題風格,我們誠邀你參與體驗。是否要將主題色切換至「銅器時代」?\n切換後,你可以在「設定 → 外觀 → 主題色」中切換回原來的主題色。 launcher.april_fools.copper.confirm=確認切換主題色?\n點選「確定」後 HMCL 將會變更主題色至隨時間氧化的銅製品顏色;點選「取消」繼續使用目前主題色。 -launcher.april_fools.copper.exposed=斑駁的 -launcher.april_fools.copper.oxidized=氧化的 -launcher.april_fools.copper.waxed=上蠟的 -launcher.april_fools.copper.weathered=風化的 +launcher.april_fools.copper.state.exposed=斑駁的 +launcher.april_fools.copper.state.oxidized=氧化的 +launcher.april_fools.copper.state.waxed=上蠟的 +launcher.april_fools.copper.state.weathered=風化的 launcher.background=背景圖片 launcher.background.choose=選取背景圖片 launcher.background.builtin=內建 From 072b2418d9d6aafd8e143abb7b7598c45b341b5f Mon Sep 17 00:00:00 2001 From: ToobLac Date: Wed, 30 Sep 2026 18:26:01 +0800 Subject: [PATCH 10/16] update i18n --- .../java/org/jackhuang/hmcl/ui/main/MainPage.java | 3 +-- .../hmcl/util/aprilfools/TheCopperAge.java | 15 ++++++++++----- .../main/resources/assets/lang/I18N.properties | 10 ++++++---- .../main/resources/assets/lang/I18N_ar.properties | 2 ++ .../main/resources/assets/lang/I18N_de.properties | 2 ++ .../main/resources/assets/lang/I18N_es.properties | 10 ++++++---- .../main/resources/assets/lang/I18N_ja.properties | 2 ++ .../resources/assets/lang/I18N_lzh.properties | 2 ++ .../main/resources/assets/lang/I18N_ru.properties | 2 ++ .../main/resources/assets/lang/I18N_uk.properties | 2 ++ .../resources/assets/lang/I18N_zh_Hans.properties | 10 ++++++---- .../resources/assets/lang/I18N_zh_Hant.properties | 10 ++++++---- 12 files changed, 47 insertions(+), 23 deletions(-) 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 efad5b640b1..d1684544e4a 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 @@ -129,8 +129,7 @@ public final class MainPage extends StackPane implements DecoratorPage { protected String computeValue() { var copperState = TheCopperAge.displayedState().get(); if (copperState.isEmpty()) return Metadata.FULL_TITLE; - if (!copperState.endsWith(" ")) copperState += " "; - return Metadata.FULL_TITLE + " (%sHMCL)".formatted(copperState); + return Metadata.FULL_TITLE + " (%s)".formatted(copperState); } }); if (I18n.isUpsideDown()) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java index b49875a17d5..df5a9cf773e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java @@ -49,15 +49,20 @@ public final class TheCopperAge { @Override protected String computeValue() { if (!oxidizing.get()) return ""; - String state; + + String waxedString = i18n("launcher.april_fools.copper.state.waxed"); + var degree = oxidationDegree.get(); if (degree == Degree.UNOXIDIZED) { - state = ""; + return waxed.get() ? + i18n("launcher.april_fools.copper.state.0", waxedString) + : ""; } else { - state = i18n("launcher.april_fools.copper.state." + degree.name().toLowerCase(Locale.ROOT)); + String degreeName = i18n("launcher.april_fools.copper.state." + degree.name().toLowerCase(Locale.ROOT)); + return waxed.get() + ? i18n("launcher.april_fools.copper.state.1", waxedString, degreeName) + : i18n("launcher.april_fools.copper.state.0", degreeName); } - if (waxed.get()) state = i18n("launcher.april_fools.copper.state.waxed") + state; - return state; } }; diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 72f7a779786..6c3361b5f6e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -929,10 +929,12 @@ launcher.april_fools.switch_lzh=HMCL Launcher now supports Classical Chinese, do launcher.april_fools.switch_lzh.confirm=Confirm switching to Classical Chinese mode?\nClick "OK" to exit HMCL and restart it in Classical Chinese mode; click "Cancel" to keep the current language and enter HMCL homepage. launcher.april_fools.copper=HMCL Launcher now supports "The Copper Age" theme, do you want to switch to "The Copper Age" theme color type?\nYou can switch it back in "Settings → Appearance → Theme Color" launcher.april_fools.copper.confirm=Confirm switching theme color type to "The Copper Age"?\nClick "OK" to change the theme color, or click "Cancel" to keep the currently selected color. -launcher.april_fools.copper.state.exposed=Exposed -launcher.april_fools.copper.state.oxidized=Oxidized -launcher.april_fools.copper.state.waxed=Waxed -launcher.april_fools.copper.state.weathered=Weathered +launcher.april_fools.copper.state.0=%1s HMCL +launcher.april_fools.copper.state.1=%1s %2s HMCL +launcher.april_fools.copper.state.exposed=Exposed +launcher.april_fools.copper.state.oxidized=Oxidized +launcher.april_fools.copper.state.waxed=Waxed +launcher.april_fools.copper.state.weathered=Weathered launcher.background=Background Image launcher.background.choose=Choose background image launcher.background.builtin=Built-in diff --git a/HMCL/src/main/resources/assets/lang/I18N_ar.properties b/HMCL/src/main/resources/assets/lang/I18N_ar.properties index 90bd05cae01..03798fccdec 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ar.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ar.properties @@ -930,6 +930,8 @@ launcher.agreement.hint=يجب الموافقة على اتفاقية الترخ # launcher.april_fools.switch_lzh.confirm= # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.state.0= +# launcher.april_fools.copper.state.1= # launcher.april_fools.copper.state.exposed= # launcher.april_fools.copper.state.oxidized= # launcher.april_fools.copper.state.waxed= diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index 2a775fe4fff..d6d016140d5 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -929,6 +929,8 @@ launcher.april_fools.switch_lzh=HMCL Launcher unterstützt nun Klassisches Chine launcher.april_fools.switch_lzh.confirm=Wechsel in den Modus für Klassisches Chinesisch bestätigen?\nKlicken Sie auf "OK", um HMCL zu beenden und im Modus für Klassisches Chinesisch neu zu starten; klicken Sie auf "Abbrechen", um die aktuelle Sprache beizubehalten und zur HMCL-Startseite zu gelangen. # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= +launcher.april_fools.copper.state.0=%1s HMCL +launcher.april_fools.copper.state.1=%1s %2s HMCL launcher.april_fools.copper.state.exposed=Angelaufener launcher.april_fools.copper.state.oxidized=Oxidierter launcher.april_fools.copper.state.waxed=Gewachster diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 7731e98e00a..2a8e12de0ae 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -922,10 +922,12 @@ launcher.agreement.hint=Debe aceptar el EULA para utilizar este software. # launcher.april_fools.switch_lzh.confirm= # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= -# launcher.april_fools.copper.state.exposed= -# launcher.april_fools.copper.state.oxidized= -# launcher.april_fools.copper.state.waxed= -# launcher.april_fools.copper.state.weathered= +# launcher.april_fools.copper.state.0=HMCL %1s +# launcher.april_fools.copper.state.1=HMCL %2s %1s +# launcher.april_fools.copper.state.exposed=Expuesto +# launcher.april_fools.copper.state.oxidized=Oxidado +# launcher.april_fools.copper.state.waxed=Encerado +# launcher.april_fools.copper.state.weathered=Degradado launcher.background=Imagen de fondo launcher.background.choose=Elige una imagen de fondo # launcher.background.builtin= diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index 6ca20567f73..16d2a94d717 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -732,6 +732,8 @@ launcher.agreement.hint=このソフトウェアを使用するには、EULAに # launcher.april_fools.switch_lzh.confirm= # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.state.0= +# launcher.april_fools.copper.state.1= # launcher.april_fools.copper.state.exposed= # launcher.april_fools.copper.state.oxidized= # launcher.april_fools.copper.state.waxed= diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 1292446df2d..46107fdbf47 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -744,6 +744,8 @@ launcher.agreement.hint=將用而須許其客契與免責告示。 # launcher.april_fools.switch_lzh.confirm= # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= +launcher.april_fools.copper.state.0=%1s HMCL +launcher.april_fools.copper.state.1=%1s%2s HMCL launcher.april_fools.copper.state.exposed=渡濕 launcher.april_fools.copper.state.oxidized=風蝕 launcher.april_fools.copper.state.waxed=蠟引 diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index 15844ce50b5..3674afab25d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -861,6 +861,8 @@ launcher.april_fools.switch_lzh=Лаунчер HMCL теперь поддерж launcher.april_fools.switch_lzh.confirm=Переключиться на классический китайский?\nНажмите «ОК», чтобы закрыть HMCL и перезапустить его на классическом китайском; нажмите «Отмена», чтобы оставить текущий язык и перейти на главную страницу HMCL. # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.state.0= +# launcher.april_fools.copper.state.1= # launcher.april_fools.copper.state.exposed= # launcher.april_fools.copper.state.oxidized= # launcher.april_fools.copper.state.waxed= diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 252807eb265..8044cb20c0e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -884,6 +884,8 @@ launcher.april_fools.switch_lzh=HMCL Launcher тепер підтримує кл launcher.april_fools.switch_lzh.confirm=Підтвердити перемикання на класичний китайський режим?\nНатисніть "ОК", щоб вийти з HMCL і перезапустити його у класичному китайському режимі; натисніть "Скасувати", щоб зберегти поточну мову та увійти на головну сторінку HMCL. # launcher.april_fools.copper= # launcher.april_fools.copper.confirm= +# launcher.april_fools.copper.state.0= +# launcher.april_fools.copper.state.1= # launcher.april_fools.copper.state.exposed= # launcher.april_fools.copper.state.oxidized= # launcher.april_fools.copper.state.waxed= diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties index 926265288a6..91f9f00fe68 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties @@ -747,10 +747,12 @@ launcher.april_fools.switch_lzh=HMCL 启动器现已支持文言文,我们诚 launcher.april_fools.switch_lzh.confirm=确认切换语言?\n点击“确定”后 HMCL 将自动重启并将界面语言切换至中文 (文言);点击“取消”将继续使用中文 (简体) 进入 HMCL 主页。 launcher.april_fools.copper=HMCL 启动器现已支持“铜器时代”主题风格,我们诚邀你参与体验。是否要将主题色切换至“铜器时代”?\n切换后,你可以在“设置 → 外观 → 主题色”中切换回原来的主题色。 launcher.april_fools.copper.confirm=确认切换主题色?\n点击“确定”后 HMCL 将会变更主题色至随时间氧化的铜制品颜色;点击“取消”继续使用当前主题色。 -launcher.april_fools.copper.state.exposed=斑驳的 -launcher.april_fools.copper.state.oxidized=氧化的 -launcher.april_fools.copper.state.waxed=涂蜡的 -launcher.april_fools.copper.state.weathered=锈蚀的 +launcher.april_fools.copper.state.0=%1s的 HMCL +launcher.april_fools.copper.state.1=%1s的%2s HMCL +launcher.april_fools.copper.state.exposed=斑驳 +launcher.april_fools.copper.state.oxidized=氧化 +launcher.april_fools.copper.state.waxed=涂蜡 +launcher.april_fools.copper.state.weathered=锈蚀 launcher.background=背景图片 launcher.background.choose=选择背景图片 launcher.background.builtin=内置 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties index 9573a8d66f5..032a6963eab 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties @@ -741,10 +741,12 @@ launcher.april_fools.switch_lzh=HMCL 啟動器現已支援文言文,我們誠 launcher.april_fools.switch_lzh.confirm=確認切換語言?\n點擊「確定」後 HMCL 將自動重新啟動並將介面語言切換至中文 (文言);點擊「取消」將繼續使用中文 (繁體) 進入 HMCL 主頁。 launcher.april_fools.copper=HMCL 啟動器現已支援「銅器時代」主題風格,我們誠邀你參與體驗。是否要將主題色切換至「銅器時代」?\n切換後,你可以在「設定 → 外觀 → 主題色」中切換回原來的主題色。 launcher.april_fools.copper.confirm=確認切換主題色?\n點選「確定」後 HMCL 將會變更主題色至隨時間氧化的銅製品顏色;點選「取消」繼續使用目前主題色。 -launcher.april_fools.copper.state.exposed=斑駁的 -launcher.april_fools.copper.state.oxidized=氧化的 -launcher.april_fools.copper.state.waxed=上蠟的 -launcher.april_fools.copper.state.weathered=風化的 +launcher.april_fools.copper.state.0=%1s的 HMCL +launcher.april_fools.copper.state.1=%1s的%2s HMCL +launcher.april_fools.copper.state.exposed=斑駁 +launcher.april_fools.copper.state.oxidized=氧化 +launcher.april_fools.copper.state.waxed=上蠟 +launcher.april_fools.copper.state.weathered=風化 launcher.background=背景圖片 launcher.background.choose=選取背景圖片 launcher.background.builtin=內建 From 89efcffc0a8c157f725b211af7b25f7e627831d1 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Wed, 30 Sep 2026 18:35:13 +0800 Subject: [PATCH 11/16] update upside-down English --- .../org/jackhuang/hmcl/ui/main/MainPage.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) 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 d1684544e4a..469531f3d3a 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 @@ -120,7 +120,18 @@ public final class MainPage extends StackPane implements DecoratorPage { ImageView titleIcon = new ImageView(FXUtils.newBuiltinImage("/assets/img/icon-title.png")); Label titleLabel = new Label(Metadata.FULL_TITLE); - titleLabel.textProperty().bind(new StringBinding() { + if (I18n.isUpsideDown()) { + titleIcon.setRotate(180); + titleLabel.setRotate(180); + } + titleLabel.getStyleClass().add("jfx-decorator-title"); + titleLabel.textFillProperty().bind(Themes.titleFillProperty()); + titleNode.getChildren().setAll(titleIcon, titleLabel); + + Label copperStateLabel = new Label(); + copperStateLabel.getStyleClass().add("jfx-decorator-title"); + copperStateLabel.textFillProperty().bind(Themes.titleFillProperty()); + copperStateLabel.textProperty().bind(new StringBinding() { { bind(TheCopperAge.displayedState()); } @@ -128,17 +139,16 @@ public final class MainPage extends StackPane implements DecoratorPage { @Override protected String computeValue() { var copperState = TheCopperAge.displayedState().get(); - if (copperState.isEmpty()) return Metadata.FULL_TITLE; - return Metadata.FULL_TITLE + " (%s)".formatted(copperState); + if (copperState.isEmpty()) return ""; + return "(%s)".formatted(copperState); } }); if (I18n.isUpsideDown()) { - titleIcon.setRotate(180); - titleLabel.setRotate(180); + // Not rotating because we're already in upside-down English + titleNode.getChildren().add(1, copperStateLabel); // Place it between icon and title + } else { + titleNode.getChildren().add(copperStateLabel); } - titleLabel.getStyleClass().add("jfx-decorator-title"); - titleLabel.textFillProperty().bind(Themes.titleFillProperty()); - titleNode.getChildren().setAll(titleIcon, titleLabel); state.setValue(new State(null, titleNode, false, false, true)); From 8795cb48184340f9f8ff16ddc30d04a1faf73511 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Wed, 30 Sep 2026 18:38:14 +0800 Subject: [PATCH 12/16] tooltip --- .../org/jackhuang/hmcl/ui/main/MainPage.java | 219 +++++++++--------- .../resources/assets/lang/I18N.properties | 2 + .../assets/lang/I18N_zh_Hans.properties | 2 + .../assets/lang/I18N_zh_Hant.properties | 2 + 4 files changed, 112 insertions(+), 113 deletions(-) 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 469531f3d3a..175b080a2b1 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 @@ -236,135 +236,128 @@ protected String computeValue() { updatePane.getChildren().setAll(hBox, closeUpdateButton); } - getChildren().add(updatePane); - HBox buttonsPane = new HBox(16); + HBox launchPane = new HBox(); + launchPane.getStyleClass().add("launch-pane"); + FXUtils.onChangeAndOperate(selectedRepositorySnapshot, ignored -> mutableInstances.setAll(GameDirectoryManager.getSelectedRepository().getDisplayInstances().toList())); + FXUtils.onScroll(launchPane, instances, list -> { + @Nullable HMCLGameInstance currentGame = getCurrentGame(); + @Nullable GameInstanceID currentId = currentGame != null ? currentGame.getId() : null; + return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); + }, instance -> instance.getRepository().setSelectedInstance(instance)); + + StackPane.setAlignment(launchPane, Pos.BOTTOM_RIGHT); { - HBox copperUtilities = new HBox(16); - copperUtilities.getStyleClass().add("card"); - copperUtilities.setMaxHeight(Region.USE_PREF_SIZE); + JFXButton launchButton = new JFXButton(); + launchButton.getStyleClass().add("launch-button"); + launchButton.setDefaultButton(true); + { + VBox graphic = new VBox(); + graphic.setAlignment(Pos.CENTER); + Label launchLabel = new Label(); + launchLabel.setStyle("-fx-font-size: 16px;"); + Label currentLabel = new Label(); + currentLabel.setStyle("-fx-font-size: 12px;"); + + FXUtils.onChangeAndOperate(currentGameProperty(), new Consumer<>() { + private Tooltip tooltip; + + @Override + public void accept(@Nullable HMCLGameInstance currentGame) { + if (currentGame == null) { + launchLabel.setText(i18n("instance.launch.empty")); + currentLabel.setText(null); + graphic.getChildren().setAll(launchLabel); + FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launchNoGame); + if (tooltip == null) + tooltip = new Tooltip(i18n("instance.launch.empty.tooltip")); + FXUtils.installFastTooltip(launchButton, tooltip); + } else { + launchLabel.setText(i18n("instance.launch")); + currentLabel.setText(currentGame.getId().toString()); + graphic.getChildren().setAll(launchLabel, currentLabel); + FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launch); + if (tooltip != null) + Tooltip.uninstall(launchButton, tooltip); + } + } + }); + + launchButton.setGraphic(graphic); + } + + menuButton = new JFXButton(); + menuButton.getStyleClass().add("menu-button"); + menuButton.setOnAction(e -> { + if (GameListPopupMenu.hideShowing(menuButton)) { + return; + } + JFXPopup popup = GameListPopupMenu.showAndGetPopup( + menuButton, + JFXPopup.PopupVPosition.BOTTOM, + JFXPopup.PopupHPosition.RIGHT, + 0, + -menuButton.getHeight(), + instances + ); + + Node graphic = menuButton.getGraphic(); + if (graphic != null) { + if (AnimationUtils.isAnimationEnabled()) { + Duration duration = Duration.millis(200); + RotateTransition rotateOpen = new RotateTransition(duration, graphic); + rotateOpen.setToAngle(-180); + FXUtils.playAnimation(graphic, "arrow-rotation", rotateOpen); + + popup.setOnHidden(windowEvent -> { + RotateTransition rotateClose = new RotateTransition(duration, graphic); + rotateClose.setToAngle(0); + FXUtils.playAnimation(graphic, "arrow-rotation", rotateClose); + }); + } else { + graphic.setRotate(-180); + popup.setOnHidden(windowEvent -> graphic.setRotate(0)); + } + } + }); + FXUtils.installFastTooltip(menuButton, i18n("instance.switch")); + menuButton.setGraphic(SVG.ARROW_DROP_UP.createIcon(30)); + + EventHandler secondaryClickHandle = event -> { + if (event.getButton() == MouseButton.SECONDARY && event.getClickCount() == 1) { + menuButton.fire(); + event.consume(); + } + }; + launchButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle); + menuButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle); + + launchPane.getChildren().setAll(launchButton, menuButton); + } + + HBox copperUtilities = new HBox(16); + copperUtilities.getStyleClass().add("card"); + copperUtilities.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); + copperUtilities.visibleProperty().bind(TheCopperAge.oxidizingProperty()); + StackPane.setAlignment(copperUtilities, Pos.BOTTOM_LEFT); + { JFXButton btnAxe = new JFXButton(); btnAxe.getStyleClass().add("toggle-icon4"); btnAxe.setGraphic(new ImageView(FXUtils.newBuiltinImage("/assets/img/iron_axe.png", 32D, 32D, true, true))); + FXUtils.installFastTooltip(btnAxe, i18n("launcher.april_fools.copper.action.axe")); btnAxe.setOnAction(e -> TheCopperAge.useAxe()); JFXButton btnWax = new JFXButton(); btnWax.getStyleClass().add("toggle-icon4"); btnWax.setGraphic(new ImageView(FXUtils.newBuiltinImage("/assets/img/honeycomb.png", 32D, 32D, true, true))); + FXUtils.installFastTooltip(btnWax, i18n("launcher.april_fools.copper.action.wax")); btnWax.setOnAction(e -> TheCopperAge.wax()); copperUtilities.getChildren().setAll(btnAxe, btnWax); - copperUtilities.visibleProperty().bind(TheCopperAge.oxidizingProperty()); - - buttonsPane.getChildren().add(copperUtilities); - } - { - HBox launchPane = new HBox(); - launchPane.getStyleClass().add("launch-pane"); - FXUtils.onChangeAndOperate(selectedRepositorySnapshot, ignored -> mutableInstances.setAll(GameDirectoryManager.getSelectedRepository().getDisplayInstances().toList())); - FXUtils.onScroll(launchPane, instances, list -> { - @Nullable HMCLGameInstance currentGame = getCurrentGame(); - @Nullable GameInstanceID currentId = currentGame != null ? currentGame.getId() : null; - return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); - }, instance -> instance.getRepository().setSelectedInstance(instance)); - - { - JFXButton launchButton = new JFXButton(); - launchButton.getStyleClass().add("launch-button"); - launchButton.setDefaultButton(true); - { - VBox graphic = new VBox(); - graphic.setAlignment(Pos.CENTER); - Label launchLabel = new Label(); - launchLabel.setStyle("-fx-font-size: 16px;"); - Label currentLabel = new Label(); - currentLabel.setStyle("-fx-font-size: 12px;"); - - FXUtils.onChangeAndOperate(currentGameProperty(), new Consumer<>() { - private Tooltip tooltip; - - @Override - public void accept(@Nullable HMCLGameInstance currentGame) { - if (currentGame == null) { - launchLabel.setText(i18n("instance.launch.empty")); - currentLabel.setText(null); - graphic.getChildren().setAll(launchLabel); - FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launchNoGame); - if (tooltip == null) - tooltip = new Tooltip(i18n("instance.launch.empty.tooltip")); - FXUtils.installFastTooltip(launchButton, tooltip); - } else { - launchLabel.setText(i18n("instance.launch")); - currentLabel.setText(currentGame.getId().toString()); - graphic.getChildren().setAll(launchLabel, currentLabel); - FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launch); - if (tooltip != null) - Tooltip.uninstall(launchButton, tooltip); - } - } - }); - - launchButton.setGraphic(graphic); - } - - menuButton = new JFXButton(); - menuButton.getStyleClass().add("menu-button"); - menuButton.setOnAction(e -> { - if (GameListPopupMenu.hideShowing(menuButton)) { - return; - } - - JFXPopup popup = GameListPopupMenu.showAndGetPopup( - menuButton, - JFXPopup.PopupVPosition.BOTTOM, - JFXPopup.PopupHPosition.RIGHT, - 0, - -menuButton.getHeight(), - instances - ); - - Node graphic = menuButton.getGraphic(); - if (graphic != null) { - if (AnimationUtils.isAnimationEnabled()) { - Duration duration = Duration.millis(200); - RotateTransition rotateOpen = new RotateTransition(duration, graphic); - rotateOpen.setToAngle(-180); - FXUtils.playAnimation(graphic, "arrow-rotation", rotateOpen); - - popup.setOnHidden(windowEvent -> { - RotateTransition rotateClose = new RotateTransition(duration, graphic); - rotateClose.setToAngle(0); - FXUtils.playAnimation(graphic, "arrow-rotation", rotateClose); - }); - } else { - graphic.setRotate(-180); - popup.setOnHidden(windowEvent -> graphic.setRotate(0)); - } - } - }); - FXUtils.installFastTooltip(menuButton, i18n("instance.switch")); - menuButton.setGraphic(SVG.ARROW_DROP_UP.createIcon(30)); - - EventHandler secondaryClickHandle = event -> { - if (event.getButton() == MouseButton.SECONDARY && event.getClickCount() == 1) { - menuButton.fire(); - event.consume(); - } - }; - launchButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle); - menuButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle); - - launchPane.getChildren().setAll(launchButton, menuButton); - } - - buttonsPane.getChildren().add(launchPane); } - buttonsPane.setAlignment(Pos.BOTTOM_RIGHT); - buttonsPane.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); - StackPane.setAlignment(buttonsPane, Pos.BOTTOM_RIGHT); - getChildren().add(buttonsPane); + getChildren().addAll(updatePane, launchPane, copperUtilities); } private void showUpdateDialog(boolean show) { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 6c3361b5f6e..ffeb3f00fd3 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -929,6 +929,8 @@ launcher.april_fools.switch_lzh=HMCL Launcher now supports Classical Chinese, do launcher.april_fools.switch_lzh.confirm=Confirm switching to Classical Chinese mode?\nClick "OK" to exit HMCL and restart it in Classical Chinese mode; click "Cancel" to keep the current language and enter HMCL homepage. launcher.april_fools.copper=HMCL Launcher now supports "The Copper Age" theme, do you want to switch to "The Copper Age" theme color type?\nYou can switch it back in "Settings → Appearance → Theme Color" launcher.april_fools.copper.confirm=Confirm switching theme color type to "The Copper Age"?\nClick "OK" to change the theme color, or click "Cancel" to keep the currently selected color. +launcher.april_fools.copper.action.axe=Remove wax or a layer of aerugo +launcher.april_fools.copper.action.wax=Wax launcher.april_fools.copper.state.0=%1s HMCL launcher.april_fools.copper.state.1=%1s %2s HMCL launcher.april_fools.copper.state.exposed=Exposed diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties index 91f9f00fe68..740f45a0050 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties @@ -749,6 +749,8 @@ launcher.april_fools.copper=HMCL 启动器现已支持“铜器时代”主题 launcher.april_fools.copper.confirm=确认切换主题色?\n点击“确定”后 HMCL 将会变更主题色至随时间氧化的铜制品颜色;点击“取消”继续使用当前主题色。 launcher.april_fools.copper.state.0=%1s的 HMCL launcher.april_fools.copper.state.1=%1s的%2s HMCL +launcher.april_fools.copper.action.axe=去除蜡或一层铜锈 +launcher.april_fools.copper.action.wax=涂蜡 launcher.april_fools.copper.state.exposed=斑驳 launcher.april_fools.copper.state.oxidized=氧化 launcher.april_fools.copper.state.waxed=涂蜡 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties index 032a6963eab..89ed432e58e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties @@ -741,6 +741,8 @@ launcher.april_fools.switch_lzh=HMCL 啟動器現已支援文言文,我們誠 launcher.april_fools.switch_lzh.confirm=確認切換語言?\n點擊「確定」後 HMCL 將自動重新啟動並將介面語言切換至中文 (文言);點擊「取消」將繼續使用中文 (繁體) 進入 HMCL 主頁。 launcher.april_fools.copper=HMCL 啟動器現已支援「銅器時代」主題風格,我們誠邀你參與體驗。是否要將主題色切換至「銅器時代」?\n切換後,你可以在「設定 → 外觀 → 主題色」中切換回原來的主題色。 launcher.april_fools.copper.confirm=確認切換主題色?\n點選「確定」後 HMCL 將會變更主題色至隨時間氧化的銅製品顏色;點選「取消」繼續使用目前主題色。 +launcher.april_fools.copper.action.axe=去除蠟或一層銅鏽 +launcher.april_fools.copper.action.wax=上蠟 launcher.april_fools.copper.state.0=%1s的 HMCL launcher.april_fools.copper.state.1=%1s的%2s HMCL launcher.april_fools.copper.state.exposed=斑駁 From 44a7629d6e3f99f188c1442d2973042e7a007c87 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Thu, 1 Oct 2026 11:45:15 +0800 Subject: [PATCH 13/16] update --- .../hmcl/util/aprilfools/AprilFools.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/AprilFools.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/AprilFools.java index f9e2e44e1e1..b810d408a9b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/AprilFools.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/AprilFools.java @@ -57,20 +57,16 @@ public final class AprilFools { "ES", "DE", "FR", "GB", "RU", "UA", "US" ).contains(LocaleUtils.SYSTEM_DEFAULT.getCountry()); - boolean aprilFoolsMode, forceAprilFools = false; String value = System.getProperty("hmcl.april_fools", System.getenv("HMCL_APRIL_FOOLS")); if ("true".equalsIgnoreCase(value)) { - aprilFoolsMode = true; - forceAprilFools = true; - } else if ("false".equalsIgnoreCase(value) || !supportedRegion) { - aprilFoolsMode = false; + ENABLED = true; + } else if ("false".equalsIgnoreCase(value)) { + ENABLED = false; } else { - aprilFoolsMode = date.getMonth() == Month.APRIL && date.getDayOfMonth() == 1; + ENABLED = supportedRegion && date.getMonth() == Month.APRIL && date.getDayOfMonth() == 1 + && !settings().disableAprilFoolsProperty().get() + && !(state().getShownTips().get(APRIL_FOOLS_TIP) instanceof Number year && year.intValue() >= currentYear); } - - ENABLED = aprilFoolsMode - && !settings().disableAprilFoolsProperty().get() - && (forceAprilFools || !(state().getShownTips().get(APRIL_FOOLS_TIP) instanceof Number year && year.intValue() >= currentYear)); } /// Whether April Fools is enabled. From 81f35eb9bf2aecc6adcda41cd98f21ea1fa42dad Mon Sep 17 00:00:00 2001 From: ToobLac Date: Thu, 1 Oct 2026 18:08:31 +0800 Subject: [PATCH 14/16] =?UTF-8?q?=E4=B8=8A=E8=B0=83=E6=B0=A7=E5=8C=96?= =?UTF-8?q?=E6=A6=82=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java index df5a9cf773e..f88d0b4f4bb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java @@ -96,7 +96,7 @@ public static void startOxidation() { var random = new Random(); oxidationScheduler.scheduleAtFixedRate(() -> { - if (random.nextDouble() < 0.1) tryOxidize(); + if (random.nextDouble() < 0.2) tryOxidize(); }, 2, 5, TimeUnit.SECONDS); } From 0188b0a2c40d2284bc08b416912a61e8bd637dda Mon Sep 17 00:00:00 2001 From: ToobLac Date: Thu, 15 Oct 2026 21:56:03 +0800 Subject: [PATCH 15/16] use jfx 17 api --- .../jackhuang/hmcl/util/aprilfools/TheCopperAge.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java index f88d0b4f4bb..92d9f9b39da 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/aprilfools/TheCopperAge.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.util.aprilfools; +import javafx.beans.binding.ObjectBinding; import javafx.beans.binding.StringBinding; import javafx.beans.property.*; import javafx.beans.value.ObservableValue; @@ -36,7 +37,16 @@ public final class TheCopperAge { private static final ReadOnlyObjectWrapper oxidationDegree = new ReadOnlyObjectWrapper<>(Degree.UNOXIDIZED); - private static final ObservableValue color = oxidationDegree.map(Degree::getColor); + private static final ObjectBinding color = new ObjectBinding<>() { + { + bind(oxidationDegree); + } + + @Override + protected Color computeValue() { + return oxidationDegree.get().getColor(); + } + }; private static final BooleanProperty waxed = new SimpleBooleanProperty(false); private static final ReadOnlyBooleanWrapper oxidizing = new ReadOnlyBooleanWrapper(false); From 8efd5c48d63060a125c118f2566fa8c62c12184b Mon Sep 17 00:00:00 2001 From: ToobLac Date: Sat, 17 Oct 2026 18:11:54 +0800 Subject: [PATCH 16/16] enhance(i18n): update prompts for Classical Chinese and "The Copper Age" theme to reflect Minecraft 1.21.9 updates --- HMCL/src/main/resources/assets/lang/I18N.properties | 4 ++-- HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 38a7dae2699..f67ea7cd1d2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -929,9 +929,9 @@ launcher.agreement=ToS and EULA launcher.agreement.accept=Accept launcher.agreement.decline=Decline launcher.agreement.hint=You must agree to the EULA to use this software. -launcher.april_fools.switch_lzh=HMCL Launcher now supports Classical Chinese, do you want to switch to Classical Chinese mode?\nYou can switch back to English in "置設 → 貫用 → 文". +launcher.april_fools.switch_lzh=HMCL Launcher now supports Classical Chinese. Do you want to switch to Classical Chinese mode?\nYou can switch back to English in "置設 → 貫用 → 文". launcher.april_fools.switch_lzh.confirm=Confirm switching to Classical Chinese mode?\nClick "OK" to exit HMCL and restart it in Classical Chinese mode; click "Cancel" to keep the current language and enter HMCL homepage. -launcher.april_fools.copper=HMCL Launcher now supports "The Copper Age" theme, do you want to switch to "The Copper Age" theme color type?\nYou can switch it back in "Settings → Appearance → Theme Color" +launcher.april_fools.copper=In order to keep up with the updates in Minecraft 1.21.9, HMCL Launcher now supports "The Copper Age" theme. Do you want to switch to "The Copper Age" theme color type?\nYou can switch it back in "Settings → Appearance → Theme Color" launcher.april_fools.copper.confirm=Confirm switching theme color type to "The Copper Age"?\nClick "OK" to change the theme color, or click "Cancel" to keep the currently selected color. launcher.april_fools.copper.action.axe=Remove wax or a layer of aerugo launcher.april_fools.copper.action.wax=Wax diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties index 75ca7a6fdfe..ea2a53a7ba4 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hans.properties @@ -749,7 +749,7 @@ launcher.agreement.decline=拒绝 launcher.agreement.hint=同意本软件的用户协议与免责声明以使用本软件。 launcher.april_fools.switch_lzh=HMCL 启动器现已支持文言文,我们诚邀你参与体验。是否要将界面语言切换至中文 (文言)?\n在切换语言后,你可以在“置設 → 貫用 → 文”中将语言切换回中文 (简体)。 launcher.april_fools.switch_lzh.confirm=确认切换语言?\n点击“确定”后 HMCL 将自动重启并将界面语言切换至中文 (文言);点击“取消”将继续使用中文 (简体) 进入 HMCL 主页。 -launcher.april_fools.copper=HMCL 启动器现已支持“铜器时代”主题风格,我们诚邀你参与体验。是否要将主题色切换至“铜器时代”?\n切换后,你可以在“设置 → 外观 → 主题色”中切换回原来的主题色。 +launcher.april_fools.copper=为跟进 Minecraft 在 1.21.9 中的更新内容,HMCL 启动器现已支持“铜器时代”主题风格,我们诚邀你参与体验。是否要将主题色切换至“铜器时代”?\n切换后,你可以在“设置 → 外观 → 主题色”中切换回原来的主题色。 launcher.april_fools.copper.confirm=确认切换主题色?\n点击“确定”后 HMCL 将会变更主题色至随时间氧化的铜制品颜色;点击“取消”继续使用当前主题色。 launcher.april_fools.copper.state.0=%1s的 HMCL launcher.april_fools.copper.state.1=%1s的%2s HMCL diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties index 4ccf767487c..efb0ba0b959 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_Hant.properties @@ -743,7 +743,7 @@ launcher.agreement.decline=拒絕 launcher.agreement.hint=同意本軟體的使用者協議與免責宣告以使用本軟體。 launcher.april_fools.switch_lzh=HMCL 啟動器現已支援文言文,我們誠邀你參與體驗。是否要將介面語言切換至中文 (文言)?\n在切換語言後,你可以在「置設 → 貫用 → 文」中將語言切換回中文 (繁體)。 launcher.april_fools.switch_lzh.confirm=確認切換語言?\n點擊「確定」後 HMCL 將自動重新啟動並將介面語言切換至中文 (文言);點擊「取消」將繼續使用中文 (繁體) 進入 HMCL 主頁。 -launcher.april_fools.copper=HMCL 啟動器現已支援「銅器時代」主題風格,我們誠邀你參與體驗。是否要將主題色切換至「銅器時代」?\n切換後,你可以在「設定 → 外觀 → 主題色」中切換回原來的主題色。 +launcher.april_fools.copper=為跟進 Minecraft 在 1.21.9 中的更新內容,HMCL 啟動器現已支援「銅器時代」主題風格,我們誠邀你參與體驗。是否要將主題色切換至「銅器時代」?\n切換後,你可以在「設定 → 外觀 → 主題色」中切換回原來的主題色。 launcher.april_fools.copper.confirm=確認切換主題色?\n點選「確定」後 HMCL 將會變更主題色至隨時間氧化的銅製品顏色;點選「取消」繼續使用目前主題色。 launcher.april_fools.copper.action.axe=去除蠟或一層銅鏽 launcher.april_fools.copper.action.wax=上蠟