ChatComponent.java

net.minecraft.client.gui.components.ChatComponent

信息

  • 全限定名:net.minecraft.client.gui.components.ChatComponent
  • 类型:public class
  • 包:net.minecraft.client.gui.components
  • 源码路径:src/main/java/net/minecraft/client/gui/components/ChatComponent.java
  • 起始行号:L46
  • 职责:

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L47
    • 说明:

      TODO

  • MAX_CHAT_HISTORY

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L48
    • 说明:

      TODO

  • MESSAGE_INDENT

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L49
    • 说明:

      TODO

  • BOTTOM_MARGIN

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L50
    • 说明:

      TODO

  • TOOLTIP_MAX_WIDTH

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L51
    • 说明:

      TODO

  • TIME_BEFORE_MESSAGE_DELETION

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L52
    • 说明:

      TODO

  • DELETED_CHAT_MESSAGE

    • 类型: Component
    • 修饰符: private static final
    • 源码定位: L53
    • 说明:

      TODO

  • MESSAGE_BOTTOM_TO_MESSAGE_TOP

    • 类型: int
    • 修饰符: public static final
    • 源码定位: L54
    • 说明:

      TODO

  • QUEUE_EXPAND_ID

    • 类型: Identifier
    • 修饰符: public static final
    • 源码定位: L55
    • 说明:

      TODO

  • QUEUE_EXPAND_TEXT_STYLE

    • 类型: Style
    • 修饰符: private static final
    • 源码定位: L56
    • 说明:

      TODO

  • GO_TO_RESTRICTIONS_SCREEN

    • 类型: Identifier
    • 修饰符: public static final
    • 源码定位: L59
    • 说明:

      TODO

  • RESTRICTED_CHAT_MESSAGE

    • 类型: Component
    • 修饰符: private static final
    • 源码定位: L60
    • 说明:

      TODO

  • RESTRICTED_CHAT_MESSAGE_WITH_HOVER

    • 类型: Component
    • 修饰符: private static final
    • 源码定位: L64
    • 说明:

      TODO

  • minecraft

    • 类型: Minecraft
    • 修饰符: private final
    • 源码定位: L67
    • 说明:

      TODO

  • recentChat

    • 类型: ArrayListDeque<String>
    • 修饰符: private final
    • 源码定位: L68
    • 说明:

      TODO

  • allMessages

    • 类型: List<GuiMessage>
    • 修饰符: private final
    • 源码定位: L69
    • 说明:

      TODO

  • trimmedMessages

    • 类型: List<GuiMessage.Line>
    • 修饰符: private final
    • 源码定位: L70
    • 说明:

      TODO

  • chatScrollbarPos

    • 类型: int
    • 修饰符: private
    • 源码定位: L71
    • 说明:

      TODO

  • newMessageSinceScroll

    • 类型: boolean
    • 修饰符: private
    • 源码定位: L72
    • 说明:

      TODO

  • latestDraft

    • 类型: ChatComponent.Draft
    • 修饰符: private
    • 源码定位: L73
    • 说明:

      TODO

  • preservedScreen

    • 类型: ChatScreen
    • 修饰符: private
    • 源码定位: L74
    • 说明:

      TODO

  • messageDeletionQueue

    • 类型: List<ChatComponent.DelayedMessageDeletion>
    • 修饰符: private final
    • 源码定位: L75
    • 说明:

      TODO

  • visibleMessageFilter

    • 类型: Predicate<GuiMessage>
    • 修饰符: private
    • 源码定位: L76
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.gui.components.ChatComponent.AlphaCalculator

    • 类型: interface
    • 修饰符: private
    • 源码定位: L482
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.ChatGraphicsAccess

    • 类型: interface
    • 修饰符: public
    • 源码定位: L501
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.ChatMethod

    • 类型: enum
    • 修饰符: public static
    • 源码定位: L514
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.ClickableTextOnlyGraphicsAccess

    • 类型: class
    • 修饰符: private static
    • 源码定位: L542
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.DelayedMessageDeletion

    • 类型: record
    • 修饰符: private
    • 源码定位: L577
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.DisplayMode

    • 类型: enum
    • 修饰符: public static
    • 源码定位: L581
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.Draft

    • 类型: record
    • 修饰符: public
    • 源码定位: L596
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.DrawingBackgroundGraphicsAccess

    • 类型: class
    • 修饰符: private static
    • 源码定位: L600
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.DrawingFocusedGraphicsAccess

    • 类型: class
    • 修饰符: private static
    • 源码定位: L640
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.LineConsumer

    • 类型: interface
    • 修饰符: private
    • 源码定位: L729
    • 说明:

      TODO

  • net.minecraft.client.gui.components.ChatComponent.State

    • 类型: class
    • 修饰符: public static
    • 源码定位: L734
    • 说明:

      TODO

