Compare commits

...
Author SHA1 Message Date
Zkitefly c16d6e8d5f 添加 NeoForge 的 BMCLAPI 代理支持 (#3605) 2025-02-15 16:38:30 +08:00
Glavo 6df73aeb68 修复语言设置中英语选项名称错误的问题 (#3612) 2025-02-15 12:19:09 +08:00
3gf8jv4dv 23d11eca81 fix(locale): adjust time formats for various languages (#3613)
- Change to a standard and commonly used format.
- Rename i18n key as it is used in more than one place.
2025-02-15 12:16:42 +08:00
Glavo 0e307f3b71 简化 AdvancedListBox (#3611) 2025-02-12 21:53:10 +08:00
14 changed files with 75 additions and 139 deletions

No files matched your search

@@ -24,6 +24,8 @@ import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.versions.VersionPage;
import java.util.function.Consumer;
@@ -54,11 +56,36 @@ public class AdvancedListBox extends ScrollPane {
return this;
}
public AdvancedListBox addNavigationDrawerItem(Consumer<AdvancedListItem> fn) {
private AdvancedListItem createNavigationDrawerItem(String title, SVG leftGraphic) {
AdvancedListItem item = new AdvancedListItem();
item.getStyleClass().add("navigation-drawer-item");
item.setActionButtonVisible(false);
fn.accept(item);
item.setTitle(title);
if (leftGraphic != null) {
item.setLeftGraphic(VersionPage.wrap(leftGraphic));
}
return item;
}
public AdvancedListBox addNavigationDrawerItem(String title, SVG leftGraphic, Runnable onAction) {
return addNavigationDrawerItem(title, leftGraphic, onAction, null);
}
public AdvancedListBox addNavigationDrawerItem(String title, SVG leftGraphic, Runnable onAction, Consumer<AdvancedListItem> initializer) {
AdvancedListItem item = createNavigationDrawerItem(title, leftGraphic);
if (onAction != null) {
item.setOnAction(e -> onAction.run());
}
if (initializer != null) {
initializer.accept(item);
}
return add(item);
}
public AdvancedListBox addNavigationDrawerTab(TabHeader tabHeader, TabControl.Tab<?> tab, String title, SVG leftGraphic) {
AdvancedListItem item = createNavigationDrawerItem(title, leftGraphic);
item.activeProperty().bind(tabHeader.getSelectionModel().selectedItemProperty().isEqualTo(tab));
item.setOnAction(e -> tabHeader.select(tab));
return add(item);
}
@@ -60,7 +60,6 @@ import java.util.concurrent.CancellationException;
import java.util.function.Supplier;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage {
@@ -100,43 +99,16 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE);
});
{
AdvancedListBox sideBar = new AdvancedListBox()
.startCategory(i18n("download.game").toUpperCase(Locale.ROOT))
.addNavigationDrawerItem(item -> {
item.setTitle(i18n("game"));
item.setLeftGraphic(wrap(SVG.GAMEPAD));
item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(newGameTab));
item.setOnAction(e -> tab.select(newGameTab));
})
.addNavigationDrawerItem(settingsItem -> {
settingsItem.setTitle(i18n("modpack"));
settingsItem.setLeftGraphic(wrap(SVG.PACK));
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(modpackTab));
settingsItem.setOnAction(e -> tab.select(modpackTab));
})
.startCategory(i18n("download.content").toUpperCase(Locale.ROOT))
.addNavigationDrawerItem(item -> {
item.setTitle(i18n("mods"));
item.setLeftGraphic(wrap(SVG.PUZZLE));
item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(modTab));
item.setOnAction(e -> tab.select(modTab));
})
.addNavigationDrawerItem(item -> {
item.setTitle(i18n("resourcepack"));
item.setLeftGraphic(wrap(SVG.TEXTURE_BOX));
item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(resourcePackTab));
item.setOnAction(e -> tab.select(resourcePackTab));
})
.addNavigationDrawerItem(item -> {
item.setTitle(i18n("world"));
item.setLeftGraphic(wrap(SVG.EARTH));
item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(worldTab));
item.setOnAction(e -> selectTabIfCurseForgeAvailable(worldTab));
});
FXUtils.setLimitWidth(sideBar, 200);
setLeft(sideBar);
}
AdvancedListBox sideBar = new AdvancedListBox()
.startCategory(i18n("download.game").toUpperCase(Locale.ROOT))
.addNavigationDrawerTab(tab, newGameTab, i18n("game"), SVG.GAMEPAD)
.addNavigationDrawerTab(tab, modpackTab, i18n("modpack"), SVG.PACK)
.startCategory(i18n("download.content").toUpperCase(Locale.ROOT))
.addNavigationDrawerTab(tab, modTab, i18n("mods"), SVG.PUZZLE)
.addNavigationDrawerTab(tab, resourcePackTab, i18n("resourcepack"), SVG.TEXTURE_BOX)
.addNavigationDrawerTab(tab, worldTab, i18n("world"), SVG.EARTH);
FXUtils.setLimitWidth(sideBar, 200);
setLeft(sideBar);
setCenter(transitionPane);
}
@@ -35,7 +35,6 @@ import org.jackhuang.hmcl.ui.versions.VersionSettingsPage;
import java.util.Locale;
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class LauncherSettingsPage extends DecoratorAnimatedPage implements DecoratorPage, PageAware {
@@ -69,61 +68,19 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE);
});
{
AdvancedListBox sideBar = new AdvancedListBox()
.addNavigationDrawerItem(settingsItem -> {
settingsItem.setTitle(i18n("settings.type.global.manage"));
settingsItem.setLeftGraphic(wrap(SVG.GAMEPAD));
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(gameTab));
settingsItem.setOnAction(e -> tab.select(gameTab));
})
.addNavigationDrawerItem(javaItem -> {
javaItem.setTitle(i18n("java.management"));
javaItem.setLeftGraphic(wrap(SVG.WRENCH_OUTLINE));
javaItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(javaManagementTab));
javaItem.setOnAction(e -> tab.select(javaManagementTab));
})
.startCategory(i18n("launcher").toUpperCase(Locale.ROOT))
.addNavigationDrawerItem(settingsItem -> {
settingsItem.setTitle(i18n("settings.launcher.general"));
settingsItem.setLeftGraphic(wrap(SVG.APPLICATION_OUTLINE));
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(settingsTab));
settingsItem.setOnAction(e -> tab.select(settingsTab));
})
.addNavigationDrawerItem(personalizationItem -> {
personalizationItem.setTitle(i18n("settings.launcher.appearance"));
personalizationItem.setLeftGraphic(wrap(SVG.STYLE_OUTLINE));
personalizationItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(personalizationTab));
personalizationItem.setOnAction(e -> tab.select(personalizationTab));
})
.addNavigationDrawerItem(downloadItem -> {
downloadItem.setTitle(i18n("download"));
downloadItem.setLeftGraphic(wrap(SVG.DOWNLOAD_OUTLINE));
downloadItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(downloadTab));
downloadItem.setOnAction(e -> tab.select(downloadTab));
})
.startCategory(i18n("help").toUpperCase(Locale.ROOT))
.addNavigationDrawerItem(helpItem -> {
helpItem.setTitle(i18n("help"));
helpItem.setLeftGraphic(wrap(SVG.HELP_CIRCLE_OUTLINE));
helpItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(helpTab));
helpItem.setOnAction(e -> tab.select(helpTab));
})
.addNavigationDrawerItem(feedbackItem -> {
feedbackItem.setTitle(i18n("feedback"));
feedbackItem.setLeftGraphic(wrap(SVG.MESSAGE_ALERT_OUTLINE));
feedbackItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(feedbackTab));
feedbackItem.setOnAction(e -> tab.select(feedbackTab));
})
.addNavigationDrawerItem(aboutItem -> {
aboutItem.setTitle(i18n("about"));
aboutItem.setLeftGraphic(wrap(SVG.INFORMATION_OUTLINE));
aboutItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(aboutTab));
aboutItem.setOnAction(e -> tab.select(aboutTab));
});
FXUtils.setLimitWidth(sideBar, 200);
setLeft(sideBar);
}
AdvancedListBox sideBar = new AdvancedListBox()
.addNavigationDrawerTab(tab, gameTab, i18n("settings.type.global.manage"), SVG.GAMEPAD)
.addNavigationDrawerTab(tab, javaManagementTab, i18n("java.management"), SVG.WRENCH_OUTLINE)
.startCategory(i18n("launcher").toUpperCase(Locale.ROOT))
.addNavigationDrawerTab(tab, settingsTab, i18n("settings.launcher.general"), SVG.APPLICATION_OUTLINE)
.addNavigationDrawerTab(tab, personalizationTab, i18n("settings.launcher.appearance"), SVG.STYLE_OUTLINE)
.addNavigationDrawerTab(tab, downloadTab, i18n("download"), SVG.DOWNLOAD_OUTLINE)
.startCategory(i18n("help").toUpperCase(Locale.ROOT))
.addNavigationDrawerTab(tab, helpTab, i18n("help"), SVG.HELP_CIRCLE_OUTLINE)
.addNavigationDrawerTab(tab, feedbackTab, i18n("feedback"), SVG.MESSAGE_ALERT_OUTLINE)
.addNavigationDrawerTab(tab, aboutTab, i18n("about"), SVG.INFORMATION_OUTLINE);
FXUtils.setLimitWidth(sideBar, 200);
setLeft(sideBar);
setCenter(transitionPane);
}
@@ -87,26 +87,10 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
}
AdvancedListBox bottomLeftCornerList = new AdvancedListBox()
.addNavigationDrawerItem(installNewGameItem -> {
installNewGameItem.setTitle(i18n("install.new_game"));
installNewGameItem.setLeftGraphic(VersionPage.wrap(SVG.PLUS_CIRCLE_OUTLINE));
installNewGameItem.setOnAction(e -> Versions.addNewGame());
})
.addNavigationDrawerItem(installModpackItem -> {
installModpackItem.setTitle(i18n("install.modpack"));
installModpackItem.setLeftGraphic(VersionPage.wrap(SVG.PACK));
installModpackItem.setOnAction(e -> Versions.importModpack());
})
.addNavigationDrawerItem(refreshItem -> {
refreshItem.setTitle(i18n("button.refresh"));
refreshItem.setLeftGraphic(VersionPage.wrap(SVG.REFRESH));
refreshItem.setOnAction(e -> gameList.refreshList());
})
.addNavigationDrawerItem(globalManageItem -> {
globalManageItem.setTitle(i18n("settings.type.global.manage"));
globalManageItem.setLeftGraphic(VersionPage.wrap(SVG.GEAR_OUTLINE));
globalManageItem.setOnAction(e -> modifyGlobalGameSettings());
});
.addNavigationDrawerItem(i18n("install.new_game"), SVG.PLUS_CIRCLE_OUTLINE, Versions::addNewGame)
.addNavigationDrawerItem(i18n("install.modpack"), SVG.PACK, Versions::importModpack)
.addNavigationDrawerItem(i18n("button.refresh"), SVG.REFRESH, gameList::refreshList)
.addNavigationDrawerItem(i18n("settings.type.global.manage"), SVG.GEAR_OUTLINE, this::modifyGlobalGameSettings);
FXUtils.setLimitHeight(bottomLeftCornerList, 40 * 4 + 12 * 2);
setLeft(pane, bottomLeftCornerList);
}
@@ -308,25 +308,14 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
);
AdvancedListBox toolbar = new AdvancedListBox()
.addNavigationDrawerItem(upgradeItem -> {
upgradeItem.setTitle(i18n("version.update"));
upgradeItem.setLeftGraphic(wrap(SVG.UPDATE));
.addNavigationDrawerItem(i18n("version.update"), SVG.UPDATE, control::updateGame, upgradeItem -> {
upgradeItem.visibleProperty().bind(control.currentVersionUpgradable);
upgradeItem.setOnAction(e -> control.updateGame());
})
.addNavigationDrawerItem(testGameItem -> {
testGameItem.setTitle(i18n("version.launch.test"));
testGameItem.setLeftGraphic(wrap(SVG.ROCKET_LAUNCH_OUTLINE));
testGameItem.setOnAction(e -> control.testGame());
})
.addNavigationDrawerItem(browseMenuItem -> {
browseMenuItem.setTitle(i18n("settings.game.exploration"));
browseMenuItem.setLeftGraphic(wrap(SVG.FOLDER_OUTLINE));
.addNavigationDrawerItem(i18n("version.launch.test"), SVG.ROCKET_LAUNCH_OUTLINE, control::testGame)
.addNavigationDrawerItem(i18n("settings.game.exploration"), SVG.FOLDER_OUTLINE, null, browseMenuItem -> {
browseMenuItem.setOnAction(e -> browsePopup.show(browseMenuItem, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, browseMenuItem.getWidth(), 0));
})
.addNavigationDrawerItem(managementItem -> {
managementItem.setTitle(i18n("settings.game.management"));
managementItem.setLeftGraphic(wrap(SVG.WRENCH_OUTLINE));
.addNavigationDrawerItem(i18n("settings.game.management"), SVG.WRENCH_OUTLINE, null, managementItem -> {
managementItem.setOnAction(e -> managementPopup.show(managementItem, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, managementItem.getWidth(), 0));
});
toolbar.getStyleClass().add("advanced-list-box-clear-padding");
@@ -77,7 +77,7 @@ public final class I18n {
public static String formatDateTime(Instant instant) {
DateTimeFormatter formatter = dateTimeFormatter;
if (formatter == null) {
formatter = dateTimeFormatter = DateTimeFormatter.ofPattern(getResourceBundle().getString("world.time")).withZone(ZoneId.systemDefault());
formatter = dateTimeFormatter = DateTimeFormatter.ofPattern(getResourceBundle().getString("datetime.format")).withZone(ZoneId.systemDefault());
}
return formatter.format(instant);
@@ -37,7 +37,7 @@ public final class Locales {
/**
* English
*/
public static final SupportedLocale EN = new SupportedLocale(Locale.ENGLISH);
public static final SupportedLocale EN = new SupportedLocale(Locale.ROOT);
/**
* Spanish
@@ -319,6 +319,8 @@ curse.sort.name=Name
curse.sort.popularity=Popularity
curse.sort.total_downloads=Total Downloads
datetime.format=MMM d, yyyy, h\:mm\:ss a
download=Download
download.hint=Install games and modpacks or download mods, resource packs, and worlds.
download.code.404=File "%s" not found on the remote server.
@@ -1088,7 +1090,6 @@ world.name=World Name
world.name.enter=Enter the world name
world.reveal=Reveal in Explorer
world.show_all=Show All
world.time=h\:mm\:ss a, EEE, MMM d yyyy
profile=Game Directories
profile.already_exists=This name already exists. Please use a different name.
@@ -319,6 +319,8 @@ curse.sort.name=Nombre
curse.sort.popularity=Popularidad
curse.sort.total_downloads=Descargas totales
datetime.format=d MMM yyyy, H\:mm\:ss
download=Descargar
download.hint=Instalar juegos y modpacks o descargar mods, paquetes de recursos y mundos.
download.code.404=Archivo no encontrado en el servidor remoto: %s
@@ -1035,7 +1037,6 @@ world.name=Nombre del mundo
world.name.enter=Introducir el nombre del mundo
world.reveal=Abrir en el Explorador
world.show_all=Mostrar todo
world.time=EEE, MMM d, yyyy HH\:mm\:ss
profile=Directorios del juego
profile.already_exists=Este nombre ya existe, por favor, utilice un nombre diferente.
@@ -283,6 +283,8 @@ curse.sort.name=名前
curse.sort.popularity=人気
curse.sort.total_downloads=合計ダウンロード数
datetime.format=yyyy/MM/dd H:mm:ss
download=ダウンロード
download.hint=ゲームや modpack をインストールするか、mod、リソース パック、マップをダウンロードします
download.code.404=リモートサーバーにファイルが見つかりません:%s
@@ -717,7 +719,6 @@ world.name=マップ名
world.name.enter=マップ名を入力してください
world.reveal=フォルダを表示
world.show_all=すべて表示
world.time=EEE、MMM d、yyyy HH:mm:ss
profile=ゲームディレクトリ
profile.already_exists=この名前はすでに存在します。別の名前を使用してください。
@@ -295,6 +295,8 @@ curse.sort.name=Название
curse.sort.popularity=Популярность
curse.sort.total_downloads=Скачиваний
datetime.format=d MMM yyyy г., HH\:mm\:ss
download=Скачать
download.hint=Установите игры и пакеты модов или загрузите моды, пакеты ресурсов и карты
download.code.404=Файл не найден на удалённом сервере: %s
@@ -776,7 +778,6 @@ world.name=Название мира
world.name.enter=Введите название мира
world.reveal=Показать в проводнике
world.show_all=Показать все
world.time=EEE, MMM d, yyyy HH\:mm\:ss
profile=Папки с играми
profile.already_exists=Имя уже существует, используйте другое имя.
@@ -319,6 +319,8 @@ curse.sort.name=名稱
curse.sort.popularity=熱度
curse.sort.total_downloads=下載量
datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss
download=下載
download.hint=安裝遊戲和模組包或下載模組、資源包和地圖
download.code.404=遠端伺服器沒有需要下載的檔案:%s
@@ -892,7 +894,6 @@ world.name=世界名稱
world.name.enter=輸入世界名稱
world.reveal=開啟目錄
world.show_all=全部顯示
world.time=yyyy 年 MM 月 dd 日 HH:mm:ss
profile=遊戲目錄
profile.already_exists=該名稱已存在
@@ -328,6 +328,8 @@ curse.sort.name=名称
curse.sort.popularity=热度
curse.sort.total_downloads=下载量
datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss
download=下载
download.hint=安装游戏和整合包或下载模组、资源包和地图
download.code.404=远程服务器不包含需要下载的文件: %s\n你可以点击右上角帮助按钮进行求助。
@@ -903,7 +905,6 @@ world.name=世界名称
world.name.enter=输入世界名称
world.reveal=打开文件夹
world.show_all=显示全部
world.time=yyyy 年 MM 月 dd 日 HH:mm:ss
profile=游戏文件夹
profile.already_exists=该名称已存在
@@ -72,6 +72,7 @@ public final class BMCLAPIDownloadProvider implements DownloadProvider {
pair("https://files.minecraftforge.net/maven", apiRoot + "/maven"),
pair("https://maven.minecraftforge.net", apiRoot + "/maven"),
pair("https://maven.neoforged.net/releases/net/neoforged/forge", apiRoot + "/maven/net/neoforged/forge"),
pair("https://maven.neoforged.net/releases/net/neoforged/neoforge", apiRoot + "/maven/net/neoforged/neoforge"),
pair("http://dl.liteloader.com/versions/versions.json", apiRoot + "/maven/com/mumfrey/liteloader/versions.json"),
pair("http://dl.liteloader.com/versions", apiRoot + "/maven"),
pair("https://meta.fabricmc.net", apiRoot + "/fabric-meta"),