mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-24 03:33:46 -04:00
将 IconedTwoLineListItem#tags 的类型修改为 ObservableList<Label> (#4473)
This commit is contained in:
parent
811b1fb5f4
commit
e7526e39bf
@ -10,6 +10,7 @@ import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
@ -21,7 +22,7 @@ import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
public class IconedTwoLineListItem extends HBox {
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title");
|
||||
private final ObservableList<String> tags = FXCollections.observableArrayList();
|
||||
private final ObservableList<Label> tags = FXCollections.observableArrayList();
|
||||
private final StringProperty subtitle = new SimpleStringProperty(this, "subtitle");
|
||||
private final StringProperty externalLink = new SimpleStringProperty(this, "externalLink");
|
||||
private final ObjectProperty<Image> image = new SimpleObjectProperty<>(this, "image");
|
||||
@ -62,7 +63,7 @@ public class IconedTwoLineListItem extends HBox {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
public ObservableList<String> getTags() {
|
||||
public ObservableList<Label> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
|
@ -29,16 +29,22 @@ import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.AggregatedObservableList;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
public class TwoLineListItem extends VBox {
|
||||
private static final String DEFAULT_STYLE_CLASS = "two-line-list-item";
|
||||
|
||||
public static Label createTagLabel(String tag) {
|
||||
Label tagLabel = new Label();
|
||||
tagLabel.getStyleClass().add("tag");
|
||||
tagLabel.setText(tag);
|
||||
HBox.setMargin(tagLabel, new Insets(0, 8, 0, 0));
|
||||
return tagLabel;
|
||||
}
|
||||
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title");
|
||||
private final ObservableList<String> tags = FXCollections.observableArrayList();
|
||||
private final ObservableList<Label> tags = FXCollections.observableArrayList();
|
||||
private final StringProperty subtitle = new SimpleStringProperty(this, "subtitle");
|
||||
|
||||
private final ObservableList<Node> tagLabels;
|
||||
private final AggregatedObservableList<Node> firstLineChildren;
|
||||
|
||||
public TwoLineListItem(String titleString, String subtitleString) {
|
||||
@ -58,16 +64,9 @@ public class TwoLineListItem extends VBox {
|
||||
lblTitle.getStyleClass().add("title");
|
||||
lblTitle.textProperty().bind(title);
|
||||
|
||||
tagLabels = MappedObservableList.create(tags, tag -> {
|
||||
Label tagLabel = new Label();
|
||||
tagLabel.getStyleClass().add("tag");
|
||||
tagLabel.setText(tag);
|
||||
HBox.setMargin(tagLabel, new Insets(0, 8, 0, 0));
|
||||
return tagLabel;
|
||||
});
|
||||
firstLineChildren = new AggregatedObservableList<>();
|
||||
firstLineChildren.appendList(FXCollections.singletonObservableList(lblTitle));
|
||||
firstLineChildren.appendList(tagLabels);
|
||||
firstLineChildren.appendList(tags);
|
||||
Bindings.bindContent(firstLine.getChildren(), firstLineChildren.getAggregatedList());
|
||||
|
||||
Label lblSubtitle = new Label();
|
||||
@ -111,7 +110,11 @@ public class TwoLineListItem extends VBox {
|
||||
this.subtitle.set(subtitle);
|
||||
}
|
||||
|
||||
public ObservableList<String> getTags() {
|
||||
public void addTag(String tag) {
|
||||
getTags().add(createTagLabel(tag));
|
||||
}
|
||||
|
||||
public ObservableList<Label> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
|
@ -229,27 +229,28 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
|
||||
} else {
|
||||
twoLineListItem.setSubtitle(null);
|
||||
}
|
||||
twoLineListItem.getTags().clear();
|
||||
|
||||
if (remoteVersion instanceof GameRemoteVersion) {
|
||||
RemoteVersion.Type versionType = remoteVersion.getVersionType();
|
||||
switch (versionType) {
|
||||
case RELEASE:
|
||||
twoLineListItem.getTags().setAll(i18n("version.game.release"));
|
||||
twoLineListItem.addTag(i18n("version.game.release"));
|
||||
imageView.setImage(VersionIconType.GRASS.getIcon());
|
||||
break;
|
||||
case PENDING:
|
||||
case SNAPSHOT:
|
||||
if (versionType == RemoteVersion.Type.SNAPSHOT
|
||||
&& GameVersionNumber.asGameVersion(remoteVersion.getGameVersion()).isAprilFools()) {
|
||||
twoLineListItem.getTags().setAll(i18n("version.game.april_fools"));
|
||||
twoLineListItem.addTag(i18n("version.game.april_fools"));
|
||||
imageView.setImage(VersionIconType.APRIL_FOOLS.getIcon());
|
||||
} else {
|
||||
twoLineListItem.getTags().setAll(i18n("version.game.snapshot"));
|
||||
twoLineListItem.addTag(i18n("version.game.snapshot"));
|
||||
imageView.setImage(VersionIconType.COMMAND.getIcon());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
twoLineListItem.getTags().setAll(i18n("version.game.old"));
|
||||
twoLineListItem.addTag(i18n("version.game.old"));
|
||||
imageView.setImage(VersionIconType.CRAFT_TABLE.getIcon());
|
||||
break;
|
||||
}
|
||||
@ -276,7 +277,7 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
|
||||
if (twoLineListItem.getSubtitle() == null)
|
||||
twoLineListItem.setSubtitle(remoteVersion.getGameVersion());
|
||||
else
|
||||
twoLineListItem.getTags().setAll(remoteVersion.getGameVersion());
|
||||
twoLineListItem.addTag(remoteVersion.getGameVersion());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,9 +250,9 @@ public final class JavaManagementPage extends ListPageBase<JavaManagementPage.Ja
|
||||
TwoLineListItem item = new TwoLineListItem();
|
||||
item.setTitle((java.isJDK() ? "JDK" : "JRE") + " " + java.getVersion());
|
||||
item.setSubtitle(java.getBinary().toString());
|
||||
item.getTags().add(i18n("java.info.architecture") + ": " + java.getArchitecture().getDisplayName());
|
||||
item.addTag(i18n("java.info.architecture") + ": " + java.getArchitecture().getDisplayName());
|
||||
if (vendor != null)
|
||||
item.getTags().add(i18n("java.info.vendor") + ": " + vendor);
|
||||
item.addTag(i18n("java.info.vendor") + ": " + vendor);
|
||||
BorderPane.setAlignment(item, Pos.CENTER);
|
||||
center.getChildren().setAll(item);
|
||||
root.setCenter(center);
|
||||
|
@ -523,9 +523,9 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
|
||||
// ListViewBehavior would consume ESC pressed event, preventing us from handling it, so we ignore it here
|
||||
ignoreEvent(listView, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE);
|
||||
listView.setCellFactory(x -> new FloatListCell<RemoteMod>(listView) {
|
||||
TwoLineListItem content = new TwoLineListItem();
|
||||
ImageView imageView = new ImageView();
|
||||
listView.setCellFactory(x -> new FloatListCell<>(listView) {
|
||||
private final TwoLineListItem content = new TwoLineListItem();
|
||||
private final ImageView imageView = new ImageView();
|
||||
|
||||
{
|
||||
HBox container = new HBox(8);
|
||||
@ -542,10 +542,10 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(getSkinnable().repository.getType()).getModByCurseForgeId(dataItem.getSlug());
|
||||
content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : dataItem.getTitle());
|
||||
content.setSubtitle(dataItem.getDescription());
|
||||
content.getTags().setAll(dataItem.getCategories().stream()
|
||||
content.getTags().clear();
|
||||
dataItem.getCategories().stream()
|
||||
.map(category -> getSkinnable().getLocalizedCategory(category))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
.forEach(content::addTag);
|
||||
if (StringUtils.isNotBlank(dataItem.getIconUrl())) {
|
||||
imageView.imageProperty().bind(FXUtils.newRemoteImage(dataItem.getIconUrl(), 40, 40, true, true));
|
||||
}
|
||||
|
@ -228,9 +228,9 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
ModTranslations.Mod mod = getSkinnable().translations.getModByCurseForgeId(getSkinnable().addon.getSlug());
|
||||
content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : getSkinnable().addon.getTitle());
|
||||
content.setSubtitle(getSkinnable().addon.getDescription());
|
||||
content.getTags().setAll(getSkinnable().addon.getCategories().stream()
|
||||
getSkinnable().addon.getCategories().stream()
|
||||
.map(category -> getSkinnable().page.getLocalizedCategory(category))
|
||||
.collect(Collectors.toList()));
|
||||
.forEach(content::addTag);
|
||||
descriptionPane.getChildren().add(content);
|
||||
|
||||
if (getSkinnable().mod != null) {
|
||||
@ -353,10 +353,9 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(page.repository.getType()).getModByCurseForgeId(addon.getSlug());
|
||||
content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : addon.getTitle());
|
||||
content.setSubtitle(addon.getDescription());
|
||||
content.getTags().setAll(addon.getCategories().stream()
|
||||
addon.getCategories().stream()
|
||||
.map(page::getLocalizedCategory)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
.forEach(content::addTag);
|
||||
if (StringUtils.isNotBlank(addon.getIconUrl())) {
|
||||
imageView.imageProperty().bind(FXUtils.newRemoteImage(addon.getIconUrl(), 40, 40, true, true));
|
||||
}
|
||||
@ -389,15 +388,15 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
|
||||
switch (dataItem.getVersionType()) {
|
||||
case Alpha:
|
||||
content.getTags().add(i18n("mods.channel.alpha"));
|
||||
content.addTag(i18n("mods.channel.alpha"));
|
||||
graphicPane.getChildren().setAll(SVG.ALPHA_CIRCLE.createIcon(Theme.blackFill(), 24));
|
||||
break;
|
||||
case Beta:
|
||||
content.getTags().add(i18n("mods.channel.beta"));
|
||||
content.addTag(i18n("mods.channel.beta"));
|
||||
graphicPane.getChildren().setAll(SVG.BETA_CIRCLE.createIcon(Theme.blackFill(), 24));
|
||||
break;
|
||||
case Release:
|
||||
content.getTags().add(i18n("mods.channel.release"));
|
||||
content.addTag(i18n("mods.channel.release"));
|
||||
graphicPane.getChildren().setAll(SVG.RELEASE_CIRCLE.createIcon(Theme.blackFill(), 24));
|
||||
break;
|
||||
}
|
||||
@ -405,22 +404,22 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
for (ModLoaderType modLoaderType : dataItem.getLoaders()) {
|
||||
switch (modLoaderType) {
|
||||
case FORGE:
|
||||
content.getTags().add(i18n("install.installer.forge"));
|
||||
content.addTag(i18n("install.installer.forge"));
|
||||
break;
|
||||
case CLEANROOM:
|
||||
content.getTags().add(i18n("install.installer.cleanroom"));
|
||||
content.addTag(i18n("install.installer.cleanroom"));
|
||||
break;
|
||||
case NEO_FORGED:
|
||||
content.getTags().add(i18n("install.installer.neoforge"));
|
||||
content.addTag(i18n("install.installer.neoforge"));
|
||||
break;
|
||||
case FABRIC:
|
||||
content.getTags().add(i18n("install.installer.fabric"));
|
||||
content.addTag(i18n("install.installer.fabric"));
|
||||
break;
|
||||
case LITE_LOADER:
|
||||
content.getTags().add(i18n("install.installer.liteloader"));
|
||||
content.addTag(i18n("install.installer.liteloader"));
|
||||
break;
|
||||
case QUILT:
|
||||
content.getTags().add(i18n("install.installer.quilt"));
|
||||
content.addTag(i18n("install.installer.quilt"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -47,11 +47,9 @@ public class GameItemSkin extends SkinBase<GameItem> {
|
||||
TwoLineListItem item = new TwoLineListItem();
|
||||
item.titleProperty().bind(skinnable.titleProperty());
|
||||
FXUtils.onChangeAndOperate(skinnable.tagProperty(), tag -> {
|
||||
if (StringUtils.isNotBlank(tag)) {
|
||||
item.getTags().setAll(tag);
|
||||
} else {
|
||||
item.getTags().clear();
|
||||
}
|
||||
item.getTags().clear();
|
||||
if (StringUtils.isNotBlank(tag))
|
||||
item.addTag(tag);
|
||||
});
|
||||
item.subtitleProperty().bind(skinnable.subtitleProperty());
|
||||
BorderPane.setAlignment(item, Pos.CENTER);
|
||||
|
@ -395,7 +395,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
TwoLineListItem title = new TwoLineListItem();
|
||||
title.setTitle(modInfo.getModInfo().getName());
|
||||
if (StringUtils.isNotBlank(modInfo.getModInfo().getVersion())) {
|
||||
title.getTags().setAll(modInfo.getModInfo().getVersion());
|
||||
title.addTag(modInfo.getModInfo().getVersion());
|
||||
}
|
||||
title.setSubtitle(FileUtils.getName(modInfo.getModInfo().getFile()));
|
||||
|
||||
@ -442,9 +442,10 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
List<String> tags = title.getTags();
|
||||
if (!tags.contains(loaderName)) {
|
||||
tags.add(loaderName);
|
||||
if (title.getTags()
|
||||
.stream()
|
||||
.noneMatch(it -> it.getText().equals(loaderName))) {
|
||||
title.addTag(loaderName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,26 +552,26 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
content.getTags().clear();
|
||||
switch (dataItem.getModInfo().getModLoaderType()) {
|
||||
case FORGE:
|
||||
content.getTags().add(i18n("install.installer.forge"));
|
||||
content.addTag(i18n("install.installer.forge"));
|
||||
break;
|
||||
case CLEANROOM:
|
||||
content.getTags().add(i18n("install.installer.cleanroom"));
|
||||
content.addTag(i18n("install.installer.cleanroom"));
|
||||
break;
|
||||
case NEO_FORGED:
|
||||
content.getTags().add(i18n("install.installer.neoforge"));
|
||||
content.addTag(i18n("install.installer.neoforge"));
|
||||
break;
|
||||
case FABRIC:
|
||||
content.getTags().add(i18n("install.installer.fabric"));
|
||||
content.addTag(i18n("install.installer.fabric"));
|
||||
break;
|
||||
case LITE_LOADER:
|
||||
content.getTags().add(i18n("install.installer.liteloader"));
|
||||
content.addTag(i18n("install.installer.liteloader"));
|
||||
break;
|
||||
case QUILT:
|
||||
content.getTags().add(i18n("install.installer.quilt"));
|
||||
content.addTag(i18n("install.installer.quilt"));
|
||||
break;
|
||||
}
|
||||
if (dataItem.getMod() != null && I18n.isUseChinese()) {
|
||||
content.getTags().add(dataItem.getMod().getDisplayName());
|
||||
content.addTag(dataItem.getMod().getDisplayName());
|
||||
}
|
||||
content.setSubtitle(dataItem.getSubtitle());
|
||||
if (booleanProperty != null) {
|
||||
|
@ -245,7 +245,8 @@ public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.Backup
|
||||
item.setTitle(parseColorEscapes(skinnable.getBackupWorld().getWorldName()));
|
||||
item.setSubtitle(formatDateTime(skinnable.getBackupTime()) + (skinnable.count == 0 ? "" : " (" + skinnable.count + ")"));
|
||||
|
||||
if (world.getGameVersion() != null) item.getTags().add(world.getGameVersion());
|
||||
if (world.getGameVersion() != null)
|
||||
item.addTag(world.getGameVersion());
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -76,9 +76,9 @@ public final class WorldListItemSkin extends SkinBase<WorldListItem> {
|
||||
item.setSubtitle(i18n("world.datetime", formatDateTime(Instant.ofEpochMilli(world.getLastPlayed())), world.getGameVersion() == null ? i18n("message.unknown") : world.getGameVersion()));
|
||||
|
||||
if (world.getGameVersion() != null)
|
||||
item.getTags().add(world.getGameVersion());
|
||||
item.addTag(world.getGameVersion());
|
||||
if (world.isLocked())
|
||||
item.getTags().add(i18n("world.locked"));
|
||||
item.addTag(i18n("world.locked"));
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -28,10 +28,10 @@ import java.util.function.Function;
|
||||
|
||||
public final class AggregatedObservableList<T> {
|
||||
|
||||
protected final List<ObservableList<T>> lists = new ArrayList<>();
|
||||
final private List<Integer> sizes = new ArrayList<>();
|
||||
final private List<InternalListModificationListener> listeners = new ArrayList<>();
|
||||
final protected ObservableList<T> aggregatedList = FXCollections.observableArrayList();
|
||||
private final List<ObservableList<? extends T>> lists = new ArrayList<>();
|
||||
private final List<Integer> sizes = new ArrayList<>();
|
||||
private final List<InternalListModificationListener> listeners = new ArrayList<>();
|
||||
private final ObservableList<T> aggregatedList = FXCollections.observableArrayList();
|
||||
|
||||
public AggregatedObservableList() {
|
||||
|
||||
@ -46,7 +46,7 @@ public final class AggregatedObservableList<T> {
|
||||
return aggregatedList;
|
||||
}
|
||||
|
||||
public void appendList(@NotNull ObservableList<T> list) {
|
||||
public void appendList(@NotNull ObservableList<? extends T> list) {
|
||||
assert !lists.contains(list) : "List is already contained: " + list;
|
||||
lists.add(list);
|
||||
final InternalListModificationListener listener = new InternalListModificationListener(list);
|
||||
@ -59,7 +59,7 @@ public final class AggregatedObservableList<T> {
|
||||
"lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
|
||||
}
|
||||
|
||||
public void prependList(@NotNull ObservableList<T> list) {
|
||||
public void prependList(@NotNull ObservableList<? extends T> list) {
|
||||
assert !lists.contains(list) : "List is already contained: " + list;
|
||||
lists.add(0, list);
|
||||
final InternalListModificationListener listener = new InternalListModificationListener(list);
|
||||
@ -72,7 +72,7 @@ public final class AggregatedObservableList<T> {
|
||||
"lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
|
||||
}
|
||||
|
||||
public void removeList(@NotNull ObservableList<T> list) {
|
||||
public void removeList(@NotNull ObservableList<? extends T> list) {
|
||||
assert lists.size() == sizes.size() && lists.size() == listeners.size() :
|
||||
"lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
|
||||
final int index = lists.indexOf(list);
|
||||
@ -98,7 +98,7 @@ public final class AggregatedObservableList<T> {
|
||||
* @param list the list in question
|
||||
* @return the start index of this list in the aggregated List
|
||||
*/
|
||||
private int getStartIndex(@NotNull ObservableList<T> list) {
|
||||
private int getStartIndex(@NotNull ObservableList<? extends T> list) {
|
||||
int startIndex = 0;
|
||||
//System.out.println("=== searching startIndex of " + list);
|
||||
assert lists.size() == sizes.size() : "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size();
|
||||
@ -120,7 +120,7 @@ public final class AggregatedObservableList<T> {
|
||||
* @param startIndex the start of the list (retrieve with {@link #getStartIndex(ObservableList)}
|
||||
* @return the end index of this list in the aggregated List
|
||||
*/
|
||||
private int getEndIndex(@NotNull ObservableList<T> list, int startIndex) {
|
||||
private int getEndIndex(@NotNull ObservableList<? extends T> list, int startIndex) {
|
||||
assert lists.size() == sizes.size() : "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size();
|
||||
final int index = lists.indexOf(list);
|
||||
return startIndex + sizes.get(index) - 1;
|
||||
@ -129,9 +129,9 @@ public final class AggregatedObservableList<T> {
|
||||
private final class InternalListModificationListener implements ListChangeListener<T> {
|
||||
|
||||
@NotNull
|
||||
private final ObservableList<T> list;
|
||||
private final ObservableList<? extends T> list;
|
||||
|
||||
public InternalListModificationListener(@NotNull ObservableList<T> list) {
|
||||
public InternalListModificationListener(@NotNull ObservableList<? extends T> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user