构造器

public ChatComponent(Minecraft minecraft) @ L78

  • 构造器名:ChatComponent
  • 源码定位:L78
  • 修饰符:public

参数:

  • minecraft: Minecraft

说明:

TODO

方法

下面的方法块按源码顺序生成。

public void tick() @ L83

  • 方法名:tick
  • 源码定位:L83
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public void setVisibleMessageFilter(Predicate<GuiMessage> visibleMessageFilter) @ L89

  • 方法名:setVisibleMessageFilter
  • 源码定位:L89
  • 返回类型:void
  • 修饰符:public

参数:

  • visibleMessageFilter: Predicate

说明:

TODO

private int forEachLine(ChatComponent.AlphaCalculator alphaCalculator, ChatComponent.LineConsumer lineConsumer) @ L94

  • 方法名:forEachLine
  • 源码定位:L94
  • 返回类型:int
  • 修饰符:private

参数:

  • alphaCalculator: ChatComponent.AlphaCalculator
  • lineConsumer: ChatComponent.LineConsumer

说明:

TODO

public void extractRenderState(GuiGraphicsExtractor graphics, Font font, int ticks, int mouseX, int mouseY, ChatComponent.DisplayMode displayMode, boolean changeCursorOnInsertions) @ L111

  • 方法名:extractRenderState
  • 源码定位:L111
  • 返回类型:void
  • 修饰符:public

参数:

  • graphics: GuiGraphicsExtractor
  • font: Font
  • ticks: int
  • mouseX: int
  • mouseY: int
  • displayMode: ChatComponent.DisplayMode
  • changeCursorOnInsertions: boolean

说明:

TODO

public void captureClickableText(ActiveTextCollector activeTextCollector, int screenHeight, int ticks, ChatComponent.DisplayMode displayMode) @ L126

  • 方法名:captureClickableText
  • 源码定位:L126
  • 返回类型:void
  • 修饰符:public

参数:

  • activeTextCollector: ActiveTextCollector
  • screenHeight: int
  • ticks: int
  • displayMode: ChatComponent.DisplayMode

说明:

TODO

private void extractRenderState(ChatComponent.ChatGraphicsAccess graphics, int screenHeight, int ticks, ChatComponent.DisplayMode displayMode) @ L130

  • 方法名:extractRenderState
  • 源码定位:L130
  • 返回类型:void
  • 修饰符:private

参数:

  • graphics: ChatComponent.ChatGraphicsAccess
  • screenHeight: int
  • ticks: int
  • displayMode: ChatComponent.DisplayMode

说明:

TODO

public void clearMessages(boolean history) @ L234

  • 方法名:clearMessages
  • 源码定位:L234
  • 返回类型:void
  • 修饰符:public

参数:

  • history: boolean

说明:

TODO

public void addClientSystemMessage(Component message) @ L245

  • 方法名:addClientSystemMessage
  • 源码定位:L245
  • 返回类型:void
  • 修饰符:public

参数:

  • message: Component

说明:

TODO

public void addServerSystemMessage(Component message) @ L249

  • 方法名:addServerSystemMessage
  • 源码定位:L249
  • 返回类型:void
  • 修饰符:public

参数:

  • message: Component

说明:

TODO

public void addPlayerMessage(Component message, MessageSignature signature, GuiMessageTag tag) @ L253

  • 方法名:addPlayerMessage
  • 源码定位:L253
  • 返回类型:void
  • 修饰符:public

参数:

  • message: Component
  • signature: MessageSignature
  • tag: GuiMessageTag

