mirror of
https://github.com/GTNewHorizons/Applied-Energistics-2-Unofficial.git
synced 2026-10-26 14:25:20 +00:00
Add line wrapping support for all interface names in the Interface Terminal (#1120)
Co-authored-by: Martin Robertz <dream-master@gmx.net> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
1 file changed
+150
-66
@@ -10,6 +10,7 @@
|
||||
|
||||
package appeng.client.gui.implementations;
|
||||
|
||||
import java.text.BreakIterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -17,6 +18,7 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@@ -474,7 +476,8 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
*/
|
||||
while (viewY < this.viewHeight && entryIdx < visibleSections.size()) {
|
||||
InterfaceSection section = visibleSections.get(entryIdx);
|
||||
int sectionHeight = section.getHeight();
|
||||
int titleHeight = getWrappedTitleHeight(section.name, VIEW_WIDTH - 4);
|
||||
int sectionHeight = titleHeight + section.getEntriesHeight();
|
||||
|
||||
/* Is it viewable/in the viewport at all? */
|
||||
if (viewY + sectionHeight < 0) {
|
||||
@@ -491,6 +494,89 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total height of the title after wrapping, using TITLE_HEIGHT as step
|
||||
*/
|
||||
public int getWrappedTitleHeight(String name, int maxWidth) {
|
||||
List<String> lines = breakText(name, maxWidth);
|
||||
return Math.max(InterfaceSection.TITLE_HEIGHT, lines.size() * InterfaceSection.TITLE_HEIGHT);
|
||||
}
|
||||
|
||||
public List<String> breakText(String text, int maxWidth) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
if (text == null || text.isEmpty()) return lines;
|
||||
|
||||
var lang = mc.getLanguageManager().getCurrentLanguage();
|
||||
Locale locale = Locale.forLanguageTag(lang.toString().replace(" (", "-").replace(")", ""));
|
||||
if (locale == null) locale = Locale.forLanguageTag(lang.getLanguageCode());
|
||||
if (locale == null) locale = Locale.ENGLISH;
|
||||
|
||||
var breaker = BreakIterator.getLineInstance(locale);
|
||||
breaker.setText(text);
|
||||
|
||||
var currentLine = new StringBuilder();
|
||||
int start = breaker.first();
|
||||
for (int end = breaker.next(); end != BreakIterator.DONE; start = end, end = breaker.next()) {
|
||||
String word = text.substring(start, end);
|
||||
|
||||
if (fontRendererObj.getStringWidth(word) > maxWidth) {
|
||||
if (currentLine.length() > 0) {
|
||||
lines.add(currentLine.toString());
|
||||
currentLine = new StringBuilder(toControlCodes(getControlCodes(currentLine.toString())));
|
||||
}
|
||||
|
||||
for (int i = 0; i < word.length(); i++) {
|
||||
char c = word.charAt(i);
|
||||
if (fontRendererObj.getStringWidth(currentLine.toString() + c) > maxWidth) {
|
||||
lines.add(currentLine.toString());
|
||||
currentLine = new StringBuilder(toControlCodes(getControlCodes(currentLine.toString())) + c);
|
||||
} else {
|
||||
currentLine.append(c);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
String linePreview = currentLine + word;
|
||||
if (fontRendererObj.getStringWidth(linePreview) > maxWidth && currentLine.length() > 0) {
|
||||
lines.add(currentLine.toString());
|
||||
String codes = toControlCodes(getControlCodes(currentLine.toString()));
|
||||
currentLine = new StringBuilder(codes + word);
|
||||
} else {
|
||||
currentLine.append(word);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentLine.length() > 0) {
|
||||
lines.add(currentLine.toString());
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the effective Minecraft formatting control codes from a string.
|
||||
*
|
||||
* @param s the source string containing formatting codes (e.g., "§6§lHello§r§cWorld")
|
||||
* @return the sequence of formatting codes that remain active at the end of the string, accounting for style resets
|
||||
* (e.g., "r§c")
|
||||
*/
|
||||
public static String getControlCodes(String s) {
|
||||
String controls = s.replaceAll("(?<!\u00a7)(.)", "");
|
||||
String wiped = controls.replaceAll(".*r", "r");
|
||||
return wiped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw character sequence into a string of Minecraft formatting codes.
|
||||
*
|
||||
* @param s the string of raw formatting characters to convert (e.g., "6l")
|
||||
* @return the formatted string with each character prefixed by a section sign (e.g., "§6§l")
|
||||
*/
|
||||
public static String toControlCodes(String s) {
|
||||
return s.replaceAll(".", "\u00a7$0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the section (if it is visible)
|
||||
*
|
||||
@@ -501,74 +587,64 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
* @return the height of the section rendered in viewport coordinates, max of viewHeight.
|
||||
*/
|
||||
private int drawSection(InterfaceSection section, int viewY, int relMouseX, int relMouseY) {
|
||||
int title;
|
||||
int renderY = 0;
|
||||
final int sectionBottom = viewY + section.getHeight() - 1;
|
||||
final int fontColor = GuiColors.InterfaceTerminalInventory.getColor();
|
||||
/*
|
||||
* Render title
|
||||
*/
|
||||
bindTexture(BACKGROUND);
|
||||
GL11.glTranslatef(0.0f, 0.0f, ITEM_STACK_OVERLAY_Z + ITEM_STACK_Z + STEP_Z);
|
||||
if (sectionBottom > 0 && sectionBottom < InterfaceSection.TITLE_HEIGHT) {
|
||||
/* Transition draw */
|
||||
title = sectionBottom;
|
||||
} else if (viewY < 0) {
|
||||
/* Hidden title draw */
|
||||
title = 0;
|
||||
} else {
|
||||
/* Normal title draw */
|
||||
title = 0;
|
||||
}
|
||||
GL11.glTranslatef(0.0f, 0.0f, -(ITEM_STACK_OVERLAY_Z + ITEM_STACK_Z + STEP_Z));
|
||||
GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
List<String> titleLines = breakText(section.name, VIEW_WIDTH - 4);
|
||||
|
||||
int actualTitleHeight = Math
|
||||
.max(InterfaceSection.TITLE_HEIGHT, titleLines.size() * InterfaceSection.TITLE_HEIGHT);
|
||||
|
||||
/*
|
||||
* Render entries first (to keep titles on top layer if needed)
|
||||
*/
|
||||
Iterator<InterfaceTerminalEntry> visible = section.getVisible();
|
||||
while (visible.hasNext()) {
|
||||
InterfaceTerminalEntry entry = visible.next();
|
||||
if (viewY + renderY + entry.rows * 18 + 1 > 0 && viewY + renderY < viewHeight) {
|
||||
renderY += drawEntry(
|
||||
entry,
|
||||
viewY + InterfaceSection.TITLE_HEIGHT + renderY,
|
||||
title,
|
||||
relMouseX,
|
||||
relMouseY);
|
||||
int entryTop = viewY + actualTitleHeight + renderY;
|
||||
int entryHeight = entry.rows * 18 + 1;
|
||||
|
||||
if (entryTop + entryHeight > 0 && entryTop < viewHeight) {
|
||||
renderY += drawEntry(entry, entryTop, viewY + actualTitleHeight, relMouseX, relMouseY);
|
||||
} else {
|
||||
entry.dispY = -9999;
|
||||
entry.optionsButton.yPosition = -1;
|
||||
renderY += entry.rows * 18 + 1;
|
||||
renderY += entryHeight;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Render title
|
||||
*/
|
||||
bindTexture(BACKGROUND);
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslatef(0.0f, 0.0f, ITEM_STACK_OVERLAY_Z + ITEM_STACK_Z + STEP_Z);
|
||||
if (sectionBottom > 0 && sectionBottom < InterfaceSection.TITLE_HEIGHT) {
|
||||
/* Transition draw */
|
||||
drawTexturedModalRect(
|
||||
0,
|
||||
0,
|
||||
VIEW_LEFT,
|
||||
HEADER_HEIGHT + InterfaceSection.TITLE_HEIGHT - sectionBottom,
|
||||
VIEW_WIDTH,
|
||||
sectionBottom);
|
||||
fontRendererObj.drawString(section.name, 2, sectionBottom - InterfaceSection.TITLE_HEIGHT + 2, fontColor);
|
||||
} else if (viewY < 0) {
|
||||
/* Hidden title draw */
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glTranslatef(0.0f, 0.0f, 100f);
|
||||
drawTexturedModalRect(0, 0, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, InterfaceSection.TITLE_HEIGHT);
|
||||
fontRendererObj.drawString(section.name, 2, 2, fontColor);
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
} else {
|
||||
/* Normal title draw */
|
||||
drawTexturedModalRect(0, viewY, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, InterfaceSection.TITLE_HEIGHT);
|
||||
fontRendererObj.drawString(section.name, 2, viewY + 2, fontColor);
|
||||
}
|
||||
GL11.glTranslatef(0.0f, 0.0f, -(ITEM_STACK_OVERLAY_Z + ITEM_STACK_Z + STEP_Z));
|
||||
|
||||
return InterfaceSection.TITLE_HEIGHT + renderY;
|
||||
if (viewY < viewHeight && viewY + actualTitleHeight > 0) {
|
||||
// Draw background: one TITLE_HEIGHT block per line
|
||||
for (int i = 0; i < titleLines.size(); i++) {
|
||||
int lineY = viewY + (i * InterfaceSection.TITLE_HEIGHT);
|
||||
if (lineY + InterfaceSection.TITLE_HEIGHT > 0 && lineY < viewHeight) {
|
||||
drawTexturedModalRect(
|
||||
0,
|
||||
lineY,
|
||||
VIEW_LEFT,
|
||||
HEADER_HEIGHT,
|
||||
VIEW_WIDTH,
|
||||
InterfaceSection.TITLE_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < titleLines.size(); i++) {
|
||||
int textY = viewY + 2 + (i * InterfaceSection.TITLE_HEIGHT);
|
||||
if (textY + 9 > 0 && textY < viewHeight) {
|
||||
fontRendererObj.drawString(titleLines.get(i), 2, textY, fontColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glPopMatrix();
|
||||
GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
return actualTitleHeight + renderY;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -618,7 +694,7 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
entry.optionsButton.yPosition = viewY + 5;
|
||||
entry.optionsButton.drawButton(mc, relMouseX, relMouseY);
|
||||
if (entry.optionsButton.getMouseIn()
|
||||
&& relMouseY >= Math.max(InterfaceSection.TITLE_HEIGHT, entry.optionsButton.yPosition)) {
|
||||
&& relMouseY >= Math.max(titleBottom - viewY + entry.optionsButton.yPosition, entry.optionsButton.yPosition)) {
|
||||
// draw a tooltip
|
||||
GL11.glTranslatef(0f, 0f, TOOLTIP_Z);
|
||||
GL11.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
@@ -650,7 +726,7 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
ItemStack stack = inv.getStackInSlot(slotIdx);
|
||||
|
||||
boolean tooltip = relMouseX > colLeft - 1 && relMouseX < colRight - 1
|
||||
&& relMouseY >= Math.max(viewY + rowYTop, InterfaceSection.TITLE_HEIGHT)
|
||||
&& relMouseY >= Math.max(viewY + rowYTop, titleBottom)
|
||||
&& relMouseY < Math.min(viewY + rowYBot, viewHeight);
|
||||
if (stack != null) {
|
||||
// just in case non-pattern items show up (like in a GT AE machine), render them normally
|
||||
@@ -1241,19 +1317,26 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
if (isDirty) {
|
||||
update();
|
||||
}
|
||||
return height;
|
||||
// Dynamically calculate title height and add entries height
|
||||
return getWrappedTitleHeight(this.name, VIEW_WIDTH - 4) + getEntriesHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets total height of all visible entries only.
|
||||
*/
|
||||
public int getEntriesHeight() {
|
||||
if (isDirty) {
|
||||
update();
|
||||
}
|
||||
int entriesHeight = 0;
|
||||
for (InterfaceTerminalEntry entry : visibleEntries) {
|
||||
entriesHeight += entry.guiHeight;
|
||||
}
|
||||
return entriesHeight;
|
||||
}
|
||||
|
||||
private void update() {
|
||||
refreshVisible();
|
||||
if (visibleEntries.isEmpty()) {
|
||||
height = 0;
|
||||
} else {
|
||||
height = TITLE_HEIGHT;
|
||||
for (InterfaceTerminalEntry entry : visibleEntries) {
|
||||
height += entry.guiHeight;
|
||||
}
|
||||
}
|
||||
isDirty = false;
|
||||
}
|
||||
|
||||
@@ -1432,7 +1515,6 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
final int newHasItem = stack != null ? 1 : 0;
|
||||
|
||||
inv.setInventorySlotContents(idx, stack);
|
||||
// Update item count
|
||||
numItems += newHasItem - oldHasItem;
|
||||
assert numItems >= 0;
|
||||
}
|
||||
@@ -1507,8 +1589,10 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
if (!section.visible || btn < 0 || btn > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate options button click
|
||||
if (mouseX >= optionsButton.xPosition && mouseX < 2 + optionsButton.width
|
||||
&& mouseY > Math.max(optionsButton.yPosition, InterfaceSection.TITLE_HEIGHT)
|
||||
&& mouseY > optionsButton.yPosition
|
||||
&& mouseY <= Math.min(optionsButton.yPosition + optionsButton.height, viewHeight)) {
|
||||
optionsButton.func_146113_a(mc.getSoundHandler());
|
||||
|
||||
@@ -1538,7 +1622,7 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
int offsetY = mouseY - dispY - 1;
|
||||
int offsetX = mouseX - (VIEW_WIDTH - rowSize * 18) - 1;
|
||||
if (offsetX >= 0 && offsetX < (rowSize * 18)
|
||||
&& mouseY > Math.max(dispY, InterfaceSection.TITLE_HEIGHT)
|
||||
&& mouseY > dispY
|
||||
&& offsetY < Math.min(viewHeight - dispY, guiHeight - 1)) {
|
||||
final int col = offsetX / 18;
|
||||
final int row = offsetY / 18;
|
||||
|
||||
Reference in new issue
Block a user