282 lines
9 KiB
Java
282 lines
9 KiB
Java
package net.minecraft.client.gui.screens.inventory;
|
|
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.client.GameNarrator;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.gui.GuiGraphics;
|
|
import net.minecraft.client.gui.components.Button;
|
|
import net.minecraft.client.gui.screens.Screen;
|
|
import net.minecraft.client.renderer.RenderType;
|
|
import net.minecraft.core.component.DataComponents;
|
|
import net.minecraft.network.chat.ClickEvent;
|
|
import net.minecraft.network.chat.CommonComponents;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.chat.FormattedText;
|
|
import net.minecraft.network.chat.Style;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.util.FormattedCharSequence;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.component.WritableBookContent;
|
|
import net.minecraft.world.item.component.WrittenBookContent;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class BookViewScreen extends Screen {
|
|
public static final int PAGE_INDICATOR_TEXT_Y_OFFSET = 16;
|
|
public static final int PAGE_TEXT_X_OFFSET = 36;
|
|
public static final int PAGE_TEXT_Y_OFFSET = 30;
|
|
private static final int BACKGROUND_TEXTURE_WIDTH = 256;
|
|
private static final int BACKGROUND_TEXTURE_HEIGHT = 256;
|
|
public static final BookViewScreen.BookAccess EMPTY_ACCESS = new BookViewScreen.BookAccess(List.of());
|
|
public static final ResourceLocation BOOK_LOCATION = ResourceLocation.withDefaultNamespace("textures/gui/book.png");
|
|
protected static final int TEXT_WIDTH = 114;
|
|
protected static final int TEXT_HEIGHT = 128;
|
|
protected static final int IMAGE_WIDTH = 192;
|
|
protected static final int IMAGE_HEIGHT = 192;
|
|
private BookViewScreen.BookAccess bookAccess;
|
|
private int currentPage;
|
|
/**
|
|
* Holds a copy of the page text, split into page width lines
|
|
*/
|
|
private List<FormattedCharSequence> cachedPageComponents = Collections.emptyList();
|
|
private int cachedPage = -1;
|
|
private Component pageMsg = CommonComponents.EMPTY;
|
|
private PageButton forwardButton;
|
|
private PageButton backButton;
|
|
/**
|
|
* Determines if a sound is played when the page is turned
|
|
*/
|
|
private final boolean playTurnSound;
|
|
|
|
public BookViewScreen(BookViewScreen.BookAccess bookAccess) {
|
|
this(bookAccess, true);
|
|
}
|
|
|
|
public BookViewScreen() {
|
|
this(EMPTY_ACCESS, false);
|
|
}
|
|
|
|
private BookViewScreen(BookViewScreen.BookAccess bookAccess, boolean playTurnSound) {
|
|
super(GameNarrator.NO_TITLE);
|
|
this.bookAccess = bookAccess;
|
|
this.playTurnSound = playTurnSound;
|
|
}
|
|
|
|
public void setBookAccess(BookViewScreen.BookAccess bookAccess) {
|
|
this.bookAccess = bookAccess;
|
|
this.currentPage = Mth.clamp(this.currentPage, 0, bookAccess.getPageCount());
|
|
this.updateButtonVisibility();
|
|
this.cachedPage = -1;
|
|
}
|
|
|
|
/**
|
|
* Moves the book to the specified page and returns true if it exists, {@code false} otherwise.
|
|
*/
|
|
public boolean setPage(int pageNum) {
|
|
int i = Mth.clamp(pageNum, 0, this.bookAccess.getPageCount() - 1);
|
|
if (i != this.currentPage) {
|
|
this.currentPage = i;
|
|
this.updateButtonVisibility();
|
|
this.cachedPage = -1;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* I'm not sure why this exists. The function it calls is public and does all the work.
|
|
*/
|
|
protected boolean forcePage(int pageNum) {
|
|
return this.setPage(pageNum);
|
|
}
|
|
|
|
@Override
|
|
protected void init() {
|
|
this.createMenuControls();
|
|
this.createPageControlButtons();
|
|
}
|
|
|
|
protected void createMenuControls() {
|
|
this.addRenderableWidget(Button.builder(CommonComponents.GUI_DONE, button -> this.onClose()).bounds(this.width / 2 - 100, 196, 200, 20).build());
|
|
}
|
|
|
|
protected void createPageControlButtons() {
|
|
int i = (this.width - 192) / 2;
|
|
int j = 2;
|
|
this.forwardButton = this.addRenderableWidget(new PageButton(i + 116, 159, true, button -> this.pageForward(), this.playTurnSound));
|
|
this.backButton = this.addRenderableWidget(new PageButton(i + 43, 159, false, button -> this.pageBack(), this.playTurnSound));
|
|
this.updateButtonVisibility();
|
|
}
|
|
|
|
private int getNumPages() {
|
|
return this.bookAccess.getPageCount();
|
|
}
|
|
|
|
/**
|
|
* Moves the display back one page
|
|
*/
|
|
protected void pageBack() {
|
|
if (this.currentPage > 0) {
|
|
this.currentPage--;
|
|
}
|
|
|
|
this.updateButtonVisibility();
|
|
}
|
|
|
|
/**
|
|
* Moves the display forward one page
|
|
*/
|
|
protected void pageForward() {
|
|
if (this.currentPage < this.getNumPages() - 1) {
|
|
this.currentPage++;
|
|
}
|
|
|
|
this.updateButtonVisibility();
|
|
}
|
|
|
|
private void updateButtonVisibility() {
|
|
this.forwardButton.visible = this.currentPage < this.getNumPages() - 1;
|
|
this.backButton.visible = this.currentPage > 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
|
if (super.keyPressed(keyCode, scanCode, modifiers)) {
|
|
return true;
|
|
} else {
|
|
switch (keyCode) {
|
|
case 266:
|
|
this.backButton.onPress();
|
|
return true;
|
|
case 267:
|
|
this.forwardButton.onPress();
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
|
super.render(guiGraphics, mouseX, mouseY, partialTick);
|
|
int i = (this.width - 192) / 2;
|
|
int j = 2;
|
|
if (this.cachedPage != this.currentPage) {
|
|
FormattedText formattedText = this.bookAccess.getPage(this.currentPage);
|
|
this.cachedPageComponents = this.font.split(formattedText, 114);
|
|
this.pageMsg = Component.translatable("book.pageIndicator", this.currentPage + 1, Math.max(this.getNumPages(), 1));
|
|
}
|
|
|
|
this.cachedPage = this.currentPage;
|
|
int k = this.font.width(this.pageMsg);
|
|
guiGraphics.drawString(this.font, this.pageMsg, i - k + 192 - 44, 18, 0, false);
|
|
int l = Math.min(128 / 9, this.cachedPageComponents.size());
|
|
|
|
for (int m = 0; m < l; m++) {
|
|
FormattedCharSequence formattedCharSequence = (FormattedCharSequence)this.cachedPageComponents.get(m);
|
|
guiGraphics.drawString(this.font, formattedCharSequence, i + 36, 32 + m * 9, 0, false);
|
|
}
|
|
|
|
Style style = this.getClickedComponentStyleAt(mouseX, mouseY);
|
|
if (style != null) {
|
|
guiGraphics.renderComponentHoverEffect(this.font, style, mouseX, mouseY);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
|
this.renderTransparentBackground(guiGraphics);
|
|
guiGraphics.blit(RenderType::guiTextured, BOOK_LOCATION, (this.width - 192) / 2, 2, 0.0F, 0.0F, 192, 192, 256, 256);
|
|
}
|
|
|
|
@Override
|
|
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
|
if (button == 0) {
|
|
Style style = this.getClickedComponentStyleAt(mouseX, mouseY);
|
|
if (style != null && this.handleComponentClicked(style)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return super.mouseClicked(mouseX, mouseY, button);
|
|
}
|
|
|
|
@Override
|
|
public boolean handleComponentClicked(Style style) {
|
|
ClickEvent clickEvent = style.getClickEvent();
|
|
if (clickEvent == null) {
|
|
return false;
|
|
} else if (clickEvent instanceof ClickEvent.ChangePage(int var5)) {
|
|
return this.forcePage(var5 - 1);
|
|
} else {
|
|
boolean bl = super.handleComponentClicked(style);
|
|
if (bl && clickEvent.action() == ClickEvent.Action.RUN_COMMAND) {
|
|
this.closeScreen();
|
|
}
|
|
|
|
return bl;
|
|
}
|
|
}
|
|
|
|
protected void closeScreen() {
|
|
this.minecraft.setScreen(null);
|
|
}
|
|
|
|
@Nullable
|
|
public Style getClickedComponentStyleAt(double mouseX, double mouseY) {
|
|
if (this.cachedPageComponents.isEmpty()) {
|
|
return null;
|
|
} else {
|
|
int i = Mth.floor(mouseX - (this.width - 192) / 2 - 36.0);
|
|
int j = Mth.floor(mouseY - 2.0 - 30.0);
|
|
if (i >= 0 && j >= 0) {
|
|
int k = Math.min(128 / 9, this.cachedPageComponents.size());
|
|
if (i <= 114 && j < 9 * k + k) {
|
|
int l = j / 9;
|
|
if (l >= 0 && l < this.cachedPageComponents.size()) {
|
|
FormattedCharSequence formattedCharSequence = (FormattedCharSequence)this.cachedPageComponents.get(l);
|
|
return this.minecraft.font.getSplitter().componentStyleAtWidth(formattedCharSequence, i);
|
|
} else {
|
|
return null;
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public record BookAccess(List<Component> pages) {
|
|
/**
|
|
* Returns the size of the book
|
|
*/
|
|
public int getPageCount() {
|
|
return this.pages.size();
|
|
}
|
|
|
|
public FormattedText getPage(int page) {
|
|
return page >= 0 && page < this.getPageCount() ? (FormattedText)this.pages.get(page) : FormattedText.EMPTY;
|
|
}
|
|
|
|
@Nullable
|
|
public static BookViewScreen.BookAccess fromItem(ItemStack stack) {
|
|
boolean bl = Minecraft.getInstance().isTextFilteringEnabled();
|
|
WrittenBookContent writtenBookContent = stack.get(DataComponents.WRITTEN_BOOK_CONTENT);
|
|
if (writtenBookContent != null) {
|
|
return new BookViewScreen.BookAccess(writtenBookContent.getPages(bl));
|
|
} else {
|
|
WritableBookContent writableBookContent = stack.get(DataComponents.WRITABLE_BOOK_CONTENT);
|
|
return writableBookContent != null ? new BookViewScreen.BookAccess(writableBookContent.getPages(bl).map(Component::literal).toList()) : null;
|
|
}
|
|
}
|
|
}
|
|
}
|