mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-02-05 16:44:47 +08:00
feat: WIP: Download Page
This commit is contained in:
parent
038cc97ecf
commit
23bb926acf
@ -44,6 +44,7 @@ import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
||||
import org.jackhuang.hmcl.ui.construct.PromptDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorController;
|
||||
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;
|
||||
@ -93,6 +94,7 @@ public final class Controllers {
|
||||
}
|
||||
};
|
||||
});
|
||||
private static Lazy<DownloadPage> downloadPage = new Lazy<>(DownloadPage::new);
|
||||
private static Lazy<AccountListPage> accountListPage = new Lazy<>(() -> {
|
||||
AccountListPage accountListPage = new AccountListPage();
|
||||
accountListPage.selectedAccountProperty().bindBidirectional(Accounts.selectedAccountProperty());
|
||||
@ -149,6 +151,11 @@ public final class Controllers {
|
||||
return accountListPage.get();
|
||||
}
|
||||
|
||||
// FXThread
|
||||
public static DownloadPage getDownloadPage() {
|
||||
return downloadPage.get();
|
||||
}
|
||||
|
||||
// FXThread
|
||||
public static DecoratorController getDecorator() {
|
||||
return decorator;
|
||||
|
@ -307,6 +307,12 @@ public final class SVG {
|
||||
fill, width, height);
|
||||
}
|
||||
|
||||
public static Node textureBox(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath(
|
||||
"M20 2H4C2.9 2 2 2.9 2 4V20C2 21.11 2.9 22 4 22H20C21.11 22 22 21.11 22 20V4C22 2.9 21.11 2 20 2M4 6L6 4H10.9L4 10.9V6M4 13.7L13.7 4H18.6L4 18.6V13.7M20 18L18 20H13.1L20 13.1V18M20 10.3L10.3 20H5.4L20 5.4V10.3Z",
|
||||
fill, width, height);
|
||||
}
|
||||
|
||||
public static Node gamepad(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath(
|
||||
"M6,9H8V11H10V13H8V15H6V13H4V11H6V9M18.5,9A1.5,1.5 0 0,1 20,10.5A1.5,1.5 0 0,1 18.5,12A1.5,1.5 0 0,1 17,10.5A1.5,1.5 0 0,1 18.5,9M15.5,12A1.5,1.5 0 0,1 17,13.5A1.5,1.5 0 0,1 15.5,15A1.5,1.5 0 0,1 14,13.5A1.5,1.5 0 0,1 15.5,12M17,5A7,7 0 0,1 24,12A7,7 0 0,1 17,19C15.04,19 13.27,18.2 12,16.9C10.73,18.2 8.96,19 7,19A7,7 0 0,1 0,12A7,7 0 0,1 7,5H17M7,7A5,5 0 0,0 2,12A5,5 0 0,0 7,17C8.64,17 10.09,16.21 11,15H13C13.91,16.21 15.36,17 17,17A5,5 0 0,0 22,12A5,5 0 0,0 17,7H7Z",
|
||||
|
@ -259,6 +259,10 @@ public interface TabControl {
|
||||
this.userData.set(userData);
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return getNode() == null;
|
||||
}
|
||||
|
||||
public boolean initializeIfNeeded() {
|
||||
if (getNode() == null) {
|
||||
if (getNodeSupplier() == null) {
|
||||
|
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.download;
|
||||
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
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.task.FileDownloadTask;
|
||||
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.SVG;
|
||||
import org.jackhuang.hmcl.ui.WeakListenerHolder;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||
import org.jackhuang.hmcl.ui.construct.TabHeader;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.ui.versions.ModDownloadListPage;
|
||||
import org.jackhuang.hmcl.ui.versions.VersionPage;
|
||||
import org.jackhuang.hmcl.ui.versions.Versions;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class DownloadPage extends BorderPane implements DecoratorPage {
|
||||
private final ReadOnlyObjectWrapper<DecoratorPage.State> state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("download"), 200));
|
||||
private final TabHeader tab;
|
||||
private final TabHeader.Tab<ModDownloadListPage> modTab = new TabHeader.Tab<>("modTab");
|
||||
private final TabHeader.Tab<ModDownloadListPage> modpackTab = new TabHeader.Tab<>("modpackTab");
|
||||
private final TabHeader.Tab<ModDownloadListPage> resourcePackTab = new TabHeader.Tab<>("resourcePackTab");
|
||||
private final TransitionPane transitionPane = new TransitionPane();
|
||||
|
||||
private WeakListenerHolder listenerHolder;
|
||||
|
||||
public DownloadPage() {
|
||||
modTab.setNodeSupplier(() -> new ModDownloadListPage(CurseModManager.SECTION_MODPACK, Versions::downloadModpackImpl));
|
||||
modpackTab.setNodeSupplier(() -> new ModDownloadListPage(CurseModManager.SECTION_MOD, this::download));
|
||||
resourcePackTab.setNodeSupplier(() -> new ModDownloadListPage(CurseModManager.SECTION_MOD, this::download));
|
||||
tab = new TabHeader(modTab, modpackTab, resourcePackTab);
|
||||
|
||||
Profiles.registerVersionsListener(this::loadVersions);
|
||||
|
||||
tab.getSelectionModel().select(modTab);
|
||||
FXUtils.onChangeAndOperate(tab.getSelectionModel().selectedItemProperty(), newValue -> {
|
||||
if (newValue.initializeIfNeeded()) {
|
||||
if (newValue.getNode() instanceof VersionPage.VersionLoadable) {
|
||||
((VersionPage.VersionLoadable) newValue.getNode()).loadVersion(Profiles.getSelectedProfile(), Profiles.getSelectedVersion());
|
||||
}
|
||||
}
|
||||
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE.getAnimationProducer());
|
||||
});
|
||||
|
||||
{
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
.addNavigationDrawerItem(item -> {
|
||||
item.setTitle(i18n("mods"));
|
||||
item.setLeftGraphic(wrap(SVG.puzzle(null, 20, 20)));
|
||||
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.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.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(resourcePackTab));
|
||||
item.setOnAction(e -> tab.getSelectionModel().select(resourcePackTab));
|
||||
});
|
||||
FXUtils.setLimitWidth(sideBar, 200);
|
||||
setLeft(sideBar);
|
||||
}
|
||||
|
||||
setCenter(transitionPane);
|
||||
}
|
||||
|
||||
private void download(Profile profile, @Nullable String version, CurseAddon.LatestFile file) {
|
||||
if (version == null) {
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
Path dest = profile.getRepository().getRunDirectory(version).toPath().resolve("mods").resolve(file.getFileName());
|
||||
|
||||
TaskExecutorDialogPane downloadingPane = new TaskExecutorDialogPane(it -> {
|
||||
});
|
||||
|
||||
TaskExecutor executor = Task.composeAsync(() -> {
|
||||
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(file.getDownloadUrl()), dest.toFile());
|
||||
task.setName(file.getDisplayName());
|
||||
return task;
|
||||
}).executor(false);
|
||||
|
||||
downloadingPane.setExecutor(executor, true);
|
||||
Controllers.dialog(downloadingPane);
|
||||
executor.start();
|
||||
}
|
||||
|
||||
private void loadVersions(Profile profile) {
|
||||
WeakListenerHolder listenerHolder = new WeakListenerHolder();
|
||||
runInFX(() -> {
|
||||
if (profile == Profiles.getSelectedProfile()) {
|
||||
profile.selectedVersionProperty().addListener(listenerHolder.weak((a, b, newValue) -> {
|
||||
FXUtils.checkFxUserThread();
|
||||
|
||||
if (modTab.isInitialized()) {
|
||||
modTab.getNode().loadVersion(profile, newValue);
|
||||
}
|
||||
if (modpackTab.isInitialized()) {
|
||||
modpackTab.getNode().loadVersion(profile, newValue);
|
||||
}
|
||||
if (resourcePackTab.isInitialized()) {
|
||||
resourcePackTab.getNode().loadVersion(profile, newValue);
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadOnlyObjectProperty<State> stateProperty() {
|
||||
return state.getReadOnlyProperty();
|
||||
}
|
||||
}
|
@ -167,6 +167,14 @@ public class RootPage extends DecoratorTabPage {
|
||||
gameItem.setTitle(i18n("version.manage"));
|
||||
gameItem.setOnAction(e -> Controllers.navigate(Controllers.getGameListPage()));
|
||||
|
||||
// forth item in left sidebar
|
||||
AdvancedListItem downloadItem = new AdvancedListItem();
|
||||
downloadItem
|
||||
.setLeftGraphic(AdvancedListItem.createImageView(newImage("/assets/img/chest.png")).getKey());
|
||||
downloadItem.setActionButtonVisible(false);
|
||||
downloadItem.setTitle(i18n("download"));
|
||||
downloadItem.setOnAction(e -> Controllers.navigate(Controllers.getDownloadPage()));
|
||||
|
||||
// fifth item in left sidebar
|
||||
AdvancedListItem launcherSettingsItem = new AdvancedListItem();
|
||||
launcherSettingsItem
|
||||
@ -182,6 +190,7 @@ public class RootPage extends DecoratorTabPage {
|
||||
.startCategory(i18n("version").toUpperCase())
|
||||
.add(gameListItem)
|
||||
.add(gameItem)
|
||||
.add(downloadItem)
|
||||
.add(launcherSettingsItem);
|
||||
|
||||
// the root page, with the sidebar in left, navigator in center.
|
||||
|
@ -107,15 +107,15 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa
|
||||
setVersion(version, profile);
|
||||
preferredVersionName = version;
|
||||
|
||||
if (versionSettingsTab.getNode() != null)
|
||||
if (versionSettingsTab.isInitialized())
|
||||
versionSettingsTab.getNode().loadVersion(profile, version);
|
||||
if (modListTab.getNode() != null)
|
||||
if (modListTab.isInitialized())
|
||||
modListTab.getNode().loadVersion(profile, version);
|
||||
if (curseModListTab.getNode() != null)
|
||||
if (curseModListTab.isInitialized())
|
||||
curseModListTab.getNode().loadVersion(profile, version);
|
||||
if (installerListTab.getNode() != null)
|
||||
if (installerListTab.isInitialized())
|
||||
installerListTab.getNode().loadVersion(profile, version);
|
||||
if (worldListTab.getNode() != null)
|
||||
if (worldListTab.isInitialized())
|
||||
worldListTab.getNode().loadVersion(profile, version);
|
||||
currentVersionUpgradable.set(profile.getRepository().isModpack(version));
|
||||
}
|
||||
@ -251,7 +251,7 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa
|
||||
|
||||
AdvancedListItem modListItem = new AdvancedListItem();
|
||||
modListItem.getStyleClass().add("navigation-drawer-item");
|
||||
modListItem.setTitle(i18n("mods"));
|
||||
modListItem.setTitle(i18n("mods.manage"));
|
||||
modListItem.setLeftGraphic(wrap(SVG.puzzle(null, 20, 20)));
|
||||
modListItem.setActionButtonVisible(false);
|
||||
modListItem.activeProperty().bind(control.tab.getSelectionModel().selectedItemProperty().isEqualTo(control.modListTab));
|
||||
@ -380,7 +380,7 @@ public class VersionPage extends Control implements DecoratorPage, ModDownloadPa
|
||||
return stackPane;
|
||||
}
|
||||
|
||||
interface VersionLoadable {
|
||||
public interface VersionLoadable {
|
||||
void loadVersion(Profile profile, String version);
|
||||
}
|
||||
}
|
||||
|
@ -397,6 +397,7 @@ mods.disable=Disable
|
||||
mods.download=Mod Downloads
|
||||
mods.download.title=Mod Downloads - %1s
|
||||
mods.enable=Enable
|
||||
mods.manage=Mods
|
||||
mods.mcmod.page=MCWiki
|
||||
mods.mcmod.search=Search in MCWiki
|
||||
mods.name=Name
|
||||
@ -443,6 +444,8 @@ profile.title=Game Directories
|
||||
profile.selected=Selected
|
||||
profile.use_relative_path=Use relative path for game directory if possible
|
||||
|
||||
resourcepack=Resource Pack
|
||||
|
||||
selector.choose=Choose
|
||||
selector.choose_file=Select a file
|
||||
selector.custom=Custom
|
||||
|
@ -296,13 +296,14 @@ modpack.wizard.step.initialization.save=選擇要匯出到的遊戲整合包位
|
||||
modpack.wizard.step.initialization.warning=在製作整合包前,請您確認您選擇的版本可以正常啟動,\n並保證您的 Minecraft 是正式版而非快照版,\n而且不應將不允許非官方途徑傳播的 Mod 模組、材質包等納入整合包。\n整合包會儲存您目前的下載來源設定
|
||||
modpack.wizard.step.initialization.server=點選此處查看有關伺服器自動更新整合包的製作教學
|
||||
|
||||
mods=模組管理
|
||||
mods=模組
|
||||
mods.add=新增模組
|
||||
mods.add.failed=新增模組 %s 失敗。
|
||||
mods.add.success=成功新增模組 %s。
|
||||
mods.choose_mod=選擇模組
|
||||
mods.enable=啟用
|
||||
mods.disable=停用
|
||||
mods.mangage=模組管理
|
||||
mods.name=名稱
|
||||
mods.not_modded=你需要先在自動安裝頁面安裝 Fabric、Forge 或 LiteLoader 才能進行模組管理。
|
||||
|
||||
|
@ -389,7 +389,7 @@ modpack.wizard.step.initialization.save=选择要导出到的游戏整合包位
|
||||
modpack.wizard.step.initialization.warning=在制作整合包前,请您确认您选择的版本可以正常启动,\n并保证您的 Minecraft 是正式版而非快照版,\n而且不应当将不允许非官方途径传播的 Mod、材质包等纳入整合包。\n整合包会保存您目前的下载源设置
|
||||
modpack.wizard.step.initialization.server=点击此处查看有关服务器自动更新整合包的制作教程
|
||||
|
||||
mods=模组管理
|
||||
mods=模组
|
||||
mods.add=添加模组
|
||||
mods.add.failed=添加模组 %s 失败。
|
||||
mods.add.success=成功添加模组 %s。
|
||||
@ -399,6 +399,7 @@ mods.disable=禁用
|
||||
mods.download=模组下载
|
||||
mods.download.title=模组下载 - %1s
|
||||
mods.enable=启用
|
||||
mods.manage=模组管理
|
||||
mods.mcmod.page=MC 百科页面
|
||||
mods.mcmod.search=MC 百科搜索
|
||||
mods.name=名称
|
||||
@ -445,6 +446,8 @@ profile.title=游戏目录
|
||||
profile.selected=已选中
|
||||
profile.use_relative_path=若可能,游戏目录使用相对路径
|
||||
|
||||
resourcepack=资源包
|
||||
|
||||
search=搜索
|
||||
search.sort=排序
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user