修复 JFXListViewSkin 大量触发 ListCell#updateItem 的问题 (#5056)

This commit is contained in:
Glavo
2025-12-24 00:41:23 +08:00
committed by GitHub
parent 3eea76784d
commit c13b5e15d4
10 changed files with 13 additions and 109 deletions

View File

@@ -25,7 +25,6 @@ import com.jfoenix.controls.JFXListView;
import com.jfoenix.effects.JFXDepthManager; import com.jfoenix.effects.JFXDepthManager;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.control.skin.VirtualFlow; import javafx.scene.control.skin.VirtualFlow;
import javafx.scene.layout.Region;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
// https://github.com/HMCL-dev/HMCL/issues/4720 // https://github.com/HMCL-dev/HMCL/issues/4720
@@ -50,47 +49,4 @@ public class JFXListViewSkin<T> extends ListViewSkin<T> {
return 200; return 200;
} }
@Override
protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
final int itemsCount = getSkinnable().getItems().size();
if (getSkinnable().maxHeightProperty().isBound() || itemsCount <= 0) {
return super.computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);
}
final double fixedCellSize = getSkinnable().getFixedCellSize();
double computedHeight = fixedCellSize != Region.USE_COMPUTED_SIZE ?
fixedCellSize * itemsCount + snapVerticalInsets() : estimateHeight();
double height = super.computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);
if (height > computedHeight) {
height = computedHeight;
}
if (getSkinnable().getMaxHeight() > 0 && computedHeight > getSkinnable().getMaxHeight()) {
return getSkinnable().getMaxHeight();
}
return height;
}
private double estimateHeight() {
// compute the border/padding for the list
double borderWidth = snapVerticalInsets();
// compute the gap between list cells
JFXListView<T> listview = (JFXListView<T>) getSkinnable();
double gap = listview.isExpanded() ? ((JFXListView<T>) getSkinnable()).getVerticalGap() * (getSkinnable().getItems()
.size()) : 0;
// compute the height of each list cell
double cellsHeight = 0;
for (int i = 0; i < flow.getCellCount(); i++) {
ListCell<T> cell = flow.getCell(i);
cellsHeight += cell.getHeight();
}
return cellsHeight + gap + borderWidth;
}
private double snapVerticalInsets() {
return getSkinnable().snappedBottomInset() + getSkinnable().snappedTopInset();
}
} }

View File

