diff options
14 files changed, 207 insertions, 72 deletions
diff --git a/common/src/main/java/dev/isxander/yacl/api/Option.java b/common/src/main/java/dev/isxander/yacl/api/Option.java index 5f66c19..f0b595d 100644 --- a/common/src/main/java/dev/isxander/yacl/api/Option.java +++ b/common/src/main/java/dev/isxander/yacl/api/Option.java @@ -129,8 +129,21 @@ public interface Option<T> { */ Builder<T> name(@NotNull Component name); + /** + * Sets the description to be used by the option. + * @see OptionDescription + * @param description the static description. + * @return this builder + */ Builder<T> description(@NotNull OptionDescription description); + /** + * Sets the function to get the description by the option's current value. + * + * @see OptionDescription + * @param descriptionFunction the function to get the description by the option's current value. + * @return this builder + */ Builder<T> description(@NotNull Function<T, OptionDescription> descriptionFunction); /** diff --git a/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java b/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java index e5ed0b6..0b91d5a 100644 --- a/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java +++ b/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java @@ -2,6 +2,7 @@ package dev.isxander.yacl.api; import dev.isxander.yacl.gui.ImageRenderer; import dev.isxander.yacl.impl.OptionDescriptionImpl; +import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; @@ -9,32 +10,134 @@ import java.nio.file.Path; import java.util.Collection; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; +/** + * Provides all information for the description panel in the GUI. + * This provides no functional benefit, and is purely for UX. + */ public interface OptionDescription { - Component descriptiveName(); - + /** + * The description of the option, this is automatically wrapped and supports all styling, + * including {@link net.minecraft.network.chat.ClickEvent}s and {@link net.minecraft.network.chat.HoverEvent}s. + * @return The description of the option, with all lines merged with \n. + */ Component description(); + /** + * The image to display with the description. If the Optional is empty, no image has been provided. + * Usually, the image renderers are constructed asynchronously, so this method returns a {@link CompletableFuture}. + * <p> + * Image renderers are cached throughout the whole lifecycle of the game, and should not be generated more than once + * per image. See {@link ImageRenderer#getOrMakeAsync(ResourceLocation, Supplier)} for implementation details. + */ CompletableFuture<Optional<ImageRenderer>> image(); + /** + * @return a new builder for an {@link OptionDescription}. + */ static Builder createBuilder() { return new OptionDescriptionImpl.BuilderImpl(); } - interface Builder { - Builder name(Component name); + static OptionDescription empty() { + return new OptionDescriptionImpl(CommonComponents.EMPTY, CompletableFuture.completedFuture(Optional.empty())); + } + interface Builder { + /** + * Appends lines to the main description of the option. This can be called multiple times. + * On {@link Builder#build()}, the lines are merged with \n. + * @see OptionDescription#description() + * + * @param description the lines to append to the description. + * @return this builder + */ Builder description(Component... description); + + /** + * Appends lines to the main description of the option. This can be called multiple times. + * On {@link Builder#build()}, the lines are merged with \n. + * @see OptionDescription#description() + * + * @param lines the lines to append to the description. + * @return this builder + */ Builder description(Collection<? extends Component> lines); + /** + * Sets a static image to display with the description. This is backed by a regular minecraft resource + * in your mod's /assets folder. + * + * @param image the location of the image to display from the resource manager + * @param width the width of the texture + * @param height the height of the texture + * @return this builder + */ Builder image(ResourceLocation image, int width, int height); + + /** + * Sets a static image to display with the description. This is backed by a regular minecraft resource + * in your mod's /assets folder. This overload method allows you to specify a subsection of the texture to render. + * + * @param image the location of the image to display from the resource manager + * @param u the u coordinate + * @param v the v coordinate + * @param width the width of the subsection + * @param height the height of the subsection + * @param textureWidth the width of the whole texture file + * @param textureHeight the height of whole texture file + * @return this builder + */ + Builder image(ResourceLocation image, float u, float v, int width, int height, int textureWidth, int textureHeight); + + /** + * Sets a static image to display with the description. This is backed by a file on disk. + * The width and height is automatically determined from the image processing. + * + * @param path the absolute path to the image file + * @param uniqueLocation the unique identifier for the image, used for caching and resource manager registrar + * @return this builder + */ Builder image(Path path, ResourceLocation uniqueLocation); + /** + * Sets a static OR ANIMATED webP image to display with the description. This is backed by a regular minecraft resource + * in your mod's /assets folder. + * + * @param image the location of the image to display from the resource manager + * @return this builder + */ Builder webpImage(ResourceLocation image); + + /** + * Sets a static OR ANIMATED webP image to display with the description. This is backed by a file on disk. + * The width and height is automatically determined from the image processing. + * + * @param path the absolute path to the image file + * @param uniqueLocation the unique identifier for the image, used for caching and resource manager registrar + * @return this builder + */ Builder webpImage(Path path, ResourceLocation uniqueLocation); + /** + * Sets an animated GIF image to display with the description. This is backed by a regular minecraft resource + * in your mod's /assets folder. + * + * @param image the location of the image to display from the resource manager + * @return this builder + */ @Deprecated Builder gifImage(ResourceLocation image); + + /** + * Sets an animated GIF image to display with the description. This is backed by a file on disk. + * The width and height is automatically determined from the image processing. + * + * @param path the absolute path to the image file + * @param uniqueLocation the unique identifier for the image, used for caching and resource manager registrar + * @return this builder + */ @Deprecated Builder gifImage(Path path, ResourceLocation uniqueLocation); diff --git a/common/src/main/java/dev/isxander/yacl/gui/DescriptionWithName.java b/common/src/main/java/dev/isxander/yacl/gui/DescriptionWithName.java new file mode 100644 index 0000000..c29e0ca --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl/gui/DescriptionWithName.java @@ -0,0 +1,11 @@ +package dev.isxander.yacl.gui; + +import dev.isxander.yacl.api.OptionDescription; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; + +public record DescriptionWithName(Component name, OptionDescription description) { + public static DescriptionWithName of(Component name, OptionDescription description) { + return new DescriptionWithName(name.copy().withStyle(ChatFormatting.BOLD), description); + } +} diff --git a/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java b/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java index 1c9ac2e..26aacb7 100644 --- a/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java +++ b/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java @@ -53,11 +53,17 @@ public interface ImageRenderer { class TextureBacked implements ImageRenderer { private final ResourceLocation location; private final int width, height; + private final int textureWidth, textureHeight; + private final float u, v; - public TextureBacked(ResourceLocation location, int width, int height) { + public TextureBacked(ResourceLocation location, float u, float v, int width, int height, int textureWidth, int textureHeight) { this.location = location; this.width = width; this.height = height; + this.textureWidth = textureWidth; + this.textureHeight = textureHeight; + this.u = u; + this.v = v; } @Override @@ -68,7 +74,7 @@ public interface ImageRenderer { graphics.pose().pushPose(); graphics.pose().translate(x, y, 0); graphics.pose().scale(ratio, ratio, 1); - graphics.blit(location, 0, 0, 0, 0, this.width, this.height, this.width, this.height); + graphics.blit(location, 0, 0, this.u, this.v, this.width, this.height, this.textureWidth, this.textureHeight); graphics.pose().popPose(); return targetHeight; @@ -86,7 +92,7 @@ public interface ImageRenderer { protected NativeImage image; protected DynamicTexture texture; protected final ResourceLocation uniqueLocation; - protected int width, height; + protected final int width, height; public NativeImageBacked(NativeImage image, ResourceLocation uniqueLocation) { this.image = image; @@ -144,11 +150,11 @@ public interface ImageRenderer { private int currentFrame; private double lastFrameTime; - private double frameDelay; - private int frameCount; + private final double frameDelay; + private final int frameCount; - private int packCols, packRows; - private int frameWidth, frameHeight; + private final int packCols, packRows; + private final int frameWidth, frameHeight; public AnimatedNativeImageBacked(NativeImage image, int frameWidth, int frameHeight, int frameCount, double frameDelayMS, int packCols, int packRows, ResourceLocation uniqueLocation) { super(image, uniqueLocation); @@ -196,24 +202,26 @@ public interface ImageRenderer { ImageReader reader = new WebPImageReaderSpi().createReaderInstance(); reader.setInput(ImageIO.createImageInputStream(is)); - // WebP reader does not expose frame delay, prepare for reflection hell - int frameDelayMS; - reader.getNumImages(true); // Force reading of all frames - try { - Class<?> webpReaderClass = Class.forName("com.twelvemonkeys.imageio.plugins.webp.WebPImageReader"); - Field framesField = webpReaderClass.getDeclaredField("frames"); - framesField.setAccessible(true); - List<?> frames = (List<?>) framesField.get(reader); - Object firstFrame = frames.get(0); - - Class<?> animationFrameClass = Class.forName("com.twelvemonkeys.imageio.plugins.webp.AnimationFrame"); - Field durationField = animationFrameClass.getDeclaredField("duration"); - durationField.setAccessible(true); - frameDelayMS = (int) durationField.get(firstFrame); - } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); + int frameDelayMS = 0; + int numImages = reader.getNumImages(true); // Force reading of all frames + if (numImages > 1) { + // WebP reader does not expose frame delay, prepare for reflection hell + try { + Class<?> webpReaderClass = Class.forName("com.twelvemonkeys.imageio.plugins.webp.WebPImageReader"); + Field framesField = webpReaderClass.getDeclaredField("frames"); + framesField.setAccessible(true); + List<?> frames = (List<?>) framesField.get(reader); + Object firstFrame = frames.get(0); + + Class<?> animationFrameClass = Class.forName("com.twelvemonkeys.imageio.plugins.webp.AnimationFrame"); + Field durationField = animationFrameClass.getDeclaredField("duration"); + durationField.setAccessible(true); + frameDelayMS = (int) durationField.get(firstFrame); + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + // that was fun } - // that was fun return createFromImageReader(reader, frameDelayMS, uniqueLocation); } catch (IOException e) { @@ -301,13 +309,15 @@ public interface ImageRenderer { ); graphics.pose().popPose(); - double timeMS = Blaze3D.getTime() * 1000; - if (lastFrameTime == 0) lastFrameTime = timeMS; - if (timeMS - lastFrameTime >= frameDelay) { - currentFrame++; - lastFrameTime = timeMS; + if (frameCount > 1) { + double timeMS = Blaze3D.getTime() * 1000; + if (lastFrameTime == 0) lastFrameTime = timeMS; + if (timeMS - lastFrameTime >= frameDelay) { + currentFrame++; + lastFrameTime = timeMS; + } + if (currentFrame >= frameCount - 1) currentFrame = 0; } - if (currentFrame >= frameCount - 1) currentFrame = 0; return targetHeight; } diff --git a/common/src/main/java/dev/isxander/yacl/gui/OptionDescriptionWidget.java b/common/src/main/java/dev/isxander/yacl/gui/OptionDescriptionWidget.java index 5c346d0..4d86048 100644 --- a/common/src/main/java/dev/isxander/yacl/gui/OptionDescriptionWidget.java +++ b/common/src/main/java/dev/isxander/yacl/gui/OptionDescriptionWidget.java @@ -22,7 +22,7 @@ public class OptionDescriptionWidget extends AbstractWidget { private static final int AUTO_SCROLL_TIMER = 3000; private static final float AUTO_SCROLL_SPEED = 1; - private @Nullable OptionDescription description; + private @Nullable DescriptionWithName description; private List<FormattedCharSequence> wrappedText; private static final Minecraft minecraft = Minecraft.getInstance(); @@ -37,8 +37,8 @@ public class OptionDescriptionWidget extends AbstractWidget { private int lastInteractionTime; private boolean scrollingBackward; - public OptionDescriptionWidget(Supplier<ScreenRectangle> dimensions, @Nullable OptionDescription description) { - super(0, 0, 0, 0, description == null ? Component.empty() : description.descriptiveName()); + public OptionDescriptionWidget(Supplier<ScreenRectangle> dimensions, @Nullable DescriptionWithName description) { + super(0, 0, 0, 0, description == null ? Component.empty() : description.name()); this.dimensions = dimensions; this.setOptionDescription(description); } @@ -57,11 +57,11 @@ public class OptionDescriptionWidget extends AbstractWidget { int y = getY(); - int nameWidth = font.width(description.descriptiveName()); + int nameWidth = font.width(description.name()); if (nameWidth > getWidth()) { - renderScrollingString(graphics, font, description.descriptiveName(), getX(), y, getX() + getWidth(), y + font.lineHeight, -1); + renderScrollingString(graphics, font, description.name(), getX(), y, getX() + getWidth(), y + font.lineHeight, -1); } else { - graphics.drawString(font, description.descriptiveName(), getX(), y, 0xFFFFFF); + graphics.drawString(font, description.name(), getX(), y, 0xFFFFFF); } y += 5 + font.lineHeight; @@ -70,16 +70,16 @@ public class OptionDescriptionWidget extends AbstractWidget { y -= (int)currentScrollAmount; - if (description.image().isDone()) { - var image = description.image().join(); + if (description.description().image().isDone()) { + var image = description.description().image().join(); if (image.isPresent()) { image.get().render(graphics, getX(), y, getWidth()); y += image.get().render(graphics, getX(), y, getWidth()) + 5; } } - if (wrappedText == null && description.description() != null) - wrappedText = font.split(description.description(), getWidth()); + if (wrappedText == null) + wrappedText = font.split(description.description().description(), getWidth()); descriptionY = y; for (var line : wrappedText) { @@ -187,7 +187,7 @@ public class OptionDescriptionWidget extends AbstractWidget { } - public void setOptionDescription(OptionDescription description) { + public void setOptionDescription(DescriptionWithName description) { this.description = description; this.wrappedText = null; this.targetScrollAmount = 0; diff --git a/common/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java b/common/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java index 98272d1..fb49578 100644 --- a/common/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java +++ b/common/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java @@ -27,10 +27,10 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr private final ConfigCategory category; private ImmutableList<Entry> viewableChildren; private String searchQuery = ""; - private final Consumer<OptionDescription> hoverEvent; - private OptionDescription lastHoveredOption; + private final Consumer<DescriptionWithName> hoverEvent; + private DescriptionWithName lastHoveredOption; - public OptionListWidget(YACLScreen screen, ConfigCategory category, Minecraft client, int x, int y, int width, int height, Consumer<OptionDescription> hoverEvent) { + public OptionListWidget(YACLScreen screen, ConfigCategory category, Minecraft client, int x, int y, int width, int height, Consumer<DescriptionWithName> hoverEvent) { super(client, x, y, width, height, true); this.yaclScreen = screen; this.category = category; @@ -237,7 +237,7 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr return ret; } - private void setHoverDescription(OptionDescription description) { + private void setHoverDescription(DescriptionWithName description) { if (description != lastHoveredOption) { lastHoveredOption = description; hoverEvent.accept(description); @@ -300,7 +300,7 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr } if (isHovered() || isFocused()) { - setHoverDescription(option.description()); + setHoverDescription(DescriptionWithName.of(option.name(), option.description())); } } @@ -398,7 +398,7 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr wrappedName.renderCentered(graphics, x + entryWidth / 2, y + getYPadding()); if (isHovered() || isFocused()) { - setHoverDescription(group.description()); + setHoverDescription(DescriptionWithName.of(group.name(), group.description())); } } diff --git a/common/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java index f0e0bdd..afb2cb5 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java @@ -217,7 +217,7 @@ public final class ButtonOptionImpl implements ButtonOption { concatenatedTooltip.append(line); } - return new ButtonOptionImpl(name, OptionDescription.createBuilder().name(name).description(concatenatedTooltip).build(), action, available, controlGetter); + return new ButtonOptionImpl(name, OptionDescription.createBuilder().description(concatenatedTooltip).build(), action, available, controlGetter); } } } diff --git a/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java b/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java index ed73174..e593ce4 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java @@ -111,7 +111,7 @@ public final class ConfigCategoryImpl implements ConfigCategory { Validate.notNull(name, "`name` must not be null to build `ConfigCategory`"); List<OptionGroup> combinedGroups = new ArrayList<>(); - combinedGroups.add(new OptionGroupImpl(Component.empty(), OptionDescription.createBuilder().name(Component.literal("Root")).build(), ImmutableList.copyOf(rootOptions), false, true)); + combinedGroups.add(new OptionGroupImpl(Component.empty(), OptionDescription.empty(), ImmutableList.copyOf(rootOptions), false, true)); combinedGroups.addAll(groups); Validate.notEmpty(combinedGroups, "at least one option must be added to build `ConfigCategory`"); diff --git a/common/src/main/java/dev/isxander/yacl/impl/LabelOptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/LabelOptionImpl.java index 2a7759c..a32121d 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/LabelOptionImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/LabelOptionImpl.java @@ -29,7 +29,6 @@ public final class LabelOptionImpl implements LabelOption { this.labelController = new LabelController(this); this.binding = Binding.immutable(label); this.description = OptionDescription.createBuilder() - .name(this.name) .description(this.label) .build(); } diff --git a/common/src/main/java/dev/isxander/yacl/impl/ListOptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/ListOptionImpl.java index a5536a4..c21e733 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/ListOptionImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/ListOptionImpl.java @@ -345,7 +345,7 @@ public final class ListOptionImpl<T> implements ListOption<T> { if (ensureLegacyDescriptionBuilder()) YACLConstants.LOGGER.warn("Using deprecated `tooltip` method in list option {}. Use `description` instead.", name.getString()); - description = legacyBuilder.name(name).build(); + description = legacyBuilder.build(); } return new ListOptionImpl<>(name, description, binding, initialValue, typeClass, controllerFunction, ImmutableSet.copyOf(flags), collapsed, available, listeners); diff --git a/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java index 5d09828..0b2853f 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java @@ -14,20 +14,13 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.CompletableFuture; -public record OptionDescriptionImpl(Component descriptiveName, Component description, CompletableFuture<Optional<ImageRenderer>> image) implements OptionDescription { +public record OptionDescriptionImpl(Component description, CompletableFuture<Optional<ImageRenderer>> image) implements OptionDescription { public static class BuilderImpl implements Builder { - private Component name; private final List<Component> descriptionLines = new ArrayList<>(); private CompletableFuture<Optional<ImageRenderer>> image = CompletableFuture.completedFuture(Optional.empty()); private boolean imageUnset = true; @Override - public Builder name(Component name) { - this.name = name; - return this; - } - - @Override public Builder description(Component... description) { this.descriptionLines.addAll(Arrays.asList(description)); return this; @@ -45,7 +38,18 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip Validate.isTrue(width > 0, "Width must be greater than 0!"); Validate.isTrue(height > 0, "Height must be greater than 0!"); - this.image = ImageRenderer.getOrMakeSync(image, () -> Optional.of(new ImageRenderer.TextureBacked(image, width, height))); + this.image = ImageRenderer.getOrMakeSync(image, () -> Optional.of(new ImageRenderer.TextureBacked(image, 0, 0, width, height, width, height))); + imageUnset = false; + return this; + } + + @Override + public Builder image(ResourceLocation image, float u, float v, int width, int height, int textureWidth, int textureHeight) { + Validate.isTrue(imageUnset, "Image already set!"); + Validate.isTrue(width > 0, "Width must be greater than 0!"); + Validate.isTrue(height > 0, "Height must be greater than 0!"); + + this.image = ImageRenderer.getOrMakeSync(image, () -> Optional.of(new ImageRenderer.TextureBacked(image, u, v, width, height, textureWidth, textureHeight))); imageUnset = false; return this; } @@ -120,8 +124,6 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip @Override public OptionDescription build() { - Validate.notNull(name, "Name must be set!"); - MutableComponent concatenatedDescription = Component.empty(); Iterator<Component> iter = descriptionLines.iterator(); while (iter.hasNext()) { @@ -129,7 +131,7 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip if (iter.hasNext()) concatenatedDescription.append("\n"); } - return new OptionDescriptionImpl(name.copy().withStyle(ChatFormatting.BOLD), concatenatedDescription, image); + return new OptionDescriptionImpl(concatenatedDescription, image); } } } diff --git a/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java b/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java index 0649f16..2db8acd 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java @@ -133,7 +133,7 @@ public final class OptionGroupImpl implements OptionGroup { if (ensureLegacyDescriptionBuilder()) YACLConstants.LOGGER.warn("Using deprecated `tooltip` method in option group '{}'. Use `description` instead.", name != null ? name.getString() : "unnamed group"); - description = legacyBuilder.name(name).build(); + description = legacyBuilder.build(); } return new OptionGroupImpl(name, description, ImmutableList.copyOf(options), collapsed, false); diff --git a/common/src/main/java/dev/isxander/yacl/impl/OptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/OptionImpl.java index 98dcc23..ebb1226 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/OptionImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/OptionImpl.java @@ -312,7 +312,7 @@ public final class OptionImpl<T> implements Option<T> { return concatenatedTooltip; }; - descriptionFunction = opt -> OptionDescription.createBuilder().name(name).description(concatenatedTooltipGetter.apply(opt)).build(); + descriptionFunction = opt -> OptionDescription.createBuilder().description(concatenatedTooltipGetter.apply(opt)).build(); } return new OptionImpl<>(name, descriptionFunction, controlGetter, binding, available, ImmutableSet.copyOf(flags), typeClass, listeners); diff --git a/test-common/src/main/java/dev/isxander/yacl/test/GuiTest.java b/test-common/src/main/java/dev/isxander/yacl/test/GuiTest.java index 7175bcc..b0fe846 100644 --- a/test-common/src/main/java/dev/isxander/yacl/test/GuiTest.java +++ b/test-common/src/main/java/dev/isxander/yacl/test/GuiTest.java @@ -58,11 +58,9 @@ public class GuiTest { .tooltip(Component.literal("Example Category Description")) .group(OptionGroup.createBuilder() .name(Component.literal("Boolean Controllers")) - .tooltip(Component.literal("Test!")) .option(Option.createBuilder(boolean.class) .name(Component.literal("Boolean Toggle")) .description(OptionDescription.createBuilder() - .name(Component.literal("Boolean Toggle")) .description(Component.empty() .append(Component.literal("a").withStyle(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal("a"))))) .append(Component.literal("b").withStyle(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal("b"))))) @@ -83,7 +81,6 @@ public class GuiTest { .option(Option.createBuilder(boolean.class) .name(Component.literal("Custom Boolean Toggle")) .description(val -> OptionDescription.createBuilder() - .name(Component.literal("Custom Boolean Toggle " + val)) .description(Component.literal("You can customize controllers like so! YACL is truly infinitely customizable! This tooltip is long in order to demonstrate the cool, smooth scrolling of these descriptions. Did you know, they are also super clickable?! I know, cool right, YACL 3.x really is amazing.")) .image(Path.of("D:\\Xander\\Downloads\\_MG_0860-Enhanced-NR.png"), new ResourceLocation("yacl", "f.webp")) .build()) |