2025-11-24 22:52:51 +03:00

673 lines
25 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.annotations.VisibleForTesting
* com.google.common.collect.Lists
* com.mojang.logging.LogUtils
* org.jspecify.annotations.Nullable
* org.slf4j.Logger
*/
package net.minecraft.client.gui.screens;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.mojang.logging.LogUtils;
import java.lang.runtime.SwitchBootstraps;
import java.net.URI;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.NarratorStatus;
import net.minecraft.client.gui.ComponentPath;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.CycleButton;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.components.TabOrderedElement;
import net.minecraft.client.gui.components.events.AbstractContainerEventHandler;
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.ScreenNarrationCollector;
import net.minecraft.client.gui.navigation.FocusNavigationEvent;
import net.minecraft.client.gui.navigation.ScreenDirection;
import net.minecraft.client.gui.navigation.ScreenRectangle;
import net.minecraft.client.gui.screens.ConfirmLinkScreen;
import net.minecraft.client.input.KeyEvent;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.common.ServerboundCustomClickActionPacket;
import net.minecraft.resources.Identifier;
import net.minecraft.sounds.Music;
import net.minecraft.util.Util;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
public abstract class Screen
extends AbstractContainerEventHandler
implements Renderable {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Component USAGE_NARRATION = Component.translatable("narrator.screen.usage");
public static final Identifier MENU_BACKGROUND = Identifier.withDefaultNamespace("textures/gui/menu_background.png");
public static final Identifier HEADER_SEPARATOR = Identifier.withDefaultNamespace("textures/gui/header_separator.png");
public static final Identifier FOOTER_SEPARATOR = Identifier.withDefaultNamespace("textures/gui/footer_separator.png");
private static final Identifier INWORLD_MENU_BACKGROUND = Identifier.withDefaultNamespace("textures/gui/inworld_menu_background.png");
public static final Identifier INWORLD_HEADER_SEPARATOR = Identifier.withDefaultNamespace("textures/gui/inworld_header_separator.png");
public static final Identifier INWORLD_FOOTER_SEPARATOR = Identifier.withDefaultNamespace("textures/gui/inworld_footer_separator.png");
protected static final float FADE_IN_TIME = 2000.0f;
protected final Component title;
private final List<GuiEventListener> children = Lists.newArrayList();
private final List<NarratableEntry> narratables = Lists.newArrayList();
protected final Minecraft minecraft;
private boolean initialized;
public int width;
public int height;
private final List<Renderable> renderables = Lists.newArrayList();
protected final Font font;
private static final long NARRATE_SUPPRESS_AFTER_INIT_TIME;
private static final long NARRATE_DELAY_NARRATOR_ENABLED;
private static final long NARRATE_DELAY_MOUSE_MOVE = 750L;
private static final long NARRATE_DELAY_MOUSE_ACTION = 200L;
private static final long NARRATE_DELAY_KEYBOARD_ACTION = 200L;
private final ScreenNarrationCollector narrationState = new ScreenNarrationCollector();
private long narrationSuppressTime = Long.MIN_VALUE;
private long nextNarrationTime = Long.MAX_VALUE;
protected @Nullable CycleButton<NarratorStatus> narratorButton;
private @Nullable NarratableEntry lastNarratable;
protected final Executor screenExecutor;
protected Screen(Component title) {
this(Minecraft.getInstance(), Minecraft.getInstance().font, title);
}
protected Screen(Minecraft minecraft, Font font, Component title) {
this.minecraft = minecraft;
this.font = font;
this.title = title;
this.screenExecutor = runnable -> minecraft.execute(() -> {
if (minecraft.screen == this) {
runnable.run();
}
});
}
public Component getTitle() {
return this.title;
}
public Component getNarrationMessage() {
return this.getTitle();
}
public final void renderWithTooltipAndSubtitles(GuiGraphics graphics, int mouseX, int mouseY, float a) {
graphics.nextStratum();
this.renderBackground(graphics, mouseX, mouseY, a);
graphics.nextStratum();
this.render(graphics, mouseX, mouseY, a);
graphics.renderDeferredElements();
}
@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float a) {
for (Renderable renderable : this.renderables) {
renderable.render(graphics, mouseX, mouseY, a);
}
}
@Override
public boolean keyPressed(KeyEvent event) {
FocusNavigationEvent.ArrowNavigation navigationEvent;
if (event.isEscape() && this.shouldCloseOnEsc()) {
this.onClose();
return true;
}
if (super.keyPressed(event)) {
return true;
}
switch (event.key()) {
case 263: {
Record record = this.createArrowEvent(ScreenDirection.LEFT);
break;
}
case 262: {
Record record = this.createArrowEvent(ScreenDirection.RIGHT);
break;
}
case 265: {
Record record = this.createArrowEvent(ScreenDirection.UP);
break;
}
case 264: {
Record record = this.createArrowEvent(ScreenDirection.DOWN);
break;
}
case 258: {
Record record = this.createTabEvent(!event.hasShiftDown());
break;
}
default: {
Record record = navigationEvent = null;
}
}
if (navigationEvent != null) {
ComponentPath focusPath = super.nextFocusPath(navigationEvent);
if (focusPath == null && navigationEvent instanceof FocusNavigationEvent.TabNavigation) {
this.clearFocus();
focusPath = super.nextFocusPath(navigationEvent);
}
if (focusPath != null) {
this.changeFocus(focusPath);
}
}
return false;
}
private FocusNavigationEvent.TabNavigation createTabEvent(boolean forward) {
return new FocusNavigationEvent.TabNavigation(forward);
}
private FocusNavigationEvent.ArrowNavigation createArrowEvent(ScreenDirection direction) {
return new FocusNavigationEvent.ArrowNavigation(direction);
}
protected void setInitialFocus() {
FocusNavigationEvent.TabNavigation forwardTabEvent;
ComponentPath focusPath;
if (this.minecraft.getLastInputType().isKeyboard() && (focusPath = super.nextFocusPath(forwardTabEvent = new FocusNavigationEvent.TabNavigation(true))) != null) {
this.changeFocus(focusPath);
}
}
protected void setInitialFocus(GuiEventListener target) {
ComponentPath path = ComponentPath.path(this, target.nextFocusPath(new FocusNavigationEvent.InitialFocus()));
if (path != null) {
this.changeFocus(path);
}
}
public void clearFocus() {
ComponentPath componentPath = this.getCurrentFocusPath();
if (componentPath != null) {
componentPath.applyFocus(false);
}
}
@VisibleForTesting
protected void changeFocus(ComponentPath componentPath) {
this.clearFocus();
componentPath.applyFocus(true);
}
public boolean shouldCloseOnEsc() {
return true;
}
public void onClose() {
this.minecraft.setScreen(null);
}
protected <T extends GuiEventListener & Renderable> T addRenderableWidget(T widget) {
this.renderables.add(widget);
return this.addWidget(widget);
}
protected <T extends Renderable> T addRenderableOnly(T renderable) {
this.renderables.add(renderable);
return renderable;
}
protected <T extends GuiEventListener & NarratableEntry> T addWidget(T widget) {
this.children.add(widget);
this.narratables.add(widget);
return widget;
}
protected void removeWidget(GuiEventListener widget) {
if (widget instanceof Renderable) {
this.renderables.remove((Renderable)((Object)widget));
}
if (widget instanceof NarratableEntry) {
this.narratables.remove((NarratableEntry)((Object)widget));
}
if (this.getFocused() == widget) {
this.clearFocus();
}
this.children.remove(widget);
}
protected void clearWidgets() {
this.renderables.clear();
this.children.clear();
this.narratables.clear();
}
public static List<Component> getTooltipFromItem(Minecraft minecraft, ItemStack itemStack) {
return itemStack.getTooltipLines(Item.TooltipContext.of(minecraft.level), minecraft.player, minecraft.options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL);
}
protected void insertText(String text, boolean replace) {
}
/*
* Enabled aggressive block sorting
* Enabled unnecessary exception pruning
* Enabled aggressive exception aggregation
*/
protected static void defaultHandleGameClickEvent(ClickEvent event, Minecraft minecraft, @Nullable Screen activeScreen) {
LocalPlayer player = Objects.requireNonNull(minecraft.player, "Player not available");
ClickEvent clickEvent = event;
Objects.requireNonNull(clickEvent);
ClickEvent clickEvent2 = clickEvent;
int n = 0;
switch (SwitchBootstraps.typeSwitch("typeSwitch", new Object[]{ClickEvent.RunCommand.class, ClickEvent.ShowDialog.class, ClickEvent.Custom.class}, (Object)clickEvent2, n)) {
case 0: {
String command;
ClickEvent.RunCommand runCommand = (ClickEvent.RunCommand)clickEvent2;
try {
String string;
command = string = runCommand.command();
}
catch (Throwable throwable) {
throw new MatchException(throwable.toString(), throwable);
}
Screen.clickCommandAction(player, command, activeScreen);
return;
}
case 1: {
ClickEvent.ShowDialog dialog = (ClickEvent.ShowDialog)clickEvent2;
player.connection.showDialog(dialog.dialog(), activeScreen);
return;
}
case 2: {
ClickEvent.Custom custom = (ClickEvent.Custom)clickEvent2;
player.connection.send(new ServerboundCustomClickActionPacket(custom.id(), custom.payload()));
if (minecraft.screen == activeScreen) return;
minecraft.setScreen(activeScreen);
return;
}
}
Screen.defaultHandleClickEvent(event, minecraft, activeScreen);
}
/*
* Loose catch block
*/
protected static void defaultHandleClickEvent(ClickEvent event, Minecraft minecraft, @Nullable Screen activeScreen) {
block12: {
boolean shouldActivateScreen;
ClickEvent clickEvent = event;
Objects.requireNonNull(clickEvent);
ClickEvent clickEvent2 = clickEvent;
int n = 0;
switch (SwitchBootstraps.typeSwitch("typeSwitch", new Object[]{ClickEvent.OpenUrl.class, ClickEvent.OpenFile.class, ClickEvent.SuggestCommand.class, ClickEvent.CopyToClipboard.class}, (Object)clickEvent2, n)) {
case 0: {
URI uRI;
ClickEvent.OpenUrl openUrl = (ClickEvent.OpenUrl)clickEvent2;
URI uri = uRI = openUrl.uri();
Screen.clickUrlAction(minecraft, activeScreen, uri);
boolean bl = false;
break;
}
case 1: {
ClickEvent.OpenFile openFile = (ClickEvent.OpenFile)clickEvent2;
Util.getPlatform().openFile(openFile.file());
boolean bl = true;
break;
}
case 2: {
Object object;
ClickEvent.SuggestCommand suggestCommand = (ClickEvent.SuggestCommand)clickEvent2;
Object command = object = suggestCommand.command();
if (activeScreen != null) {
activeScreen.insertText((String)command, true);
}
boolean bl = true;
break;
}
case 3: {
String string;
Object object = (ClickEvent.CopyToClipboard)clickEvent2;
String value = string = ((ClickEvent.CopyToClipboard)object).value();
minecraft.keyboardHandler.setClipboard(value);
boolean bl = true;
break;
}
default: {
LOGGER.error("Don't know how to handle {}", (Object)event);
boolean bl = shouldActivateScreen = true;
}
}
if (shouldActivateScreen && minecraft.screen != activeScreen) {
minecraft.setScreen(activeScreen);
}
break block12;
catch (Throwable throwable) {
throw new MatchException(throwable.toString(), throwable);
}
}
}
protected static boolean clickUrlAction(Minecraft minecraft, @Nullable Screen screen, URI uri) {
if (!minecraft.options.chatLinks().get().booleanValue()) {
return false;
}
if (minecraft.options.chatLinksPrompt().get().booleanValue()) {
minecraft.setScreen(new ConfirmLinkScreen(result -> {
if (result) {
Util.getPlatform().openUri(uri);
}
minecraft.setScreen(screen);
}, uri.toString(), false));
} else {
Util.getPlatform().openUri(uri);
}
return true;
}
protected static void clickCommandAction(LocalPlayer player, String command, @Nullable Screen screenAfterCommand) {
player.connection.sendUnattendedCommand(Commands.trimOptionalPrefix(command), screenAfterCommand);
}
public final void init(int width, int height) {
this.width = width;
this.height = height;
if (!this.initialized) {
this.init();
this.setInitialFocus();
} else {
this.repositionElements();
}
this.initialized = true;
this.triggerImmediateNarration(false);
if (this.minecraft.getLastInputType().isKeyboard()) {
this.setNarrationSuppressTime(Long.MAX_VALUE);
} else {
this.suppressNarration(NARRATE_SUPPRESS_AFTER_INIT_TIME);
}
}
protected void rebuildWidgets() {
this.clearWidgets();
this.clearFocus();
this.init();
this.setInitialFocus();
}
protected void fadeWidgets(float widgetFade) {
for (GuiEventListener guiEventListener : this.children()) {
if (!(guiEventListener instanceof AbstractWidget)) continue;
AbstractWidget widget = (AbstractWidget)guiEventListener;
widget.setAlpha(widgetFade);
}
}
@Override
public List<? extends GuiEventListener> children() {
return this.children;
}
protected void init() {
}
public void tick() {
}
public void removed() {
}
public void added() {
}
public void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float a) {
if (this.isInGameUi()) {
this.renderTransparentBackground(graphics);
} else {
if (this.minecraft.level == null) {
this.renderPanorama(graphics, a);
}
this.renderBlurredBackground(graphics);
this.renderMenuBackground(graphics);
}
this.minecraft.gui.renderDeferredSubtitles();
}
protected void renderBlurredBackground(GuiGraphics graphics) {
float blurRadius = this.minecraft.options.getMenuBackgroundBlurriness();
if (blurRadius >= 1.0f) {
graphics.blurBeforeThisStratum();
}
}
protected void renderPanorama(GuiGraphics graphics, float a) {
this.minecraft.gameRenderer.getPanorama().render(graphics, this.width, this.height, this.panoramaShouldSpin());
}
protected void renderMenuBackground(GuiGraphics graphics) {
this.renderMenuBackground(graphics, 0, 0, this.width, this.height);
}
protected void renderMenuBackground(GuiGraphics graphics, int x, int y, int width, int height) {
Screen.renderMenuBackgroundTexture(graphics, this.minecraft.level == null ? MENU_BACKGROUND : INWORLD_MENU_BACKGROUND, x, y, 0.0f, 0.0f, width, height);
}
public static void renderMenuBackgroundTexture(GuiGraphics graphics, Identifier menuBackground, int x, int y, float u, float v, int width, int height) {
int size = 32;
graphics.blit(RenderPipelines.GUI_TEXTURED, menuBackground, x, y, u, v, width, height, 32, 32);
}
public void renderTransparentBackground(GuiGraphics graphics) {
graphics.fillGradient(0, 0, this.width, this.height, -1072689136, -804253680);
}
public boolean isPauseScreen() {
return true;
}
public boolean isInGameUi() {
return false;
}
protected boolean panoramaShouldSpin() {
return true;
}
public boolean isAllowedInPortal() {
return this.isPauseScreen();
}
protected void repositionElements() {
this.rebuildWidgets();
}
public void resize(int width, int height) {
this.width = width;
this.height = height;
this.repositionElements();
}
public void fillCrashDetails(CrashReport report) {
CrashReportCategory category = report.addCategory("Affected screen", 1);
category.setDetail("Screen name", () -> this.getClass().getCanonicalName());
}
protected boolean isValidCharacterForName(String currentName, int newChar, int cursorPos) {
int colonPos = currentName.indexOf(58);
int slashPos = currentName.indexOf(47);
if (newChar == 58) {
return (slashPos == -1 || cursorPos <= slashPos) && colonPos == -1;
}
if (newChar == 47) {
return cursorPos > colonPos;
}
return newChar == 95 || newChar == 45 || newChar >= 97 && newChar <= 122 || newChar >= 48 && newChar <= 57 || newChar == 46;
}
@Override
public boolean isMouseOver(double mouseX, double mouseY) {
return true;
}
public void onFilesDrop(List<Path> files) {
}
private void scheduleNarration(long delay, boolean ignoreSuppression) {
this.nextNarrationTime = Util.getMillis() + delay;
if (ignoreSuppression) {
this.narrationSuppressTime = Long.MIN_VALUE;
}
}
private void suppressNarration(long duration) {
this.setNarrationSuppressTime(Util.getMillis() + duration);
}
private void setNarrationSuppressTime(long narrationSuppressTime) {
this.narrationSuppressTime = narrationSuppressTime;
}
public void afterMouseMove() {
this.scheduleNarration(750L, false);
}
public void afterMouseAction() {
this.scheduleNarration(200L, true);
}
public void afterKeyboardAction() {
this.scheduleNarration(200L, true);
}
private boolean shouldRunNarration() {
return SharedConstants.DEBUG_UI_NARRATION || this.minecraft.getNarrator().isActive();
}
public void handleDelayedNarration() {
long currentTime;
if (this.shouldRunNarration() && (currentTime = Util.getMillis()) > this.nextNarrationTime && currentTime > this.narrationSuppressTime) {
this.runNarration(true);
this.nextNarrationTime = Long.MAX_VALUE;
}
}
public void triggerImmediateNarration(boolean onlyChanged) {
if (this.shouldRunNarration()) {
this.runNarration(onlyChanged);
}
}
private void runNarration(boolean onlyChanged) {
this.narrationState.update(this::updateNarrationState);
String narration = this.narrationState.collectNarrationText(!onlyChanged);
if (!narration.isEmpty()) {
this.minecraft.getNarrator().saySystemNow(narration);
}
}
protected boolean shouldNarrateNavigation() {
return true;
}
protected void updateNarrationState(NarrationElementOutput output) {
output.add(NarratedElementType.TITLE, this.getNarrationMessage());
if (this.shouldNarrateNavigation()) {
output.add(NarratedElementType.USAGE, USAGE_NARRATION);
}
this.updateNarratedWidget(output);
}
protected void updateNarratedWidget(NarrationElementOutput output) {
List<NarratableEntry> activeNarratables = this.narratables.stream().flatMap(narratableEntry -> narratableEntry.getNarratables().stream()).filter(NarratableEntry::isActive).sorted(Comparator.comparingInt(TabOrderedElement::getTabOrderGroup)).toList();
NarratableSearchResult result = Screen.findNarratableWidget(activeNarratables, this.lastNarratable);
if (result != null) {
if (result.priority.isTerminal()) {
this.lastNarratable = result.entry;
}
if (activeNarratables.size() > 1) {
output.add(NarratedElementType.POSITION, (Component)Component.translatable("narrator.position.screen", result.index + 1, activeNarratables.size()));
if (result.priority == NarratableEntry.NarrationPriority.FOCUSED) {
output.add(NarratedElementType.USAGE, this.getUsageNarration());
}
}
result.entry.updateNarration(output.nest());
}
}
protected Component getUsageNarration() {
return Component.translatable("narration.component_list.usage");
}
public static @Nullable NarratableSearchResult findNarratableWidget(List<? extends NarratableEntry> narratableEntries, @Nullable NarratableEntry lastNarratable) {
NarratableSearchResult result = null;
NarratableSearchResult lowPrioNarratable = null;
int narratablesSize = narratableEntries.size();
for (int i = 0; i < narratablesSize; ++i) {
NarratableEntry narratable = narratableEntries.get(i);
NarratableEntry.NarrationPriority priority = narratable.narrationPriority();
if (priority.isTerminal()) {
if (narratable == lastNarratable) {
lowPrioNarratable = new NarratableSearchResult(narratable, i, priority);
continue;
}
return new NarratableSearchResult(narratable, i, priority);
}
if (priority.compareTo(result != null ? result.priority : NarratableEntry.NarrationPriority.NONE) <= 0) continue;
result = new NarratableSearchResult(narratable, i, priority);
}
return result != null ? result : lowPrioNarratable;
}
public void updateNarratorStatus(boolean wasDisabled) {
if (wasDisabled) {
this.scheduleNarration(NARRATE_DELAY_NARRATOR_ENABLED, false);
}
if (this.narratorButton != null) {
this.narratorButton.setValue(this.minecraft.options.narrator().get());
}
}
public Font getFont() {
return this.font;
}
public boolean showsActiveEffects() {
return false;
}
public boolean canInterruptWithAnotherScreen() {
return this.shouldCloseOnEsc();
}
@Override
public ScreenRectangle getRectangle() {
return new ScreenRectangle(0, 0, this.width, this.height);
}
public @Nullable Music getBackgroundMusic() {
return null;
}
static {
NARRATE_DELAY_NARRATOR_ENABLED = NARRATE_SUPPRESS_AFTER_INIT_TIME = TimeUnit.SECONDS.toMillis(2L);
}
public record NarratableSearchResult(NarratableEntry entry, int index, NarratableEntry.NarrationPriority priority) {
}
}