说明:

TODO

private void addMessage(Component contents, MessageSignature signature, GuiMessageSource source, GuiMessageTag tag) @ L257

  • 方法名:addMessage
  • 源码定位:L257
  • 返回类型:void
  • 修饰符:private

参数:

  • contents: Component
  • signature: MessageSignature
  • source: GuiMessageSource
  • tag: GuiMessageTag

说明:

TODO

private void logChatMessage(GuiMessage message) @ L266

  • 方法名:logChatMessage
  • 源码定位:L266
  • 返回类型:void
  • 修饰符:private

参数:

  • message: GuiMessage

说明:

TODO

private void addMessageToDisplayQueue(GuiMessage message) @ L276

  • 方法名:addMessageToDisplayQueue
  • 源码定位:L276
  • 返回类型:void
  • 修饰符:private

参数:

  • message: GuiMessage

说明:

TODO

private void addMessageToQueue(GuiMessage message) @ L297

  • 方法名:addMessageToQueue
  • 源码定位:L297
  • 返回类型:void
  • 修饰符:private

参数:

  • message: GuiMessage

说明:

TODO

private void processMessageDeletionQueue() @ L305

  • 方法名:processMessageDeletionQueue
  • 源码定位:L305
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

public void deleteMessage(MessageSignature signature) @ L310

  • 方法名:deleteMessage
  • 源码定位:L310
  • 返回类型:void
  • 修饰符:public

参数:

  • signature: MessageSignature

说明:

TODO

private ChatComponent.DelayedMessageDeletion deleteMessageOrDelay(MessageSignature signature) @ L317

  • 方法名:deleteMessageOrDelay
  • 源码定位:L317
  • 返回类型:ChatComponent.DelayedMessageDeletion
  • 修饰符:private

参数:

  • signature: MessageSignature

说明:

TODO

private static GuiMessage createDeletedMarker(GuiMessage message) @ L338

  • 方法名:createDeletedMarker
  • 源码定位:L338
  • 返回类型:GuiMessage
  • 修饰符:private static

参数:

  • message: GuiMessage

说明:

TODO

public void rescaleChat() @ L342

  • 方法名:rescaleChat
  • 源码定位:L342
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

private void refreshTrimmedMessages() @ L347

  • 方法名:refreshTrimmedMessages
  • 源码定位:L347
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

public ArrayListDeque<String> getRecentChat() @ L357

  • 方法名:getRecentChat
  • 源码定位:L357
  • 返回类型:ArrayListDeque
  • 修饰符:public

参数:

说明:

TODO

public void addRecentChat(String message) @ L361

  • 方法名:addRecentChat
  • 源码定位:L361
  • 返回类型:void
  • 修饰符:public

参数:

  • message: String

说明:

TODO

public void resetChatScroll() @ L375

  • 方法名:resetChatScroll
  • 源码定位:L375
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public void scrollChat(int dir) @ L380

  • 方法名:scrollChat
  • 源码定位:L380
  • 返回类型:void
  • 修饰符:public

参数:

  • dir: int

说明:

TODO

public boolean isChatFocused() @ L393

  • 方法名:isChatFocused
  • 源码定位:L393
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

private int getWidth() @ L397

  • 方法名:getWidth
  • 源码定位:L397
  • 返回类型:int
  • 修饰符:private

参数:

说明:

TODO

private int getHeight() @ L401

  • 方法名:getHeight
  • 源码定位:L401
  • 返回类型:int
  • 修饰符:private

参数:

说明:

TODO

private double getScale() @ L405

  • 方法名:getScale
  • 源码定位:L405
  • 返回类型:double
  • 修饰符:private

参数:

说明:

TODO

public static int getWidth(double pct) @ L409

  • 方法名:getWidth
  • 源码定位:L409
  • 返回类型:int
  • 修饰符:public static

参数:

  • pct: double

说明:

TODO

public static int getHeight(double pct) @ L415

  • 方法名:getHeight
  • 源码定位:L415
  • 返回类型:int
  • 修饰符:public static

参数:

  • pct: double

说明:

TODO

