diff options
| author | isXander <xandersmith2008@gmail.com> | 2023-05-21 12:41:45 +0100 |
|---|---|---|
| committer | isXander <xandersmith2008@gmail.com> | 2023-05-21 12:41:45 +0100 |
| commit | 21afea0da3956f2d8cca81a54fa9820152e0c077 (patch) | |
| tree | e5944f94a5f85d3fcbe048da633e62f5357fe835 /common/src/main/java/dev/isxander/yacl | |
| parent | e51af159ba3eba5ebda976bea1c1957cddeee7c6 (diff) | |
| download | YetAnotherConfigLib-21afea0da3956f2d8cca81a54fa9820152e0c077.tar.gz YetAnotherConfigLib-21afea0da3956f2d8cca81a54fa9820152e0c077.tar.bz2 YetAnotherConfigLib-21afea0da3956f2d8cca81a54fa9820152e0c077.zip | |
Start overhauling UI
Diffstat (limited to 'common/src/main/java/dev/isxander/yacl')
18 files changed, 1118 insertions, 363 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 a6c0311..5f66c19 100644 --- a/common/src/main/java/dev/isxander/yacl/api/Option.java +++ b/common/src/main/java/dev/isxander/yacl/api/Option.java @@ -17,10 +17,13 @@ public interface Option<T> { */ @NotNull Component name(); + @NotNull OptionDescription description(); + /** * Tooltip (or description) of the option. * Rendered on hover. */ + @Deprecated @NotNull Component tooltip(); /** @@ -126,12 +129,17 @@ public interface Option<T> { */ Builder<T> name(@NotNull Component name); + Builder<T> description(@NotNull OptionDescription description); + + Builder<T> description(@NotNull Function<T, OptionDescription> descriptionFunction); + /** * Sets the tooltip to be used by the option. * No need to wrap the text yourself, the gui does this itself. * * @param tooltipGetter function to get tooltip depending on value {@link Builder#build()}. */ + @Deprecated Builder<T> tooltip(@NotNull Function<T, Component> tooltipGetter); /** @@ -150,6 +158,7 @@ public interface Option<T> { * * @param tooltips text lines - merged with a new-line on {@link Builder#build()}. */ + @Deprecated Builder<T> tooltip(@NotNull Component... tooltips); /** diff --git a/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java b/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java new file mode 100644 index 0000000..3b28a65 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java @@ -0,0 +1,39 @@ +package dev.isxander.yacl.api; + +import dev.isxander.yacl.gui.ImageRenderer; +import dev.isxander.yacl.impl.OptionDescriptionImpl; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import java.nio.file.Path; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +public interface OptionDescription { + Component descriptiveName(); + + Component description(); + + CompletableFuture<Optional<ImageRenderer>> image(); + + static Builder createBuilder() { + return new OptionDescriptionImpl.BuilderImpl(); + } + + interface Builder { + Builder name(Component name); + + Builder description(Component description); + + Builder image(ResourceLocation image, int width, int height); + Builder image(Path path, ResourceLocation uniqueLocation); + + Builder gifImage(ResourceLocation image); + Builder gifImage(Path path, ResourceLocation uniqueLocation); + + Builder webpImage(ResourceLocation image, int frameDelayMS); + Builder webpImage(Path path, ResourceLocation uniqueLocation, int frameDelayMS); + + OptionDescription build(); + } +} diff --git a/common/src/main/java/dev/isxander/yacl/gui/CategoryListWidget.java b/common/src/main/java/dev/isxander/yacl/gui/CategoryListWidget.java deleted file mode 100644 index 6668584..0000000 --- a/common/src/main/java/dev/isxander/yacl/gui/CategoryListWidget.java +++ /dev/null @@ -1,100 +0,0 @@ -package dev.isxander.yacl.gui; - -import com.google.common.collect.ImmutableList; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.PoseStack; -import dev.isxander.yacl.api.ConfigCategory; -import dev.isxander.yacl.gui.utils.GuiUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.narration.NarratableEntry; - -import java.util.List; - -public class CategoryListWidget extends ElementListWidgetExt<CategoryListWidget.CategoryEntry> { - private final YACLScreen yaclScreen; - - public CategoryListWidget(Minecraft client, YACLScreen yaclScreen, int screenWidth, int screenHeight) { - super(client, 0, 0, screenWidth / 3, yaclScreen.searchFieldWidget.getY() - 5, true); - this.yaclScreen = yaclScreen; - setRenderBackground(false); - setRenderTopAndBottom(false); - - for (ConfigCategory category : yaclScreen.config.categories()) { - addEntry(new CategoryEntry(category)); - } - } - - @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { - GuiUtils.enableScissor(0, 0, width, height); - super.render(graphics, mouseX, mouseY, delta); - RenderSystem.disableScissor(); - } - - @Override - public int getRowWidth() { - return Math.min(width - width / 10, 396); - } - - @Override - public int getRowLeft() { - return super.getRowLeft() - 2; - } - - @Override - protected int getScrollbarPosition() { - return width - 2; - } - - @Override - protected void renderBackground(GuiGraphics graphics) { - - } - - public class CategoryEntry extends Entry<CategoryEntry> { - private final CategoryWidget categoryButton; - public final int categoryIndex; - - public CategoryEntry(ConfigCategory category) { - this.categoryIndex = yaclScreen.config.categories().indexOf(category); - categoryButton = new CategoryWidget( - yaclScreen, - category, - categoryIndex, - getRowLeft(), 0, - getRowWidth(), 20 - ); - } - - @Override - public void render(GuiGraphics graphics, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - if (mouseY > y1) { - mouseY = -20; - } - - categoryButton.setY(y); - categoryButton.render(graphics, mouseX, mouseY, tickDelta); - } - - public void postRender(GuiGraphics graphics, int mouseX, int mouseY, float tickDelta) { - categoryButton.renderHoveredTooltip(graphics); - } - - @Override - public int getItemHeight() { - return 21; - } - - @Override - public List<? extends GuiEventListener> children() { - return ImmutableList.of(categoryButton); - } - - @Override - public List<? extends NarratableEntry> narratables() { - return ImmutableList.of(categoryButton); - } - } -} diff --git a/common/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java b/common/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java deleted file mode 100644 index 60817a2..0000000 --- a/common/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java +++ /dev/null @@ -1,38 +0,0 @@ -package dev.isxander.yacl.gui; - -import dev.isxander.yacl.api.ConfigCategory; -import net.minecraft.client.sounds.SoundManager; - -public class CategoryWidget extends TooltipButtonWidget { - private final int categoryIndex; - - public CategoryWidget(YACLScreen screen, ConfigCategory category, int categoryIndex, int x, int y, int width, int height) { - super(screen, x, y, width, height, category.name(), category.tooltip(), btn -> { - screen.searchFieldWidget.setValue(""); - screen.changeCategory(categoryIndex); - }); - this.categoryIndex = categoryIndex; - } - - private boolean isCurrentCategory() { - return ((YACLScreen) screen).getCurrentCategoryIdx() == categoryIndex; - } - - @Override - protected int getTextureY() { - int i = 1; - if (!this.active) { - i = 0; - } else if (this.isHoveredOrFocused() || isCurrentCategory()) { - i = 2; - } - - return 46 + i * 20; - } - - @Override - public void playDownSound(SoundManager soundManager) { - if (!isCurrentCategory()) - super.playDownSound(soundManager); - } -} diff --git a/common/src/main/java/dev/isxander/yacl/gui/ElementListWidgetExt.java b/common/src/main/java/dev/isxander/yacl/gui/ElementListWidgetExt.java index cb9fc87..ffeffbf 100644 --- a/common/src/main/java/dev/isxander/yacl/gui/ElementListWidgetExt.java +++ b/common/src/main/java/dev/isxander/yacl/gui/ElementListWidgetExt.java @@ -3,13 +3,18 @@ package dev.isxander.yacl.gui; import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.ContainerObjectSelectionList; import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.layouts.LayoutElement; +import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.util.Mth; import org.jetbrains.annotations.Nullable; -public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> extends ContainerObjectSelectionList<E> { - protected final int x, y; +import java.util.function.Consumer; + +public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> extends ContainerObjectSelectionList<E> implements LayoutElement { + protected int x, y; private double smoothScrollAmount = getScrollAmount(); private boolean returnSmoothAmount = false; @@ -33,9 +38,8 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten @Override protected void renderBackground(GuiGraphics graphics) { // render transparent background if in-game. - setRenderBackground(minecraft.level == null); - if (minecraft.level != null) - graphics.fill(x0, y0, x1, y1, 0x6B000000); + setRenderBackground(true); + setRenderTopAndBottom(false); } @Override @@ -48,10 +52,23 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { smoothScrollAmount = Mth.lerp(Minecraft.getInstance().getDeltaFrameTime() * 0.5, smoothScrollAmount, getScrollAmount()); returnSmoothAmount = true; + + graphics.enableScissor(x0, y0, x1, y1); + super.render(graphics, mouseX, mouseY, delta); + + graphics.disableScissor(); + returnSmoothAmount = false; } + public void updateDimensions(ScreenRectangle rectangle) { + this.x0 = rectangle.left(); + this.y0 = rectangle.top(); + this.x1 = rectangle.right(); + this.y1 = rectangle.bottom(); + } + /** * awful code to only use smooth scroll state when rendering, * not other code that needs target scroll amount @@ -141,6 +158,42 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten /* END cloth config code */ + @Override + public void setX(int i) { + this.x = x0 = i; + this.x1 = x0 + width; + } + + @Override + public void setY(int i) { + this.y = y0 = i; + this.y1 = y0 + height; + } + + @Override + public int getX() { + return x; + } + + @Override + public int getY() { + return y; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public void visitWidgets(Consumer<AbstractWidget> consumer) { + } + public abstract static class Entry<E extends Entry<E>> extends ContainerObjectSelectionList.Entry<E> { @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { diff --git a/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java b/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java new file mode 100644 index 0000000..8ea8ba3 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java @@ -0,0 +1,258 @@ +package dev.isxander.yacl.gui; + +import com.mojang.blaze3d.Blaze3D; +import com.mojang.blaze3d.platform.NativeImage; +import com.twelvemonkeys.imageio.plugins.webp.WebPImageReaderSpi; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.FastColor; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; +import java.awt.image.BufferedImage; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.IntStream; + +public interface ImageRenderer extends AutoCloseable { + int render(GuiGraphics graphics, int x, int y, int width); + + class TextureBacked implements ImageRenderer { + private final ResourceLocation location; + private final int width, height; + + public TextureBacked(ResourceLocation location, int width, int height) { + this.location = location; + this.width = width; + this.height = height; + } + + @Override + public int render(GuiGraphics graphics, int x, int y, int renderWidth) { + float ratio = renderWidth / (float)this.width; + int targetHeight = (int) (this.height * ratio); + + 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.pose().popPose(); + + return targetHeight; + } + + @Override + public void close() { + + } + } + + class NativeImageBacked implements ImageRenderer { + protected static final TextureManager textureManager = Minecraft.getInstance().getTextureManager(); + + protected NativeImage image; + protected DynamicTexture texture; + protected final ResourceLocation uniqueLocation; + protected int width, height; + + public NativeImageBacked(NativeImage image, ResourceLocation uniqueLocation) { + this.image = image; + this.texture = new DynamicTexture(image); + this.uniqueLocation = uniqueLocation; + textureManager.register(this.uniqueLocation, this.texture); + this.width = image.getWidth(); + this.height = image.getHeight(); + } + + private NativeImageBacked(Path imagePath, ResourceLocation uniqueLocation) throws IOException { + this.uniqueLocation = uniqueLocation; + this.image = NativeImage.read(new FileInputStream(imagePath.toFile())); + this.width = image.getWidth(); + this.height = image.getHeight(); + this.texture = new DynamicTexture(image); + textureManager.register(this.uniqueLocation, this.texture); + } + + public static Optional<ImageRenderer> createFromPath(Path path, ResourceLocation uniqueLocation) { + try { + return Optional.of(new NativeImageBacked(path, uniqueLocation)); + } catch (IOException e) { + e.printStackTrace(); + return Optional.empty(); + } + } + + @Override + public int render(GuiGraphics graphics, int x, int y, int renderWidth) { + if (image == null) return 0; + + float ratio = renderWidth / (float)this.width; + int targetHeight = (int) (this.height * ratio); + + graphics.pose().pushPose(); + graphics.pose().translate(x, y, 0); + graphics.pose().scale(ratio, ratio, 1); + graphics.blit(uniqueLocation, 0, 0, 0, 0, this.width, this.height, this.width, this.height); + graphics.pose().popPose(); + + return targetHeight; + } + + @Override + public void close() { + image.close(); + image = null; + texture = null; + textureManager.release(uniqueLocation); + } + } + + class AnimatedNativeImageBacked extends NativeImageBacked { + private int currentFrame; + private double lastFrameTime; + + private double frameDelay; + private int frameCount; + + private int packCols, packRows; + private int frameWidth, frameHeight; + + public AnimatedNativeImageBacked(NativeImage image, int frameWidth, int frameHeight, int frameCount, double frameDelayMS, int packCols, int packRows, ResourceLocation uniqueLocation) { + super(image, uniqueLocation); + this.frameWidth = frameWidth; + this.frameHeight = frameHeight; + this.frameCount = frameCount; + this.frameDelay = frameDelayMS; + this.packCols = packCols; + this.packRows = packRows; + } + + public static AnimatedNativeImageBacked createGIFFromTexture(ResourceLocation textureLocation) throws IOException { + ResourceManager resourceManager = Minecraft.getInstance().getResourceManager(); + Resource resource = resourceManager.getResource(textureLocation).orElseThrow(); + + return createGIF(resource.open(), textureLocation); + } + + public static AnimatedNativeImageBacked createWEBPFromTexture(ResourceLocation textureLocation, int frameDelayMS) throws IOException { + ResourceManager resourceManager = Minecraft.getInstance().getResourceManager(); + Resource resource = resourceManager.getResource(textureLocation).orElseThrow(); + + return createWEBP(resource.open(), textureLocation, frameDelayMS); + } + + public static AnimatedNativeImageBacked createGIF(InputStream is, ResourceLocation uniqueLocation) { + try (is) { + ImageReader reader = ImageIO.getImageReadersBySuffix("gif").next(); + reader.setInput(ImageIO.createImageInputStream(is)); + + IIOMetadata metadata = reader.getImageMetadata(0); + String metaFormatName = metadata.getNativeMetadataFormatName(); + IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(metaFormatName); + IIOMetadataNode graphicsControlExtensionNode = (IIOMetadataNode) root.getElementsByTagName("GraphicControlExtension").item(0); + int delay = Integer.parseInt(graphicsControlExtensionNode.getAttribute("delayTime")) * 10; + + return createFromImageReader(reader, delay, uniqueLocation); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static AnimatedNativeImageBacked createWEBP(InputStream is, ResourceLocation uniqueLocation, int frameDelayMS) { + try (is) { + ImageReader reader = ImageIO.getImageReadersBySuffix("webp").next(); + reader.setInput(ImageIO.createImageInputStream(is)); + return createFromImageReader(reader, frameDelayMS, uniqueLocation); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static AnimatedNativeImageBacked createFromImageReader(ImageReader reader, int frameDelayMS, ResourceLocation uniqueLocation) throws IOException { + int frameCount = reader.getNumImages(true); + + int frameWidth = reader.getWidth(0); + int frameHeight = reader.getHeight(0); + + // Packs the frames into an optimal 1:1 texture. + // OpenGL can only have texture axis with a max of 32768 pixels, + // and packing them to that length is not efficient, apparently. + double ratio = frameWidth / (double)frameHeight; + int cols = (int)Math.ceil(Math.sqrt(frameCount) / Math.sqrt(ratio)); + int rows = (int)Math.ceil(frameCount / (double)cols); + + NativeImage image = new NativeImage(frameWidth * cols, frameHeight * rows, true); + for (int i = reader.getMinIndex(); i < frameCount - 1; i++) { + BufferedImage bi = reader.read(i); + for (int w = 0; w < bi.getWidth(); w++) { + for (int h = 0; h < bi.getHeight(); h++) { + int rgb = bi.getRGB(w, h); + int r = FastColor.ARGB32.red(rgb); + int g = FastColor.ARGB32.green(rgb); + int b = FastColor.ARGB32.blue(rgb); + + int col = i % cols; + int row = (int) Math.floor(i / (double)cols); + + image.setPixelRGBA( + bi.getWidth() * col + w, + bi.getHeight() * row + h, + FastColor.ABGR32.color(255, b, g, r) // NativeImage uses ABGR for some reason + ); + } + } + } + image.upload(0, 0, 0, false); + + return new AnimatedNativeImageBacked(image, frameWidth, frameHeight, frameCount, frameDelayMS, cols, rows, uniqueLocation); + } + + @Override + public int render(GuiGraphics graphics, int x, int y, int renderWidth) { + if (image == null) return 0; + + float ratio = renderWidth / (float)frameWidth; + int targetHeight = (int) (frameHeight * ratio); + + int currentCol = currentFrame % packCols; + int currentRow = (int) Math.floor(currentFrame / (double)packCols); + + graphics.pose().pushPose(); + graphics.pose().translate(x, y, 0); + graphics.pose().scale(ratio, ratio, 1); + graphics.blit( + uniqueLocation, + 0, 0, + frameWidth * currentCol, frameHeight * currentRow, + frameWidth, frameHeight, + this.width, this.height + ); + graphics.pose().popPose(); + + double timeMS = Blaze3D.getTime() * 1000; + if (lastFrameTime == 0) lastFrameTime = timeMS; + if (timeMS - lastFrameTime >= frameDelay) { + currentFrame++; + lastFrameTime = timeMS; + } + if (currentFrame >= frameCount) 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 new file mode 100644 index 0000000..d9ad045 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl/gui/OptionDescriptionWidget.java @@ -0,0 +1,156 @@ +package dev.isxander.yacl.gui; + +import dev.isxander.yacl.api.OptionDescription; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.MultiLineLabel; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.util.Mth; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Supplier; + +public class OptionDescriptionWidget extends AbstractWidget implements AutoCloseable { + private @Nullable OptionDescription description; + private List<FormattedCharSequence> wrappedText; + + private static final Minecraft minecraft = Minecraft.getInstance(); + private static final Font font = minecraft.font; + + private Supplier<ScreenRectangle> dimensions; + + private int scrollAmount; + private int maxScrollAmount; + private int descriptionY; + + public OptionDescriptionWidget(Supplier<ScreenRectangle> dimensions, @Nullable OptionDescription description) { + super(0, 0, 0, 0, description == null ? Component.empty() : description.descriptiveName()); + this.dimensions = dimensions; + this.setOptionDescription(description); + } + + @Override + public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float delta) { + if (description == null) return; + + ScreenRectangle dimensions = this.dimensions.get(); + this.setX(dimensions.left()); + this.setY(dimensions.top()); + this.width = dimensions.width(); + this.height = dimensions.height(); + + int y = getY(); + + int nameWidth = font.width(description.descriptiveName()); + if (nameWidth > getWidth()) { + renderScrollingString(graphics, font, description.descriptiveName(), getX(), y, getX() + getWidth(), y + font.lineHeight, -1); + } else { + graphics.drawString(font, description.descriptiveName(), getX(), y, 0xFFFFFF); + } + + y += 5 + font.lineHeight; + + graphics.enableScissor(getX(), y, getX() + getWidth(), getY() + getHeight()); + + y -= scrollAmount; + + if (description.image().isDone()) { + var image = 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()); + + descriptionY = y; + for (var line : wrappedText) { + graphics.drawString(font, line, getX(), y, 0xFFFFFF); + y += font.lineHeight; + } + + graphics.disableScissor(); + + maxScrollAmount = Math.max(0, y + scrollAmount - getY() - getHeight()); + + Style hoveredStyle = getDescStyle(mouseX, mouseY); + if (hoveredStyle != null && hoveredStyle.getHoverEvent() != null) { + graphics.renderComponentHoverEffect(font, hoveredStyle, mouseX, mouseY); + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + Style clickedStyle = getDescStyle((int) mouseX, (int) mouseY); + if (clickedStyle != null && clickedStyle.getClickEvent() != null) { + if (minecraft.screen.handleComponentClicked(clickedStyle)) { + playDownSound(minecraft.getSoundManager()); + return true; + } + return false; + } + + return false; + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (isMouseOver(mouseX, mouseY)) { + scrollAmount = Mth.clamp(scrollAmount - (int) amount * 10, 0, maxScrollAmount); + return true; + } + return false; + } + + private Style getDescStyle(int mouseX, int mouseY) { + if (!clicked(mouseX, mouseY)) + return null; + + int x = mouseX - getX(); + int y = mouseY - descriptionY; + + if (x < 0 || x > getX() + getWidth()) return null; + if (y < 0 || y > getY() + getHeight()) return null; + + int line = y / font.lineHeight; + + if (line >= wrappedText.size()) return null; + + return font.getSplitter().componentStyleAtWidth(wrappedText.get(line), x); + } + + @Override + protected void updateWidgetNarration(NarrationElementOutput builder) { + + } + + public void setOptionDescription(OptionDescription description) { + //this.close(); + this.description = description; + this.wrappedText = null; + } + + @Override + public void close() { + if (description != null) { + description.image().thenAccept(image -> { + if (image.isPresent()) { + try { + image.get().close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + } +} 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 fc7c317..390e6c0 100644 --- a/common/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java +++ b/common/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java @@ -1,7 +1,6 @@ package dev.isxander.yacl.gui; import com.google.common.collect.ImmutableList; -import com.mojang.blaze3d.vertex.PoseStack; import dev.isxander.yacl.api.*; import dev.isxander.yacl.api.utils.Dimension; import dev.isxander.yacl.impl.utils.YACLConstants; @@ -21,24 +20,27 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; |
