package net.minecraft.client.gui.screens.inventory; import java.util.Collections; import java.util.List; import javax.annotation.Nullable; 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 net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.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; private List cachedPageComponents = Collections.emptyList(); private int cachedPage = -1; private Component pageMsg = CommonComponents.EMPTY; private PageButton forwardButton; private PageButton backButton; private final boolean playTurnSound; public BookViewScreen(BookViewScreen.BookAccess p_98264_) { this(p_98264_, true); } public BookViewScreen() { this(EMPTY_ACCESS, false); } private BookViewScreen(BookViewScreen.BookAccess p_98266_, boolean p_98267_) { super(GameNarrator.NO_TITLE); this.bookAccess = p_98266_; this.playTurnSound = p_98267_; } public void setBookAccess(BookViewScreen.BookAccess p_98289_) { this.bookAccess = p_98289_; this.currentPage = Mth.clamp(this.currentPage, 0, p_98289_.getPageCount()); this.updateButtonVisibility(); this.cachedPage = -1; } public boolean setPage(int p_98276_) { int i = Mth.clamp(p_98276_, 0, this.bookAccess.getPageCount() - 1); if (i != this.currentPage) { this.currentPage = i; this.updateButtonVisibility(); this.cachedPage = -1; return true; } else { return false; } } protected boolean forcePage(int p_98295_) { return this.setPage(p_98295_); } @Override protected void init() { this.createMenuControls(); this.createPageControlButtons(); } protected void createMenuControls() { this.addRenderableWidget(Button.builder(CommonComponents.GUI_DONE, p_389311_ -> 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, p_98297_ -> this.pageForward(), this.playTurnSound)); this.backButton = this.addRenderableWidget(new PageButton(i + 43, 159, false, p_98287_ -> this.pageBack(), this.playTurnSound)); this.updateButtonVisibility(); } private int getNumPages() { return this.bookAccess.getPageCount(); } protected void pageBack() { if (this.currentPage > 0) { this.currentPage--; } this.updateButtonVisibility(); } 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 p_98278_, int p_98279_, int p_98280_) { if (super.keyPressed(p_98278_, p_98279_, p_98280_)) { return true; } else { switch (p_98278_) { case 266: this.backButton.onPress(); return true; case 267: this.forwardButton.onPress(); return true; default: return false; } } } @Override public void render(GuiGraphics p_281997_, int p_281262_, int p_283321_, float p_282251_) { super.render(p_281997_, p_281262_, p_283321_, p_282251_); 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 i1 = this.font.width(this.pageMsg); p_281997_.drawString(this.font, this.pageMsg, i - i1 + 192 - 44, 18, 0, false); int k = Math.min(128 / 9, this.cachedPageComponents.size()); for (int l = 0; l < k; l++) { FormattedCharSequence formattedcharsequence = this.cachedPageComponents.get(l); p_281997_.drawString(this.font, formattedcharsequence, i + 36, 32 + l * 9, 0, false); } Style style = this.getClickedComponentStyleAt(p_281262_, p_283321_); if (style != null) { p_281997_.renderComponentHoverEffect(this.font, style, p_281262_, p_283321_); } } @Override public void renderBackground(GuiGraphics p_301081_, int p_297765_, int p_300192_, float p_297977_) { this.renderTransparentBackground(p_301081_); p_301081_.blit(RenderType::guiTextured, BOOK_LOCATION, (this.width - 192) / 2, 2, 0.0F, 0.0F, 192, 192, 256, 256); } @Override public boolean mouseClicked(double p_98272_, double p_98273_, int p_98274_) { if (p_98274_ == 0) { Style style = this.getClickedComponentStyleAt(p_98272_, p_98273_); if (style != null && this.handleComponentClicked(style)) { return true; } } return super.mouseClicked(p_98272_, p_98273_, p_98274_); } @Override public boolean handleComponentClicked(Style p_98293_) { ClickEvent clickevent = p_98293_.getClickEvent(); if (clickevent == null) { return false; } else if (clickevent instanceof ClickEvent.ChangePage(int i)) { return this.forcePage(i - 1); } else { boolean flag = super.handleComponentClicked(p_98293_); if (flag && clickevent.action() == ClickEvent.Action.RUN_COMMAND) { this.closeScreen(); } return flag; } } protected void closeScreen() { this.minecraft.setScreen(null); } @Nullable public Style getClickedComponentStyleAt(double p_98269_, double p_98270_) { if (this.cachedPageComponents.isEmpty()) { return null; } else { int i = Mth.floor(p_98269_ - (this.width - 192) / 2 - 36.0); int j = Mth.floor(p_98270_ - 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 = this.cachedPageComponents.get(l); return this.minecraft.font.getSplitter().componentStyleAtWidth(formattedcharsequence, i); } else { return null; } } else { return null; } } else { return null; } } } @OnlyIn(Dist.CLIENT) public record BookAccess(List pages) { public int getPageCount() { return this.pages.size(); } public FormattedText getPage(int p_98311_) { return p_98311_ >= 0 && p_98311_ < this.getPageCount() ? this.pages.get(p_98311_) : FormattedText.EMPTY; } @Nullable public static BookViewScreen.BookAccess fromItem(ItemStack p_98309_) { boolean flag = Minecraft.getInstance().isTextFilteringEnabled(); WrittenBookContent writtenbookcontent = p_98309_.get(DataComponents.WRITTEN_BOOK_CONTENT); if (writtenbookcontent != null) { return new BookViewScreen.BookAccess(writtenbookcontent.getPages(flag)); } else { WritableBookContent writablebookcontent = p_98309_.get(DataComponents.WRITABLE_BOOK_CONTENT); return writablebookcontent != null ? new BookViewScreen.BookAccess(writablebookcontent.getPages(flag).map(Component::literal).toList()) : null; } } } }