@@ -1309,17 +1309,11 @@ public final class FXUtils {
} }
public static <T> Callback<ListView<T>, ListCell<T>> jfxListCellFactory(Function<T, Node> graphicBuilder) { public static <T> Callback<ListView<T>, ListCell<T>> jfxListCellFactory(Function<T, Node> graphicBuilder) {
Holder<Object> lastCell = new Holder<>();
return view -> new JFXListCell<T>() { return view -> new JFXListCell<T>() {
@Override @Override
public void updateItem(T item, boolean empty) { public void updateItem(T item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.value && !isVisible())
return;
lastCell.value = this;
if (!empty) { if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY); setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(graphicBuilder.apply(item)); setGraphic(graphicBuilder.apply(item));

View File

@@ -46,7 +46,6 @@ import org.jackhuang.hmcl.setting.StyleSheets;
import org.jackhuang.hmcl.theme.Themes; import org.jackhuang.hmcl.theme.Themes;
import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel; import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel;
import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.CircularArrayList; import org.jackhuang.hmcl.util.CircularArrayList;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Log4jLevel; import org.jackhuang.hmcl.util.Log4jLevel;
@@ -344,8 +343,7 @@ public final class LogWindow extends Stage {
listView.setStyle("-fx-font-family: \"" + Lang.requireNonNullElse(config().getFontFamily(), FXUtils.DEFAULT_MONOSPACE_FONT) listView.setStyle("-fx-font-family: \"" + Lang.requireNonNullElse(config().getFontFamily(), FXUtils.DEFAULT_MONOSPACE_FONT)
+ "\"; -fx-font-size: " + config().getFontSize() + "px;"); + "\"; -fx-font-size: " + config().getFontSize() + "px;");
Holder<Object> lastCell = new Holder<>(); listView.setCellFactory(x -> new ListCell<>() {
listView.setCellFactory(x -> new ListCell<Log>() {
{ {
x.setSelectionModel(new NoneMultipleSelectionModel<>()); x.setSelectionModel(new NoneMultipleSelectionModel<>());
getStyleClass().add("log-window-list-cell"); getStyleClass().add("log-window-list-cell");
@@ -389,11 +387,6 @@ public final class LogWindow extends Stage {
protected void updateItem(Log item, boolean empty) { protected void updateItem(Log item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.value && !isVisible())
return;
lastCell.value = this;
pseudoClassStateChanged(EMPTY, empty); pseudoClassStateChanged(EMPTY, empty);
pseudoClassStateChanged(FATAL, !empty && item.getLevel() == Log4jLevel.FATAL); pseudoClassStateChanged(FATAL, !empty && item.getLevel() == Log4jLevel.FATAL);
pseudoClassStateChanged(ERROR, !empty && item.getLevel() == Log4jLevel.ERROR); pseudoClassStateChanged(ERROR, !empty && item.getLevel() == Log4jLevel.ERROR);

View File

@@ -24,17 +24,14 @@ import javafx.scene.control.ListCell;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.Holder;
public abstract class MDListCell<T> extends ListCell<T> { public abstract class MDListCell<T> extends ListCell<T> {
private final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected"); private final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
private final StackPane container = new StackPane(); private final StackPane container = new StackPane();
private final StackPane root = new StackPane(); private final StackPane root = new StackPane();
private final Holder<Object> lastCell;
public MDListCell(JFXListView<T> listView, Holder<Object> lastCell) { public MDListCell(JFXListView<T> listView) {
this.lastCell = lastCell;
setText(null); setText(null);
setGraphic(null); setGraphic(null);
@@ -58,13 +55,6 @@ public abstract class MDListCell<T> extends ListCell<T> {
protected void updateItem(T item, boolean empty) { protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (lastCell != null) {
if (this == lastCell.value && !isVisible())
return;
lastCell.value = this;
}
updateControl(item, empty); updateControl(item, empty);
if (empty) { if (empty) {
setGraphic(null); setGraphic(null);

View File

@@ -92,8 +92,6 @@ public final class TaskListPane extends StackPane {
private final ObjectProperty<Insets> progressNodePadding = new SimpleObjectProperty<>(Insets.EMPTY); private final ObjectProperty<Insets> progressNodePadding = new SimpleObjectProperty<>(Insets.EMPTY);
private final DoubleProperty cellWidth = new SimpleDoubleProperty(); private final DoubleProperty cellWidth = new SimpleDoubleProperty();
private Cell lastCell;
public TaskListPane() { public TaskListPane() {
listView.setPadding(new Insets(12, 0, 0, 0)); listView.setPadding(new Insets(12, 0, 0, 0));
listView.setCellFactory(l -> new Cell()); listView.setCellFactory(l -> new Cell());
@@ -316,11 +314,6 @@ public final class TaskListPane extends StackPane {
protected void updateItem(Node item, boolean empty) { protected void updateItem(Node item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell && !isVisible())
return;
lastCell = this;
pane.paddingProperty().unbind(); pane.paddingProperty().unbind();
title.textProperty().unbind(); title.textProperty().unbind();
message.textProperty().unbind(); message.textProperty().unbind();

View File

@@ -57,7 +57,6 @@ import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.wizard.Navigation; import org.jackhuang.hmcl.ui.wizard.Navigation;
import org.jackhuang.hmcl.ui.wizard.Refreshable; import org.jackhuang.hmcl.ui.wizard.Refreshable;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.NativePatcher; import org.jackhuang.hmcl.util.NativePatcher;
import org.jackhuang.hmcl.util.SettingsMap; import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
@@ -164,10 +163,7 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
private final ImageView imageView = new ImageView(); private final ImageView imageView = new ImageView();
private final StackPane pane = new StackPane(); private final StackPane pane = new StackPane();
private final Holder<RemoteVersionListCell> lastCell; RemoteVersionListCell(VersionsPage control) {
RemoteVersionListCell(Holder<RemoteVersionListCell> lastCell, VersionsPage control) {
this.lastCell = lastCell;
this.control = control; this.control = control;
HBox hbox = new HBox(16); HBox hbox = new HBox(16);
@@ -222,11 +218,6 @@ public final class VersionsPage extends Control implements WizardPage, Refreshab
public void updateItem(RemoteVersion remoteVersion, boolean empty) { public void updateItem(RemoteVersion remoteVersion, boolean empty) {
super.updateItem(remoteVersion, empty); super.updateItem(remoteVersion, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.value && !isVisible())
return;
lastCell.value = this;
if (empty) { if (empty) {
setGraphic(null); setGraphic(null);
return; return;
@@ -398,8 +389,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<>(); list.setCellFactory(listView -> new RemoteVersionListCell(control));
list.setCellFactory(listView -> new RemoteVersionListCell(lastCell, control));
ComponentList.setVgrow(list, Priority.ALWAYS); ComponentList.setVgrow(list, Priority.ALWAYS);

View File

@@ -27,7 +27,6 @@ import javafx.scene.control.TreeView;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.util.Callback; import javafx.util.Callback;
import org.jackhuang.hmcl.util.Holder;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.EnumMap; import java.util.EnumMap;
@@ -45,7 +44,6 @@ public final class NBTTreeView extends JFXTreeView<Tag> {
} }
private static Callback<TreeView<Tag>, TreeCell<Tag>> cellFactory() { private static Callback<TreeView<Tag>, TreeCell<Tag>> cellFactory() {
Holder<Object> lastCell = new Holder<>();
EnumMap<NBTTagType, Image> icons = new EnumMap<>(NBTTagType.class); EnumMap<NBTTagType, Image> icons = new EnumMap<>(NBTTagType.class);
return view -> new TreeCell<>() { return view -> new TreeCell<>() {
@@ -69,11 +67,6 @@ public final class NBTTreeView extends JFXTreeView<Tag> {
public void updateItem(Tag item, boolean empty) { public void updateItem(Tag item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.value && !isVisible())
return;
lastCell.value = this;
ImageView imageView = (ImageView) this.getGraphic(); ImageView imageView = (ImageView) this.getGraphic();
if (imageView == null) { if (imageView == null) {
imageView = new ImageView(); imageView = new ImageView();

View File

@@ -54,7 +54,6 @@ import org.jackhuang.hmcl.ui.construct.ComponentList;
import org.jackhuang.hmcl.ui.construct.MDListCell; import org.jackhuang.hmcl.ui.construct.MDListCell;
import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -180,8 +179,7 @@ final class DatapackListPageSkin extends SkinBase<DatapackListPage> {
center.getStyleClass().add("large-spinner-pane"); center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(skinnable.loadingProperty()); center.loadingProperty().bind(skinnable.loadingProperty());
Holder<Object> lastCell = new Holder<>(); listView.setCellFactory(x -> new DatapackInfoListCell(listView));
listView.setCellFactory(x -> new DatapackInfoListCell(listView, lastCell));
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
this.listView.setItems(filteredList); this.listView.setItems(filteredList);
@@ -304,8 +302,8 @@ final class DatapackListPageSkin extends SkinBase<DatapackListPage> {
final TwoLineListItem content = new TwoLineListItem(); final TwoLineListItem content = new TwoLineListItem();
BooleanProperty booleanProperty; BooleanProperty booleanProperty;
DatapackInfoListCell(JFXListView<DatapackInfoObject> listView, Holder<Object> lastCell) { DatapackInfoListCell(JFXListView<DatapackInfoObject> listView) {
super(listView, lastCell); super(listView);
HBox container = new HBox(8); HBox container = new HBox(8);
container.setPickOnBounds(false); container.setPickOnBounds(false);

View File

@@ -189,8 +189,7 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
center.getStyleClass().add("large-spinner-pane"); center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(skinnable.loadingProperty()); center.loadingProperty().bind(skinnable.loadingProperty());
Holder<Object> lastCell = new Holder<>(); listView.setCellFactory(x -> new ModInfoListCell(listView));
listView.setCellFactory(x -> new ModInfoListCell(listView, lastCell));
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
Bindings.bindContent(listView.getItems(), skinnable.getItems()); Bindings.bindContent(listView.getItems(), skinnable.getItems());
skinnable.getItems().addListener((ListChangeListener<? super ModInfoObject>) c -> { skinnable.getItems().addListener((ListChangeListener<? super ModInfoObject>) c -> {
@@ -562,8 +561,8 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
Tooltip warningTooltip; Tooltip warningTooltip;
ModInfoListCell(JFXListView<ModInfoObject> listView, Holder<Object> lastCell) { ModInfoListCell(JFXListView<ModInfoObject> listView) {
super(listView, lastCell); super(listView);
this.getStyleClass().add("mod-info-list-cell"); this.getStyleClass().add("mod-info-list-cell");

View File

@@ -24,7 +24,6 @@ import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.ListPageBase; import org.jackhuang.hmcl.ui.ListPageBase;
import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -156,8 +155,7 @@ public final class ResourcepackListPage extends ListPageBase<ResourcepackListPag
center.getStyleClass().add("large-spinner-pane"); center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(control.loadingProperty()); center.loadingProperty().bind(control.loadingProperty());
Holder<Object> lastCell = new Holder<>(); listView.setCellFactory(x -> new ResourcepackListCell(listView, control));
listView.setCellFactory(x -> new ResourcepackListCell(listView, lastCell, control));
Bindings.bindContent(listView.getItems(), control.getItems()); Bindings.bindContent(listView.getItems(), control.getItems());
center.setContent(listView); center.setContent(listView);
@@ -210,8 +208,8 @@ public final class ResourcepackListPage extends ListPageBase<ResourcepackListPag
private final JFXButton btnDelete = new JFXButton(); private final JFXButton btnDelete = new JFXButton();
private final ResourcepackListPage page; private final ResourcepackListPage page;
public ResourcepackListCell(JFXListView<ResourcepackInfoObject> listView, Holder<Object> lastCell, ResourcepackListPage page) { public ResourcepackListCell(JFXListView<ResourcepackInfoObject> listView, ResourcepackListPage page) {
super(listView, lastCell); super(listView);
this.page = page; this.page = page;