Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,21 @@ public MapProperty<String, Profile> getConfigurations() {
return configurations;
}

@SerializedName("saveCustomGameIcons")
private final ObjectProperty<EnumAskable> saveCustomGameIcons = new SimpleObjectProperty<>(EnumAskable.ASK);

public ObjectProperty<EnumAskable> 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<Config> {
@Override
protected Config createInstance() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.jackhuang.hmcl.setting;

public enum EnumAskable {
TRUE, FALSE, ASK
}
17 changes: 17 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<Boolean, Boolean> 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<String> prompt(String title, FutureCallback<String> onResult) {
return prompt(title, onResult, "");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
15 changes: 15 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -288,6 +289,20 @@ else if (locale.isSameLanguage(currentLocale))
settingsPane.getContent().add(disableAutoGameOptionsPane);
}

{
LineSelectButton<EnumAskable> 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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
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;
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;
Expand All @@ -33,12 +33,17 @@
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 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;
Expand Down Expand Up @@ -71,24 +76,67 @@ 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);
}
}
}

private void exploreIcon() {
FileChooser chooser = new FileChooser();
chooser.getExtensionFilters().add(FXUtils.getImageExtensionFilter());
Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage()));
if (selectedFile != null) {
try {
profile.getRepository().setVersionIconFile(versionId, selectedFile);
EnumAskable saveOption = ConfigHolder.config().getSaveCustomGameIcons();
if (saveOption == EnumAskable.ASK && !GAME_ICONS_DIR.equals(selectedFile.getParent())) {
Controllers.ask(
i18n("settings.icon.save_custom"),
i18n("message.question"),
(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);
}
}

Expand Down Expand Up @@ -117,6 +165,33 @@ 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, () -> {
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();
}
});
return container;
}

@Override
protected void onAccept() {
profile.getRepository().onVersionIconChanged.fireEvent(new Event(this));
Expand Down
4 changes: 4 additions & 0 deletions HMCL/src/main/resources/assets/lang/I18N.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions HMCL/src/main/resources/assets/lang/I18N_zh.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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=正在下載……
Expand Down Expand Up @@ -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=外觀
Expand Down Expand Up @@ -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=關閉動畫
Expand Down
4 changes: 4 additions & 0 deletions HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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=正在下载
Expand Down Expand Up @@ -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=外观
Expand Down Expand Up @@ -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=关闭动画
Expand Down
Loading