From bb15d72032617848c26a674f9036bea0a113c0c8 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 4 Sep 2021 02:06:56 +0800 Subject: [PATCH 01/21] fix: add some missing tranditional Chinese translations. --- .../hmcl/ui/account/AccountListPage.java | 9 +- .../hmcl/ui/main/LauncherSettingsPage.java | 2 +- .../resources/assets/lang/I18N_zh.properties | 142 +++++++++++++++++- .../assets/lang/I18N_zh_CN.properties | 4 +- 4 files changed, 150 insertions(+), 7 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java index 3885df38e..e95a41786 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java @@ -28,9 +28,13 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.setting.Accounts; -import org.jackhuang.hmcl.ui.*; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.ListPageBase; +import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.AdvancedListBox; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; +import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.javafx.MappedObservableList; import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap; @@ -91,6 +95,9 @@ public class AccountListPage extends ListPageBase implements De }) .addNavigationDrawerItem(settingsItem -> { settingsItem.setTitle(i18n("account.methods.authlib_injector")); + if (I18n.hasKey("account.methods.authlib_injector.subtitle")) { + settingsItem.setSubtitle(i18n("account.methods.authlib_injector.subtitle")); + } settingsItem.setLeftGraphic(wrap(SVG.gear(null, 20, 20))); settingsItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_AUTHLIB_INJECTOR))); }) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java index 747c2924e..91cd3c35d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java @@ -67,7 +67,7 @@ public class LauncherSettingsPage extends BorderPane implements DecoratorPage { { AdvancedListBox sideBar = new AdvancedListBox() .addNavigationDrawerItem(settingsItem -> { - settingsItem.setTitle(i18n("settings.game.current")); + settingsItem.setTitle(i18n("settings.type.global.manag")); settingsItem.setLeftGraphic(wrap(SVG.gamepad(null, 20, 20))); settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(gameTab)); settingsItem.setOnAction(e -> tab.getSelectionModel().select(gameTab)); diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 6f8db0075..2c869178f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -65,7 +65,7 @@ account.missing.add=按一下此處加入帳戶 account.password=密碼 account.skin.file=皮膚圖片檔案 account.skin.upload=上傳皮膚 -account.skin.upload=皮膚上傳失敗 +account.skin.upload.failed=皮膚上傳失敗 account.skin.invalid_skin=無法識別的皮膚文件 account.username=使用者名稱 @@ -98,6 +98,112 @@ color.custom=自訂顏色 crash.NoClassDefFound=請確認 Hello Minecraft! Launcher 本體是否完整,或更新您的 Java。 crash.user_fault=您的系統或 Java 環境可能安裝不當導致本軟體當機,請檢查您的 Java 環境或您的電腦! 可以嘗試重新安裝 Java。 +curse.category.0=全部 + +# https://addons-ecs.forgesvc.net/api/v2/category/section/4471 +curse.category.4474=科幻 +curse.category.4481=輕量整合包 +curse.category.4483=戰鬥 PVP +curse.category.4477=小遊戲 +curse.category.4478=任務 +curse.category.4484=多人 +curse.category.4476=探索 +curse.category.4736=空島 +curse.category.4475=冒險 RPG +curse.category.4487=FTB 整合包 +curse.category.4480=有特定地圖 +curse.category.4479=高難度 +curse.category.4482=大型整合包 +curse.category.4472=科技 +curse.category.4473=魔法 + +# https://addons-ecs.forgesvc.net/api/v2/category/section/6 +curse.category.423=訊息展示 +curse.category.426=模組擴展 +curse.category.434=裝備武器 +curse.category.409=自然生成 +curse.category.4485=血魔法 +curse.category.420=儲存 +curse.category.429=工業 (Industrialcraft) +curse.category.419=魔法 +curse.category.412=科技 +curse.category.4557=紅石 +curse.category.428=匠魂 +curse.category.414=交通運輸 +curse.category.4486=幸運方塊 (Lucky Blocks) +curse.category.432=建築 (Buildcraft) +curse.category.418=基因 +curse.category.4671=Twitch +curse.category.408=礦物資源 +curse.category.4773=CraftTweaker +curse.category.430=神秘 (Thaumcraft) +curse.category.422=冒險 RPG +curse.category.413=機器處理 +curse.category.417=能源 +curse.category.415=物流運輸 +curse.category.433=林業 (Forestry) +curse.category.425=其他 +curse.category.4545=應用能源 2 (Applied Energistics 2) +curse.category.416=農業 +curse.category.421=支持庫 +curse.category.4780=Fabric +curse.category.424=裝飾 +curse.category.406=世界生成 +curse.category.435=伺服器 +curse.category.411=生物 +curse.category.407=生物群系 +curse.category.427=熱力膨脹 (Thermal Expansion) +curse.category.410=維度 +curse.category.436=食物 +curse.category.4558=紅石 +curse.category.4843=自動化 +curse.category.4906=MCreator + +# https://addons-ecs.forgesvc.net/api/v2/category/section/6 +curse.category.399=蒸汽朋克 +curse.category.396=128x +curse.category.398=512x 及更高 +curse.category.397=256x +curse.category.405=其他 +curse.category.395=64x +curse.category.400=模擬 +curse.category.393=16x +curse.category.403=傳統 +curse.category.394=32x +curse.category.404=動態效果 +curse.category.4465=模組支持 +curse.category.402=中世紀風格 +curse.category.401=現代風格 + +# https://addons-ecs.forgesvc.net/api/v2/category/section/17 +curse.category.4464=模組 +curse.category.250=遊戲挑戰 +curse.category.249=創造模式 +curse.category.251=跑酷 +curse.category.253=生存模式 +curse.category.248=冒險模式 +curse.category.252=解謎類 + +# https://addons-ecs.forgesvc.net/api/v2/category/section/4546 +curse.category.4551=硬核任務模式 +curse.category.4548=幸運方塊 (Lucky Blocks) +curse.category.4556=任務進度 +curse.category.4752=小物件 +curse.category.4553=CraftTweaker +curse.category.4554=合成表 +curse.category.4549=指引書 +curse.category.4547=配置 +curse.category.4550=任務 +curse.category.4555=世界生成 +curse.category.4552=腳本 + +curse.sort.author=作者 +curse.sort.date_created=創建日期 +curse.sort.last_updated=最近更新 +curse.sort.name=名稱 +curse.sort.popularity=熱度 +curse.sort.total_downloads=下載量 + download=下載 download.code.404=遠端伺服器沒有需要下載的檔案: %s download.failed=下載失敗: %1$s,錯誤碼:%2$d @@ -129,7 +235,8 @@ folder.resourcepacks=資源包資料夾 folder.saves=遊戲存檔資料夾 folder.screenshots=截圖資料夾 -help=Hello Minecraft! Launcher 說明文件 +help=說明 +help.doc=Hello Minecraft! Launcher 說明文件 help.detail=可查閱資料包、整合包製作指南等內容。 input.email=[使用者名稱] 必須是電子信箱格式 @@ -222,6 +329,7 @@ logwindow.show_lines=顯示行數 logwindow.terminate_game=結束遊戲處理程序 logwindow.title=記錄 logwindow.autoscroll=自動滾動 +logwindow.export_game_crash_logs=導出遊戲崩潰訊息 main_page=首頁 @@ -350,6 +458,17 @@ profile.title=遊戲目錄 profile.selected=已選取 profile.use_relative_path=如可行,則在遊戲目錄使用相對路徑 +repositories.custom=自訂 Maven 倉庫(%s) +repositories.maven_central=Maven Central(通用) +repositories.aliyun_mirror=阿里雲 Maven 鏡像源(中國大陸) +repositories.chooser=JavaFX 缺失。是否需要從網路下載並載入 OpenJFX?\n請選擇下載源: +repositories.chooser.title=是否下載 JavaFX? + +resourcepack=資源包 + +search=搜索 +search.sort=排序 + selector.choose=選擇 selector.choose_file=選擇檔案 selector.custom=自訂 @@ -412,8 +531,16 @@ settings.icon=遊戲圖示 settings.launcher=啟動器設定 settings.launcher.common_path.tooltip=啟動器將所有遊戲資源及相依元件庫檔案放於此集中管理,如果遊戲資料夾內有現成的將不會使用公共庫檔案 +settings.launcher.debug=除錯 +settings.launcher.download=下載 +settings.launcher.download.threads=並發數 +settings.launcher.download.threads.auto=自動選擇並發數 +settings.launcher.download.threads.hint=並發數過大可能導致系統卡頓。你的下載速度會受到寬頻運營商、伺服器等方面的影響,調大下載並發數不一定能大幅提升總下載速度。 settings.launcher.download_source=下載來源 +settings.launcher.download_source.auto=自動選擇下載來源 settings.launcher.enable_game_list=在首頁內顯示遊戲列表 +settings.launcher.font=字體 +settings.launcher.general=通用 settings.launcher.language=語言 settings.launcher.launcher_log.export=匯出啟動器日誌 settings.launcher.launcher_log.export.failed=無法匯出日誌 @@ -430,8 +557,16 @@ settings.launcher.proxy.port=連線埠 settings.launcher.proxy.socks=Socks settings.launcher.proxy.username=帳戶 settings.launcher.theme=主題 +settings.launcher.version_list_source=版本列表來源 -settings.max_memory=最大記憶體(MB) +settings.memory=遊戲記憶體 +settings.memory.allocate.auto=最低分配 %1$.1f GB / 實際分配 %2$.1f GB +settings.memory.allocate.auto.exceeded=最低分配 %1$.1f GB / 實際分配 %2$.1f GB (%3$.1f GB 可用) +settings.memory.allocate.manual=遊戲分配 %1$.1f GB +settings.memory.allocate.manual.exceeded=遊戲分配 %1$.1f GB (%3$.1f GB 可用) +settings.memory.auto_allocate=自動分配 +settings.memory.lower_bound=最低分配 +settings.memory.used_per_total=已使用 %1$.1f GB / 總記憶體 %2$.1f GB settings.physical_memory=實體記憶體大小 settings.show_log=查看記錄 settings.tabs.installers=自動安裝 @@ -441,6 +576,7 @@ settings.type.global.manage=全域遊戲設定 settings.type.global.edit=編輯全域遊戲設定 settings.type.special.enable=啟用遊戲特別設定(不影響其他遊戲版本) +sponsor=贊助 sponsor.bmclapi=大中華區下載源由 BMCLAPI 和我的世界中文論壇 (MCBBS) 提供高速下載服務 sponsor.hmcl=Hello Minecraft! Launcher 是一個免費、開源的 Minecraft 啟動器,允許玩家方便快捷地安裝、管理、執行遊戲。您的贊助將幫助 Hello Minecraft! Launcher 獲得更好的發展、支援穩定高速的遊戲安裝與文件下載服務。點選此處查閱更多詳細訊息。 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 c812d59e9..3db04b099 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -68,7 +68,8 @@ account.injector.server_url=服务器地址 account.injector.server_name=服务器名称 account.manage=账户列表 account.methods=登录方式 -account.methods.authlib_injector=外置登录 (authlib-injector) +account.methods.authlib_injector=外置登录 +account.methods.authlib_injector.subtitle=authlib-injector account.methods.microsoft=微软账户 account.methods.microsoft.close_page=已完成微软账号授权,接下来启动器还需要完成剩余登录步骤。你已经可以关闭本页面了。 account.methods.microsoft.error.add_family=由于你未满 18 岁,你的账号必须被加入到家庭中才能登录游戏。 @@ -505,7 +506,6 @@ repositories.aliyun_mirror=阿里云 Maven 镜像源(中国大陆) repositories.chooser=JavaFX 缺失。是否需要从网络下载并加载 OpenJFX?\n请选择下载源: repositories.chooser.title=是否下载 JavaFX? - resourcepack=资源包 search=搜索 From 72ca0f20a129ad87747875f35a4cc6c2bfd7f7f7 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 4 Sep 2021 02:07:58 +0800 Subject: [PATCH 02/21] feat: MicrosoftAccount: find skin by uuid instead of fetching profile. --- .../hmcl/auth/microsoft/MicrosoftAccount.java | 17 ++++-- .../hmcl/auth/microsoft/MicrosoftService.java | 53 +++++++++++++++---- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java index aae5c2062..227b032bc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java @@ -21,14 +21,17 @@ import javafx.beans.binding.ObjectBinding; import org.jackhuang.hmcl.auth.*; import org.jackhuang.hmcl.auth.yggdrasil.Texture; import org.jackhuang.hmcl.auth.yggdrasil.TextureType; +import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService; import org.jackhuang.hmcl.util.javafx.BindingMapping; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import java.util.logging.Level; import static java.util.Objects.requireNonNull; +import static org.jackhuang.hmcl.util.Logging.LOG; public class MicrosoftAccount extends Account { @@ -77,7 +80,7 @@ public class MicrosoftAccount extends Account { @Override public AuthInfo logIn() throws AuthenticationException { if (!authenticated) { - if (service.validate(session.getTokenType(), session.getAccessToken())) { + if (service.validate(session.getNotAfter(), session.getTokenType(), session.getAccessToken())) { authenticated = true; } else { MicrosoftSession acquiredSession = service.refresh(session); @@ -116,9 +119,15 @@ public class MicrosoftAccount extends Account { @Override public ObjectBinding>> getTextures() { - return BindingMapping.of(service.getProfileRepository().binding(session.getAuthorization())) - .map(profile -> profile.flatMap(MicrosoftService::getTextures)); - + return BindingMapping.of(service.getProfileRepository().binding(getUUID())) + .map(profile -> profile.flatMap(it -> { + try { + return YggdrasilService.getTextures(it); + } catch (ServerResponseMalformedException e) { + LOG.log(Level.WARNING, "Failed to parse texture payload", e); + return Optional.empty(); + } + })); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java index a322446a8..1af3d4a40 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java @@ -17,15 +17,16 @@ */ package org.jackhuang.hmcl.auth.microsoft; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.auth.*; +import org.jackhuang.hmcl.auth.yggdrasil.CompleteGameProfile; import org.jackhuang.hmcl.auth.yggdrasil.RemoteAuthenticationException; import org.jackhuang.hmcl.auth.yggdrasil.Texture; import org.jackhuang.hmcl.auth.yggdrasil.TextureType; -import org.jackhuang.hmcl.util.gson.JsonUtils; -import org.jackhuang.hmcl.util.gson.TolerableValidationException; -import org.jackhuang.hmcl.util.gson.Validation; +import org.jackhuang.hmcl.util.gson.*; import org.jackhuang.hmcl.util.io.HttpRequest; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.ResponseCodeException; @@ -60,17 +61,17 @@ public class MicrosoftService { private final OAuthCallback callback; - private final ObservableOptionalCache profileRepository; + private final ObservableOptionalCache profileRepository; public MicrosoftService(OAuthCallback callback) { this.callback = requireNonNull(callback); - this.profileRepository = new ObservableOptionalCache<>(authorization -> { - LOG.info("Fetching properties"); - return getCompleteProfile(authorization); + this.profileRepository = new ObservableOptionalCache<>(uuid -> { + LOG.info("Fetching properties of " + uuid); + return getCompleteGameProfile(uuid); }, (uuid, e) -> LOG.log(Level.WARNING, "Failed to fetch properties of " + uuid, e), POOL); } - public ObservableOptionalCache getProfileRepository() { + public ObservableOptionalCache getProfileRepository() { return profileRepository; } @@ -213,10 +214,14 @@ public class MicrosoftService { } } - public boolean validate(String tokenType, String accessToken) throws AuthenticationException { + public boolean validate(long notAfter, String tokenType, String accessToken) throws AuthenticationException { requireNonNull(tokenType); requireNonNull(accessToken); + if (System.currentTimeMillis() > notAfter) { + return false; + } + try { getMinecraftProfile(tokenType, accessToken); return true; @@ -275,6 +280,31 @@ public class MicrosoftService { return JsonUtils.fromNonNullJson(result, MinecraftProfileResponse.class); } + public Optional getCompleteGameProfile(UUID uuid) throws AuthenticationException { + Objects.requireNonNull(uuid); + + return Optional.ofNullable(GSON.fromJson(request(NetworkUtils.toURL("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(uuid)), null), CompleteGameProfile.class)); + } + + private static String request(URL url, Object payload) throws AuthenticationException { + try { + if (payload == null) + return NetworkUtils.doGet(url); + else + return NetworkUtils.doPost(url, payload instanceof String ? (String) payload : GSON.toJson(payload), "application/json"); + } catch (IOException e) { + throw new ServerDisconnectException(e); + } + } + + private static T fromJson(String text, Class typeOfT) throws ServerResponseMalformedException { + try { + return GSON.fromJson(text, typeOfT); + } catch (JsonParseException e) { + throw new ServerResponseMalformedException(text, e); + } + } + public static class XboxAuthorizationException extends AuthenticationException { private final long errorCode; @@ -487,4 +517,9 @@ public class MicrosoftService { String waitFor() throws InterruptedException, ExecutionException; } + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE) + .registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE) + .create(); + } From 1000cc1ab0ec4997ef68b31dbb7638e154a71268 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 4 Sep 2021 15:49:34 +0800 Subject: [PATCH 03/21] feat: WIP: account login progress --- .../hmcl/ui/account/CreateAccountPane.java | 77 ++++++------------- .../resources/assets/lang/I18N_zh.properties | 8 ++ .../assets/lang/I18N_zh_CN.properties | 1 + .../jackhuang/hmcl/auth/AccountFactory.java | 18 +++-- .../AuthlibInjectorAccountFactory.java | 2 +- .../microsoft/MicrosoftAccountFactory.java | 2 +- .../auth/offline/OfflineAccountFactory.java | 2 +- .../yggdrasil/YggdrasilAccountFactory.java | 2 +- 8 files changed, 51 insertions(+), 61 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java index 9d68190d3..a176e38f4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java @@ -17,26 +17,17 @@ */ package org.jackhuang.hmcl.ui.account; -import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableList; -import static javafx.beans.binding.Bindings.bindContent; -import static javafx.beans.binding.Bindings.createBooleanBinding; -import static org.jackhuang.hmcl.setting.ConfigHolder.config; -import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory; -import static org.jackhuang.hmcl.ui.FXUtils.onChange; -import static org.jackhuang.hmcl.ui.FXUtils.onChangeAndOperate; -import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; -import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; -import static org.jackhuang.hmcl.ui.FXUtils.setValidateWhileTextChanged; -import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.classPropertyFor; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; - +import com.jfoenix.controls.*; +import javafx.application.Platform; +import javafx.beans.binding.BooleanBinding; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.*; import org.jackhuang.hmcl.auth.AccountFactory; import org.jackhuang.hmcl.auth.CharacterSelector; import org.jackhuang.hmcl.auth.NoSelectedCharacterException; @@ -53,40 +44,22 @@ import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.construct.AdvancedListBox; -import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; -import org.jackhuang.hmcl.ui.construct.IconedItem; -import org.jackhuang.hmcl.ui.construct.RequiredValidator; -import org.jackhuang.hmcl.ui.construct.SpinnerPane; -import org.jackhuang.hmcl.ui.construct.TabControl; -import org.jackhuang.hmcl.ui.construct.TabHeader; -import org.jackhuang.hmcl.ui.construct.TwoLineListItem; -import org.jackhuang.hmcl.ui.construct.Validator; +import org.jackhuang.hmcl.ui.construct.*; import org.jetbrains.annotations.Nullable; -import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXComboBox; -import com.jfoenix.controls.JFXDialogLayout; -import com.jfoenix.controls.JFXPasswordField; -import com.jfoenix.controls.JFXTextField; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; -import javafx.application.Platform; -import javafx.beans.binding.BooleanBinding; -import javafx.geometry.HPos; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.Node; -import javafx.scene.control.Hyperlink; -import javafx.scene.control.Label; -import javafx.scene.image.ImageView; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static javafx.beans.binding.Bindings.bindContent; +import static javafx.beans.binding.Bindings.createBooleanBinding; +import static org.jackhuang.hmcl.setting.ConfigHolder.config; +import static org.jackhuang.hmcl.ui.FXUtils.*; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; +import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.classPropertyFor; public class CreateAccountPane extends JFXDialogLayout { @@ -216,7 +189,7 @@ public class CreateAccountPane extends JFXDialogLayout { additionalData = null; } - loginTask = Task.supplyAsync(() -> factory.create(new DialogCharacterSelector(), username, password, additionalData)) + loginTask = Task.supplyAsync(() -> factory.create(new DialogCharacterSelector(), username, password, null, additionalData)) .whenComplete(Schedulers.javafx(), account -> { int oldIndex = Accounts.getAccounts().indexOf(account); if (oldIndex == -1) { diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 2c869178f..94d5d8396 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -58,6 +58,14 @@ account.manage=帳戶列表 account.methods=登入方式 account.methods.authlib_injector=authlib-injector 登入 account.methods.microsoft=微軟帳戶 +account.methods.microsoft.close_page=已完成微軟帳號授權,接下來啟動器還需要完成剩餘登錄步驟。你已經可以關閉本頁面了。 +account.methods.microsoft.error.add_family=由於你未滿 18 歲,你的帳號必須被加入到家庭中才能登錄遊戲。 +account.methods.microsoft.error.missing_xbox_account=你的微軟帳號尚未關聯 XBox 帳號,你必須先創建 XBox 帳號,才能登錄遊戲。 +account.methods.microsoft.error.unknown=登錄失敗,錯誤碼:%d +account.methods.microsoft.logging_in=登錄中... +account.methods.microsoft.hint=點擊確定以登錄 +account.methods.microsoft.manual=您需要在新打開的瀏覽器窗口中完成登錄。若頁面未能打開,您可以點擊此處複製連結,並手動在瀏覽器中打開網頁。 +account.methods.microsoft.waiting_browser=等待在新打開的瀏覽器窗口中完成登錄... account.methods.offline=離線模式 account.methods.yggdrasil=正版登入 account.missing=沒有遊戲帳戶 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 3db04b099..a0b722813 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -76,6 +76,7 @@ account.methods.microsoft.error.add_family=由于你未满 18 岁,你的账号 account.methods.microsoft.error.missing_xbox_account=你的微软账号尚未关联 XBox 账号,你必须先创建 XBox 账号,才能登录游戏。 account.methods.microsoft.error.unknown=登录失败,错误码:%d account.methods.microsoft.logging_in=登录中... +account.methods.microsoft.hint=点击确定以登录 account.methods.microsoft.manual=您需要在新打开的浏览器窗口中完成登录。若页面未能打开,您可以点击此处复制链接,并手动在浏览器中打开网页。 account.methods.microsoft.waiting_browser=等待在新打开的浏览器窗口中完成登录... account.methods.offline=离线模式 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountFactory.java index 5e5db757a..7670ae27f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountFactory.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountFactory.java @@ -50,25 +50,33 @@ public abstract class AccountFactory { } } + public interface ProgressCallback { + void onProgressChanged(String stageName); + } + /** * Informs how this account factory verifies user's credential. + * * @see AccountLoginType */ public abstract AccountLoginType getLoginType(); /** * Create a new(to be verified via network) account, and log in. - * @param selector for character selection if multiple characters belong to single account. Pick out which character to act as. - * @param username username of the account if needed. - * @param password password of the account if needed. - * @param additionalData extra data for specific account factory. + * + * @param selector for character selection if multiple characters belong to single account. Pick out which character to act as. + * @param username username of the account if needed. + * @param password password of the account if needed. + * @param progressCallback notify login progress. + * @param additionalData extra data for specific account factory. * @return logged-in account. * @throws AuthenticationException if an error occurs when logging in. */ - public abstract T create(CharacterSelector selector, String username, String password, Object additionalData) throws AuthenticationException; + public abstract T create(CharacterSelector selector, String username, String password, ProgressCallback progressCallback, Object additionalData) throws AuthenticationException; /** * Create a existing(stored in local files) account. + * * @param storage serialized account data. * @return account stored in local storage. Credentials may expired, and you should refresh account state later. */ diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccountFactory.java index 2e135c541..04eb219ea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccountFactory.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccountFactory.java @@ -50,7 +50,7 @@ public class AuthlibInjectorAccountFactory extends AccountFactory { } @Override - public MicrosoftAccount create(CharacterSelector selector, String username, String password, Object additionalData) throws AuthenticationException { + public MicrosoftAccount create(CharacterSelector selector, String username, String password, ProgressCallback progressCallback, Object additionalData) throws AuthenticationException { Objects.requireNonNull(selector); return new MicrosoftAccount(service, selector); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccountFactory.java index 92d319f4a..7871171d0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccountFactory.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccountFactory.java @@ -47,7 +47,7 @@ public final class OfflineAccountFactory extends AccountFactory } @Override - public OfflineAccount create(CharacterSelector selector, String username, String password, Object additionalData) { + public OfflineAccount create(CharacterSelector selector, String username, String password, ProgressCallback progressCallback, Object additionalData) { return new OfflineAccount(username, getUUIDFromUserName(username)); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java index 91c9e8f4d..0d5955be2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java @@ -48,7 +48,7 @@ public class YggdrasilAccountFactory extends AccountFactory { } @Override - public YggdrasilAccount create(CharacterSelector selector, String username, String password, Object additionalData) throws AuthenticationException { + public YggdrasilAccount create(CharacterSelector selector, String username, String password, ProgressCallback progressCallback, Object additionalData) throws AuthenticationException { Objects.requireNonNull(selector); Objects.requireNonNull(username); Objects.requireNonNull(password); From 723f216e6b7e497a266d7317d930739c38559e7f Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 4 Sep 2021 15:49:55 +0800 Subject: [PATCH 04/21] feat: JSTUN --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..c96b42156 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "JSTUN"] + path = JSTUN + url = https://github.com/huanghongxun/JSTUN From c11d947099a481c4404def4842aac99b4b75e1f6 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 4 Sep 2021 15:52:40 +0800 Subject: [PATCH 05/21] FIx: too large spinner in accountlistitem. Closes #999. --- .../org/jackhuang/hmcl/ui/account/AccountListItemSkin.java | 1 + .../java/org/jackhuang/hmcl/ui/construct/SpinnerPane.java | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java index 9a4af5470..b84d29583 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java @@ -90,6 +90,7 @@ public class AccountListItemSkin extends SkinBase { JFXButton btnRefresh = new JFXButton(); SpinnerPane spinnerRefresh = new SpinnerPane(); + spinnerRefresh.getStyleClass().setAll("small-spinner-pane"); btnRefresh.setOnMouseClicked(e -> { spinnerRefresh.showSpinner(); skinnable.refreshAsync() diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/SpinnerPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/SpinnerPane.java index 68671de95..0d2dd38b9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/SpinnerPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/SpinnerPane.java @@ -36,6 +36,10 @@ public class SpinnerPane extends Control { private final BooleanProperty loading = new SimpleBooleanProperty(this, "loading"); private final StringProperty failedReason = new SimpleStringProperty(this, "failedReason"); + public SpinnerPane() { + getStyleClass().add("spinner-pane"); + } + public void showSpinner() { setLoading(true); } @@ -99,7 +103,6 @@ public class SpinnerPane extends Control { protected Skin(SpinnerPane control) { super(control); - root.getStyleClass().add("spinner-pane"); topPane.getChildren().setAll(spinner); topPane.getStyleClass().add("notice-pane"); failedPane.getChildren().setAll(failedReasonLabel); From 7166a809c3eb10d40479a7b62f22a28c1ff72906 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 4 Sep 2021 15:54:23 +0800 Subject: [PATCH 06/21] feat: jstun? --- JSTUN | 1 + 1 file changed, 1 insertion(+) create mode 160000 JSTUN diff --git a/JSTUN b/JSTUN new file mode 160000 index 000000000..6516d37c7 --- /dev/null +++ b/JSTUN @@ -0,0 +1 @@ +Subproject commit 6516d37c703b74d6dbbbdffdaa2a056ce2f2fdd5 From 8a0faa72c1a1455ad838ddaa509ff0c4c6c4a941 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 5 Sep 2021 19:48:45 +0800 Subject: [PATCH 07/21] fix: listview not filled the blank --- .../java/org/jackhuang/hmcl/ui/construct/ComponentList.java | 4 ++++ .../java/org/jackhuang/hmcl/ui/download/VersionsPage.java | 1 + 2 files changed, 5 insertions(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java index 5e6b93f66..76633a0b8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java @@ -34,6 +34,7 @@ import javafx.scene.control.Control; import javafx.scene.control.Label; import javafx.scene.control.SkinBase; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import org.jackhuang.hmcl.util.javafx.MappedObservableList; @@ -132,6 +133,9 @@ public class ComponentList extends Control { list = MappedObservableList.create(control.getContent(), node -> { ComponentListCell cell = new ComponentListCell(node); cell.getStyleClass().add("options-list-item"); + if (node.getProperties().containsKey("vgrow")) { + VBox.setVgrow(cell, Priority.ALWAYS); + } return cell; }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index d6c8faa67..b7f56cc05 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -105,6 +105,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres } else { centrePane.getContent().setAll(list); } + list.getProperties().put("vgrow", true); InvalidationListener listener = o -> list.getItems().setAll(loadVersions()); chkRelease.selectedProperty().addListener(listener); From 3a406744c21e1ca02b2f83031fff98e13690d530 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 5 Sep 2021 19:49:39 +0800 Subject: [PATCH 08/21] fix: padding around sponsor pane --- HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java index 9717de6d9..2b0645110 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java @@ -69,6 +69,7 @@ public abstract class SettingsView extends StackPane { StackPane sponsorPane = new StackPane(); sponsorPane.setCursor(Cursor.HAND); sponsorPane.setOnMouseClicked(e -> onSponsor()); + sponsorPane.setPadding(new Insets(8, 0, 8, 0)); GridPane gridPane = new GridPane(); From 47540e7f91158573eff1cb05f4b886adf51439f4 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 5 Sep 2021 19:50:05 +0800 Subject: [PATCH 09/21] fix: wrong i18n id --- .../java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java index 91cd3c35d..44c1df8fa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java @@ -67,7 +67,7 @@ public class LauncherSettingsPage extends BorderPane implements DecoratorPage { { AdvancedListBox sideBar = new AdvancedListBox() .addNavigationDrawerItem(settingsItem -> { - settingsItem.setTitle(i18n("settings.type.global.manag")); + settingsItem.setTitle(i18n("settings.type.global.manage")); settingsItem.setLeftGraphic(wrap(SVG.gamepad(null, 20, 20))); settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(gameTab)); settingsItem.setOnAction(e -> tab.getSelectionModel().select(gameTab)); From 21e28ab038e3e31911928e937ca9c626b49304ba Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 5 Sep 2021 19:50:48 +0800 Subject: [PATCH 10/21] feat: show more human-readable error message when no Java Edition profile existing in MicrosoftAccount. --- HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java | 2 ++ .../org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java index 91beb47db..8688afbe3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java @@ -365,6 +365,8 @@ public final class Accounts { } else { return i18n("account.methods.microsoft.error.unknown", errorCode); } + } else if (exception instanceof MicrosoftService.NoMinecraftJavaEditionProfileException) { + return i18n("account.methods.microsoft.error.no_character"); } else if (exception.getClass() == AuthenticationException.class) { return exception.getLocalizedMessage(); } else { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java index 1af3d4a40..5db3aedd2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java @@ -271,7 +271,7 @@ public class MicrosoftService { .createConnection(); int responseCode = conn.getResponseCode(); if (responseCode == HTTP_NOT_FOUND) { - throw new NoCharacterException(); + throw new NoMinecraftJavaEditionProfileException(); } else if (responseCode != 200) { throw new ResponseCodeException(new URL("https://api.minecraftservices.com/minecraft/profile"), responseCode); } @@ -320,6 +320,9 @@ public class MicrosoftService { public static final long ADD_FAMILY = 2148916238L; } + public static class NoMinecraftJavaEditionProfileException extends AuthenticationException { + } + /** * Error response: {"error":"invalid_grant","error_description":"The provided * value for the 'redirect_uri' is not valid. The value must exactly match the From b3e3192876b70b6cecaa088defbce06a8d632631 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 5 Sep 2021 21:47:26 +0800 Subject: [PATCH 11/21] feat: refine mod list ui. --- .../java/org/jackhuang/hmcl/ui/FXUtils.java | 16 +++++ .../hmcl/ui/ToolbarListPageSkin.java | 8 +++ .../hmcl/ui/construct/ComponentList.java | 8 ++- .../hmcl/ui/construct/MDListCell.java | 62 ++++++++++++++++ .../hmcl/ui/download/VersionsPage.java | 3 +- .../hmcl/ui/versions/ModListPage.java | 9 +++ .../hmcl/ui/versions/ModListPageSkin.java | 71 +++++++++++-------- .../hmcl/ui/versions/VersionPage.java | 2 +- HMCL/src/main/resources/assets/css/root.css | 4 ++ .../resources/assets/lang/I18N.properties | 2 + .../resources/assets/lang/I18N_zh.properties | 1 + .../assets/lang/I18N_zh_CN.properties | 11 +++ 12 files changed, 162 insertions(+), 35 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MDListCell.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index 54465b9a5..2c5b98f51 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -59,6 +59,7 @@ import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URI; +import java.nio.file.Path; import java.util.List; import java.util.Objects; import java.util.function.BooleanSupplier; @@ -363,6 +364,21 @@ public final class FXUtils { } } + public static void showFileInExplorer(Path file) { + switch (OperatingSystem.CURRENT_OS) { + case WINDOWS: + try { + Runtime.getRuntime().exec(new String[]{"explorer.exe", "/select,", file.toAbsolutePath().toString()}); + } catch (IOException e) { + Logging.LOG.log(Level.SEVERE, "Unable to open " + file + " by executing explorer /select", e); + } + break; + default: + // Currently unsupported. + break; + } + } + private static final String[] linuxBrowsers = { "xdg-open", "google-chrome", diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ToolbarListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/ToolbarListPageSkin.java index f398060ab..c9f2885e2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ToolbarListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/ToolbarListPageSkin.java @@ -99,6 +99,14 @@ public abstract class ToolbarListPageSkin return ret; } + public static JFXButton createToolbarButton2(String text, SVG.SVGIcon creator, Runnable onClick) { + JFXButton ret = new JFXButton(); + ret.getStyleClass().add("jfx-tool-bar-button"); + ret.setGraphic(wrap(creator.createIcon(Theme.blackFillBinding(), -1, -1))); + ret.setText(text); + ret.setOnMouseClicked(e -> onClick.run()); + return ret; + } public static JFXButton createDecoratorButton(String tooltip, SVG.SVGIcon creator, Runnable onClick) { JFXButton ret = new JFXButton(); ret.getStyleClass().add("jfx-decorator-button"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java index 76633a0b8..9c6d04875 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java @@ -133,8 +133,8 @@ public class ComponentList extends Control { list = MappedObservableList.create(control.getContent(), node -> { ComponentListCell cell = new ComponentListCell(node); cell.getStyleClass().add("options-list-item"); - if (node.getProperties().containsKey("vgrow")) { - VBox.setVgrow(cell, Priority.ALWAYS); + if (node.getProperties().containsKey("ComponentList.vgrow")) { + VBox.setVgrow(cell, (Priority) node.getProperties().get("ComponentList.vgrow")); } return cell; }); @@ -176,4 +176,8 @@ public class ComponentList extends Control { } return node; } + + public static void setVgrow(Node node, Priority priority) { + node.getProperties().put("ComponentList.vgrow", priority); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MDListCell.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MDListCell.java new file mode 100644 index 000000000..18c262c2e --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MDListCell.java @@ -0,0 +1,62 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2021 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.ui.construct; + +import javafx.css.PseudoClass; +import javafx.scene.control.ListCell; +import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.ui.FXUtils; + +public abstract class MDListCell extends ListCell { + private final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected"); + + private final StackPane container = new StackPane(); + private final StackPane root = new StackPane(); + + public MDListCell() { + setText(null); + setGraphic(null); + + root.getStyleClass().add("md-list-cell"); + RipplerContainer ripplerContainer = new RipplerContainer(container); + root.getChildren().setAll(ripplerContainer); + } + + @Override + protected void updateItem(T item, boolean empty) { + super.updateItem(item, empty); + updateControl(item, empty); + if (empty) { + setGraphic(null); + } else { + setGraphic(root); + } + } + + protected StackPane getContainer() { + return container; + } + + protected void setSelectable() { + FXUtils.onChangeAndOperate(selectedProperty(), selected -> { + root.pseudoClassStateChanged(SELECTED, selected); + }); + } + + protected abstract void updateControl(T item, boolean empty); +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index b7f56cc05..875eb010b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -28,6 +28,7 @@ import javafx.scene.control.ListCell; import javafx.scene.image.Image; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.RemoteVersion; @@ -105,7 +106,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres } else { centrePane.getContent().setAll(list); } - list.getProperties().put("vgrow", true); + ComponentList.setVgrow(list, Priority.ALWAYS); InvalidationListener listener = o -> list.getItems().setAll(loadVersions()); chkRelease.selectedProperty().addListener(listener); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java index 4d33ac1a1..667d466a4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java @@ -54,6 +54,8 @@ public final class ModListPage extends ListPageBase Arrays.asList("jar", "zip", "litemod").contains(FileUtils.getExtension(it)), mods -> { @@ -79,6 +81,9 @@ public final class ModListPage extends ListPageBase info.setActive(false)); } + public void openModFolder() { + FXUtils.openFolder(new File(profile.getRepository().getRunDirectory(versionId), "mods")); + } + public boolean isModded() { return modded.get(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java index 093cb3cd2..b1face0c8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java @@ -22,16 +22,15 @@ import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXDialogLayout; import com.jfoenix.controls.JFXListView; import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject; -import com.jfoenix.effects.JFXDepthManager; import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; +import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.control.SelectionMode; import javafx.scene.control.SkinBase; import javafx.scene.image.Image; import javafx.scene.image.ImageView; -import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; @@ -42,10 +41,7 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; -import org.jackhuang.hmcl.ui.construct.FloatListCell; -import org.jackhuang.hmcl.ui.construct.SpinnerPane; -import org.jackhuang.hmcl.ui.construct.TwoLineListItem; +import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; @@ -59,7 +55,7 @@ import java.nio.file.Files; import java.nio.file.Path; import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; -import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton; +import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton2; import static org.jackhuang.hmcl.util.Lang.mapOf; import static org.jackhuang.hmcl.util.Pair.pair; import static org.jackhuang.hmcl.util.StringUtils.isNotBlank; @@ -71,42 +67,44 @@ class ModListPageSkin extends SkinBase { super(skinnable); StackPane pane = new StackPane(); + pane.setPadding(new Insets(10)); pane.getStyleClass().addAll("notice-pane"); - BorderPane root = new BorderPane(); + ComponentList root = new ComponentList(); + root.getStyleClass().add("no-padding"); JFXListView listView = new JFXListView<>(); { HBox toolbar = new HBox(); - toolbar.getStyleClass().add("jfx-tool-bar-second"); - JFXDepthManager.setDepth(toolbar, 1); - toolbar.setPickOnBounds(false); - - toolbar.getChildren().add(createToolbarButton(i18n("button.refresh"), SVG::refresh, skinnable::refresh)); - toolbar.getChildren().add(createToolbarButton(i18n("mods.add"), SVG::plus, skinnable::add)); - toolbar.getChildren().add(createToolbarButton(i18n("button.remove"), SVG::delete, () -> { - Controllers.confirm(i18n("button.remove.confirm"), i18n("button.remove"), () -> { - skinnable.removeSelected(listView.getSelectionModel().getSelectedItems()); - }, null); - })); - toolbar.getChildren().add(createToolbarButton(i18n("mods.enable"), SVG::check, () -> - skinnable.enableSelected(listView.getSelectionModel().getSelectedItems()))); - toolbar.getChildren().add(createToolbarButton(i18n("mods.disable"), SVG::close, () -> - skinnable.disableSelected(listView.getSelectionModel().getSelectedItems()))); - root.setTop(toolbar); + toolbar.getChildren().setAll( + createToolbarButton2(i18n("button.refresh"), SVG::refresh, skinnable::refresh), + createToolbarButton2(i18n("mods.add"), SVG::plus, skinnable::add), + createToolbarButton2(i18n("button.remove"), SVG::delete, () -> { + Controllers.confirm(i18n("button.remove.confirm"), i18n("button.remove"), () -> { + skinnable.removeSelected(listView.getSelectionModel().getSelectedItems()); + }, null); + }), + createToolbarButton2(i18n("mods.enable"), SVG::check, () -> + skinnable.enableSelected(listView.getSelectionModel().getSelectedItems())), + createToolbarButton2(i18n("mods.disable"), SVG::close, () -> + skinnable.disableSelected(listView.getSelectionModel().getSelectedItems())), + createToolbarButton2(i18n("folder.mod"), SVG::folderOpen, () -> + skinnable.openModFolder())); + root.getContent().add(toolbar); } { SpinnerPane center = new SpinnerPane(); + ComponentList.setVgrow(center, Priority.ALWAYS); center.getStyleClass().add("large-spinner-pane"); center.loadingProperty().bind(skinnable.loadingProperty()); - listView.setCellFactory(x -> new ModInfoListCell(listView)); + listView.setCellFactory(x -> new ModInfoListCell()); listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); Bindings.bindContent(listView.getItems(), skinnable.getItems()); center.setContent(listView); - root.setCenter(center); + root.getContent().add(center); } Label label = new Label(i18n("mods.not_modded")); @@ -231,23 +229,31 @@ class ModListPageSkin extends SkinBase { } } - static class ModInfoListCell extends FloatListCell { + static class ModInfoListCell extends MDListCell { JFXCheckBox checkBox = new JFXCheckBox(); TwoLineListItem content = new TwoLineListItem(); JFXButton infoButton = new JFXButton(); + JFXButton revealButton = new JFXButton(); BooleanProperty booleanProperty; - ModInfoListCell(JFXListView listView) { - super(listView); + ModInfoListCell() { HBox container = new HBox(8); + container.setPickOnBounds(false); container.setAlignment(Pos.CENTER_LEFT); - pane.getChildren().add(container); HBox.setHgrow(content, Priority.ALWAYS); + content.setMouseTransparent(true); + setSelectable(); + + revealButton.getStyleClass().add("toggle-icon4"); + revealButton.setGraphic(FXUtils.limitingSize(SVG.folderOutline(Theme.blackFillBinding(), 24, 24), 24, 24)); infoButton.getStyleClass().add("toggle-icon4"); infoButton.setGraphic(FXUtils.limitingSize(SVG.informationOutline(Theme.blackFillBinding(), 24, 24), 24, 24)); - container.getChildren().setAll(checkBox, content, infoButton); + container.getChildren().setAll(checkBox, content, revealButton, infoButton); + + StackPane.setMargin(container, new Insets(10, 16, 10, 16)); + getContainer().getChildren().setAll(container); } @Override @@ -259,6 +265,9 @@ class ModListPageSkin extends SkinBase { checkBox.selectedProperty().unbindBidirectional(booleanProperty); } checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active); + revealButton.setOnMouseClicked(e -> { + FXUtils.showFileInExplorer(dataItem.getModInfo().getFile()); + }); infoButton.setOnMouseClicked(e -> { Controllers.dialog(new ModInfoDialog(dataItem)); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java index 2f69851e1..ca918c77e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2021 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 diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index afa9ff8ea..4ec9bc6dd 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -793,6 +793,10 @@ -fx-border-width: 0 0 1 0; } +.md-list-cell:selected { + -fx-background-color: derive(-fx-base-color, 60%); +} + .options-sublist { -fx-background-color: white; } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 904757b4a..17c0910f0 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -68,6 +68,7 @@ account.methods.microsoft=Microsoft Account account.methods.microsoft.close_page=Microsoft account authorization has been finished. There are some remaining logging-in steps to be finished later. You can close this page right now. account.methods.microsoft.error.add_family=Since you are not yet 18 years old, an adult must add you to a family in order for you to play Minecraft. account.methods.microsoft.error.missing_xbox_account=Your Microsoft account is not connected to an Xbox account. Please create one before continuing. +account.methods.microsoft.error.no_character=Account is missing a Minecraft Java profile. While the Microsoft account is valid, it does not own the game. account.methods.microsoft.error.unknown=Failed to log in. Microsoft respond with error code %d. account.methods.microsoft.logging_in=Logging in... account.methods.microsoft.manual=You should finish authorization in the newly opened browser window. If the browser window failed to show, you can click here to copy the URL, and manually open it in your browser. @@ -505,6 +506,7 @@ selector.custom=Custom settings=Settings settings.advanced=Advanced Settings +settings.advanced.custom_commands=Custom Commands settings.advanced.dont_check_game_completeness=Do not scan game files settings.advanced.dont_check_jvm_validity=Don't check whether JVM can launch the game or not settings.advanced.game_dir.default=Standard (.minecraft/) diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 94d5d8396..bf65cf480 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -61,6 +61,7 @@ account.methods.microsoft=微軟帳戶 account.methods.microsoft.close_page=已完成微軟帳號授權,接下來啟動器還需要完成剩餘登錄步驟。你已經可以關閉本頁面了。 account.methods.microsoft.error.add_family=由於你未滿 18 歲,你的帳號必須被加入到家庭中才能登錄遊戲。 account.methods.microsoft.error.missing_xbox_account=你的微軟帳號尚未關聯 XBox 帳號,你必須先創建 XBox 帳號,才能登錄遊戲。 +account.methods.microsoft.error.no_character=該帳號沒有包含 Minecraft Java 版購買記錄 account.methods.microsoft.error.unknown=登錄失敗,錯誤碼:%d account.methods.microsoft.logging_in=登錄中... account.methods.microsoft.hint=點擊確定以登錄 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 a0b722813..820be427c 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -74,6 +74,7 @@ account.methods.microsoft=微软账户 account.methods.microsoft.close_page=已完成微软账号授权,接下来启动器还需要完成剩余登录步骤。你已经可以关闭本页面了。 account.methods.microsoft.error.add_family=由于你未满 18 岁,你的账号必须被加入到家庭中才能登录游戏。 account.methods.microsoft.error.missing_xbox_account=你的微软账号尚未关联 XBox 账号,你必须先创建 XBox 账号,才能登录游戏。 +account.methods.microsoft.error.no_character=该账号没有包含 Minecraft Java 版购买记录 account.methods.microsoft.error.unknown=登录失败,错误码:%d account.methods.microsoft.logging_in=登录中... account.methods.microsoft.hint=点击确定以登录 @@ -460,6 +461,16 @@ mods.name=名称 mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理。 mods.url=官方页面 +multiplayer=联机 +multiplayer.nat=网络检测 +multiplayer.nat.hint=执行网络检测可以让你更清楚里的网络状况是否符合联机功能的需求。不符合联机功能运行条件的网络状况将可能导致联机失败。 +multiplayer.nat.latency=延迟 +multiplayer.nat.not_yet_tested=尚未检测 +multiplayer.nat.packet_loss_ratio=丢包率 +multiplayer.nat.testing=检测中 +multiplayer.nat.type=NAT 类型 +multiplayer.room=房间 + datapack=数据包 datapack.add=添加数据包 datapack.choose_datapack=选择要导入的数据包压缩包 From c844a77f023a77bf8e1d9cfb39ebab6004b017fb Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 5 Sep 2021 21:48:29 +0800 Subject: [PATCH 12/21] fix: sort mod download list page items. Closes #1001. --- .../jackhuang/hmcl/ui/versions/ModDownloadPage.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadPage.java index e7abf648c..9270b9d8e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadPage.java @@ -89,6 +89,7 @@ public class ModDownloadPage extends Control implements DecoratorPage { List files = CurseModManager.getFiles(addon); items.setAll(files.stream() .filter(file -> file.getGameVersion().contains(gameVersion.get())) + .sorted(Comparator.comparing(CurseAddon.LatestFile::getParsedFileDate).reversed()) .collect(Collectors.toList())); return; } @@ -276,6 +277,18 @@ public class ModDownloadPage extends Control implements DecoratorPage { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withLocale(Locale.getDefault()).withZone(ZoneId.systemDefault()); + public interface Project { + + } + + public interface ProjectVersion { + + } + + public interface DownloadSource { + + } + public interface DownloadCallback { void download(Profile profile, @Nullable String version, CurseAddon.LatestFile file); } From d521d10a85e9b8378b7a5b1b118b0f2c9285cf05 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Mon, 6 Sep 2021 00:45:25 +0800 Subject: [PATCH 13/21] feat: WIP: multiplayer --- HMCL/build.gradle | 1 + .../org/jackhuang/hmcl/ui/Controllers.java | 7 + .../main/java/org/jackhuang/hmcl/ui/SVG.java | 6 + .../hmcl/ui/ToolbarListPageSkin.java | 1 + .../org/jackhuang/hmcl/ui/main/RootPage.java | 10 + .../ui/multiplayer/MultiplayerManager.java | 27 +++ .../hmcl/ui/multiplayer/MultiplayerPage.java | 86 +++++++++ .../ui/multiplayer/MultiplayerPageSkin.java | 174 ++++++++++++++++++ .../resources/assets/lang/I18N.properties | 29 +++ .../resources/assets/lang/I18N_zh.properties | 29 +++ .../assets/lang/I18N_zh_CN.properties | 19 ++ build.gradle | 4 + settings.gradle | 2 +- 13 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java diff --git a/HMCL/build.gradle b/HMCL/build.gradle index be822d9f7..714294810 100644 --- a/HMCL/build.gradle +++ b/HMCL/build.gradle @@ -44,6 +44,7 @@ mainClassName = 'org.jackhuang.hmcl.Main' dependencies { implementation project(":HMCLCore") + implementation project(":JSTUN") implementation rootProject.files("lib/JFoenix.jar") } 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 ea0651887..ac4759256 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -48,6 +48,7 @@ import org.jackhuang.hmcl.ui.download.DownloadPage; import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider; import org.jackhuang.hmcl.ui.main.LauncherSettingsPage; import org.jackhuang.hmcl.ui.main.RootPage; +import org.jackhuang.hmcl.ui.multiplayer.MultiplayerPage; import org.jackhuang.hmcl.ui.versions.GameListPage; import org.jackhuang.hmcl.ui.versions.ModDownloadListPage; import org.jackhuang.hmcl.ui.versions.VersionPage; @@ -101,6 +102,7 @@ public final class Controllers { accountListPage.accountsProperty().bindContent(Accounts.accountsProperty()); return accountListPage; }); + private static Lazy multiplayerPage = new Lazy<>(MultiplayerPage::new); private static Lazy settingsPage = new Lazy<>(LauncherSettingsPage::new); private Controllers() { @@ -141,6 +143,11 @@ public final class Controllers { return modDownloadListPage.get(); } + // FXThread + public static MultiplayerPage getMultiplayerPage() { + return multiplayerPage.get(); + } + // FXThread public static LauncherSettingsPage getSettingsPage() { return settingsPage.get(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index 5273d9765..97068cf69 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -391,6 +391,12 @@ public final class SVG { fill, width, height); } + public static Node accountArrowRightOutline(ObjectBinding fill, double width, double height) { + return createSVGPath( + "M19,21V19H15V17H19V15L22,18L19,21M13,18C13,18.71 13.15,19.39 13.42,20H2V17C2,14.79 5.58,13 10,13C11,13 11.96,13.09 12.85,13.26C13.68,13.42 14.44,13.64 15.11,13.92C13.83,14.83 13,16.32 13,18M4,17V18H11C11,16.96 11.23,15.97 11.64,15.08L10,15C6.69,15 4,15.9 4,17M10,4A4,4 0 0,1 14,8A4,4 0 0,1 10,12A4,4 0 0,1 6,8A4,4 0 0,1 10,4M10,6A2,2 0 0,0 8,8A2,2 0 0,0 10,10A2,2 0 0,0 12,8A2,2 0 0,0 10,6Z", + fill, width, height); + } + public static Node styleOutline(ObjectBinding fill, double width, double height) { return createSVGPath( "M2.5 19.6L3.8 20.2V11.2L1.4 17C1 18.1 1.5 19.2 2.5 19.6M15.2 4.8L20.2 16.8L12.9 19.8L7.9 7.9V7.8L15.2 4.8M15.3 2.8C15 2.8 14.8 2.8 14.5 2.9L7.1 6C6.4 6.3 5.9 7 5.9 7.8C5.9 8 5.9 8.3 6 8.6L11 20.5C11.3 21.3 12 21.7 12.8 21.7C13.1 21.7 13.3 21.7 13.6 21.6L21 18.5C22 18.1 22.5 16.9 22.1 15.9L17.1 4C16.8 3.2 16 2.8 15.3 2.8M10.5 9.9C9.9 9.9 9.5 9.5 9.5 8.9S9.9 7.9 10.5 7.9C11.1 7.9 11.5 8.4 11.5 8.9S11.1 9.9 10.5 9.9M5.9 19.8C5.9 20.9 6.8 21.8 7.9 21.8H9.3L5.9 13.5V19.8Z", diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ToolbarListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/ToolbarListPageSkin.java index c9f2885e2..0033552bd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/ToolbarListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/ToolbarListPageSkin.java @@ -107,6 +107,7 @@ public abstract class ToolbarListPageSkin ret.setOnMouseClicked(e -> onClick.run()); return ret; } + public static JFXButton createDecoratorButton(String tooltip, SVG.SVGIcon creator, Runnable onClick) { JFXButton ret = new JFXButton(); ret.getStyleClass().add("jfx-decorator-button"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index d5f3c39e0..6d14c88b6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -176,6 +176,14 @@ public class RootPage extends DecoratorTabPage { downloadItem.setOnAction(e -> Controllers.navigate(Controllers.getDownloadPage())); // fifth item in left sidebar + AdvancedListItem multiplayerItem = new AdvancedListItem(); + multiplayerItem + .setLeftGraphic(AdvancedListItem.createImageView(newImage("/assets/img/command.png")).getKey()); + multiplayerItem.setActionButtonVisible(false); + multiplayerItem.setTitle(i18n("multiplayer")); + multiplayerItem.setOnAction(e -> Controllers.navigate(Controllers.getMultiplayerPage())); + + // sixth item in left sidebar AdvancedListItem launcherSettingsItem = new AdvancedListItem(); launcherSettingsItem .setLeftGraphic(AdvancedListItem.createImageView(newImage("/assets/img/command.png")).getKey()); @@ -191,6 +199,8 @@ public class RootPage extends DecoratorTabPage { .add(gameListItem) .add(gameItem) .add(downloadItem) + .startCategory(i18n("settings.launcher.general").toLowerCase()) +// .add(multiplayerItem) .add(launcherSettingsItem); // the root page, with the sidebar in left, navigator in center. diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java new file mode 100644 index 000000000..7c15f012c --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java @@ -0,0 +1,27 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2021 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.ui.multiplayer; + +public class MultiplayerManager { + + enum State { + DISCONNECTED, + MASTER, + SLAVE + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java new file mode 100644 index 000000000..e3edf4e58 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java @@ -0,0 +1,86 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2021 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.ui.multiplayer; + +import de.javawi.jstun.test.DiscoveryInfo; +import de.javawi.jstun.test.DiscoveryTest; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.property.ReadOnlyObjectWrapper; +import javafx.beans.property.SimpleObjectProperty; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class MultiplayerPage extends Control implements DecoratorPage { + private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("multiplayer"), -1)); + + private final ObjectProperty multiplayerState = new SimpleObjectProperty<>(); + private final ReadOnlyObjectWrapper natState = new ReadOnlyObjectWrapper<>(); + + public MultiplayerPage() { + testNAT(); + } + + @Override + protected Skin createDefaultSkin() { + return new MultiplayerPageSkin(this); + } + + public MultiplayerManager.State getMultiplayerState() { + return multiplayerState.get(); + } + + public ObjectProperty multiplayerStateProperty() { + return multiplayerState; + } + + public void setMultiplayerState(MultiplayerManager.State multiplayerState) { + this.multiplayerState.set(multiplayerState); + } + + public DiscoveryInfo getNatState() { + return natState.get(); + } + + public ReadOnlyObjectProperty natStateProperty() { + return natState.getReadOnlyProperty(); + } + + public void testNAT() { + Task.supplyAsync(() -> { + DiscoveryTest tester = new DiscoveryTest(null, 0, "stun.qq.com", 3478); + return tester.test(); + }).whenComplete(Schedulers.javafx(), (info, exception) -> { + if (exception == null) { + natState.set(info); + } else { + natState.set(null); + } + }).start(); + } + + @Override + public ReadOnlyObjectProperty stateProperty() { + return state; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java new file mode 100644 index 000000000..dc39acc0f --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java @@ -0,0 +1,174 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2021 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.ui.multiplayer; + +import de.javawi.jstun.test.DiscoveryInfo; +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.SkinBase; +import javafx.scene.layout.*; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.util.javafx.BindingMapping; + +import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class MultiplayerPageSkin extends SkinBase { + + /** + * Constructor for all SkinBase instances. + * + * @param control The control for which this Skin should attach to. + */ + protected MultiplayerPageSkin(MultiplayerPage control) { + super(control); + + BorderPane root = new BorderPane(); + root.setPadding(new Insets(10)); + getChildren().setAll(root); + { + VBox roomPane = new VBox(); + { + AdvancedListItem createRoomItem = new AdvancedListItem(); + createRoomItem.setTitle(i18n("multiplayer.room.create")); + createRoomItem.setLeftGraphic(wrap(SVG.plusCircleOutline(null, 24, 24))); + createRoomItem.setOnAction(e -> FXUtils.openLink("")); + + AdvancedListItem joinRoomItem = new AdvancedListItem(); + joinRoomItem.setTitle(i18n("multiplayer.room.join")); + joinRoomItem.setLeftGraphic(wrap(SVG.accountArrowRightOutline(null, 24, 24))); + joinRoomItem.setOnAction(e -> FXUtils.openLink("")); + + AdvancedListItem copyLinkItem = new AdvancedListItem(); + copyLinkItem.setTitle(i18n("multiplayer.room.copy_room_code")); + copyLinkItem.setLeftGraphic(wrap(SVG.accountArrowRightOutline(null, 24, 24))); + copyLinkItem.setOnAction(e -> FXUtils.openLink("")); + + AdvancedListItem quitItem = new AdvancedListItem(); + quitItem.setTitle(i18n("multiplayer.room.quit")); + quitItem.setLeftGraphic(wrap(SVG.closeCircle(null, 24, 24))); + quitItem.setOnAction(e -> FXUtils.openLink("")); + + AdvancedListItem closeRoomItem = new AdvancedListItem(); + closeRoomItem.setTitle(i18n("multiplayer.room.quit")); + closeRoomItem.setLeftGraphic(wrap(SVG.closeCircle(null, 24, 24))); + closeRoomItem.setOnAction(e -> FXUtils.openLink("")); + + FXUtils.onChangeAndOperate(getSkinnable().multiplayerStateProperty(), state -> { + if (state == MultiplayerManager.State.DISCONNECTED) { + roomPane.getChildren().setAll(createRoomItem, joinRoomItem); + } else if (state == MultiplayerManager.State.MASTER) { + roomPane.getChildren().setAll(copyLinkItem); + roomPane.getChildren().setAll(closeRoomItem); + } else if (state == MultiplayerManager.State.SLAVE) { + roomPane.getChildren().setAll(copyLinkItem); + roomPane.getChildren().setAll(quitItem); + } + }); + } + + AdvancedListBox sideBar = new AdvancedListBox() + .startCategory("multiplayer.room") + .add(roomPane) + .startCategory("help") + .addNavigationDrawerItem(settingsItem -> { + settingsItem.setTitle(i18n("help")); + settingsItem.setLeftGraphic(wrap(SVG.gamepad(null, 20, 20))); + settingsItem.setOnAction(e -> FXUtils.openLink("")); + }); + FXUtils.setLimitWidth(sideBar, 200); + root.setLeft(sideBar); + } + + { + VBox content = new VBox(16); + content.setFillWidth(true); + ScrollPane scrollPane = new ScrollPane(content); + scrollPane.setFitToWidth(true); + scrollPane.setFitToHeight(true); + root.setCenter(scrollPane); + + ComponentList roomPane = new ComponentList(); + { + VBox pane = new VBox(); + + } + + ComponentList natDetectionPane = new ComponentList(); + { + GridPane pane = new GridPane(); + ColumnConstraints title = new ColumnConstraints(); + ColumnConstraints value = new ColumnConstraints(); + pane.getColumnConstraints().setAll(title, value); + value.setFillWidth(true); + value.setHgrow(Priority.ALWAYS); + pane.setHgap(16); + pane.setVgap(8); + + HintPane hintPane = new HintPane(MessageDialogPane.MessageType.INFORMATION); + hintPane.setText(i18n("multiplayer.nat.hint")); + GridPane.setColumnSpan(hintPane, 2); + pane.addRow(0, hintPane); + + Label natResult = new Label(); + natResult.textProperty().bind(BindingMapping.of(getSkinnable().natStateProperty()) + .map(MultiplayerPageSkin::getNATType)); + pane.addRow(1, new Label(i18n("multiplayer.nat.type")), natResult); + +// Label natResult = new Label(); +// natResult.textProperty().bind(BindingMapping.of(getSkinnable().natStateProperty()) +// .map(MultiplayerPageSkin::getNATType)); +// pane.addRow(1, new Label(i18n("multiplayer.nat.latency")), natResult); + + natDetectionPane.getContent().add(pane); + } + + content.getChildren().setAll( + ComponentList.createComponentListTitle(i18n("multiplayer.room")), + roomPane, + ComponentList.createComponentListTitle(i18n("multiplayer.nat")), + natDetectionPane + ); + } + } + + private static String getNATType(DiscoveryInfo info) { + if (info == null) { + return i18n("multiplayer.nat.testing"); + } else if (info.isBlockedUDP()) { + return i18n("multiplayer.nat.type.blocked_udp"); + } else if (info.isFullCone()) { + return i18n("multiplayer.nat.type.full_cone"); + } else if (info.isOpenAccess()) { + return i18n("multiplayer.nat.type.open_access"); + } else if (info.isPortRestrictedCone()) { + return i18n("multiplayer.nat.type.port_restricted_cone"); + } else if (info.isRestrictedCone()) { + return i18n("multiplayer.nat.type.restricted_cone"); + } else if (info.isSymmetric()) { + return i18n("multiplayer.nat.type.symmetric"); + } else if (info.isSymmetricUDPFirewall()) { + return i18n("multiplayer.nat.type.symmetric_udp_firewall"); + } else { + return i18n("multiplayer.nat.type.unknown"); + } + } +} diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 17c0910f0..5abac1ee1 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -450,6 +450,35 @@ mods.name=Name mods.not_modded=You should install a modloader first (Fabric, Forge or LiteLoader) mods.url=Official Page +multiplayer=Multiplayer +multiplayer.nat=Network Type Detection +multiplayer.nat.hint=Network type detection will make it clear whether your network fulfills our requirement for multiplayer mode. +multiplayer.nat.latency=Latency +multiplayer.nat.not_yet_tested=Not yet testsed +multiplayer.nat.packet_loss_ratio=Packet Loss Ratio +multiplayer.nat.testing=Testing +multiplayer.nat.type=NAT Type +multiplayer.nat.type.blocked_udp=Very bad (Blocked UDP) +multiplayer.nat.type.full_cone=Medium (Full Cone) +multiplayer.nat.type.open_access=Good (Open Access) +multiplayer.nat.type.port_restricted_cone=Medium (Port Restricted Cone) +multiplayer.nat.type.restricted_cone=Medium (Restricted Cone) +multiplayer.nat.type.symmetric=Bad (Symmetric) +multiplayer.nat.type.symmetric_udp_firewall=Bad (Symmetric with UDP Firewall) +multiplayer.nat.type.unknown=Unknownn +multiplayer.room=Room +multiplayer.room.name.format=%1$s's Room +multiplayer.room.close=Close Room +multiplayer.room.copy_room_code=Copy Invitation Code +multiplayer.room.create=Create Room +multiplayer.room.error.port=Cannot detect game port, you must click "Open LAN Server" in game to enable multiplayer functionality. +multiplayer.room.hint=You must click "Open LAN Server" in game in order to enable multiplayer functionality. +multiplayer.room.join=Join Room +multiplayer.room.members=Room Members +multiplayer.room.port.prompt=Enter game port +multiplayer.room.quit=Quit Room +multiplayer.room.username=Username + datapack=Datapacks datapack.add=Install datapack datapack.choose_datapack=Choose the datapack zip to import diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index bf65cf480..1816309b0 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -426,6 +426,35 @@ mods.mangage=模組管理 mods.name=名稱 mods.not_modded=你需要先在自動安裝頁面安裝 Fabric、Forge 或 LiteLoader 才能進行模組管理。 +multiplayer=聯機 +multiplayer.nat=網路檢測 +multiplayer.nat.hint=執行網路檢測可以讓你更清楚裡的網路狀況是否符合聯機功能的需求。不符合聯機功能運行條件的網路狀況將可能導致聯機失敗。 +multiplayer.nat.latency=延遲 +multiplayer.nat.not_yet_tested=尚未檢測 +multiplayer.nat.packet_loss_ratio=丟包率 +multiplayer.nat.testing=檢測中 +multiplayer.nat.type=NAT 類型 +multiplayer.nat.type.blocked_udp=極差(網路禁止 UDP 協議) +multiplayer.nat.type.full_cone=中(完全圓錐型) +multiplayer.nat.type.open_access=好(公網開放型) +multiplayer.nat.type.port_restricted_cone=中(埠受限圓錐型) +multiplayer.nat.type.restricted_cone=中(受限圓錐型) +multiplayer.nat.type.symmetric=差(對稱型) +multiplayer.nat.type.symmetric_udp_firewall=差(對稱型+防火牆) +multiplayer.nat.type.unknown=未知 +multiplayer.room=房間 +multiplayer.room.name.format=%1$s 的房間 +multiplayer.room.close=關閉房間 +multiplayer.room.copy_room_code=複製邀請碼 +multiplayer.room.create=創建房間 +multiplayer.room.error.port=無法檢測遊戲埠號,你必須先啟動遊戲並在遊戲內打開對區域網路開放選項後才能啟動聯機。 +multiplayer.room.hint=你必須先啟動遊戲並在遊戲內打開對區域網路開放選項後才能創建房間 +multiplayer.room.join=加入房間 +multiplayer.room.members=房間成員 +multiplayer.room.port.prompt=輸入埠號 +multiplayer.room.quit=退出房間 +multiplayer.room.username=使用者名稱 + datapack=資料包 datapack.add=加入資料包 datapack.choose_datapack=選擇要匯入的資料包壓縮檔 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 820be427c..5d241a5f9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -469,7 +469,26 @@ multiplayer.nat.not_yet_tested=尚未检测 multiplayer.nat.packet_loss_ratio=丢包率 multiplayer.nat.testing=检测中 multiplayer.nat.type=NAT 类型 +multiplayer.nat.type.blocked_udp=极差(网络禁止 UDP 协议) +multiplayer.nat.type.full_cone=中(完全圆锥型) +multiplayer.nat.type.open_access=好(公网开放型) +multiplayer.nat.type.port_restricted_cone=中(端口受限圆锥型) +multiplayer.nat.type.restricted_cone=中(受限圆锥型) +multiplayer.nat.type.symmetric=差(对称型) +multiplayer.nat.type.symmetric_udp_firewall=差(对称型+防火墙) +multiplayer.nat.type.unknown=未知 multiplayer.room=房间 +multiplayer.room.name.format=%1$s 的房间 +multiplayer.room.close=关闭房间 +multiplayer.room.copy_room_code=复制邀请码 +multiplayer.room.create=创建房间 +multiplayer.room.error.port=无法检测游戏端口号,你必须先启动游戏并在游戏内打开对局域网开放选项后才能启动联机。 +multiplayer.room.hint=你必须先启动游戏并在游戏内打开对局域网开放选项后才能创建房间 +multiplayer.room.join=加入房间 +multiplayer.room.members=房间成员 +multiplayer.room.port.prompt=输入端口号 +multiplayer.room.quit=退出房间 +multiplayer.room.username=用户名 datapack=数据包 datapack.add=添加数据包 diff --git a/build.gradle b/build.gradle index 64219ae39..96cc4ff64 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,10 @@ subprojects { sourceSets = [] } + tasks.withType(Checkstyle) { + exclude 'de/javawi/jstun' + } + sourceCompatibility = 1.8 compileJava.options.encoding = "UTF-8" compileTestJava.options.encoding = "UTF-8" diff --git a/settings.gradle b/settings.gradle index 28fbb873a..d5019c6d4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,4 +2,4 @@ rootProject.name = 'HMCL3' include ':HMCL' include ':HMCLCore' include ':HMCLTransformerDiscoveryService' - +include ':JSTUN' From 56bd2e0d9557159dcaa7f3baf90c10a06290772e Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Mon, 6 Sep 2021 10:53:22 +0800 Subject: [PATCH 14/21] feat: move auth servers to sidebar --- HMCL/.attach_pid426462 | 0 .../org/jackhuang/hmcl/ui/Controllers.java | 11 +- .../main/java/org/jackhuang/hmcl/ui/SVG.java | 6 + .../hmcl/ui/account/AccountListPage.java | 124 +++++++++++++----- .../ui/account/AuthlibInjectorServerItem.java | 67 ---------- .../account/AuthlibInjectorServersPage.java | 56 -------- .../hmcl/ui/account/CreateAccountPane.java | 21 +-- .../ui/construct/AdvancedListItemSkin.java | 1 - .../ui/profile/ProfileAdvancedListItem.java | 52 -------- HMCL/src/main/resources/assets/css/root.css | 2 +- 10 files changed, 109 insertions(+), 231 deletions(-) create mode 100644 HMCL/.attach_pid426462 delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServerItem.java delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java diff --git a/HMCL/.attach_pid426462 b/HMCL/.attach_pid426462 new file mode 100644 index 000000000..e69de29bb 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 ac4759256..6a77deabd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -36,7 +36,6 @@ import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.account.AccountListPage; -import org.jackhuang.hmcl.ui.account.AuthlibInjectorServersPage; import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.construct.InputDialogPane; import org.jackhuang.hmcl.ui.construct.MessageDialogPane; @@ -85,7 +84,6 @@ public final class Controllers { }); return gameListPage; }); - private static AuthlibInjectorServersPage serversPage = null; private static Lazy rootPage = new Lazy<>(RootPage::new); private static DecoratorController decorator; private static Lazy modDownloadListPage = new Lazy<>(() -> { @@ -100,6 +98,7 @@ public final class Controllers { AccountListPage accountListPage = new AccountListPage(); accountListPage.selectedAccountProperty().bindBidirectional(Accounts.selectedAccountProperty()); accountListPage.accountsProperty().bindContent(Accounts.accountsProperty()); + accountListPage.authServersProperty().bindContentBidirectional(config().getAuthlibInjectorServers()); return accountListPage; }); private static Lazy multiplayerPage = new Lazy<>(MultiplayerPage::new); @@ -131,13 +130,6 @@ public final class Controllers { return rootPage.get(); } - // FXThread - public static AuthlibInjectorServersPage getServersPage() { - if (serversPage == null) - serversPage = new AuthlibInjectorServersPage(); - return serversPage; - } - // FXThread public static ModDownloadListPage getModpackDownloadListPage() { return modDownloadListPage.get(); @@ -287,7 +279,6 @@ public final class Controllers { public static void shutdown() { rootPage = null; versionPage = null; - serversPage = null; gameListPage = null; settingsPage = null; modDownloadListPage = null; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index 97068cf69..3e3d56393 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -438,4 +438,10 @@ public final class SVG { "M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z", fill, width, height); } + + public static Node server(ObjectBinding fill, double width, double height) { + return createSVGPath( + "M13,19H14A1,1 0 0,1 15,20H22V22H15A1,1 0 0,1 14,23H10A1,1 0 0,1 9,22H2V20H9A1,1 0 0,1 10,19H11V17H4A1,1 0 0,1 3,16V12A1,1 0 0,1 4,11H20A1,1 0 0,1 21,12V16A1,1 0 0,1 20,17H13V19M4,3H20A1,1 0 0,1 21,4V8A1,1 0 0,1 20,9H4A1,1 0 0,1 3,8V4A1,1 0 0,1 4,3M9,7H10V5H9V7M9,15H10V13H9V15M5,5V7H7V5H5M5,13V15H7V13H5Z", + fill, width, height); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java index e95a41786..51be0d003 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java @@ -17,24 +17,31 @@ */ package org.jackhuang.hmcl.ui.account; +import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXScrollPane; import javafx.beans.binding.Bindings; import javafx.beans.property.*; +import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.scene.control.ScrollPane; import javafx.scene.control.Skin; import javafx.scene.control.SkinBase; +import javafx.scene.control.Tooltip; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.ListPageBase; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.construct.AdvancedListBox; +import org.jackhuang.hmcl.ui.construct.AdvancedListItem; +import org.jackhuang.hmcl.ui.construct.ClassTitle; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.util.i18n.I18n; +import org.jackhuang.hmcl.util.javafx.BindingMapping; import org.jackhuang.hmcl.util.javafx.MappedObservableList; import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap; @@ -44,6 +51,7 @@ import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.createSelectedIt public class AccountListPage extends ListPageBase implements DecoratorPage { private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("account.manage"), -1)); private final ListProperty accounts = new SimpleListProperty<>(this, "accounts", FXCollections.observableArrayList()); + private final ListProperty authServers = new SimpleListProperty<>(this, "authServers", FXCollections.observableArrayList()); private final ObjectProperty selectedAccount; public AccountListPage() { @@ -64,50 +72,98 @@ public class AccountListPage extends ListPageBase implements De return state.getReadOnlyProperty(); } + public ListProperty authServersProperty() { + return authServers; + } + @Override protected Skin createDefaultSkin() { return new AccountListPageSkin(this); } private static class AccountListPageSkin extends SkinBase { + + private final ObservableList authServerItems; + public AccountListPageSkin(AccountListPage skinnable) { super(skinnable); BorderPane root = new BorderPane(); { - AdvancedListBox sideBar = new AdvancedListBox() - .startCategory(i18n("account.create")) - .addNavigationDrawerItem(settingsItem -> { - settingsItem.setTitle(i18n("account.methods.offline")); - settingsItem.setLeftGraphic(wrap(SVG.account(null, 20, 20))); - settingsItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_OFFLINE))); - }) - .addNavigationDrawerItem(settingsItem -> { - settingsItem.setTitle(i18n("account.methods.yggdrasil")); - settingsItem.setLeftGraphic(wrap(SVG.mojang(null, 20, 20))); - settingsItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MOJANG))); - }) - .addNavigationDrawerItem(settingsItem -> { - settingsItem.setTitle(i18n("account.methods.microsoft")); - settingsItem.setLeftGraphic(wrap(SVG.microsoft(null, 20, 20))); - settingsItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MICROSOFT))); - }) - .addNavigationDrawerItem(settingsItem -> { - settingsItem.setTitle(i18n("account.methods.authlib_injector")); - if (I18n.hasKey("account.methods.authlib_injector.subtitle")) { - settingsItem.setSubtitle(i18n("account.methods.authlib_injector.subtitle")); - } - settingsItem.setLeftGraphic(wrap(SVG.gear(null, 20, 20))); - settingsItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_AUTHLIB_INJECTOR))); - }) - .addNavigationDrawerItem(settingsItem -> { - settingsItem.setTitle(i18n("account.create")); - settingsItem.setLeftGraphic(wrap(SVG.plus(null, 20, 20))); - settingsItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane())); - }); - FXUtils.setLimitWidth(sideBar, 200); - root.setLeft(sideBar); + VBox left = new VBox(); + left.getStyleClass().add("advanced-list-box-content"); + + left.getChildren().add(new ClassTitle(i18n("account.create"))); + + AdvancedListItem offlineItem = new AdvancedListItem(); + offlineItem.getStyleClass().add("navigation-drawer-item"); + offlineItem.setActionButtonVisible(false); + offlineItem.setTitle(i18n("account.methods.offline")); + offlineItem.setLeftGraphic(wrap(SVG.account(Theme.blackFillBinding(), 24, 24))); + offlineItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_OFFLINE))); + left.getChildren().add(offlineItem); + + AdvancedListItem mojangItem = new AdvancedListItem(); + mojangItem.getStyleClass().add("navigation-drawer-item"); + mojangItem.setActionButtonVisible(false); + mojangItem.setTitle(i18n("account.methods.yggdrasil")); + mojangItem.setLeftGraphic(wrap(SVG.mojang(Theme.blackFillBinding(), 24, 24))); + mojangItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MOJANG))); + left.getChildren().add(mojangItem); + + AdvancedListItem microsoftItem = new AdvancedListItem(); + microsoftItem.getStyleClass().add("navigation-drawer-item"); + microsoftItem.setActionButtonVisible(false); + microsoftItem.setTitle(i18n("account.methods.microsoft")); + microsoftItem.setLeftGraphic(wrap(SVG.microsoft(Theme.blackFillBinding(), 24, 24))); + microsoftItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MICROSOFT))); + left.getChildren().add(microsoftItem); + + left.getChildren().add(new ClassTitle(i18n("account.methods.authlib_injector"))); + + { + VBox wrapper = new VBox(); + FXUtils.setLimitWidth(wrapper, 200); + + VBox box = new VBox(); + authServerItems = MappedObservableList.create(skinnable.authServersProperty(), server -> { + AdvancedListItem item = new AdvancedListItem(); + item.getStyleClass().add("navigation-drawer-item"); + item.setLeftGraphic(SVG.server(null, 20, 20)); + item.setOnAction(e -> Controllers.dialog(new CreateAccountPane(server))); + + JFXButton btnRemove = new JFXButton(); + btnRemove.setOnAction(e -> skinnable.authServersProperty().remove(server)); + btnRemove.getStyleClass().add("toggle-icon4"); + btnRemove.setGraphic(SVG.close(Theme.blackFillBinding(), 14, 14)); + item.setRightGraphic(btnRemove); + + ObservableValue title = BindingMapping.of(server, AuthlibInjectorServer::getName); + String url = server.getUrl(); + item.titleProperty().bind(title); + item.subtitleProperty().set(url); + Tooltip tooltip = new Tooltip(); + tooltip.textProperty().bind(Bindings.format("%s (%s)", title, url)); + FXUtils.installFastTooltip(item, tooltip); + + return item; + }); + Bindings.bindContent(box.getChildren(), authServerItems); + + AdvancedListItem addAuthServerItem = new AdvancedListItem(); + addAuthServerItem.getStyleClass().add("navigation-drawer-item"); + addAuthServerItem.setTitle(i18n("account.injector.add")); + addAuthServerItem.setActionButtonVisible(false); + addAuthServerItem.setLeftGraphic(SVG.plusCircleOutline(Theme.blackFillBinding(), 24, 24)); + addAuthServerItem.setOnAction(e -> Controllers.dialog(new AddAuthlibInjectorServerPane())); + + wrapper.getChildren().addAll(box, addAuthServerItem); + left.getChildren().add(new ScrollPane(wrapper)); + } + + FXUtils.setLimitWidth(left, 200); + root.setLeft(left); } ScrollPane scrollPane = new ScrollPane(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServerItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServerItem.java deleted file mode 100644 index ebae6ad26..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServerItem.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 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.ui.account; - -import com.jfoenix.controls.JFXButton; -import com.jfoenix.effects.JFXDepthManager; - -import javafx.beans.binding.Bindings; -import javafx.geometry.Pos; -import javafx.scene.control.Label; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.VBox; -import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; -import org.jackhuang.hmcl.setting.Theme; -import org.jackhuang.hmcl.ui.SVG; - -import java.util.function.Consumer; - -public final class AuthlibInjectorServerItem extends BorderPane { - private final AuthlibInjectorServer server; - - private final Label lblServerName = new Label(); - private final Label lblServerUrl = new Label(); - - public AuthlibInjectorServerItem(AuthlibInjectorServer server, Consumer deleteCallback) { - this.server = server; - - lblServerName.setStyle("-fx-font-size: 15;"); - lblServerUrl.setStyle("-fx-font-size: 10;"); - - VBox center = new VBox(); - BorderPane.setAlignment(center, Pos.CENTER); - center.getChildren().addAll(lblServerName, lblServerUrl); - setCenter(center); - - JFXButton right = new JFXButton(); - right.setOnMouseClicked(e -> deleteCallback.accept(this)); - right.getStyleClass().add("toggle-icon4"); - BorderPane.setAlignment(right, Pos.CENTER); - right.setGraphic(SVG.close(Theme.blackFillBinding(), 15, 15)); - setRight(right); - - setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"); - JFXDepthManager.setDepth(this, 1); - lblServerName.textProperty().bind(Bindings.createStringBinding(server::getName, server)); - lblServerUrl.setText(server.getUrl()); - } - - public AuthlibInjectorServer getServer() { - return server; - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java deleted file mode 100644 index d10aa329e..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AuthlibInjectorServersPage.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 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.ui.account; - -import javafx.beans.binding.Bindings; -import javafx.beans.property.ReadOnlyObjectWrapper; -import javafx.collections.ObservableList; -import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; -import org.jackhuang.hmcl.ui.Controllers; -import org.jackhuang.hmcl.ui.ListPage; -import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.util.javafx.MappedObservableList; - -import static org.jackhuang.hmcl.setting.ConfigHolder.config; -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; - -public class AuthlibInjectorServersPage extends ListPage implements DecoratorPage { - private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("account.injector.manage.title"))); - - private final ObservableList serverItems; - - public AuthlibInjectorServersPage() { - serverItems = MappedObservableList.create(config().getAuthlibInjectorServers(), this::createServerItem); - Bindings.bindContent(itemsProperty(), serverItems); - } - - private AuthlibInjectorServerItem createServerItem(AuthlibInjectorServer server) { - return new AuthlibInjectorServerItem(server, - item -> config().getAuthlibInjectorServers().remove(item.getServer())); - } - - @Override - public void add() { - Controllers.dialog(new AddAuthlibInjectorServerPane()); - } - - @Override - public ReadOnlyObjectWrapper stateProperty() { - return state; - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java index a176e38f4..d3d1260f1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java @@ -78,7 +78,7 @@ public class CreateAccountPane extends JFXDialogLayout { private TaskExecutor loginTask; public CreateAccountPane() { - this(null); + this((AccountFactory) null); } public CreateAccountPane(AccountFactory factory) { @@ -170,6 +170,11 @@ public class CreateAccountPane extends JFXDialogLayout { setPrefWidth(560); } + public CreateAccountPane(AuthlibInjectorServer authserver) { + this(Accounts.FACTORY_AUTHLIB_INJECTOR); + ((AccountDetailsInputPane) detailsPane).selectAuthServer(authserver); + } + private void onAccept() { spinner.showSpinner(); lblErrorMessage.setText(""); @@ -322,15 +327,7 @@ public class CreateAccountPane extends JFXDialogLayout { Controllers.dialog(new AddAuthlibInjectorServerPane()); }); - JFXButton btnManageServers = new JFXButton(); - btnManageServers.setGraphic(SVG.gear(null, 20, 20)); - btnManageServers.getStyleClass().add("toggle-icon4"); - btnManageServers.setOnAction(e -> { - fireEvent(new DialogCloseEvent()); - Controllers.navigate(Controllers.getServersPage()); - }); - - HBox boxServers = new HBox(cboServers, linksContainer, btnAddServer, btnManageServers); + HBox boxServers = new HBox(cboServers, linksContainer, btnAddServer); add(boxServers, 1, rowIndex); rowIndex++; @@ -422,6 +419,10 @@ public class CreateAccountPane extends JFXDialogLayout { public BooleanBinding validProperty() { return valid; } + + public void selectAuthServer(AuthlibInjectorServer authserver) { + cboServers.getSelectionModel().select(authserver); + } } private static class DialogCharacterSelector extends BorderPane implements CharacterSelector { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java index 074a73bd1..148e74291 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java @@ -62,7 +62,6 @@ public class AdvancedListItemSkin extends SkinBase { HBox right = new HBox(); right.setAlignment(Pos.CENTER); - right.setMouseTransparent(true); right.getStyleClass().add("toggle-icon4"); FXUtils.setLimitWidth(right, 40); FXUtils.onChangeAndOperate(skinnable.rightGraphicProperty(), diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java deleted file mode 100644 index 811e42053..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfileAdvancedListItem.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 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.ui.profile; - -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import org.jackhuang.hmcl.setting.Profile; -import org.jackhuang.hmcl.setting.Profiles; -import org.jackhuang.hmcl.setting.Theme; -import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.construct.AdvancedListItem; - -import static org.jackhuang.hmcl.ui.FXUtils.newImage; - -public class ProfileAdvancedListItem extends AdvancedListItem { - private ObjectProperty profile = new SimpleObjectProperty() { - - @Override - protected void invalidated() { - Profile profile = get(); - if (profile == null) { - } else { - setTitle(Profiles.getProfileDisplayName(profile)); - setSubtitle(profile.getGameDir().toString()); - } - } - }; - - public ProfileAdvancedListItem() { - setLeftGraphic(createImageView(newImage("/assets/img/craft_table.png")).getKey()); - setRightGraphic(SVG.viewList(Theme.blackFillBinding(), -1, -1)); - } - - public ObjectProperty profileProperty() { - return profile; - } -} diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 4ec9bc6dd..5f15611eb 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -162,7 +162,7 @@ } .advanced-list-box-content { - -fx-padding: 12 0 12 0; + -fx-padding: 12 0 0 0; -fx-spacing: 0; } From 6a2ffa4f30f1ab6630f87b39d348d3abb13f6fb6 Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Mon, 6 Sep 2021 13:04:54 +0800 Subject: [PATCH 15/21] fix: inconsistent sidebar icon size & color --- .../hmcl/ui/download/DownloadPage.java | 13 +++++++------ .../hmcl/ui/main/LauncherSettingsPage.java | 15 ++++++++------- .../hmcl/ui/versions/VersionPage.java | 18 +++++++++--------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index 2402d3a85..c06aefd41 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -24,6 +24,7 @@ import org.jackhuang.hmcl.mod.curse.CurseAddon; import org.jackhuang.hmcl.mod.curse.CurseModManager; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; @@ -85,37 +86,37 @@ public class DownloadPage extends BorderPane implements DecoratorPage { AdvancedListBox sideBar = new AdvancedListBox() .addNavigationDrawerItem(item -> { item.setTitle(i18n("install.new_game")); - item.setLeftGraphic(wrap(SVG.gamepad(null, 20, 20))); + item.setLeftGraphic(wrap(SVG.gamepad(Theme.blackFillBinding(), 24, 24))); item.setOnAction(e -> Versions.addNewGame()); }) .startCategory(i18n("download")) .addNavigationDrawerItem(item -> { item.setTitle(i18n("mods")); - item.setLeftGraphic(wrap(SVG.puzzle(null, 20, 20))); + item.setLeftGraphic(wrap(SVG.puzzle(Theme.blackFillBinding(), 24, 24))); item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(modTab)); item.setOnAction(e -> tab.getSelectionModel().select(modTab)); }) .addNavigationDrawerItem(settingsItem -> { settingsItem.setTitle(i18n("modpack")); - settingsItem.setLeftGraphic(wrap(SVG.pack(null, 20, 20))); + settingsItem.setLeftGraphic(wrap(SVG.pack(Theme.blackFillBinding(), 24, 24))); settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(modpackTab)); settingsItem.setOnAction(e -> tab.getSelectionModel().select(modpackTab)); }) .addNavigationDrawerItem(item -> { item.setTitle(i18n("resourcepack")); - item.setLeftGraphic(wrap(SVG.textureBox(null, 20, 20))); + item.setLeftGraphic(wrap(SVG.textureBox(Theme.blackFillBinding(), 24, 24))); item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(resourcePackTab)); item.setOnAction(e -> tab.getSelectionModel().select(resourcePackTab)); }) // .addNavigationDrawerItem(item -> { // item.setTitle(i18n("download.curseforge.customization")); -// item.setLeftGraphic(wrap(SVG.script(null, 20, 20))); +// item.setLeftGraphic(wrap(SVG.script(Theme.blackFillBinding(), 24, 24))); // item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(customizationTab)); // item.setOnAction(e -> tab.getSelectionModel().select(customizationTab)); // }) .addNavigationDrawerItem(item -> { item.setTitle(i18n("world")); - item.setLeftGraphic(wrap(SVG.earth(null, 20, 20))); + item.setLeftGraphic(wrap(SVG.earth(Theme.blackFillBinding(), 24, 24))); item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(worldTab)); item.setOnAction(e -> tab.getSelectionModel().select(worldTab)); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java index 44c1df8fa..18882e491 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java @@ -22,6 +22,7 @@ import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.scene.layout.BorderPane; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.animation.ContainerAnimations; @@ -68,45 +69,45 @@ public class LauncherSettingsPage extends BorderPane implements DecoratorPage { AdvancedListBox sideBar = new AdvancedListBox() .addNavigationDrawerItem(settingsItem -> { settingsItem.setTitle(i18n("settings.type.global.manage")); - settingsItem.setLeftGraphic(wrap(SVG.gamepad(null, 20, 20))); + settingsItem.setLeftGraphic(wrap(SVG.gamepad(Theme.blackFillBinding(), 24, 24))); settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(gameTab)); settingsItem.setOnAction(e -> tab.getSelectionModel().select(gameTab)); }) .startCategory(i18n("launcher")) .addNavigationDrawerItem(settingsItem -> { settingsItem.setTitle(i18n("settings.launcher.general")); - settingsItem.setLeftGraphic(wrap(SVG.applicationOutline(null, 20, 20))); + settingsItem.setLeftGraphic(wrap(SVG.applicationOutline(Theme.blackFillBinding(), 24, 24))); settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(settingsTab)); settingsItem.setOnAction(e -> tab.getSelectionModel().select(settingsTab)); }) .addNavigationDrawerItem(personalizationItem -> { personalizationItem.setTitle(i18n("settings.launcher.appearance")); - personalizationItem.setLeftGraphic(wrap(SVG.styleOutline(null, 20, 20))); + personalizationItem.setLeftGraphic(wrap(SVG.styleOutline(Theme.blackFillBinding(), 24, 24))); personalizationItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(personalizationTab)); personalizationItem.setOnAction(e -> tab.getSelectionModel().select(personalizationTab)); }) .addNavigationDrawerItem(downloadItem -> { downloadItem.setTitle(i18n("download")); - downloadItem.setLeftGraphic(wrap(SVG.downloadOutline(null, 20, 20))); + downloadItem.setLeftGraphic(wrap(SVG.downloadOutline(Theme.blackFillBinding(), 24, 24))); downloadItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(downloadTab)); downloadItem.setOnAction(e -> tab.getSelectionModel().select(downloadTab)); }) .startCategory(i18n("help")) .addNavigationDrawerItem(helpItem -> { helpItem.setTitle(i18n("help")); - helpItem.setLeftGraphic(wrap(SVG.helpCircleOutline(null, 20, 20))); + helpItem.setLeftGraphic(wrap(SVG.helpCircleOutline(Theme.blackFillBinding(), 24, 24))); helpItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(helpTab)); helpItem.setOnAction(e -> tab.getSelectionModel().select(helpTab)); }) .addNavigationDrawerItem(sponsorItem -> { sponsorItem.setTitle(i18n("sponsor")); - sponsorItem.setLeftGraphic(wrap(SVG.handHearOutline(null, 20, 20))); + sponsorItem.setLeftGraphic(wrap(SVG.handHearOutline(Theme.blackFillBinding(), 24, 24))); sponsorItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(sponsorTab)); sponsorItem.setOnAction(e -> tab.getSelectionModel().select(sponsorTab)); }) .addNavigationDrawerItem(aboutItem -> { aboutItem.setTitle(i18n("about")); - aboutItem.setLeftGraphic(wrap(SVG.informationOutline(null, 20, 20))); + aboutItem.setLeftGraphic(wrap(SVG.informationOutline(Theme.blackFillBinding(), 24, 24))); aboutItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(aboutTab)); aboutItem.setOnAction(e -> tab.getSelectionModel().select(aboutTab)); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java index ca918c77e..68c7ce050 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java @@ -244,7 +244,7 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa AdvancedListItem versionSettingsItem = new AdvancedListItem(); versionSettingsItem.getStyleClass().add("navigation-drawer-item"); versionSettingsItem.setTitle(i18n("settings.game")); - versionSettingsItem.setLeftGraphic(wrap(SVG.gearOutline(null, 20, 20))); + versionSettingsItem.setLeftGraphic(wrap(SVG.gearOutline(Theme.blackFillBinding(), 24, 24))); versionSettingsItem.setActionButtonVisible(false); versionSettingsItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.versionSettingsTab)); versionSettingsItem.setOnAction(e -> control.tab.getSelectionModel().select(control.versionSettingsTab)); @@ -252,7 +252,7 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa AdvancedListItem modListItem = new AdvancedListItem(); modListItem.getStyleClass().add("navigation-drawer-item"); modListItem.setTitle(i18n("mods.manage")); - modListItem.setLeftGraphic(wrap(SVG.puzzle(null, 20, 20))); + modListItem.setLeftGraphic(wrap(SVG.puzzle(Theme.blackFillBinding(), 24, 24))); modListItem.setActionButtonVisible(false); modListItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.modListTab)); modListItem.setOnAction(e -> control.tab.getSelectionModel().select(control.modListTab)); @@ -260,7 +260,7 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa AdvancedListItem curseModListItem = new AdvancedListItem(); curseModListItem.getStyleClass().add("navigation-drawer-item"); curseModListItem.setTitle(i18n("mods.download")); - curseModListItem.setLeftGraphic(wrap(SVG.fire(null, 20, 20))); + curseModListItem.setLeftGraphic(wrap(SVG.fire(Theme.blackFillBinding(), 24, 24))); curseModListItem.setActionButtonVisible(false); curseModListItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.curseModListTab)); curseModListItem.setOnAction(e -> control.tab.getSelectionModel().select(control.curseModListTab)); @@ -268,7 +268,7 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa AdvancedListItem installerListItem = new AdvancedListItem(); installerListItem.getStyleClass().add("navigation-drawer-item"); installerListItem.setTitle(i18n("settings.tabs.installers")); - installerListItem.setLeftGraphic(wrap(SVG.cube(null, 20, 20))); + installerListItem.setLeftGraphic(wrap(SVG.cube(Theme.blackFillBinding(), 24, 24))); installerListItem.setActionButtonVisible(false); installerListItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.installerListTab)); installerListItem.setOnAction(e -> control.tab.getSelectionModel().select(control.installerListTab)); @@ -276,7 +276,7 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa AdvancedListItem worldListItem = new AdvancedListItem(); worldListItem.getStyleClass().add("navigation-drawer-item"); worldListItem.setTitle(i18n("world.manage")); - worldListItem.setLeftGraphic(wrap(SVG.earth(null, 20, 20))); + worldListItem.setLeftGraphic(wrap(SVG.earth(Theme.blackFillBinding(), 24, 24))); worldListItem.setActionButtonVisible(false); worldListItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.worldListTab)); worldListItem.setOnAction(e -> control.tab.getSelectionModel().select(control.worldListTab)); @@ -320,7 +320,7 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa AdvancedListItem upgradeItem = new AdvancedListItem(); upgradeItem.getStyleClass().add("navigation-drawer-item"); upgradeItem.setTitle(i18n("version.update")); - upgradeItem.setLeftGraphic(wrap(SVG.update(Theme.blackFillBinding(), 20, 20))); + upgradeItem.setLeftGraphic(wrap(SVG.update(Theme.blackFillBinding(), 24, 24))); upgradeItem.setActionButtonVisible(false); upgradeItem.visibleProperty().bind(control.currentVersionUpgradable); upgradeItem.setOnAction(e -> control.updateGame()); @@ -328,21 +328,21 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa AdvancedListItem testGameItem = new AdvancedListItem(); testGameItem.getStyleClass().add("navigation-drawer-item"); testGameItem.setTitle(i18n("version.launch.test")); - testGameItem.setLeftGraphic(wrap(SVG.rocketLaunchOutline(Theme.blackFillBinding(), 20, 20))); + testGameItem.setLeftGraphic(wrap(SVG.rocketLaunchOutline(Theme.blackFillBinding(), 24, 24))); testGameItem.setActionButtonVisible(false); testGameItem.setOnAction(e -> control.testGame()); AdvancedListItem browseMenuItem = new AdvancedListItem(); browseMenuItem.getStyleClass().add("navigation-drawer-item"); browseMenuItem.setTitle(i18n("settings.game.exploration")); - browseMenuItem.setLeftGraphic(wrap(SVG.folderOutline(Theme.blackFillBinding(), 20, 20))); + browseMenuItem.setLeftGraphic(wrap(SVG.folderOutline(Theme.blackFillBinding(), 24, 24))); browseMenuItem.setActionButtonVisible(false); browseMenuItem.setOnAction(e -> browsePopup.show(browseMenuItem, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, browseMenuItem.getWidth(), 0)); AdvancedListItem managementItem = new AdvancedListItem(); managementItem.getStyleClass().add("navigation-drawer-item"); managementItem.setTitle(i18n("settings.game.management")); - managementItem.setLeftGraphic(wrap(SVG.wrenchOutline(Theme.blackFillBinding(), 20, 20))); + managementItem.setLeftGraphic(wrap(SVG.wrenchOutline(Theme.blackFillBinding(), 24, 24))); managementItem.setActionButtonVisible(false); managementItem.setOnAction(e -> managementPopup.show(managementItem, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, managementItem.getWidth(), 0)); From dc2911f7d88b3acc0e484926fff60551d3d19308 Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Mon, 6 Sep 2021 20:17:03 +0800 Subject: [PATCH 16/21] fix: build --- .github/workflows/gradle.yml | 12 +++++++----- JSTUN | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 6f0e014c4..6a3a65dc9 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -9,19 +9,21 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Checkout submodules + run: git submodule update --init --recursive - name: Set up JDK 1.8 uses: actions/setup-java@v1 with: java-version: 1.8 java-package: jdk+fx - - name: Check style main - run: ./gradlew checkstyleMain - - name: Check style test - run: ./gradlew checkstyleTest - name: Build with Gradle run: ./gradlew build - name: Upload Artifacts uses: actions/upload-artifact@v2 with: name: HMCL - path: HMCL/build/libs \ No newline at end of file + path: HMCL/build/libs + - name: Check style main + run: ./gradlew checkstyleMain + - name: Check style test + run: ./gradlew checkstyleTest diff --git a/JSTUN b/JSTUN index 6516d37c7..08ab1f848 160000 --- a/JSTUN +++ b/JSTUN @@ -1 +1 @@ -Subproject commit 6516d37c703b74d6dbbbdffdaa2a056ce2f2fdd5 +Subproject commit 08ab1f8483aba307931494e695e27cbde0cc2657 From 305133216557991f73a813834e40651e578a6d4b Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Mon, 6 Sep 2021 21:55:16 +0800 Subject: [PATCH 17/21] feat: continue launching after adding account --- .../org/jackhuang/hmcl/ui/main/RootPage.java | 35 +---------- .../jackhuang/hmcl/ui/versions/Versions.java | 59 ++++++++++++++----- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 6d14c88b6..c0e17e2f2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -17,7 +17,6 @@ */ package org.jackhuang.hmcl.ui.main; -import javafx.application.Platform; import javafx.scene.Node; import javafx.scene.control.SkinBase; import javafx.scene.layout.BorderPane; @@ -34,7 +33,6 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.account.AccountAdvancedListItem; -import org.jackhuang.hmcl.ui.account.CreateAccountPane; import org.jackhuang.hmcl.ui.construct.AdvancedListBox; import org.jackhuang.hmcl.ui.construct.AdvancedListItem; import org.jackhuang.hmcl.ui.construct.TabHeader; @@ -140,13 +138,7 @@ public class RootPage extends DecoratorTabPage { // first item in left sidebar AccountAdvancedListItem accountListItem = new AccountAdvancedListItem(); - accountListItem.setOnAction(e -> { - Controllers.navigate(Controllers.getAccountListPage()); - - if (Accounts.getAccounts().isEmpty()) { - Controllers.dialog(new CreateAccountPane()); - } - }); + accountListItem.setOnAction(e -> Controllers.navigate(Controllers.getAccountListPage())); accountListItem.accountProperty().bind(Accounts.selectedAccountProperty()); // second item in left sidebar @@ -220,27 +212,6 @@ public class RootPage extends DecoratorTabPage { } - // ==== Accounts ==== - - private boolean checkedAccont = false; - - public void checkAccount() { - if (checkedAccont) - return; - checkedAccont = true; - checkAccountForcibly(); - } - - public void checkAccountForcibly() { - if (Accounts.getAccounts().isEmpty()) - Platform.runLater(this::addNewAccount); - } - - private void addNewAccount() { - Controllers.dialog(new CreateAccountPane()); - } - // ==== - private boolean checkedModpack = false; private void onRefreshedVersions(HMCLGameRepository repository) { @@ -257,7 +228,7 @@ public class RootPage extends DecoratorTabPage { .thenApplyAsync(modpack -> ModpackHelper .getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack) - .withRunAsync(Schedulers.javafx(), this::checkAccount).executor()) + .executor()) .thenAcceptAsync(Schedulers.javafx(), executor -> { Controllers.taskDialog(executor, i18n("modpack.installing")); executor.start(); @@ -265,8 +236,6 @@ public class RootPage extends DecoratorTabPage { } } } - - checkAccount(); }); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 55812f1f4..1cd3c1271 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -18,7 +18,11 @@ package org.jackhuang.hmcl.ui.versions; import com.jfoenix.controls.JFXButton; + +import javafx.application.Platform; import javafx.stage.FileChooser; + +import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.download.game.GameAssetDownloadTask; import org.jackhuang.hmcl.game.GameDirectoryType; import org.jackhuang.hmcl.game.GameRepository; @@ -33,6 +37,8 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.account.CreateAccountPane; +import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.PromptDialogPane; import org.jackhuang.hmcl.ui.construct.Validator; @@ -50,6 +56,7 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; import java.util.logging.Level; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -185,7 +192,9 @@ public final class Versions { } public static void generateLaunchScript(Profile profile, String id) { - if (checkForLaunching(profile, id)) { + if (!checkVersionForLaunching(profile, id)) + return; + ensureSelectedAccount(account -> { GameRepository repository = profile.getRepository(); FileChooser chooser = new FileChooser(); if (repository.getRunDirectory(id).isDirectory()) @@ -196,33 +205,55 @@ public final class Versions { : new FileChooser.ExtensionFilter(i18n("extension.sh"), "*.sh")); File file = chooser.showSaveDialog(Controllers.getStage()); if (file != null) - new LauncherHelper(profile, Accounts.getSelectedAccount(), id).makeLaunchScript(file); - } + new LauncherHelper(profile, account, id).makeLaunchScript(file); + }); } public static void launch(Profile profile, String id) { - if (checkForLaunching(profile, id)) - new LauncherHelper(profile, Accounts.getSelectedAccount(), id).launch(); + if (!checkVersionForLaunching(profile, id)) + return; + ensureSelectedAccount(account -> { + new LauncherHelper(profile, account, id).launch(); + }); } public static void testGame(Profile profile, String id) { - if (checkForLaunching(profile, id)) { - LauncherHelper helper = new LauncherHelper(profile, Accounts.getSelectedAccount(), id); + if (!checkVersionForLaunching(profile, id)) + return; + ensureSelectedAccount(account -> { + LauncherHelper helper = new LauncherHelper(profile, account, id); helper.setTestMode(); helper.launch(); - } + }); } - private static boolean checkForLaunching(Profile profile, String id) { - if (Accounts.getSelectedAccount() == null) - Controllers.getRootPage().checkAccountForcibly(); - else if (id == null || !profile.getRepository().isLoaded() || !profile.getRepository().hasVersion(id)) + private static boolean checkVersionForLaunching(Profile profile, String id) { + if (id == null || !profile.getRepository().isLoaded() || !profile.getRepository().hasVersion(id)) { Controllers.dialog(i18n("version.empty.launch"), i18n("launch.failed"), MessageDialogPane.MessageType.ERROR, () -> { Controllers.navigate(Controllers.getGameListPage()); }); - else + return false; + } else { return true; - return false; + } + } + + private static void ensureSelectedAccount(Consumer action) { + Account account = Accounts.getSelectedAccount(); + if (account == null) { + CreateAccountPane dialog = new CreateAccountPane(); + dialog.addEventHandler(DialogCloseEvent.CLOSE, e -> { + Account newAccount = Accounts.getSelectedAccount(); + if (newAccount == null) { + // user cancelled operation + } else { + Platform.runLater(() -> action.accept(newAccount)); + } + }); + Controllers.dialog(dialog); + } else { + action.accept(account); + } } public static void modifyGlobalSettings(Profile profile) { From d8943c266e9c8e2818daf0af501bbc245a67bb8b Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Mon, 6 Sep 2021 21:56:17 +0800 Subject: [PATCH 18/21] fix: polish accounts list page --- .../org/jackhuang/hmcl/ui/account/AccountListPage.java | 7 +++++-- .../org/jackhuang/hmcl/ui/account/CreateAccountPane.java | 5 +++-- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 5 +---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java index 51be0d003..a3531cbd0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java @@ -130,11 +130,14 @@ public class AccountListPage extends ListPageBase implements De authServerItems = MappedObservableList.create(skinnable.authServersProperty(), server -> { AdvancedListItem item = new AdvancedListItem(); item.getStyleClass().add("navigation-drawer-item"); - item.setLeftGraphic(SVG.server(null, 20, 20)); + item.setLeftGraphic(SVG.server(Theme.blackFillBinding(), 24, 24)); item.setOnAction(e -> Controllers.dialog(new CreateAccountPane(server))); JFXButton btnRemove = new JFXButton(); - btnRemove.setOnAction(e -> skinnable.authServersProperty().remove(server)); + btnRemove.setOnAction(e -> { + skinnable.authServersProperty().remove(server); + e.consume(); + }); btnRemove.getStyleClass().add("toggle-icon4"); btnRemove.setGraphic(SVG.close(Theme.blackFillBinding(), 14, 14)); item.setRightGraphic(btnRemove); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java index d3d1260f1..24c33d05a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java @@ -38,6 +38,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService; import org.jackhuang.hmcl.game.TexturesLoader; import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; @@ -312,16 +313,16 @@ public class CreateAccountPane extends JFXDialogLayout { classPropertyFor(cboServers, "jfx-combo-box-warning").bind(noServers); classPropertyFor(cboServers, "jfx-combo-box").bind(noServers.not()); HBox.setHgrow(cboServers, Priority.ALWAYS); + HBox.setMargin(cboServers, new Insets(0, 10, 0, 0)); cboServers.setMaxWidth(Double.MAX_VALUE); HBox linksContainer = new HBox(); linksContainer.setAlignment(Pos.CENTER); - linksContainer.setPadding(new Insets(0, 5, 0, 15)); onChangeAndOperate(cboServers.valueProperty(), server -> linksContainer.getChildren().setAll(createHyperlinks(server))); linksContainer.setMinWidth(USE_PREF_SIZE); JFXButton btnAddServer = new JFXButton(); - btnAddServer.setGraphic(SVG.plus(null, 20, 20)); + btnAddServer.setGraphic(SVG.plus(Theme.blackFillBinding(), 20, 20)); btnAddServer.getStyleClass().add("toggle-icon4"); btnAddServer.setOnAction(e -> { Controllers.dialog(new AddAuthlibInjectorServerPane()); 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 5d241a5f9..ff665da9f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -59,8 +59,6 @@ account.failed.no_character=该帐号没有角色 account.failed.server_response_malformed=无法解析认证服务器响应,可能是服务器故障 account.injector.add=添加认证服务器 account.injector.empty=无(点击右侧加号添加) -account.injector.manage=管理认证服务器 -account.injector.manage.title=认证服务器 account.injector.http=警告:此服务器使用不安全的 HTTP 协议,您的密码在登录时会被明文传输。 account.injector.link.register=注册 account.injector.server=认证服务器 @@ -68,8 +66,7 @@ account.injector.server_url=服务器地址 account.injector.server_name=服务器名称 account.manage=账户列表 account.methods=登录方式 -account.methods.authlib_injector=外置登录 -account.methods.authlib_injector.subtitle=authlib-injector +account.methods.authlib_injector=外置登录 (authlib-injector) account.methods.microsoft=微软账户 account.methods.microsoft.close_page=已完成微软账号授权,接下来启动器还需要完成剩余登录步骤。你已经可以关闭本页面了。 account.methods.microsoft.error.add_family=由于你未满 18 岁,你的账号必须被加入到家庭中才能登录游戏。 From b7997dfbce57c75aee0474fdee517dcf14c27609 Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Mon, 6 Sep 2021 22:45:37 +0800 Subject: [PATCH 19/21] fix: error message overlapping in add account dialog --- .../java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java index 24c33d05a..7cf6a82e8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java @@ -282,7 +282,7 @@ public class CreateAccountPane extends JFXDialogLayout { public AccountDetailsInputPane(AccountFactory factory, Runnable onAction) { this.factory = factory; - setVgap(15); + setVgap(22); setHgap(15); setAlignment(Pos.CENTER); From b78f5407cc048c0a0f91d2cf75858bdaa95ab161 Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Tue, 7 Sep 2021 00:46:46 +0800 Subject: [PATCH 20/21] redesign account list page sidebar --- .../hmcl/ui/account/AccountListPage.java | 131 ++++++++++-------- .../account/AddAuthlibInjectorServerPane.java | 2 +- .../hmcl/ui/account/CreateAccountPane.java | 15 +- 3 files changed, 86 insertions(+), 62 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java index a3531cbd0..28c307b6c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java @@ -24,6 +24,7 @@ import javafx.beans.property.*; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.geometry.Insets; import javafx.scene.control.ScrollPane; import javafx.scene.control.Skin; import javafx.scene.control.SkinBase; @@ -48,6 +49,8 @@ import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.createSelectedItemPropertyFor; +import java.net.URI; + public class AccountListPage extends ListPageBase implements DecoratorPage { private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("account.manage"), -1)); private final ListProperty accounts = new SimpleListProperty<>(this, "accounts", FXCollections.observableArrayList()); @@ -91,81 +94,89 @@ public class AccountListPage extends ListPageBase implements De BorderPane root = new BorderPane(); { - VBox left = new VBox(); - left.getStyleClass().add("advanced-list-box-content"); - - left.getChildren().add(new ClassTitle(i18n("account.create"))); - - AdvancedListItem offlineItem = new AdvancedListItem(); - offlineItem.getStyleClass().add("navigation-drawer-item"); - offlineItem.setActionButtonVisible(false); - offlineItem.setTitle(i18n("account.methods.offline")); - offlineItem.setLeftGraphic(wrap(SVG.account(Theme.blackFillBinding(), 24, 24))); - offlineItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_OFFLINE))); - left.getChildren().add(offlineItem); - - AdvancedListItem mojangItem = new AdvancedListItem(); - mojangItem.getStyleClass().add("navigation-drawer-item"); - mojangItem.setActionButtonVisible(false); - mojangItem.setTitle(i18n("account.methods.yggdrasil")); - mojangItem.setLeftGraphic(wrap(SVG.mojang(Theme.blackFillBinding(), 24, 24))); - mojangItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MOJANG))); - left.getChildren().add(mojangItem); - - AdvancedListItem microsoftItem = new AdvancedListItem(); - microsoftItem.getStyleClass().add("navigation-drawer-item"); - microsoftItem.setActionButtonVisible(false); - microsoftItem.setTitle(i18n("account.methods.microsoft")); - microsoftItem.setLeftGraphic(wrap(SVG.microsoft(Theme.blackFillBinding(), 24, 24))); - microsoftItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MICROSOFT))); - left.getChildren().add(microsoftItem); - - left.getChildren().add(new ClassTitle(i18n("account.methods.authlib_injector"))); + BorderPane left = new BorderPane(); + FXUtils.setLimitWidth(left, 200); { - VBox wrapper = new VBox(); - FXUtils.setLimitWidth(wrapper, 200); + VBox boxItemList = new VBox(); + boxItemList.getStyleClass().add("advanced-list-box-content"); - VBox box = new VBox(); - authServerItems = MappedObservableList.create(skinnable.authServersProperty(), server -> { - AdvancedListItem item = new AdvancedListItem(); - item.getStyleClass().add("navigation-drawer-item"); - item.setLeftGraphic(SVG.server(Theme.blackFillBinding(), 24, 24)); - item.setOnAction(e -> Controllers.dialog(new CreateAccountPane(server))); + boxItemList.getChildren().add(new ClassTitle(i18n("account.create"))); - JFXButton btnRemove = new JFXButton(); - btnRemove.setOnAction(e -> { - skinnable.authServersProperty().remove(server); - e.consume(); + { + VBox boxMethods = new VBox(); + FXUtils.setLimitWidth(boxMethods, 200); + + AdvancedListItem offlineItem = new AdvancedListItem(); + offlineItem.getStyleClass().add("navigation-drawer-item"); + offlineItem.setActionButtonVisible(false); + offlineItem.setTitle(i18n("account.methods.offline")); + offlineItem.setLeftGraphic(wrap(SVG.account(Theme.blackFillBinding(), 24, 24))); + offlineItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_OFFLINE))); + boxMethods.getChildren().add(offlineItem); + + AdvancedListItem mojangItem = new AdvancedListItem(); + mojangItem.getStyleClass().add("navigation-drawer-item"); + mojangItem.setActionButtonVisible(false); + mojangItem.setTitle(i18n("account.methods.yggdrasil")); + mojangItem.setLeftGraphic(wrap(SVG.mojang(Theme.blackFillBinding(), 24, 24))); + mojangItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MOJANG))); + boxMethods.getChildren().add(mojangItem); + + AdvancedListItem microsoftItem = new AdvancedListItem(); + microsoftItem.getStyleClass().add("navigation-drawer-item"); + microsoftItem.setActionButtonVisible(false); + microsoftItem.setTitle(i18n("account.methods.microsoft")); + microsoftItem.setLeftGraphic(wrap(SVG.microsoft(Theme.blackFillBinding(), 24, 24))); + microsoftItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MICROSOFT))); + boxMethods.getChildren().add(microsoftItem); + + VBox boxAuthServers = new VBox(); + authServerItems = MappedObservableList.create(skinnable.authServersProperty(), server -> { + AdvancedListItem item = new AdvancedListItem(); + item.getStyleClass().add("navigation-drawer-item"); + item.setLeftGraphic(wrap(SVG.server(Theme.blackFillBinding(), 24, 24))); + item.setOnAction(e -> Controllers.dialog(new CreateAccountPane(server))); + + JFXButton btnRemove = new JFXButton(); + btnRemove.setOnAction(e -> { + skinnable.authServersProperty().remove(server); + e.consume(); + }); + btnRemove.getStyleClass().add("toggle-icon4"); + btnRemove.setGraphic(SVG.close(Theme.blackFillBinding(), 14, 14)); + item.setRightGraphic(btnRemove); + + ObservableValue title = BindingMapping.of(server, AuthlibInjectorServer::getName); + item.titleProperty().bind(title); + item.subtitleProperty().set(URI.create(server.getUrl()).getHost()); + Tooltip tooltip = new Tooltip(); + tooltip.textProperty().bind(Bindings.format("%s (%s)", title, server.getUrl())); + FXUtils.installFastTooltip(item, tooltip); + + return item; }); - btnRemove.getStyleClass().add("toggle-icon4"); - btnRemove.setGraphic(SVG.close(Theme.blackFillBinding(), 14, 14)); - item.setRightGraphic(btnRemove); + Bindings.bindContent(boxAuthServers.getChildren(), authServerItems); + boxMethods.getChildren().add(boxAuthServers); - ObservableValue title = BindingMapping.of(server, AuthlibInjectorServer::getName); - String url = server.getUrl(); - item.titleProperty().bind(title); - item.subtitleProperty().set(url); - Tooltip tooltip = new Tooltip(); - tooltip.textProperty().bind(Bindings.format("%s (%s)", title, url)); - FXUtils.installFastTooltip(item, tooltip); + boxItemList.getChildren().add(new ScrollPane(boxMethods)); + } - return item; - }); - Bindings.bindContent(box.getChildren(), authServerItems); + left.setCenter(boxItemList); + } + { AdvancedListItem addAuthServerItem = new AdvancedListItem(); addAuthServerItem.getStyleClass().add("navigation-drawer-item"); addAuthServerItem.setTitle(i18n("account.injector.add")); + addAuthServerItem.setSubtitle(i18n("account.methods.authlib_injector")); addAuthServerItem.setActionButtonVisible(false); - addAuthServerItem.setLeftGraphic(SVG.plusCircleOutline(Theme.blackFillBinding(), 24, 24)); + addAuthServerItem.setLeftGraphic(wrap(SVG.plusCircleOutline(Theme.blackFillBinding(), 24, 24))); addAuthServerItem.setOnAction(e -> Controllers.dialog(new AddAuthlibInjectorServerPane())); - - wrapper.getChildren().addAll(box, addAuthServerItem); - left.getChildren().add(new ScrollPane(wrapper)); + BorderPane.setMargin(addAuthServerItem, new Insets(0, 0, 12, 0)); + left.setBottom(addAuthServerItem); } - FXUtils.setLimitWidth(left, 200); root.setLeft(left); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java index 59d05cd72..d59e2c4eb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java @@ -132,7 +132,7 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa @FXML private void onAddFinish() { if (!config().getAuthlibInjectorServers().contains(serverBeingAdded)) { - config().getAuthlibInjectorServers().add(0, serverBeingAdded); + config().getAuthlibInjectorServers().add(serverBeingAdded); } fireEvent(new DialogCloseEvent()); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java index 7cf6a82e8..a3186791e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java @@ -62,7 +62,7 @@ import static org.jackhuang.hmcl.ui.FXUtils.*; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.classPropertyFor; -public class CreateAccountPane extends JFXDialogLayout { +public class CreateAccountPane extends JFXDialogLayout implements DialogAware { private boolean showMethodSwitcher; private AccountFactory factory; @@ -424,6 +424,12 @@ public class CreateAccountPane extends JFXDialogLayout { public void selectAuthServer(AuthlibInjectorServer authserver) { cboServers.getSelectionModel().select(authserver); } + + public void focus() { + if (txtUsername != null) { + txtUsername.requestFocus(); + } + } } private static class DialogCharacterSelector extends BorderPane implements CharacterSelector { @@ -486,4 +492,11 @@ public class CreateAccountPane extends JFXDialogLayout { } } } + + @Override + public void onDialogShown() { + if (detailsPane instanceof AccountDetailsInputPane) { + ((AccountDetailsInputPane) detailsPane).focus(); + } + } } From e7ce68d3cb28f8055744beaa837e15c086d5b04c Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Tue, 7 Sep 2021 11:52:47 +0800 Subject: [PATCH 21/21] remove useless files --- HMCL/.attach_pid426462 | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 HMCL/.attach_pid426462 diff --git a/HMCL/.attach_pid426462 b/HMCL/.attach_pid426462 deleted file mode 100644 index e69de29bb..000000000