package net.minecraft.client.gui.components; import java.util.List; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ComponentPath; import net.minecraft.client.gui.components.events.ContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; 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.narration.NarratableEntry.NarrationPriority; import net.minecraft.client.gui.navigation.FocusNavigationEvent; import net.minecraft.client.gui.navigation.ScreenAxis; import net.minecraft.client.gui.navigation.ScreenDirection; import net.minecraft.client.gui.navigation.FocusNavigationEvent.ArrowNavigation; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public abstract class ContainerObjectSelectionList> extends AbstractSelectionList { public ContainerObjectSelectionList(Minecraft minecraft, int i, int j, int k, int l) { super(minecraft, i, j, k, l); } public ContainerObjectSelectionList(Minecraft minecraft, int i, int j, int k, int l, int m) { super(minecraft, i, j, k, l, m); } @Nullable @Override public ComponentPath nextFocusPath(FocusNavigationEvent event) { if (this.getItemCount() == 0) { return null; } else if (!(event instanceof ArrowNavigation arrowNavigation)) { return super.nextFocusPath(event); } else { E entry = this.getFocused(); if (arrowNavigation.direction().getAxis() == ScreenAxis.HORIZONTAL && entry != null) { return ComponentPath.path(this, entry.nextFocusPath(event)); } else { int i = -1; ScreenDirection screenDirection = arrowNavigation.direction(); if (entry != null) { i = entry.children().indexOf(entry.getFocused()); } if (i == -1) { switch (screenDirection) { case LEFT: i = Integer.MAX_VALUE; screenDirection = ScreenDirection.DOWN; break; case RIGHT: i = 0; screenDirection = ScreenDirection.DOWN; break; default: i = 0; } } E entry2 = entry; ComponentPath componentPath; do { entry2 = this.nextEntry(screenDirection, entryx -> !entryx.children().isEmpty(), entry2); if (entry2 == null) { return null; } componentPath = entry2.focusPathAtIndex(arrowNavigation, i); } while (componentPath == null); return ComponentPath.path(this, componentPath); } } } @Override public void setFocused(@Nullable GuiEventListener focused) { if (this.getFocused() != focused) { super.setFocused(focused); if (focused == null) { this.setSelected(null); } } } @Override public NarrationPriority narrationPriority() { return this.isFocused() ? NarrationPriority.FOCUSED : super.narrationPriority(); } @Override protected boolean isSelectedItem(int index) { return false; } @Override public void updateWidgetNarration(NarrationElementOutput narrationElementOutput) { E entry = this.getHovered(); if (entry != null) { entry.updateNarration(narrationElementOutput.nest()); this.narrateListElementPosition(narrationElementOutput, entry); } else { E entry2 = this.getFocused(); if (entry2 != null) { entry2.updateNarration(narrationElementOutput.nest()); this.narrateListElementPosition(narrationElementOutput, entry2); } } narrationElementOutput.add(NarratedElementType.USAGE, Component.translatable("narration.component_list.usage")); } @Environment(EnvType.CLIENT) public abstract static class Entry> extends net.minecraft.client.gui.components.AbstractSelectionList.Entry implements ContainerEventHandler { @Nullable private GuiEventListener focused; @Nullable private NarratableEntry lastNarratable; private boolean dragging; @Override public boolean isDragging() { return this.dragging; } @Override public void setDragging(boolean isDragging) { this.dragging = isDragging; } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { return ContainerEventHandler.super.mouseClicked(mouseX, mouseY, button); } @Override public void setFocused(@Nullable GuiEventListener focused) { if (this.focused != null) { this.focused.setFocused(false); } if (focused != null) { focused.setFocused(true); } this.focused = focused; } @Nullable @Override public GuiEventListener getFocused() { return this.focused; } @Nullable public ComponentPath focusPathAtIndex(FocusNavigationEvent event, int index) { if (this.children().isEmpty()) { return null; } else { ComponentPath componentPath = ((GuiEventListener)this.children().get(Math.min(index, this.children().size() - 1))).nextFocusPath(event); return ComponentPath.path(this, componentPath); } } @Nullable @Override public ComponentPath nextFocusPath(FocusNavigationEvent event) { if (event instanceof ArrowNavigation arrowNavigation) { int i = switch (arrowNavigation.direction()) { case LEFT -> -1; case RIGHT -> 1; case UP, DOWN -> 0; }; if (i == 0) { return null; } int j = Mth.clamp(i + this.children().indexOf(this.getFocused()), 0, this.children().size() - 1); for (int k = j; k >= 0 && k < this.children().size(); k += i) { GuiEventListener guiEventListener = (GuiEventListener)this.children().get(k); ComponentPath componentPath = guiEventListener.nextFocusPath(event); if (componentPath != null) { return ComponentPath.path(this, componentPath); } } } return ContainerEventHandler.super.nextFocusPath(event); } public abstract List narratables(); void updateNarration(NarrationElementOutput narrationElementOutput) { List list = this.narratables(); Screen.NarratableSearchResult narratableSearchResult = Screen.findNarratableWidget(list, this.lastNarratable); if (narratableSearchResult != null) { if (narratableSearchResult.priority.isTerminal()) { this.lastNarratable = narratableSearchResult.entry; } if (list.size() > 1) { narrationElementOutput.add( NarratedElementType.POSITION, Component.translatable("narrator.position.object_list", narratableSearchResult.index + 1, list.size()) ); if (narratableSearchResult.priority == NarrationPriority.FOCUSED) { narrationElementOutput.add(NarratedElementType.USAGE, Component.translatable("narration.component_list.usage")); } } narratableSearchResult.entry.updateNarration(narrationElementOutput.nest()); } } } }