Skip to content

重构 WorldListPage 以使用 ListCell 展示元素#5224

Merged
Glavo merged 8 commits into
HMCL-dev:mainfrom
Glavo:WorldListPage
Jan 14, 2026
Merged

重构 WorldListPage 以使用 ListCell 展示元素#5224
Glavo merged 8 commits into
HMCL-dev:mainfrom
Glavo:WorldListPage

Conversation

@Glavo
Copy link
Copy Markdown
Member

@Glavo Glavo commented Jan 13, 2026

No description provided.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors WorldListPage to use JavaFX's standard ListCell pattern for displaying world items, eliminating the need for custom WorldListItem and WorldListItemSkin classes.

Changes:

  • Replaced custom WorldListItem Control class with direct use of World domain objects in the list
  • Migrated UI rendering logic from WorldListItemSkin into a new WorldListCell inner class that properly implements the ListCell pattern
  • Moved world operation methods (export, delete, copy, reveal, launch, etc.) from WorldListItem to WorldListPage as they now operate directly on World objects

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
WorldListPage.java Changed generic type from WorldListItem to World, added new WorldListCell inner class implementing ListCell<World>, moved world operation methods from WorldListItem to this class, and added createListCell() override in WorldListPageSkin
WorldListItem.java Deleted - functionality moved to WorldListPage and WorldListCell
WorldListItemSkin.java Deleted - UI rendering logic moved to WorldListCell.updateItem() method

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

public void delete(World world) {
WorldManageUIUtils.delete(world, () -> this.getItems().remove(world));
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The delete method removes the world from the displayed items list but does not update the worlds field. This creates an inconsistency: if the user toggles the "show all" filter or triggers a refresh, the deleted world may reappear briefly until the refresh completes. Consider also removing the world from the worlds list or calling refresh() after deletion instead of just removing from the items.

Suggested change
WorldManageUIUtils.delete(world, () -> this.getItems().remove(world));
WorldManageUIUtils.delete(world, this::refresh);

Copilot uses AI. Check for mistakes.
Task.runAsync(() -> world.install(savesDir, name))
.whenComplete(Schedulers.javafx(), () -> {
itemsProperty().add(new WorldListItem(this, new World(savesDir.resolve(name)), backupsDir, profile, id));
itemsProperty().add(new World(savesDir.resolve(name)));
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a world is successfully installed, it's added directly to the items list but not to the worlds field. This creates inconsistency similar to the delete method: toggling filters or other operations that rely on the worlds field won't include the newly added world until a full refresh occurs. Consider calling refresh() after successful installation or updating both the worlds field and the items list.

Suggested change
itemsProperty().add(new World(savesDir.resolve(name)));
itemsProperty().add(new World(savesDir.resolve(name)));
// Ensure backing 'worlds' list and filters are updated as well
refresh();

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 162 to +163
resolve.run();
refresh();
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refresh() is called after resolve.run() instead of before it, which could lead to incorrect UI state. The resolve.run() callback likely dismisses the prompt dialog, and calling refresh() after it means the list might update after the dialog has already closed. This ordering could cause the new world to not appear in the list until a manual refresh. Consider moving refresh() call before resolve.run().

Suggested change
resolve.run();
refresh();
refresh();
resolve.run();

Copilot uses AI. Check for mistakes.
}
}).start();
Task.supplyAsync(Schedulers.io(), () -> {
// Ensure the game version number is parsed
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states 'Ensure the game version number is parsed' but the return value is ignored. This comment should clarify why we're calling this method for its side effects (likely to cache the version) rather than using the result.

Suggested change
// Ensure the game version number is parsed
// Preload and cache the game version number for this profile.
// The result is intentionally ignored; parsing may trigger repository-side caching.

Copilot uses AI. Check for mistakes.
return stream.toList();
}
}).whenComplete(Schedulers.javafx(), (result, exception) -> {
if (refreshCount != currentRefresh) {
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refreshCount comparison uses != instead of <, which could theoretically fail if refreshCount wraps around after Integer.MAX_VALUE refreshes. While unlikely in practice, using < would be more robust: if (currentRefresh < refreshCount).

Suggested change
if (refreshCount != currentRefresh) {
if (currentRefresh < refreshCount) {

Copilot uses AI. Check for mistakes.
} else {
imageView.setImage(world.getIcon() == null ? FXUtils.newBuiltinImage("/assets/img/unknown_server.png") : world.getIcon());
leftTooltip.setText(world.getFile().toString());
content.setTitle(world.getWorldName() != null ? parseColorEscapes(world.getWorldName()) : "");
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting an empty string when world.getWorldName() is null is inconsistent with the logic in the 'if (empty || world == null)' branch at line 311 which also sets title to "". Consider using a more descriptive placeholder or consistent null handling pattern.

Suggested change
content.setTitle(world.getWorldName() != null ? parseColorEscapes(world.getWorldName()) : "");
String worldName = world.getWorldName();
if (worldName == null || worldName.isEmpty()) {
Path worldFile = world.getFile();
worldName = worldFile != null && worldFile.getFileName() != null ? worldFile.getFileName().toString() : "";
}
content.setTitle(worldName.isEmpty() ? "" : parseColorEscapes(worldName));

Copilot uses AI. Check for mistakes.
@Glavo Glavo merged commit 7d76fea into HMCL-dev:main Jan 14, 2026
8 checks passed
@Glavo Glavo deleted the WorldListPage branch January 14, 2026 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants