aboutsummaryrefslogtreecommitdiff
path: root/common/src/main
diff options
context:
space:
mode:
authorisXander <xandersmith2008@gmail.com>2023-05-22 19:33:00 +0100
committerisXander <xandersmith2008@gmail.com>2023-05-22 19:33:00 +0100
commitead0b794ac57e9ab2558338f7f3da7545d2e12ff (patch)
treed1c92f8d3283a8989923e038a377af4a00c24124 /common/src/main
parent590e69f4bf445a39737b0b1552cf116ff780d75e (diff)
downloadYetAnotherConfigLib-ead0b794ac57e9ab2558338f7f3da7545d2e12ff.tar.gz
YetAnotherConfigLib-ead0b794ac57e9ab2558338f7f3da7545d2e12ff.tar.bz2
YetAnotherConfigLib-ead0b794ac57e9ab2558338f7f3da7545d2e12ff.zip
scrollable navbar, group descriptions, auto-scroll descriptions
Diffstat (limited to 'common/src/main')
-rw-r--r--common/src/main/java/dev/isxander/yacl/api/OptionDescription.java4
-rw-r--r--common/src/main/java/dev/isxander/yacl/api/OptionGroup.java6
-rw-r--r--common/src/main/java/dev/isxander/yacl/gui/OptionDescriptionWidget.java70
-rw-r--r--common/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java27
-rw-r--r--common/src/main/java/dev/isxander/yacl/gui/ScrollableNavigationBar.java79
-rw-r--r--common/src/main/java/dev/isxander/yacl/gui/TabListWidget.java14
-rw-r--r--common/src/main/java/dev/isxander/yacl/gui/YACLScreen.java23
-rw-r--r--common/src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java7
-rw-r--r--common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java25
-rw-r--r--common/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java51
-rw-r--r--common/src/main/resources/yacl.accesswidener8
11 files changed, 254 insertions, 60 deletions
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<? extends Component> 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<FormattedCharSequence> wrappedText;
@@ -25,10 +30,13 @@ public class OptionDescriptionWidget extends AbstractWidget {
private Supplier<ScreenRectangle> dimensions;
- private int scrollAmount;
+ private float targetScrollAmount, currentScrollAmount;
private int maxScrollAmount;
private int descriptionY;
+ 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());
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<OptionListWidget.Entr
private final ConfigCategory category;
private ImmutableList<Entry> viewableChildren;
private String searchQuery = "";
- private final Consumer<Option<?>> hoverEvent;
- private Option<?> lastHoveredOption;
+ private final Consumer<OptionDescription> hoverEvent;
+ private OptionDescription lastHoveredOption;
- public OptionListWidget(YACLScreen screen, ConfigCategory category, Minecraft client, int x, int y, int width, int height, Consumer<Option<?>> hoverEvent) {
+ public OptionListWidget(YACLScreen screen, ConfigCategory category, Minecraft client, int x, int y, int width, int height, Consumer<OptionDescription> hoverEvent) {
super(client, x, y, width, height, true);
this.yaclScreen = screen;
this.category = category;
@@ -237,6 +237,13 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
return ret;
}
+ private void setHoverDescription(OptionDescription description) {
+ if (description != lastHoveredOption) {
+ lastHoveredOption = description;
+ hoverEvent.accept(description);
+ }
+ }
+
public abstract class Entry extends ElementListWidgetExt.Entry<Entry> {
public boolean isViewable() {
return true;
@@ -292,11 +299,8 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
resetButton.render(graphics, mouseX, mouseY, tickDelta);
}
- if (isHovered()) {
- if (lastHoveredOption != option) {
- lastHoveredOption = option;
- hoverEvent.accept(option);
- }
+ if (isHovered() || isFocused()) {
+ setHoverDescription(option.description());
}
}
@@ -392,12 +396,9 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
expandMinimizeButton.render(graphics, mouseX, mouseY, tickDelta);
wrappedName.renderCentered(graphics, x + entryWidth / 2, y + getYPadding());
- }
- @Override
- public void postRender(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
- if ((isHovered() && !expandMinimizeButton.isMouseOver(mouseX, mouseY)) || expandMinimizeButton.isFocused()) {
- YACLScreen.renderMultilineTooltip(graphics, font, wrappedTooltip, getRowLeft() + getRowWidth() / 2, y - 3, y + getItemHeight() + 3, screen.width, screen.height);
+ if (isHovered() || isFocused()) {
+ setHoverDescription(group.description());
}
}
diff --git a/common/src/main/java/dev/isxander/yacl/gui/ScrollableNavigationBar.java b/common/src/main/java/dev/isxander/yacl/gui/ScrollableNavigationBar.java
new file mode 100644
index 0000000..e2d9ab8
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/gui/ScrollableNavigationBar.java
@@ -0,0 +1,79 @@
+package dev.isxander.yacl.gui;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Font;
+import net.minecraft.client.gui.components.AbstractWidget;
+import net.minecraft.client.gui.components.TabButton;
+import net.minecraft.client.gui.components.events.GuiEventListener;
+import net.minecraft.client.gui.components.tabs.Tab;
+import net.minecraft.client.gui.components.tabs.TabManager;
+import net.minecraft.client.gui.components.tabs.TabNavigationBar;
+import net.minecraft.util.Mth;
+import org.jetbrains.annotations.Nullable;
+
+public class ScrollableNavigationBar extends TabNavigationBar {
+ private static final int NAVBAR_MARGIN = 28;
+
+ private static final Font font = Minecraft.getInstance().font;
+
+ private int scrollOffset;
+ private int maxScrollOffset;
+
+ public ScrollableNavigationBar(int i, TabManager tabManager, Iterable<Tab> 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<T extends ElementListWidgetExt<?>> 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<OptionGroup> 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<Optional<ImageRenderer>> image) implements OptionDescription {
public static class BuilderImpl implements Builder {
private Component name;
- private Component description;
+ private final List<Component> descriptionLines = new ArrayList<>();
private CompletableFuture<Optional<ImageRenderer>> 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<? extends Component> 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<Component> 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<? extends Option<?>> options;
private final boolean collapsed;
private final boolean isRoot;
- public OptionGroupImpl(@NotNull Component name, @NotNull Component tooltip, ImmutableList<? extends Option<?>> options, boolean collapsed, boolean isRoot) {
+ public OptionGroupImpl(@NotNull Component name, @NotNull OptionDescription description, ImmutableList<? extends Option<?>> options, boolean collapsed, boolean isRoot) {
this.name = name;
- this.tooltip = tooltip;
+ this.description = description;
this.options = options;
this.collapsed = collapsed;
this.isRoot = isRoot;
@@ -37,8 +39,13 @@ public final class OptionGroupImpl implements OptionGroup {
}
@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<Component> tooltipLines = new ArrayList<>();
+ private OptionDescription description = null;
+ private OptionDescription.Builder legacyBuilder = null;
private final List<Option<?>> options = new ArrayList<>();
private boolean collapsed = false;
@@ -72,10 +80,22 @@ public final class OptionGroupImpl implements OptionGroup {
}
@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 <init> (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;