669 lines
26 KiB
Java
669 lines
26 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.Lists
|
|
* com.mojang.logging.LogUtils
|
|
* org.joml.Matrix3x2f
|
|
* org.joml.Matrix3x2fc
|
|
* org.joml.Vector2f
|
|
* org.jspecify.annotations.Nullable
|
|
* org.slf4j.Logger
|
|
*/
|
|
package net.minecraft.client.gui.components;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.mojang.logging.LogUtils;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.ListIterator;
|
|
import java.util.Optional;
|
|
import java.util.function.Consumer;
|
|
import net.minecraft.ChatFormatting;
|
|
import net.minecraft.Optionull;
|
|
import net.minecraft.client.GuiMessage;
|
|
import net.minecraft.client.GuiMessageTag;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.gui.ActiveTextCollector;
|
|
import net.minecraft.client.gui.Font;
|
|
import net.minecraft.client.gui.GuiGraphics;
|
|
import net.minecraft.client.gui.TextAlignment;
|
|
import net.minecraft.client.gui.screens.ChatScreen;
|
|
import net.minecraft.client.gui.screens.Screen;
|
|
import net.minecraft.network.chat.ClickEvent;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.chat.HoverEvent;
|
|
import net.minecraft.network.chat.MessageSignature;
|
|
import net.minecraft.network.chat.MutableComponent;
|
|
import net.minecraft.network.chat.Style;
|
|
import net.minecraft.resources.Identifier;
|
|
import net.minecraft.util.ARGB;
|
|
import net.minecraft.util.ArrayListDeque;
|
|
import net.minecraft.util.FormattedCharSequence;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.profiling.Profiler;
|
|
import net.minecraft.util.profiling.ProfilerFiller;
|
|
import net.minecraft.world.entity.player.ChatVisiblity;
|
|
import org.joml.Matrix3x2f;
|
|
import org.joml.Matrix3x2fc;
|
|
import org.joml.Vector2f;
|
|
import org.jspecify.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class ChatComponent {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final int MAX_CHAT_HISTORY = 100;
|
|
private static final int MESSAGE_INDENT = 4;
|
|
private static final int BOTTOM_MARGIN = 40;
|
|
private static final int TOOLTIP_MAX_WIDTH = 210;
|
|
private static final int TIME_BEFORE_MESSAGE_DELETION = 60;
|
|
private static final Component DELETED_CHAT_MESSAGE = Component.translatable("chat.deleted_marker").withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC);
|
|
public static final int MESSAGE_BOTTOM_TO_MESSAGE_TOP = 8;
|
|
public static final Identifier QUEUE_EXPAND_ID = Identifier.withDefaultNamespace("internal/expand_chat_queue");
|
|
private static final Style QUEUE_EXPAND_TEXT_STYLE = Style.EMPTY.withClickEvent(new ClickEvent.Custom(QUEUE_EXPAND_ID, Optional.empty())).withHoverEvent(new HoverEvent.ShowText(Component.translatable("chat.queue.tooltip")));
|
|
private final Minecraft minecraft;
|
|
private final ArrayListDeque<String> recentChat = new ArrayListDeque(100);
|
|
private final List<GuiMessage> allMessages = Lists.newArrayList();
|
|
private final List<GuiMessage.Line> trimmedMessages = Lists.newArrayList();
|
|
private int chatScrollbarPos;
|
|
private boolean newMessageSinceScroll;
|
|
private @Nullable Draft latestDraft;
|
|
private @Nullable ChatScreen preservedScreen;
|
|
private final List<DelayedMessageDeletion> messageDeletionQueue = new ArrayList<DelayedMessageDeletion>();
|
|
|
|
public ChatComponent(Minecraft minecraft) {
|
|
this.minecraft = minecraft;
|
|
this.recentChat.addAll(minecraft.commandHistory().history());
|
|
}
|
|
|
|
public void tick() {
|
|
if (!this.messageDeletionQueue.isEmpty()) {
|
|
this.processMessageDeletionQueue();
|
|
}
|
|
}
|
|
|
|
private int forEachLine(AlphaCalculator alphaCalculator, LineConsumer lineConsumer) {
|
|
int perPage = this.getLinesPerPage();
|
|
int count = 0;
|
|
for (int i = Math.min(this.trimmedMessages.size() - this.chatScrollbarPos, perPage) - 1; i >= 0; --i) {
|
|
int messageIndex = i + this.chatScrollbarPos;
|
|
GuiMessage.Line message = this.trimmedMessages.get(messageIndex);
|
|
float alpha = alphaCalculator.calculate(message);
|
|
if (!(alpha > 1.0E-5f)) continue;
|
|
++count;
|
|
lineConsumer.accept(message, i, alpha);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public void render(GuiGraphics graphics, Font font, int ticks, int mouseX, int mouseY, boolean isChatting) {
|
|
graphics.pose().pushMatrix();
|
|
this.render(isChatting ? new DrawingFocusedGraphicsAccess(graphics, font, mouseX, mouseY) : new DrawingBackgroundGraphicsAccess(graphics), graphics.guiHeight(), ticks, isChatting);
|
|
graphics.pose().popMatrix();
|
|
}
|
|
|
|
public void captureClickableText(ActiveTextCollector activeTextCollector, int screenHeight, int ticks, boolean isChatting) {
|
|
this.render(new ClickableTextOnlyGraphicsAccess(activeTextCollector), screenHeight, ticks, isChatting);
|
|
}
|
|
|
|
private void render(final ChatGraphicsAccess graphics, int screenHeight, int ticks, boolean isChatting) {
|
|
if (this.isChatHidden()) {
|
|
return;
|
|
}
|
|
int total = this.trimmedMessages.size();
|
|
if (total <= 0) {
|
|
return;
|
|
}
|
|
ProfilerFiller profiler = Profiler.get();
|
|
profiler.push("chat");
|
|
float scale = (float)this.getScale();
|
|
int maxWidth = Mth.ceil((float)this.getWidth() / scale);
|
|
final int chatBottom = Mth.floor((float)(screenHeight - 40) / scale);
|
|
final float textOpacity = this.minecraft.options.chatOpacity().get().floatValue() * 0.9f + 0.1f;
|
|
float backgroundOpacity = this.minecraft.options.textBackgroundOpacity().get().floatValue();
|
|
final int messageHeight = this.minecraft.font.lineHeight;
|
|
int messageBottomToMessageTop = 8;
|
|
double chatLineSpacing = this.minecraft.options.chatLineSpacing().get();
|
|
final int entryHeight = (int)((double)messageHeight * (chatLineSpacing + 1.0));
|
|
final int entryBottomToMessageY = (int)Math.round(8.0 * (chatLineSpacing + 1.0) - 4.0 * chatLineSpacing);
|
|
long queueSize = this.minecraft.getChatListener().queueSize();
|
|
AlphaCalculator alphaCalculator = isChatting ? AlphaCalculator.FULLY_VISIBLE : AlphaCalculator.timeBased(ticks);
|
|
graphics.updatePose(pose -> {
|
|
pose.scale(scale, scale);
|
|
pose.translate(4.0f, 0.0f);
|
|
});
|
|
this.forEachLine(alphaCalculator, (line, lineIndex, alpha) -> {
|
|
int entryBottom = chatBottom - lineIndex * entryHeight;
|
|
int entryTop = entryBottom - entryHeight;
|
|
graphics.fill(-4, entryTop, maxWidth + 4 + 4, entryBottom, ARGB.black(alpha * backgroundOpacity));
|
|
});
|
|
if (queueSize > 0L) {
|
|
graphics.fill(-2, chatBottom, maxWidth + 4, chatBottom + messageHeight, ARGB.black(backgroundOpacity));
|
|
}
|
|
int count = this.forEachLine(alphaCalculator, new LineConsumer(){
|
|
boolean hoveredOverCurrentMessage;
|
|
|
|
@Override
|
|
public void accept(GuiMessage.Line line, int lineIndex, float alpha) {
|
|
boolean forceIconRendering;
|
|
int entryBottom = chatBottom - lineIndex * entryHeight;
|
|
int entryTop = entryBottom - entryHeight;
|
|
int textTop = entryBottom - entryBottomToMessageY;
|
|
boolean hoveredOverCurrentLine = graphics.handleMessage(textTop, alpha * textOpacity, line.content());
|
|
this.hoveredOverCurrentMessage |= hoveredOverCurrentLine;
|
|
if (line.endOfEntry()) {
|
|
forceIconRendering = this.hoveredOverCurrentMessage;
|
|
this.hoveredOverCurrentMessage = false;
|
|
} else {
|
|
forceIconRendering = false;
|
|
}
|
|
GuiMessageTag tag = line.tag();
|
|
if (tag != null) {
|
|
graphics.handleTag(-4, entryTop, -2, entryBottom, alpha * textOpacity, tag);
|
|
if (tag.icon() != null) {
|
|
int iconLeft = line.getTagIconLeft(ChatComponent.this.minecraft.font);
|
|
int textBottom = textTop + messageHeight;
|
|
graphics.handleTagIcon(iconLeft, textBottom, forceIconRendering, tag, tag.icon());
|
|
}
|
|
}
|
|
}
|
|
});
|
|
if (queueSize > 0L) {
|
|
int queueLineBottom = chatBottom + messageHeight;
|
|
MutableComponent queueMessage = Component.translatable("chat.queue", queueSize).setStyle(QUEUE_EXPAND_TEXT_STYLE);
|
|
graphics.handleMessage(queueLineBottom - 8, 0.5f * textOpacity, queueMessage.getVisualOrderText());
|
|
}
|
|
if (isChatting) {
|
|
int virtualHeight = total * entryHeight;
|
|
int chatHeight = count * entryHeight;
|
|
int y = this.chatScrollbarPos * chatHeight / total - chatBottom;
|
|
int height = chatHeight * chatHeight / virtualHeight;
|
|
if (virtualHeight != chatHeight) {
|
|
int alpha2 = y > 0 ? 170 : 96;
|
|
int color = this.newMessageSinceScroll ? 0xCC3333 : 0x3333AA;
|
|
int scrollBarStartX = maxWidth + 4;
|
|
graphics.fill(scrollBarStartX, -y, scrollBarStartX + 2, -y - height, ARGB.color(alpha2, color));
|
|
graphics.fill(scrollBarStartX + 2, -y, scrollBarStartX + 1, -y - height, ARGB.color(alpha2, 0xCCCCCC));
|
|
}
|
|
}
|
|
profiler.pop();
|
|
}
|
|
|
|
private boolean isChatHidden() {
|
|
return this.minecraft.options.chatVisibility().get() == ChatVisiblity.HIDDEN;
|
|
}
|
|
|
|
public void clearMessages(boolean history) {
|
|
this.minecraft.getChatListener().flushQueue();
|
|
this.messageDeletionQueue.clear();
|
|
this.trimmedMessages.clear();
|
|
this.allMessages.clear();
|
|
if (history) {
|
|
this.recentChat.clear();
|
|
this.recentChat.addAll(this.minecraft.commandHistory().history());
|
|
}
|
|
}
|
|
|
|
public void addMessage(Component message) {
|
|
this.addMessage(message, null, this.minecraft.isSingleplayer() ? GuiMessageTag.systemSinglePlayer() : GuiMessageTag.system());
|
|
}
|
|
|
|
public void addMessage(Component contents, @Nullable MessageSignature signature, @Nullable GuiMessageTag tag) {
|
|
GuiMessage message = new GuiMessage(this.minecraft.gui.getGuiTicks(), contents, signature, tag);
|
|
this.logChatMessage(message);
|
|
this.addMessageToDisplayQueue(message);
|
|
this.addMessageToQueue(message);
|
|
}
|
|
|
|
private void logChatMessage(GuiMessage message) {
|
|
String messageString = message.content().getString().replaceAll("\r", "\\\\r").replaceAll("\n", "\\\\n");
|
|
String logTag = Optionull.map(message.tag(), GuiMessageTag::logTag);
|
|
if (logTag != null) {
|
|
LOGGER.info("[{}] [CHAT] {}", (Object)logTag, (Object)messageString);
|
|
} else {
|
|
LOGGER.info("[CHAT] {}", (Object)messageString);
|
|
}
|
|
}
|
|
|
|
private void addMessageToDisplayQueue(GuiMessage message) {
|
|
int maxWidth = Mth.floor((double)this.getWidth() / this.getScale());
|
|
List<FormattedCharSequence> lines = message.splitLines(this.minecraft.font, maxWidth);
|
|
boolean chatting = this.isChatFocused();
|
|
for (int i = 0; i < lines.size(); ++i) {
|
|
FormattedCharSequence line = lines.get(i);
|
|
if (chatting && this.chatScrollbarPos > 0) {
|
|
this.newMessageSinceScroll = true;
|
|
this.scrollChat(1);
|
|
}
|
|
boolean endOfEntry = i == lines.size() - 1;
|
|
this.trimmedMessages.addFirst(new GuiMessage.Line(message.addedTime(), line, message.tag(), endOfEntry));
|
|
}
|
|
while (this.trimmedMessages.size() > 100) {
|
|
this.trimmedMessages.removeLast();
|
|
}
|
|
}
|
|
|
|
private void addMessageToQueue(GuiMessage message) {
|
|
this.allMessages.addFirst(message);
|
|
while (this.allMessages.size() > 100) {
|
|
this.allMessages.removeLast();
|
|
}
|
|
}
|
|
|
|
private void processMessageDeletionQueue() {
|
|
int time = this.minecraft.gui.getGuiTicks();
|
|
this.messageDeletionQueue.removeIf(entry -> {
|
|
if (time >= entry.deletableAfter()) {
|
|
return this.deleteMessageOrDelay(entry.signature()) == null;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
public void deleteMessage(MessageSignature signature) {
|
|
DelayedMessageDeletion delayedMessage = this.deleteMessageOrDelay(signature);
|
|
if (delayedMessage != null) {
|
|
this.messageDeletionQueue.add(delayedMessage);
|
|
}
|
|
}
|
|
|
|
private @Nullable DelayedMessageDeletion deleteMessageOrDelay(MessageSignature signature) {
|
|
int time = this.minecraft.gui.getGuiTicks();
|
|
ListIterator<GuiMessage> iterator = this.allMessages.listIterator();
|
|
while (iterator.hasNext()) {
|
|
GuiMessage message = iterator.next();
|
|
if (!signature.equals(message.signature())) continue;
|
|
int deletableAfter = message.addedTime() + 60;
|
|
if (time >= deletableAfter) {
|
|
iterator.set(this.createDeletedMarker(message));
|
|
this.refreshTrimmedMessages();
|
|
return null;
|
|
}
|
|
return new DelayedMessageDeletion(signature, deletableAfter);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private GuiMessage createDeletedMarker(GuiMessage message) {
|
|
return new GuiMessage(message.addedTime(), DELETED_CHAT_MESSAGE, null, GuiMessageTag.system());
|
|
}
|
|
|
|
public void rescaleChat() {
|
|
this.resetChatScroll();
|
|
this.refreshTrimmedMessages();
|
|
}
|
|
|
|
private void refreshTrimmedMessages() {
|
|
this.trimmedMessages.clear();
|
|
for (GuiMessage message : Lists.reverse(this.allMessages)) {
|
|
this.addMessageToDisplayQueue(message);
|
|
}
|
|
}
|
|
|
|
public ArrayListDeque<String> getRecentChat() {
|
|
return this.recentChat;
|
|
}
|
|
|
|
public void addRecentChat(String message) {
|
|
if (!message.equals(this.recentChat.peekLast())) {
|
|
if (this.recentChat.size() >= 100) {
|
|
this.recentChat.removeFirst();
|
|
}
|
|
this.recentChat.addLast(message);
|
|
}
|
|
if (message.startsWith("/")) {
|
|
this.minecraft.commandHistory().addCommand(message);
|
|
}
|
|
}
|
|
|
|
public void resetChatScroll() {
|
|
this.chatScrollbarPos = 0;
|
|
this.newMessageSinceScroll = false;
|
|
}
|
|
|
|
public void scrollChat(int dir) {
|
|
this.chatScrollbarPos += dir;
|
|
int max = this.trimmedMessages.size();
|
|
if (this.chatScrollbarPos > max - this.getLinesPerPage()) {
|
|
this.chatScrollbarPos = max - this.getLinesPerPage();
|
|
}
|
|
if (this.chatScrollbarPos <= 0) {
|
|
this.chatScrollbarPos = 0;
|
|
this.newMessageSinceScroll = false;
|
|
}
|
|
}
|
|
|
|
public boolean isChatFocused() {
|
|
return this.minecraft.screen instanceof ChatScreen;
|
|
}
|
|
|
|
private int getWidth() {
|
|
return ChatComponent.getWidth(this.minecraft.options.chatWidth().get());
|
|
}
|
|
|
|
private int getHeight() {
|
|
return ChatComponent.getHeight(this.isChatFocused() ? this.minecraft.options.chatHeightFocused().get() : this.minecraft.options.chatHeightUnfocused().get());
|
|
}
|
|
|
|
private double getScale() {
|
|
return this.minecraft.options.chatScale().get();
|
|
}
|
|
|
|
public static int getWidth(double pct) {
|
|
int max = 320;
|
|
int min = 40;
|
|
return Mth.floor(pct * 280.0 + 40.0);
|
|
}
|
|
|
|
public static int getHeight(double pct) {
|
|
int max = 180;
|
|
int min = 20;
|
|
return Mth.floor(pct * 160.0 + 20.0);
|
|
}
|
|
|
|
public static double defaultUnfocusedPct() {
|
|
int max = 180;
|
|
int min = 20;
|
|
return 70.0 / (double)(ChatComponent.getHeight(1.0) - 20);
|
|
}
|
|
|
|
public int getLinesPerPage() {
|
|
return this.getHeight() / this.getLineHeight();
|
|
}
|
|
|
|
private int getLineHeight() {
|
|
return (int)((double)this.minecraft.font.lineHeight * (this.minecraft.options.chatLineSpacing().get() + 1.0));
|
|
}
|
|
|
|
public void saveAsDraft(String text) {
|
|
boolean isCommand = text.startsWith("/");
|
|
this.latestDraft = new Draft(text, isCommand ? ChatMethod.COMMAND : ChatMethod.MESSAGE);
|
|
}
|
|
|
|
public void discardDraft() {
|
|
this.latestDraft = null;
|
|
}
|
|
|
|
public <T extends ChatScreen> T createScreen(ChatMethod chatMethod, ChatScreen.ChatConstructor<T> chat) {
|
|
if (this.latestDraft != null && chatMethod.isDraftRestorable(this.latestDraft)) {
|
|
return chat.create(this.latestDraft.text(), true);
|
|
}
|
|
return chat.create(chatMethod.prefix(), false);
|
|
}
|
|
|
|
public void openScreen(ChatMethod chatMethod, ChatScreen.ChatConstructor<?> chat) {
|
|
this.minecraft.setScreen((Screen)this.createScreen(chatMethod, chat));
|
|
}
|
|
|
|
public void preserveCurrentChatScreen() {
|
|
Screen screen = this.minecraft.screen;
|
|
if (screen instanceof ChatScreen) {
|
|
ChatScreen chatScreen;
|
|
this.preservedScreen = chatScreen = (ChatScreen)screen;
|
|
}
|
|
}
|
|
|
|
public @Nullable ChatScreen restoreChatScreen() {
|
|
ChatScreen restoredScreen = this.preservedScreen;
|
|
this.preservedScreen = null;
|
|
return restoredScreen;
|
|
}
|
|
|
|
public State storeState() {
|
|
return new State(List.copyOf(this.allMessages), List.copyOf(this.recentChat), List.copyOf(this.messageDeletionQueue));
|
|
}
|
|
|
|
public void restoreState(State state) {
|
|
this.recentChat.clear();
|
|
this.recentChat.addAll(state.history);
|
|
this.messageDeletionQueue.clear();
|
|
this.messageDeletionQueue.addAll(state.delayedMessageDeletions);
|
|
this.allMessages.clear();
|
|
this.allMessages.addAll(state.messages);
|
|
this.refreshTrimmedMessages();
|
|
}
|
|
|
|
@FunctionalInterface
|
|
private static interface AlphaCalculator {
|
|
public static final AlphaCalculator FULLY_VISIBLE = message -> 1.0f;
|
|
|
|
public static AlphaCalculator timeBased(int currentTickTime) {
|
|
return message -> {
|
|
int tickDelta = currentTickTime - message.addedTime();
|
|
double t = (double)tickDelta / 200.0;
|
|
t = 1.0 - t;
|
|
t *= 10.0;
|
|
t = Mth.clamp(t, 0.0, 1.0);
|
|
t *= t;
|
|
return (float)t;
|
|
};
|
|
}
|
|
|
|
public float calculate(GuiMessage.Line var1);
|
|
}
|
|
|
|
@FunctionalInterface
|
|
private static interface LineConsumer {
|
|
public void accept(GuiMessage.Line var1, int var2, float var3);
|
|
}
|
|
|
|
private static class DrawingFocusedGraphicsAccess
|
|
implements ChatGraphicsAccess,
|
|
Consumer<Style> {
|
|
private final GuiGraphics graphics;
|
|
private final Font font;
|
|
private final ActiveTextCollector textRenderer;
|
|
private ActiveTextCollector.Parameters parameters;
|
|
private final int globalMouseX;
|
|
private final int globalMouseY;
|
|
private final Vector2f localMousePos = new Vector2f();
|
|
private @Nullable Style hoveredStyle;
|
|
|
|
public DrawingFocusedGraphicsAccess(GuiGraphics graphics, Font font, int mouseX, int mouseY) {
|
|
this.graphics = graphics;
|
|
this.font = font;
|
|
this.textRenderer = graphics.textRenderer(GuiGraphics.HoveredTextEffects.TOOLTIP_AND_CURSOR, this);
|
|
this.globalMouseX = mouseX;
|
|
this.globalMouseY = mouseY;
|
|
this.parameters = this.textRenderer.defaultParameters();
|
|
this.updateLocalMousePos();
|
|
}
|
|
|
|
private void updateLocalMousePos() {
|
|
this.graphics.pose().invert(new Matrix3x2f()).transformPosition((float)this.globalMouseX, (float)this.globalMouseY, this.localMousePos);
|
|
}
|
|
|
|
@Override
|
|
public void updatePose(Consumer<Matrix3x2f> updater) {
|
|
updater.accept((Matrix3x2f)this.graphics.pose());
|
|
this.parameters = this.parameters.withPose((Matrix3x2fc)new Matrix3x2f((Matrix3x2fc)this.graphics.pose()));
|
|
this.updateLocalMousePos();
|
|
}
|
|
|
|
@Override
|
|
public void fill(int x0, int y0, int x1, int y1, int color) {
|
|
this.graphics.fill(x0, y0, x1, y1, color);
|
|
}
|
|
|
|
@Override
|
|
public void accept(Style style) {
|
|
this.hoveredStyle = style;
|
|
}
|
|
|
|
@Override
|
|
public boolean handleMessage(int textTop, float opacity, FormattedCharSequence message) {
|
|
this.hoveredStyle = null;
|
|
this.textRenderer.accept(TextAlignment.LEFT, 0, textTop, this.parameters.withOpacity(opacity), message);
|
|
return this.hoveredStyle != null;
|
|
}
|
|
|
|
private boolean isMouseOver(int left, int top, int right, int bottom) {
|
|
return ActiveTextCollector.isPointInRectangle(this.localMousePos.x, this.localMousePos.y, left, top, right, bottom);
|
|
}
|
|
|
|
@Override
|
|
public void handleTag(int x0, int y0, int x1, int y1, float opacity, GuiMessageTag tag) {
|
|
int indicatorColor = ARGB.color(opacity, tag.indicatorColor());
|
|
this.graphics.fill(x0, y0, x1, y1, indicatorColor);
|
|
if (this.isMouseOver(x0, y0, x1, y1)) {
|
|
this.showTooltip(tag);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void handleTagIcon(int left, int bottom, boolean forceVisible, GuiMessageTag tag, GuiMessageTag.Icon icon) {
|
|
int top = bottom - icon.height - 1;
|
|
int right = left + icon.width;
|
|
boolean isMouseOver = this.isMouseOver(left, top, right, bottom);
|
|
if (isMouseOver) {
|
|
this.showTooltip(tag);
|
|
}
|
|
if (forceVisible || isMouseOver) {
|
|
icon.draw(this.graphics, left, top);
|
|
}
|
|
}
|
|
|
|
private void showTooltip(GuiMessageTag tag) {
|
|
if (tag.text() != null) {
|
|
this.graphics.setTooltipForNextFrame(this.font, this.font.split(tag.text(), 210), this.globalMouseX, this.globalMouseY);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class DrawingBackgroundGraphicsAccess
|
|
implements ChatGraphicsAccess {
|
|
private final GuiGraphics graphics;
|
|
private final ActiveTextCollector textRenderer;
|
|
private ActiveTextCollector.Parameters parameters;
|
|
|
|
public DrawingBackgroundGraphicsAccess(GuiGraphics graphics) {
|
|
this.graphics = graphics;
|
|
this.textRenderer = graphics.textRenderer(GuiGraphics.HoveredTextEffects.NONE, null);
|
|
this.parameters = this.textRenderer.defaultParameters();
|
|
}
|
|
|
|
@Override
|
|
public void updatePose(Consumer<Matrix3x2f> updater) {
|
|
updater.accept((Matrix3x2f)this.graphics.pose());
|
|
this.parameters = this.parameters.withPose((Matrix3x2fc)new Matrix3x2f((Matrix3x2fc)this.graphics.pose()));
|
|
}
|
|
|
|
@Override
|
|
public void fill(int x0, int y0, int x1, int y1, int color) {
|
|
this.graphics.fill(x0, y0, x1, y1, color);
|
|
}
|
|
|
|
@Override
|
|
public boolean handleMessage(int textTop, float opacity, FormattedCharSequence message) {
|
|
this.textRenderer.accept(TextAlignment.LEFT, 0, textTop, this.parameters.withOpacity(opacity), message);
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void handleTag(int x0, int y0, int x1, int y1, float opacity, GuiMessageTag tag) {
|
|
int indicatorColor = ARGB.color(opacity, tag.indicatorColor());
|
|
this.graphics.fill(x0, y0, x1, y1, indicatorColor);
|
|
}
|
|
|
|
@Override
|
|
public void handleTagIcon(int left, int bottom, boolean forceVisible, GuiMessageTag tag, GuiMessageTag.Icon icon) {
|
|
}
|
|
}
|
|
|
|
public static interface ChatGraphicsAccess {
|
|
public void updatePose(Consumer<Matrix3x2f> var1);
|
|
|
|
public void fill(int var1, int var2, int var3, int var4, int var5);
|
|
|
|
public boolean handleMessage(int var1, float var2, FormattedCharSequence var3);
|
|
|
|
public void handleTag(int var1, int var2, int var3, int var4, float var5, GuiMessageTag var6);
|
|
|
|
public void handleTagIcon(int var1, int var2, boolean var3, GuiMessageTag var4, GuiMessageTag.Icon var5);
|
|
}
|
|
|
|
private static class ClickableTextOnlyGraphicsAccess
|
|
implements ChatGraphicsAccess {
|
|
private final ActiveTextCollector output;
|
|
|
|
public ClickableTextOnlyGraphicsAccess(ActiveTextCollector output) {
|
|
this.output = output;
|
|
}
|
|
|
|
@Override
|
|
public void updatePose(Consumer<Matrix3x2f> updater) {
|
|
ActiveTextCollector.Parameters defaultParameters = this.output.defaultParameters();
|
|
Matrix3x2f newPose = new Matrix3x2f(defaultParameters.pose());
|
|
updater.accept(newPose);
|
|
this.output.defaultParameters(defaultParameters.withPose((Matrix3x2fc)newPose));
|
|
}
|
|
|
|
@Override
|
|
public void fill(int x0, int y0, int x1, int y1, int color) {
|
|
}
|
|
|
|
@Override
|
|
public boolean handleMessage(int textTop, float opacity, FormattedCharSequence message) {
|
|
this.output.accept(TextAlignment.LEFT, 0, textTop, message);
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void handleTag(int x0, int y0, int x1, int y1, float opacity, GuiMessageTag tag) {
|
|
}
|
|
|
|
@Override
|
|
public void handleTagIcon(int left, int bottom, boolean forceVisible, GuiMessageTag tag, GuiMessageTag.Icon icon) {
|
|
}
|
|
}
|
|
|
|
private record DelayedMessageDeletion(MessageSignature signature, int deletableAfter) {
|
|
}
|
|
|
|
public record Draft(String text, ChatMethod chatMethod) {
|
|
}
|
|
|
|
public static enum ChatMethod {
|
|
MESSAGE(""){
|
|
|
|
@Override
|
|
public boolean isDraftRestorable(Draft draft) {
|
|
return true;
|
|
}
|
|
}
|
|
,
|
|
COMMAND("/"){
|
|
|
|
@Override
|
|
public boolean isDraftRestorable(Draft draft) {
|
|
return this == draft.chatMethod;
|
|
}
|
|
};
|
|
|
|
private final String prefix;
|
|
|
|
private ChatMethod(String prefix) {
|
|
this.prefix = prefix;
|
|
}
|
|
|
|
public String prefix() {
|
|
return this.prefix;
|
|
}
|
|
|
|
public abstract boolean isDraftRestorable(Draft var1);
|
|
}
|
|
|
|
public static class State {
|
|
private final List<GuiMessage> messages;
|
|
private final List<String> history;
|
|
private final List<DelayedMessageDeletion> delayedMessageDeletions;
|
|
|
|
public State(List<GuiMessage> messages, List<String> history, List<DelayedMessageDeletion> delayedMessageDeletions) {
|
|
this.messages = messages;
|
|
this.history = history;
|
|
this.delayedMessageDeletions = delayedMessageDeletions;
|
|
}
|
|
}
|
|
}
|
|
|