minecraft-src/net/minecraft/client/gui/components/CycleButton.java
2025-07-04 02:00:41 +03:00

290 lines
9.6 KiB
Java

package net.minecraft.client.gui.components;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.OptionInstance;
import net.minecraft.client.gui.components.CycleButton.ValueListSupplier.1;
import net.minecraft.client.gui.components.CycleButton.ValueListSupplier.2;
import net.minecraft.client.gui.narration.NarratedElementType;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.util.Mth;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class CycleButton<T> extends AbstractButton {
public static final BooleanSupplier DEFAULT_ALT_LIST_SELECTOR = Screen::hasAltDown;
private static final List<Boolean> BOOLEAN_OPTIONS = ImmutableList.of(Boolean.TRUE, Boolean.FALSE);
private final Component name;
private int index;
private T value;
private final CycleButton.ValueListSupplier<T> values;
private final Function<T, Component> valueStringifier;
private final Function<CycleButton<T>, MutableComponent> narrationProvider;
private final CycleButton.OnValueChange<T> onValueChange;
private final boolean displayOnlyValue;
private final OptionInstance.TooltipSupplier<T> tooltipSupplier;
CycleButton(
int x,
int y,
int width,
int height,
Component message,
Component name,
int index,
T value,
CycleButton.ValueListSupplier<T> values,
Function<T, Component> valueStringifier,
Function<CycleButton<T>, MutableComponent> narrationProvider,
CycleButton.OnValueChange<T> onValueChange,
OptionInstance.TooltipSupplier<T> tooltipSupplier,
boolean displayOnlyValue
) {
super(x, y, width, height, message);
this.name = name;
this.index = index;
this.value = value;
this.values = values;
this.valueStringifier = valueStringifier;
this.narrationProvider = narrationProvider;
this.onValueChange = onValueChange;
this.displayOnlyValue = displayOnlyValue;
this.tooltipSupplier = tooltipSupplier;
this.updateTooltip();
}
private void updateTooltip() {
this.setTooltip(this.tooltipSupplier.apply(this.value));
}
@Override
public void onPress() {
if (Screen.hasShiftDown()) {
this.cycleValue(-1);
} else {
this.cycleValue(1);
}
}
private void cycleValue(int delta) {
List<T> list = this.values.getSelectedList();
this.index = Mth.positiveModulo(this.index + delta, list.size());
T object = (T)list.get(this.index);
this.updateValue(object);
this.onValueChange.onValueChange(this, object);
}
private T getCycledValue(int delta) {
List<T> list = this.values.getSelectedList();
return (T)list.get(Mth.positiveModulo(this.index + delta, list.size()));
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
if (scrollY > 0.0) {
this.cycleValue(-1);
} else if (scrollY < 0.0) {
this.cycleValue(1);
}
return true;
}
public void setValue(T value) {
List<T> list = this.values.getSelectedList();
int i = list.indexOf(value);
if (i != -1) {
this.index = i;
}
this.updateValue(value);
}
private void updateValue(T value) {
Component component = this.createLabelForValue(value);
this.setMessage(component);
this.value = value;
this.updateTooltip();
}
private Component createLabelForValue(T value) {
return (Component)(this.displayOnlyValue ? (Component)this.valueStringifier.apply(value) : this.createFullName(value));
}
private MutableComponent createFullName(T value) {
return CommonComponents.optionNameValue(this.name, (Component)this.valueStringifier.apply(value));
}
public T getValue() {
return this.value;
}
@Override
protected MutableComponent createNarrationMessage() {
return (MutableComponent)this.narrationProvider.apply(this);
}
@Override
public void updateWidgetNarration(NarrationElementOutput narrationElementOutput) {
narrationElementOutput.add(NarratedElementType.TITLE, this.createNarrationMessage());
if (this.active) {
T object = this.getCycledValue(1);
Component component = this.createLabelForValue(object);
if (this.isFocused()) {
narrationElementOutput.add(NarratedElementType.USAGE, Component.translatable("narration.cycle_button.usage.focused", component));
} else {
narrationElementOutput.add(NarratedElementType.USAGE, Component.translatable("narration.cycle_button.usage.hovered", component));
}
}
}
public MutableComponent createDefaultNarrationMessage() {
return wrapDefaultNarrationMessage((Component)(this.displayOnlyValue ? this.createFullName(this.value) : this.getMessage()));
}
public static <T> CycleButton.Builder<T> builder(Function<T, Component> valueStringifier) {
return new CycleButton.Builder<>(valueStringifier);
}
public static CycleButton.Builder<Boolean> booleanBuilder(Component componentOn, Component componentOff) {
return new CycleButton.Builder<Boolean>(boolean_ -> boolean_ ? componentOn : componentOff).withValues(BOOLEAN_OPTIONS);
}
public static CycleButton.Builder<Boolean> onOffBuilder() {
return new CycleButton.Builder<Boolean>(boolean_ -> boolean_ ? CommonComponents.OPTION_ON : CommonComponents.OPTION_OFF).withValues(BOOLEAN_OPTIONS);
}
public static CycleButton.Builder<Boolean> onOffBuilder(boolean initialValue) {
return onOffBuilder().withInitialValue(initialValue);
}
@Environment(EnvType.CLIENT)
public static class Builder<T> {
private int initialIndex;
@Nullable
private T initialValue;
private final Function<T, Component> valueStringifier;
private OptionInstance.TooltipSupplier<T> tooltipSupplier = object -> null;
private Function<CycleButton<T>, MutableComponent> narrationProvider = CycleButton::createDefaultNarrationMessage;
private CycleButton.ValueListSupplier<T> values = CycleButton.ValueListSupplier.create(ImmutableList.<T>of());
private boolean displayOnlyValue;
public Builder(Function<T, Component> valueStringifier) {
this.valueStringifier = valueStringifier;
}
public CycleButton.Builder<T> withValues(Collection<T> values) {
return this.withValues(CycleButton.ValueListSupplier.create(values));
}
@SafeVarargs
public final CycleButton.Builder<T> withValues(T... values) {
return this.withValues(ImmutableList.<T>copyOf(values));
}
public CycleButton.Builder<T> withValues(List<T> defaultList, List<T> selectedList) {
return this.withValues(CycleButton.ValueListSupplier.create(CycleButton.DEFAULT_ALT_LIST_SELECTOR, defaultList, selectedList));
}
public CycleButton.Builder<T> withValues(BooleanSupplier altListSelector, List<T> defaultList, List<T> selectedList) {
return this.withValues(CycleButton.ValueListSupplier.create(altListSelector, defaultList, selectedList));
}
public CycleButton.Builder<T> withValues(CycleButton.ValueListSupplier<T> values) {
this.values = values;
return this;
}
public CycleButton.Builder<T> withTooltip(OptionInstance.TooltipSupplier<T> tooltipSupplier) {
this.tooltipSupplier = tooltipSupplier;
return this;
}
public CycleButton.Builder<T> withInitialValue(T initialValue) {
this.initialValue = initialValue;
int i = this.values.getDefaultList().indexOf(initialValue);
if (i != -1) {
this.initialIndex = i;
}
return this;
}
public CycleButton.Builder<T> withCustomNarration(Function<CycleButton<T>, MutableComponent> narrationProvider) {
this.narrationProvider = narrationProvider;
return this;
}
public CycleButton.Builder<T> displayOnlyValue() {
this.displayOnlyValue = true;
return this;
}
public CycleButton<T> create(Component message, CycleButton.OnValueChange<T> onValueChange) {
return this.create(0, 0, 150, 20, message, onValueChange);
}
public CycleButton<T> create(int x, int y, int width, int height, Component name) {
return this.create(x, y, width, height, name, (cycleButton, object) -> {});
}
public CycleButton<T> create(int x, int y, int width, int height, Component name, CycleButton.OnValueChange<T> onValueChange) {
List<T> list = this.values.getDefaultList();
if (list.isEmpty()) {
throw new IllegalStateException("No values for cycle button");
} else {
T object = (T)(this.initialValue != null ? this.initialValue : list.get(this.initialIndex));
Component component = (Component)this.valueStringifier.apply(object);
Component component2 = (Component)(this.displayOnlyValue ? component : CommonComponents.optionNameValue(name, component));
return new CycleButton<>(
x,
y,
width,
height,
component2,
name,
this.initialIndex,
object,
this.values,
this.valueStringifier,
this.narrationProvider,
onValueChange,
this.tooltipSupplier,
this.displayOnlyValue
);
}
}
}
@Environment(EnvType.CLIENT)
public interface OnValueChange<T> {
void onValueChange(CycleButton<T> cycleButton, T object);
}
@Environment(EnvType.CLIENT)
public interface ValueListSupplier<T> {
List<T> getSelectedList();
List<T> getDefaultList();
static <T> CycleButton.ValueListSupplier<T> create(Collection<T> values) {
List<T> list = ImmutableList.copyOf(values);
return new 1(list);
}
static <T> CycleButton.ValueListSupplier<T> create(BooleanSupplier altListSelector, List<T> defaultList, List<T> selectedList) {
List<T> list = ImmutableList.copyOf(defaultList);
List<T> list2 = ImmutableList.copyOf(selectedList);
return new 2(altListSelector, list2, list);
}
}
}