public static double defaultUnfocusedPct() @ L421

  • 方法名:defaultUnfocusedPct
  • 源码定位:L421
  • 返回类型:double
  • 修饰符:public static

参数:

说明:

TODO

public int getLinesPerPage() @ L427

  • 方法名:getLinesPerPage
  • 源码定位:L427
  • 返回类型:int
  • 修饰符:public

参数:

说明:

TODO

private int getLineHeight() @ L431

  • 方法名:getLineHeight
  • 源码定位:L431
  • 返回类型:int
  • 修饰符:private

参数:

说明:

TODO

public void saveAsDraft(String text) @ L435

  • 方法名:saveAsDraft
  • 源码定位:L435
  • 返回类型:void
  • 修饰符:public

参数:

  • text: String

说明:

TODO

public void discardDraft() @ L440

  • 方法名:discardDraft
  • 源码定位:L440
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public <T extends ChatScreen> T createScreen(ChatComponent.ChatMethod chatMethod, ChatScreen.ChatConstructor<T> chat) @ L444

  • 方法名:createScreen
  • 源码定位:L444
  • 返回类型: T
  • 修饰符:public

参数:

  • chatMethod: ChatComponent.ChatMethod
  • chat: ChatScreen.ChatConstructor

说明:

TODO

public void openScreen(ChatComponent.ChatMethod chatMethod, ChatScreen.ChatConstructor<?> chat) @ L450

  • 方法名:openScreen
  • 源码定位:L450
  • 返回类型:void
  • 修饰符:public

参数:

  • chatMethod: ChatComponent.ChatMethod
  • chat: ChatScreen.ChatConstructor<?>

说明:

TODO

public void preserveCurrentChatScreen() @ L454

  • 方法名:preserveCurrentChatScreen
  • 源码定位:L454
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public ChatScreen restoreChatScreen() @ L460

  • 方法名:restoreChatScreen
  • 源码定位:L460
  • 返回类型:ChatScreen
  • 修饰符:public

参数:

说明:

TODO

public ChatComponent.State storeState() @ L466

  • 方法名:storeState
  • 源码定位:L466
  • 返回类型:ChatComponent.State
  • 修饰符:public

参数:

说明:

TODO

public void restoreState(ChatComponent.State state) @ L470

  • 方法名:restoreState
  • 源码定位:L470
  • 返回类型:void
  • 修饰符:public

