package net.minecraft.client.gui.components; import com.google.common.collect.Lists; import java.util.AbstractList; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; import javax.annotation.Nullable; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.events.ContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.layouts.HeaderAndFooterLayout; import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.narration.NarratedElementType; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.navigation.ScreenDirection; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.RenderType; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) public abstract class AbstractSelectionList> extends AbstractContainerWidget { private static final ResourceLocation MENU_LIST_BACKGROUND = ResourceLocation.withDefaultNamespace("textures/gui/menu_list_background.png"); private static final ResourceLocation INWORLD_MENU_LIST_BACKGROUND = ResourceLocation.withDefaultNamespace("textures/gui/inworld_menu_list_background.png"); protected final Minecraft minecraft; protected final int itemHeight; private final List children = new AbstractSelectionList.TrackedList(); protected boolean centerListVertically = true; private boolean renderHeader; protected int headerHeight; @Nullable private E selected; @Nullable private E hovered; public AbstractSelectionList(Minecraft p_93404_, int p_93405_, int p_93406_, int p_93407_, int p_93408_) { super(0, p_93407_, p_93405_, p_93406_, CommonComponents.EMPTY); this.minecraft = p_93404_; this.itemHeight = p_93408_; } public AbstractSelectionList(Minecraft p_375613_, int p_378036_, int p_378191_, int p_377008_, int p_375798_, int p_377043_) { this(p_375613_, p_378036_, p_378191_, p_377008_, p_375798_); this.renderHeader = true; this.headerHeight = p_377043_; } @Nullable public E getSelected() { return this.selected; } public void setSelectedIndex(int p_364551_) { if (p_364551_ == -1) { this.setSelected(null); } else if (this.getItemCount() != 0) { this.setSelected(this.getEntry(p_364551_)); } } public void setSelected(@Nullable E p_93462_) { this.selected = p_93462_; } public E getFirstElement() { return this.children.get(0); } @Nullable public E getFocused() { return (E)super.getFocused(); } @Override public final List children() { return this.children; } protected void clearEntries() { this.children.clear(); this.selected = null; } public void replaceEntries(Collection p_93470_) { this.clearEntries(); this.children.addAll(p_93470_); } protected E getEntry(int p_93501_) { return this.children().get(p_93501_); } protected int addEntry(E p_93487_) { this.children.add(p_93487_); return this.children.size() - 1; } protected void addEntryToTop(E p_239858_) { double d0 = this.maxScrollAmount() - this.scrollAmount(); this.children.add(0, p_239858_); this.setScrollAmount(this.maxScrollAmount() - d0); } protected boolean removeEntryFromTop(E p_239046_) { double d0 = this.maxScrollAmount() - this.scrollAmount(); boolean flag = this.removeEntry(p_239046_); this.setScrollAmount(this.maxScrollAmount() - d0); return flag; } protected int getItemCount() { return this.children().size(); } protected boolean isSelectedItem(int p_93504_) { return Objects.equals(this.getSelected(), this.children().get(p_93504_)); } @Nullable protected final E getEntryAtPosition(double p_93413_, double p_93414_) { int i = this.getRowWidth() / 2; int j = this.getX() + this.width / 2; int k = j - i; int l = j + i; int i1 = Mth.floor(p_93414_ - this.getY()) - this.headerHeight + (int)this.scrollAmount() - 4; int j1 = i1 / this.itemHeight; return p_93413_ >= k && p_93413_ <= l && j1 >= 0 && i1 >= 0 && j1 < this.getItemCount() ? this.children().get(j1) : null; } public void updateSize(int p_336225_, HeaderAndFooterLayout p_331081_) { this.updateSizeAndPosition(p_336225_, p_331081_.getContentHeight(), p_331081_.getHeaderHeight()); } public void updateSizeAndPosition(int p_334988_, int p_333730_, int p_328806_) { this.setSize(p_334988_, p_333730_); this.setPosition(0, p_328806_); this.refreshScrollAmount(); } @Override protected int contentHeight() { return this.getItemCount() * this.itemHeight + this.headerHeight + 4; } protected void renderHeader(GuiGraphics p_282337_, int p_93444_, int p_93445_) { } protected void renderDecorations(GuiGraphics p_281477_, int p_93459_, int p_93460_) { } @Override public void renderWidget(GuiGraphics p_282708_, int p_283242_, int p_282891_, float p_283683_) { this.hovered = this.isMouseOver(p_283242_, p_282891_) ? this.getEntryAtPosition(p_283242_, p_282891_) : null; this.renderListBackground(p_282708_); this.enableScissor(p_282708_); if (this.renderHeader) { int i = this.getRowLeft(); int j = this.getY() + 4 - (int)this.scrollAmount(); this.renderHeader(p_282708_, i, j); } this.renderListItems(p_282708_, p_283242_, p_282891_, p_283683_); p_282708_.disableScissor(); this.renderListSeparators(p_282708_); this.renderScrollbar(p_282708_); this.renderDecorations(p_282708_, p_283242_, p_282891_); } protected void renderListSeparators(GuiGraphics p_331270_) { ResourceLocation resourcelocation = this.minecraft.level == null ? Screen.HEADER_SEPARATOR : Screen.INWORLD_HEADER_SEPARATOR; ResourceLocation resourcelocation1 = this.minecraft.level == null ? Screen.FOOTER_SEPARATOR : Screen.INWORLD_FOOTER_SEPARATOR; p_331270_.blit(RenderType::guiTextured, resourcelocation, this.getX(), this.getY() - 2, 0.0F, 0.0F, this.getWidth(), 2, 32, 2); p_331270_.blit(RenderType::guiTextured, resourcelocation1, this.getX(), this.getBottom(), 0.0F, 0.0F, this.getWidth(), 2, 32, 2); } protected void renderListBackground(GuiGraphics p_333412_) { ResourceLocation resourcelocation = this.minecraft.level == null ? MENU_LIST_BACKGROUND : INWORLD_MENU_LIST_BACKGROUND; p_333412_.blit( RenderType::guiTextured, resourcelocation, this.getX(), this.getY(), this.getRight(), this.getBottom() + (int)this.scrollAmount(), this.getWidth(), this.getHeight(), 32, 32 ); } protected void enableScissor(GuiGraphics p_282811_) { p_282811_.enableScissor(this.getX(), this.getY(), this.getRight(), this.getBottom()); } protected void centerScrollOn(E p_93495_) { this.setScrollAmount(this.children().indexOf(p_93495_) * this.itemHeight + this.itemHeight / 2 - this.height / 2); } protected void ensureVisible(E p_93499_) { int i = this.getRowTop(this.children().indexOf(p_93499_)); int j = i - this.getY() - 4 - this.itemHeight; if (j < 0) { this.scroll(j); } int k = this.getBottom() - i - this.itemHeight - this.itemHeight; if (k < 0) { this.scroll(-k); } } private void scroll(int p_93430_) { this.setScrollAmount(this.scrollAmount() + p_93430_); } @Override protected double scrollRate() { return this.itemHeight / 2.0; } @Override protected int scrollBarX() { return this.getRowRight() + 6 + 2; } @Override public Optional getChildAt(double p_376745_, double p_377088_) { return Optional.ofNullable(this.getEntryAtPosition(p_376745_, p_377088_)); } @Override public void setFocused(@Nullable GuiEventListener p_265738_) { E e = this.getFocused(); if (e != p_265738_ && e instanceof ContainerEventHandler containereventhandler) { containereventhandler.setFocused(null); } super.setFocused(p_265738_); int i = this.children.indexOf(p_265738_); if (i >= 0) { E e1 = this.children.get(i); this.setSelected(e1); if (this.minecraft.getLastInputType().isKeyboard()) { this.ensureVisible(e1); } } } @Nullable protected E nextEntry(ScreenDirection p_265160_) { return this.nextEntry(p_265160_, p_93510_ -> true); } @Nullable protected E nextEntry(ScreenDirection p_265210_, Predicate p_265604_) { return this.nextEntry(p_265210_, p_265604_, this.getSelected()); } @Nullable protected E nextEntry(ScreenDirection p_265159_, Predicate p_265109_, @Nullable E p_265379_) { int i = switch (p_265159_) { case RIGHT, LEFT -> 0; case UP -> -1; case DOWN -> 1; }; if (!this.children().isEmpty() && i != 0) { int j; if (p_265379_ == null) { j = i > 0 ? 0 : this.children().size() - 1; } else { j = this.children().indexOf(p_265379_) + i; } for (int k = j; k >= 0 && k < this.children.size(); k += i) { E e = this.children().get(k); if (p_265109_.test(e)) { return e; } } } return null; } protected void renderListItems(GuiGraphics p_282079_, int p_239229_, int p_239230_, float p_239231_) { int i = this.getRowLeft(); int j = this.getRowWidth(); int k = this.itemHeight - 4; int l = this.getItemCount(); for (int i1 = 0; i1 < l; i1++) { int j1 = this.getRowTop(i1); int k1 = this.getRowBottom(i1); if (k1 >= this.getY() && j1 <= this.getBottom()) { this.renderItem(p_282079_, p_239229_, p_239230_, p_239231_, i1, i, j1, j, k); } } } protected void renderItem( GuiGraphics p_282205_, int p_238966_, int p_238967_, float p_238968_, int p_238969_, int p_238970_, int p_238971_, int p_238972_, int p_238973_ ) { E e = this.getEntry(p_238969_); e.renderBack(p_282205_, p_238969_, p_238971_, p_238970_, p_238972_, p_238973_, p_238966_, p_238967_, Objects.equals(this.hovered, e), p_238968_); if (this.isSelectedItem(p_238969_)) { int i = this.isFocused() ? -1 : -8355712; this.renderSelection(p_282205_, p_238971_, p_238972_, p_238973_, i, -16777216); } e.render(p_282205_, p_238969_, p_238971_, p_238970_, p_238972_, p_238973_, p_238966_, p_238967_, Objects.equals(this.hovered, e), p_238968_); } protected void renderSelection(GuiGraphics p_283589_, int p_240142_, int p_240143_, int p_240144_, int p_240145_, int p_240146_) { int i = this.getX() + (this.width - p_240143_) / 2; int j = this.getX() + (this.width + p_240143_) / 2; p_283589_.fill(i, p_240142_ - 2, j, p_240142_ + p_240144_ + 2, p_240145_); p_283589_.fill(i + 1, p_240142_ - 1, j - 1, p_240142_ + p_240144_ + 1, p_240146_); } public int getRowLeft() { return this.getX() + this.width / 2 - this.getRowWidth() / 2 + 2; } public int getRowRight() { return this.getRowLeft() + this.getRowWidth(); } public int getRowTop(int p_93512_) { return this.getY() + 4 - (int)this.scrollAmount() + p_93512_ * this.itemHeight + this.headerHeight; } public int getRowBottom(int p_93486_) { return this.getRowTop(p_93486_) + this.itemHeight; } public int getRowWidth() { return 220; } @Override public NarratableEntry.NarrationPriority narrationPriority() { if (this.isFocused()) { return NarratableEntry.NarrationPriority.FOCUSED; } else { return this.hovered != null ? NarratableEntry.NarrationPriority.HOVERED : NarratableEntry.NarrationPriority.NONE; } } @Nullable protected E remove(int p_93515_) { E e = this.children.get(p_93515_); return this.removeEntry(this.children.get(p_93515_)) ? e : null; } protected boolean removeEntry(E p_93503_) { boolean flag = this.children.remove(p_93503_); if (flag && p_93503_ == this.getSelected()) { this.setSelected(null); } return flag; } @Nullable protected E getHovered() { return this.hovered; } void bindEntryToSelf(AbstractSelectionList.Entry p_93506_) { p_93506_.list = this; } protected void narrateListElementPosition(NarrationElementOutput p_168791_, E p_168792_) { List list = this.children(); if (list.size() > 1) { int i = list.indexOf(p_168792_); if (i != -1) { p_168791_.add(NarratedElementType.POSITION, Component.translatable("narrator.position.list", i + 1, list.size())); } } } @OnlyIn(Dist.CLIENT) protected abstract static class Entry> implements GuiEventListener { @Deprecated AbstractSelectionList list; @Override public void setFocused(boolean p_265302_) { } @Override public boolean isFocused() { return this.list.getFocused() == this; } public abstract void render( GuiGraphics p_283112_, int p_93524_, int p_93525_, int p_93526_, int p_93527_, int p_93528_, int p_93529_, int p_93530_, boolean p_93531_, float p_93532_ ); public void renderBack( GuiGraphics p_282673_, int p_275556_, int p_275667_, int p_275713_, int p_275408_, int p_275330_, int p_275603_, int p_275450_, boolean p_275434_, float p_275384_ ) { } @Override public boolean isMouseOver(double p_93537_, double p_93538_) { return Objects.equals(this.list.getEntryAtPosition(p_93537_, p_93538_), this); } } @OnlyIn(Dist.CLIENT) class TrackedList extends AbstractList { private final List delegate = Lists.newArrayList(); public E get(int p_93557_) { return this.delegate.get(p_93557_); } @Override public int size() { return this.delegate.size(); } public E set(int p_93559_, E p_93560_) { E e = this.delegate.set(p_93559_, p_93560_); AbstractSelectionList.this.bindEntryToSelf(p_93560_); return e; } public void add(int p_93567_, E p_93568_) { this.delegate.add(p_93567_, p_93568_); AbstractSelectionList.this.bindEntryToSelf(p_93568_); } public E remove(int p_93565_) { return this.delegate.remove(p_93565_); } } }