PackSelectionScreen.java
net.minecraft.client.gui.screens.packs.PackSelectionScreen
信息
- 全限定名:net.minecraft.client.gui.screens.packs.PackSelectionScreen
- 类型:public class
- 包:net.minecraft.client.gui.screens.packs
- 源码路径:src/main/java/net/minecraft/client/gui/screens/packs/PackSelectionScreen.java
- 起始行号:L60
- 继承:Screen
- 职责:
TODO
字段/常量
-
LOGGER- 类型:
Logger - 修饰符:
private static final - 源码定位:
L61 - 说明:
TODO
- 类型:
-
AVAILABLE_TITLE- 类型:
Component - 修饰符:
private static final - 源码定位:
L62 - 说明:
TODO
- 类型:
-
SELECTED_TITLE- 类型:
Component - 修饰符:
private static final - 源码定位:
L63 - 说明:
TODO
- 类型:
-
OPEN_PACK_FOLDER_TITLE- 类型:
Component - 修饰符:
private static final - 源码定位:
L64 - 说明:
TODO
- 类型:
-
SEARCH- 类型:
Component - 修饰符:
private static final - 源码定位:
L65 - 说明:
TODO
- 类型:
-
LIST_WIDTH- 类型:
int - 修饰符:
private static final - 源码定位:
L66 - 说明:
TODO
- 类型:
-
HEADER_ELEMENT_SPACING- 类型:
int - 修饰符:
private static final - 源码定位:
L67 - 说明:
TODO
- 类型:
-
SEARCH_BOX_HEIGHT- 类型:
int - 修饰符:
private static final - 源码定位:
L68 - 说明:
TODO
- 类型:
-
DRAG_AND_DROP- 类型:
Component - 修饰符:
private static final - 源码定位:
L69 - 说明:
TODO
- 类型:
-
DIRECTORY_BUTTON_TOOLTIP- 类型:
Component - 修饰符:
private static final - 源码定位:
L70 - 说明:
TODO
- 类型:
-
RELOAD_COOLDOWN- 类型:
int - 修饰符:
private static final - 源码定位:
L71 - 说明:
TODO
- 类型:
-
DEFAULT_ICON- 类型:
Identifier - 修饰符:
private static final - 源码定位:
L72 - 说明:
TODO
- 类型:
-
layout- 类型:
HeaderAndFooterLayout - 修饰符:
private final - 源码定位:
L73 - 说明:
TODO
- 类型:
-
model- 类型:
PackSelectionModel - 修饰符:
private final - 源码定位:
L74 - 说明:
TODO
- 类型:
-
watcher- 类型:
PackSelectionScreen.Watcher - 修饰符:
private - 源码定位:
L75 - 说明:
TODO
- 类型:
-
ticksToReload- 类型:
long - 修饰符:
private - 源码定位:
L76 - 说明:
TODO
- 类型:
-
availablePackList- 类型:
TransferableSelectionList - 修饰符:
private - 源码定位:
L77 - 说明:
TODO
- 类型:
-
selectedPackList- 类型:
TransferableSelectionList - 修饰符:
private - 源码定位:
L78 - 说明:
TODO
- 类型:
-
search- 类型:
EditBox - 修饰符:
private - 源码定位:
L79 - 说明:
TODO
- 类型:
-
packDir- 类型:
Path - 修饰符:
private final - 源码定位:
L80 - 说明:
TODO
- 类型:
-
doneButton- 类型:
Button - 修饰符:
private - 源码定位:
L81 - 说明:
TODO
- 类型:
-
packIcons- 类型:
Map<String,Identifier> - 修饰符:
private final - 源码定位:
L82 - 说明:
TODO
- 类型:
内部类/嵌套类型
net.minecraft.client.gui.screens.packs.PackSelectionScreen.Watcher- 类型:
class - 修饰符:
private static - 源码定位:
L343 - 说明:
TODO
- 类型:
构造器
public PackSelectionScreen(PackRepository repository, Consumer<PackRepository> output, Path packDir, Component title) @ L84
- 构造器名:PackSelectionScreen
- 源码定位:L84
- 修饰符:public
参数:
- repository: PackRepository
- output: Consumer
- packDir: Path
- title: Component
说明:
TODO
方法
下面的方法块按源码顺序生成。
public void onClose() @ L91
- 方法名:onClose
- 源码定位:L91
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
private void closeWatcher() @ L97
- 方法名:closeWatcher
- 源码定位:L97
- 返回类型:void
- 修饰符:private
参数:
- 无
说明:
TODO
protected void init() @ L107
- 方法名:init
- 源码定位:L107
- 返回类型:void
- 修饰符:protected
参数:
- 无
说明:
TODO
protected void setInitialFocus() @ L131
- 方法名:setInitialFocus
- 源码定位:L131
- 返回类型:void
- 修饰符:protected
参数:
- 无
说明:
TODO
private void updateFilteredEntries(String value) @ L140
- 方法名:updateFilteredEntries
- 源码定位:L140
- 返回类型:void
- 修饰符:private
参数:
- value: String
说明:
TODO
private void filterEntries(String value, Stream<PackSelectionModel.Entry> oldEntries, TransferableSelectionList listToUpdate) @ L145
- 方法名:filterEntries
- 源码定位:L145
- 返回类型:void
- 修饰符:private
参数:
- value: String
- oldEntries: Stream<PackSelectionModel.Entry>
- listToUpdate: TransferableSelectionList
说明:
TODO
protected void repositionElements() @ L158
- 方法名:repositionElements
- 源码定位:L158
- 返回类型:void
- 修饰符:protected
参数:
- 无
说明:
TODO
public void tick() @ L170
- 方法名:tick
- 源码定位:L170
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
private void populateLists(PackSelectionModel.EntryBase transferredEntry) @ L188
- 方法名:populateLists
- 源码定位:L188
- 返回类型:void
- 修饰符:private
参数:
- transferredEntry: PackSelectionModel.EntryBase
说明:
TODO
private void reload() @ L206
- 方法名:reload
- 源码定位:L206
- 返回类型:void
- 修饰符:private
参数:
- 无
说明:
TODO
protected static void copyPacks(Minecraft minecraft, List<Path> files, Path targetDir) @ L213
- 方法名:copyPacks
- 源码定位:L213
- 返回类型:void
- 修饰符:protected static
参数:
- minecraft: Minecraft
- files: List
- targetDir: Path
说明:
TODO
public void onFilesDrop(List<Path> files) @ L235
- 方法名:onFilesDrop
- 源码定位:L235
- 返回类型:void
- 修饰符:public
参数:
- files: List
说明:
TODO
private static Stream<String> extractPackNames(Collection<Path> files) @ L306
- 方法名:extractPackNames
- 源码定位:L306
- 返回类型:Stream
- 修饰符:private static
参数:
- files: Collection
说明:
TODO
private Identifier loadPackIcon(TextureManager textureManager, Pack pack) @ L310
- 方法名:loadPackIcon
- 源码定位:L310
- 返回类型:Identifier
- 修饰符:private
参数:
- textureManager: TextureManager
- pack: Pack
说明:
TODO
private Identifier getPackIcon(Pack pack) @ L338
- 方法名:getPackIcon
- 源码定位:L338
- 返回类型:Identifier
- 修饰符:private
参数:
- pack: Pack
说明:
TODO
代码
@OnlyIn(Dist.CLIENT)
public class PackSelectionScreen extends Screen {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Component AVAILABLE_TITLE = Component.translatable("pack.available.title");
private static final Component SELECTED_TITLE = Component.translatable("pack.selected.title");
private static final Component OPEN_PACK_FOLDER_TITLE = Component.translatable("pack.openFolder");
private static final Component SEARCH = Component.translatable("gui.packSelection.search").withStyle(EditBox.SEARCH_HINT_STYLE);
private static final int LIST_WIDTH = 200;
private static final int HEADER_ELEMENT_SPACING = 4;
private static final int SEARCH_BOX_HEIGHT = 15;
private static final Component DRAG_AND_DROP = Component.translatable("pack.dropInfo").withStyle(ChatFormatting.GRAY);
private static final Component DIRECTORY_BUTTON_TOOLTIP = Component.translatable("pack.folderInfo");
private static final int RELOAD_COOLDOWN = 20;
private static final Identifier DEFAULT_ICON = Identifier.withDefaultNamespace("textures/misc/unknown_pack.png");
private final HeaderAndFooterLayout layout = new HeaderAndFooterLayout(this);
private final PackSelectionModel model;
private PackSelectionScreen.@Nullable Watcher watcher;
private long ticksToReload;
private @Nullable TransferableSelectionList availablePackList;
private @Nullable TransferableSelectionList selectedPackList;
private @Nullable EditBox search;
private final Path packDir;
private @Nullable Button doneButton;
private final Map<String, Identifier> packIcons = Maps.newHashMap();
public PackSelectionScreen(PackRepository repository, Consumer<PackRepository> output, Path packDir, Component title) {
super(title);
this.model = new PackSelectionModel(this::populateLists, this::getPackIcon, repository, output);
this.packDir = packDir;
this.watcher = PackSelectionScreen.Watcher.create(packDir);
}
@Override
public void onClose() {
this.model.commit();
this.closeWatcher();
}
private void closeWatcher() {
if (this.watcher != null) {
try {
this.watcher.close();
this.watcher = null;
} catch (Exception var2) {
}
}
}
@Override
protected void init() {
this.layout.setHeaderHeight(4 + 9 + 4 + 9 + 4 + 15 + 4);
LinearLayout header = this.layout.addToHeader(LinearLayout.vertical().spacing(4));
header.defaultCellSetting().alignHorizontallyCenter();
header.addChild(new StringWidget(this.getTitle(), this.font));
header.addChild(new StringWidget(DRAG_AND_DROP, this.font));
this.search = header.addChild(new EditBox(this.font, 0, 0, 200, 15, Component.empty()));
this.search.setHint(SEARCH);
this.search.setResponder(this::updateFilteredEntries);
this.availablePackList = this.layout.addToContents(new TransferableSelectionList(this.minecraft, this, 200, this.height - 66, AVAILABLE_TITLE));
this.selectedPackList = this.layout.addToContents(new TransferableSelectionList(this.minecraft, this, 200, this.height - 66, SELECTED_TITLE));
LinearLayout footer = this.layout.addToFooter(LinearLayout.horizontal().spacing(8));
footer.addChild(
Button.builder(OPEN_PACK_FOLDER_TITLE, button -> Util.getPlatform().openPath(this.packDir))
.tooltip(Tooltip.create(DIRECTORY_BUTTON_TOOLTIP))
.build()
);
this.doneButton = footer.addChild(Button.builder(CommonComponents.GUI_DONE, button -> this.onClose()).build());
this.layout.visitWidgets(x$0 -> this.addRenderableWidget(x$0));
this.repositionElements();
this.reload();
}
@Override
protected void setInitialFocus() {
if (this.search != null) {
this.setInitialFocus(this.search);
} else {
super.setInitialFocus();
}
}
private void updateFilteredEntries(String value) {
this.filterEntries(value, this.model.getSelected(), this.selectedPackList);
this.filterEntries(value, this.model.getUnselected(), this.availablePackList);
}
private void filterEntries(String value, Stream<PackSelectionModel.Entry> oldEntries, @Nullable TransferableSelectionList listToUpdate) {
if (listToUpdate != null) {
String lowerCaseValue = value.toLowerCase(Locale.ROOT);
Stream<PackSelectionModel.Entry> filteredEntries = oldEntries.filter(
packEntry -> value.isBlank()
|| packEntry.getId().toLowerCase(Locale.ROOT).contains(lowerCaseValue)
|| packEntry.getTitle().getString().toLowerCase(Locale.ROOT).contains(lowerCaseValue)
|| packEntry.getDescription().getString().toLowerCase(Locale.ROOT).contains(lowerCaseValue)
);
listToUpdate.updateList(filteredEntries, null);
}
}
@Override
protected void repositionElements() {
this.layout.arrangeElements();
if (this.availablePackList != null) {
this.availablePackList.updateSizeAndPosition(200, this.layout.getContentHeight(), this.width / 2 - 15 - 200, this.layout.getHeaderHeight());
}
if (this.selectedPackList != null) {
this.selectedPackList.updateSizeAndPosition(200, this.layout.getContentHeight(), this.width / 2 + 15, this.layout.getHeaderHeight());
}
}
@Override
public void tick() {
if (this.watcher != null) {
try {
if (this.watcher.pollForChanges()) {
this.ticksToReload = 20L;
}
} catch (IOException var2) {
LOGGER.warn("Failed to poll for directory {} changes, stopping", this.packDir);
this.closeWatcher();
}
}
if (this.ticksToReload > 0L && --this.ticksToReload == 0L) {
this.reload();
}
}
private void populateLists(PackSelectionModel.@Nullable EntryBase transferredEntry) {
if (this.selectedPackList != null) {
this.selectedPackList.updateList(this.model.getSelected(), transferredEntry);
}
if (this.availablePackList != null) {
this.availablePackList.updateList(this.model.getUnselected(), transferredEntry);
}
if (this.search != null) {
this.updateFilteredEntries(this.search.getValue());
}
if (this.doneButton != null) {
this.doneButton.active = !this.selectedPackList.children().isEmpty();
}
}
private void reload() {
this.model.findNewPacks();
this.populateLists(null);
this.ticksToReload = 0L;
this.packIcons.clear();
}
protected static void copyPacks(Minecraft minecraft, List<Path> files, Path targetDir) {
MutableBoolean showErrorToast = new MutableBoolean();
files.forEach(pack -> {
try (Stream<Path> contents = Files.walk(pack)) {
contents.forEach(path -> {
try {
Util.copyBetweenDirs(pack.getParent(), targetDir, path);
} catch (IOException var5) {
LOGGER.warn("Failed to copy datapack file from {} to {}", path, targetDir, var5);
showErrorToast.setTrue();
}
});
} catch (IOException var8) {
LOGGER.warn("Failed to copy datapack file from {} to {}", pack, targetDir);
showErrorToast.setTrue();
}
});
if (showErrorToast.isTrue()) {
SystemToast.onPackCopyFailure(minecraft, targetDir.toString());
}
}
@Override
public void onFilesDrop(List<Path> files) {
String names = extractPackNames(files).collect(Collectors.joining(", "));
this.minecraft
.setScreen(
new ConfirmScreen(
result -> {
if (result) {
List<Path> packCandidates = new ArrayList<>(files.size());
Set<Path> leftoverPacks = new HashSet<>(files);
PackDetector<Path> packDetector = new PackDetector<Path>(this.minecraft.directoryValidator()) {
{
Objects.requireNonNull(PackSelectionScreen.this);
}
protected Path createZipPack(Path content) {
return content;
}
protected Path createDirectoryPack(Path content) {
return content;
}
};
List<ForbiddenSymlinkInfo> issues = new ArrayList<>();
for (Path path : files) {
try {
Path candidate = packDetector.detectPackResources(path, issues);
if (candidate == null) {
LOGGER.warn("Path {} does not seem like pack", path);
} else {
packCandidates.add(candidate);
leftoverPacks.remove(candidate);
}
} catch (IOException var10) {
LOGGER.warn("Failed to check {} for packs", path, var10);
}
}
if (!issues.isEmpty()) {
this.minecraft.setScreen(NoticeWithLinkScreen.createPackSymlinkWarningScreen(() -> this.minecraft.setScreen(this)));
return;
}
if (!packCandidates.isEmpty()) {
copyPacks(this.minecraft, packCandidates, this.packDir);
this.reload();
}
if (!leftoverPacks.isEmpty()) {
String leftoverNames = extractPackNames(leftoverPacks).collect(Collectors.joining(", "));
this.minecraft
.setScreen(
new AlertScreen(
() -> this.minecraft.setScreen(this),
Component.translatable("pack.dropRejected.title"),
Component.translatable("pack.dropRejected.message", leftoverNames)
)
);
return;
}
}
this.minecraft.setScreen(this);
},
Component.translatable("pack.dropConfirm"),
Component.literal(names)
)
);
}
private static Stream<String> extractPackNames(Collection<Path> files) {
return files.stream().map(Path::getFileName).map(Path::toString);
}
private Identifier loadPackIcon(TextureManager textureManager, Pack pack) {
try {
Identifier var9;
try (PackResources packResources = pack.open()) {
IoSupplier<InputStream> resource = packResources.getRootResource("pack.png");
if (resource == null) {
return DEFAULT_ICON;
}
String id = pack.getId();
Identifier location = Identifier.withDefaultNamespace(
"pack/" + Util.sanitizeName(id, Identifier::validPathChar) + "/" + Hashing.sha1().hashUnencodedChars(id) + "/icon"
);
try (InputStream stream = resource.get()) {
NativeImage iconImage = NativeImage.read(stream);
textureManager.register(location, new DynamicTexture(location::toString, iconImage));
var9 = location;
}
}
return var9;
} catch (Exception var14) {
LOGGER.warn("Failed to load icon from pack {}", pack.getId(), var14);
return DEFAULT_ICON;
}
}
private Identifier getPackIcon(Pack pack) {
return this.packIcons.computeIfAbsent(pack.getId(), s -> this.loadPackIcon(this.minecraft.getTextureManager(), pack));
}
@OnlyIn(Dist.CLIENT)
private static class Watcher implements AutoCloseable {
private final WatchService watcher;
private final Path packPath;
public Watcher(Path packPath) throws IOException {
this.packPath = packPath;
this.watcher = packPath.getFileSystem().newWatchService();
try {
this.watchDir(packPath);
try (DirectoryStream<Path> paths = Files.newDirectoryStream(packPath)) {
for (Path path : paths) {
if (Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) {
this.watchDir(path);
}
}
}
} catch (Exception var7) {
this.watcher.close();
throw var7;
}
}
public static PackSelectionScreen.@Nullable Watcher create(Path packDir) {
try {
return new PackSelectionScreen.Watcher(packDir);
} catch (IOException var2) {
PackSelectionScreen.LOGGER.warn("Failed to initialize pack directory {} monitoring", packDir, var2);
return null;
}
}
private void watchDir(Path packPath) throws IOException {
packPath.register(this.watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
}
public boolean pollForChanges() throws IOException {
boolean hasChanges = false;
WatchKey key;
while ((key = this.watcher.poll()) != null) {
for (WatchEvent<?> watchEvent : key.pollEvents()) {
hasChanges = true;
if (key.watchable() == this.packPath && watchEvent.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
Path newPath = this.packPath.resolve((Path)watchEvent.context());
if (Files.isDirectory(newPath, LinkOption.NOFOLLOW_LINKS)) {
this.watchDir(newPath);
}
}
}
key.reset();
}
return hasChanges;
}
@Override
public void close() throws IOException {
this.watcher.close();
}
}
}引用的其他类
-
- 引用位置:
方法调用 - 关联成员:
NativeImage.read()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
字段/方法调用 - 关联成员:
Button.builder()
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
EditBox()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
StringWidget()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Tooltip.create()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SystemToast.onPackCopyFailure()
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
HeaderAndFooterLayout()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
LinearLayout.horizontal(), LinearLayout.vertical()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
AlertScreen()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ConfirmScreen()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
NoticeWithLinkScreen.createPackSymlinkWarningScreen()
- 引用位置:
-
- 引用位置:
继承
- 引用位置:
-
- 引用位置:
参数/字段/构造调用 - 关联成员:
PackSelectionModel()
- 引用位置:
-
- 引用位置:
参数/字段/构造调用 - 关联成员:
TransferableSelectionList()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
DynamicTexture()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/字段/方法调用 - 关联成员:
Component.empty(), Component.literal(), Component.translatable()
- 引用位置:
-
- 引用位置:
字段/方法调用/返回值 - 关联成员:
Identifier.withDefaultNamespace()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Util.copyBetweenDirs(), Util.getPlatform(), Util.sanitizeName()
- 引用位置: