重构 SchematicsPage 以使用 ListCell 展示元素 (#5276)

This commit is contained in:
Glavo
2026-01-21 22:04:45 +08:00
committed by GitHub
parent db38b4d532
commit 20249ec4bf

View File

@@ -19,8 +19,10 @@ package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialogLayout;
import com.jfoenix.controls.JFXListView;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.image.Image;
@@ -30,6 +32,7 @@ import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.SVGPath;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.schematic.LitematicFile;
import org.jackhuang.hmcl.setting.Profile;
@@ -221,7 +224,7 @@ public final class SchematicsPage extends ListPageBase<SchematicsPage.Item> impl
getItems().addAll(item.children);
}
abstract class Item extends Control implements Comparable<Item> {
abstract sealed class Item implements Comparable<Item> {
boolean isDirectory() {
return this instanceof DirItem;
@@ -258,11 +261,6 @@ public final class SchematicsPage extends ListPageBase<SchematicsPage.Item> impl
return this.getName().compareTo(o.getName());
}
@Override
protected Skin<?> createDefaultSkin() {
return new ItemSkin(this);
}
}
private final class BackItem extends Item {
@@ -438,6 +436,10 @@ public final class SchematicsPage extends ListPageBase<SchematicsPage.Item> impl
return SVG.SCHEMA;
}
public @Nullable Image getImage() {
return image;
}
Node getIcon(int size) {
if (image == null) {
return super.getIcon(size);
@@ -540,62 +542,122 @@ public final class SchematicsPage extends ListPageBase<SchematicsPage.Item> impl
}
}
private static final class ItemSkin extends SkinBase<Item> {
public ItemSkin(Item item) {
super(item);
private static final class Cell extends ListCell<Item> {
BorderPane root = new BorderPane();
private final RipplerContainer graphics;
private final BorderPane root;
private final StackPane left;
private final TwoLineListItem center;
private final HBox right;
private final ImageView iconImageView;
private final SVGPath iconSVG;
private final StackPane iconSVGWrapper;
private final Tooltip tooltip = new Tooltip();
public Cell() {
this.root = new BorderPane();
root.getStyleClass().add("md-list-cell");
root.setPadding(new Insets(8));
{
StackPane left = new StackPane();
left.setMaxSize(32, 32);
left.setPrefSize(32, 32);
left.getChildren().add(item.getIcon(24));
this.left = new StackPane();
left.setPadding(new Insets(0, 8, 0, 0));
Path path = item.getPath();
if (path != null) {
FXUtils.installSlowTooltip(left, path.toAbsolutePath().normalize().toString());
}
this.iconImageView = new ImageView();
FXUtils.limitSize(iconImageView, 32, 32);
this.iconSVG = new SVGPath();
iconSVG.getStyleClass().add("svg");
iconSVG.setScaleX(32.0 / SVG.DEFAULT_SIZE);
iconSVG.setScaleY(32.0 / SVG.DEFAULT_SIZE);
this.iconSVGWrapper = new StackPane(new Group(iconSVG));
iconSVGWrapper.setAlignment(Pos.CENTER);
FXUtils.setLimitWidth(iconSVGWrapper, 32);
FXUtils.setLimitHeight(iconSVGWrapper, 32);
BorderPane.setAlignment(left, Pos.CENTER);
root.setLeft(left);
}
{
TwoLineListItem center = new TwoLineListItem();
center.setTitle(item.getName());
center.setSubtitle(item.getDescription());
this.center = new TwoLineListItem();
root.setCenter(center);
}
if (!(item instanceof BackItem)) {
HBox right = new HBox(8);
{
this.right = new HBox(8);
right.setAlignment(Pos.CENTER_RIGHT);
JFXButton btnReveal = new JFXButton();
FXUtils.installFastTooltip(btnReveal, i18n("reveal.in_file_manager"));
btnReveal.getStyleClass().add("toggle-icon4");
btnReveal.setGraphic(SVG.FOLDER_OPEN.createIcon());
btnReveal.setOnAction(event -> item.onReveal());
btnReveal.setOnAction(event -> {
Item item = getItem();
if (item != null && !(item instanceof BackItem))
item.onReveal();
});
JFXButton btnDelete = new JFXButton();
btnDelete.getStyleClass().add("toggle-icon4");
btnDelete.setGraphic(SVG.DELETE_FOREVER.createIcon());
btnDelete.setOnAction(event ->
btnDelete.setOnAction(event -> {
Item item = getItem();
if (item != null && !(item instanceof BackItem)) {
Controllers.confirm(i18n("button.remove.confirm"), i18n("button.remove"),
item::onDelete, null));
item::onDelete, null);
}
});
right.getChildren().setAll(btnReveal, btnDelete);
root.setRight(right);
}
RipplerContainer container = new RipplerContainer(root);
FXUtils.onClicked(container, item::onClick);
this.getChildren().add(container);
this.graphics = new RipplerContainer(root);
FXUtils.onClicked(graphics, () -> {
Item item = getItem();
if (item != null)
item.onClick();
});
}
@Override
protected void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
iconImageView.setImage(null);
if (empty || item == null) {
setGraphic(null);
center.setTitle("");
center.setSubtitle("");
} else {
if (item instanceof LitematicFileItem fileItem && fileItem.getImage() != null) {
iconImageView.setImage(fileItem.getImage());
left.getChildren().setAll(iconImageView);
} else {
iconSVG.setContent(item.getIcon().getPath());
left.getChildren().setAll(iconSVGWrapper);
}
center.setTitle(item.getName());
center.setSubtitle(item.getDescription());
Path path = item.getPath();
if (path != null) {
tooltip.setText(FileUtils.getAbsolutePath(path));
FXUtils.installSlowTooltip(left, tooltip);
} else {
tooltip.setText("");
Tooltip.uninstall(left, tooltip);
}
root.setRight(item instanceof BackItem ? null : right);
setGraphic(graphics);
}
}
}
@@ -612,5 +674,10 @@ public final class SchematicsPage extends ListPageBase<SchematicsPage.Item> impl
createToolbarButton2(i18n("schematics.create_directory"), SVG.CREATE_NEW_FOLDER, skinnable::onCreateDirectory)
);
}
@Override
protected ListCell<Item> createListCell(JFXListView<Item> listView) {
return new Cell();
}
}
}