diff --git a/src/main/java/codechicken/nei/Button.java b/src/main/java/codechicken/nei/Button.java index 6fc3f20c2..026147666 100644 --- a/src/main/java/codechicken/nei/Button.java +++ b/src/main/java/codechicken/nei/Button.java @@ -57,6 +57,10 @@ public String getRenderLabel() { return label; } + public int getRenderLabelColor() { + return 0; + } + public final String label; public Image icon; diff --git a/src/main/java/codechicken/nei/ColorUtils.java b/src/main/java/codechicken/nei/ColorUtils.java new file mode 100644 index 000000000..0784727ae --- /dev/null +++ b/src/main/java/codechicken/nei/ColorUtils.java @@ -0,0 +1,22 @@ +package codechicken.nei; + +import codechicken.nei.api.ColorResource; + +public class ColorUtils { + + private static final ColorResource.Factory color = new ColorResource.Factory("nei"); + + public static final ColorResource + // spotless:off + textGray = color.rgb("textGray", "0x404040"), + textLightGray = color.rgb("textLightGray", "0x606060"), + recipeTitle = color.rgb("recipeTitle", "0xFFFF55"), + recipeTitleHover = color.rgb("recipeTitleHover", "0xFFFFFF"), + subsetWidget = color.rgb("subsetWidget", "0xAA00AA"), + recipeBadge = color.rgb("recipeBadge", "0xFDD835"), + buttonLabelNormal = color.rgb("buttonLabelNormal", "0xE0E0E0"), + buttonLabelHover = color.rgb("buttonLabelHover", "0xFFFFA0"), + buttonLabelDisabled = color.rgb("buttonLabelDisabled", "0x601010"), + buttonLabelInactive = color.rgb("buttonLabelInactive", "0xA0A0A0"); + // spotless:on +} diff --git a/src/main/java/codechicken/nei/GuiEnchantmentModifier.java b/src/main/java/codechicken/nei/GuiEnchantmentModifier.java index 6a03c0196..70b030799 100644 --- a/src/main/java/codechicken/nei/GuiEnchantmentModifier.java +++ b/src/main/java/codechicken/nei/GuiEnchantmentModifier.java @@ -24,8 +24,8 @@ public GuiEnchantmentModifier(InventoryPlayer inventoryplayer, World world, int @Override protected void drawGuiContainerForegroundLayer(int par1, int par2) { - fontRendererObj.drawString(translate("enchant"), 12, 6, 0x404040); - fontRendererObj.drawString(translate("enchant.level"), 19, 20, 0x404040); + fontRendererObj.drawString(translate("enchant"), 12, 6, ColorUtils.textGray.getColor()); + fontRendererObj.drawString(translate("enchant.level"), 19, 20, ColorUtils.textGray.getColor()); } protected void drawGuiContainerBackgroundLayer(float f, int i, int j) { @@ -39,7 +39,11 @@ protected void drawGuiContainerBackgroundLayer(float f, int i, int j) { container.drawScrollBar(this); String levelstring = "" + container.level; - fontRendererObj.drawString(levelstring, 33 - fontRendererObj.getStringWidth(levelstring) / 2, 34, 0xFF606060); + fontRendererObj.drawString( + levelstring, + 33 - fontRendererObj.getStringWidth(levelstring) / 2, + 34, + ColorUtils.textLightGray.getColor()); GL11.glTranslatef(-guiLeft, -guiTop, 0); } diff --git a/src/main/java/codechicken/nei/GuiNEIButton.java b/src/main/java/codechicken/nei/GuiNEIButton.java index 2774d0877..a31615c0f 100644 --- a/src/main/java/codechicken/nei/GuiNEIButton.java +++ b/src/main/java/codechicken/nei/GuiNEIButton.java @@ -45,15 +45,12 @@ public void drawButton(Minecraft minecraft, int x, int y) { } protected int getTextColour(boolean mouseOver) { - int color = 0xe0e0e0; - if (!enabled) { - color = 0xffa0a0a0; + return ColorUtils.buttonLabelInactive.getColor(); } else if (mouseOver) { - color = 0xffffa0; + return ColorUtils.buttonLabelHover.getColor(); } - - return color; + return ColorUtils.buttonLabelNormal.getColor(); } protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) { diff --git a/src/main/java/codechicken/nei/GuiPotionCreator.java b/src/main/java/codechicken/nei/GuiPotionCreator.java index 23eae1339..66dd54eca 100644 --- a/src/main/java/codechicken/nei/GuiPotionCreator.java +++ b/src/main/java/codechicken/nei/GuiPotionCreator.java @@ -289,10 +289,10 @@ public void drawBackground() { CCRenderState.changeTexture("nei:textures/gui/potion.png"); drawTexturedModalRect(0, 0, 0, 0, xSize, ySize); - FontUtils.drawCenteredString(translate("potion.favourite"), xSize / 2, 4, 0x404040); - fontRendererObj.drawString(translate("potion.duration"), 12, 40, 0x404040); - fontRendererObj.drawString(translate("potion.level"), 19, 73, 0x404040); - FontUtils.drawCenteredString(translateAmplifier(amplifier), 33, 86, 0xFF606060); + FontUtils.drawCenteredString(translate("potion.favourite"), xSize / 2, 4, ColorUtils.textGray.getColor()); + fontRendererObj.drawString(translate("potion.duration"), 12, 40, ColorUtils.textGray.getColor()); + fontRendererObj.drawString(translate("potion.level"), 19, 73, ColorUtils.textGray.getColor()); + FontUtils.drawCenteredString(translateAmplifier(amplifier), 33, 86, ColorUtils.textLightGray.getColor()); } @Override diff --git a/src/main/java/codechicken/nei/LayoutStyleMinecraft.java b/src/main/java/codechicken/nei/LayoutStyleMinecraft.java index 521dc64e9..7d8bda93f 100644 --- a/src/main/java/codechicken/nei/LayoutStyleMinecraft.java +++ b/src/main/java/codechicken/nei/LayoutStyleMinecraft.java @@ -244,7 +244,11 @@ else if ((b.state & 0x4) == 0 && b.contains(mousex, mousey) || // not a state bu Image icon = b.getRenderIcon(); if (icon == null) { - int colour = tex == 2 ? 0xffffa0 : tex == 0 ? 0x601010 : 0xe0e0e0; + int labelColor = b.getRenderLabelColor(); + int colour = labelColor != 0 ? labelColor + : tex == 2 ? ColorUtils.buttonLabelHover.getColor() + : tex == 0 ? ColorUtils.buttonLabelDisabled.getColor() + : ColorUtils.buttonLabelNormal.getColor(); drawStringC(b.getRenderLabel(), b.x + b.w / 2, b.y + (b.h - 8) / 2, colour); } else { diff --git a/src/main/java/codechicken/nei/NEIClientUtils.java b/src/main/java/codechicken/nei/NEIClientUtils.java index e4a405e48..b6641c3a4 100644 --- a/src/main/java/codechicken/nei/NEIClientUtils.java +++ b/src/main/java/codechicken/nei/NEIClientUtils.java @@ -531,11 +531,6 @@ public static void reportErrorBuffered(Throwable e, Set buffer, ItemStac } } - public static String getTextColorOrDefault(String key, String defaultColor) { - final String translated = NEIClientUtils.translate(key); // Optional localization string for resource packs - return translated.startsWith("nei.") ? defaultColor : translated; - } - public static void drawRect(double left, double top, double width, double height, Color color) { final boolean is2DTexture = GL11.glGetBoolean(GL11.GL_TEXTURE_2D); final boolean isBlend = GL11.glGetBoolean(GL11.GL_BLEND); diff --git a/src/main/java/codechicken/nei/NEIModContainer.java b/src/main/java/codechicken/nei/NEIModContainer.java index e1ee76152..7e2c969de 100644 --- a/src/main/java/codechicken/nei/NEIModContainer.java +++ b/src/main/java/codechicken/nei/NEIModContainer.java @@ -9,11 +9,15 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.IReloadableResourceManager; + import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import codechicken.core.CommonUtils; import codechicken.core.launch.CodeChickenCorePlugin; +import codechicken.nei.api.ColorResource; import codechicken.nei.api.IConfigureNEI; import codechicken.nei.asm.NEICorePlugin; import codechicken.nei.config.IMCHandler; @@ -105,6 +109,8 @@ public void init(FMLInitializationEvent event) { if (CommonUtils.isClient()) { ClientHandler.load(); IMCForNEI.IMCSender(); + ((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()) + .registerReloadListener(new ColorResource.CacheReloadListener()); } ServerHandler.load(); } diff --git a/src/main/java/codechicken/nei/SubsetWidget.java b/src/main/java/codechicken/nei/SubsetWidget.java index 72c2ed9bb..505655ae4 100644 --- a/src/main/java/codechicken/nei/SubsetWidget.java +++ b/src/main/java/codechicken/nei/SubsetWidget.java @@ -689,17 +689,17 @@ public SubsetWidget() { } @Override - public String getRenderLabel() { + public int getRenderLabelColor() { + return NEIClientConfig.subsetWidgetOnTop() ? 0 : ColorUtils.subsetWidget.getColor(); + } + @Override + public String getRenderLabel() { if (NEIClientConfig.subsetWidgetOnTop()) { return NEIClientUtils.translate("inventory.item_subsets"); } else { - return NEIClientUtils.getTextColorOrDefault( - "gui.button.label.subset", - EnumChatFormatting.DARK_PURPLE - + String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX))); + return String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX)); } - } @Override diff --git a/src/main/java/codechicken/nei/api/ColorResource.java b/src/main/java/codechicken/nei/api/ColorResource.java new file mode 100644 index 000000000..dc34aadf5 --- /dev/null +++ b/src/main/java/codechicken/nei/api/ColorResource.java @@ -0,0 +1,173 @@ +package codechicken.nei.api; + +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; + +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.resources.IResourceManagerReloadListener; +import net.minecraft.util.StatCollector; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * A color constant supporting both ARGB and RGB formats, with resource pack override and per-instance caching. + *

+ * Use {@link Factory} to avoid repeating the mod ID on every line: + * + *

+ * 
+ *  public static class ColorUtils {
+ *      private static final ColorResource.Factory color = new ColorResource.Factory("mymod");
+ *
+ *      public static final ColorResource
+ *      // spotless:off
+ *          background      = color.rgb("background",       "0x202020"),
+ *
+ *          guiOverlayWhite = color.argb("guiOverlayWhite", "0x80FFFFFF"),
+ *          text            = color.argb("text",            "0xFFFFFFFF");
+ *      // spotless:on
+ *  }
+ * 
+ * 
+ *

+ * Then use the color anywhere: + * + *

+ * 
+ *  GuiDraw.drawRect(x, y, w, h, ColorUtils.background.getColor());
+ * 
+ * 
+ *

+ * Resource packs override colors via a lang file entry: {@code color.resource.mymod.background=80FF20AA} + *

+ * ARGB colors use 8-char hex (AARRGGBB). RGB colors use 6-char hex (RRGGBB), alpha is always FF. + *

+ * Colors are resolved on every resource reload (F3+T) via {@link CacheReloadListener} + */ +public class ColorResource { + + private static final Logger LOG = LogManager.getLogger(ColorResource.class); + private static final Set INSTANCES = Collections.newSetFromMap(new WeakHashMap<>()); + + private final String langKey; + private final int defaultColor; + private final boolean argb; + private volatile int cachedColor; + + /** + * @param modId the mod ID used as the namespace in the lang key + * @param name the color name used in the lang key + * @param hex default color — AARRGGBB if {@code argb} is true, RRGGBB otherwise + * @param argb true to include the alpha channel, false to force alpha to FF + */ + public ColorResource(String modId, String name, String hex, boolean argb) { + this.langKey = "color.resource." + modId + "." + name; + this.argb = argb; + this.defaultColor = parseHex(hex, argb); + this.cachedColor = resolveColor(); + synchronized (INSTANCES) { + INSTANCES.add(this); + } + } + + private static String stripPrefix(String hex) { + String s = hex.trim(); + if (s.startsWith("0x") || s.startsWith("0X")) return s.substring(2); + if (s.startsWith("#")) return s.substring(1); + return s; + } + + private static int parseHex(String hex, boolean argb) { + long value = Long.parseLong(stripPrefix(hex), 16); + return argb ? (int) value : (int) (0xFF000000L | value); + } + + /** Lang key used to look up a resource pack override. */ + public String getLangKey() { + return langKey; + } + + /** + * Returns the resolved ARGB color value. Checks the resource pack lang file first; falls back to the default color. + * Updated on every resource reload (F3+T). + *

+ * Example usage: + * + *

+     * 
+     *  GuiDraw.drawRect(x, y, w, h, ColorUtils.background.getColor());
+     * 
+     * 
+ */ + public int getColor() { + return cachedColor; + } + + private int resolveColor() { + if (StatCollector.canTranslate(langKey)) { + String value = stripPrefix(StatCollector.translateToLocal(langKey)); + try { + if (!argb && value.length() > 6) { + LOG.warn( + "[ColorResource] Lang key '{}' received ARGB hex '{}' but this color is RGB-only — alpha will be ignored.", + langKey, + value); + } + long parsed = Long.parseLong(value, 16); + return argb ? (int) parsed : (int) (0xFF000000L | parsed); + } catch (NumberFormatException e) { + LOG.warn("[ColorResource] Invalid hex '{}' for lang key '{}', using default.", value, langKey); + return defaultColor; + } + } + return defaultColor; + } + + /** + * Factory that holds a mod ID so it does not need to be repeated on every color declaration. + *

+ * Example usage: + * + *

+     * 
+     *  private static final ColorResource.Factory colors = new ColorResource.Factory("mymod");
+     *  public static final ColorResource
+     *      background = colors.argb("background", "0xFF202020"),
+     *      text       = colors.rgb("text",        "0xFFFFFF");
+     * 
+     * 
+ */ + public static class Factory { + + private final String modId; + + public Factory(String modId) { + this.modId = modId; + } + + /** Creates an ARGB color (AARRGGBB hex). */ + public ColorResource argb(String name, String hex) { + return new ColorResource(modId, name, hex, true); + } + + /** Creates an RGB color (RRGGBB hex), alpha is always FF. */ + public ColorResource rgb(String name, String hex) { + return new ColorResource(modId, name, hex, false); + } + } + + /** Resolves all color values on resource reload (F3+T). */ + public static class CacheReloadListener implements IResourceManagerReloadListener { + + @Override + public void onResourceManagerReload(IResourceManager resourceManager) { + synchronized (INSTANCES) { + for (ColorResource instance : INSTANCES) { + instance.cachedColor = instance.resolveColor(); + } + } + } + } +} diff --git a/src/main/java/codechicken/nei/config/preset/CheckboxButton.java b/src/main/java/codechicken/nei/config/preset/CheckboxButton.java index a675efbde..9e3520677 100644 --- a/src/main/java/codechicken/nei/config/preset/CheckboxButton.java +++ b/src/main/java/codechicken/nei/config/preset/CheckboxButton.java @@ -5,6 +5,7 @@ import org.lwjgl.opengl.GL11; import codechicken.nei.Button; +import codechicken.nei.ColorUtils; import codechicken.nei.LayoutManager; public abstract class CheckboxButton extends Button { @@ -38,7 +39,8 @@ public void draw(int mousex, int mousey) { tex = 1; } - int colour = tex == 2 ? 0xffffa0 : tex == 0 ? 0x601010 : 0xe0e0e0; + int colour = tex == 2 ? ColorUtils.buttonLabelHover.getColor() + : tex == 0 ? ColorUtils.buttonLabelDisabled.getColor() : ColorUtils.buttonLabelNormal.getColor(); LayoutManager.drawButtonBackground(x, y, w, h, true, isChecked() ? 0 : tex); drawStringC(getRenderLabel(), x + w / 2, y + (h - 8) / 2, colour); } diff --git a/src/main/java/codechicken/nei/recipe/Badge.java b/src/main/java/codechicken/nei/recipe/Badge.java index 1be9860cb..e9829bbc4 100644 --- a/src/main/java/codechicken/nei/recipe/Badge.java +++ b/src/main/java/codechicken/nei/recipe/Badge.java @@ -7,6 +7,7 @@ import net.minecraft.util.EnumChatFormatting; import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.ColorUtils; import codechicken.nei.NEIClientUtils; import codechicken.nei.NEIClientUtils.Alignment; @@ -14,16 +15,14 @@ public class Badge { protected final String text; protected final List tooltip; - protected int badgeTextColor = 0xFDD835; + protected int badgeTextColor; protected boolean shadow = false; protected Alignment alignment = Alignment.TopLeft; public Badge(String text, String... tooltip) { this.text = text; this.tooltip = Arrays.stream(tooltip).map(t -> EnumChatFormatting.GRAY + t).collect(Collectors.toList()); - this.badgeTextColor = getHexValue( - NEIClientUtils.getTextColorOrDefault("recipe.badge.color", "0xFDD835"), - 0xFDD835); + this.badgeTextColor = ColorUtils.recipeBadge.getColor(); } public String getText() { @@ -61,14 +60,6 @@ public Alignment getAlignment() { return this.alignment; } - private static int getHexValue(String color, int defaultValue) { - try { - return (int) Long.parseLong(color.replace("0x", ""), 16); - } catch (NumberFormatException e) { - return defaultValue; - } - } - public void draw(Rectangle4i rect) { NEIClientUtils.drawNEIOverlayText(getText(), rect, 0.5f, getColor(), hasShadow(), getAlignment()); } diff --git a/src/main/java/codechicken/nei/recipe/GuiRecipe.java b/src/main/java/codechicken/nei/recipe/GuiRecipe.java index 7362d6746..61c4c8f0a 100644 --- a/src/main/java/codechicken/nei/recipe/GuiRecipe.java +++ b/src/main/java/codechicken/nei/recipe/GuiRecipe.java @@ -17,7 +17,6 @@ import net.minecraft.client.renderer.RenderHelper; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumChatFormatting; import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.GL11; @@ -25,6 +24,7 @@ import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; import codechicken.nei.Button; +import codechicken.nei.ColorUtils; import codechicken.nei.GuiNEIButton; import codechicken.nei.NEICPH; import codechicken.nei.NEIClientConfig; @@ -878,13 +878,13 @@ private void drawJEITabs(int mouseX, int mouseY) { 0x30000000); final String handlerTitle = this.handler.original.getRecipeName().trim(); - final String titleColorCode = getHandlerTitleColorCode(isHandlerTitleHovered(mouseX, mouseY)); + final int titleColor = getHandlerTitleColor(isHandlerTitleHovered(mouseX, mouseY)); drawCenteredString( this.fontRendererObj, - titleColorCode + handlerTitle + EnumChatFormatting.RESET, + handlerTitle, this.guiLeft + this.xSize / 2, this.typeArea.y + textMiddle, - 0xffffff); + titleColor); if (this.handler.searchingAvailable()) { GuiRecipe.toggleSearch.draw(mouseX, mouseY); @@ -938,16 +938,8 @@ private boolean isHandlerTitleHovered(int mousex, int mousey) { return new Rectangle(titleX, titleY, titleWidth, this.fontRendererObj.FONT_HEIGHT).contains(mousex, mousey); } - private String getHandlerTitleColorCode(boolean hovered) { - - if (hovered) { - return NEIClientUtils - .getTextColorOrDefault("recipe.title.color.hover", EnumChatFormatting.YELLOW.toString()); - } else { - return NEIClientUtils - .getTextColorOrDefault("recipe.title.color.normal", EnumChatFormatting.WHITE.toString()); - } - + private int getHandlerTitleColor(boolean hovered) { + return hovered ? ColorUtils.recipeTitleHover.getColor() : ColorUtils.recipeTitle.getColor(); } @Override diff --git a/src/main/java/codechicken/nei/recipe/GuiRecipeTab.java b/src/main/java/codechicken/nei/recipe/GuiRecipeTab.java index fdd7fc350..4eadd71d3 100644 --- a/src/main/java/codechicken/nei/recipe/GuiRecipeTab.java +++ b/src/main/java/codechicken/nei/recipe/GuiRecipeTab.java @@ -27,6 +27,7 @@ import org.lwjgl.opengl.GL12; import codechicken.lib.gui.GuiDraw; +import codechicken.nei.ColorUtils; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; import codechicken.nei.Widget; @@ -127,7 +128,7 @@ public void drawForeground(int mouseX, int mouseY) { int textCenterX = x + (int) (getWidth() / 2f); int textCenterY = y + (int) (getHeight() / 2f) - 3; - int color = selected ? 0xffffa0 : 0xe0e0e0; + int color = selected ? ColorUtils.buttonLabelHover.getColor() : ColorUtils.buttonLabelNormal.getColor(); fontRenderer.drawStringWithShadow( text, textCenterX - (int) (fontRenderer.getStringWidth(text) / 2f), diff --git a/src/main/java/codechicken/nei/recipe/ProfilerRecipeHandler.java b/src/main/java/codechicken/nei/recipe/ProfilerRecipeHandler.java index d6e58c346..182e39b63 100644 --- a/src/main/java/codechicken/nei/recipe/ProfilerRecipeHandler.java +++ b/src/main/java/codechicken/nei/recipe/ProfilerRecipeHandler.java @@ -19,6 +19,7 @@ import codechicken.core.TaskProfiler.ProfilerResult; import codechicken.lib.gui.GuiDraw; +import codechicken.nei.ColorUtils; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; import codechicken.nei.PositionedStack; @@ -161,7 +162,7 @@ public void drawForeground(int recipe) { 3, hovered ? 0xFFFFDD00 : 0xFF808080, false); - GuiDraw.drawString(info.time, WIDTH - valueWidth, 3, 0xFF404040, false); + GuiDraw.drawString(info.time, WIDTH - valueWidth, 3, ColorUtils.textGray.getColor(), false); } private boolean isHandlerTitleHovered(int recipe, Point mouse) {