Package: com.codex.composer.api.v1.tooltips
Canonical Name: com.codex.composer.api.v1.tooltips.*
Composer offers a system for dynamically registering tooltips for items. It is an alternate to the fabric tooltip API and completely bypasses it, though both can be used in parallel. Composer's systems allow you to specify a check to make sure they are relevant, and a modifier to show them. If that key combination is not pressed, it will show the required button to show it.
Classes
TooltipContext
TooltipContext is passed on each render pass to the dynamic tooltip, and contains the item stack and held keys. These keys are defined in it as three booleans, but there does exist a Modifier enum with values SHIFT, CTRL and ALT, and a function which takes a TargetingContext and returns the related boolean.
DynamicTooltip
DynamicTooltip is the interface you implement. Inheritors must implement:
/* ? if minecraft is 1.21.4 or below */void appendTooltip(TooltipContext context, List<Text> out);/* ? if minecraft is 1.21.5 or above */void appendTooltip(TooltipContext context, Consumer<Text> out);/* If false, the appendTooltip method will not be called at all. There is a configoption to override this accessible from mod menu or through /configure. */boolean isRelevant(TooltipContext context);/* Where to inject the tooltip. The available values for thischange based on what minecraft version you are using. */Location where();
Location
As mentioned above, specified where to inject the tooltip. The enum's values follow each other in order that they are in the item tooltip. Available values are:
enum Location {HEAD,AFTER_NAME,AFTER_MAP_ID,AFTER_ITEM_TOOLTIP,// if minecraft is 1.21 or aboveAFTER_JUKEBOX_PLAYABLE,AFTER_TRIM,//? if minecraft: >=1.20.6AFTER_STORED_ENCHANTMENTS,AFTER_ENCHANTMENTS,AFTER_DYED_COLOR,AFTER_LORE,AFTER_ATTRIBUTE_MODIFIERS,AFTER_UNBREAKABLE,/* if minecraft is 1.21 or above */AFTER_OMINOUS_BOTTLE_AMPLIFIER,AFTER_SUSPICIOUS_STEW_EFFECTS, /* This is 1.21+ too *//* if minecraft is 1.20.6 or above */AFTER_CAN_BREAK,AFTER_CAN_PLACE_ON,//? }AFTER_DURABILITY,AFTER_ITEM_ID,/* if minecraft is 1.20.6 or above */AFTER_COMPONENTS,/* if minecraft is 1.20.4 or below */AFTER_TAGS,AFTER_DISABLED_TEXT,/* if minecraft is 1.21 or above */AFTER_OPERATOR_WARNINGS,TAIL,/* This only appears in creative mode! */AFTER_CREATIVE_TOOLTIP}
Section & SectionBuilder
A "Section" represents a single collapsible block of tooltip content. It is constructed via SectionBuilder.create()1 and supports the following properties:
| Property | Description |
|---|---|
| title | A translation key for the section header. Pass an empty string to omit it. |
| details | Hint text 'details'. Gets passed through Text.translatable() and inserted into "Press <button> to show <details>" |
| keyCombo | A function that returns a Modifier from a TooltipContext |
| content | A function that returns a list of Text instances that is used as the "main" content |
| children | Nested sections in this one. These can have different keybinds, and are part of the content block. |
| titleFormat | Formatting applied to the title line |
| contentFormat | Formatting applied to content lines (Gray by default) |
| hiddenFormat | Formatting applied to the details hint (Gray by default). |
DynamicTooltipRegistry
Singleton registry used to store dynamic tooltips. Has it's own deferred registry, it is recommended to use that.
Creating a Tooltip
The simplest way to create a dynamic tooltip is through extending SimpleDynamicTooltip, as it directly integrates with sections. Implementing DynamicTooltip is possible, but not recommended.
public class ItemTooltip extends SimpleDynamicTooltip {@Overridepublic Section root() {return SectionBuilder.create().title("example.tooltip.title").keyCombo(ctx -> Modifier.SHIFT).content(ctx -> List.of(Text.translatable("example.tooltip.line1"),Text.translatable("example.tooltip.line2"))).build();}@Overridepublic boolean isRelevant(TooltipContext context) {return context.stack.isOf(ModItems.EXAMPLE_ITEM);}@Overridepublic Location where() {return Location.AFTER_NAME;}}
Then, if you like simple things, use:
DeferredDynamicTooltipRegistry REGISTRY = new DeferredDynamicTooltipRegistry(ExampleMod.MOD_ID);REGISTRY.add("tooltip", ItemTooltip::new);
Or, alternatively use the registry directly:
DynamicTooltipRegistry.getInstance().register(Identifier.of(ExampleMod.MOD_ID, "tooltip"), new MyItemTooltip());
Example Implementation
From Composer
public class SoulboundTooltip extends SimpleDynamicTooltip {@Overridepublic Section root() {Section droppable = SectionBuilder.create().title("").keyCombo(ctx -> Modifier.ALT).content(ctx -> List.of(Text.translatable(LanguageUtils.negate("composer.tooltips.soulbound.droppable", SoulboundComponent.canDropSoulbound(ctx.stack))))) /* "This item is (not) soulbound" */.build();return SectionBuilder.create().title("").details(Text.translatable("composer.tooltips.soulbound.details")).keyCombo(ctx -> SoulboundComponent.isSoulbound(ctx.stack) ? null : Modifier.SHIFT).content(ctx -> List.of(Text.translatable(LanguageUtils.negate("composer.tooltips.soulbound", SoulboundComponent.isSoulbound(ctx.stack))))) /* "This item is (not) droppable when soulbound." */.children().push(droppable).end().build();}@Overridepublic boolean isRelevant(TooltipContext context) {return SoulboundComponent.isSoulbound(context.stack); // Hide if not soulbound}@Overridepublic Location where() {return Location.AFTER_NAME;}}
Footnotes
-
SectionBuilder is generated by Constructive, don't come complaining that it's missing methods (though I did make Constructive but ignore that) ↩