minecraft-src/net/minecraft/client/gui/screens/dialog/DialogScreen.java
2025-09-18 12:27:44 +00:00

254 lines
9 KiB
Java

package net.minecraft.client.gui.screens.dialog;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.client.gui.components.ScrollableLayout;
import net.minecraft.client.gui.components.StringWidget;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.components.WidgetSprites;
import net.minecraft.client.gui.layouts.GridLayout;
import net.minecraft.client.gui.layouts.HeaderAndFooterLayout;
import net.minecraft.client.gui.layouts.LayoutElement;
import net.minecraft.client.gui.layouts.LinearLayout;
import net.minecraft.client.gui.screens.ConfirmScreen;
import net.minecraft.client.gui.screens.PauseScreen;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.dialog.body.DialogBodyHandlers;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.dialog.Dialog;
import net.minecraft.server.dialog.DialogAction;
import net.minecraft.server.dialog.Input;
import net.minecraft.server.dialog.body.DialogBody;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public abstract class DialogScreen<T extends Dialog> extends Screen {
public static final Component DISCONNECT = Component.translatable("menu.custom_screen_info.disconnect");
private static final int WARNING_BUTTON_SIZE = 20;
private static final WidgetSprites WARNING_BUTTON_SPRITES = new WidgetSprites(
ResourceLocation.withDefaultNamespace("dialog/warning_button"),
ResourceLocation.withDefaultNamespace("dialog/warning_button_disabled"),
ResourceLocation.withDefaultNamespace("dialog/warning_button_highlighted")
);
private final T dialog;
private final HeaderAndFooterLayout layout = new HeaderAndFooterLayout(this);
@Nullable
private final Screen previousScreen;
@Nullable
private ScrollableLayout bodyScroll;
private Button warningButton;
private final DialogConnectionAccess connectionAccess;
private Supplier<Optional<ClickEvent>> onClose = DialogControlSet.EMPTY_ACTION;
public DialogScreen(@Nullable Screen previousScreen, T dialog, DialogConnectionAccess connectionAccess) {
super(dialog.common().title());
this.dialog = dialog;
this.previousScreen = previousScreen;
this.connectionAccess = connectionAccess;
}
@Override
protected final void init() {
super.init();
this.warningButton = this.createWarningButton();
this.warningButton.setTabOrderGroup(-10);
DialogControlSet dialogControlSet = new DialogControlSet(this);
LinearLayout linearLayout = LinearLayout.vertical().spacing(10);
linearLayout.defaultCellSetting().alignHorizontallyCenter();
this.layout.addToHeader(this.createTitleWithWarningButton());
for (DialogBody dialogBody : this.dialog.common().body()) {
LayoutElement layoutElement = DialogBodyHandlers.createBodyElement(this, dialogBody);
if (layoutElement != null) {
linearLayout.addChild(layoutElement);
}
}
for (Input input : this.dialog.common().inputs()) {
dialogControlSet.addInput(input, linearLayout::addChild);
}
this.populateBodyElements(linearLayout, dialogControlSet, this.dialog, this.connectionAccess);
this.bodyScroll = new ScrollableLayout(this.minecraft, linearLayout, this.layout.getContentHeight());
this.layout.addToContents(this.bodyScroll);
this.updateHeaderAndFooter(this.layout, dialogControlSet, this.dialog, this.connectionAccess);
this.onClose = dialogControlSet.bindAction(this.dialog.onCancel());
this.layout.visitWidgets(abstractWidget -> {
if (abstractWidget != this.warningButton) {
this.addRenderableWidget(abstractWidget);
}
});
this.addRenderableWidget(this.warningButton);
this.repositionElements();
}
protected void populateBodyElements(LinearLayout layout, DialogControlSet controls, T dialog, DialogConnectionAccess connectionAccess) {
}
protected void updateHeaderAndFooter(HeaderAndFooterLayout layout, DialogControlSet controls, T dialog, DialogConnectionAccess connectionAccess) {
}
@Override
protected void repositionElements() {
this.bodyScroll.setMaxHeight(this.layout.getContentHeight());
this.layout.arrangeElements();
this.makeSureWarningButtonIsInBounds();
}
protected LayoutElement createTitleWithWarningButton() {
LinearLayout linearLayout = LinearLayout.horizontal().spacing(10);
linearLayout.defaultCellSetting().alignHorizontallyCenter().alignVerticallyMiddle();
linearLayout.addChild(new StringWidget(this.title, this.font));
linearLayout.addChild(this.warningButton);
return linearLayout;
}
protected void makeSureWarningButtonIsInBounds() {
int i = this.warningButton.getX();
int j = this.warningButton.getY();
if (i < 0 || j < 0 || i > this.width - 20 || j > this.height - 20) {
this.warningButton.setX(Math.max(0, this.width - 40));
this.warningButton.setY(Math.min(5, this.height));
}
}
private Button createWarningButton() {
ImageButton imageButton = new ImageButton(
0,
0,
20,
20,
WARNING_BUTTON_SPRITES,
button -> this.minecraft.setScreen(DialogScreen.WarningScreen.create(this.minecraft, this)),
Component.translatable("menu.custom_screen_info.button_narration")
);
imageButton.setTooltip(Tooltip.create(Component.translatable("menu.custom_screen_info.tooltip")));
return imageButton;
}
@Override
public boolean isPauseScreen() {
return this.dialog.common().pause();
}
@Override
public boolean shouldCloseOnEsc() {
return this.dialog.common().canCloseWithEscape();
}
@Override
public void onClose() {
this.runAction((Optional<ClickEvent>)this.onClose.get(), DialogAction.CLOSE);
}
public void runAction(Optional<ClickEvent> clickEvent) {
this.runAction(clickEvent, this.dialog.common().afterAction());
}
public void runAction(Optional<ClickEvent> clickEvent, DialogAction action) {
Screen screen = (Screen)(switch (action) {
case NONE -> this;
case CLOSE -> this.previousScreen;
case WAIT_FOR_RESPONSE -> new WaitingForResponseScreen(this.previousScreen);
});
if (clickEvent.isPresent()) {
this.handleDialogClickEvent((ClickEvent)clickEvent.get(), screen);
} else {
this.minecraft.setScreen(screen);
}
}
private void handleDialogClickEvent(ClickEvent clickEvent, @Nullable Screen previousScreen) {
switch (clickEvent) {
case ClickEvent.RunCommand(String var10):
this.connectionAccess.runCommand(Commands.trimOptionalPrefix(var10), previousScreen);
break;
case ClickEvent.ShowDialog showDialog:
this.connectionAccess.openDialog(showDialog.dialog(), previousScreen);
break;
case ClickEvent.Custom custom:
this.connectionAccess.sendCustomAction(custom.id(), custom.payload());
this.minecraft.setScreen(previousScreen);
break;
default:
defaultHandleClickEvent(clickEvent, this.minecraft, previousScreen);
}
}
@Nullable
public Screen previousScreen() {
return this.previousScreen;
}
protected static LayoutElement packControlsIntoColumns(List<? extends LayoutElement> controls, int columns) {
GridLayout gridLayout = new GridLayout();
gridLayout.defaultCellSetting().alignHorizontallyCenter();
gridLayout.columnSpacing(2).rowSpacing(2);
int i = controls.size();
int j = i / columns;
int k = j * columns;
for (int l = 0; l < k; l++) {
gridLayout.addChild((T)((LayoutElement)controls.get(l)), l / columns, l % columns);
}
if (i != k) {
LinearLayout linearLayout = LinearLayout.horizontal().spacing(2);
linearLayout.defaultCellSetting().alignHorizontallyCenter();
for (int m = k; m < i; m++) {
linearLayout.addChild((T)((LayoutElement)controls.get(m)));
}
gridLayout.addChild(linearLayout, j, 0, 1, columns);
}
return gridLayout;
}
@Environment(EnvType.CLIENT)
public static class WarningScreen extends ConfirmScreen {
private final MutableObject<Screen> returnScreen;
public static Screen create(Minecraft minecraft, Screen returnScreen) {
return new DialogScreen.WarningScreen(minecraft, new MutableObject<>(returnScreen));
}
private WarningScreen(Minecraft minecraft, MutableObject<Screen> returnScreen) {
super(
bl -> {
if (bl) {
PauseScreen.disconnectFromWorld(minecraft, DialogScreen.DISCONNECT);
} else {
minecraft.setScreen(returnScreen.getValue());
}
},
Component.translatable("menu.custom_screen_info.title"),
Component.translatable("menu.custom_screen_info.contents"),
CommonComponents.disconnectButtonLabel(minecraft.isLocalServer()),
CommonComponents.GUI_BACK
);
this.returnScreen = returnScreen;
}
@Nullable
public Screen returnScreen() {
return this.returnScreen.getValue();
}
public void updateReturnScreen(@Nullable Screen returnScreen) {
this.returnScreen.setValue(returnScreen);
}
}
}