feat: WIP: Download Page

This commit is contained in:
huanghongxun 2021-08-29 05:53:19 +08:00
parent 038cc97ecf
commit 23bb926acf
9 changed files with 195 additions and 9 deletions

View File

@ -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;

View File

@ -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",

View File

@ -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) {

View File

@ -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();
}
}

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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

View File

@ -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 才能進行模組管理。

View File

@ -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=排序