From eb4d01a4cee6a83da4ff62884a12eadde06eb2f4 Mon Sep 17 00:00:00 2001 From: Calboot Date: Mon, 23 Mar 2026 21:04:07 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E6=B0=B8=E4=B9=85=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E6=B8=B8=E6=88=8F=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hmcl/ui/versions/VersionIconDialog.java | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index a48fc63a09..28f08b3094 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -18,9 +18,11 @@ package org.jackhuang.hmcl.ui.versions; import javafx.scene.Node; +import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.FlowPane; import javafx.stage.FileChooser; +import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.event.Event; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.VersionIconType; @@ -33,12 +35,18 @@ import org.jackhuang.hmcl.util.io.FileUtils; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public class VersionIconDialog extends DialogPane { + public static final Path GAME_ICONS_DIR = Metadata.HMCL_CURRENT_DIRECTORY.resolve("game_icons"); + private final Profile profile; private final String versionId; private final Runnable onFinish; @@ -71,6 +79,23 @@ public VersionIconDialog(Profile profile, String versionId, Runnable onFinish) { createIcon(VersionIconType.FURNACE), createIcon(VersionIconType.QUILT) ); + + if (Files.isDirectory(GAME_ICONS_DIR)) { + try (var stream = Files.list(GAME_ICONS_DIR)) { + pane.getChildren().addAll( + stream.filter(p -> Files.isRegularFile(p) && FXUtils.IMAGE_EXTENSIONS.contains(FileUtils.getExtension(p).toLowerCase(Locale.ROOT))) + .map(this::createIcon) + .filter(Objects::nonNull) + .toList() + ); + } catch (Exception e) { + LOG.warning("Failed to load custom game icons", e); + } + } + if (vs != null && vs.getVersionIcon() == VersionIconType.DEFAULT) { + Optional current = profile.getRepository().getVersionIconFile(versionId); + current.ifPresent(path -> pane.getChildren().add(1, createIcon(path))); + } } private void exploreIcon() { @@ -79,7 +104,14 @@ private void exploreIcon() { Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (selectedFile != null) { try { - profile.getRepository().setVersionIconFile(versionId, selectedFile); + Path dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName()); + int i = 1; + while (Files.exists(dest)) { + dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName() + " " + i); + i++; + } + FileUtils.copyFile(selectedFile, dest); + profile.getRepository().setVersionIconFile(versionId, dest); if (vs != null) { vs.setVersionIcon(VersionIconType.DEFAULT); @@ -117,6 +149,28 @@ private Node createIcon(VersionIconType type) { return container; } + private Node createIcon(Path path) { + ImageView imageView; + try { + imageView = new ImageView(new Image(Files.newInputStream(path), 72, 72, true, false)); + } catch (IOException e) { + LOG.warning("Failed to load custom game icon: " + path, e); + return null; + } + imageView.setMouseTransparent(true); + FXUtils.limitSize(imageView, 36, 36); + RipplerContainer container = new RipplerContainer(imageView); + FXUtils.setLimitWidth(container, 36); + FXUtils.setLimitHeight(container, 36); + FXUtils.onClicked(container, () -> { + if (vs != null) { + vs.setVersionIcon(VersionIconType.DEFAULT); + onAccept(); + } + }); + return container; + } + @Override protected void onAccept() { profile.getRepository().onVersionIconChanged.fireEvent(new Event(this)); From 8bf9ac11eacdbc81895c6709ad47ad3bbfa4c4c8 Mon Sep 17 00:00:00 2001 From: Calboot Date: Mon, 23 Mar 2026 21:06:20 +0800 Subject: [PATCH 02/11] update --- .../hmcl/ui/versions/VersionIconDialog.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index 28f08b3094..632782b2b7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -104,13 +104,18 @@ private void exploreIcon() { Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (selectedFile != null) { try { - Path dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName()); - int i = 1; - while (Files.exists(dest)) { - dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName() + " " + i); - i++; + Path dest; + if (selectedFile.getParent().equals(GAME_ICONS_DIR)) { + dest = selectedFile; + } else { + dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName()); + int i = 1; + while (Files.exists(dest)) { + dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName() + " " + i); + i++; + } + FileUtils.copyFile(selectedFile, dest); } - FileUtils.copyFile(selectedFile, dest); profile.getRepository().setVersionIconFile(versionId, dest); if (vs != null) { From 998a92288a1b29bc045f8f613f66dfa40f0afd47 Mon Sep 17 00:00:00 2001 From: Calboot Date: Mon, 23 Mar 2026 22:33:48 +0800 Subject: [PATCH 03/11] update --- .../org/jackhuang/hmcl/ui/versions/VersionIconDialog.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index 632782b2b7..611ecffece 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -39,7 +39,6 @@ import java.nio.file.Path; import java.util.Locale; import java.util.Objects; -import java.util.Optional; import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -92,10 +91,6 @@ public VersionIconDialog(Profile profile, String versionId, Runnable onFinish) { LOG.warning("Failed to load custom game icons", e); } } - if (vs != null && vs.getVersionIcon() == VersionIconType.DEFAULT) { - Optional current = profile.getRepository().getVersionIconFile(versionId); - current.ifPresent(path -> pane.getChildren().add(1, createIcon(path))); - } } private void exploreIcon() { From e4d30b088cbd70a3d0d865f9a266b24b5bcbbad1 Mon Sep 17 00:00:00 2001 From: Calboot Date: Mon, 23 Mar 2026 22:36:52 +0800 Subject: [PATCH 04/11] update --- .../org/jackhuang/hmcl/ui/versions/VersionIconDialog.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index 611ecffece..948d3f6303 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -105,8 +105,10 @@ private void exploreIcon() { } else { dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName()); int i = 1; + String name = FileUtils.getNameWithoutExtension(selectedFile); + String ext = FileUtils.getExtension(selectedFile).toLowerCase(Locale.ROOT); while (Files.exists(dest)) { - dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName() + " " + i); + dest = GAME_ICONS_DIR.resolve(name + " " + i + "." + ext); i++; } FileUtils.copyFile(selectedFile, dest); From 40aee225558acbbc9997ddffedf3c6e025cbbd0b Mon Sep 17 00:00:00 2001 From: Calboot Date: Mon, 23 Mar 2026 23:03:51 +0800 Subject: [PATCH 05/11] update --- .../org/jackhuang/hmcl/ui/versions/VersionIconDialog.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index 948d3f6303..65470a599b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -100,13 +100,13 @@ private void exploreIcon() { if (selectedFile != null) { try { Path dest; - if (selectedFile.getParent().equals(GAME_ICONS_DIR)) { + if (GAME_ICONS_DIR.equals(selectedFile.getParent())) { dest = selectedFile; } else { dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName()); int i = 1; String name = FileUtils.getNameWithoutExtension(selectedFile); - String ext = FileUtils.getExtension(selectedFile).toLowerCase(Locale.ROOT); + String ext = FileUtils.getExtension(selectedFile); while (Files.exists(dest)) { dest = GAME_ICONS_DIR.resolve(name + " " + i + "." + ext); i++; From 9e87a2d933c79f611c3819058aec1a94fb3baa8e Mon Sep 17 00:00:00 2001 From: Calboot Date: Tue, 24 Mar 2026 18:49:07 +0800 Subject: [PATCH 06/11] update --- .../org/jackhuang/hmcl/setting/Config.java | 15 +++++ .../jackhuang/hmcl/setting/EnumAskable.java | 5 ++ .../org/jackhuang/hmcl/ui/Controllers.java | 17 +++++ .../hmcl/ui/construct/MessageDialogPane.java | 5 ++ .../jackhuang/hmcl/ui/main/SettingsPage.java | 15 +++++ .../hmcl/ui/versions/VersionIconDialog.java | 67 ++++++++++++------- .../resources/assets/lang/I18N.properties | 4 ++ .../resources/assets/lang/I18N_zh.properties | 4 ++ .../assets/lang/I18N_zh_CN.properties | 4 ++ 9 files changed, 112 insertions(+), 24 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumAskable.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java index 0320e13b18..b4307398d3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java @@ -777,6 +777,21 @@ public MapProperty getConfigurations() { return configurations; } + @SerializedName("saveCustomGameIcons") + private final ObjectProperty saveCustomGameIcons = new SimpleObjectProperty<>(EnumAskable.ASK); + + public ObjectProperty saveCustomGameIconsProperty() { + return saveCustomGameIcons; + } + + public EnumAskable getSaveCustomGameIcons() { + return saveCustomGameIcons.get(); + } + + public void setSaveCustomGameIcons(EnumAskable saveCustomGameIcons) { + this.saveCustomGameIcons.set(saveCustomGameIcons); + } + public static final class Adapter extends ObservableSetting.Adapter { @Override protected Config createInstance() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumAskable.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumAskable.java new file mode 100644 index 0000000000..18140ec3fe --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumAskable.java @@ -0,0 +1,5 @@ +package org.jackhuang.hmcl.setting; + +public enum EnumAskable { + TRUE, FALSE, ASK +} 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 c94a86accc..5e72c410a7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXDialogLayout; import com.jfoenix.validation.base.ValidatorBase; import javafx.animation.KeyFrame; @@ -77,6 +78,7 @@ import java.time.LocalDate; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; import static org.jackhuang.hmcl.setting.ConfigHolder.config; import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig; @@ -558,6 +560,21 @@ public static void confirmWithCountdown(String text, String title, int seconds, timeline.play(); } + /// @param consumer Consumer for the result, with the first boolean for yes or no and the second for whether no more asking is needed + /// @see EnumAskable + public static void ask(String text, String title, BiConsumer consumer) { + var check = new JFXCheckBox(i18n("message.do_not_ask_again")); + var dialog = new MessageDialogPane.Builder( + text, + title, + MessageDialogPane.MessageType.QUESTION + ) + .addActionNoClosing(check) + .yesOrNo(() -> consumer.accept(true, check.isSelected()), () -> consumer.accept(false, check.isSelected())) + .build(); + dialog(dialog); + } + public static CompletableFuture prompt(String title, FutureCallback onResult) { return prompt(title, onResult, ""); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java index 2bed442a5f..7e5b7c7f0f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java @@ -169,6 +169,11 @@ public Builder addAction(String text, @Nullable Runnable action) { return this; } + public Builder addActionNoClosing(Node actionNode) { + dialog.actions.getChildren().add(actionNode); + return this; + } + public Builder ok(@Nullable Runnable ok) { JFXButton btnOk = new JFXButton(i18n("button.ok")); btnOk.getStyleClass().add("dialog-accept"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 40012b4426..499de253ec 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -34,6 +34,7 @@ import javafx.scene.layout.*; import javafx.scene.text.TextAlignment; import org.jackhuang.hmcl.Metadata; +import org.jackhuang.hmcl.setting.EnumAskable; import org.jackhuang.hmcl.setting.EnumCommonDirectory; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.ui.Controllers; @@ -288,6 +289,20 @@ else if (locale.isSameLanguage(currentLocale)) settingsPane.getContent().add(disableAutoGameOptionsPane); } + { + LineSelectButton saveCustomGameIconsPane = new LineSelectButton<>(); + saveCustomGameIconsPane.setTitle(i18n("settings.launcher.save_custom_game_icons")); + saveCustomGameIconsPane.setConverter(a -> switch (a) { + case ASK -> i18n("message.ask"); + case TRUE -> i18n("button.yes"); + case FALSE -> i18n("button.no"); + }); + saveCustomGameIconsPane.setItems(EnumAskable.values()); + saveCustomGameIconsPane.valueProperty().bindBidirectional(config().saveCustomGameIconsProperty()); + + settingsPane.getContent().add(saveCustomGameIconsPane); + } + { BorderPane debugPane = new BorderPane(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index 65470a599b..a1b2735bee 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -24,9 +24,7 @@ import javafx.stage.FileChooser; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.event.Event; -import org.jackhuang.hmcl.setting.Profile; -import org.jackhuang.hmcl.setting.VersionIconType; -import org.jackhuang.hmcl.setting.VersionSetting; +import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; @@ -98,31 +96,47 @@ private void exploreIcon() { chooser.getExtensionFilters().add(FXUtils.getImageExtensionFilter()); Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (selectedFile != null) { - try { - Path dest; - if (GAME_ICONS_DIR.equals(selectedFile.getParent())) { - dest = selectedFile; - } else { - dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName()); - int i = 1; - String name = FileUtils.getNameWithoutExtension(selectedFile); - String ext = FileUtils.getExtension(selectedFile); - while (Files.exists(dest)) { - dest = GAME_ICONS_DIR.resolve(name + " " + i + "." + ext); - i++; - } - FileUtils.copyFile(selectedFile, dest); - } - profile.getRepository().setVersionIconFile(versionId, dest); + EnumAskable saveOption = ConfigHolder.config().getSaveCustomGameIcons(); + if (saveOption == EnumAskable.ASK && !GAME_ICONS_DIR.equals(selectedFile.getParent())) { + Controllers.ask( + i18n("settings.icon.save_custom"), + i18n("settings.launcher.save_custom_game_icons"), + (res, doNotAsk) -> { + if (doNotAsk) ConfigHolder.config().setSaveCustomGameIcons(res ? EnumAskable.TRUE : EnumAskable.FALSE); + setCustomIcon(selectedFile, res); + } + ); + } else { + setCustomIcon(selectedFile, saveOption == EnumAskable.TRUE); + } + } + } - if (vs != null) { - vs.setVersionIcon(VersionIconType.DEFAULT); + private void setCustomIcon(Path selectedFile, boolean save) { + try { + Path dest; + if (GAME_ICONS_DIR.equals(selectedFile.getParent()) || !save) { + dest = selectedFile; + } else { + dest = GAME_ICONS_DIR.resolve(selectedFile.getFileName()); + int i = 1; + String name = FileUtils.getNameWithoutExtension(selectedFile); + String ext = FileUtils.getExtension(selectedFile); + while (Files.exists(dest)) { + dest = GAME_ICONS_DIR.resolve(name + " " + i + "." + ext); + i++; } + FileUtils.copyFile(selectedFile, dest); + } + profile.getRepository().setVersionIconFile(versionId, dest); - onAccept(); - } catch (IOException | IllegalArgumentException e) { - LOG.error("Failed to set icon file: " + selectedFile, e); + if (vs != null) { + vs.setVersionIcon(VersionIconType.DEFAULT); } + + onAccept(); + } catch (IOException | IllegalArgumentException e) { + LOG.error("Failed to set icon file: " + selectedFile, e); } } @@ -165,6 +179,11 @@ private Node createIcon(Path path) { FXUtils.setLimitWidth(container, 36); FXUtils.setLimitHeight(container, 36); FXUtils.onClicked(container, () -> { + try { + profile.getRepository().setVersionIconFile(versionId, path); + } catch (IOException e) { + LOG.error("Failed to set icon file: " + path, e); + } if (vs != null) { vs.setVersionIcon(VersionIconType.DEFAULT); onAccept(); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 605e328b8c..2f6c317fa3 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -881,9 +881,11 @@ logwindow.export_dump.no_dependency=Your Java does not contain the dependencies main_page=Home +message.ask=Ask message.cancelled=Operation Canceled message.confirm=Confirm message.copied=Copied to clipboard +message.do_not_ask_again=Do not ask again message.default=Default message.doing=Please wait message.downloading=Downloading @@ -1414,6 +1416,7 @@ settings.game.working_directory.hint=Enable the "Isolated" option in "Working Di It is recommended to enable this option to avoid mod conflicts, but you will need to move your worlds manually. settings.icon=Icon +settings.icon.save_custom=Save custom game icon? settings.launcher=Launcher Settings settings.launcher.appearance=Appearance @@ -1455,6 +1458,7 @@ settings.launcher.proxy.password=Password settings.launcher.proxy.port=Port settings.launcher.proxy.socks=SOCKS settings.launcher.proxy.username=Username +settings.launcher.save_custom_game_icons=Save Custom Game Icons settings.launcher.theme=Theme Color settings.launcher.title_transparent=Transparent Titlebar settings.launcher.turn_off_animations=Disable Animation diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 0f7d9c6633..ef56a623c3 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -685,9 +685,11 @@ logwindow.export_dump.no_dependency=你的 Java 不包含用於建立遊戲執 main_page=首頁 +message.ask=詢問 message.cancelled=操作被取消 message.confirm=提示 message.copied=已複製到剪貼簿 +message.do_not_ask_again=不再詢問 message.default=預設 message.doing=請耐心等待 message.downloading=正在下載…… @@ -1199,6 +1201,7 @@ settings.game.working_directory.choose=選取執行目錄 settings.game.working_directory.hint=在「執行路徑」選項中選取「各實例獨立」使目前實例獨立存放設定、世界、模組等資料。使用模組時建議開啟此選項以避免不同實例模組衝突。修改此選項後需自行移動世界等檔案。 settings.icon=遊戲圖示 +settings.icon.save_custom=保存自定義遊戲圖示? settings.launcher=啟動器設定 settings.launcher.appearance=外觀 @@ -1240,6 +1243,7 @@ settings.launcher.proxy.password=密碼 settings.launcher.proxy.port=連線埠 settings.launcher.proxy.socks=SOCKS settings.launcher.proxy.username=帳戶 +settings.launcher.save_custom_game_icons=保存自定義遊戲圖標 settings.launcher.theme=主題色 settings.launcher.title_transparent=標題欄透明 settings.launcher.turn_off_animations=關閉動畫 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 9e408399c3..ce3b94d9bd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -690,9 +690,11 @@ logwindow.export_dump.no_dependency=你的 Java 不包含用于创建游戏运 main_page=主页 +message.ask=询问 message.cancelled=操作被取消 message.confirm=提示 message.copied=已复制到剪贴板 +message.do_not_ask_again=不再询问 message.default=默认 message.doing=请耐心等待 message.downloading=正在下载 @@ -1204,6 +1206,7 @@ settings.game.working_directory.choose=选择运行文件夹 settings.game.working_directory.hint=在“版本隔离”中选择“各实例独立”使当前实例独立存放设置、世界、模组等数据。使用模组时建议启用此选项以避免不同实例模组冲突。修改此选项后需自行移动世界等文件。 settings.icon=游戏图标 +settings.icon.save_custom=保存自定义游戏图标? settings.launcher=启动器设置 settings.launcher.appearance=外观 @@ -1245,6 +1248,7 @@ settings.launcher.proxy.password=密码 settings.launcher.proxy.port=端口 settings.launcher.proxy.socks=SOCKS settings.launcher.proxy.username=账户 +settings.launcher.save_custom_game_icons=保存自定义游戏图标 settings.launcher.theme=主题色 settings.launcher.title_transparent=标题栏透明 settings.launcher.turn_off_animations=关闭动画 From 6cfe8a116514fad23db958402cbb35405f6ea5cc Mon Sep 17 00:00:00 2001 From: Calboot Date: Tue, 24 Mar 2026 18:52:21 +0800 Subject: [PATCH 07/11] update --- .../java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index a1b2735bee..f5bc32ccd5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -100,7 +100,7 @@ private void exploreIcon() { if (saveOption == EnumAskable.ASK && !GAME_ICONS_DIR.equals(selectedFile.getParent())) { Controllers.ask( i18n("settings.icon.save_custom"), - i18n("settings.launcher.save_custom_game_icons"), + i18n("message.question"), (res, doNotAsk) -> { if (doNotAsk) ConfigHolder.config().setSaveCustomGameIcons(res ? EnumAskable.TRUE : EnumAskable.FALSE); setCustomIcon(selectedFile, res); diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index ef56a623c3..91b094d177 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1201,7 +1201,7 @@ settings.game.working_directory.choose=選取執行目錄 settings.game.working_directory.hint=在「執行路徑」選項中選取「各實例獨立」使目前實例獨立存放設定、世界、模組等資料。使用模組時建議開啟此選項以避免不同實例模組衝突。修改此選項後需自行移動世界等檔案。 settings.icon=遊戲圖示 -settings.icon.save_custom=保存自定義遊戲圖示? +settings.icon.save_custom=是否要保存自定義遊戲圖示? settings.launcher=啟動器設定 settings.launcher.appearance=外觀 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index ce3b94d9bd..fb4da01863 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1206,7 +1206,7 @@ settings.game.working_directory.choose=选择运行文件夹 settings.game.working_directory.hint=在“版本隔离”中选择“各实例独立”使当前实例独立存放设置、世界、模组等数据。使用模组时建议启用此选项以避免不同实例模组冲突。修改此选项后需自行移动世界等文件。 settings.icon=游戏图标 -settings.icon.save_custom=保存自定义游戏图标? +settings.icon.save_custom=是否要保存自定义游戏图标? settings.launcher=启动器设置 settings.launcher.appearance=外观 From d92e9ef22324e0918c5b3a3f975146d33b0b37d7 Mon Sep 17 00:00:00 2001 From: Calboot Date: Wed, 25 Mar 2026 19:55:29 +0800 Subject: [PATCH 08/11] update --- .../org/jackhuang/hmcl/setting/EnumAskable.java | 17 +++++++++++++++++ .../jackhuang/hmcl/ui/main/SettingsPage.java | 8 ++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumAskable.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumAskable.java index 18140ec3fe..a2c1a0dc38 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumAskable.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumAskable.java @@ -1,3 +1,20 @@ +/* + * 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.setting; public enum EnumAskable { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 499de253ec..2009fdf4dd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -293,10 +293,10 @@ else if (locale.isSameLanguage(currentLocale)) LineSelectButton saveCustomGameIconsPane = new LineSelectButton<>(); saveCustomGameIconsPane.setTitle(i18n("settings.launcher.save_custom_game_icons")); saveCustomGameIconsPane.setConverter(a -> switch (a) { - case ASK -> i18n("message.ask"); - case TRUE -> i18n("button.yes"); - case FALSE -> i18n("button.no"); - }); + case ASK -> i18n("message.ask"); + case TRUE -> i18n("button.yes"); + case FALSE -> i18n("button.no"); + }); saveCustomGameIconsPane.setItems(EnumAskable.values()); saveCustomGameIconsPane.valueProperty().bindBidirectional(config().saveCustomGameIconsProperty()); From 11a8db311fc8f3abf0896350fb8c5072f2cd0d65 Mon Sep 17 00:00:00 2001 From: Calboot Date: Fri, 27 Mar 2026 21:37:47 +0800 Subject: [PATCH 09/11] update --- .../org/jackhuang/hmcl/ui/versions/VersionIconDialog.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index f5bc32ccd5..6069a37bdf 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.ui.versions; import javafx.scene.Node; -import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.FlowPane; import javafx.stage.FileChooser; @@ -168,8 +167,8 @@ private Node createIcon(VersionIconType type) { private Node createIcon(Path path) { ImageView imageView; try { - imageView = new ImageView(new Image(Files.newInputStream(path), 72, 72, true, false)); - } catch (IOException e) { + imageView = new ImageView(FXUtils.loadImage(path, 72, 72, true, false)); + } catch (Exception e) { LOG.warning("Failed to load custom game icon: " + path, e); return null; } From d4137e0da2dac358257f3deb21c38f3911eda240 Mon Sep 17 00:00:00 2001 From: Calboot Date: Sat, 28 Mar 2026 11:08:08 +0800 Subject: [PATCH 10/11] update --- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 2f6c317fa3..2d1b37facf 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1416,7 +1416,7 @@ settings.game.working_directory.hint=Enable the "Isolated" option in "Working Di It is recommended to enable this option to avoid mod conflicts, but you will need to move your worlds manually. settings.icon=Icon -settings.icon.save_custom=Save custom game icon? +settings.icon.save_custom=Save custom icon? settings.launcher=Launcher Settings settings.launcher.appearance=Appearance From e9acf6211560889cb74734ca5ea57ed3b7fb185b Mon Sep 17 00:00:00 2001 From: Calboot Date: Sun, 29 Mar 2026 14:18:47 +0800 Subject: [PATCH 11/11] update --- HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java | 2 +- HMCL/src/main/resources/assets/lang/I18N.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_zh.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) 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 5e72c410a7..ca9ea4cb15 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -563,7 +563,7 @@ public static void confirmWithCountdown(String text, String title, int seconds, /// @param consumer Consumer for the result, with the first boolean for yes or no and the second for whether no more asking is needed /// @see EnumAskable public static void ask(String text, String title, BiConsumer consumer) { - var check = new JFXCheckBox(i18n("message.do_not_ask_again")); + var check = new JFXCheckBox(i18n("button.do_not_show_again")); var dialog = new MessageDialogPane.Builder( text, title, diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 7c72ec294e..098b98da84 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -888,7 +888,6 @@ message.ask=Ask message.cancelled=Operation Canceled message.confirm=Confirm message.copied=Copied to clipboard -message.do_not_ask_again=Do not ask again message.default=Default message.doing=Please wait message.downloading=Downloading diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index c3752bb9cd..68676a73ee 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -692,7 +692,6 @@ message.ask=詢問 message.cancelled=操作被取消 message.confirm=提示 message.copied=已複製到剪貼簿 -message.do_not_ask_again=不再詢問 message.default=預設 message.doing=請耐心等待 message.downloading=正在下載…… diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 26ca36a1d6..e5ce57745d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -697,7 +697,6 @@ message.ask=询问 message.cancelled=操作被取消 message.confirm=提示 message.copied=已复制到剪贴板 -message.do_not_ask_again=不再询问 message.default=默认 message.doing=请耐心等待 message.downloading=正在下载