328 lines
8.9 KiB
Java
328 lines
8.9 KiB
Java
package net.minecraft.client.gui.components;
|
|
|
|
import java.time.Duration;
|
|
import java.util.function.Consumer;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.gui.ComponentPath;
|
|
import net.minecraft.client.gui.Font;
|
|
import net.minecraft.client.gui.GuiGraphics;
|
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
|
import net.minecraft.client.gui.layouts.LayoutElement;
|
|
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.navigation.FocusNavigationEvent;
|
|
import net.minecraft.client.gui.navigation.ScreenRectangle;
|
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
|
import net.minecraft.client.sounds.SoundManager;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.chat.MutableComponent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.util.Mth;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public abstract class AbstractWidget implements Renderable, GuiEventListener, LayoutElement, NarratableEntry {
|
|
private static final double PERIOD_PER_SCROLLED_PIXEL = 0.5;
|
|
private static final double MIN_SCROLL_PERIOD = 3.0;
|
|
protected int width;
|
|
protected int height;
|
|
private int x;
|
|
private int y;
|
|
private Component message;
|
|
protected boolean isHovered;
|
|
public boolean active = true;
|
|
public boolean visible = true;
|
|
protected float alpha = 1.0F;
|
|
private int tabOrderGroup;
|
|
private boolean focused;
|
|
private final WidgetTooltipHolder tooltip = new WidgetTooltipHolder();
|
|
|
|
public AbstractWidget(int x, int y, int width, int height, Component message) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = width;
|
|
this.height = height;
|
|
this.message = message;
|
|
}
|
|
|
|
@Override
|
|
public int getHeight() {
|
|
return this.height;
|
|
}
|
|
|
|
@Override
|
|
public final void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
|
if (this.visible) {
|
|
this.isHovered = guiGraphics.containsPointInScissor(mouseX, mouseY)
|
|
&& mouseX >= this.getX()
|
|
&& mouseY >= this.getY()
|
|
&& mouseX < this.getX() + this.width
|
|
&& mouseY < this.getY() + this.height;
|
|
this.renderWidget(guiGraphics, mouseX, mouseY, partialTick);
|
|
this.tooltip.refreshTooltipForNextRenderPass(this.isHovered(), this.isFocused(), this.getRectangle());
|
|
}
|
|
}
|
|
|
|
public void setTooltip(@Nullable Tooltip tooltip) {
|
|
this.tooltip.set(tooltip);
|
|
}
|
|
|
|
@Nullable
|
|
public Tooltip getTooltip() {
|
|
return this.tooltip.get();
|
|
}
|
|
|
|
public void setTooltipDelay(Duration tooltipDelay) {
|
|
this.tooltip.setDelay(tooltipDelay);
|
|
}
|
|
|
|
protected MutableComponent createNarrationMessage() {
|
|
return wrapDefaultNarrationMessage(this.getMessage());
|
|
}
|
|
|
|
public static MutableComponent wrapDefaultNarrationMessage(Component message) {
|
|
return Component.translatable("gui.narrate.button", message);
|
|
}
|
|
|
|
protected abstract void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick);
|
|
|
|
protected static void renderScrollingString(GuiGraphics guiGraphics, Font font, Component text, int minX, int minY, int maxX, int maxY, int color) {
|
|
renderScrollingString(guiGraphics, font, text, (minX + maxX) / 2, minX, minY, maxX, maxY, color);
|
|
}
|
|
|
|
protected static void renderScrollingString(GuiGraphics guiGraphics, Font font, Component text, int centerX, int minX, int minY, int maxX, int maxY, int color) {
|
|
int i = font.width(text);
|
|
int j = (minY + maxY - 9) / 2 + 1;
|
|
int k = maxX - minX;
|
|
if (i > k) {
|
|
int l = i - k;
|
|
double d = Util.getMillis() / 1000.0;
|
|
double e = Math.max(l * 0.5, 3.0);
|
|
double f = Math.sin((Math.PI / 2) * Math.cos((Math.PI * 2) * d / e)) / 2.0 + 0.5;
|
|
double g = Mth.lerp(f, 0.0, (double)l);
|
|
guiGraphics.enableScissor(minX, minY, maxX, maxY);
|
|
guiGraphics.drawString(font, text, minX - (int)g, j, color);
|
|
guiGraphics.disableScissor();
|
|
} else {
|
|
int l = Mth.clamp(centerX, minX + i / 2, maxX - i / 2);
|
|
guiGraphics.drawCenteredString(font, text, l, j, color);
|
|
}
|
|
}
|
|
|
|
protected void renderScrollingString(GuiGraphics guiGraphics, Font font, int width, int color) {
|
|
int i = this.getX() + width;
|
|
int j = this.getX() + this.getWidth() - width;
|
|
renderScrollingString(guiGraphics, font, this.getMessage(), i, this.getY(), j, this.getY() + this.getHeight(), color);
|
|
}
|
|
|
|
public void onClick(double mouseX, double mouseY) {
|
|
}
|
|
|
|
public void onRelease(double mouseX, double mouseY) {
|
|
}
|
|
|
|
protected void onDrag(double mouseX, double mouseY, double dragX, double dragY) {
|
|
}
|
|
|
|
@Override
|
|
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
|
if (this.active && this.visible) {
|
|
if (this.isValidClickButton(button)) {
|
|
boolean bl = this.isMouseOver(mouseX, mouseY);
|
|
if (bl) {
|
|
this.playDownSound(Minecraft.getInstance().getSoundManager());
|
|
this.onClick(mouseX, mouseY);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
|
if (this.isValidClickButton(button)) {
|
|
this.onRelease(mouseX, mouseY);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected boolean isValidClickButton(int button) {
|
|
return button == 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) {
|
|
if (this.isValidClickButton(button)) {
|
|
this.onDrag(mouseX, mouseY, dragX, dragY);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public ComponentPath nextFocusPath(FocusNavigationEvent event) {
|
|
if (!this.active || !this.visible) {
|
|
return null;
|
|
} else {
|
|
return !this.isFocused() ? ComponentPath.leaf(this) : null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isMouseOver(double mouseX, double mouseY) {
|
|
return this.active && this.visible && mouseX >= this.getX() && mouseY >= this.getY() && mouseX < this.getRight() && mouseY < this.getBottom();
|
|
}
|
|
|
|
public void playDownSound(SoundManager handler) {
|
|
playButtonClickSound(handler);
|
|
}
|
|
|
|
public static void playButtonClickSound(SoundManager soundManager) {
|
|
soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
|
|
}
|
|
|
|
@Override
|
|
public int getWidth() {
|
|
return this.width;
|
|
}
|
|
|
|
public void setWidth(int width) {
|
|
this.width = width;
|
|
}
|
|
|
|
public void setHeight(int height) {
|
|
this.height = height;
|
|
}
|
|
|
|
public void setAlpha(float alpha) {
|
|
this.alpha = alpha;
|
|
}
|
|
|
|
public void setMessage(Component message) {
|
|
this.message = message;
|
|
}
|
|
|
|
public Component getMessage() {
|
|
return this.message;
|
|
}
|
|
|
|
@Override
|
|
public boolean isFocused() {
|
|
return this.focused;
|
|
}
|
|
|
|
public boolean isHovered() {
|
|
return this.isHovered;
|
|
}
|
|
|
|
public boolean isHoveredOrFocused() {
|
|
return this.isHovered() || this.isFocused();
|
|
}
|
|
|
|
@Override
|
|
public boolean isActive() {
|
|
return this.visible && this.active;
|
|
}
|
|
|
|
@Override
|
|
public void setFocused(boolean focused) {
|
|
this.focused = focused;
|
|
}
|
|
|
|
@Override
|
|
public NarratableEntry.NarrationPriority narrationPriority() {
|
|
if (this.isFocused()) {
|
|
return NarratableEntry.NarrationPriority.FOCUSED;
|
|
} else {
|
|
return this.isHovered ? NarratableEntry.NarrationPriority.HOVERED : NarratableEntry.NarrationPriority.NONE;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public final void updateNarration(NarrationElementOutput narrationElementOutput) {
|
|
this.updateWidgetNarration(narrationElementOutput);
|
|
this.tooltip.updateNarration(narrationElementOutput);
|
|
}
|
|
|
|
protected abstract void updateWidgetNarration(NarrationElementOutput narrationElementOutput);
|
|
|
|
protected void defaultButtonNarrationText(NarrationElementOutput narrationElementOutput) {
|
|
narrationElementOutput.add(NarratedElementType.TITLE, this.createNarrationMessage());
|
|
if (this.active) {
|
|
if (this.isFocused()) {
|
|
narrationElementOutput.add(NarratedElementType.USAGE, Component.translatable("narration.button.usage.focused"));
|
|
} else {
|
|
narrationElementOutput.add(NarratedElementType.USAGE, Component.translatable("narration.button.usage.hovered"));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getX() {
|
|
return this.x;
|
|
}
|
|
|
|
@Override
|
|
public void setX(int x) {
|
|
this.x = x;
|
|
}
|
|
|
|
@Override
|
|
public int getY() {
|
|
return this.y;
|
|
}
|
|
|
|
@Override
|
|
public void setY(int y) {
|
|
this.y = y;
|
|
}
|
|
|
|
public int getRight() {
|
|
return this.getX() + this.getWidth();
|
|
}
|
|
|
|
public int getBottom() {
|
|
return this.getY() + this.getHeight();
|
|
}
|
|
|
|
@Override
|
|
public void visitWidgets(Consumer<AbstractWidget> consumer) {
|
|
consumer.accept(this);
|
|
}
|
|
|
|
public void setSize(int width, int height) {
|
|
this.width = width;
|
|
this.height = height;
|
|
}
|
|
|
|
@Override
|
|
public ScreenRectangle getRectangle() {
|
|
return LayoutElement.super.getRectangle();
|
|
}
|
|
|
|
public void setRectangle(int width, int height, int x, int y) {
|
|
this.setSize(width, height);
|
|
this.setPosition(x, y);
|
|
}
|
|
|
|
@Override
|
|
public int getTabOrderGroup() {
|
|
return this.tabOrderGroup;
|
|
}
|
|
|
|
public void setTabOrderGroup(int tabOrderGroup) {
|
|
this.tabOrderGroup = tabOrderGroup;
|
|
}
|
|
}
|