package net.minecraft.client.gui.screens.worldselection; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.BooleanSupplier; import java.util.function.Consumer; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.components.CycleButton; import net.minecraft.client.gui.components.MultiLineTextWidget; import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.layouts.GridLayout; import net.minecraft.client.gui.layouts.Layout; import net.minecraft.client.gui.layouts.SpacerElement; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) class SwitchGrid { private static final int DEFAULT_SWITCH_BUTTON_WIDTH = 44; private final List switches; private final Layout layout; SwitchGrid(List switches, Layout layout) { this.switches = switches; this.layout = layout; } public Layout layout() { return this.layout; } public void refreshStates() { this.switches.forEach(SwitchGrid.LabeledSwitch::refreshState); } public static SwitchGrid.Builder builder(int width) { return new SwitchGrid.Builder(width); } @Environment(EnvType.CLIENT) public static class Builder { final int width; private final List switchBuilders = new ArrayList(); int paddingLeft; int rowSpacing = 4; int rowCount; Optional infoUnderneath = Optional.empty(); public Builder(int width) { this.width = width; } void increaseRow() { this.rowCount++; } public SwitchGrid.SwitchBuilder addSwitch(Component label, BooleanSupplier stateSupplier, Consumer onClicked) { SwitchGrid.SwitchBuilder switchBuilder = new SwitchGrid.SwitchBuilder(label, stateSupplier, onClicked, 44); this.switchBuilders.add(switchBuilder); return switchBuilder; } public SwitchGrid.Builder withPaddingLeft(int paddingLeft) { this.paddingLeft = paddingLeft; return this; } public SwitchGrid.Builder withRowSpacing(int rowSpacing) { this.rowSpacing = rowSpacing; return this; } public SwitchGrid build() { GridLayout gridLayout = new GridLayout().rowSpacing(this.rowSpacing); gridLayout.addChild(SpacerElement.width(this.width - 44), 0, 0); gridLayout.addChild(SpacerElement.width(44), 0, 1); List list = new ArrayList(); this.rowCount = 0; for (SwitchGrid.SwitchBuilder switchBuilder : this.switchBuilders) { list.add(switchBuilder.build(this, gridLayout, 0)); } gridLayout.arrangeElements(); SwitchGrid switchGrid = new SwitchGrid(list, gridLayout); switchGrid.refreshStates(); return switchGrid; } public SwitchGrid.Builder withInfoUnderneath(int maxInfoRows, boolean alwaysMaxHeight) { this.infoUnderneath = Optional.of(new SwitchGrid.InfoUnderneathSettings(maxInfoRows, alwaysMaxHeight)); return this; } } @Environment(EnvType.CLIENT) record InfoUnderneathSettings(int maxInfoRows, boolean alwaysMaxHeight) { } @Environment(EnvType.CLIENT) record LabeledSwitch(CycleButton button, BooleanSupplier stateSupplier, @Nullable BooleanSupplier isActiveCondition) { public void refreshState() { this.button.setValue(this.stateSupplier.getAsBoolean()); if (this.isActiveCondition != null) { this.button.active = this.isActiveCondition.getAsBoolean(); } } } @Environment(EnvType.CLIENT) public static class SwitchBuilder { private final Component label; private final BooleanSupplier stateSupplier; private final Consumer onClicked; @Nullable private Component info; @Nullable private BooleanSupplier isActiveCondition; private final int buttonWidth; SwitchBuilder(Component label, BooleanSupplier stateSupplier, Consumer onClicked, int buttonWidth) { this.label = label; this.stateSupplier = stateSupplier; this.onClicked = onClicked; this.buttonWidth = buttonWidth; } public SwitchGrid.SwitchBuilder withIsActiveCondition(BooleanSupplier isActiveCondition) { this.isActiveCondition = isActiveCondition; return this; } public SwitchGrid.SwitchBuilder withInfo(Component info) { this.info = info; return this; } SwitchGrid.LabeledSwitch build(SwitchGrid.Builder builder, GridLayout gridLayout, int column) { builder.increaseRow(); StringWidget stringWidget = new StringWidget(this.label, Minecraft.getInstance().font).alignLeft(); gridLayout.addChild(stringWidget, builder.rowCount, column, gridLayout.newCellSettings().align(0.0F, 0.5F).paddingLeft(builder.paddingLeft)); Optional optional = builder.infoUnderneath; CycleButton.Builder builder2 = CycleButton.onOffBuilder(this.stateSupplier.getAsBoolean()); builder2.displayOnlyValue(); boolean bl = this.info != null && optional.isEmpty(); if (bl) { Tooltip tooltip = Tooltip.create(this.info); builder2.withTooltip(boolean_ -> tooltip); } if (this.info != null && !bl) { builder2.withCustomNarration(cycleButtonx -> CommonComponents.joinForNarration(this.label, cycleButtonx.createDefaultNarrationMessage(), this.info)); } else { builder2.withCustomNarration(cycleButtonx -> CommonComponents.joinForNarration(this.label, cycleButtonx.createDefaultNarrationMessage())); } CycleButton cycleButton = builder2.create( 0, 0, this.buttonWidth, 20, Component.empty(), (cycleButtonx, boolean_) -> this.onClicked.accept(boolean_) ); if (this.isActiveCondition != null) { cycleButton.active = this.isActiveCondition.getAsBoolean(); } gridLayout.addChild(cycleButton, builder.rowCount, column + 1, gridLayout.newCellSettings().alignHorizontallyRight()); if (this.info != null) { optional.ifPresent(infoUnderneathSettings -> { Component component = this.info.copy().withStyle(ChatFormatting.GRAY); Font font = Minecraft.getInstance().font; MultiLineTextWidget multiLineTextWidget = new MultiLineTextWidget(component, font); multiLineTextWidget.setMaxWidth(builder.width - builder.paddingLeft - this.buttonWidth); multiLineTextWidget.setMaxRows(infoUnderneathSettings.maxInfoRows()); builder.increaseRow(); int j = infoUnderneathSettings.alwaysMaxHeight ? 9 * infoUnderneathSettings.maxInfoRows - multiLineTextWidget.getHeight() : 0; gridLayout.addChild(multiLineTextWidget, builder.rowCount, column, gridLayout.newCellSettings().paddingTop(-builder.rowSpacing).paddingBottom(j)); }); } return new SwitchGrid.LabeledSwitch(cycleButton, this.stateSupplier, this.isActiveCondition); } } }