diff options
Diffstat (limited to 'src/main/java/com/anthonyhilyard/iceberg/util')
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; } |
