From ead0b794ac57e9ab2558338f7f3da7545d2e12ff Mon Sep 17 00:00:00 2001 From: isXander Date: Mon, 22 May 2023 19:33:00 +0100 Subject: scrollable navbar, group descriptions, auto-scroll descriptions --- build.gradle.kts | 7 +- .../dev/isxander/yacl/api/OptionDescription.java | 4 +- .../java/dev/isxander/yacl/api/OptionGroup.java | 6 ++ .../isxander/yacl/gui/OptionDescriptionWidget.java | 70 +++++++++++++++++-- .../dev/isxander/yacl/gui/OptionListWidget.java | 27 ++++---- .../isxander/yacl/gui/ScrollableNavigationBar.java | 79 ++++++++++++++++++++++ .../java/dev/isxander/yacl/gui/TabListWidget.java | 14 ++++ .../java/dev/isxander/yacl/gui/YACLScreen.java | 23 +++---- .../dev/isxander/yacl/impl/ConfigCategoryImpl.java | 7 +- .../isxander/yacl/impl/OptionDescriptionImpl.java | 25 +++++-- .../dev/isxander/yacl/impl/OptionGroupImpl.java | 51 +++++++++----- common/src/main/resources/yacl.accesswidener | 8 ++- fabric/build.gradle.kts | 9 +-- forge/build.gradle.kts | 10 ++- .../main/java/dev/isxander/yacl/test/GuiTest.java | 38 +++++++++-- 15 files changed, 303 insertions(+), 75 deletions(-) create mode 100644 common/src/main/java/dev/isxander/yacl/gui/ScrollableNavigationBar.java diff --git a/build.gradle.kts b/build.gradle.kts index d85c3fb..b9fb2df 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ architectury { minecraft = libs.versions.minecraft.get() } -version = "2.5.1-beta.1+1.20" +version = "3.0.0-beta.1+1.20" val isBeta = "beta" in version.toString() val changelogText = rootProject.file("changelogs/${project.version}.md").takeIf { it.exists() }?.readText() ?: "No changelog provided." @@ -92,6 +92,7 @@ githubRelease { tagName("${project.version}") targetCommitish(grgit.branch.current().name) body(changelogText) + prerelease(isBeta) releaseAssets( { findProject(":fabric")?.tasks?.get("remapJar")?.outputs?.files }, { findProject(":fabric")?.tasks?.get("remapSourcesJar")?.outputs?.files }, @@ -109,6 +110,6 @@ tasks.register("releaseMod") { tasks.register("buildAll") { group = "mod" - dependsOn(project(":fabric").tasks["build"]) - dependsOn(project(":forge").tasks["build"]) + findProject(":fabric")?.let { dependsOn(it.tasks["build"]) } + findProject(":forge")?.let { dependsOn(it.tasks["build"]) } } 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 22eebc9..e5ed0b6 100644 --- a/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java +++ b/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java @@ -6,6 +6,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import java.nio.file.Path; +import java.util.Collection; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -23,7 +24,8 @@ public interface OptionDescription { interface Builder { Builder name(Component name); - Builder description(Component description); + Builder description(Component... description); + Builder description(Collection lines); Builder image(ResourceLocation image, int width, int height); Builder image(Path path, ResourceLocation uniqueLocation); diff --git a/common/src/main/java/dev/isxander/yacl/api/OptionGroup.java b/common/src/main/java/dev/isxander/yacl/api/OptionGroup.java index 4fe43c7..f1b2b5a 100644 --- a/common/src/main/java/dev/isxander/yacl/api/OptionGroup.java +++ b/common/src/main/java/dev/isxander/yacl/api/OptionGroup.java @@ -19,9 +19,12 @@ public interface OptionGroup { */ Component name(); + OptionDescription description(); + /** * Tooltip displayed on hover. */ + @Deprecated Component tooltip(); /** @@ -55,6 +58,8 @@ public interface OptionGroup { */ Builder name(@NotNull Component name); + Builder description(@NotNull OptionDescription description); + /** * Sets the tooltip to be used by the option group. * Can be invoked twice to append more lines. @@ -62,6 +67,7 @@ public interface OptionGroup { * * @param tooltips Component lines - merged with a new-line on {@link Builder#build()}. */ + @Deprecated Builder tooltip(@NotNull Component... tooltips); /** 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 882d75b..5c346d0 100644 --- a/common/src/main/java/dev/isxander/yacl/gui/OptionDescriptionWidget.java +++ b/common/src/main/java/dev/isxander/yacl/gui/OptionDescriptionWidget.java @@ -1,5 +1,7 @@ package dev.isxander.yacl.gui; +import com.mojang.blaze3d.Blaze3D; +import com.mojang.blaze3d.platform.InputConstants; import dev.isxander.yacl.api.OptionDescription; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; @@ -17,6 +19,9 @@ import java.util.List; import java.util.function.Supplier; 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 List wrappedText; @@ -25,10 +30,13 @@ public class OptionDescriptionWidget extends AbstractWidget { private Supplier dimensions; - private int scrollAmount; + private float targetScrollAmount, currentScrollAmount; private int maxScrollAmount; private int descriptionY; + private int lastInteractionTime; + private boolean scrollingBackward; + public OptionDescriptionWidget(Supplier dimensions, @Nullable OptionDescription description) { super(0, 0, 0, 0, description == null ? Component.empty() : description.descriptiveName()); this.dimensions = dimensions; @@ -39,6 +47,8 @@ public class OptionDescriptionWidget extends AbstractWidget { public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float delta) { if (description == null) return; + currentScrollAmount = Mth.lerp(delta * 0.5f, currentScrollAmount, targetScrollAmount); + ScreenRectangle dimensions = this.dimensions.get(); this.setX(dimensions.left()); this.setY(dimensions.top()); @@ -58,7 +68,7 @@ public class OptionDescriptionWidget extends AbstractWidget { graphics.enableScissor(getX(), y, getX() + getWidth(), getY() + getHeight()); - y -= scrollAmount; + y -= (int)currentScrollAmount; if (description.image().isDone()) { var image = description.image().join(); @@ -79,12 +89,19 @@ public class OptionDescriptionWidget extends AbstractWidget { graphics.disableScissor(); - maxScrollAmount = Math.max(0, y + scrollAmount - getY() - getHeight()); + maxScrollAmount = Math.max(0, y + (int)currentScrollAmount - getY() - getHeight()); + if (isHoveredOrFocused()) { + lastInteractionTime = currentTimeMS(); + } Style hoveredStyle = getDescStyle(mouseX, mouseY); if (hoveredStyle != null && hoveredStyle.getHoverEvent() != null) { graphics.renderComponentHoverEffect(font, hoveredStyle, mouseX, mouseY); } + + if (isFocused()) { + graphics.renderOutline(getX(), getY(), getWidth(), getHeight(), -1); + } } @Override @@ -104,12 +121,50 @@ public class OptionDescriptionWidget extends AbstractWidget { @Override public boolean mouseScrolled(double mouseX, double mouseY, double amount) { if (isMouseOver(mouseX, mouseY)) { - scrollAmount = Mth.clamp(scrollAmount - (int) amount * 10, 0, maxScrollAmount); + targetScrollAmount = Mth.clamp(targetScrollAmount - (int) amount * 10, 0, maxScrollAmount); + lastInteractionTime = currentTimeMS(); + return true; + } + return false; + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (isFocused()) { + switch (keyCode) { + case InputConstants.KEY_UP -> + targetScrollAmount = Mth.clamp(targetScrollAmount - 10, 0, maxScrollAmount); + case InputConstants.KEY_DOWN -> + targetScrollAmount = Mth.clamp(targetScrollAmount + 10, 0, maxScrollAmount); + default -> { + return false; + } + } return true; } return false; } + public void tick() { + float pxPerTick = AUTO_SCROLL_SPEED / 20f * font.lineHeight; + if (maxScrollAmount > 0 && currentTimeMS() - lastInteractionTime > AUTO_SCROLL_TIMER) { + if (scrollingBackward) { + pxPerTick *= -1; + if (targetScrollAmount + pxPerTick < 0) { + scrollingBackward = false; + lastInteractionTime = currentTimeMS(); + } + } else { + if (targetScrollAmount + pxPerTick > maxScrollAmount) { + scrollingBackward = true; + lastInteractionTime = currentTimeMS(); + } + } + + targetScrollAmount = Mth.clamp(targetScrollAmount + pxPerTick, 0, maxScrollAmount); + } + } + private Style getDescStyle(int mouseX, int mouseY) { if (!clicked(mouseX, mouseY)) return null; @@ -135,5 +190,12 @@ public class OptionDescriptionWidget extends AbstractWidget { public void setOptionDescription(OptionDescription description) { this.description = description; this.wrappedText = null; + this.targetScrollAmount = 0; + this.currentScrollAmount = 0; + this.lastInteractionTime = currentTimeMS(); + } + + private int currentTimeMS() { + return (int)(Blaze3D.getTime() * 1000); } } 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 390e6c0..98272d1 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 viewableChildren; private String searchQuery = ""; - private final Consumer> hoverEvent; - private Option lastHoveredOption; + private final Consumer hoverEvent; + private OptionDescription lastHoveredOption; - public OptionListWidget(YACLScreen screen, ConfigCategory category, Minecraft client, int x, int y, int width, int height, Consumer> hoverEvent) { + public OptionListWidget(YACLScreen screen, ConfigCategory category, Minecraft client, int x, int y, int width, int height, Consumer hoverEvent) { super(client, x, y, width, height, true); this.yaclScreen = screen; this.category = category; @@ -237,6 +237,13 @@ public class OptionListWidget extends ElementListWidgetExt { public boolean isViewable() { return true; @@ -292,11 +299,8 @@ public class OptionListWidget extends ElementListWidgetExt iterable) { + super(i, tabManager, iterable); + } + + @Override + public void arrangeElements() { + int noScrollWidth = this.width - NAVBAR_MARGIN*2; + int minimumSize = tabButtons.stream() + .map(AbstractWidget::getMessage) + .mapToInt(label -> font.width(label) + 3) + .min().orElse(0); + int singleTabWidth = Math.max(noScrollWidth / Math.min(this.tabButtons.size(), 3), minimumSize); + for (TabButton tabButton : this.tabButtons) { + tabButton.setWidth(singleTabWidth); + } + + this.layout.arrangeElements(); + this.layout.setY(0); + this.scrollOffset = 0; + + int allTabsWidth = singleTabWidth * this.tabButtons.size(); + this.layout.setX(Math.max((this.width - allTabsWidth) / 2, NAVBAR_MARGIN)); + this.maxScrollOffset = Math.max(0, allTabsWidth - noScrollWidth); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + this.setScrollOffset(this.scrollOffset - (int)(amount*10)); + return true; + } + + @Override + public boolean isMouseOver(double mouseX, double mouseY) { + return mouseY <= 24; + } + + public void setScrollOffset(int scrollOffset) { + layout.setX(layout.getX() + this.scrollOffset); + this.scrollOffset = Mth.clamp(scrollOffset, 0, maxScrollOffset); + layout.setX(layout.getX() - this.scrollOffset); + } + + @Override + public void setFocused(@Nullable GuiEventListener child) { + super.setFocused(child); + if (child instanceof TabButton tabButton) { + this.ensureVisible(tabButton); + } + } + + protected void ensureVisible(TabButton tabButton) { + if (tabButton.getX() < NAVBAR_MARGIN) { + this.setScrollOffset(this.scrollOffset - (NAVBAR_MARGIN - tabButton.getX())); + } else if (tabButton.getX() + tabButton.getWidth() > this.width - NAVBAR_MARGIN) { + this.setScrollOffset(this.scrollOffset + (tabButton.getX() + tabButton.getWidth() - (this.width - NAVBAR_MARGIN))); + } + } +} diff --git a/common/src/main/java/dev/isxander/yacl/gui/TabListWidget.java b/common/src/main/java/dev/isxander/yacl/gui/TabListWidget.java index b9e756c..041c06a 100644 --- a/common/src/main/java/dev/isxander/yacl/gui/TabListWidget.java +++ b/common/src/main/java/dev/isxander/yacl/gui/TabListWidget.java @@ -1,11 +1,13 @@ package dev.isxander.yacl.gui; import com.google.common.collect.ImmutableList; +import net.minecraft.client.gui.ComponentPath; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.events.ContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.gui.navigation.FocusNavigationEvent; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.network.chat.CommonComponents; import org.jetbrains.annotations.Nullable; @@ -102,4 +104,16 @@ public class TabListWidget> extends AbstractWi public void setFocused(@Nullable GuiEventListener listener) { this.list.setFocused(listener); } + + @Nullable + @Override + public ComponentPath nextFocusPath(FocusNavigationEvent event) { + return this.list.nextFocusPath(event); + } + + @Nullable + @Override + public ComponentPath getCurrentFocusPath() { + return this.list.getCurrentFocusPath(); + } } diff --git a/common/src/main/java/dev/isxander/yacl/gui/YACLScreen.java b/common/src/main/java/dev/isxander/yacl/gui/YACLScreen.java index 0ce98ba..a72640e 100644 --- a/common/src/main/java/dev/isxander/yacl/gui/YACLScreen.java +++ b/common/src/main/java/dev/isxander/yacl/gui/YACLScreen.java @@ -56,17 +56,13 @@ public class YACLScreen extends Screen { @Override protected void init() { - tabNavigationBar = TabNavigationBar.builder(tabManager, this.width) - .addTabs(tabs = config.categories() - .stream() - .map(category -> { - if (category instanceof PlaceholderCategory placeholder) - return new PlaceholderTab(placeholder); - return new CategoryTab(category); - }) - .toArray(Tab[]::new) - ) - .build(); + tabNavigationBar = new ScrollableNavigationBar(this.width, tabManager, config.categories() + .stream() + .map(category -> { + if (category instanceof PlaceholderCategory placeholder) + return new PlaceholderTab(placeholder); + return new CategoryTab(category); + }).toList()); tabNavigationBar.selectTab(0, false); tabNavigationBar.arrangeElements(); ScreenRectangle navBarArea = tabNavigationBar.getRectangle(); @@ -239,8 +235,8 @@ public class YACLScreen extends Screen { this.optionList = new TabListWidget<>( () -> new ScreenRectangle(tabArea.position(), tabArea.width() / 3 * 2 + 1, tabArea.height()), - new OptionListWidget(YACLScreen.this, category, minecraft, 0, 0, width / 3 * 2 + 1, height, hoveredOption -> { - descriptionWidget.setOptionDescription(hoveredOption.description()); + new OptionListWidget(YACLScreen.this, category, minecraft, 0, 0, width / 3 * 2 + 1, height, desc -> { + descriptionWidget.setOptionDescription(desc); }) ); @@ -316,6 +312,7 @@ public class YACLScreen extends Screen { public void tick() { updateButtons(); searchField.tick(); + descriptionWidget.tick(); } private void updateButtons() { 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 2d39eb9..ed73174 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java @@ -1,10 +1,7 @@ package dev.isxander.yacl.impl; import com.google.common.collect.ImmutableList; -import dev.isxander.yacl.api.ConfigCategory; -import dev.isxander.yacl.api.ListOption; -import dev.isxander.yacl.api.Option; -import dev.isxander.yacl.api.OptionGroup; +import dev.isxander.yacl.api.*; import dev.isxander.yacl.impl.utils.YACLConstants; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentContents; @@ -114,7 +111,7 @@ public final class ConfigCategoryImpl implements ConfigCategory { Validate.notNull(name, "`name` must not be null to build `ConfigCategory`"); List combinedGroups = new ArrayList<>(); - combinedGroups.add(new OptionGroupImpl(Component.empty(), Component.empty(), ImmutableList.copyOf(rootOptions), false, true)); + combinedGroups.add(new OptionGroupImpl(Component.empty(), OptionDescription.createBuilder().name(Component.literal("Root")).build(), 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/OptionDescriptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java index c866b43..5d09828 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java @@ -4,19 +4,20 @@ import dev.isxander.yacl.api.OptionDescription; import dev.isxander.yacl.gui.ImageRenderer; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceLocation; import org.apache.commons.lang3.Validate; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Path; -import java.util.Optional; +import java.util.*; import java.util.concurrent.CompletableFuture; public record OptionDescriptionImpl(Component descriptiveName, Component description, CompletableFuture> image) implements OptionDescription { public static class BuilderImpl implements Builder { private Component name; - private Component description; + private final List descriptionLines = new ArrayList<>(); private CompletableFuture> image = CompletableFuture.completedFuture(Optional.empty()); private boolean imageUnset = true; @@ -27,8 +28,14 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip } @Override - public Builder description(Component description) { - this.description = description; + public Builder description(Component... description) { + this.descriptionLines.addAll(Arrays.asList(description)); + return this; + } + + @Override + public Builder description(Collection lines) { + this.descriptionLines.addAll(lines); return this; } @@ -115,10 +122,14 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip public OptionDescription build() { Validate.notNull(name, "Name must be set!"); - if (description == null) - description = Component.empty(); + MutableComponent concatenatedDescription = Component.empty(); + Iterator iter = descriptionLines.iterator(); + while (iter.hasNext()) { + concatenatedDescription.append(iter.next()); + if (iter.hasNext()) concatenatedDescription.append("\n"); + } - return new OptionDescriptionImpl(name.copy().withStyle(ChatFormatting.BOLD), description, image); + return new OptionDescriptionImpl(name.copy().withStyle(ChatFormatting.BOLD), 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 113aefc..a72aa71 100644 --- a/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java +++ b/common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java @@ -3,6 +3,7 @@ package dev.isxander.yacl.impl; import com.google.common.collect.ImmutableList; import dev.isxander.yacl.api.ListOption; import dev.isxander.yacl.api.Option; +import dev.isxander.yacl.api.OptionDescription; import dev.isxander.yacl.api.OptionGroup; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentContents; @@ -10,6 +11,7 @@ import net.minecraft.network.chat.MutableComponent; import org.apache.commons.lang3.Validate; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; @@ -18,14 +20,14 @@ import java.util.List; @ApiStatus.Internal public final class OptionGroupImpl implements OptionGroup { private final @NotNull Component name; - private final @NotNull Component tooltip; + private final @NotNull OptionDescription description; private final ImmutableList> options; private final boolean collapsed; private final boolean isRoot; - public OptionGroupImpl(@NotNull Component name, @NotNull Component tooltip, ImmutableList> options, boolean collapsed, boolean isRoot) { + public OptionGroupImpl(@NotNull Component name, @NotNull OptionDescription description, ImmutableList> options, boolean collapsed, boolean isRoot) { this.name = name; - this.tooltip = tooltip; + this.description = description; this.options = options; this.collapsed = collapsed; this.isRoot = isRoot; @@ -36,9 +38,14 @@ public final class OptionGroupImpl implements OptionGroup { return name; } + @Override + public OptionDescription description() { + return description; + } + @Override public @NotNull Component tooltip() { - return tooltip; + return description.description(); } @Override @@ -59,7 +66,8 @@ public final class OptionGroupImpl implements OptionGroup { @ApiStatus.Internal public static final class BuilderImpl implements Builder { private Component name = Component.empty(); - private final List tooltipLines = new ArrayList<>(); + private OptionDescription description = null; + private OptionDescription.Builder legacyBuilder = null; private final List> options = new ArrayList<>(); private boolean collapsed = false; @@ -71,11 +79,23 @@ public final class OptionGroupImpl implements OptionGroup { return this; } + @Override + public Builder description(@NotNull OptionDescription description) { + Validate.isTrue(legacyBuilder == null, "Cannot set description when deprecated `tooltip` method is used"); + Validate.notNull(description, "`description` must not be null"); + + this.description = description; + return this; + } + @Override public Builder tooltip(@NotNull Component... tooltips) { + Validate.isTrue(description == null, "Cannot use deprecated `tooltip` method when `description` in use."); Validate.notEmpty(tooltips, "`tooltips` cannot be empty"); - tooltipLines.addAll(List.of(tooltips)); + ensureLegacyDescriptionBuilder(); + + legacyBuilder.description(tooltips); return this; } @@ -111,19 +131,18 @@ public final class OptionGroupImpl implements OptionGroup { public OptionGroup build() { Validate.notEmpty(options, "`options` must not be empty to build `OptionGroup`"); - MutableComponent concatenatedTooltip = Component.empty(); - boolean first = true; - for (Component line : tooltipLines) { - if (line.getContents() == ComponentContents.EMPTY) - continue; + if (description == null) { + ensureLegacyDescriptionBuilder(); + description = legacyBuilder.name(name).build(); + } - if (!first) concatenatedTooltip.append("\n"); - first = false; + return new OptionGroupImpl(name, description, ImmutableList.copyOf(options), collapsed, false); + } - concatenatedTooltip.append(line); + private void ensureLegacyDescriptionBuilder() { + if (legacyBuilder == null) { + legacyBuilder = OptionDescription.createBuilder(); } - - return new OptionGroupImpl(name, concatenatedTooltip, ImmutableList.copyOf(options), collapsed, false); } } } diff --git a/common/src/main/resources/yacl.accesswidener b/common/src/main/resources/yacl.accesswidener index 2188ea5..303b57a 100644 --- a/common/src/main/resources/yacl.accesswidener +++ b/common/src/main/resources/yacl.accesswidener @@ -3,4 +3,10 @@ accessWidener v2 named extendable method net/minecraft/client/gui/components/AbstractSelectionList children ()Ljava/util/List; extendable method net/minecraft/client/gui/components/AbstractSelectionList getEntryAtPosition (DD)Lnet/minecraft/client/gui/components/AbstractSelectionList$Entry; accessible class net/minecraft/client/gui/components/AbstractSelectionList$Entry -extendable method net/minecraft/client/gui/components/AbstractButton getTextureY ()I \ No newline at end of file +extendable method net/minecraft/client/gui/components/AbstractButton getTextureY ()I +accessible method net/minecraft/client/gui/components/tabs/TabNavigationBar (ILnet/minecraft/client/gui/components/tabs/TabManager;Ljava/lang/Iterable;)V +accessible field net/minecraft/client/gui/components/tabs/TabNavigationBar layout Lnet/minecraft/client/gui/layouts/GridLayout; +accessible field net/minecraft/client/gui/components/tabs/TabNavigationBar width I +accessible field net/minecraft/client/gui/components/tabs/TabNavigationBar tabManager Lnet/minecraft/client/gui/components/tabs/TabManager; +accessible field net/minecraft/client/gui/components/tabs/TabNavigationBar tabs Lcom/google/common/collect/ImmutableList; +accessible field net/minecraft/client/gui/components/tabs/TabNavigationBar tabButtons Lcom/google/common/collect/ImmutableList; diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 6d527d6..690fe30 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -109,12 +109,13 @@ tasks { } } -components["java"].withGroovyBuilder { - "withVariantsFromConfiguration"(configurations["shadowRuntimeElements"]) { - "skip"() +components["java"].run { + if (this is AdhocComponentWithVariants) { + withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { + skip() + } } } - val changelogText: String by ext val isBeta: Boolean by ext diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index 88e1b5f..4c822d3 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -1,4 +1,6 @@ +import org.gradle.api.component.AdhocComponentWithVariants import org.gradle.jvm.tasks.Jar +import org.gradle.kotlin.dsl.get import org.gradle.kotlin.dsl.libs plugins { @@ -113,9 +115,11 @@ tasks { } } -components["java"].withGroovyBuilder { - "withVariantsFromConfiguration"(configurations["shadowRuntimeElements"]) { - "skip"() +components["java"].run { + if (this is AdhocComponentWithVariants) { + withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { + skip() + } } } 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 f274fe2..7c9cf9d 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 @@ -84,7 +84,7 @@ public class GuiTest { .name(Component.literal("Custom Boolean Toggle")) .description(OptionDescription.createBuilder() .name(Component.literal("Custom Boolean Toggle")) - .description(Component.literal("You can customize controllers like so! YACL is truly infinitely customizable!")) + .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()) .tooltip(Component.literal("You can customize these controllers like this!")) @@ -283,10 +283,6 @@ public class GuiTest { .initial(Component.literal("Initial label")) .build()) .build()) - .category(PlaceholderCategory.createBuilder() - .name(Component.literal("Placeholder Category")) - .screen((client, yaclScreen) -> new RequireRestartScreen(yaclScreen)) - .build()) .category(ConfigCategory.createBuilder() .name(Component.literal("Group Test")) .option(Option.createBuilder(boolean.class) @@ -332,6 +328,38 @@ public class GuiTest { .build()) .build()) .build()) + .category(ConfigCategory.createBuilder() + .name(Component.literal("Category Test")) + .option(LabelOption.create(Component.literal("This is a test category!"))) + .build()) + .category(ConfigCategory.createBuilder() + .name(Component.literal("Category Test")) + .option(LabelOption.create(Component.literal("This is a test category!"))) + .build()) + .category(ConfigCategory.createBuilder() + .name(Component.literal("Category Test")) + .option(LabelOption.create(Component.literal("This is a test category!"))) + .build()) + .category(ConfigCategory.createBuilder() + .name(Component.literal("Category Test")) + .option(LabelOption.create(Component.literal("This is a test category!"))) + .build()) + .category(ConfigCategory.createBuilder() + .name(Component.literal("Category Test")) + .option(LabelOption.create(Component.literal("This is a test category!"))) + .build()) + .category(ConfigCategory.createBuilder() + .name(Component.literal("Category Test")) + .option(LabelOption.create(Component.literal("This is a test category!"))) + .build()) + .category(ConfigCategory.createBuilder() + .name(Component.literal("Category Test")) + .option(LabelOption.create(Component.literal("This is a test category!"))) + .build()) + .category(PlaceholderCategory.createBuilder() + .name(Component.literal("Placeholder Category")) + .screen((client, yaclScreen) -> new RequireRestartScreen(yaclScreen)) + .build()) .save(() -> { Minecraft.getInstance().options.save(); ConfigTest.GSON.save(); -- cgit