538 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			538 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| 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 net.fabricmc.api.EnvType;
 | |
| import net.fabricmc.api.Environment;
 | |
| 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.GuiGraphics;
 | |
| import net.minecraft.client.gui.screens.ChatScreen;
 | |
| import net.minecraft.client.multiplayer.chat.ChatListener;
 | |
| import net.minecraft.network.chat.Component;
 | |
| import net.minecraft.network.chat.MessageSignature;
 | |
| import net.minecraft.network.chat.Style;
 | |
| 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.jetbrains.annotations.Nullable;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| public class ChatComponent {
 | |
| 	private static final Logger LOGGER = LogUtils.getLogger();
 | |
| 	private static final int MAX_CHAT_HISTORY = 100;
 | |
| 	private static final int MESSAGE_NOT_FOUND = -1;
 | |
| 	private static final int MESSAGE_INDENT = 4;
 | |
| 	private static final int MESSAGE_TAG_MARGIN_LEFT = 4;
 | |
| 	private static final int BOTTOM_MARGIN = 40;
 | |
| 	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);
 | |
| 	private final Minecraft minecraft;
 | |
| 	/**
 | |
| 	 * A list of messages previously sent through the chat GUI
 | |
| 	 */
 | |
| 	private final ArrayListDeque<String> recentChat = new ArrayListDeque<>(100);
 | |
| 	/**
 | |
| 	 * Chat lines to be displayed in the chat box
 | |
| 	 */
 | |
| 	private final List<GuiMessage> allMessages = Lists.<GuiMessage>newArrayList();
 | |
| 	/**
 | |
| 	 * List of the ChatLines currently drawn
 | |
| 	 */
 | |
| 	private final List<GuiMessage.Line> trimmedMessages = Lists.<GuiMessage.Line>newArrayList();
 | |
| 	private int chatScrollbarPos;
 | |
| 	private boolean newMessageSinceScroll;
 | |
| 	private final List<ChatComponent.DelayedMessageDeletion> messageDeletionQueue = new ArrayList();
 | |
| 
 | |
