aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/anthonyhilyard/iceberg/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/anthonyhilyard/iceberg/util')
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/DynamicResourcePack.java59
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/GuiHelper.java2
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java85
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java229
4 files changed, 271 insertions, 104 deletions
diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/DynamicResourcePack.java b/src/main/java/com/anthonyhilyard/iceberg/util/DynamicResourcePack.java
index a59449a..4c0bfbf 100644
--- a/src/main/java/com/anthonyhilyard/iceberg/util/DynamicResourcePack.java
+++ b/src/main/java/com/anthonyhilyard/iceberg/util/DynamicResourcePack.java
@@ -3,19 +3,18 @@ package com.anthonyhilyard.iceberg.util;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackResources;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.metadata.MetadataSectionSerializer;
+import net.minecraft.server.packs.resources.IoSupplier;
/**
* DynamicResourcePack allows resources that are defined arbitrarily to do cool things with resources.
@@ -26,7 +25,7 @@ public class DynamicResourcePack implements PackResources
private record DynamicResourceKey(String type, String namespace, String path) {}
private final String packName;
- private Map<DynamicResourceKey, Supplier<InputStream>> dynamicResourceMap = new HashMap<DynamicResourceKey, Supplier<InputStream>>();
+ private Map<DynamicResourceKey, IoSupplier<InputStream>> dynamicResourceMap = new HashMap<DynamicResourceKey, IoSupplier<InputStream>>();
public DynamicResourcePack(String packName)
{
@@ -52,17 +51,17 @@ public class DynamicResourcePack implements PackResources
}
}
- public boolean registerResource(PackType type, ResourceLocation location, Supplier<InputStream> resourceSupplier)
+ public boolean registerResource(PackType type, ResourceLocation location, IoSupplier<InputStream> resourceSupplier)
{
return register(type.getDirectory(), location.getNamespace(), location.getPath(), resourceSupplier);
}
- public boolean registerRootResource(String path, Supplier<InputStream> resourceSupplier)
+ public boolean registerRootResource(String path, IoSupplier<InputStream> resourceSupplier)
{
return register("root", "", path, resourceSupplier);
}
- private boolean register(String directory, String namespace, String path, Supplier<InputStream> resourceSupplier)
+ private boolean register(String directory, String namespace, String path, IoSupplier<InputStream> resourceSupplier)
{
DynamicResourceKey key = new DynamicResourceKey(directory, namespace, path);
if (!dynamicResourceMap.containsKey(key))
@@ -74,23 +73,39 @@ public class DynamicResourcePack implements PackResources
}
@Override
- public InputStream getRootResource(String path) throws IOException
+ @Nullable
+ public IoSupplier<InputStream> getRootResource(String... path)
{
- return getResource("root", "", path);
+ try
+ {
+ return getResource("root", "", String.join("/", path));
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
}
@Override
- public InputStream getResource(PackType type, ResourceLocation location) throws IOException
+ @Nullable
+ public IoSupplier<InputStream> getResource(PackType type, ResourceLocation location)
{
- return getResource(type.getDirectory(), location.getNamespace(), location.getPath());
+ try
+ {
+ return getResource(type.getDirectory(), location.getNamespace(), location.getPath());
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
}
- private InputStream getResource(String directory, String namespace, String path) throws IOException
+ private IoSupplier<InputStream> getResource(String directory, String namespace, String path) throws IOException
{
DynamicResourceKey key = new DynamicResourceKey(directory, namespace, path);
if (dynamicResourceMap.containsKey(key))
{
- return dynamicResourceMap.get(key).get();
+ return dynamicResourceMap.get(key);
}
else
{
@@ -99,21 +114,13 @@ public class DynamicResourcePack implements PackResources
}
@Override
- public Collection<ResourceLocation> getResources(PackType type, String namespace, String path, Predicate<ResourceLocation> filter)
+ public void listResources(PackType type, String namespace, String path, ResourceOutput output)
{
- return dynamicResourceMap.entrySet().stream()
+ dynamicResourceMap.entrySet().stream()
.filter(entry -> entry.getKey().namespace.contentEquals(namespace))
.filter(entry -> entry.getKey().path.startsWith(path))
.filter(entry -> entry.getKey().type.contentEquals(type.getDirectory()))
- .filter(entry -> filter.test(new ResourceLocation(entry.getKey().namespace, entry.getKey().path)))
- .map(entry -> new ResourceLocation(namespace, entry.getKey().path))
- .collect(Collectors.toList());
- }
-
- @Override
- public boolean hasResource(PackType type, ResourceLocation location)
- {
- return dynamicResourceMap.containsKey(new DynamicResourceKey(type.getDirectory(), location.getNamespace(), location.getPath()));
+ .forEach(entry -> output.accept(new ResourceLocation(namespace, entry.getKey().path), entry.getValue()));
}
@Override
@@ -139,7 +146,7 @@ public class DynamicResourcePack implements PackResources
}
@Override
- public String getName()
+ public String packId()
{
return packName;
}
diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/GuiHelper.java b/src/main/java/com/anthonyhilyard/iceberg/util/GuiHelper.java
index 73800cb..1421c37 100644
--- a/src/main/java/com/anthonyhilyard/iceberg/util/GuiHelper.java
+++ b/src/main/java/com/anthonyhilyard/iceberg/util/GuiHelper.java
@@ -7,7 +7,7 @@ import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat;
-import com.mojang.math.Matrix4f;
+import org.joml.Matrix4f;
public class GuiHelper
{
diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java b/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java
index b21c168..df9e292 100644
--- a/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java
+++ b/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java
@@ -9,7 +9,7 @@ import java.util.function.BiPredicate;
import java.util.List;
import net.minecraft.client.Minecraft;
-import net.minecraft.core.Registry;
+import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.Tag;
@@ -87,14 +87,17 @@ public class Selectors
public static List<SelectorDocumentation> selectorDocumentation()
{
return Arrays.asList(
- new SelectorDocumentation("Item name", "Use item name for vanilla items or include mod name for modded items.", "minecraft:stick", "iron_ore"),
- new SelectorDocumentation("Tag", "$ followed by tag name.", "$forge:stone", "$planks"),
- new SelectorDocumentation("Mod name", "@ followed by mod identifier.", "@spoiledeggs"),
- new SelectorDocumentation("Rarity", "! followed by item's rarity. This is ONLY vanilla rarities.", "!uncommon", "!rare", "!epic"),
- new SelectorDocumentation("Item name color", "# followed by color hex code, the hex code must match exactly.", "#23F632"),
- new SelectorDocumentation("Display name", "% followed by any text. Will match any item with this text in its tooltip display name.", "%Netherite", "%[Uncommon]"),
- new SelectorDocumentation("Tooltip text", "Will match any item with this text anywhere in the tooltip text (besides the name).", "^Legendary"),
- new SelectorDocumentation("NBT tag", "& followed by tag name and optional comparator (=, >, <, or !=) and value, in the format <tag><comparator><value> or just <tag>.", "&Damage=0", "&Tier>1", "&map!=128", "&Enchantments")
+ new SelectorDocumentation("Match all", "Specifying just an asterisk (*) will match all items.", "*"),
+ new SelectorDocumentation("Item ID", "Use item ID to match single items. Must include mod name for modded items.", "minecraft:stick", "iron_ore", "spoiledeggs:spoiled_egg"),
+ new SelectorDocumentation("Tag", "$ followed by tag name to match all items with that tag.", "$forge:stone", "$planks"),
+ new SelectorDocumentation("Mod name", "@ followed by mod identifier to match all items from that mod.", "@spoiledeggs"),
+ new SelectorDocumentation("Rarity", "! followed by item's rarity to match all items with that rarity. This is ONLY vanilla rarities.", "!uncommon", "!rare", "!epic"),
+ new SelectorDocumentation("Item name color", "# followed by color hex code, to match all items with that exact color item name.", "#23F632"),
+ new SelectorDocumentation("Display name", "% followed by any text. Will match any item with this text (case-sensitive) in its tooltip display name.", "%Netherite", "%Uncommon"),
+ new SelectorDocumentation("Tooltip text", "^ followed by any text. Will match any item with this text (case-sensitive) anywhere in the tooltip text (besides the name).", "^Legendary"),
+ new SelectorDocumentation("NBT tag", "& followed by tag name and optional comparator (=, >, <, or !=) and value, in the format <tag><comparator><value> or just <tag>.", "&Damage=0", "&Tier>1", "&map!=128", "&Enchantments"),
+ new SelectorDocumentation("Negation", "~ followed by any selector above. This selector will be negated, matching every item that does NOT match the selector.", "~minecraft:stick", "~!uncommon", "~@minecraft"),
+ new SelectorDocumentation("Combining selectors", "Any number of selectors can be combined by separating them with a plus sign.", "minecraft:diamond_sword+&Enchantments", "minecraft:stick+~!common+&Damage=0")
);
}
@@ -105,6 +108,25 @@ public class Selectors
*/
public static boolean validateSelector(String value)
{
+ // First check if this is a combination of selectors.
+ if (value.contains("+"))
+ {
+ for (String selector : value.split("\\+"))
+ {
+ if (!validateSelector(selector))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // If this is a negation, remove the ~ and validate the rest.
+ if (value.startsWith("~"))
+ {
+ return validateSelector(value.substring(1));
+ }
+
// This is a tag, which should be a resource location.
if (value.startsWith("$"))
{
@@ -151,12 +173,45 @@ public class Selectors
@SuppressWarnings({"deprecation", "removal"})
public static boolean itemMatches(ItemStack item, String selector)
{
- String itemResourceLocation = ForgeRegistries.ITEMS.getKey(item.getItem()).toString();
+ // If this is a combination of selectors, check each one.
+ if (selector.contains("+"))
+ {
+ for (String subSelector : selector.split("\\+"))
+ {
+ if (!itemMatches(item, subSelector))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // If this is a negation, remove the ~ and check the rest.
+ if (selector.startsWith("~"))
+ {
+ return !itemMatches(item, selector.substring(1));
+ }
+
+ // Wildcard
+ if (selector.contentEquals("*"))
+ {
+ return true;
+ }
+
// Item ID
+ String itemResourceLocation = ForgeRegistries.ITEMS.getKey(item.getItem()).toString();
if (selector.equals(itemResourceLocation) || selector.equals(itemResourceLocation.replace("minecraft:", "")))
{
return true;
}
+ // Mod ID
+ else if (selector.startsWith("@"))
+ {
+ if (itemResourceLocation.startsWith(selector.substring(1) + ":"))
+ {
+ return true;
+ }
+ }
// Item name color
else if (selector.startsWith("#"))
{
@@ -174,18 +229,10 @@ public class Selectors
return true;
}
}
- // Mod ID
- else if (selector.startsWith("@"))
- {
- if (itemResourceLocation.startsWith(selector.substring(1) + ":"))
- {
- return true;
- }
- }
// Item tag
else if (selector.startsWith("$"))
{
- Optional<TagKey<Item>> matchingTag = Registry.ITEM.getTagNames().filter(tagKey -> tagKey.location().equals(new ResourceLocation(selector.substring(1)))).findFirst();
+ Optional<TagKey<Item>> matchingTag = BuiltInRegistries.ITEM.getTagNames().filter(tagKey -> tagKey.location().equals(new ResourceLocation(selector.substring(1)))).findFirst();
if (matchingTag.isPresent() && item.is(matchingTag.get()))
{
return true;
diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java b/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java
index db952f8..47019f5 100644
--- a/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java
+++ b/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java
@@ -10,14 +10,25 @@ import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import com.anthonyhilyard.iceberg.Loader;
import com.anthonyhilyard.iceberg.events.GatherComponentsExtEvent;
import com.anthonyhilyard.iceberg.events.RenderTooltipExtEvent;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferBuilder;
+import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.DefaultVertexFormat;
+import com.mojang.blaze3d.vertex.Tesselator;
+import com.mojang.blaze3d.vertex.VertexFormat;
+import com.mojang.datafixers.util.Either;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTextTooltip;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
+import net.minecraft.client.gui.screens.inventory.tooltip.TooltipRenderUtil;
+import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.client.renderer.MultiBufferSource.BufferSource;
@@ -26,23 +37,52 @@ import net.minecraft.locale.Language;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.network.chat.Style;
+import net.minecraft.network.chat.TextColor;
import net.minecraft.util.FormattedCharSequence;
-import com.mojang.blaze3d.vertex.Tesselator;
-import com.mojang.datafixers.util.Either;
-
import net.minecraft.world.inventory.tooltip.TooltipComponent;
import net.minecraft.world.item.ItemStack;
-import com.mojang.math.Matrix4f;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.event.RenderTooltipEvent;
+import net.minecraftforge.client.event.RegisterClientTooltipComponentFactoriesEvent;
import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.joml.Matrix4f;
public class Tooltips
{
+ public record TooltipColors(TextColor backgroundColorStart, TextColor backgroundColorEnd, TextColor borderColorStart, TextColor borderColorEnd) {}
+ private static final TooltipColors DEFAULT_COLORS = new TooltipColors(TextColor.fromRgb(0xF0100010), TextColor.fromRgb(0xF0100010), TextColor.fromRgb(0x505000FF), TextColor.fromRgb(0x5028007F));
+
private static final FormattedCharSequence SPACE = FormattedCharSequence.forward(" ", Style.EMPTY);
private static ItemRenderer itemRenderer = null;
+ private static boolean tooltipWidthWarningShown = false;
+
+ public static TooltipColors currentColors = DEFAULT_COLORS;
+
+ public static class TitleBreakComponent implements TooltipComponent, ClientTooltipComponent
+ {
+ @Override
+ public int getHeight() { return 0; }
+
+ @Override
+ public int getWidth(Font font) { return 0; }
+
+ public static void registerFactory()
+ {
+ FMLJavaModLoadingContext.get().getModEventBus().addListener(TitleBreakComponent::onRegisterTooltipEvent);
+ }
+
+ private static void onRegisterTooltipEvent(RegisterClientTooltipComponentFactoriesEvent event)
+ {
+ event.register(TitleBreakComponent.class, x -> x);
+ }
+ }
+
+ public static interface InlineComponent { }
public static class TooltipInfo
{
@@ -51,10 +91,12 @@ public class Tooltips
private Font font;
private List<ClientTooltipComponent> components = new ArrayList<>();
- public TooltipInfo(List<ClientTooltipComponent> components, Font font)
+ public TooltipInfo(List<ClientTooltipComponent> components, Font font, int titleLines)
{
this.components = components;
this.font = font;
+ this.titleLines = titleLines;
+ this.tooltipWidth = getMaxLineWidth();
}
public int getTooltipWidth() { return tooltipWidth; }
@@ -85,6 +127,39 @@ public class Tooltips
}
}
+ public static int calculateTitleLines(List<ClientTooltipComponent> components)
+ {
+ if (components == null || components.isEmpty())
+ {
+ return 0;
+ }
+
+ // Determine the number of "title lines". This will be the number of text components before the first TitleBreakComponent.
+ // If for some reason there is no TitleBreakComponent, we'll default to 1.
+ int titleLines = 0;
+ boolean foundTitleBreak = false;
+ for (ClientTooltipComponent component : components)
+ {
+ if (component instanceof ClientTextTooltip)
+ {
+ titleLines++;
+ }
+ else if (component instanceof TitleBreakComponent)
+ {
+ foundTitleBreak = true;
+ break;
+ }
+ }
+
+ // We didn't find a title break (shouldn't happen normally), so default to 1.
+ if (!foundTitleBreak)
+ {
+ titleLines = 1;
+ }
+
+ return titleLines;
+ }
+
public static void renderItemTooltip(@Nonnull final ItemStack stack, PoseStack poseStack, TooltipInfo info,
Rect2i rect, int screenWidth, int screenHeight,
int backgroundColor, int borderColorStart, int borderColorEnd)
@@ -125,7 +200,7 @@ public class Tooltips
// Center the title now if needed.
if (centeredTitle)
{
- info = new TooltipInfo(centerTitle(info.getComponents(), info.getFont(), rect.getWidth()), info.getFont());
+ info = new TooltipInfo(centerTitle(info.getComponents(), info.getFont(), info.getMaxLineWidth(), info.getTitleLines()), info.getFont(), info.getTitleLines());
}
int rectX = rect.getX() + 4;
@@ -148,7 +223,12 @@ public class Tooltips
final int zLevel = 400;
float f = itemRenderer.blitOffset;
itemRenderer.blitOffset = zLevel;
- Matrix4f mat = poseStack.last().pose();
+
+ Tesselator tesselator = Tesselator.getInstance();
+ BufferBuilder bufferbuilder = tesselator.getBuilder();
+ RenderSystem.setShader(GameRenderer::getPositionColorShader);
+ bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
+ Matrix4f matrix4f = poseStack.last().pose();
RenderTooltipExtEvent.Color colorEvent = new RenderTooltipExtEvent.Color(stack, poseStack, rectX, rectY, info.getFont(), backgroundColorStart, backgroundColorEnd, borderColorStart, borderColorEnd, info.getComponents(), comparison, index);
MinecraftForge.EVENT_BUS.post(colorEvent);
@@ -158,35 +238,41 @@ public class Tooltips
borderColorStart = colorEvent.getBorderStart();
borderColorEnd = colorEvent.getBorderEnd();
- GuiHelper.drawGradientRect(mat, zLevel, rectX - 3, rectY - 4, rectX + rect.getWidth() + 3, rectY - 3, backgroundColorStart, backgroundColorStart);
- GuiHelper.drawGradientRect(mat, zLevel, rectX - 3, rectY + rect.getHeight() + 3, rectX + rect.getWidth() + 3, rectY + rect.getHeight() + 4, backgroundColorEnd, backgroundColorEnd);
- GuiHelper.drawGradientRect(mat, zLevel, rectX - 3, rectY - 3, rectX + rect.getWidth() + 3, rectY + rect.getHeight() + 3, backgroundColorStart, backgroundColorEnd);
- GuiHelper.drawGradientRect(mat, zLevel, rectX - 4, rectY - 3, rectX - 3, rectY + rect.getHeight() + 3, backgroundColorStart, backgroundColorEnd);
- GuiHelper.drawGradientRect(mat, zLevel, rectX + rect.getWidth() + 3, rectY - 3, rectX + rect.getWidth() + 4, rectY + rect.getHeight() + 3, backgroundColorStart, backgroundColorEnd);
- GuiHelper.drawGradientRect(mat, zLevel, rectX - 3, rectY - 3 + 1, rectX - 3 + 1, rectY + rect.getHeight() + 3 - 1, borderColorStart, borderColorEnd);
- GuiHelper.drawGradientRect(mat, zLevel, rectX + rect.getWidth() + 2, rectY - 3 + 1, rectX + rect.getWidth() + 3, rectY + rect.getHeight() + 3 - 1, borderColorStart, borderColorEnd);
- GuiHelper.drawGradientRect(mat, zLevel, rectX - 3, rectY - 3, rectX + rect.getWidth() + 3, rectY - 3 + 1, borderColorStart, borderColorStart);
- GuiHelper.drawGradientRect(mat, zLevel, rectX - 3, rectY + rect.getHeight() + 2, rectX + rect.getWidth() + 3, rectY + rect.getHeight() + 3, borderColorEnd, borderColorEnd);
+ currentColors = new TooltipColors(TextColor.fromRgb(backgroundColorStart), TextColor.fromRgb(backgroundColorEnd), TextColor.fromRgb(borderColorStart), TextColor.fromRgb(borderColorEnd));
+
+ TooltipRenderUtil.renderTooltipBackground((matrix, bufferBuilder, left, top, right, bottom, z, startColor, endColor) -> {
+ GuiHelper.drawGradientRect(matrix, bufferBuilder, left, top, right, bottom, z, startColor, endColor);
+ }, matrix4f, bufferbuilder, rectX, rectY, rect.getWidth(), rect.getHeight(), zLevel);
- BufferSource renderType = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
- poseStack.translate(0.0D, 0.0D, zLevel);
+ RenderSystem.enableDepthTest();
+ RenderSystem.disableTexture();
+ RenderSystem.enableBlend();
+ RenderSystem.defaultBlendFunc();
+ BufferUploader.drawWithShader(bufferbuilder.end());
+ RenderSystem.disableBlend();
+ RenderSystem.enableTexture();
+ BufferSource bufferSource = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
+ poseStack.translate(0.0f, 0.0f, zLevel);
int tooltipTop = rectY;
- boolean titleRendered = false;
+ int titleLines = info.getTitleLines();
for (int componentNumber = 0; componentNumber < info.getComponents().size(); ++componentNumber)
{
ClientTooltipComponent textComponent = info.getComponents().get(componentNumber);
- textComponent.renderText(preEvent.getFont(), rectX, tooltipTop, mat, renderType);
+ textComponent.renderText(preEvent.getFont(), rectX, tooltipTop, matrix4f, bufferSource);
tooltipTop += textComponent.getHeight();
- if (!titleRendered && textComponent instanceof ClientTextTooltip)
+ if ((textComponent instanceof ClientTextTooltip || textComponent instanceof InlineComponent) && titleLines > 0)
{
- tooltipTop += 2;
- titleRendered = true;
+ titleLines -= (textComponent instanceof InlineComponent) ? 2 : 1;
+ if (titleLines <= 0)
+ {
+ tooltipTop += 2;
+ }
}
}
- renderType.endBatch();
+ bufferSource.endBatch();
poseStack.popPose();
tooltipTop = rectY;
@@ -226,7 +312,22 @@ public class Tooltips
}
int tooltipTextWidth = event.getTooltipElements().stream()
- .mapToInt(either -> either.map(font::width, component -> 0))
+ .mapToInt(either -> either.map(component -> {
+ try
+ {
+ return font.width(component);
+ }
+ catch (Exception e)
+ {
+ // Log this exception, but only once.
+ if (!tooltipWidthWarningShown)
+ {
+ Loader.LOGGER.error("Error rendering tooltip component: \n" + ExceptionUtils.getStackTrace(e));
+ tooltipWidthWarningShown = true;
+ }
+ return 0;
+ }
+ }, component -> 0))
.max()
.orElse(0);
@@ -239,9 +340,13 @@ public class Tooltips
if (tooltipX < 4) // if the tooltip doesn't fit on the screen
{
if (mouseX > screenWidth / 2)
+ {
tooltipTextWidth = mouseX - 12 - 8;
+ }
else
+ {
tooltipTextWidth = screenWidth - 16 - mouseX;
+ }
needsWrap = true;
}
}
@@ -300,12 +405,24 @@ public class Tooltips
int tooltipTextWidth = minWidth;
int tooltipHeight = components.size() == 1 ? -2 : 0;
+ int titleLines = calculateTitleLines(components);
if (centeredTitle)
{
- components = centerTitle(components, font, minWidth);
+ // Calculate the current tooltip width prior to centering.
+ for (ClientTooltipComponent component : components)
+ {
+ int componentWidth = component.getWidth(event.getFont());
+ if (componentWidth > tooltipTextWidth)
+ {
+ tooltipTextWidth = componentWidth;
+ }
+ }
+ components = centerTitle(components, font, tooltipTextWidth, titleLines);
}
+ tooltipTextWidth = minWidth;
+
for (ClientTooltipComponent component : components)
{
int componentWidth = component.getWidth(event.getFont());
@@ -333,25 +450,20 @@ public class Tooltips
return rect;
}
- public static List<ClientTooltipComponent> centerTitle(List<ClientTooltipComponent> components, Font font, int minWidth)
+ public static List<ClientTooltipComponent> centerTitle(List<ClientTooltipComponent> components, Font font, int width)
{
- // Calculate tooltip width first.
- int tooltipWidth = minWidth;
+ return centerTitle(components, font, width, 1);
+ }
- for (ClientTooltipComponent clienttooltipcomponent : components)
+ public static List<ClientTooltipComponent> centerTitle(List<ClientTooltipComponent> components, Font font, int width, int titleLines)
+ {
+ List<ClientTooltipComponent> result = new ArrayList<>(components);
+
+ if (components.isEmpty() || titleLines <= 0 || titleLines >= components.size())
{
- if (clienttooltipcomponent == null)
- {
- return components;
- }
- int componentWidth = clienttooltipcomponent.getWidth(font);
- if (componentWidth > tooltipWidth)
- {
- tooltipWidth = componentWidth;
- }
+ return result;
}
- // TODO: If the title is multiple lines, we need to extend this for each one.
// Find the title component, which is the first text component.
int titleIndex = 0;
for (ClientTooltipComponent clienttooltipcomponent : components)
@@ -363,29 +475,30 @@ public class Tooltips
titleIndex++;
}
- if (titleIndex >= components.size())
+ for (int i = 0; i < titleLines; i++)
{
- titleIndex = 0;
- }
+ ClientTooltipComponent titleComponent = components.get(titleIndex + i);
- List<FormattedText> recomposedLines = StringRecomposer.recompose(List.of(components.get(titleIndex)));
- if (recomposedLines.isEmpty())
- {
- return components;
- }
+ if (titleComponent != null)
+ {
+ List<FormattedText> recomposedLines = StringRecomposer.recompose(List.of(titleComponent));
+ if (recomposedLines.isEmpty())
+ {
+ return components;
+ }
- List<ClientTooltipComponent> result = new ArrayList<>(components);
+ FormattedCharSequence title = Language.getInstance().getVisualOrder(recomposedLines.get(0));
- FormattedCharSequence title = Language.getInstance().getVisualOrder(recomposedLines.get(0));
- while (result.get(titleIndex).getWidth(font) < tooltipWidth)
- {
- title = FormattedCharSequence.fromList(List.of(SPACE, title, SPACE));
- if (title == null)
- {
- break;
+ while (ClientTooltipComponent.create(title).getWidth(font) < width)
+ {
+ title = FormattedCharSequence.fromList(List.of(SPACE, title, SPACE));
+ if (title == null)
+ {
+ break;
+ }
+ }
+ result.set(titleIndex + i, ClientTooltipComponent.create(title));
}
-
- result.set(titleIndex, ClientTooltipComponent.create(title));
}
return result;
}