From b07dd0b06a6377a41dd496198e4254300079df03 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 2 May 2026 00:49:13 +0100 Subject: [PATCH 1/3] Begin work on modern command suggestions - Took me longer to compact patches than it is to implement --- .../minecraft/client/gui/GuiChat.java.patch | 173 ++++++++++++++ .../chat/suggestion/SuggestionList.java | 214 ++++++++++++++++++ .../chat/suggestion/SuggestionUpdater.java | 48 ++++ 3 files changed, 435 insertions(+) create mode 100644 patches/minecraft/net/minecraft/client/gui/GuiChat.java.patch create mode 100644 src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionList.java create mode 100644 src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java diff --git a/patches/minecraft/net/minecraft/client/gui/GuiChat.java.patch b/patches/minecraft/net/minecraft/client/gui/GuiChat.java.patch new file mode 100644 index 000000000..456059dcd --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiChat.java.patch @@ -0,0 +1,173 @@ +--- before/net/minecraft/client/gui/GuiChat.java ++++ after/net/minecraft/client/gui/GuiChat.java +@@ -2,6 +2,9 @@ + + import java.io.IOException; + import javax.annotation.Nullable; ++ ++import com.cleanroommc.client.chat.suggestion.SuggestionList; ++import com.cleanroommc.client.chat.suggestion.SuggestionUpdater; + import net.minecraft.client.Minecraft; + import net.minecraft.util.ITabCompleter; + import net.minecraft.util.TabCompleter; +@@ -26,6 +29,7 @@ + private TabCompleter tabCompleter; + protected GuiTextField inputField; + private String defaultInputFieldText = ""; ++ private SuggestionList suggestionList; + + public GuiChat() + { +@@ -48,6 +52,13 @@ + this.inputField.setText(this.defaultInputFieldText); + this.inputField.setCanLoseFocus(false); + this.tabCompleter = new GuiChat.ChatTabCompleter(this.inputField); ++ this.suggestionList = new SuggestionList(); ++ SuggestionUpdater updater = new SuggestionUpdater(this.suggestionList); ++ this.inputField.setGuiResponder(updater); ++ if (this.defaultInputFieldText != null) ++ { ++ updater.setEntryValue(0, this.defaultInputFieldText); ++ } + } + + @Override +@@ -70,6 +81,15 @@ + + if (keyCode == 15) + { ++ if (this.suggestionList.isVisible()) ++ { ++ String selected = this.suggestionList.getSelected(); ++ if (selected == null) { ++ selected = this.suggestionList.getFirst(); ++ } ++ this.suggestionList.applySuggestion(this.inputField, selected); ++ return; ++ } + this.tabCompleter.complete(); + } + else +@@ -79,10 +99,20 @@ + + if (keyCode == 1) + { ++ if (this.suggestionList.isVisible()) ++ { ++ this.suggestionList.hide(); ++ } + this.mc.displayGuiScreen(null); + } + else if (keyCode == 28 || keyCode == 156) + { ++ String selected = this.suggestionList.getSelected(); ++ if (selected != null) ++ { ++ this.suggestionList.applySuggestion(this.inputField, selected); ++ return; ++ } + String s = this.inputField.getText().trim(); + + if (!s.isEmpty()) +@@ -94,10 +124,20 @@ + } + else if (keyCode == 200) + { ++ if (this.suggestionList.isVisible()) ++ { ++ this.suggestionList.selectPrev(); ++ return; ++ } + this.getSentHistory(-1); + } + else if (keyCode == 208) + { ++ if (this.suggestionList.isVisible()) ++ { ++ this.suggestionList.selectNext(); ++ return; ++ } + this.getSentHistory(1); + } + else if (keyCode == 201) +@@ -132,6 +172,13 @@ + i = -1; + } + ++ int mouseX = Mouse.getEventX() * this.width / this.mc.displayWidth; ++ int mouseY = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1; ++ if (this.suggestionList.isMouseOver(this.inputField.x, this.inputField.y, this.inputField.width, mouseX, mouseY)) ++ { ++ this.suggestionList.scroll(i); ++ return; ++ } + if (!isShiftKeyDown()) + { + i *= 7; +@@ -146,6 +193,12 @@ + { + if (mouseButton == 0) + { ++ String clicked = this.suggestionList.mouseClicked(this.inputField.x, this.inputField.y, this.inputField.width, mouseX, mouseY); ++ if (clicked != null) ++ { ++ this.suggestionList.applySuggestion(this.inputField, clicked); ++ return; ++ } + ITextComponent itextcomponent = this.mc.ingameGUI.getChatGUI().getChatComponent(Mouse.getX(), Mouse.getY()); + + if (itextcomponent != null && this.handleComponentClick(itextcomponent)) +@@ -201,6 +254,7 @@ + public void drawScreen(int mouseX, int mouseY, float partialTicks) + { + drawRect(2, this.height - 14, this.width - 2, this.height - 2, Integer.MIN_VALUE); ++ this.suggestionList.drawGhostText(this.inputField, this.fontRenderer); + this.inputField.drawTextBox(); + ITextComponent itextcomponent = this.mc.ingameGUI.getChatGUI().getChatComponent(Mouse.getX(), Mouse.getY()); + +@@ -209,6 +263,7 @@ + this.handleComponentHover(itextcomponent, mouseX, mouseY); + } + ++ this.suggestionList.render(this.inputField.x, this.inputField.y, this.inputField.width, mouseX, mouseY); + super.drawScreen(mouseX, mouseY, partialTicks); + } + +@@ -222,6 +277,7 @@ + public void setCompletions(String... newCompletions) + { + this.tabCompleter.setCompletions(newCompletions); ++ this.suggestionList.updateSuggestions(newCompletions); + } + + @SideOnly(Side.CLIENT) +@@ -232,29 +288,6 @@ + public ChatTabCompleter(GuiTextField p_i46749_1_) + { + super(p_i46749_1_, false); +- } +- +- @Override +- public void complete() +- { +- super.complete(); +- +- if (this.completions.size() > 1) +- { +- StringBuilder stringbuilder = new StringBuilder(); +- +- for (String s : this.completions) +- { +- if (stringbuilder.length() > 0) +- { +- stringbuilder.append(", "); +- } +- +- stringbuilder.append(s); +- } +- +- this.client.ingameGUI.getChatGUI().printChatMessageWithOptionalDeletion(new TextComponentString(stringbuilder.toString()), 1); +- } + } + + @Nullable diff --git a/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionList.java b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionList.java new file mode 100644 index 000000000..47144a657 --- /dev/null +++ b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionList.java @@ -0,0 +1,214 @@ +package com.cleanroommc.client.chat.suggestion; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiTextField; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.client.ClientCommandHandler; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +@SideOnly(Side.CLIENT) +public class SuggestionList { + + private static final int MAX_VISIBLE = 10; + private static final int ENTRY_HEIGHT = 12; + private static final int PADDING_X = 3; + + private List suggestions = Collections.emptyList(); + private int selectedIndex = -1; + private int scrollOffset = 0; + private int cachedWidth = 0; + + public void setSuggestions(List newSuggestions) { + this.suggestions = newSuggestions; + this.selectedIndex = -1; + this.scrollOffset = 0; + this.cachedWidth = newSuggestions.stream() + .mapToInt(Minecraft.getMinecraft().fontRenderer::getStringWidth) + .max() + .orElse(0) + PADDING_X * 2; + } + + public boolean isInvisible() { + return this.suggestions.isEmpty(); + } + + public boolean isVisible() { + return !this.suggestions.isEmpty(); + } + + public void hide() { + this.suggestions = Collections.emptyList(); + this.selectedIndex = -1; + this.scrollOffset = 0; + this.cachedWidth = 0; + } + + public void selectNext() { + if (this.suggestions.isEmpty()) { + return; + } + this.selectedIndex = this.selectedIndex < this.suggestions.size() - 1 ? this.selectedIndex + 1 : -1; + this.clampScroll(); + } + + public void selectPrev() { + if (this.isInvisible()) { + return; + } + this.selectedIndex = this.selectedIndex == -1 ? this.suggestions.size() - 1 : Math.max(-1, this.selectedIndex - 1); + this.clampScroll(); + } + + private void clampScroll() { + if (this.selectedIndex == -1) { + this.scrollOffset = 0; + return; + } + if (this.selectedIndex < this.scrollOffset) { + this.scrollOffset = this.selectedIndex; + } else if (this.selectedIndex >= this.scrollOffset + MAX_VISIBLE) { + this.scrollOffset = this.selectedIndex - MAX_VISIBLE + 1; + } + } + + public String getSelected() { + if (this.isVisible() && this.selectedIndex >= 0 && this.selectedIndex < this.suggestions.size()) { + return this.suggestions.get(this.selectedIndex); + } + return null; + } + + public String getFirst() { + return this.suggestions.isEmpty() ? null : this.suggestions.getFirst(); + } + + public void render(int inputX, int inputY, int inputWidth, int mouseX, int mouseY) { + if (this.isInvisible()) { + return; + } + Minecraft mc = Minecraft.getMinecraft(); + int visibleCount = Math.min(MAX_VISIBLE, this.suggestions.size()); + int listHeight = visibleCount * ENTRY_HEIGHT; + int listY = inputY - listHeight - 2; // bottom flush with the top of the input background rect + int listWidth = Math.min(cachedWidth, inputWidth); + Gui.drawRect(inputX, listY, inputX + listWidth, listY + listHeight, 0xC0000000); + for (int i = 0; i < visibleCount; i++) { + int idx = i + this.scrollOffset; + String text = this.suggestions.get(idx); + int entryY = listY + i * ENTRY_HEIGHT; + boolean selected = idx == selectedIndex; + boolean hovered = mouseX >= inputX && mouseX < inputX + listWidth && mouseY >= entryY && mouseY < entryY + ENTRY_HEIGHT; + mc.fontRenderer.drawStringWithShadow(text, inputX + PADDING_X, entryY + 2, (selected || hovered) ? 0xFFFF55 : 0xFFFFFF); + } + if (this.suggestions.size() > MAX_VISIBLE) { + int barX = inputX + listWidth; + int thumbHeight = Math.max(ENTRY_HEIGHT, listHeight * MAX_VISIBLE / this.suggestions.size()); + int maxScroll = this.suggestions.size() - MAX_VISIBLE; + int thumbY = listY + this.scrollOffset * (listHeight - thumbHeight) / maxScroll; + Gui.drawRect(barX, listY, barX + 2, listY + listHeight, 0xFF333333); + Gui.drawRect(barX, thumbY, barX + 2, thumbY + thumbHeight, 0xFFAAAAAA); + } +// int separatorY = listY + listHeight - 1; +// for (int x = inputX; x < inputX + listWidth; x++) { +// Gui.drawRect(x, separatorY, x + 1, separatorY + 1, (x - inputX) % 2 == 0 ? 0xFFFFFFFF : 0xFF000000); +// } + } + + public void updateSuggestions(String... serverCompletions) { + List list = new ArrayList<>(); + String[] clientComplete = ClientCommandHandler.instance.latestAutoComplete; + if (clientComplete != null) { + for (String s : clientComplete) { + if (!s.isEmpty()) list.add(s); + } + } + for (String s : serverCompletions) { + if (!s.isEmpty() && !list.contains(s)) list.add(s); + } + this.setSuggestions(list); + } + + public void applySuggestion(GuiTextField inputField, String suggestion) { + int wordStart = inputField.getNthWordFromPosWS(-1, inputField.getCursorPosition(), false); + inputField.deleteFromCursor(wordStart - inputField.getCursorPosition()); + inputField.writeText(TextFormatting.getTextWithoutFormattingCodes(suggestion)); + this.hide(); + } + + public void drawGhostText(GuiTextField inputField, FontRenderer fontRenderer) { + if (this.isInvisible()) { + return; + } + String suggestion = this.getSelected(); + if (suggestion == null) { + suggestion = this.getFirst(); + } + int cursorPos = inputField.getCursorPosition(); + int wordStart = inputField.getNthWordFromPosWS(-1, cursorPos, false); + String typedWord = inputField.getText().substring(wordStart, cursorPos); + if (!suggestion.toLowerCase(Locale.ROOT).startsWith(typedWord.toLowerCase(Locale.ROOT))) { + return; + } + String suffix = suggestion.substring(typedWord.length()); + if (suffix.isEmpty()) { + return; + } + int ghostX = inputField.x + fontRenderer.getStringWidth(inputField.getText().substring(0, cursorPos)); + int fieldRight = inputField.x + inputField.width; + if (ghostX >= fieldRight) { + return; + } + while (suffix.length() > 1 && ghostX + fontRenderer.getStringWidth(suffix) > fieldRight) { + suffix = suffix.substring(0, suffix.length() - 1); + } + fontRenderer.drawString(suffix, ghostX, inputField.y, 0xFF808080); + } + + public boolean isMouseOver(int inputX, int inputY, int inputWidth, int mouseX, int mouseY) { + if (!this.isVisible()) { + return false; + } + int visibleCount = Math.min(MAX_VISIBLE, this.suggestions.size()); + int listHeight = visibleCount * ENTRY_HEIGHT; + int listY = inputY - listHeight - 2; + int listWidth = Math.min(this.cachedWidth, inputWidth); + return mouseX >= inputX && mouseX < inputX + listWidth && mouseY >= listY && mouseY < listY + listHeight; + } + + public void scroll(int wheelDelta) { + if (!this.isVisible() || this.suggestions.size() <= MAX_VISIBLE) { + return; + } + int maxOffset = this.suggestions.size() - MAX_VISIBLE; + this.scrollOffset = Math.clamp(this.scrollOffset + (wheelDelta > 0 ? -1 : 1), 0, maxOffset); + } + + public String mouseClicked(int inputX, int inputY, int inputWidth, int mouseX, int mouseY) { + if (!this.isVisible()) { + return null; + } + int visibleCount = Math.min(MAX_VISIBLE, this.suggestions.size()); + int listHeight = visibleCount * ENTRY_HEIGHT; + int listY = inputY - listHeight - 2; + int listWidth = Math.min(this.cachedWidth, inputWidth); + if (mouseX < inputX || mouseX >= inputX + listWidth || mouseY < listY || mouseY >= listY + listHeight) { + return null; + } + for (int i = 0; i < visibleCount; i++) { + int entryY = listY + i * ENTRY_HEIGHT; + if (mouseY >= entryY && mouseY < entryY + ENTRY_HEIGHT) { + return this.suggestions.get(i + scrollOffset); + } + } + return null; + } + +} diff --git a/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java new file mode 100644 index 000000000..bb98288cc --- /dev/null +++ b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java @@ -0,0 +1,48 @@ +package com.cleanroommc.client.chat.suggestion; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiPageButtonList; +import net.minecraft.network.play.client.CPacketTabComplete; +import net.minecraftforge.client.ClientCommandHandler; + +/** + * GuiResponder for the {@link net.minecraft.client.gui.GuiTextField} in {@link net.minecraft.client.gui.GuiChat} + * so that text changes fires a server tab-complete request, populating the {@link SuggestionList} + */ +public class SuggestionUpdater implements GuiPageButtonList.GuiResponder { + + private final SuggestionList suggestionList; + + private String lastText = ""; + + public SuggestionUpdater(SuggestionList suggestionList) { + this.suggestionList = suggestionList; + } + + @Override + public void setEntryValue(int id, boolean value) { } + + @Override + public void setEntryValue(int id, float value) { } + + @Override + public void setEntryValue(int id, String value) { + if (value.equals(this.lastText)) { + return; + } + this.lastText = value; + if (value.isEmpty()) { + this.suggestionList.hide(); + return; + } + Minecraft mc = Minecraft.getMinecraft(); + if (mc.player == null) { + return; + } + // Client-side commands + ClientCommandHandler.instance.autoComplete(value); + // Server-side completions + mc.player.connection.sendPacket(new CPacketTabComplete(value, null, false)); + } + +} From 4d8641895e4396e0dbf1211d76ecebf4cbce751e Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 2 May 2026 08:41:45 +0100 Subject: [PATCH 2/3] Take TabCompleter's target pos into account --- .../client/chat/suggestion/SuggestionUpdater.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java index bb98288cc..53c46c35e 100644 --- a/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java +++ b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java @@ -3,6 +3,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiPageButtonList; import net.minecraft.network.play.client.CPacketTabComplete; +import net.minecraft.util.TabCompleter; import net.minecraftforge.client.ClientCommandHandler; /** @@ -12,11 +13,13 @@ public class SuggestionUpdater implements GuiPageButtonList.GuiResponder { private final SuggestionList suggestionList; + private final TabCompleter tabCompleter; private String lastText = ""; - public SuggestionUpdater(SuggestionList suggestionList) { + public SuggestionUpdater(SuggestionList suggestionList, TabCompleter tabCompleter) { this.suggestionList = suggestionList; + this.tabCompleter = tabCompleter; } @Override @@ -42,7 +45,7 @@ public void setEntryValue(int id, String value) { // Client-side commands ClientCommandHandler.instance.autoComplete(value); // Server-side completions - mc.player.connection.sendPacket(new CPacketTabComplete(value, null, false)); + mc.player.connection.sendPacket(new CPacketTabComplete(value, this.tabCompleter.getTargetBlockPos(), false)); } } From 62e5634aa09882c93155bd1a548761028c719f9b Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 2 May 2026 09:42:31 +0100 Subject: [PATCH 3/3] Red = incorrect, green = correct --- .../minecraft/client/gui/GuiChat.java.patch | 14 ++--- .../client/gui/GuiTextField.java.patch | 14 ++++- .../chat/suggestion/SuggestionList.java | 53 ++++++++++++++++--- .../chat/suggestion/SuggestionUpdater.java | 4 ++ 4 files changed, 71 insertions(+), 14 deletions(-) diff --git a/patches/minecraft/net/minecraft/client/gui/GuiChat.java.patch b/patches/minecraft/net/minecraft/client/gui/GuiChat.java.patch index 456059dcd..948ae126a 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiChat.java.patch +++ b/patches/minecraft/net/minecraft/client/gui/GuiChat.java.patch @@ -22,8 +22,8 @@ this.inputField.setText(this.defaultInputFieldText); this.inputField.setCanLoseFocus(false); this.tabCompleter = new GuiChat.ChatTabCompleter(this.inputField); -+ this.suggestionList = new SuggestionList(); -+ SuggestionUpdater updater = new SuggestionUpdater(this.suggestionList); ++ this.suggestionList = new SuggestionList(this.inputField); ++ SuggestionUpdater updater = new SuggestionUpdater(this.suggestionList, this.tabCompleter); + this.inputField.setGuiResponder(updater); + if (this.defaultInputFieldText != null) + { @@ -117,15 +117,17 @@ ITextComponent itextcomponent = this.mc.ingameGUI.getChatGUI().getChatComponent(Mouse.getX(), Mouse.getY()); if (itextcomponent != null && this.handleComponentClick(itextcomponent)) -@@ -201,6 +254,7 @@ +@@ -201,7 +254,9 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { drawRect(2, this.height - 14, this.width - 2, this.height - 2, Integer.MIN_VALUE); + this.suggestionList.drawGhostText(this.inputField, this.fontRenderer); this.inputField.drawTextBox(); ++ this.suggestionList.drawCommandColor(this.inputField, this.fontRenderer); ITextComponent itextcomponent = this.mc.ingameGUI.getChatGUI().getChatComponent(Mouse.getX(), Mouse.getY()); -@@ -209,6 +263,7 @@ + if (itextcomponent != null && itextcomponent.getStyle().getHoverEvent() != null) +@@ -209,6 +264,7 @@ this.handleComponentHover(itextcomponent, mouseX, mouseY); } @@ -133,7 +135,7 @@ super.drawScreen(mouseX, mouseY, partialTicks); } -@@ -222,6 +277,7 @@ +@@ -222,6 +278,7 @@ public void setCompletions(String... newCompletions) { this.tabCompleter.setCompletions(newCompletions); @@ -141,7 +143,7 @@ } @SideOnly(Side.CLIENT) -@@ -232,29 +288,6 @@ +@@ -232,29 +289,6 @@ public ChatTabCompleter(GuiTextField p_i46749_1_) { super(p_i46749_1_, false); diff --git a/patches/minecraft/net/minecraft/client/gui/GuiTextField.java.patch b/patches/minecraft/net/minecraft/client/gui/GuiTextField.java.patch index bdb0e09bd..284178533 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiTextField.java.patch +++ b/patches/minecraft/net/minecraft/client/gui/GuiTextField.java.patch @@ -7,7 +7,19 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import net.minecraft.client.Minecraft; -@@ -631,6 +632,7 @@ +@@ -603,6 +604,11 @@ + return this.cursorPosition; + } + ++ public int getLineScrollOffset() ++ { ++ return this.lineScrollOffset; ++ } ++ + public boolean getEnableBackgroundDrawing() + { + return this.enableBackgroundDrawing; +@@ -631,6 +637,7 @@ } this.isFocused = isFocusedIn; diff --git a/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionList.java b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionList.java index 47144a657..45d632588 100644 --- a/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionList.java +++ b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionList.java @@ -11,8 +11,10 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Set; @SideOnly(Side.CLIENT) public class SuggestionList { @@ -20,11 +22,17 @@ public class SuggestionList { private static final int MAX_VISIBLE = 10; private static final int ENTRY_HEIGHT = 12; private static final int PADDING_X = 3; + private static final Set knownCommands = new HashSet<>(); + private final GuiTextField field; private List suggestions = Collections.emptyList(); private int selectedIndex = -1; private int scrollOffset = 0; private int cachedWidth = 0; + public SuggestionList(GuiTextField field) { + this.field = field; + knownCommands.addAll(ClientCommandHandler.instance.getCommands().keySet()); + } public void setSuggestions(List newSuggestions) { this.suggestions = newSuggestions; @@ -52,7 +60,7 @@ public void hide() { } public void selectNext() { - if (this.suggestions.isEmpty()) { + if (this.isInvisible()) { return; } this.selectedIndex = this.selectedIndex < this.suggestions.size() - 1 ? this.selectedIndex + 1 : -1; @@ -116,10 +124,6 @@ public void render(int inputX, int inputY, int inputWidth, int mouseX, int mouse Gui.drawRect(barX, listY, barX + 2, listY + listHeight, 0xFF333333); Gui.drawRect(barX, thumbY, barX + 2, thumbY + thumbHeight, 0xFFAAAAAA); } -// int separatorY = listY + listHeight - 1; -// for (int x = inputX; x < inputX + listWidth; x++) { -// Gui.drawRect(x, separatorY, x + 1, separatorY + 1, (x - inputX) % 2 == 0 ? 0xFFFFFFFF : 0xFF000000); -// } } public void updateSuggestions(String... serverCompletions) { @@ -127,11 +131,27 @@ public void updateSuggestions(String... serverCompletions) { String[] clientComplete = ClientCommandHandler.instance.latestAutoComplete; if (clientComplete != null) { for (String s : clientComplete) { - if (!s.isEmpty()) list.add(s); + if (!s.isEmpty()) { + list.add(s); + } } } for (String s : serverCompletions) { - if (!s.isEmpty() && !list.contains(s)) list.add(s); + if (!s.isEmpty() && !list.contains(s)) { + list.add(s); + } + } + String currentText = this.field.getText(); + if (currentText.isEmpty() || (currentText.startsWith("/") && !currentText.contains(" "))) { + for (String s : list) { + String name = s.startsWith("/") ? s.substring(1) : s; + if (!name.isEmpty()) { + knownCommands.add(name); + } + } + } + if (currentText.isEmpty()) { + return; } this.setSuggestions(list); } @@ -172,6 +192,25 @@ public void drawGhostText(GuiTextField inputField, FontRenderer fontRenderer) { fontRenderer.drawString(suffix, ghostX, inputField.y, 0xFF808080); } + public void drawCommandColor(GuiTextField inputField, FontRenderer fontRenderer) { + String text = inputField.getText(); + if (!text.startsWith("/")) { + return; + } + int firstSpaceIdx = text.indexOf(' '); + String firstWord = firstSpaceIdx == -1 ? text : text.substring(0, firstSpaceIdx); + String cmdName = firstWord.substring(1); + if (cmdName.isEmpty()) { + return; + } + int scrollOffset = inputField.getLineScrollOffset(); + if (scrollOffset >= firstWord.length()) { + return; + } + int color = knownCommands.contains(cmdName) ? 0x55FF55 : 0xFF5555; + fontRenderer.drawStringWithShadow(firstWord.substring(scrollOffset), inputField.x, inputField.y, color); + } + public boolean isMouseOver(int inputX, int inputY, int inputWidth, int mouseX, int mouseY) { if (!this.isVisible()) { return false; diff --git a/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java index 53c46c35e..7013d6132 100644 --- a/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java +++ b/src/main/java/com/cleanroommc/client/chat/suggestion/SuggestionUpdater.java @@ -20,6 +20,10 @@ public class SuggestionUpdater implements GuiPageButtonList.GuiResponder { public SuggestionUpdater(SuggestionList suggestionList, TabCompleter tabCompleter) { this.suggestionList = suggestionList; this.tabCompleter = tabCompleter; + Minecraft mc = Minecraft.getMinecraft(); + if (mc.player != null) { + mc.player.connection.sendPacket(new CPacketTabComplete("/", null, false)); + } } @Override