| 	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(int linesPerPage, int tickCount, boolean focused, int bottomY, ChatComponent.LineConsumer action) {
 | |
| 		int i = this.getLineHeight();
 | |
| 		int j = 0;
 | |
| 
 | |
| 		for (int k = Math.min(this.trimmedMessages.size() - this.chatScrollbarPos, linesPerPage) - 1; k >= 0; k--) {
 | |
| 			int l = k + this.chatScrollbarPos;
 | |
| 			GuiMessage.Line line = (GuiMessage.Line)this.trimmedMessages.get(l);
 | |
| 			if (line != null) {
 | |
| 				int m = tickCount - line.addedTime();
 | |
| 				float f = focused ? 1.0F : (float)getTimeFactor(m);
 | |
| 				if (f > 1.0E-5F) {
 | |
| 					j++;
 | |
| 					int n = bottomY - k * i;
 | |
| 					int o = n - i;
 | |
| 					action.accept(0, o, n, line, k, f);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return j;
 | |
| 	}
 | |
| 
 | |
| 	public void render(GuiGraphics guiGraphics, int tickCount, int mouseX, int mouseY, boolean focused) {
 | |
| 		if (!this.isChatHidden()) {
 | |
| 			int i = this.getLinesPerPage();
 | |
| 			int j = this.trimmedMessages.size();
 | |
| 			if (j > 0) {
 | |
| 				ProfilerFiller profilerFiller = Profiler.get();
 | |
| 				profilerFiller.push("chat");
 | |
| 				float f = (float)this.getScale();
 | |
| 				int k = Mth.ceil(this.getWidth() / f);
 | |
| 				int l = guiGraphics.guiHeight();
 | |
| 				guiGraphics.pose().pushMatrix();
 | |
| 				guiGraphics.pose().scale(f, f);
 | |
| 				guiGraphics.pose().translate(4.0F, 0.0F);
 | |
| 				int m = Mth.floor((l - 40) / f);
 | |
| 				int n = this.getMessageEndIndexAt(this.screenToChatX(mouseX), this.screenToChatY(mouseY));
 | |
| 				float g = this.minecraft.options.chatOpacity().get().floatValue() * 0.9F + 0.1F;
 | |
| 				float h = this.minecraft.options.textBackgroundOpacity().get().floatValue();
 | |
| 				double d = this.minecraft.options.chatLineSpacing().get();
 | |
| 				int o = (int)Math.round(-8.0 * (d + 1.0) + 4.0 * d);
 | |
| 				this.forEachLine(i, tickCount, focused, m, (lx, mx, nx, line, ox, hx) -> {
 | |
| 					guiGraphics.fill(lx - 4, mx, lx + k + 4 + 4, nx, ARGB.color(hx * h, -16777216));
 | |
| 					GuiMessageTag guiMessageTag = line.tag();
 | |
| 					if (guiMessageTag != null) {
 | |
| 						int px = ARGB.color(hx * g, guiMessageTag.indicatorColor());
 | |
| 						guiGraphics.fill(lx - 4, mx, lx - 2, nx, px);
 | |
| 						if (ox == n && guiMessageTag.icon() != null) {
 | |
| 							int qx = this.getTagIconLeft(line);
 | |
| 							int rx = nx + o + 9;
 | |
| 							this.drawTagIcon(guiGraphics, qx, rx, guiMessageTag.icon());
 | |
| 						}
 | |
| 					}
 | |
| 				});
 | |
| 				int p = this.forEachLine(i, tickCount, focused, m, (jx, kx, lx, line, mx, gx) -> {
 | |
| 					int nx = lx + o;
 | |
| 					guiGraphics.drawString(this.minecraft.font, line.content(), jx, nx, ARGB.color(gx * g, -1));
 | |
| 				});
 | |
| 				long q = this.minecraft.getChatListener().queueSize();
 | |
| 				if (q > 0L) {
 | |
| 					int r = (int)(128.0F * g);
 | |
| 					int s = (int)(255.0F * h);
 | |
| 					guiGraphics.pose().pushMatrix();
 | |
| 					guiGraphics.pose().translate(0.0F, m);
 | |
| 					guiGraphics.fill(-2, 0, k + 4, 9, s << 24);
 | |
| 					guiGraphics.drawString(this.minecraft.font, Component.translatable("chat.queue", q), 0, 1, ARGB.color(r, -1));
 | |
| 					guiGraphics.pose().popMatrix();
 | |
| 				}
 | |
| 
 | |
| 				if (focused) {
 | |
| 					int r = this.getLineHeight();
 | |
| 					int s = j * r;
 | |
| 					int t = p * r;
 | |
| 					int u = this.chatScrollbarPos * t / j - m;
 | |
| 					int v = t * t / s;
 | |
| 					if (s != t) {
 | |
| 						int w = u > 0 ? 170 : 96;
 | |
| 						int x = this.newMessageSinceScroll ? 13382451 : 3355562;
 | |
| 						int y = k + 4;
 | |
| 						guiGraphics.fill(y, -u, y + 2, -u - v, ARGB.color(w, x));
 | |
| 						guiGraphics.fill(y + 2, -u, y + 1, -u - v, ARGB.color(w, 13421772));
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				guiGraphics.pose().popMatrix();
 | |
| 				profilerFiller.pop();
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void drawTagIcon(GuiGraphics guiGraphics, int left, int bottom, GuiMessageTag.Icon tagIcon) {
 | |
| 		int i = bottom - tagIcon.height - 1;
 | |
| 		tagIcon.draw(guiGraphics, left, i);
 | |
| 	}
 | |
| 
 | |
| 	private int getTagIconLeft(GuiMessage.Line line) {
 | |
| 		return this.minecraft.font.width(line.content()) + 4;
 | |
| 	}
 | |
| 
 | |
| 	private boolean isChatHidden() {
 | |
| 		return this.minecraft.options.chatVisibility().get() == ChatVisiblity.HIDDEN;
 | |
| 	}
 | |
| 
 | |
| 	private static double getTimeFactor(int counter) {
 | |
| 		double d = counter / 200.0;
 | |
| 		d = 1.0 - d;
 | |
| 		d *= 10.0;
 | |
| 		d = Mth.clamp(d, 0.0, 1.0);
 | |
| 		return d * d;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Clears the chat.
 | |
| 	 * 
 | |
| 	 * @param clearSentMsgHistory Whether to clear the user's sent message history
 | |
| 	 */
 | |
| 	public void clearMessages(boolean clearSentMsgHistory) {
 | |
| 		this.minecraft.getChatListener().clearQueue();
 | |
| 		this.messageDeletionQueue.clear();
 | |
| 		this.trimmedMessages.clear();
 | |
| 		this.allMessages.clear();
 | |
| 		if (clearSentMsgHistory) {
 | |
| 			this.recentChat.clear();
 | |
| 			this.recentChat.addAll(this.minecraft.commandHistory().history());
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void addMessage(Component chatComponent) {
 | |
| 		this.addMessage(chatComponent, null, this.minecraft.isSingleplayer() ? GuiMessageTag.systemSinglePlayer() : GuiMessageTag.system());
 | |
| 	}
 | |
| 
 | |
| 	public void addMessage(Component chatComponent, @Nullable MessageSignature headerSignature, @Nullable GuiMessageTag tag) {
 | |
| 		GuiMessage guiMessage = new GuiMessage(this.minecraft.gui.getGuiTicks(), chatComponent, headerSignature, tag);
 | |
| 		this.logChatMessage(guiMessage);
 | |
| 		this.addMessageToDisplayQueue(guiMessage);
 | |
| 		this.addMessageToQueue(guiMessage);
 | |
| 	}
 | |
| 
 | |
| 	private void logChatMessage(GuiMessage message) {
 | |
| 		String string = message.content().getString().replaceAll("\r", "\\\\r").replaceAll("\n", "\\\\n");
 | |
| 		String string2 = Optionull.map(message.tag(), GuiMessageTag::logTag);
 | |
| 		if (string2 != null) {
 | |
| 			LOGGER.info("[{}] [CHAT] {}", string2, string);
 | |
| 		} else {
 | |
| 			LOGGER.info("[CHAT] {}", string);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void addMessageToDisplayQueue(GuiMessage message) {
 | |
| 		int i = Mth.floor(this.getWidth() / this.getScale());
 | |
| 		GuiMessageTag.Icon icon = message.icon();
 | |
| 		if (icon != null) {
 | |
| 			i -= icon.width + 4 + 2;
 | |
| 		}
 | |
| 
 | |
| 		List<FormattedCharSequence> list = ComponentRenderUtils.wrapComponents(message.content(), i, this.minecraft.font);
 | |
| 		boolean bl = this.isChatFocused();
 | |
| 
 | |
| 		for (int j = 0; j < list.size(); j++) {
 | |
| 			FormattedCharSequence formattedCharSequence = (FormattedCharSequence)list.get(j);
 | |
| 			if (bl && this.chatScrollbarPos > 0) {
 | |
| 				this.newMessageSinceScroll = true;
 | |
| 				this.scrollChat(1);
 | |
| 			}
 | |
| 
 | |
| 			boolean bl2 = j == list.size() - 1;
 | |
| 			this.trimmedMessages.add(0, new GuiMessage.Line(message.addedTime(), formattedCharSequence, message.tag(), bl2));
 | |
| 		}
 | |
| 
 | |
| 		while (this.trimmedMessages.size() > 100) {
 | |
| 			this.trimmedMessages.remove(this.trimmedMessages.size() - 1);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void addMessageToQueue(GuiMessage message) {
 | |
| 		this.allMessages.add(0, message);
 | |
| 
 | |
| 		while (this.allMessages.size() > 100) {
 | |
| 			this.allMessages.remove(this.allMessages.size() - 1);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void processMessageDeletionQueue() {
 | |
| 		int i = this.minecraft.gui.getGuiTicks();
 | |
| 		this.messageDeletionQueue
 | |
| 			.removeIf(
 | |
| 				delayedMessageDeletion -> i >= delayedMessageDeletion.deletableAfter() ? this.deleteMessageOrDelay(delayedMessageDeletion.signature()) == null : false
 | |
| 			);
 | |
| 	}
 | |
| 
 | |
| 	public void deleteMessage(MessageSignature messageSignature) {
 | |
| 		ChatComponent.DelayedMessageDeletion delayedMessageDeletion = this.deleteMessageOrDelay(messageSignature);
 | |
| 		if (delayedMessageDeletion != null) {
 | |
| 			this.messageDeletionQueue.add(delayedMessageDeletion);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	private ChatComponent.DelayedMessageDeletion deleteMessageOrDelay(MessageSignature messageSignature) {
 | |
| 		int i = this.minecraft.gui.getGuiTicks();
 | |
| 		ListIterator<GuiMessage> listIterator = this.allMessages.listIterator();
 | |
| 
 | |
| 		while (listIterator.hasNext()) {
 | |
| 			GuiMessage guiMessage = (GuiMessage)listIterator.next();
 | |
| 			if (messageSignature.equals(guiMessage.signature())) {
 | |
| 				int j = guiMessage.addedTime() + 60;
 | |
| 				if (i >= j) {
 | |
| 					listIterator.set(this.createDeletedMarker(guiMessage));
 | |
| 					this.refreshTrimmedMessages();
 | |
| 					return null;
 | |
| 				}
 | |
| 
 | |
| 				return new ChatComponent.DelayedMessageDeletion(messageSignature, j);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		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 guiMessage : Lists.reverse(this.allMessages)) {
 | |
| 			this.addMessageToDisplayQueue(guiMessage);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public ArrayListDeque<String> getRecentChat() {
 | |
| 		return this.recentChat;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Adds this string to the list of sent messages, for recall using the up/down arrow keys
 | |
| 	 */
 | |
| 	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);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Resets the chat scroll (executed when the GUI is closed, among others)
 | |
| 	 */
 | |
| 	public void resetChatScroll() {
 | |
| 		this.chatScrollbarPos = 0;
 | |
| 		this.newMessageSinceScroll = false;
 | |
| 	}
 | |
| 
 | |
| 	public void scrollChat(int posInc) {
 | |
| 		this.chatScrollbarPos += posInc;
 | |
| 		int i = this.trimmedMessages.size();
 | |
| 		if (this.chatScrollbarPos > i - this.getLinesPerPage()) {
 | |
| 			this.chatScrollbarPos = i - this.getLinesPerPage();
 | |
| 		}
 | |
| 
 | |
| 		if (this.chatScrollbarPos <= 0) {
 | |
| 			this.chatScrollbarPos = 0;
 | |
| 			this.newMessageSinceScroll = false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean handleChatQueueClicked(double mouseX, double mouseY) {
 | |
| 		if (this.isChatFocused() && !this.minecraft.options.hideGui && !this.isChatHidden()) {
 | |
| 			ChatListener chatListener = this.minecraft.getChatListener();
 | |
| 			if (chatListener.queueSize() == 0L) {
 | |
| 				return false;
 | |
| 			} else {
 | |
| 				double d = mouseX - 2.0;
 | |
| 				double e = this.minecraft.getWindow().getGuiScaledHeight() - mouseY - 40.0;
 | |
| 				if (d <= Mth.floor(this.getWidth() / this.getScale()) && e < 0.0 && e > Mth.floor(-9.0 * this.getScale())) {
 | |
| 					chatListener.acceptNextDelayedMessage();
 | |
| 					return true;
 | |
| 				} else {
 | |
| 					return false;
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			return false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	public Style getClickedComponentStyleAt(double mouseX, double mouseY) {
 | |
| 		double d = this.screenToChatX(mouseX);
 | |
| 		double e = this.screenToChatY(mouseY);
 | |
| 		int i = this.getMessageLineIndexAt(d, e);
 | |
| 		if (i >= 0 && i < this.trimmedMessages.size()) {
 | |
| 			GuiMessage.Line line = (GuiMessage.Line)this.trimmedMessages.get(i);
 | |
| 			return this.minecraft.font.getSplitter().componentStyleAtWidth(line.content(), Mth.floor(d));
 | |
| 		} else {
 | |
| 			return null;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	public GuiMessageTag getMessageTagAt(double mouseX, double mouseY) {
 | |
| 		double d = this.screenToChatX(mouseX);
 | |
| 		double e = this.screenToChatY(mouseY);
 | |
| 		int i = this.getMessageEndIndexAt(d, e);
 | |
| 		if (i >= 0 && i < this.trimmedMessages.size()) {
 | |
| 			GuiMessage.Line line = (GuiMessage.Line)this.trimmedMessages.get(i);
 | |
| 			GuiMessageTag guiMessageTag = line.tag();
 | |
| 			if (guiMessageTag != null && this.hasSelectedMessageTag(d, line, guiMessageTag)) {
 | |
| 				return guiMessageTag;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	private boolean hasSelectedMessageTag(double x, GuiMessage.Line line, GuiMessageTag tag) {
 | |
| 		if (x < 0.0) {
 | |
| 			return true;
 | |
| 		} else {
 | |
| 			GuiMessageTag.Icon icon = tag.icon();
 | |
| 			if (icon == null) {
 | |
| 				return false;
 | |
| 			} else {
 | |
| 				int i = this.getTagIconLeft(line);
 | |
| 				int j = i + icon.width;
 | |
| 				return x >= i && x <= j;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private double screenToChatX(double x) {
 | |
| 		return x / this.getScale() - 4.0;
 | |
| 	}
 | |
| 
 | |
| 	private double screenToChatY(double y) {
 | |
| 		double d = this.minecraft.getWindow().getGuiScaledHeight() - y - 40.0;
 | |
| 		return d / (this.getScale() * this.getLineHeight());
 | |
| 	}
 | |
| 
 | |
| 	private int getMessageEndIndexAt(double mouseX, double mouseY) {
 | |
| 		int i = this.getMessageLineIndexAt(mouseX, mouseY);
 | |
| 		if (i == -1) {
 | |
| 			return -1;
 | |
| 		} else {
 | |
| 			while (i >= 0) {
 | |
| 				if (((GuiMessage.Line)this.trimmedMessages.get(i)).endOfEntry()) {
 | |
| 					return i;
 | |
| 				}
 | |
| 
 | |
| 				i--;
 | |
| 			}
 | |
| 
 | |
| 			return i;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private int getMessageLineIndexAt(double mouseX, double mouseY) {
 | |
| 		if (this.isChatFocused() && !this.isChatHidden()) {
 | |
| 			if (!(mouseX < -4.0) && !(mouseX > Mth.floor(this.getWidth() / this.getScale()))) {
 | |
| 				int i = Math.min(this.getLinesPerPage(), this.trimmedMessages.size());
 | |
| 				if (mouseY >= 0.0 && mouseY < i) {
 | |
| 					int j = Mth.floor(mouseY + this.chatScrollbarPos);
 | |
| 					if (j >= 0 && j < this.trimmedMessages.size()) {
 | |
| 						return j;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return -1;
 | |
| 			} else {
 | |
| 				return -1;
 | |
| 			}
 | |
| 		} else {
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns {@code true} if the chat GUI is open
 | |
| 	 */
 | |
| 	public boolean isChatFocused() {
 | |
| 		return this.minecraft.screen instanceof ChatScreen;
 | |
| 	}
 | |
| 
 | |
| 	public int getWidth() {
 | |
| 		return getWidth(this.minecraft.options.chatWidth().get());
 | |
| 	}
 | |
| 
 | |
| 	public int getHeight() {
 | |
| 		return getHeight(this.isChatFocused() ? this.minecraft.options.chatHeightFocused().get() : this.minecraft.options.chatHeightUnfocused().get());
 | |
| 	}
 | |
| 
 | |
| 	public double getScale() {
 | |
| 		return this.minecraft.options.chatScale().get();
 | |
| 	}
 | |
| 
 | |
| 	public static int getWidth(double width) {
 | |
| 		int i = 320;
 | |
| 		int j = 40;
 | |
| 		return Mth.floor(width * 280.0 + 40.0);
 | |
| 	}
 | |
| 
 | |
| 	public static int getHeight(double height) {
 | |
| 		int i = 180;
 | |
| 		int j = 20;
 | |
| 		return Mth.floor(height * 160.0 + 20.0);
 | |
| 	}
 | |
| 
 | |
| 	public static double defaultUnfocusedPct() {
 | |
| 		int i = 180;
 | |
| 		int j = 20;
 | |
| 		return 70.0 / (getHeight(1.0) - 20);
 | |
| 	}
 | |
| 
 | |
| 	public int getLinesPerPage() {
 | |
| 		return this.getHeight() / this.getLineHeight();
 | |
| 	}
 | |
| 
 | |
| 	private int getLineHeight() {
 | |
| 		return (int)(9.0 * (this.minecraft.options.chatLineSpacing().get() + 1.0));
 | |
| 	}
 | |
| 
 | |
| 	public ChatComponent.State storeState() {
 | |
| 		return new ChatComponent.State(List.copyOf(this.allMessages), List.copyOf(this.recentChat), List.copyOf(this.messageDeletionQueue));
 | |
| 	}
 | |
| 
 | |
| 	public void restoreState(ChatComponent.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();
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	record DelayedMessageDeletion(MessageSignature signature, int deletableAfter) {
 | |
| 	}
 | |
| 
 | |
| 	@FunctionalInterface
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	interface LineConsumer {
 | |
| 		void accept(int i, int j, int k, GuiMessage.Line line, int l, float f);
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	public static class State {
 | |
| 		final List<GuiMessage> messages;
 | |
| 		final List<String> history;
 | |
| 		final List<ChatComponent.DelayedMessageDeletion> delayedMessageDeletions;
 | |
| 
 | |
| 		public State(List<GuiMessage> messages, List<String> history, List<ChatComponent.DelayedMessageDeletion> delayedMessageDeletions) {
 | |
| 			this.messages = messages;
 | |
| 			this.history = history;
 | |
| 			this.delayedMessageDeletions = delayedMessageDeletions;
 | |
| 		}
 | |
| 	}
 | |
| }
 |