参数:

  • state: ChatComponent.State

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
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")));
    public static final Identifier GO_TO_RESTRICTIONS_SCREEN = Identifier.withDefaultNamespace("internal/go_to_restrictions_screen");
    private static final Component RESTRICTED_CHAT_MESSAGE = Component.translatable("chat_screen.restricted")
        .withStyle(
            Style.EMPTY.withColor(ChatFormatting.RED).withUnderlined(true).withClickEvent(new ClickEvent.Custom(GO_TO_RESTRICTIONS_SCREEN, Optional.empty()))
        );
    private static final Component RESTRICTED_CHAT_MESSAGE_WITH_HOVER = ComponentUtils.mergeStyles(
        RESTRICTED_CHAT_MESSAGE, Style.EMPTY.withHoverEvent(new HoverEvent.ShowText(Component.translatable("chat_screen.restricted")))
    );
    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 ChatComponent.@Nullable Draft latestDraft;
    private @Nullable ChatScreen preservedScreen;
    private final List<ChatComponent.DelayedMessageDeletion> messageDeletionQueue = new ArrayList<>();
    private Predicate<GuiMessage> visibleMessageFilter = var0 -> true;
 
    public ChatComponent(Minecraft minecraft) {
        this.minecraft = minecraft;
        this.recentChat.addAll(minecraft.commandHistory().history());
    }
 
    public void tick() {
        if (!this.messageDeletionQueue.isEmpty()) {
            this.processMessageDeletionQueue();
        }
    }
 
    public void setVisibleMessageFilter(Predicate<GuiMessage> visibleMessageFilter) {
        this.visibleMessageFilter = visibleMessageFilter;
        this.refreshTrimmedMessages();
    }
 
    private int forEachLine(ChatComponent.AlphaCalculator alphaCalculator, ChatComponent.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) {
                count++;
                lineConsumer.accept(message, i, alpha);
            }
        }
 
        return count;
    }
 
    public void extractRenderState(
        GuiGraphicsExtractor graphics, Font font, int ticks, int mouseX, int mouseY, ChatComponent.DisplayMode displayMode, boolean changeCursorOnInsertions
    ) {
        graphics.pose().pushMatrix();
        this.extractRenderState(
            (ChatComponent.ChatGraphicsAccess)(displayMode.foreground
                ? new ChatComponent.DrawingFocusedGraphicsAccess(graphics, font, mouseX, mouseY, changeCursorOnInsertions)
                : new ChatComponent.DrawingBackgroundGraphicsAccess(graphics)),
            graphics.guiHeight(),
            ticks,
            displayMode
        );
        graphics.pose().popMatrix();
    }
 
    public void captureClickableText(ActiveTextCollector activeTextCollector, int screenHeight, int ticks, ChatComponent.DisplayMode displayMode) {
        this.extractRenderState(new ChatComponent.ClickableTextOnlyGraphicsAccess(activeTextCollector), screenHeight, ticks, displayMode);
    }
 
    private void extractRenderState(ChatComponent.ChatGraphicsAccess graphics, int screenHeight, int ticks, ChatComponent.DisplayMode displayMode) {
        boolean isForeground = displayMode.foreground;
        boolean isRestricted = displayMode.showRestrictedPrompt;
        int total = this.trimmedMessages.size();
        if (total > 0 || isRestricted) {
            ProfilerFiller profiler = Profiler.get();
            profiler.push("chat");
            float scale = (float)this.getScale();
            int maxWidth = Mth.ceil(this.getWidth() / scale);
            final int chatBottom = Mth.floor((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 = 9;
            int messageBottomToMessageTop = 8;
            double chatLineSpacing = this.minecraft.options.chatLineSpacing().get();
            final int entryHeight = (int)(messageHeight * (chatLineSpacing + 1.0));
            final int entryBottomToMessageY = (int)Math.round(8.0 * (chatLineSpacing + 1.0) - 4.0 * chatLineSpacing);
            long queueSize = this.minecraft.getChatListener().queueSize();
            ChatComponent.AlphaCalculator alphaCalculator = isForeground
                ? ChatComponent.AlphaCalculator.FULLY_VISIBLE
                : ChatComponent.AlphaCalculator.timeBased(ticks);
            graphics.updatePose(pose -> {
                pose.scale(scale, scale);
                pose.translate(4.0F, 0.0F);
            });
            int count = this.forEachLine(alphaCalculator, (var5x, lineIndex, alphax) -> {
                int entryBottom = chatBottom - lineIndex * entryHeight;
                int entryTop = entryBottom - entryHeight;
                graphics.fill(-4, entryTop, maxWidth + 4 + 4, entryBottom, ARGB.black(alphax * backgroundOpacity));
            });
            int lineAboveMessagesY = chatBottom - (count + 1) * entryHeight;
            if (queueSize > 0L) {
                graphics.fill(-2, chatBottom, maxWidth + 4, chatBottom + messageHeight, ARGB.black(backgroundOpacity));
            }
 
            if (isRestricted) {
                graphics.fill(-2, lineAboveMessagesY, maxWidth + 4 + 4, lineAboveMessagesY + entryHeight, ARGB.black(backgroundOpacity));
            }
 
            this.forEachLine(alphaCalculator, new ChatComponent.LineConsumer() {
                boolean hoveredOverCurrentMessage;
 
                {
                    Objects.requireNonNull(ChatComponent.this);
                }
 
                @Override
                public void accept(GuiMessage.Line line, int lineIndex, float alpha) {
                    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;
                    boolean forceIconRendering;
                    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;
                Component queueMessage = Component.translatable("chat.queue", queueSize).setStyle(QUEUE_EXPAND_TEXT_STYLE);
                graphics.handleMessage(queueLineBottom - 8, 0.5F * textOpacity, queueMessage.getVisualOrderText());
            }
 
            if (isRestricted) {
                int restrictedMessageWidth = this.minecraft.font.width(RESTRICTED_CHAT_MESSAGE);
                FormattedCharSequence restrictedMessage = restrictedMessageWidth > maxWidth
                    ? ComponentRenderUtils.clipText(RESTRICTED_CHAT_MESSAGE_WITH_HOVER, this.minecraft.font, maxWidth)
                    : RESTRICTED_CHAT_MESSAGE.getVisualOrderText();
                graphics.handleMessage(lineAboveMessagesY + entryHeight - entryBottomToMessageY - 1, textOpacity, restrictedMessage);
            }
 
            if (total > 0 && isForeground) {
                int chatHeight = count * entryHeight;
                int virtualHeight = total * entryHeight;
                int y = this.chatScrollbarPos * chatHeight / total - chatBottom;
                int height = chatHeight * chatHeight / virtualHeight;
                if (virtualHeight != chatHeight) {
                    int alpha = y > 0 ? 170 : 96;
                    int color = this.newMessageSinceScroll ? 13382451 : 3355562;
                    int scrollBarStartX = maxWidth + 4;
                    graphics.fill(scrollBarStartX, -y, scrollBarStartX + 2, -y - height, ARGB.color(alpha, color));
                    graphics.fill(scrollBarStartX + 2, -y, scrollBarStartX + 1, -y - height, ARGB.color(alpha, 13421772));
                }
            }
 
            profiler.pop();
        }
    }
 
    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 addClientSystemMessage(Component message) {
        this.addMessage(message, null, GuiMessageSource.SYSTEM_CLIENT, GuiMessageTag.systemSinglePlayer());
    }
 
    public void addServerSystemMessage(Component message) {
        this.addMessage(message, null, GuiMessageSource.SYSTEM_SERVER, GuiMessageTag.systemSinglePlayer());
    }
 
    public void addPlayerMessage(Component message, @Nullable MessageSignature signature, @Nullable GuiMessageTag tag) {
        this.addMessage(message, signature, GuiMessageSource.PLAYER, tag);
    }
 
    private void addMessage(Component contents, @Nullable MessageSignature signature, GuiMessageSource source, @Nullable GuiMessageTag tag) {
        GuiMessage message = new GuiMessage(this.minecraft.gui.getGuiTicks(), contents, signature, source, tag);
        if (this.visibleMessageFilter.test(message)) {
            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] {}", logTag, messageString);
        } else {
            LOGGER.info("[CHAT] {}", messageString);
        }
    }
 
    private void addMessageToDisplayQueue(GuiMessage message) {
        int maxWidth = Mth.floor(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, line, 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 -> time >= entry.deletableAfter() ? this.deleteMessageOrDelay(entry.signature()) == null : false);
    }
 
    public void deleteMessage(MessageSignature signature) {
        ChatComponent.DelayedMessageDeletion delayedMessage = this.deleteMessageOrDelay(signature);
        if (delayedMessage != null) {
            this.messageDeletionQueue.add(delayedMessage);
        }
    }
 
    private ChatComponent.@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())) {
                int deletableAfter = message.addedTime() + 60;
                if (time >= deletableAfter) {
                    iterator.set(createDeletedMarker(message));
                    this.refreshTrimmedMessages();
                    return null;
                }
 
                return new ChatComponent.DelayedMessageDeletion(signature, deletableAfter);
            }
        }
 
        return null;
    }
 
    private static GuiMessage createDeletedMarker(GuiMessage message) {
        return new GuiMessage(message.addedTime(), DELETED_CHAT_MESSAGE, null, GuiMessageSource.SYSTEM_SERVER, GuiMessageTag.system());
    }
 
    public void rescaleChat() {
        this.resetChatScroll();
        this.refreshTrimmedMessages();
    }
 
    private void refreshTrimmedMessages() {
        this.trimmedMessages.clear();
 
        for (GuiMessage message : Lists.reverse(this.allMessages)) {
            if (this.visibleMessageFilter.test(message)) {
                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 getWidth(this.minecraft.options.chatWidth().get());
    }
 
    private int getHeight() {
        return 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 / (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 void saveAsDraft(String text) {
        boolean isCommand = text.startsWith("/");
        this.latestDraft = new ChatComponent.Draft(text, isCommand ? ChatComponent.ChatMethod.COMMAND : ChatComponent.ChatMethod.MESSAGE);
    }
 
    public void discardDraft() {
        this.latestDraft = null;
    }
 
    public <T extends ChatScreen> T createScreen(ChatComponent.ChatMethod chatMethod, ChatScreen.ChatConstructor<T> chat) {
        return this.latestDraft != null && chatMethod.isDraftRestorable(this.latestDraft)
            ? chat.create(this.latestDraft.text(), true)
            : chat.create(chatMethod.prefix(), false);
    }
 
    public void openScreen(ChatComponent.ChatMethod chatMethod, ChatScreen.ChatConstructor<?> chat) {
        this.minecraft.setScreen(this.createScreen(chatMethod, chat));
    }
 
    public void preserveCurrentChatScreen() {
        if (this.minecraft.screen instanceof ChatScreen chatScreen) {
            this.preservedScreen = chatScreen;
        }
    }
 
    public @Nullable ChatScreen restoreChatScreen() {
        ChatScreen restoredScreen = this.preservedScreen;
        this.preservedScreen = null;
        return restoredScreen;
    }
 
    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();
    }
 
    @FunctionalInterface
    @OnlyIn(Dist.CLIENT)
    private interface AlphaCalculator {
        ChatComponent.AlphaCalculator FULLY_VISIBLE = var0 -> 1.0F;
 
        static ChatComponent.AlphaCalculator timeBased(int currentTickTime) {
            return message -> {
                int tickDelta = currentTickTime - message.addedTime();
                double t = tickDelta / 200.0;
                t = 1.0 - t;
                t *= 10.0;
                t = Mth.clamp(t, 0.0, 1.0);
                t *= t;
                return (float)t;
            };
        }
 
        float calculate(GuiMessage.Line message);
    }
 
    @OnlyIn(Dist.CLIENT)
    public interface ChatGraphicsAccess {
        void updatePose(final Consumer<Matrix3x2f> updater);
 
        void fill(int x0, int y0, int x1, int y1, int color);
 
        boolean handleMessage(int textTop, float opacity, FormattedCharSequence message);
 
        void handleTag(int x0, int y0, int x1, int y1, float opacity, GuiMessageTag tag);
 
        void handleTagIcon(int left, int bottom, boolean forceVisible, GuiMessageTag tag, GuiMessageTag.Icon icon);
    }
 
    @OnlyIn(Dist.CLIENT)
    public static enum ChatMethod {
        MESSAGE("") {
            @Override
            public boolean isDraftRestorable(ChatComponent.Draft draft) {
                return true;
            }
        },
        COMMAND("/") {
            @Override
            public boolean isDraftRestorable(ChatComponent.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(ChatComponent.Draft draft);
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class ClickableTextOnlyGraphicsAccess implements ChatComponent.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(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) {
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private record DelayedMessageDeletion(MessageSignature signature, int deletableAfter) {
    }
 
    @OnlyIn(Dist.CLIENT)
    public static enum DisplayMode {
        BACKGROUND(false, false),
        FOREGROUND(true, false),
        FOREGROUND_RESTRICTED(true, true);
 
        public final boolean foreground;
        public final boolean showRestrictedPrompt;
 
        private DisplayMode(boolean foreground, boolean showRestrictedPrompt) {
            this.foreground = foreground;
            this.showRestrictedPrompt = showRestrictedPrompt;
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    public record Draft(String text, ChatComponent.ChatMethod chatMethod) {
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class DrawingBackgroundGraphicsAccess implements ChatComponent.ChatGraphicsAccess {
        private final GuiGraphicsExtractor graphics;
        private final ActiveTextCollector textRenderer;
        private ActiveTextCollector.Parameters parameters;
 
        public DrawingBackgroundGraphicsAccess(GuiGraphicsExtractor graphics) {
            this.graphics = graphics;
            this.textRenderer = graphics.textRenderer(GuiGraphicsExtractor.HoveredTextEffects.NONE, null);
            this.parameters = this.textRenderer.defaultParameters();
        }
 
        @Override
        public void updatePose(Consumer<Matrix3x2f> updater) {
            updater.accept(this.graphics.pose());
            this.parameters = this.parameters.withPose(new Matrix3x2f(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) {
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class DrawingFocusedGraphicsAccess implements ChatComponent.ChatGraphicsAccess, Consumer<Style> {
        private final GuiGraphicsExtractor 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;
        private final boolean changeCursorOnInsertions;
 
        public DrawingFocusedGraphicsAccess(GuiGraphicsExtractor graphics, Font font, int mouseX, int mouseY, boolean changeCursorOnInsertions) {
            this.graphics = graphics;
            this.font = font;
            this.textRenderer = graphics.textRenderer(GuiGraphicsExtractor.HoveredTextEffects.TOOLTIP_AND_CURSOR, this);
            this.globalMouseX = mouseX;
            this.globalMouseY = mouseY;
            this.changeCursorOnInsertions = changeCursorOnInsertions;
            this.parameters = this.textRenderer.defaultParameters();
            this.updateLocalMousePos();
        }
 
        private void updateLocalMousePos() {
            this.graphics.pose().invert(new Matrix3x2f()).transformPosition(this.globalMouseX, this.globalMouseY, this.localMousePos);
        }
 
        @Override
        public void updatePose(Consumer<Matrix3x2f> updater) {
            updater.accept(this.graphics.pose());
            this.parameters = this.parameters.withPose(new Matrix3x2f(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);
        }
 
        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);
            if (this.changeCursorOnInsertions && this.hoveredStyle != null && this.hoveredStyle.getInsertion() != null) {
                this.graphics.requestCursor(CursorTypes.POINTING_HAND);
            }
 
            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.extractRenderState(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);
            }
        }
    }
 
    @FunctionalInterface
    @OnlyIn(Dist.CLIENT)
    private interface LineConsumer {
        void accept(GuiMessage.Line line, int lineIndex, float alpha);
    }
 
    @OnlyIn(Dist.CLIENT)
    public static class State {
        private final List<GuiMessage> messages;
        private final List<String> history;
        private 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;
        }
    }
}

引用的其他类

  • Optionull

    • 引用位置: 方法调用
    • 关联成员: Optionull.map()
  • Minecraft

    • 引用位置: 参数/字段
  • ActiveTextCollector

    • 引用位置: 参数/方法调用
    • 关联成员: ActiveTextCollector.isPointInRectangle()
  • Font

    • 引用位置: 参数
  • GuiGraphicsExtractor

    • 引用位置: 参数
  • ComponentRenderUtils

    • 引用位置: 方法调用
    • 关联成员: ComponentRenderUtils.clipText()
  • ChatScreen

    • 引用位置: 参数/字段/返回值
  • GuiMessage

    • 引用位置: 参数/字段/方法调用/构造调用/返回值
    • 关联成员: GuiMessage(), GuiMessage.Line(), Line()
  • GuiMessageSource

    • 引用位置: 参数
  • GuiMessageTag

    • 引用位置: 参数/方法调用
    • 关联成员: GuiMessageTag.system(), GuiMessageTag.systemSinglePlayer()
  • ClickEvent

    • 引用位置: 方法调用/构造调用
    • 关联成员: ClickEvent.Custom(), Custom()
  • Component

    • 引用位置: 参数/字段/方法调用
    • 关联成员: Component.translatable()
  • ComponentUtils

    • 引用位置: 方法调用
    • 关联成员: ComponentUtils.mergeStyles()
  • HoverEvent

    • 引用位置: 方法调用/构造调用
    • 关联成员: HoverEvent.ShowText(), ShowText()
  • MessageSignature

    • 引用位置: 参数
  • Style

    • 引用位置: 字段
  • Identifier

    • 引用位置: 字段/方法调用
    • 关联成员: Identifier.withDefaultNamespace()
  • ARGB

    • 引用位置: 方法调用
    • 关联成员: ARGB.black(), ARGB.color()
  • ArrayListDeque

    • 引用位置: 字段/返回值
  • Mth

    • 引用位置: 方法调用
    • 关联成员: Mth.ceil(), Mth.clamp(), Mth.floor()
  • Profiler

    • 引用位置: 方法调用
    • 关联成员: Profiler.get()