优化游戏下载界面 (#4405)

This commit is contained in:
Glavo
2025-09-07 16:33:49 +08:00
committed by GitHub
parent 55a7169e21
commit c3352d2b73
2 changed files with 82 additions and 42 deletions

View File

@@ -52,6 +52,7 @@ import javafx.stage.Stage;
import javafx.util.Callback; import javafx.util.Callback;
import javafx.util.Duration; import javafx.util.Duration;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.CacheFileTask;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
@@ -1159,6 +1160,13 @@ public final class FXUtils {
return button; return button;
} }
public static JFXButton newToggleButton4(SVG icon) {
JFXButton button = new JFXButton();
button.getStyleClass().add("toggle-icon4");
button.setGraphic(icon.createIcon(Theme.blackFill(), -1));
return button;
}
public static Label truncatedLabel(String text, int limit) { public static Label truncatedLabel(String text, int limit) {
Label label = new Label(); Label label = new Label();
if (text.length() <= limit) { if (text.length() <= limit) {

View File

@@ -19,12 +19,13 @@ package org.jackhuang.hmcl.ui.download;
import com.jfoenix.controls.*; import com.jfoenix.controls.*;
import javafx.beans.InvalidationListener; import javafx.beans.InvalidationListener;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.*;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.layout.*; import javafx.scene.layout.*;
@@ -41,7 +42,6 @@ import org.jackhuang.hmcl.download.neoforge.NeoForgeRemoteVersion;
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion; import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
import org.jackhuang.hmcl.download.quilt.QuiltAPIRemoteVersion; import org.jackhuang.hmcl.download.quilt.QuiltAPIRemoteVersion;
import org.jackhuang.hmcl.download.quilt.QuiltRemoteVersion; import org.jackhuang.hmcl.download.quilt.QuiltRemoteVersion;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.setting.VersionIconType; import org.jackhuang.hmcl.setting.VersionIconType;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
@@ -73,17 +73,23 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
private final String libraryId; private final String libraryId;
private final String title; private final String title;
private final Navigation navigation; private final Navigation navigation;
private final DownloadProvider downloadProvider;
private final VersionList<?> versionList; private final VersionList<?> versionList;
private final Runnable callback; private final Runnable callback;
private final ObservableList<RemoteVersion> versions = FXCollections.observableArrayList(); private final ObservableList<RemoteVersion> versions = FXCollections.observableArrayList();
private final ObjectProperty<Status> status = new SimpleObjectProperty<>(Status.LOADING); private final ObjectProperty<Status> status = new SimpleObjectProperty<>(Status.LOADING);
public VersionsPage(Navigation navigation, String title, String gameVersion, DownloadProvider downloadProvider, String libraryId, Runnable callback) { public VersionsPage(Navigation navigation,
String title, String gameVersion,
DownloadProvider downloadProvider,
String libraryId,
Runnable callback) {
this.title = title; this.title = title;
this.gameVersion = gameVersion; this.gameVersion = gameVersion;
this.libraryId = libraryId; this.libraryId = libraryId;
this.navigation = navigation; this.navigation = navigation;
this.downloadProvider = downloadProvider;
this.versionList = downloadProvider.getVersionListById(libraryId); this.versionList = downloadProvider.getVersionListById(libraryId);
this.callback = callback; this.callback = callback;
@@ -130,10 +136,6 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
navigation.onPrev(true); navigation.onPrev(true);
} }
private void onSponsor() {
FXUtils.openLink("https://bmclapidoc.bangbang93.com");
}
private enum Status { private enum Status {
LOADING, LOADING,
FAILED, FAILED,
@@ -149,22 +151,61 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
} }
private static class RemoteVersionListCell extends ListCell<RemoteVersion> { private static class RemoteVersionListCell extends ListCell<RemoteVersion> {
private final IconedTwoLineListItem content = new IconedTwoLineListItem(); private final VersionsPage control;
private final RipplerContainer ripplerContainer = new RipplerContainer(content);
private final TwoLineListItem twoLineListItem = new TwoLineListItem();
private final ImageView imageView = new ImageView();
private final StackPane pane = new StackPane(); private final StackPane pane = new StackPane();
private final Holder<RemoteVersionListCell> lastCell; private final Holder<RemoteVersionListCell> lastCell;
RemoteVersionListCell(Holder<RemoteVersionListCell> lastCell, String libraryId) { RemoteVersionListCell(Holder<RemoteVersionListCell> lastCell, VersionsPage control) {
this.lastCell = lastCell; this.lastCell = lastCell;
if ("game".equals(libraryId)) { this.control = control;
content.getExternalLinkButton().setGraphic(SVG.GLOBE_BOOK.createIcon(Theme.blackFill(), -1));
FXUtils.installFastTooltip(content.getExternalLinkButton(), i18n("wiki.tooltip")); HBox hbox = new HBox(16);
HBox.setHgrow(twoLineListItem, Priority.ALWAYS);
hbox.setAlignment(Pos.CENTER);
HBox actions = new HBox(8);
actions.setAlignment(Pos.CENTER);
{
if ("game".equals(control.libraryId)) {
JFXButton wikiButton = newToggleButton4(SVG.GLOBE_BOOK);
wikiButton.setOnAction(event -> onOpenWiki());
FXUtils.installFastTooltip(wikiButton, i18n("wiki.tooltip"));
actions.getChildren().add(wikiButton);
}
JFXButton actionButton = newToggleButton4(SVG.ARROW_FORWARD);
actionButton.setOnAction(e -> onAction());
actions.getChildren().add(actionButton);
} }
hbox.getChildren().setAll(imageView, twoLineListItem, actions);
pane.getStyleClass().add("md-list-cell"); pane.getStyleClass().add("md-list-cell");
StackPane.setMargin(content, new Insets(10, 16, 10, 16)); StackPane.setMargin(hbox, new Insets(10, 16, 10, 16));
pane.getChildren().setAll(ripplerContainer); pane.getChildren().setAll(new RipplerContainer(hbox));
FXUtils.onClicked(this, this::onAction);
}
private void onAction() {
RemoteVersion item = getItem();
if (item == null)
return;
control.navigation.getSettings().put(control.libraryId, item);
control.callback.run();
}
private void onOpenWiki() {
RemoteVersion item = getItem();
if (!(item instanceof GameRemoteVersion))
return;
FXUtils.openLink(I18n.getWikiLink((GameRemoteVersion) item));
} }
@Override @Override
@@ -182,37 +223,36 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
} }
setGraphic(pane); setGraphic(pane);
content.setTitle(I18n.getDisplaySelfVersion(remoteVersion)); twoLineListItem.setTitle(I18n.getDisplaySelfVersion(remoteVersion));
if (remoteVersion.getReleaseDate() != null) { if (remoteVersion.getReleaseDate() != null) {
content.setSubtitle(I18n.formatDateTime(remoteVersion.getReleaseDate())); twoLineListItem.setSubtitle(I18n.formatDateTime(remoteVersion.getReleaseDate()));
} else { } else {
content.setSubtitle(null); twoLineListItem.setSubtitle(null);
} }
if (remoteVersion instanceof GameRemoteVersion) { if (remoteVersion instanceof GameRemoteVersion) {
RemoteVersion.Type versionType = remoteVersion.getVersionType(); RemoteVersion.Type versionType = remoteVersion.getVersionType();
switch (versionType) { switch (versionType) {
case RELEASE: case RELEASE:
content.getTags().setAll(i18n("version.game.release")); twoLineListItem.getTags().setAll(i18n("version.game.release"));
content.setImage(VersionIconType.GRASS.getIcon()); imageView.setImage(VersionIconType.GRASS.getIcon());
break; break;
case PENDING: case PENDING:
case SNAPSHOT: case SNAPSHOT:
if (versionType == RemoteVersion.Type.SNAPSHOT if (versionType == RemoteVersion.Type.SNAPSHOT
&& GameVersionNumber.asGameVersion(remoteVersion.getGameVersion()).isAprilFools()) { && GameVersionNumber.asGameVersion(remoteVersion.getGameVersion()).isAprilFools()) {
content.getTags().setAll(i18n("version.game.april_fools")); twoLineListItem.getTags().setAll(i18n("version.game.april_fools"));
content.setImage(VersionIconType.APRIL_FOOLS.getIcon()); imageView.setImage(VersionIconType.APRIL_FOOLS.getIcon());
} else { } else {
content.getTags().setAll(i18n("version.game.snapshot")); twoLineListItem.getTags().setAll(i18n("version.game.snapshot"));
content.setImage(VersionIconType.COMMAND.getIcon()); imageView.setImage(VersionIconType.COMMAND.getIcon());
} }
break; break;
default: default:
content.getTags().setAll(i18n("version.game.old")); twoLineListItem.getTags().setAll(i18n("version.game.old"));
content.setImage(VersionIconType.CRAFT_TABLE.getIcon()); imageView.setImage(VersionIconType.CRAFT_TABLE.getIcon());
break; break;
} }
content.setExternalLink(I18n.getWikiLink((GameRemoteVersion) remoteVersion));
} else { } else {
VersionIconType iconType; VersionIconType iconType;
if (remoteVersion instanceof LiteLoaderRemoteVersion) if (remoteVersion instanceof LiteLoaderRemoteVersion)
@@ -230,14 +270,13 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
else if (remoteVersion instanceof QuiltRemoteVersion || remoteVersion instanceof QuiltAPIRemoteVersion) else if (remoteVersion instanceof QuiltRemoteVersion || remoteVersion instanceof QuiltAPIRemoteVersion)
iconType = VersionIconType.QUILT; iconType = VersionIconType.QUILT;
else else
iconType = null; iconType = VersionIconType.COMMAND;
content.setImage(iconType != null ? iconType.getIcon() : null); imageView.setImage(iconType.getIcon());
if (content.getSubtitle() == null) if (twoLineListItem.getSubtitle() == null)
content.setSubtitle(remoteVersion.getGameVersion()); twoLineListItem.setSubtitle(remoteVersion.getGameVersion());
else else
content.getTags().setAll(remoteVersion.getGameVersion()); twoLineListItem.getTags().setAll(remoteVersion.getGameVersion());
content.setExternalLink(null);
} }
} }
} }
@@ -355,14 +394,7 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
control.versions.addListener((InvalidationListener) o -> updateList()); control.versions.addListener((InvalidationListener) o -> updateList());
Holder<RemoteVersionListCell> lastCell = new Holder<>(); Holder<RemoteVersionListCell> lastCell = new Holder<>();
list.setCellFactory(listView -> new RemoteVersionListCell(lastCell, control.libraryId)); list.setCellFactory(listView -> new RemoteVersionListCell(lastCell, control));
FXUtils.onClicked(list, () -> {
if (list.getSelectionModel().getSelectedIndex() < 0)
return;
control.navigation.getSettings().put(control.libraryId, list.getSelectionModel().getSelectedItem());
control.callback.run();
});
ComponentList.setVgrow(list, Priority.ALWAYS); ComponentList.setVgrow(list, Priority.ALWAYS);