From 15d69c1e076ed30dc6f20f09cd7d02d2e310f84c Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:51:30 +0200 Subject: [PATCH 01/15] quick nav config screen start --- .../screens/quicknav/ItemEditPopup.java | 92 +++++++ .../quicknav/QuickNavConfigScreen.java | 257 ++++++++++++++++++ .../skyblock/quicknav/QuickNav.java | 26 +- .../skyblock/quicknav/QuickNavButton.java | 12 +- .../argumenttypes/item/ComponentArgument.java | 45 +++ .../argumenttypes/item/ComponentParser.java | 86 ++++++ .../argumenttypes/item/package-info.java | 4 + .../suggestions/TextFieldSuggestions.java | 94 +++++++ .../command/suggestions/package-info.java | 4 + src/main/resources/skyblocker.classtweaker | 23 ++ 10 files changed, 633 insertions(+), 10 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java create mode 100644 src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentArgument.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentParser.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/package-info.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/command/suggestions/package-info.java diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java new file mode 100644 index 00000000000..1bb8661996c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -0,0 +1,92 @@ +package de.hysky.skyblocker.config.screens.quicknav; + +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.tree.ArgumentCommandNode; +import com.mojang.brigadier.tree.CommandNode; +import de.hysky.skyblocker.config.configs.QuickNavigationConfig; +import de.hysky.skyblocker.utils.command.argumenttypes.item.ComponentArgument; +import de.hysky.skyblocker.utils.command.suggestions.TextFieldSuggestions; +import de.hysky.skyblocker.utils.render.gui.AbstractPopupScreen; +import net.fabricmc.fabric.api.client.command.v2.ClientCommands; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.CommandSuggestions; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.layouts.LinearLayout; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.client.multiplayer.ClientSuggestionProvider; +import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.network.chat.Component; + +class ItemEditPopup extends AbstractPopupScreen { + + private final Runnable onClose; + private final QuickNavigationConfig.QuickNavItem item; + private final QuickNavConfigScreen.ConfigItemSetter setter; + private final LinearLayout layout = LinearLayout.vertical().spacing(4); + private TextFieldSuggestions suggestions; + + protected ItemEditPopup(Screen backgroundScreen, Runnable onClose, QuickNavigationConfig.QuickNavItem item, QuickNavConfigScreen.ConfigItemSetter setter) { + super(Component.literal("Edit button or something"), backgroundScreen); + this.onClose = onClose; + this.item = item; + this.setter = setter; + } + + @Override + protected void init() { + EditBox box = layout.addChild(new EditBox(font, 250, 20, Component.empty())); + this.suggestions = new TextFieldSuggestions( + this.minecraft, this, box, this.font, true, 7, + RequiredArgumentBuilder.argument("argument", new ComponentArgument(TextFieldSuggestions.getContext())).build() + ); + this.suggestions.setAllowSuggestions(true); + layout.visitWidgets(this::addRenderableWidget); + box.setResponder(_ -> suggestions.updateCommandInfo()); + super.init(); + } + + @Override + public boolean keyPressed(KeyEvent event) { + if (suggestions.keyPressed(event)) return true; + return super.keyPressed(event); + } + + @Override + public boolean mouseScrolled(double x, double y, double scrollX, double scrollY) { + return suggestions.mouseScrolled(scrollY) || super.mouseScrolled(x, y, scrollX, scrollY); + } + + @Override + public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { + return suggestions.mouseClicked(event) || super.mouseClicked(event, doubleClick); + } + + @Override + public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + super.extractBackground(graphics, mouseX, mouseY, a); + extractPopupBackground(graphics, layout.getX(), layout.getY(), layout.getWidth(), layout.getHeight()); + } + + @Override + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + super.extractRenderState(graphics, mouseX, mouseY, a); + suggestions.extractRenderState(graphics, mouseX, mouseY); + graphics.text(font, String.valueOf(suggestions.isVisible()), 0, 0, -1); + } + + @Override + protected void repositionElements() { + super.repositionElements(); + layout.arrangeElements(); + layout.setPosition((width - layout.getWidth()) / 2, (height - layout.getHeight()) / 2); + } + + @Override + public void onClose() { + super.onClose(); + onClose.run(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java new file mode 100644 index 00000000000..ca06cc49926 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java @@ -0,0 +1,257 @@ +package de.hysky.skyblocker.config.screens.quicknav; + +import com.mojang.blaze3d.platform.InputConstants; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.configs.QuickNavigationConfig; +import de.hysky.skyblocker.skyblock.quicknav.QuickNavButton; +import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.InventoryScreen; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.client.input.MouseButtonInfo; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.network.chat.Component; +import net.minecraft.util.ARGB; +import net.minecraft.util.CommonColors; +import net.minecraft.world.entity.EntityTypes; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.feline.Cat; +import net.minecraft.world.item.ItemStack; +import org.jspecify.annotations.Nullable; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +public class QuickNavConfigScreen extends Screen { + private static final int INVENTORY_WIDTH = 176; + private static final int INVENTORY_HEIGHT = 166; + private static final ConfigItemSupplier[] ITEM_SUPPLIERS = new ConfigItemSupplier[]{ + config -> config.button1, + config -> config.button2, + config -> config.button3, + config -> config.button4, + config -> config.button5, + config -> config.button6, + config -> config.button7, + config -> config.button8, + config -> config.button9, + config -> config.button10, + config -> config.button11, + config -> config.button12, + config -> config.button13, + config -> config.button14 + }; + private static final ConfigItemSetter[] SETTERS = new ConfigItemSetter[]{ + (config, item) -> config.button1 = item, + (config, item) -> config.button2 = item, + (config, item) -> config.button3 = item, + (config, item) -> config.button4 = item, + (config, item) -> config.button5 = item, + (config, item) -> config.button6 = item, + (config, item) -> config.button7 = item, + (config, item) -> config.button8 = item, + (config, item) -> config.button9 = item, + (config, item) -> config.button10 = item, + (config, item) -> config.button11 = item, + (config, item) -> config.button12 = item, + (config, item) -> config.button13 = item, + (config, item) -> config.button14 = item + }; + + private final QuickNavConfigButton[] buttons = new QuickNavConfigButton[ITEM_SUPPLIERS.length]; + private @Nullable QuickNavConfigButton highlightedButton; + private final @Nullable LivingEntity entityToRender; + + public QuickNavConfigScreen() { + super(Component.literal("Quick Navigation Config")); + if (Math.random() < 0.001 && minecraft.level != null) { + entityToRender = new Cat(EntityTypes.CAT, minecraft.level); + entityToRender.setId("meow".hashCode()); + + } else { + entityToRender = minecraft.player; + } + } + + @Override + protected void init() { + super.init(); + for (int i = 0; i < buttons.length; i++) { + QuickNavigationConfig.QuickNavItem item = ITEM_SUPPLIERS[i].apply(SkyblockerConfigManager.get().quickNav); + buttons[i] = addWidget(new QuickNavConfigButton( + item.render, + i, + ItemStackComponentizationFixer.fromComponentsString(item.itemData.item.toString(), Math.clamp(item.itemData.count, 1, 99), item.itemData.components), + item.tooltip + )); + } + repositionElements(); + } + + @Override + protected void repositionElements() { + for (QuickNavConfigButton button : buttons) { + button.setPositionFrom((width - INVENTORY_WIDTH) / 2, (height - INVENTORY_HEIGHT) / 2, INVENTORY_WIDTH, INVENTORY_HEIGHT); + } + } + + private void refreshButton(int index) { + QuickNavigationConfig.QuickNavItem item = ITEM_SUPPLIERS[index].apply(SkyblockerConfigManager.get().quickNav); + removeWidget(buttons[index]); + buttons[index] = addWidget(new QuickNavConfigButton( + item.render, + index, + ItemStackComponentizationFixer.fromComponentsString(item.itemData.item.toString(), Math.clamp(item.itemData.count, 1, 99), item.itemData.components), + item.tooltip + )); + repositionElements(); + } + + @Override + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + super.extractRenderState(graphics, mouseX, mouseY, a); + graphics.centeredText(font, "Right click to disable/enable buttons.", width / 2, 4, CommonColors.GRAY); + graphics.centeredText(font, "Left click to edit button options.", width / 2, 4 + font.lineHeight, CommonColors.GRAY); + QuickNavConfigButton dragged = null; + for (QuickNavConfigButton button : buttons) { + if (button.dragging && dragged == null) { + dragged = button; + continue; + } + button.extractRenderState(graphics, mouseX, mouseY, a); + } + if (highlightedButton != null) { + graphics.fill(highlightedButton.getX(), highlightedButton.getY(), highlightedButton.getRight(), highlightedButton.getBottom(), ARGB.color(0.3f, CommonColors.YELLOW)); + } + // render dragged in front of everything + if (dragged != null) dragged.extractRenderState(graphics, mouseX, mouseY, a); + if (entityToRender != null) { + int xo = (width - INVENTORY_WIDTH) / 2; + int yo = (height - INVENTORY_HEIGHT) / 2; + InventoryScreen.extractEntityInInventoryFollowsMouse(graphics, xo + 26, yo + 8, xo + 75, yo + 78, 30, 0.0625F, mouseX, mouseY, entityToRender); + } + } + + @Override + public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + super.extractBackground(graphics, mouseX, mouseY, a); + int backgroundX = (width - INVENTORY_WIDTH) / 2; + int backgroundY = (height - INVENTORY_HEIGHT) / 2; + graphics.blit(RenderPipelines.GUI_TEXTURED, InventoryScreen.INVENTORY_LOCATION, backgroundX, backgroundY, 0.0F, 0.0F, INVENTORY_WIDTH, INVENTORY_HEIGHT, 256, 256); + } + + @Override + public void removed() { + super.removed(); + SkyblockerConfigManager.update(_ -> {}); + } + + private class QuickNavConfigButton extends QuickNavButton { + private boolean enabled; + private double dragX, dragY; + private boolean dragging; + + /** + * Constructs a new QuickNavButton with the given parameters. + * + * @param index the index of the button. + * @param icon the icon to display on the button. + * @param tooltip the tooltip to show when hovered + */ + public QuickNavConfigButton(boolean enabled, int index, ItemStack icon, String tooltip) { + super(index, true, "", icon, tooltip); + this.enabled = enabled; + setRenderInFront(true); + } + + @Override + protected void updateCoordinates() {} + + @Override + public void onClick(MouseButtonEvent click, boolean doubled) { + if (click.button() == InputConstants.MOUSE_BUTTON_RIGHT) { + enabled = !enabled; + SkyblockerConfigManager.updateOnly(config -> ITEM_SUPPLIERS[index].apply(config.quickNav).render = enabled); + } + dragX = click.x(); + dragY = click.y(); + } + + @Override + protected void setPositionFrom(int backgroundX, int backgroundY, int imageWidth, int imageHeight) { + super.setPositionFrom(backgroundX, backgroundY, imageWidth, imageHeight); + } + + @Override + protected void onDrag(MouseButtonEvent event, double dx, double dy) { + super.onDrag(event, dx, dy); + if (!dragging && enabled) { + double diffX = event.x() - dragX; + double diffY = event.y() - dragY; + if (diffX * diffX + diffY * diffY > 10 * 10) { + dragging = true; + setTooltip(null); + } + } + if (!dragging) return; + setPosition((int) (event.x() - getWidth() / 2d), (int) (event.y() - getHeight() / 2d)); + highlightedButton = null; + for (QuickNavConfigButton button : buttons) { + if (button == this) continue; + if (button.isMouseOver(event.x(), event.y())) { + highlightedButton = button; + break; + } + } + if (highlightedButton != null) setTooltip(Tooltip.create(Component.literal("Release to swap"))); + else setTooltip(null); + } + + @Override + public void onRelease(MouseButtonEvent event) { + super.onRelease(event); + if (dragging) { + if (highlightedButton != null) { + int i1 = highlightedButton.index; + int i2 = this.index; + SkyblockerConfigManager.updateOnly(config -> { + QuickNavigationConfig.QuickNavItem item1 = ITEM_SUPPLIERS[i1].apply(config.quickNav); + QuickNavigationConfig.QuickNavItem item2 = ITEM_SUPPLIERS[i2].apply(config.quickNav); + SETTERS[i1].accept(config.quickNav, item2); + SETTERS[i2].accept(config.quickNav, item1); + }); + refreshButton(i1); + refreshButton(i2); + highlightedButton = null; + } else { + repositionElements(); + } + dragging = false; + } else { + minecraft.gui.setScreen(new ItemEditPopup(QuickNavConfigScreen.this, () -> refreshButton(index), ITEM_SUPPLIERS[index].apply(SkyblockerConfigManager.get().quickNav), SETTERS[index])); + } + setTooltip(tooltip); + } + + @Override + public void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + if (enabled) { + super.extractWidgetRenderState(graphics, mouseX, mouseY, delta); + } + } + + @Override + protected boolean isValidClickButton(MouseButtonInfo buttonInfo) { + return super.isValidClickButton(buttonInfo) || buttonInfo.button() == InputConstants.MOUSE_BUTTON_RIGHT; + } + } + + // Little interface because type inference is wacky on arrays + @FunctionalInterface + interface ConfigItemSupplier extends Function {} + + @FunctionalInterface + interface ConfigItemSetter extends BiConsumer {} +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java index 2145b3141c8..b94b73cc41a 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java @@ -1,26 +1,40 @@ package de.hysky.skyblocker.skyblock.quicknav; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.annotations.Init; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.config.configs.QuickNavigationConfig; +import de.hysky.skyblocker.config.screens.quicknav.QuickNavConfigScreen; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.PatternSyntaxException; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.ClientCommands; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.PatternSyntaxException; public class QuickNav { static final Logger LOGGER = LoggerFactory.getLogger(QuickNav.class); + @Init + public static void commandInit() { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, _) -> { + dispatcher.register(ClientCommands.literal(SkyblockerMod.NAMESPACE).then( + ClientCommands.literal("quickNav").executes(Scheduler.queueOpenScreenCommand(QuickNavConfigScreen::new)))); + }); + } + public static List init(String screenTitle) { List buttons = new ArrayList<>(); QuickNavigationConfig data = SkyblockerConfigManager.get().quickNav; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java index 615ebf1d9ee..bcbc11b41bc 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java @@ -42,7 +42,7 @@ public class QuickNavButton extends AbstractWidget { @SuppressWarnings("unchecked") private static final @Nullable FallbackedTexture[] TAB_TEXTURES_SELECTED = new FallbackedTexture[14]; - private final int index; + protected final int index; private final boolean toggled; private final String command; private final ItemStack icon; @@ -106,7 +106,7 @@ public QuickNavButton(int index, boolean toggled, String command, ItemStack icon setTooltipDelay(Duration.ofMillis(100)); } - private void updateCoordinates() { + protected void updateCoordinates() { Screen screen = Minecraft.getInstance().gui.screen(); while (screen instanceof PopupScreen || screen instanceof AbstractPopupScreen) { if (screen instanceof PopupScreen) { @@ -127,11 +127,15 @@ private void updateCoordinates() { int h = accessibleScreen.getImageHeight(); if (handledScreen instanceof ContainerScreen) h--; // they messed up the height on these. int w = accessibleScreen.getImageWidth(); - this.setX(x + this.index % 7 * 25 + w / 2 - 176 / 2); - this.setY(this.index < 7 ? y - 28 : y + h - 4); + setPositionFrom(x, y, w, h); } } + protected void setPositionFrom(int backgroundX, int backgroundY, int imageWidth, int imageHeight) { + this.setX(backgroundX + this.index % 7 * 25 + imageWidth / 2 - 176 / 2); + this.setY(this.index < 7 ? backgroundY - 28 : backgroundY + imageHeight - 4); + } + /** * Handles click events. If the button is not currently toggled, * it sets the toggled state to true and sends a message with the command after cooldown. diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentArgument.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentArgument.java new file mode 100644 index 00000000000..f0c763414d5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentArgument.java @@ -0,0 +1,45 @@ +package de.hysky.skyblocker.utils.command.argumenttypes.item; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.CommandBuildContext; +import net.minecraft.commands.arguments.item.ItemParser; +import net.minecraft.core.component.DataComponentPatch; + +import java.util.concurrent.CompletableFuture; + +public class ComponentArgument implements ArgumentType { + + private final ComponentParser parser; + + public ComponentArgument(CommandBuildContext context) { + parser = new ComponentParser(context); + } + + @Override + public DataComponentPatch parse(StringReader reader) throws CommandSyntaxException { + return parser.parse(reader).components(); + // insert a fake item so we directly go to the component part of the ItemParser + /*final String fakeItem = "minecraft:stone"; + StringReader fakeReader = new StringReader(new StringBuilder(reader.getString()).insert(reader.getCursor(), fakeItem).toString()); + fakeReader.setCursor(reader.getCursor()); + DataComponentPatch components = this.parser.parse(fakeReader).components(); + reader.setCursor(fakeReader.getCursor() - fakeItem.length()); + return components;*/ + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + /*StringBuilder stringBuilder = new StringBuilder(builder.getInput()); + stringBuilder.insert(builder.getStart(), "minecraft:stone"); + SuggestionsBuilder fakeBuilder = new SuggestionsBuilder(stringBuilder.toString(), builder.getStart()); + System.out.println("Original: " + builder.getInput() + " " + builder.getStart() + " " + builder.getRemaining()); + System.out.println("Fake: " + fakeBuilder.getInput() + " " + fakeBuilder.getStart() + " " + fakeBuilder.getRemaining()); + return this.parser.fillSuggestions(fakeBuilder);*/ + return parser.fillSuggestions(builder); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentParser.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentParser.java new file mode 100644 index 00000000000..b319ba44fe0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentParser.java @@ -0,0 +1,86 @@ +package de.hysky.skyblocker.utils.command.argumenttypes.item; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.arguments.item.ItemInput; +import net.minecraft.commands.arguments.item.ItemParser; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import org.apache.commons.lang3.mutable.MutableObject; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; + +public class ComponentParser extends ItemParser { + public ComponentParser(HolderLookup.Provider registries) { + super(registries); + } + + @Override + public ItemInput parse(StringReader reader) throws CommandSyntaxException { + final DataComponentPatch.Builder componentsBuilder = DataComponentPatch.builder(); + this.parse(reader, new ItemParser.Visitor() /* ItemParser$1 */ { + @Override + public void visitComponent(final DataComponentType type, final T value) { + componentsBuilder.set(type, value); + } + + @Override + public void visitRemovedComponent(final DataComponentType type) { + componentsBuilder.remove(type); + } + }); + DataComponentPatch components = componentsBuilder.build(); + return new ItemInput(Holder.direct(Items.AIR), components); + } + + @Override + public void parse(StringReader reader, Visitor visitor) throws CommandSyntaxException { + int cursor = reader.getCursor(); + + try { + (new ComponentState(reader, visitor)).parse(); + } catch (CommandSyntaxException e) { + reader.setCursor(cursor); + throw e; + } + } + + @Override + public CompletableFuture fillSuggestions(SuggestionsBuilder builder) { + StringReader reader = new StringReader(builder.getInput()); + reader.setCursor(builder.getStart()); + SuggestionsVisitor handler = new ItemParser.SuggestionsVisitor(); + ItemParser.State state = new ComponentState(reader, handler); + + try { + state.parse(); + } catch (CommandSyntaxException _) { + } + + return handler.resolveSuggestions(builder, reader); + } + + + private class ComponentState extends State { + + protected ComponentState(StringReader reader, Visitor visitor) { + super(reader, visitor); + } + + @Override + protected void readItem() {} + + @Override + protected CompletableFuture suggestItem(SuggestionsBuilder builder) { + return Suggestions.empty(); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/package-info.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/package-info.java new file mode 100644 index 00000000000..3d116338e94 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package de.hysky.skyblocker.utils.command.argumenttypes.item; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java new file mode 100644 index 00000000000..9d0d13b535b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java @@ -0,0 +1,94 @@ +package de.hysky.skyblocker.utils.command.suggestions; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.tree.CommandNode; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.CommandSuggestions; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.multiplayer.ClientSuggestionProvider; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.commands.CommandBuildContext; +import net.minecraft.util.ARGB; +import net.minecraft.util.FormattedCharSequence; + +public class TextFieldSuggestions extends CommandSuggestions { + + private final CommandDispatcher dispatcher; + + public TextFieldSuggestions(Minecraft minecraft, Screen screen, EditBox input, Font font, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node) { + super(minecraft, screen, input, font, true, onlyShowIfCursorPastError, 0, suggestionLineLimit, false, ARGB.black(0.5f)); + this.dispatcher = new CommandDispatcher<>(); + this.dispatcher.getRoot().addChild(node); + } + + public TextFieldSuggestions(Screen screen, EditBox input, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node) { + this(Minecraft.getInstance(), screen, input, Minecraft.getInstance().font, onlyShowIfCursorPastError, suggestionLineLimit, node); + } + + // FIXME nullable shit + public static CommandBuildContext getContext() { + LocalPlayer player = Minecraft.getInstance().player; + return CommandBuildContext.simple(player.connection.registryAccess(), player.connection.enabledFeatures()); + } + + @Override + public void showSuggestions(boolean immediateNarration) { + super.showSuggestions(immediateNarration); + if (suggestions != null) { + suggestions.rect = new Rect2i( + suggestions.rect.getX(), + input.getY() - suggestions.rect.getHeight(), + suggestions.rect.getWidth(), + suggestions.rect.getHeight() + ); + } + } + + @Override + public void extractUsage(GuiGraphicsExtractor graphics) { + int y = 0; + + for (FormattedCharSequence line : this.commandUsage) { + int lineY = this.input.getBottom() + 12 * y; + graphics.fill(commandUsagePosition - 1, lineY, this.commandUsagePosition + this.commandUsageWidth + 1, lineY + 12, this.fillColor); + graphics.text(this.font, line, this.commandUsagePosition, lineY + 2, -1); + y++; + } + } + + @Override + public void updateCommandInfo() { + String command = this.input.getValue(); + if (this.currentParse != null && !this.currentParse.getReader().getString().equals(command)) { + this.currentParse = null; + } + + if (!this.keepSuggestions) { + this.input.setSuggestion(null); + this.suggestions = null; + } + + commandUsage.clear(); + StringReader reader = new StringReader(command); + int cursorPosition = this.input.getCursorPosition(); + CommandDispatcher commands = dispatcher; + if (this.currentParse == null) { + this.currentParse = commands.parse(reader, this.minecraft.player.connection.getSuggestionsProvider()); + } + + int parseStart = this.onlyShowIfCursorPastError ? reader.getCursor() : 1; + if (cursorPosition >= parseStart && (this.suggestions == null || !this.keepSuggestions)) { + this.pendingSuggestions = commands.getCompletionSuggestions(this.currentParse, cursorPosition); + this.pendingSuggestions.thenAccept(suggestionResult -> { + if (this.pendingSuggestions.isDone()) { + this.updateUsageInfo(this.currentParse, suggestionResult); + } + }); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/package-info.java b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/package-info.java new file mode 100644 index 00000000000..e4f64e725a3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package de.hysky.skyblocker.utils.command.suggestions; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/resources/skyblocker.classtweaker b/src/main/resources/skyblocker.classtweaker index d6f80c3bce3..3d461a96bfb 100644 --- a/src/main/resources/skyblocker.classtweaker +++ b/src/main/resources/skyblocker.classtweaker @@ -76,3 +76,26 @@ inject-interface net/minecraft/client/gui/screens/inventory/InventoryScreen de/h inject-interface net/minecraft/client/renderer/LevelRenderer de/hysky/skyblocker/injected/EntityRenderMarker inject-interface net/minecraft/client/renderer/feature/ItemFeatureRenderer$Submit de/hysky/skyblocker/injected/CustomGlowState inject-interface net/minecraft/client/renderer/rendertype/RenderType de/hysky/skyblocker/injected/GlowRenderTypeHolder + +# Suggestions +accessible field net/minecraft/client/gui/components/CommandSuggestions input Lnet/minecraft/client/gui/components/EditBox; +accessible field net/minecraft/client/gui/components/CommandSuggestions currentParse Lcom/mojang/brigadier/ParseResults; +accessible field net/minecraft/client/gui/components/CommandSuggestions keepSuggestions Z +accessible field net/minecraft/client/gui/components/CommandSuggestions minecraft Lnet/minecraft/client/Minecraft; +accessible field net/minecraft/client/gui/components/CommandSuggestions suggestions Lnet/minecraft/client/gui/components/CommandSuggestions$SuggestionsList; +accessible field net/minecraft/client/gui/components/CommandSuggestions onlyShowIfCursorPastError Z +accessible field net/minecraft/client/gui/components/CommandSuggestions pendingSuggestions Ljava/util/concurrent/CompletableFuture; +accessible field net/minecraft/client/gui/components/CommandSuggestions commandUsage Ljava/util/List; +accessible field net/minecraft/client/gui/components/CommandSuggestions font Lnet/minecraft/client/gui/Font; +accessible field net/minecraft/client/gui/components/CommandSuggestions commandUsagePosition I +accessible field net/minecraft/client/gui/components/CommandSuggestions commandUsageWidth I +accessible field net/minecraft/client/gui/components/CommandSuggestions fillColor I +mutable field net/minecraft/client/gui/components/CommandSuggestions$SuggestionsList rect Lnet/minecraft/client/renderer/Rect2i; +accessible field net/minecraft/client/gui/components/CommandSuggestions$SuggestionsList rect Lnet/minecraft/client/renderer/Rect2i; +accessible method net/minecraft/client/gui/components/CommandSuggestions updateUsageInfo (Lcom/mojang/brigadier/ParseResults;Lcom/mojang/brigadier/suggestion/Suggestions;)V +extendable method net/minecraft/commands/arguments/item/ItemParser$State (Lnet/minecraft/commands/arguments/item/ItemParser;Lcom/mojang/brigadier/StringReader;Lnet/minecraft/commands/arguments/item/ItemParser$Visitor;)V +extendable method net/minecraft/commands/arguments/item/ItemParser$State suggestItem (Lcom/mojang/brigadier/suggestion/SuggestionsBuilder;)Ljava/util/concurrent/CompletableFuture; +extendable method net/minecraft/commands/arguments/item/ItemParser$State readItem ()V +extendable method net/minecraft/commands/arguments/item/ItemParser$State readComponents ()V +extendable method net/minecraft/commands/arguments/item/ItemParser$State suggestStartComponents (Lcom/mojang/brigadier/suggestion/SuggestionsBuilder;)Ljava/util/concurrent/CompletableFuture; +accessible method net/minecraft/commands/arguments/item/ItemParser$SuggestionsVisitor ()V From 410fb165918fad9e1adc258c8644afd7d1d1cd6f Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Fri, 24 Jul 2026 21:47:54 +0200 Subject: [PATCH 02/15] more things and uuuuhhh stuff --- .../screens/quicknav/ItemEditPopup.java | 115 +++++++---- .../ArgumentWithAlternatives.java | 104 ++++++++++ .../GreedyComponentArgument.java | 53 ++++++ .../suggestions/TextFieldSuggestions.java | 25 +++ .../utils/render/gui/AutocompleteEditBox.java | 179 ++++++++++++++++++ src/main/resources/skyblocker.classtweaker | 3 +- 6 files changed, 442 insertions(+), 37 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/ArgumentWithAlternatives.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/GreedyComponentArgument.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/AutocompleteEditBox.java diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index 1bb8661996c..298d32c9fa3 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -1,24 +1,26 @@ package de.hysky.skyblocker.config.screens.quicknav; -import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import com.mojang.brigadier.tree.ArgumentCommandNode; -import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; import de.hysky.skyblocker.config.configs.QuickNavigationConfig; -import de.hysky.skyblocker.utils.command.argumenttypes.item.ComponentArgument; +import de.hysky.skyblocker.utils.command.argumenttypes.ArgumentWithAlternatives; import de.hysky.skyblocker.utils.command.suggestions.TextFieldSuggestions; +import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; import de.hysky.skyblocker.utils.render.gui.AbstractPopupScreen; -import net.fabricmc.fabric.api.client.command.v2.ClientCommands; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import de.hysky.skyblocker.utils.render.gui.AutocompleteEditBox; import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.components.CommandSuggestions; -import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.StringWidget; +import net.minecraft.client.gui.layouts.GridLayout; import net.minecraft.client.gui.layouts.LinearLayout; +import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.input.KeyEvent; -import net.minecraft.client.input.MouseButtonEvent; -import net.minecraft.client.multiplayer.ClientSuggestionProvider; -import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.commands.CommandBuildContext; +import net.minecraft.commands.arguments.ComponentArgument; +import net.minecraft.commands.arguments.item.ItemArgument; +import net.minecraft.commands.arguments.item.ItemInput; import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; class ItemEditPopup extends AbstractPopupScreen { @@ -26,42 +28,66 @@ class ItemEditPopup extends AbstractPopupScreen { private final QuickNavigationConfig.QuickNavItem item; private final QuickNavConfigScreen.ConfigItemSetter setter; private final LinearLayout layout = LinearLayout.vertical().spacing(4); - private TextFieldSuggestions suggestions; + + private int currentCount; + private String currentTooltip; protected ItemEditPopup(Screen backgroundScreen, Runnable onClose, QuickNavigationConfig.QuickNavItem item, QuickNavConfigScreen.ConfigItemSetter setter) { super(Component.literal("Edit button or something"), backgroundScreen); this.onClose = onClose; this.item = item; this.setter = setter; + currentCount = item.itemData.count; + currentTooltip = item.tooltip; } @Override protected void init() { - EditBox box = layout.addChild(new EditBox(font, 250, 20, Component.empty())); - this.suggestions = new TextFieldSuggestions( - this.minecraft, this, box, this.font, true, 7, - RequiredArgumentBuilder.argument("argument", new ComponentArgument(TextFieldSuggestions.getContext())).build() + CommandBuildContext context = TextFieldSuggestions.getContext(); + // tooltip + layout.addChild(new StringWidget(Component.literal("Tooltip"), font)); + AutocompleteEditBox.Argument tooltipBox = AutocompleteEditBox.builder().width(300).autoTrim(false).buildArg( + minecraft, font, this, Component.empty(), + ArgumentWithAlternatives.of(ComponentArgument.textComponent(context), StringArgumentType.greedyString(), Component::literal) ); - this.suggestions.setAllowSuggestions(true); - layout.visitWidgets(this::addRenderableWidget); - box.setResponder(_ -> suggestions.updateCommandInfo()); - super.init(); - } + tooltipBox.setMaxLength(4096); + tooltipBox.setValue(item.tooltip); + tooltipBox.setResponder(s -> tooltipBox.getParsedValue().ifPresent(_ -> currentTooltip = s)); + layout.addChild(tooltipBox); - @Override - public boolean keyPressed(KeyEvent event) { - if (suggestions.keyPressed(event)) return true; - return super.keyPressed(event); - } - @Override - public boolean mouseScrolled(double x, double y, double scrollX, double scrollY) { - return suggestions.mouseScrolled(scrollY) || super.mouseScrolled(x, y, scrollX, scrollY); - } + // item selection + GridLayout itemLayout = layout.addChild(new GridLayout()).columnSpacing(4).rowSpacing(2); - @Override - public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { - return suggestions.mouseClicked(event) || super.mouseClicked(event, doubleClick); + ItemStack stack = ItemStackComponentizationFixer.fromComponentsString(item.itemData.item.toString(), item.itemData.count, item.itemData.components); + ItemWidget itemWidget = itemLayout.addChild(new ItemWidget(stack), 1, 0, l -> l.alignVerticallyMiddle().alignHorizontallyCenter()); + int itemWidth = 250; + itemLayout.addChild(new StringWidget(Component.literal("Item"), font), 0, 1).setMaxWidth(itemWidth, StringWidget.TextOverflow.SCROLLING); + AutocompleteEditBox.Argument itemBox = AutocompleteEditBox.builder().width(250).buildArg( + minecraft, font, this, Component.empty(), + new ItemArgument(context) + ); + itemLayout.addChild(itemBox, 1, 1); + itemBox.setMaxLength(4096); + itemBox.setValue(item.itemData.item + item.itemData.components); + + int countWidth = 30; + itemLayout.addChild(new StringWidget(Component.literal("Count"), font), 0, 2).setMaxWidth(countWidth, StringWidget.TextOverflow.SCROLLING); + AutocompleteEditBox.Argument countBox = AutocompleteEditBox.builder().width(20).buildArg( + minecraft, font, this, Component.empty(), + IntegerArgumentType.integer(1) + ); + itemLayout.addChild(countBox, 1, 2); + countBox.setMaxLength(2); + countBox.setValue(String.valueOf(item.itemData.count)); + + layout.visitWidgets(this::addRenderableWidget); + itemBox.setValueResponder(itemInput -> itemWidget.itemStack = new ItemStack(itemInput.item(), currentCount, itemInput.components())); + countBox.setValueResponder(count -> { + currentCount = Math.max(count, 1); + itemWidget.itemStack = itemWidget.itemStack.copyWithCount(currentCount); + }); + super.init(); } @Override @@ -73,8 +99,6 @@ public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mou @Override public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { super.extractRenderState(graphics, mouseX, mouseY, a); - suggestions.extractRenderState(graphics, mouseX, mouseY); - graphics.text(font, String.valueOf(suggestions.isVisible()), 0, 0, -1); } @Override @@ -89,4 +113,23 @@ public void onClose() { super.onClose(); onClose.run(); } + + private static class ItemWidget extends AbstractWidget { + private ItemStack itemStack; + + private ItemWidget(ItemStack stack) { + super(0, 0, 16, 16, stack.getItemName()); + this.itemStack = stack; + } + + @Override + protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + graphics.item(itemStack, getX(), getY()); + } + + @Override + protected void updateWidgetNarration(NarrationElementOutput output) { + + } + } } diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/ArgumentWithAlternatives.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/ArgumentWithAlternatives.java new file mode 100644 index 00000000000..3cd6b4f5ab4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/ArgumentWithAlternatives.java @@ -0,0 +1,104 @@ +package de.hysky.skyblocker.utils.command.argumenttypes; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandExceptionType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import com.mojang.datafixers.util.Either; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.stream.Stream; + +public final class ArgumentWithAlternatives { + public static final CommandExceptionType TYPE = new CommandExceptionType() {}; + + public static ArgumentType> of(final ArgumentType a, final ArgumentType b) { + return new EitherArgumentType<>(a, b); + } + + public static ArgumentType of(final ArgumentType a, final ArgumentType b, Function converter) { + EitherArgumentType type = new EitherArgumentType<>(a, b); + return new ArgumentType<>() { + @Override + public A parse(StringReader reader) throws CommandSyntaxException { + return type.parse(reader).map(Function.identity(), converter); + } + + @Override + public A parse(StringReader reader, S source) throws CommandSyntaxException { + return type.parse(reader).map(Function.identity(), converter); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return type.listSuggestions(context, builder); + } + + @Override + public Collection getExamples() { + return type.getExamples(); + } + }; + } + + private record EitherArgumentType(ArgumentType a, ArgumentType b) implements ArgumentType> { + + @Override + public Either parse(StringReader reader) throws CommandSyntaxException { + CommandSyntaxException first; + int cursor = reader.getCursor(); + try { + A parse = a.parse(reader); + System.out.println("Got A: " + parse); + return Either.left(parse); + } catch (CommandSyntaxException e) { + first = e; + } + reader.setCursor(cursor); + try { + B parse = b.parse(reader); + System.out.println("Got B: " + parse); + return Either.right(parse); + } catch (CommandSyntaxException e) { + throw new CommandSyntaxException(TYPE, () -> first.getMessage() + " or " + first.getMessage()); + } + } + + @Override + public Either parse(StringReader reader, S source) throws CommandSyntaxException { + CommandSyntaxException first; + int cursor = reader.getCursor(); + try { + return Either.left(a.parse(reader, source)); + } catch (CommandSyntaxException e) { + first = e; + } + reader.setCursor(cursor); + try { + return Either.right(b.parse(reader, source)); + } catch (CommandSyntaxException e) { + throw new CommandSyntaxException(TYPE, () -> first.getMessage() + " or " + first.getMessage()); + } + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + CompletableFuture first = a.listSuggestions(context, builder); + CompletableFuture second = b.listSuggestions(context, builder); + final CompletableFuture result = new CompletableFuture<>(); + CompletableFuture.allOf(first, second).thenRun(() -> result.complete(Suggestions.merge(context.getInput(), List.of(first.join(), second.join())))); + return result; + } + + @Override + public Collection getExamples() { + return Stream.concat(a.getExamples().stream(), a.getExamples().stream()).toList(); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/GreedyComponentArgument.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/GreedyComponentArgument.java new file mode 100644 index 00000000000..3c1f30fe99d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/GreedyComponentArgument.java @@ -0,0 +1,53 @@ +package de.hysky.skyblocker.utils.command.argumenttypes; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.arguments.ComponentArgument; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +public class GreedyComponentArgument implements ArgumentType { + + private final ComponentArgument delegate; + + public GreedyComponentArgument(ComponentArgument delegate) { + this.delegate = delegate; + } + + @Override + public Component parse(StringReader reader) throws CommandSyntaxException { + if (reader.getRemaining().indexOf(' ') >= 0 && reader.peek() != '"' && reader.peek() != '\'') { + MutableComponent literal = Component.literal(reader.getRemaining()); + reader.setCursor(reader.getTotalLength()); + return literal; + } + return delegate.parse(reader); + } + + @Override + public Component parse(StringReader reader, S source) throws CommandSyntaxException { + if (reader.getRemaining().indexOf(' ') >= 0 && reader.peek() != '"' && reader.peek() != '\'') { + MutableComponent literal = Component.literal(reader.getRemaining()); + reader.setCursor(reader.getTotalLength()); + return literal; + } + return delegate.parse(reader, source); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return delegate.listSuggestions(context, builder); + } + + @Override + public Collection getExamples() { + return delegate.getExamples(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java index 9d0d13b535b..f54a3bfe4d4 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java @@ -2,6 +2,8 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.context.SuggestionContext; import com.mojang.brigadier.tree.CommandNode; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; @@ -13,12 +15,18 @@ import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.Rect2i; import net.minecraft.commands.CommandBuildContext; +import net.minecraft.network.chat.Style; import net.minecraft.util.ARGB; import net.minecraft.util.FormattedCharSequence; +import org.jspecify.annotations.Nullable; + +import java.util.List; +import java.util.Optional; public class TextFieldSuggestions extends CommandSuggestions { private final CommandDispatcher dispatcher; + private @Nullable CommandContext context; public TextFieldSuggestions(Minecraft minecraft, Screen screen, EditBox input, Font font, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node) { super(minecraft, screen, input, font, true, onlyShowIfCursorPastError, 0, suggestionLineLimit, false, ARGB.black(0.5f)); @@ -49,6 +57,9 @@ public void showSuggestions(boolean immediateNarration) { } } + /** + * Overridden to change the position. + */ @Override public void extractUsage(GuiGraphicsExtractor graphics) { int y = 0; @@ -61,11 +72,16 @@ public void extractUsage(GuiGraphicsExtractor graphics) { } } + public Optional getArgument(String argument, Class type) { + return Optional.ofNullable(context).map(c -> c.getArgument(argument, type)); + } + @Override public void updateCommandInfo() { String command = this.input.getValue(); if (this.currentParse != null && !this.currentParse.getReader().getString().equals(command)) { this.currentParse = null; + this.context = null; } if (!this.keepSuggestions) { @@ -79,6 +95,7 @@ public void updateCommandInfo() { CommandDispatcher commands = dispatcher; if (this.currentParse == null) { this.currentParse = commands.parse(reader, this.minecraft.player.connection.getSuggestionsProvider()); + if (currentParse.getExceptions().isEmpty()) this.context = currentParse.getContext().build(command); } int parseStart = this.onlyShowIfCursorPastError ? reader.getCursor() : 1; @@ -91,4 +108,12 @@ public void updateCommandInfo() { }); } } + + /** + * Overridden to hide the {@code } usage hint that shows up if your cursor is at the end + */ + @Override + protected List fillNodeUsage(SuggestionContext suggestionContext, Style usageFormat) { + return List.of(); + } } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AutocompleteEditBox.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/AutocompleteEditBox.java new file mode 100644 index 00000000000..4577aec2742 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/AutocompleteEditBox.java @@ -0,0 +1,179 @@ +package de.hysky.skyblocker.utils.render.gui; + +import com.mojang.blaze3d.platform.InputConstants; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.tree.ArgumentCommandNode; +import com.mojang.brigadier.tree.CommandNode; +import de.hysky.skyblocker.utils.command.suggestions.TextFieldSuggestions; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.client.multiplayer.ClientSuggestionProvider; +import net.minecraft.network.chat.Component; +import org.jspecify.annotations.Nullable; + +import java.util.Optional; +import java.util.function.Consumer; + +public class AutocompleteEditBox extends EditBox { + + private final TextFieldSuggestions suggestions; + private final boolean autoTrim; + private final @Nullable String argument; + private @Nullable Consumer responder; + + public AutocompleteEditBox(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node, boolean autoTrim) { + super(font, width, height, narration); + this.suggestions = new TextFieldSuggestions(minecraft, screen, this, font, onlyShowIfCursorPastError, suggestionLineLimit, node); + this.autoTrim = autoTrim; + suggestions.setAllowSuggestions(true); + super.setResponder(this::onUpdate); + if (node instanceof ArgumentCommandNode argumentCommandNode) argument = argumentCommandNode.getName(); + else argument = null; + } + + @Override + public void setResponder(Consumer responder) { + this.responder = responder; + } + + public boolean isValid() { + return suggestions.currentParse != null && suggestions.currentParse.getExceptions().isEmpty(); + } + + private void onUpdate(String string) { + if (autoTrim && !string.trim().equals(string)) setValue(string.trim()); + else { + suggestions.updateCommandInfo(); + if (responder != null) responder.accept(string); + } + } + + public Optional getParsedValue(Class type) { + return argument == null ? Optional.empty() : getParsedValue(argument, type); + } + + public Optional getParsedValue(String argument, Class type) { + return suggestions.getArgument(argument, type); + } + + + @Override + public void setFocused(boolean focused) { + super.setFocused(focused); + if (!focused) suggestions.hide(); + else suggestions.updateCommandInfo(); + } + + @Override + public boolean isMouseOver(double mouseX, double mouseY) { + return super.isMouseOver(mouseX, mouseY) || (suggestions.suggestions != null && suggestions.suggestions.rect.contains((int) mouseX, (int) mouseY)); + } + + @Override + public boolean mouseScrolled(double x, double y, double scrollX, double scrollY) { + if (suggestions.mouseScrolled(scrollY)) return true; + return super.mouseScrolled(x, y, scrollX, scrollY); + } + + @Override + public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { + if (suggestions.mouseClicked(event)) return true; + return super.mouseClicked(event, doubleClick); + } + + @Override + public void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + super.extractWidgetRenderState(graphics, mouseX, mouseY, a); + if (visible) suggestions.extractRenderState(graphics, mouseX, mouseY); + } + + @Override + public boolean keyPressed(KeyEvent event) { + if (suggestions.keyPressed(event)) return true; + if (this.isActive() && this.isFocused() && event.key() == InputConstants.KEY_ESCAPE) { + setFocused(false); + return true; + } + return super.keyPressed(event); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private int width = 150; + private int height = 20; + private int suggestionLineLimit = 7; + private boolean onlyShowIfCursorPastError = true; + private boolean autoTrim = true; + + public Builder width(int width) { + this.width = width; + return this; + } + + public Builder height(int height) { + this.height = height; + return this; + } + + public Builder suggestionLineLimit(int suggestionLineLimit) { + this.suggestionLineLimit = suggestionLineLimit; + return this; + } + + public Builder onlyShowIfCursorPastError(boolean onlyShowIfCursorPastError) { + this.onlyShowIfCursorPastError = onlyShowIfCursorPastError; + return this; + } + + public Builder autoTrim(boolean autoTrim) { + this.autoTrim = autoTrim; + return this; + } + + public AutocompleteEditBox build(Screen screen, Component narration, CommandNode node) { + return build(Minecraft.getInstance(), Minecraft.getInstance().font, screen, narration, node); + } + + public AutocompleteEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, ArgumentBuilder builder) { + return build(minecraft, font, screen, narration, builder.build()); + } + + public Argument buildArg(Minecraft minecraft, Font font, Screen screen, Component narration, ArgumentType argumentType) { + return new Argument<>(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, argumentType, autoTrim); + } + + public AutocompleteEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, CommandNode node) { + return new AutocompleteEditBox(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, node, autoTrim); + } + } + + public static class Argument extends AutocompleteEditBox { + + public Argument(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, ArgumentType argumentType, boolean autoTrim) { + super(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, RequiredArgumentBuilder.argument("argument", argumentType).build(), autoTrim); + + } + + public Optional getParsedValue() { + return (Optional) getParsedValue(Object.class); + } + + public void setValueResponder(Consumer valueResponder) { + setResponder(_ -> getParsedValue().ifPresent(valueResponder)); + } + + public void setOptionalValueResponder(Consumer<@Nullable T> optionalValueResponder) { + setResponder(_ -> optionalValueResponder.accept(getParsedValue().orElse(null))); + } + } +} diff --git a/src/main/resources/skyblocker.classtweaker b/src/main/resources/skyblocker.classtweaker index 3d461a96bfb..dce13ba614e 100644 --- a/src/main/resources/skyblocker.classtweaker +++ b/src/main/resources/skyblocker.classtweaker @@ -92,7 +92,8 @@ accessible field net/minecraft/client/gui/components/CommandSuggestions commandU accessible field net/minecraft/client/gui/components/CommandSuggestions fillColor I mutable field net/minecraft/client/gui/components/CommandSuggestions$SuggestionsList rect Lnet/minecraft/client/renderer/Rect2i; accessible field net/minecraft/client/gui/components/CommandSuggestions$SuggestionsList rect Lnet/minecraft/client/renderer/Rect2i; -accessible method net/minecraft/client/gui/components/CommandSuggestions updateUsageInfo (Lcom/mojang/brigadier/ParseResults;Lcom/mojang/brigadier/suggestion/Suggestions;)V +extendable method net/minecraft/client/gui/components/CommandSuggestions updateUsageInfo (Lcom/mojang/brigadier/ParseResults;Lcom/mojang/brigadier/suggestion/Suggestions;)V +extendable method net/minecraft/client/gui/components/CommandSuggestions fillNodeUsage (Lcom/mojang/brigadier/context/SuggestionContext;Lnet/minecraft/network/chat/Style;)Ljava/util/List; extendable method net/minecraft/commands/arguments/item/ItemParser$State (Lnet/minecraft/commands/arguments/item/ItemParser;Lcom/mojang/brigadier/StringReader;Lnet/minecraft/commands/arguments/item/ItemParser$Visitor;)V extendable method net/minecraft/commands/arguments/item/ItemParser$State suggestItem (Lcom/mojang/brigadier/suggestion/SuggestionsBuilder;)Ljava/util/concurrent/CompletableFuture; extendable method net/minecraft/commands/arguments/item/ItemParser$State readItem ()V From 144a0494a384a06678106cec0431f389ca36e55f Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Sun, 26 Jul 2026 18:07:50 +0200 Subject: [PATCH 03/15] some more progress --- .../screens/quicknav/ItemEditPopup.java | 80 +++++++++++--- .../skyblock/item/custom/screen/ItemTab.java | 48 +++++--- .../ArgumentWithAlternatives.java | 104 ------------------ .../GreedyComponentArgument.java | 53 --------- .../argumenttypes/RegexArgumentType.java | 28 +++++ .../suggestions/TextFieldSuggestions.java | 4 +- .../utils/render/gui/AbstractPopupScreen.java | 6 + .../render/gui/ComponentEditWidget.java} | 58 ++++------ ...teEditBox.java => SuggestionsEditBox.java} | 20 ++-- 9 files changed, 165 insertions(+), 236 deletions(-) delete mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/ArgumentWithAlternatives.java delete mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/GreedyComponentArgument.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/RegexArgumentType.java rename src/main/java/de/hysky/skyblocker/{skyblock/item/custom/screen/name/CustomizeNameWidget.java => utils/render/gui/ComponentEditWidget.java} (90%) rename src/main/java/de/hysky/skyblocker/utils/render/gui/{AutocompleteEditBox.java => SuggestionsEditBox.java} (84%) diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index 298d32c9fa3..64d4b3f0ea0 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -1,36 +1,45 @@ package de.hysky.skyblocker.config.screens.quicknav; +import com.google.gson.JsonElement; import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.serialization.JsonOps; +import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.configs.QuickNavigationConfig; -import de.hysky.skyblocker.utils.command.argumenttypes.ArgumentWithAlternatives; +import de.hysky.skyblocker.utils.command.argumenttypes.RegexArgumentType; import de.hysky.skyblocker.utils.command.suggestions.TextFieldSuggestions; import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; import de.hysky.skyblocker.utils.render.gui.AbstractPopupScreen; -import de.hysky.skyblocker.utils.render.gui.AutocompleteEditBox; +import de.hysky.skyblocker.utils.render.gui.SuggestionsEditBox; +import de.hysky.skyblocker.utils.render.gui.ComponentEditWidget; +import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.StringWidget; +import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.layouts.GridLayout; +import net.minecraft.client.gui.layouts.Layout; import net.minecraft.client.gui.layouts.LinearLayout; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.screens.Screen; import net.minecraft.commands.CommandBuildContext; -import net.minecraft.commands.arguments.ComponentArgument; import net.minecraft.commands.arguments.item.ItemArgument; import net.minecraft.commands.arguments.item.ItemInput; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentSerialization; +import net.minecraft.util.ARGB; import net.minecraft.world.item.ItemStack; +import java.util.regex.Pattern; + class ItemEditPopup extends AbstractPopupScreen { private final Runnable onClose; private final QuickNavigationConfig.QuickNavItem item; private final QuickNavConfigScreen.ConfigItemSetter setter; - private final LinearLayout layout = LinearLayout.vertical().spacing(4); + private final LinearLayout layout = LinearLayout.vertical().spacing(10); private int currentCount; - private String currentTooltip; + private Component currentTooltip; protected ItemEditPopup(Screen backgroundScreen, Runnable onClose, QuickNavigationConfig.QuickNavItem item, QuickNavConfigScreen.ConfigItemSetter setter) { super(Component.literal("Edit button or something"), backgroundScreen); @@ -38,32 +47,58 @@ protected ItemEditPopup(Screen backgroundScreen, Runnable onClose, QuickNavigati this.item = item; this.setter = setter; currentCount = item.itemData.count; - currentTooltip = item.tooltip; + try { + currentTooltip = ComponentSerialization.CODEC.decode(JsonOps.INSTANCE, SkyblockerMod.GSON.fromJson(item.tooltip, JsonElement.class)).getOrThrow().getFirst(); + } catch (Exception e) { + currentTooltip = Component.literal(item.tooltip); + } } @Override protected void init() { + layout.defaultCellSetting().padding(3); CommandBuildContext context = TextFieldSuggestions.getContext(); + LinearLayout commandLayout = layout.addChild(LinearLayout.vertical().spacing(2)); + addTitle(commandLayout, "Tooltip"); + SuggestionsEditBox commandBox = SuggestionsEditBox.builder().autoTrim(false).width(250).onlyShowIfCursorPastError(false).build( + minecraft, font, this, Component.empty(), + minecraft.player.connection.getCommands().getRoot() + ); + commandLayout.addChild(commandBox); + renderAroundLayout(commandLayout); + // tooltip - layout.addChild(new StringWidget(Component.literal("Tooltip"), font)); - AutocompleteEditBox.Argument tooltipBox = AutocompleteEditBox.builder().width(300).autoTrim(false).buildArg( + LinearLayout tooltipLayout = layout.addChild(LinearLayout.vertical().spacing(2)); + addTitle(tooltipLayout, "Tooltip"); + ComponentEditWidget editWidget = new ComponentEditWidget(this, Component.literal("Customize Tooltip"), component -> currentTooltip = component.copy()); + tooltipLayout.addChild(editWidget); + editWidget.setText(currentTooltip.copy(), false); + renderAroundLayout(tooltipLayout); + + // menu regex + LinearLayout regexLayout = layout.addChild(LinearLayout.vertical().spacing(2)); + addTitle(regexLayout, "Menu Title"); + SuggestionsEditBox.Argument patternBox = SuggestionsEditBox.builder().autoTrim(false).width(250).onlyShowIfCursorPastError(false).buildArg( minecraft, font, this, Component.empty(), - ArgumentWithAlternatives.of(ComponentArgument.textComponent(context), StringArgumentType.greedyString(), Component::literal) + new RegexArgumentType() ); - tooltipBox.setMaxLength(4096); - tooltipBox.setValue(item.tooltip); - tooltipBox.setResponder(s -> tooltipBox.getParsedValue().ifPresent(_ -> currentTooltip = s)); - layout.addChild(tooltipBox); + patternBox.setTooltip(Tooltip.create(Component.literal("The button will appear pressed in the menu matching this title. This supports Regex!"))); + patternBox.setMaxLength(2048); + patternBox.setValue(item.uiTitle); + regexLayout.addChild(patternBox); + renderAroundLayout(regexLayout); // item selection - GridLayout itemLayout = layout.addChild(new GridLayout()).columnSpacing(4).rowSpacing(2); + LinearLayout iconLayout = layout.addChild(LinearLayout.vertical().spacing(2)); + addTitle(iconLayout, "Icon"); + GridLayout itemLayout = iconLayout.addChild(new GridLayout()).columnSpacing(4).rowSpacing(2); ItemStack stack = ItemStackComponentizationFixer.fromComponentsString(item.itemData.item.toString(), item.itemData.count, item.itemData.components); ItemWidget itemWidget = itemLayout.addChild(new ItemWidget(stack), 1, 0, l -> l.alignVerticallyMiddle().alignHorizontallyCenter()); int itemWidth = 250; itemLayout.addChild(new StringWidget(Component.literal("Item"), font), 0, 1).setMaxWidth(itemWidth, StringWidget.TextOverflow.SCROLLING); - AutocompleteEditBox.Argument itemBox = AutocompleteEditBox.builder().width(250).buildArg( + SuggestionsEditBox.Argument itemBox = SuggestionsEditBox.builder().width(250).buildArg( minecraft, font, this, Component.empty(), new ItemArgument(context) ); @@ -73,13 +108,14 @@ protected void init() { int countWidth = 30; itemLayout.addChild(new StringWidget(Component.literal("Count"), font), 0, 2).setMaxWidth(countWidth, StringWidget.TextOverflow.SCROLLING); - AutocompleteEditBox.Argument countBox = AutocompleteEditBox.builder().width(20).buildArg( + SuggestionsEditBox.Argument countBox = SuggestionsEditBox.builder().width(20).buildArg( minecraft, font, this, Component.empty(), IntegerArgumentType.integer(1) ); itemLayout.addChild(countBox, 1, 2); countBox.setMaxLength(2); countBox.setValue(String.valueOf(item.itemData.count)); + renderAroundLayout(iconLayout); layout.visitWidgets(this::addRenderableWidget); itemBox.setValueResponder(itemInput -> itemWidget.itemStack = new ItemStack(itemInput.item(), currentCount, itemInput.components())); @@ -90,10 +126,18 @@ protected void init() { super.init(); } + private void addTitle(LinearLayout layout, String title) { + layout.addChild(new StringWidget(Component.literal(title).withStyle(ChatFormatting.BOLD), font), l -> l.paddingBottom(4)); + } + + private void renderAroundLayout(Layout target) { + addRenderableOnly(((graphics, _, _, _) -> graphics.fill(layout.getX(), target.getY() - 3, layout.getX() + layout.getWidth(), target.getY() + target.getHeight() + 3, ARGB.black(0.15f)))); + } + @Override public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { super.extractBackground(graphics, mouseX, mouseY, a); - extractPopupBackground(graphics, layout.getX(), layout.getY(), layout.getWidth(), layout.getHeight()); + extractLighterPopupBackground(graphics, layout.getX(), layout.getY(), layout.getWidth(), layout.getHeight()); } @Override diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ItemTab.java b/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ItemTab.java index 3ef71c35b8c..fb2b6d593d4 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ItemTab.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ItemTab.java @@ -3,8 +3,8 @@ import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.item.SkyblockInventoryScreen; -import de.hysky.skyblocker.skyblock.item.custom.screen.name.CustomizeNameWidget; import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.gui.ComponentEditWidget; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; @@ -16,6 +16,7 @@ import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.components.tabs.GridLayoutTab; +import net.minecraft.client.gui.layouts.Layout; import net.minecraft.client.gui.layouts.LinearLayout; import net.minecraft.client.gui.layouts.SpacerElement; import net.minecraft.client.gui.narration.NarrationElementOutput; @@ -23,6 +24,7 @@ import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; import net.minecraft.resources.Identifier; import net.minecraft.util.TriState; import net.minecraft.world.item.ItemStack; @@ -36,7 +38,7 @@ public class ItemTab extends GridLayoutTab { private static final Identifier INNER_SPACE_TEXTURE = SkyblockerMod.id("menu_inner_space"); private final CustomizeScreen parentScreen; - private final CustomizeNameWidget nameWidget; + private final ComponentEditWidget nameWidget; private final Button glintButton; private final IdentifierTextField modelField; @@ -70,12 +72,19 @@ public ItemTab(CustomizeScreen parentScreen) { }); }); modelField.setHint(Component.translatable("skyblocker.customization.item.modelOverride").withStyle(ChatFormatting.ITALIC).withStyle(ChatFormatting.GRAY)); - nameWidget = new CustomizeNameWidget(parentScreen); + nameWidget = new ComponentEditWidget(parentScreen, Component.literal("Customize Item Name"), component -> { + String uuid = currentItem.getUuid(); + if (uuid.isEmpty()) return; + SkyblockerConfigManager.updateOnly(config -> { + if (component.getString().isBlank()) config.general.customItemNames.remove(currentItem.getUuid()); + else config.general.customItemNames.put(currentItem.getUuid(), component.copy().setStyle(Style.EMPTY.withItalic(false).withColor(ChatFormatting.WHITE))); + }); + }); layout.addChild(new ItemSelector(), 0, 0, 1, 1); - layout.addChild(new BackgroundRenderer(), 0, 1); - LinearLayout linearLayout = layout.addChild(LinearLayout.vertical(), 0, 1, p -> p.alignHorizontallyRight().paddingRight(3).paddingVertical(3)); + + LinearLayout linearLayout = LinearLayout.vertical(); linearLayout.addChild(glintButton); linearLayout.addChild(Button.builder(Component.translatable("skyblocker.customization.item.selectModel"), _ -> { Minecraft minecraft = Minecraft.getInstance(); @@ -87,8 +96,15 @@ public ItemTab(CustomizeScreen parentScreen) { minecraft.gui.setScreen(new ModelSelectionPopup(parentScreen, applyItemModel)); }).width(120).build(), p -> p.paddingTop(4)); linearLayout.addChild(modelField); + layout.addChild(new BackgroundRenderer(linearLayout, 3), 0, 1); + layout.addChild(linearLayout, 0, 1, p -> p.alignHorizontallyRight().paddingRight(3).paddingVertical(3)); + + LinearLayout nameLayout = LinearLayout.vertical().spacing(2); + nameLayout.addChild(nameWidget); + nameLayout.addChild(new StringWidget(20 * 16, parentScreen.getFont().lineHeight, Component.translatable("skyblocker.customItemNames.screen.howToRemove").withStyle(ChatFormatting.ITALIC, ChatFormatting.GRAY), parentScreen.getFont())); + layout.addChild(new BackgroundRenderer(nameLayout, 3), 1, 0); + layout.addChild(nameLayout, 1, 0, 1, 2, p -> p.padding(3)); - layout.addChild(nameWidget, 1, 0, 1, 2); LocalPlayer player = Minecraft.getInstance().player; ItemStack handStack = player.getMainHandItem(); @@ -120,7 +136,7 @@ private void setCurrentItem(ItemStack itemStack) { visitChildren(clickableWidget -> clickableWidget.visible = !empty); if (empty) return; parentScreen.backupConfigs(itemStack); - nameWidget.setItem(itemStack); + nameWidget.setText(itemStack.getHoverName().copy(), false); if (SkyblockerConfigManager.get().general.customItemModel.containsKey(uuid)) { Identifier identifier = SkyblockerConfigManager.get().general.customItemModel.get(uuid); String string = identifier.toString(); @@ -217,23 +233,25 @@ protected void updateWidgetNarration(NarrationElementOutput builder) { } } - private class BackgroundRenderer extends AbstractWidget { + private static class BackgroundRenderer extends AbstractWidget { + private final Layout layout; + private final int padding; - BackgroundRenderer() { + BackgroundRenderer(Layout layout, int padding) { super(0, 0, 0, 0, Component.empty()); active = false; + this.layout = layout; + this.padding = padding; } @Override protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { - int x = glintButton.getX() - 3; - int y = glintButton.getY() - 3; graphics.blitSprite(RenderPipelines.GUI_TEXTURED, INNER_SPACE_TEXTURE, - x, - y, - modelField.getRight() + 3 - x, - modelField.getBottom() + 3 - y + layout.getX() - padding, + layout.getY() - padding, + layout.getWidth() + padding * 2, + layout.getHeight() + padding * 2 ); } diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/ArgumentWithAlternatives.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/ArgumentWithAlternatives.java deleted file mode 100644 index 3cd6b4f5ab4..00000000000 --- a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/ArgumentWithAlternatives.java +++ /dev/null @@ -1,104 +0,0 @@ -package de.hysky.skyblocker.utils.command.argumenttypes; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandExceptionType; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import com.mojang.datafixers.util.Either; - -import java.util.Collection; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; -import java.util.stream.Stream; - -public final class ArgumentWithAlternatives { - public static final CommandExceptionType TYPE = new CommandExceptionType() {}; - - public static ArgumentType> of(final ArgumentType a, final ArgumentType b) { - return new EitherArgumentType<>(a, b); - } - - public static ArgumentType of(final ArgumentType a, final ArgumentType b, Function converter) { - EitherArgumentType type = new EitherArgumentType<>(a, b); - return new ArgumentType<>() { - @Override - public A parse(StringReader reader) throws CommandSyntaxException { - return type.parse(reader).map(Function.identity(), converter); - } - - @Override - public A parse(StringReader reader, S source) throws CommandSyntaxException { - return type.parse(reader).map(Function.identity(), converter); - } - - @Override - public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { - return type.listSuggestions(context, builder); - } - - @Override - public Collection getExamples() { - return type.getExamples(); - } - }; - } - - private record EitherArgumentType(ArgumentType a, ArgumentType b) implements ArgumentType> { - - @Override - public Either parse(StringReader reader) throws CommandSyntaxException { - CommandSyntaxException first; - int cursor = reader.getCursor(); - try { - A parse = a.parse(reader); - System.out.println("Got A: " + parse); - return Either.left(parse); - } catch (CommandSyntaxException e) { - first = e; - } - reader.setCursor(cursor); - try { - B parse = b.parse(reader); - System.out.println("Got B: " + parse); - return Either.right(parse); - } catch (CommandSyntaxException e) { - throw new CommandSyntaxException(TYPE, () -> first.getMessage() + " or " + first.getMessage()); - } - } - - @Override - public Either parse(StringReader reader, S source) throws CommandSyntaxException { - CommandSyntaxException first; - int cursor = reader.getCursor(); - try { - return Either.left(a.parse(reader, source)); - } catch (CommandSyntaxException e) { - first = e; - } - reader.setCursor(cursor); - try { - return Either.right(b.parse(reader, source)); - } catch (CommandSyntaxException e) { - throw new CommandSyntaxException(TYPE, () -> first.getMessage() + " or " + first.getMessage()); - } - } - - @Override - public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { - CompletableFuture first = a.listSuggestions(context, builder); - CompletableFuture second = b.listSuggestions(context, builder); - final CompletableFuture result = new CompletableFuture<>(); - CompletableFuture.allOf(first, second).thenRun(() -> result.complete(Suggestions.merge(context.getInput(), List.of(first.join(), second.join())))); - return result; - } - - @Override - public Collection getExamples() { - return Stream.concat(a.getExamples().stream(), a.getExamples().stream()).toList(); - } - } -} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/GreedyComponentArgument.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/GreedyComponentArgument.java deleted file mode 100644 index 3c1f30fe99d..00000000000 --- a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/GreedyComponentArgument.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.hysky.skyblocker.utils.command.argumenttypes; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.arguments.ComponentArgument; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; - -import java.util.Collection; -import java.util.concurrent.CompletableFuture; - -public class GreedyComponentArgument implements ArgumentType { - - private final ComponentArgument delegate; - - public GreedyComponentArgument(ComponentArgument delegate) { - this.delegate = delegate; - } - - @Override - public Component parse(StringReader reader) throws CommandSyntaxException { - if (reader.getRemaining().indexOf(' ') >= 0 && reader.peek() != '"' && reader.peek() != '\'') { - MutableComponent literal = Component.literal(reader.getRemaining()); - reader.setCursor(reader.getTotalLength()); - return literal; - } - return delegate.parse(reader); - } - - @Override - public Component parse(StringReader reader, S source) throws CommandSyntaxException { - if (reader.getRemaining().indexOf(' ') >= 0 && reader.peek() != '"' && reader.peek() != '\'') { - MutableComponent literal = Component.literal(reader.getRemaining()); - reader.setCursor(reader.getTotalLength()); - return literal; - } - return delegate.parse(reader, source); - } - - @Override - public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { - return delegate.listSuggestions(context, builder); - } - - @Override - public Collection getExamples() { - return delegate.getExamples(); - } -} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/RegexArgumentType.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/RegexArgumentType.java new file mode 100644 index 00000000000..b0d8d5dfc74 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/RegexArgumentType.java @@ -0,0 +1,28 @@ +package de.hysky.skyblocker.utils.command.argumenttypes; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.exceptions.CommandExceptionType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * Greedy. + */ +public class RegexArgumentType implements ArgumentType { + public static final CommandExceptionType EXCEPTION_TYPE = new CommandExceptionType() {}; + @Override + public Pattern parse(StringReader reader) throws CommandSyntaxException { + int cursor = reader.getCursor(); + try { + Pattern compile = Pattern.compile(reader.getRemaining()); + reader.setCursor(reader.getTotalLength()); + return compile; + } catch (PatternSyntaxException e) { + reader.setCursor(cursor); + throw new CommandSyntaxException(EXCEPTION_TYPE, e::getDescription, reader.getRemaining(), e.getIndex()); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java index f54a3bfe4d4..4cb09390dc7 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java @@ -15,9 +15,11 @@ import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.Rect2i; import net.minecraft.commands.CommandBuildContext; +import net.minecraft.data.registries.VanillaRegistries; import net.minecraft.network.chat.Style; import net.minecraft.util.ARGB; import net.minecraft.util.FormattedCharSequence; +import net.minecraft.world.flag.FeatureFlagSet; import org.jspecify.annotations.Nullable; import java.util.List; @@ -38,9 +40,9 @@ public TextFieldSuggestions(Screen screen, EditBox input, boolean onlyShowIfCurs this(Minecraft.getInstance(), screen, input, Minecraft.getInstance().font, onlyShowIfCursorPastError, suggestionLineLimit, node); } - // FIXME nullable shit public static CommandBuildContext getContext() { LocalPlayer player = Minecraft.getInstance().player; + if (player == null) return CommandBuildContext.simple(VanillaRegistries.createLookup(), FeatureFlagSet.of()); return CommandBuildContext.simple(player.connection.registryAccess(), player.connection.enabledFeatures()); } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java index e244d6fdf8e..46a5b59c71f 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java @@ -1,5 +1,6 @@ package de.hysky.skyblocker.utils.render.gui; +import de.hysky.skyblocker.SkyblockerMod; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.EditBox; @@ -15,6 +16,7 @@ */ public class AbstractPopupScreen extends Screen { private static final Identifier BACKGROUND_TEXTURE = Identifier.withDefaultNamespace("popup/background"); + private static final Identifier LIGHTER_BACKGROUND_TEXTURE = SkyblockerMod.id("background"); public final Screen backgroundScreen; protected AbstractPopupScreen(Component title, Screen backgroundScreen) { @@ -43,6 +45,10 @@ public static void extractPopupBackground(GuiGraphicsExtractor graphics, int x, graphics.blitSprite(RenderPipelines.GUI_TEXTURED, BACKGROUND_TEXTURE, x - 18, y - 18, width + 36, height + 36); } + public static void extractLighterPopupBackground(GuiGraphicsExtractor graphics, int x, int y, int width, int height) { + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, LIGHTER_BACKGROUND_TEXTURE, x - 18, y - 18, width + 36, height + 36); + } + @Override protected void init() { super.init(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/name/CustomizeNameWidget.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java similarity index 90% rename from src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/name/CustomizeNameWidget.java rename to src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java index bfdf871b310..4480c5de578 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/name/CustomizeNameWidget.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java @@ -1,12 +1,11 @@ -package de.hysky.skyblocker.skyblock.item.custom.screen.name; +package de.hysky.skyblocker.utils.render.gui; import com.google.common.collect.ImmutableList; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.platform.cursor.CursorTypes; -import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.ConfigUtils; -import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.debug.Debug; +import de.hysky.skyblocker.skyblock.item.custom.screen.name.ColorPopup; import de.hysky.skyblocker.skyblock.item.custom.screen.name.visitor.GetClickedPositionVisitor; import de.hysky.skyblocker.skyblock.item.custom.screen.name.visitor.GetRenderWidthVisitor; import de.hysky.skyblocker.skyblock.item.custom.screen.name.visitor.GetStyleVisitor; @@ -37,12 +36,10 @@ import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.input.PreeditEvent; -import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.client.sounds.SoundManager; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; -import net.minecraft.resources.Identifier; import net.minecraft.util.ARGB; import net.minecraft.util.CommonColors; import net.minecraft.util.StringUtil; @@ -51,15 +48,14 @@ import org.jspecify.annotations.Nullable; import java.util.List; +import java.util.function.Consumer; import java.util.function.Predicate; -public class CustomizeNameWidget extends AbstractContainerWidget { - private static final Identifier INNER_SPACE_TEXTURE = SkyblockerMod.id("menu_inner_space"); - private static final int PADDING = 3; +public class ComponentEditWidget extends AbstractContainerWidget { private final Minecraft client = Minecraft.getInstance(); private final Font textRenderer = client.font; - private String uuid = ""; + private final Consumer responder; private Component text = Component.empty(); private String textString = ""; @@ -76,9 +72,10 @@ public class CustomizeNameWidget extends AbstractContainerWidget { private @Nullable Style insertAs; - public CustomizeNameWidget(Screen parent) { - super(0, 0, 0, 0, Component.literal("Customize Item Name"), AbstractScrollArea.defaultSettings(4)); + public ComponentEditWidget(Screen parent, Component message, Consumer responder) { + super(0, 0, 0, 0, message, AbstractScrollArea.defaultSettings(4)); ImmutableList.Builder builder = ImmutableList.builder(); + this.responder = responder; // the gui is a grid of 20 columns, should be 16 px each textField = grid.addChild(new TextField(), 1, 0, 1, 20); builder.add(textField); @@ -98,12 +95,11 @@ public CustomizeNameWidget(Screen parent) { builder.add(grid.addChild(Button.builder(Component.translatable("skyblocker.customItemNames.screen.gradientColor"), _ -> client.gui.setScreen(ColorPopup.createGradient(parent, this::createGradient)) ).size(48, 16).build(), 3, 17, 1, 3)); - builder.add(grid.addChild(new StringWidget(20 * 16, textRenderer.lineHeight, Component.translatable("skyblocker.customItemNames.screen.howToRemove").withStyle(ChatFormatting.ITALIC, ChatFormatting.GRAY), textRenderer)/*.alignLeft()*/, 4, 0, 1, 20, LayoutSettings.defaults().paddingTop(2))); - builder.add(previewWidget = grid.addChild(new StringWidget(20 * 16, textRenderer.lineHeight, Component.empty(), textRenderer).setMaxWidth(20 * 16, StringWidget.TextOverflow.SCROLLING), 5, 0, 1, 20, LayoutSettings.defaults().paddingVertical(2).alignHorizontallyCenter())); + builder.add(previewWidget = grid.addChild(new StringWidget(20 * 16, textRenderer.lineHeight, Component.empty(), textRenderer).setMaxWidth(20 * 16, StringWidget.TextOverflow.SCROLLING), 4, 0, 1, 20, LayoutSettings.defaults().paddingVertical(2).alignHorizontallyCenter())); widgets = builder.build(); grid.arrangeElements(); - grid.setPosition(getX() + PADDING, getY() + PADDING); - setSize(grid.getWidth() + PADDING * 2, grid.getHeight() + PADDING * 2); + grid.setPosition(getX(), getY()); + setSize(grid.getWidth(), grid.getHeight()); selectionStart = selectionEnd = textString.length(); } @@ -125,32 +121,24 @@ private void addFormattingButtons(ImmutableList.Builder builder) @Override public void setX(int x) { super.setX(x); - grid.setX(getX() + PADDING); + grid.setX(getX()); } @Override public void setY(int y) { super.setY(y); - grid.setY(getY() + PADDING); + grid.setY(getY()); } public void setItem(ItemStack stack) { - uuid = stack.getUuid(); setText(stack.getHoverName().copy(), false); } @Override protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float deltaTicks) { - graphics.blitSprite( - RenderPipelines.GUI_TEXTURED, - INNER_SPACE_TEXTURE, - getX(), - getY(), - getWidth(), - getHeight()); if (Debug.debugEnabled()) { - graphics.text(textRenderer, Component.literal("Selection Start: " + selectionStart + ", Selection End: " + selectionEnd), getX(), getBottom(), -1); - graphics.text(textRenderer, Component.literal("Insert Style: " + (insertAs == null ? "null" : insertAs.toString())), getX(), getBottom() + 10, -1); + graphics.text(textRenderer, Component.literal("Selection Start: " + selectionStart + ", Selection End: " + selectionEnd), getX() + getWidth() / 3, getY(), -1); + graphics.text(textRenderer, Component.literal("Insert Style: " + (insertAs == null ? "null" : insertAs.toString())), getX(), getY() + 56, -1); } for (AbstractWidget widget : widgets) { widget.extractRenderState(graphics, mouseX, mouseY, deltaTicks); @@ -158,7 +146,7 @@ protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouse } /** - * Creates a gradient that goes from {@link CustomizeNameWidget#selectionStart} to {@link CustomizeNameWidget#selectionEnd} + * Creates a gradient that goes from {@link ComponentEditWidget#selectionStart} to {@link ComponentEditWidget#selectionEnd} * * @param startColor the color at the start of the gradient * @param endColor the color at the end of the gradient @@ -213,20 +201,14 @@ private void updateStyleButtons() { * * @param text the text to set */ - public void setText(Component text, boolean updateConfig) { + public void setText(Component text, boolean callCallback) { this.text = text; textString = text.getString(); - if (updateConfig && !uuid.isEmpty()) { - SkyblockerConfigManager.updateOnly(config -> { - if (textString.isBlank()) config.general.customItemNames.remove(uuid); - else config.general.customItemNames.put(uuid, text.copy().setStyle(Style.EMPTY.withItalic(false).withColor(ChatFormatting.WHITE))); - }); - } + if (callCallback) responder.accept(text); previewWidget.setMessage(text); grid.arrangeElements(); - // called before init - if (textField != null) textField.updateMePrettyPlease = true; + textField.updateMePrettyPlease = true; } public void setText(Component text) { @@ -522,7 +504,7 @@ public boolean charTyped(CharacterEvent input) { @Override public boolean preeditUpdated(@Nullable final PreeditEvent event) { if (this.isActive()) { - this.preeditOverlay = event != null ? new IMEPreeditOverlay(event, CustomizeNameWidget.this.textRenderer, 9 + 1) : null; + this.preeditOverlay = event != null ? new IMEPreeditOverlay(event, ComponentEditWidget.this.textRenderer, 9 + 1) : null; return true; } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AutocompleteEditBox.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java similarity index 84% rename from src/main/java/de/hysky/skyblocker/utils/render/gui/AutocompleteEditBox.java rename to src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java index 4577aec2742..ee236d861f4 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/AutocompleteEditBox.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java @@ -21,14 +21,14 @@ import java.util.Optional; import java.util.function.Consumer; -public class AutocompleteEditBox extends EditBox { +public class SuggestionsEditBox extends EditBox { private final TextFieldSuggestions suggestions; private final boolean autoTrim; private final @Nullable String argument; private @Nullable Consumer responder; - public AutocompleteEditBox(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node, boolean autoTrim) { + public SuggestionsEditBox(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node, boolean autoTrim) { super(font, width, height, narration); this.suggestions = new TextFieldSuggestions(minecraft, screen, this, font, onlyShowIfCursorPastError, suggestionLineLimit, node); this.autoTrim = autoTrim; @@ -88,6 +88,12 @@ public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { return super.mouseClicked(event, doubleClick); } + @Override + public void onClick(MouseButtonEvent event, boolean doubleClick) { + super.onClick(event, doubleClick); + suggestions.updateCommandInfo(); + } + @Override public void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { super.extractWidgetRenderState(graphics, mouseX, mouseY, a); @@ -140,11 +146,11 @@ public Builder autoTrim(boolean autoTrim) { return this; } - public AutocompleteEditBox build(Screen screen, Component narration, CommandNode node) { + public SuggestionsEditBox build(Screen screen, Component narration, CommandNode node) { return build(Minecraft.getInstance(), Minecraft.getInstance().font, screen, narration, node); } - public AutocompleteEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, ArgumentBuilder builder) { + public SuggestionsEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, ArgumentBuilder builder) { return build(minecraft, font, screen, narration, builder.build()); } @@ -152,12 +158,12 @@ public Argument buildArg(Minecraft minecraft, Font font, Screen screen, C return new Argument<>(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, argumentType, autoTrim); } - public AutocompleteEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, CommandNode node) { - return new AutocompleteEditBox(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, node, autoTrim); + public SuggestionsEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, CommandNode node) { + return new SuggestionsEditBox(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, node, autoTrim); } } - public static class Argument extends AutocompleteEditBox { + public static class Argument extends SuggestionsEditBox { public Argument(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, ArgumentType argumentType, boolean autoTrim) { super(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, RequiredArgumentBuilder.argument("argument", argumentType).build(), autoTrim); From 5bac855f502552c72cbe6ee536e5f4891c815a50 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Mon, 27 Jul 2026 12:02:05 +0200 Subject: [PATCH 04/15] make things work if not inside a world --- .../screens/quicknav/ItemEditPopup.java | 22 +++++--- .../suggestions/TextFieldSuggestions.java | 54 ++++++++++++++++++- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index 64d4b3f0ea0..f4734674e96 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -14,6 +14,7 @@ import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.layouts.GridLayout; @@ -60,10 +61,17 @@ protected void init() { CommandBuildContext context = TextFieldSuggestions.getContext(); LinearLayout commandLayout = layout.addChild(LinearLayout.vertical().spacing(2)); addTitle(commandLayout, "Tooltip"); - SuggestionsEditBox commandBox = SuggestionsEditBox.builder().autoTrim(false).width(250).onlyShowIfCursorPastError(false).build( - minecraft, font, this, Component.empty(), - minecraft.player.connection.getCommands().getRoot() - ); + EditBox commandBox; + if (minecraft.player != null) { + commandBox = SuggestionsEditBox.builder().autoTrim(false).width(250).onlyShowIfCursorPastError(false).build( + minecraft, font, this, Component.empty(), + minecraft.player.connection.getCommands().getRoot() + ); + } else { + commandBox = new EditBox(font, 250, 20, Component.empty()); + } + commandBox.setValue(item.clickEvent.startsWith("/") ? item.clickEvent.substring(1) : item.clickEvent); + commandBox.setTooltip(Tooltip.create(Component.literal("Command to run."))); commandLayout.addChild(commandBox); renderAroundLayout(commandLayout); @@ -82,7 +90,7 @@ protected void init() { minecraft, font, this, Component.empty(), new RegexArgumentType() ); - patternBox.setTooltip(Tooltip.create(Component.literal("The button will appear pressed in the menu matching this title. This supports Regex!"))); + patternBox.setTooltip(Tooltip.create(Component.literal("The button will appear pressed in the menu matching this title.\nThis supports Regex!\nCan be left empty if button doesn't open a menu."))); patternBox.setMaxLength(2048); patternBox.setValue(item.uiTitle); regexLayout.addChild(patternBox); @@ -172,8 +180,6 @@ protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouse } @Override - protected void updateWidgetNarration(NarrationElementOutput output) { - - } + protected void updateWidgetNarration(NarrationElementOutput output) {} } } diff --git a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java index 4cb09390dc7..bd0a953450a 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java @@ -1,9 +1,11 @@ package de.hysky.skyblocker.utils.command.suggestions; +import com.google.common.base.Suppliers; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.SuggestionContext; +import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.tree.CommandNode; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; @@ -15,20 +17,30 @@ import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.Rect2i; import net.minecraft.commands.CommandBuildContext; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.registries.VanillaRegistries; import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.permissions.PermissionSet; import net.minecraft.util.ARGB; import net.minecraft.util.FormattedCharSequence; import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.level.Level; import org.jspecify.annotations.Nullable; +import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; public class TextFieldSuggestions extends CommandSuggestions { private final CommandDispatcher dispatcher; private @Nullable CommandContext context; + private static final Supplier FAKE_PROVIDER_SUPPLIER = Suppliers.memoize(FakeClientSuggestionProvider::new); public TextFieldSuggestions(Minecraft minecraft, Screen screen, EditBox input, Font font, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node) { super(minecraft, screen, input, font, true, onlyShowIfCursorPastError, 0, suggestionLineLimit, false, ARGB.black(0.5f)); @@ -96,7 +108,7 @@ public void updateCommandInfo() { int cursorPosition = this.input.getCursorPosition(); CommandDispatcher commands = dispatcher; if (this.currentParse == null) { - this.currentParse = commands.parse(reader, this.minecraft.player.connection.getSuggestionsProvider()); + this.currentParse = commands.parse(reader, minecraft.player != null ? minecraft.player.connection.getSuggestionsProvider() : FAKE_PROVIDER_SUPPLIER.get()); if (currentParse.getExceptions().isEmpty()) this.context = currentParse.getContext().build(command); } @@ -118,4 +130,44 @@ public void updateCommandInfo() { protected List fillNodeUsage(SuggestionContext suggestionContext, Style usageFormat) { return List.of(); } + + private static class FakeClientSuggestionProvider extends ClientSuggestionProvider { + private final RegistryAccess registryAccess; + + public FakeClientSuggestionProvider() { + // maybe dangerous? either that or 20 new imports to create a fake connection + super(null, Minecraft.getInstance(), PermissionSet.NO_PERMISSIONS); + this.registryAccess = RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY); + } + + @Override + public Set> levels() { + return Set.of(); + } + + @Override + public Collection getOnlinePlayerNames() { + return List.of(); + } + + @Override + public RegistryAccess registryAccess() { + return registryAccess; + } + + @Override + public FeatureFlagSet enabledFeatures() { + return FeatureFlagSet.of(); + } + + @Override + public Collection getAllTeams() { + return List.of(); + } + + @Override + public CompletableFuture customSuggestion(CommandContext context) { + return Suggestions.empty(); + } + } } From 10a6a68e23d9b437d1f21db7dc57fcc271c52c42 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Wed, 29 Jul 2026 21:39:43 +0200 Subject: [PATCH 05/15] continue screen, TextFieldSuggestions should somewhat work in menus --- .../de/hysky/skyblocker/SkyblockerScreen.java | 1 + .../categories/QuickNavigationCategory.java | 5 + .../config/configs/QuickNavigationConfig.java | 15 +- .../screens/quicknav/ItemEditPopup.java | 150 +++++++++++++----- .../quicknav/QuickNavConfigScreen.java | 14 +- .../utils/command/CommandUtils.java | 111 +++++++++++++ .../suggestions/TextFieldSuggestions.java | 126 ++++++--------- .../utils/render/gui/AbstractPopupScreen.java | 5 +- .../utils/render/gui/SuggestionsEditBox.java | 67 +++++--- .../assets/skyblocker/lang/en_us.json | 29 ++-- 10 files changed, 368 insertions(+), 155 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java b/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java index 2f62b1033c5..ea85c677844 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java @@ -5,6 +5,7 @@ import de.hysky.skyblocker.skyblock.Tips; import de.hysky.skyblocker.utils.FunUtils; import de.hysky.skyblocker.utils.LogsFolderFinder; +import de.hysky.skyblocker.utils.command.CommandUtils; import de.hysky.skyblocker.utils.scheduler.Scheduler; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.ClientCommands; diff --git a/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java index 8af96f61a95..38464befd4f 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java @@ -32,6 +32,11 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.quickNav.enableQuickNav = newValue) .controller(ConfigUtils.createBooleanController()) .build()) + /*.option(ButtonOption.createBuilder() + .name(Component.translatable("skyblocker.config.quickNav.quickNavConfigScreen")) + .prompt(Component.translatable("text.skyblocker.open")) + .action(screen -> Minecraft.getInstance().gui.setScreen(new QuickNavConfigScreen(screen))) + .build())*/ //Buttons .group(quickNavButton(defaults.quickNav.button1, config.quickNav.button1, 1)) diff --git a/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java index deaae568734..d77354c6cae 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java @@ -78,17 +78,26 @@ public QuickNavItem(boolean doubleClick, ItemData itemData, @Language("RegExp") this.tooltip = tooltip; } + public QuickNavItem(QuickNavItem quickNavItem) { + this.doubleClick = quickNavItem.doubleClick; + this.itemData = quickNavItem.itemData; + this.uiTitle = quickNavItem.uiTitle; + this.clickEvent = quickNavItem.clickEvent; + this.tooltip = quickNavItem.tooltip; + this.render = quickNavItem.render; + } + public boolean render = true; public boolean doubleClick = false; - public ItemData itemData; + public ItemData itemData = new ItemData(Items.POISONOUS_POTATO); - public String uiTitle; + public @Language("RegExp") String uiTitle = "none"; public String tooltip = ""; - public String clickEvent; + public String clickEvent = "/help"; } public static class ItemData { diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index f4734674e96..e628ea115f4 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -2,52 +2,63 @@ import com.google.gson.JsonElement; import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.logging.LogUtils; import com.mojang.serialization.JsonOps; import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.config.configs.QuickNavigationConfig; +import de.hysky.skyblocker.skyblock.profileviewer2.widgets.ButtonWidget; +import de.hysky.skyblocker.utils.command.CommandUtils; import de.hysky.skyblocker.utils.command.argumenttypes.RegexArgumentType; -import de.hysky.skyblocker.utils.command.suggestions.TextFieldSuggestions; import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; import de.hysky.skyblocker.utils.render.gui.AbstractPopupScreen; -import de.hysky.skyblocker.utils.render.gui.SuggestionsEditBox; import de.hysky.skyblocker.utils.render.gui.ComponentEditWidget; +import de.hysky.skyblocker.utils.render.gui.ItemSelectionPopup; +import de.hysky.skyblocker.utils.render.gui.SuggestionsEditBox; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Checkbox; import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.ScrollableLayout; import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.layouts.GridLayout; -import net.minecraft.client.gui.layouts.Layout; +import net.minecraft.client.gui.layouts.LayoutElement; +import net.minecraft.client.gui.layouts.LayoutSettings; import net.minecraft.client.gui.layouts.LinearLayout; +import net.minecraft.client.gui.layouts.SpacerElement; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.screens.Screen; import net.minecraft.commands.CommandBuildContext; import net.minecraft.commands.arguments.item.ItemArgument; import net.minecraft.commands.arguments.item.ItemInput; +import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.util.ARGB; import net.minecraft.world.item.ItemStack; +import org.slf4j.Logger; import java.util.regex.Pattern; class ItemEditPopup extends AbstractPopupScreen { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final int SCROLLABLE_CONTENT_HEIGHT_DIFF = BACKGROUND_MARGIN * 2 + 20 + 20; private final Runnable onClose; private final QuickNavigationConfig.QuickNavItem item; private final QuickNavConfigScreen.ConfigItemSetter setter; - private final LinearLayout layout = LinearLayout.vertical().spacing(10); + private final LinearLayout layout = LinearLayout.vertical().spacing(2); + private ScrollableLayout scrollableContent = new ScrollableLayout(minecraft, LinearLayout.vertical(), 0); // placeholder - private int currentCount; private Component currentTooltip; - protected ItemEditPopup(Screen backgroundScreen, Runnable onClose, QuickNavigationConfig.QuickNavItem item, QuickNavConfigScreen.ConfigItemSetter setter) { - super(Component.literal("Edit button or something"), backgroundScreen); + ItemEditPopup(Screen backgroundScreen, Runnable onClose, QuickNavigationConfig.QuickNavItem item, QuickNavConfigScreen.ConfigItemSetter setter, int index) { + super(Component.literal("Editing Button " + index).withStyle(ChatFormatting.BOLD, ChatFormatting.UNDERLINE), backgroundScreen); this.onClose = onClose; - this.item = item; + this.item = new QuickNavigationConfig.QuickNavItem(item); this.setter = setter; - currentCount = item.itemData.count; try { currentTooltip = ComponentSerialization.CODEC.decode(JsonOps.INSTANCE, SkyblockerMod.GSON.fromJson(item.tooltip, JsonElement.class)).getOrThrow().getFirst(); } catch (Exception e) { @@ -57,48 +68,44 @@ protected ItemEditPopup(Screen backgroundScreen, Runnable onClose, QuickNavigati @Override protected void init() { - layout.defaultCellSetting().padding(3); - CommandBuildContext context = TextFieldSuggestions.getContext(); - LinearLayout commandLayout = layout.addChild(LinearLayout.vertical().spacing(2)); - addTitle(commandLayout, "Tooltip"); - EditBox commandBox; - if (minecraft.player != null) { - commandBox = SuggestionsEditBox.builder().autoTrim(false).width(250).onlyShowIfCursorPastError(false).build( - minecraft, font, this, Component.empty(), - minecraft.player.connection.getCommands().getRoot() - ); - } else { - commandBox = new EditBox(font, 250, 20, Component.empty()); - } - commandBox.setValue(item.clickEvent.startsWith("/") ? item.clickEvent.substring(1) : item.clickEvent); + layout.addChild(new StringWidget(getTitle(), font), l -> l.alignHorizontallyCenter().paddingTop(2).paddingBottom(6)); + LinearLayout content = LinearLayout.vertical().spacing(10); + content.defaultCellSetting().padding(3); + CommandBuildContext context = CommandUtils.newContext(); + LinearLayout commandLayout = content.addChild(createSectionLayout()); + // click event + addTitle(commandLayout, "Click Event"); + EditBox commandBox = SuggestionsEditBox.builder().width(250).buildVanillaDispatcher( + minecraft, font, this, Component.empty(), + false); + commandBox.setValue(item.clickEvent); commandBox.setTooltip(Tooltip.create(Component.literal("Command to run."))); + commandBox.setResponder(s -> item.clickEvent = s); commandLayout.addChild(commandBox); - renderAroundLayout(commandLayout); // tooltip - LinearLayout tooltipLayout = layout.addChild(LinearLayout.vertical().spacing(2)); + LinearLayout tooltipLayout = content.addChild(createSectionLayout()); addTitle(tooltipLayout, "Tooltip"); ComponentEditWidget editWidget = new ComponentEditWidget(this, Component.literal("Customize Tooltip"), component -> currentTooltip = component.copy()); tooltipLayout.addChild(editWidget); editWidget.setText(currentTooltip.copy(), false); - renderAroundLayout(tooltipLayout); // menu regex - LinearLayout regexLayout = layout.addChild(LinearLayout.vertical().spacing(2)); + LinearLayout regexLayout = content.addChild(createSectionLayout()); addTitle(regexLayout, "Menu Title"); - SuggestionsEditBox.Argument patternBox = SuggestionsEditBox.builder().autoTrim(false).width(250).onlyShowIfCursorPastError(false).buildArg( + SuggestionsEditBox.Argument patternBox = SuggestionsEditBox.builder().width(250).onlyShowIfCursorPastError(false).buildArg( minecraft, font, this, Component.empty(), new RegexArgumentType() ); patternBox.setTooltip(Tooltip.create(Component.literal("The button will appear pressed in the menu matching this title.\nThis supports Regex!\nCan be left empty if button doesn't open a menu."))); patternBox.setMaxLength(2048); patternBox.setValue(item.uiTitle); + patternBox.setValueResponder(p -> item.uiTitle = p.pattern().isBlank() ? "lorem ipsum" : p.pattern()); regexLayout.addChild(patternBox); - renderAroundLayout(regexLayout); // item selection - LinearLayout iconLayout = layout.addChild(LinearLayout.vertical().spacing(2)); + LinearLayout iconLayout = content.addChild(createSectionLayout()); addTitle(iconLayout, "Icon"); GridLayout itemLayout = iconLayout.addChild(new GridLayout()).columnSpacing(4).rowSpacing(2); @@ -123,13 +130,47 @@ protected void init() { itemLayout.addChild(countBox, 1, 2); countBox.setMaxLength(2); countBox.setValue(String.valueOf(item.itemData.count)); - renderAroundLayout(iconLayout); + + iconLayout.addChild(ButtonWidget.builder(Component.literal("Select Item"), _ -> minecraft.gui.setScreen( + new ItemSelectionPopup(this, itemStack -> { + if (itemStack != null) { + itemWidget.stack = itemStack; + item.itemData.item = itemStack.getItem(); + String components = ItemStackComponentizationFixer.componentsAsString(itemStack); + item.itemData.components = components; + itemBox.setValue(itemStack.getItem() + components); + } + }))).build()); + + LinearLayout doubleClickLayout = content.addChild(createSectionLayout()); + doubleClickLayout.addChild(Checkbox.builder(Component.literal("Require Double Click"), font) + .onValueChange((_, value) -> item.doubleClick = value) + .tooltip(Tooltip.create(Component.literal("Useful to limit missclicks on warp buttons."))) + .selected(item.doubleClick) + .build() + ); + + content.addChild(SpacerElement.height(0)); + scrollableContent = layout.addChild(new ScrollableLayout(minecraft, content, height - SCROLLABLE_CONTENT_HEIGHT_DIFF)); + + // the buttons at the bottom + LinearLayout buttonsLayout = layout.addChild(LinearLayout.horizontal().spacing(4), LayoutSettings::alignHorizontallyCenter); + buttonsLayout.addChild(ButtonWidget.builder(CommonComponents.GUI_CANCEL, _ -> onClose()).build()); + buttonsLayout.addChild(ButtonWidget.builder(CommonComponents.GUI_DONE, _ -> { + save(); + onClose(); + }).build()); layout.visitWidgets(this::addRenderableWidget); - itemBox.setValueResponder(itemInput -> itemWidget.itemStack = new ItemStack(itemInput.item(), currentCount, itemInput.components())); + itemBox.setValueResponder(itemInput -> { + ItemStack itemStack = new ItemStack(itemInput.item(), item.itemData.count, itemInput.components()); + itemWidget.stack = itemStack; + item.itemData.item = itemStack.getItem(); + item.itemData.components = ItemStackComponentizationFixer.componentsAsString(itemStack); + }); countBox.setValueResponder(count -> { - currentCount = Math.max(count, 1); - itemWidget.itemStack = itemWidget.itemStack.copyWithCount(currentCount); + item.itemData.count = Math.max(count, 1); + itemWidget.stack = itemWidget.stack.copyWithCount(item.itemData.count); }); super.init(); } @@ -138,8 +179,10 @@ private void addTitle(LinearLayout layout, String title) { layout.addChild(new StringWidget(Component.literal(title).withStyle(ChatFormatting.BOLD), font), l -> l.paddingBottom(4)); } - private void renderAroundLayout(Layout target) { - addRenderableOnly(((graphics, _, _, _) -> graphics.fill(layout.getX(), target.getY() - 3, layout.getX() + layout.getWidth(), target.getY() + target.getHeight() + 3, ARGB.black(0.15f)))); + private LinearLayout createSectionLayout() { + LinearLayout linearLayout = LinearLayout.vertical().spacing(2); + linearLayout.addChild(new BackgroundRender(linearLayout, layout)); + return linearLayout; } @Override @@ -156,6 +199,8 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo @Override protected void repositionElements() { super.repositionElements(); + scrollableContent.arrangeElements(); + scrollableContent.setMaxHeight(height - SCROLLABLE_CONTENT_HEIGHT_DIFF); layout.arrangeElements(); layout.setPosition((width - layout.getWidth()) / 2, (height - layout.getHeight()) / 2); } @@ -166,17 +211,44 @@ public void onClose() { onClose.run(); } + private void save() { + item.tooltip = ComponentSerialization.CODEC.encodeStart(JsonOps.INSTANCE, currentTooltip) + .ifError(error -> LOGGER.error("Failed to serialize component! {}", error.message())).result() + .map(SkyblockerMod.GSON_COMPACT::toJson).orElse(""); + SkyblockerConfigManager.updateOnly(config -> setter.accept(config.quickNav, item)); + } + private static class ItemWidget extends AbstractWidget { - private ItemStack itemStack; + private ItemStack stack; private ItemWidget(ItemStack stack) { super(0, 0, 16, 16, stack.getItemName()); - this.itemStack = stack; + this.stack = stack; + } + + @Override + protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + graphics.item(stack, getX(), getY()); + } + + @Override + protected void updateWidgetNarration(NarrationElementOutput output) {} + } + + private static class BackgroundRender extends AbstractWidget { + private final LayoutElement heightLayout; + private final LayoutElement widthLayout; + + public BackgroundRender(LayoutElement heightLayout, LayoutElement widthLayout) { + super(0, 0, 0, 0, Component.empty()); + active = false; + this.heightLayout = heightLayout; + this.widthLayout = widthLayout; } @Override protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { - graphics.item(itemStack, getX(), getY()); + graphics.fill(widthLayout.getX(), heightLayout.getY() - 3, widthLayout.getX() + widthLayout.getWidth(), heightLayout.getY() + heightLayout.getHeight() + 3, ARGB.black(0.15f)); } @Override diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java index ca06cc49926..f7fe629f417 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java @@ -61,18 +61,23 @@ public class QuickNavConfigScreen extends Screen { }; private final QuickNavConfigButton[] buttons = new QuickNavConfigButton[ITEM_SUPPLIERS.length]; + private final @Nullable Screen parent; private @Nullable QuickNavConfigButton highlightedButton; private final @Nullable LivingEntity entityToRender; public QuickNavConfigScreen() { + this(null); + } + + public QuickNavConfigScreen(@Nullable Screen parent) { super(Component.literal("Quick Navigation Config")); if (Math.random() < 0.001 && minecraft.level != null) { entityToRender = new Cat(EntityTypes.CAT, minecraft.level); entityToRender.setId("meow".hashCode()); - } else { entityToRender = minecraft.player; } + this.parent = parent; } @Override @@ -148,6 +153,11 @@ public void removed() { SkyblockerConfigManager.update(_ -> {}); } + @Override + public void onClose() { + minecraft.gui.setScreen(parent); + } + private class QuickNavConfigButton extends QuickNavButton { private boolean enabled; private double dragX, dragY; @@ -230,7 +240,7 @@ public void onRelease(MouseButtonEvent event) { } dragging = false; } else { - minecraft.gui.setScreen(new ItemEditPopup(QuickNavConfigScreen.this, () -> refreshButton(index), ITEM_SUPPLIERS[index].apply(SkyblockerConfigManager.get().quickNav), SETTERS[index])); + minecraft.gui.setScreen(new ItemEditPopup(QuickNavConfigScreen.this, () -> refreshButton(index), ITEM_SUPPLIERS[index].apply(SkyblockerConfigManager.get().quickNav), SETTERS[index], index)); } setTooltip(tooltip); } diff --git a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java index d0c8f502b4d..f24d23d240d 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java @@ -1,8 +1,119 @@ package de.hysky.skyblocker.utils.command; import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.tree.RootCommandNode; +import de.hysky.skyblocker.config.configs.QuickNavigationConfig; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientSuggestionProvider; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.commands.CommandBuildContext; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.data.registries.VanillaRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.permissions.PermissionSet; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import org.jspecify.annotations.Nullable; + +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; public final class CommandUtils { + private static @Nullable CommandDispatcher offlineCommandDispatcher; + private static @Nullable ClientSuggestionProvider offlineSuggestionProvider; public static final Command noOp = _ -> -1; + + public static CommandBuildContext newContext() { + LocalPlayer player = Minecraft.getInstance().player; + if (player == null) return CommandBuildContext.simple(VanillaRegistries.createLookup(), FeatureFlagSet.of()); + return CommandBuildContext.simple(player.connection.registryAccess(), player.connection.enabledFeatures()); + } + + public static CommandDispatcher getOfflineCommandDispatcher() { + if (offlineCommandDispatcher == null) { + CommandBuildContext context = newContext(); + RootCommandNode root = unsafeCast(new Commands(Commands.CommandSelection.ALL, context).getDispatcher().getRoot()); + OfflineCommandDispatcher dispatcher = new OfflineCommandDispatcher<>(root); + ClientCommandRegistrationCallback.EVENT.invoker().register(unsafeCast(dispatcher), context); + offlineCommandDispatcher = dispatcher; + } + return offlineCommandDispatcher; + } + + public static ClientSuggestionProvider getOfflineSuggestionProvider() { + if (offlineSuggestionProvider == null) { + offlineSuggestionProvider = new FakeClientSuggestionProvider(); + } + return offlineSuggestionProvider; + } + + private static A unsafeCast(B b) { + return (A) b; + } + + + + private static class FakeClientSuggestionProvider extends ClientSuggestionProvider { + private final RegistryAccess registryAccess; + + private FakeClientSuggestionProvider() { + // maybe dangerous? either that or 20 new imports to create a fake connection + super(null, Minecraft.getInstance(), PermissionSet.NO_PERMISSIONS); + this.registryAccess = RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY); + } + + @Override + public Set> levels() { + return Set.of(); + } + + @Override + public Collection getOnlinePlayerNames() { + return List.of(); + } + + @Override + public RegistryAccess registryAccess() { + return registryAccess; + } + + @Override + public FeatureFlagSet enabledFeatures() { + return FeatureFlagSet.of(); + } + + @Override + public Collection getAllTeams() { + return List.of(); + } + + @Override + public CompletableFuture customSuggestion(CommandContext context) { + return Suggestions.empty(); + } + } + + private static class OfflineCommandDispatcher extends CommandDispatcher { + public OfflineCommandDispatcher(RootCommandNode root) { + super(root); + } + @Override + public int execute(ParseResults parse) { + throw new UnsupportedOperationException(); + } + } } diff --git a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java index bd0a953450a..5c9945ba332 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java @@ -5,8 +5,9 @@ import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.SuggestionContext; -import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.CommandNode; +import de.hysky.skyblocker.utils.command.CommandUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -14,53 +15,51 @@ import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.multiplayer.ClientSuggestionProvider; -import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.Rect2i; -import net.minecraft.commands.CommandBuildContext; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.data.registries.VanillaRegistries; +import net.minecraft.commands.Commands; import net.minecraft.network.chat.Style; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.permissions.PermissionSet; import net.minecraft.util.ARGB; import net.minecraft.util.FormattedCharSequence; -import net.minecraft.world.flag.FeatureFlagSet; -import net.minecraft.world.level.Level; import org.jspecify.annotations.Nullable; -import java.util.Collection; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; public class TextFieldSuggestions extends CommandSuggestions { private final CommandDispatcher dispatcher; + private final Mode mode; private @Nullable CommandContext context; - private static final Supplier FAKE_PROVIDER_SUPPLIER = Suppliers.memoize(FakeClientSuggestionProvider::new); - public TextFieldSuggestions(Minecraft minecraft, Screen screen, EditBox input, Font font, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node) { + protected TextFieldSuggestions(Minecraft minecraft, Screen screen, EditBox input, Font font, boolean onlyShowIfCursorPastError, int suggestionLineLimit, @Nullable CommandNode node, boolean commandOnly) { super(minecraft, screen, input, font, true, onlyShowIfCursorPastError, 0, suggestionLineLimit, false, ARGB.black(0.5f)); - this.dispatcher = new CommandDispatcher<>(); - this.dispatcher.getRoot().addChild(node); + if (node != null) { + this.dispatcher = new CommandDispatcher<>(); + this.dispatcher.getRoot().addChild(node); + this.mode = Mode.CUSTOM_NODE; + } else { + Commands.validate(); + this.dispatcher = minecraft.player == null ? CommandUtils.getOfflineCommandDispatcher() : minecraft.player.connection.getCommands(); + this.mode = commandOnly ? Mode.VANILLA_COMMANDS_ONLY : Mode.VANILLA; + } } - public TextFieldSuggestions(Screen screen, EditBox input, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node) { - this(Minecraft.getInstance(), screen, input, Minecraft.getInstance().font, onlyShowIfCursorPastError, suggestionLineLimit, node); + public static TextFieldSuggestions ofSpecificNode(Minecraft minecraft, Screen screen, EditBox input, Font font, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node) { + return new TextFieldSuggestions(minecraft, screen, input, font, onlyShowIfCursorPastError, suggestionLineLimit, node, true); } - public static CommandBuildContext getContext() { - LocalPlayer player = Minecraft.getInstance().player; - if (player == null) return CommandBuildContext.simple(VanillaRegistries.createLookup(), FeatureFlagSet.of()); - return CommandBuildContext.simple(player.connection.registryAccess(), player.connection.enabledFeatures()); + public static TextFieldSuggestions ofVanillaDispatcher(Minecraft minecraft, Screen screen, EditBox input, Font font, boolean onlyShowIfCursorPastError, int suggestionLineLimit, boolean commandOnly) { + return new TextFieldSuggestions(minecraft, screen, input, font, onlyShowIfCursorPastError, suggestionLineLimit, null, commandOnly); } @Override public void showSuggestions(boolean immediateNarration) { super.showSuggestions(immediateNarration); + updatePosition(); + } + + public final void updatePosition() { if (suggestions != null) { suggestions.rect = new Rect2i( suggestions.rect.getX(), @@ -105,21 +104,32 @@ public void updateCommandInfo() { commandUsage.clear(); StringReader reader = new StringReader(command); - int cursorPosition = this.input.getCursorPosition(); - CommandDispatcher commands = dispatcher; - if (this.currentParse == null) { - this.currentParse = commands.parse(reader, minecraft.player != null ? minecraft.player.connection.getSuggestionsProvider() : FAKE_PROVIDER_SUPPLIER.get()); - if (currentParse.getExceptions().isEmpty()) this.context = currentParse.getContext().build(command); + boolean startsWithSlash = reader.canRead() && reader.peek() == '/' && mode != Mode.CUSTOM_NODE; // do not allow slashes at all with a custom node + if (startsWithSlash) { + reader.skip(); } - - int parseStart = this.onlyShowIfCursorPastError ? reader.getCursor() : 1; - if (cursorPosition >= parseStart && (this.suggestions == null || !this.keepSuggestions)) { - this.pendingSuggestions = commands.getCompletionSuggestions(this.currentParse, cursorPosition); - this.pendingSuggestions.thenAccept(suggestionResult -> { - if (this.pendingSuggestions.isDone()) { - this.updateUsageInfo(this.currentParse, suggestionResult); + int cursorPosition = this.input.getCursorPosition(); + if (startsWithSlash || mode != Mode.VANILLA) { + CommandDispatcher commands = dispatcher; + if (this.currentParse == null) { + this.currentParse = commands.parse(reader, minecraft.player != null ? minecraft.player.connection.getSuggestionsProvider() : CommandUtils.getOfflineSuggestionProvider()); + CommandSyntaxException parseException = Commands.getParseException(currentParse); + if (mode == Mode.CUSTOM_NODE && parseException != null && parseException.getType() == CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument() && command.endsWith(" ")) { + input.setValue(command.trim()); + return; } - }); + if (currentParse.getExceptions().isEmpty()) this.context = currentParse.getContext().build(command); + } + + int parseStart = this.onlyShowIfCursorPastError ? reader.getCursor() : 1; + if (cursorPosition >= parseStart && (this.suggestions == null || !this.keepSuggestions)) { + this.pendingSuggestions = commands.getCompletionSuggestions(this.currentParse, cursorPosition); + this.pendingSuggestions.thenAccept(suggestionResult -> { + if (this.pendingSuggestions.isDone()) { + this.updateUsageInfo(this.currentParse, suggestionResult); + } + }); + } } } @@ -128,46 +138,12 @@ public void updateCommandInfo() { */ @Override protected List fillNodeUsage(SuggestionContext suggestionContext, Style usageFormat) { - return List.of(); + return mode != Mode.CUSTOM_NODE ? super.fillNodeUsage(suggestionContext, usageFormat) : List.of(); } - private static class FakeClientSuggestionProvider extends ClientSuggestionProvider { - private final RegistryAccess registryAccess; - - public FakeClientSuggestionProvider() { - // maybe dangerous? either that or 20 new imports to create a fake connection - super(null, Minecraft.getInstance(), PermissionSet.NO_PERMISSIONS); - this.registryAccess = RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY); - } - - @Override - public Set> levels() { - return Set.of(); - } - - @Override - public Collection getOnlinePlayerNames() { - return List.of(); - } - - @Override - public RegistryAccess registryAccess() { - return registryAccess; - } - - @Override - public FeatureFlagSet enabledFeatures() { - return FeatureFlagSet.of(); - } - - @Override - public Collection getAllTeams() { - return List.of(); - } - - @Override - public CompletableFuture customSuggestion(CommandContext context) { - return Suggestions.empty(); - } + private enum Mode { + VANILLA, + VANILLA_COMMANDS_ONLY, + CUSTOM_NODE } } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java index 46a5b59c71f..28683e88477 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java @@ -15,6 +15,7 @@ * A more bare-bones version of Vanilla's Popup Screen. Meant to be extended. */ public class AbstractPopupScreen extends Screen { + protected static final int BACKGROUND_MARGIN = 18; private static final Identifier BACKGROUND_TEXTURE = Identifier.withDefaultNamespace("popup/background"); private static final Identifier LIGHTER_BACKGROUND_TEXTURE = SkyblockerMod.id("background"); public final Screen backgroundScreen; @@ -42,11 +43,11 @@ public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mou * These are the inner positions and size of the popup, not outer */ public static void extractPopupBackground(GuiGraphicsExtractor graphics, int x, int y, int width, int height) { - graphics.blitSprite(RenderPipelines.GUI_TEXTURED, BACKGROUND_TEXTURE, x - 18, y - 18, width + 36, height + 36); + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, BACKGROUND_TEXTURE, x - BACKGROUND_MARGIN, y - BACKGROUND_MARGIN, width + BACKGROUND_MARGIN * 2, height + BACKGROUND_MARGIN * 2); } public static void extractLighterPopupBackground(GuiGraphicsExtractor graphics, int x, int y, int width, int height) { - graphics.blitSprite(RenderPipelines.GUI_TEXTURED, LIGHTER_BACKGROUND_TEXTURE, x - 18, y - 18, width + 36, height + 36); + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, LIGHTER_BACKGROUND_TEXTURE, x - BACKGROUND_MARGIN, y - BACKGROUND_MARGIN, width + BACKGROUND_MARGIN * 2, height + BACKGROUND_MARGIN * 2); } @Override diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java index ee236d861f4..a21be213357 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java @@ -24,14 +24,16 @@ public class SuggestionsEditBox extends EditBox { private final TextFieldSuggestions suggestions; - private final boolean autoTrim; private final @Nullable String argument; private @Nullable Consumer responder; - public SuggestionsEditBox(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node, boolean autoTrim) { + public SuggestionsEditBox(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, @Nullable CommandNode node, boolean commandsOnly) { super(font, width, height, narration); - this.suggestions = new TextFieldSuggestions(minecraft, screen, this, font, onlyShowIfCursorPastError, suggestionLineLimit, node); - this.autoTrim = autoTrim; + if (node != null) { + suggestions = TextFieldSuggestions.ofSpecificNode(minecraft, screen, this, font, onlyShowIfCursorPastError, suggestionLineLimit, node); + } else { + suggestions = TextFieldSuggestions.ofVanillaDispatcher(minecraft, screen, this, font, onlyShowIfCursorPastError, suggestionLineLimit, commandsOnly); + } suggestions.setAllowSuggestions(true); super.setResponder(this::onUpdate); if (node instanceof ArgumentCommandNode argumentCommandNode) argument = argumentCommandNode.getName(); @@ -48,11 +50,8 @@ public boolean isValid() { } private void onUpdate(String string) { - if (autoTrim && !string.trim().equals(string)) setValue(string.trim()); - else { - suggestions.updateCommandInfo(); - if (responder != null) responder.accept(string); - } + suggestions.updateCommandInfo(); + if (responder != null) responder.accept(string); } public Optional getParsedValue(Class type) { @@ -100,6 +99,30 @@ public void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, if (visible) suggestions.extractRenderState(graphics, mouseX, mouseY); } + @Override + public void setX(int x) { + super.setX(x); + suggestions.updatePosition(); + } + + @Override + public void setY(int y) { + super.setY(y); + suggestions.updatePosition(); + } + + @Override + public void setWidth(int width) { + super.setWidth(width); + suggestions.updatePosition(); + } + + @Override + public void setHeight(int height) { + super.setHeight(height); + suggestions.updatePosition(); + } + @Override public boolean keyPressed(KeyEvent event) { if (suggestions.keyPressed(event)) return true; @@ -119,7 +142,6 @@ public static class Builder { private int height = 20; private int suggestionLineLimit = 7; private boolean onlyShowIfCursorPastError = true; - private boolean autoTrim = true; public Builder width(int width) { this.width = width; @@ -141,11 +163,6 @@ public Builder onlyShowIfCursorPastError(boolean onlyShowIfCursorPastError) { return this; } - public Builder autoTrim(boolean autoTrim) { - this.autoTrim = autoTrim; - return this; - } - public SuggestionsEditBox build(Screen screen, Component narration, CommandNode node) { return build(Minecraft.getInstance(), Minecraft.getInstance().font, screen, narration, node); } @@ -154,19 +171,23 @@ public SuggestionsEditBox build(Minecraft minecraft, Font font, Screen screen, C return build(minecraft, font, screen, narration, builder.build()); } + public SuggestionsEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, CommandNode node) { + return new SuggestionsEditBox(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, node, true); + } + public Argument buildArg(Minecraft minecraft, Font font, Screen screen, Component narration, ArgumentType argumentType) { - return new Argument<>(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, argumentType, autoTrim); + return new Argument<>(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, argumentType); } - public SuggestionsEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, CommandNode node) { - return new SuggestionsEditBox(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, node, autoTrim); + public SuggestionsEditBox buildVanillaDispatcher(Minecraft minecraft, Font font, Screen screen, Component narration, boolean commandsOnly) { + return new SuggestionsEditBox(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, null, commandsOnly); } } public static class Argument extends SuggestionsEditBox { - public Argument(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, ArgumentType argumentType, boolean autoTrim) { - super(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, RequiredArgumentBuilder.argument("argument", argumentType).build(), autoTrim); + public Argument(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, ArgumentType argumentType) { + super(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, RequiredArgumentBuilder.argument("argument", argumentType).build(), true); } @@ -174,10 +195,16 @@ public Optional getParsedValue() { return (Optional) getParsedValue(Object.class); } + /** + * This responder will only get called if the value is valid and parsed correctly. + */ public void setValueResponder(Consumer valueResponder) { setResponder(_ -> getParsedValue().ifPresent(valueResponder)); } + /** + * This responder will get called even if the value isn't valid and couldn't be parsed. In those cases it will receive {@code null} + */ public void setOptionalValueResponder(Consumer<@Nullable T> optionalValueResponder) { setResponder(_ -> optionalValueResponder.accept(getParsedValue().orElse(null))); } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index e62fcc2d08c..08e180a11c8 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -1072,20 +1072,21 @@ "skyblocker.config.quickNav": "Quick Navigation", "skyblocker.config.quickNav.button": "Button %s", - "skyblocker.config.quickNav.button.chooseSkyblockItem": "Choose SkyBlock Item", - "skyblocker.config.quickNav.button.chooseSkyblockItem.@Tooltip": "Select a SkyBlock icon or item to fill this button with.", - "skyblocker.config.quickNav.button.clickEvent": "Click Event", - "skyblocker.config.quickNav.button.doubleClick": "Require Double Click", - "skyblocker.config.quickNav.button.item.components": "Item Components", - "skyblocker.config.quickNav.button.item.components.@Tooltip": "A string of item components enclosed in square brackets, which is the same format as the /give command.\n\nExample: [minecraft:enchantment_glint_override=true]", - "skyblocker.config.quickNav.button.item.count": "Item Count", - "skyblocker.config.quickNav.button.item.itemName": "Item", - "skyblocker.config.quickNav.button.render": "Render", - "skyblocker.config.quickNav.button.tooltip": "Tooltip", - "skyblocker.config.quickNav.button.tooltip.@Tooltip": "Tooltip shown when hovering your mouse over the button.\n\nYou can use text json format.\nPutting nothing will make the tooltip not render.", - "skyblocker.config.quickNav.button.uiTitle": "UI Title", - "skyblocker.config.quickNav.enableQuickNav": "Enable Quick Navigation", - "skyblocker.config.quickNav.enableQuickNav.@Tooltip": "Adds 14 customizable inventory buttons that can be styled and run a command when clicked.", + "skyblocker.config.quickNav.button.chooseSkyblockItem": "Choose SkyBlock Item", + "skyblocker.config.quickNav.button.chooseSkyblockItem.@Tooltip": "Select a SkyBlock icon or item to fill this button with.", + "skyblocker.config.quickNav.button.clickEvent": "Click Event", + "skyblocker.config.quickNav.button.doubleClick": "Require Double Click", + "skyblocker.config.quickNav.button.item.components": "Item Components", + "skyblocker.config.quickNav.button.item.components.@Tooltip": "A string of item components enclosed in square brackets, which is the same format as the /give command.\n\nExample: [minecraft:enchantment_glint_override=true]", + "skyblocker.config.quickNav.button.item.count": "Item Count", + "skyblocker.config.quickNav.button.item.itemName": "Item", + "skyblocker.config.quickNav.button.render": "Render", + "skyblocker.config.quickNav.button.tooltip": "Tooltip", + "skyblocker.config.quickNav.button.tooltip.@Tooltip": "Tooltip shown when hovering your mouse over the button.\n\nYou can use text json format.\nPutting nothing will make the tooltip not render.", + "skyblocker.config.quickNav.button.uiTitle": "UI Title", + "skyblocker.config.quickNav.enableQuickNav": "Enable Quick Navigation", + "skyblocker.config.quickNav.enableQuickNav.@Tooltip": "Adds 14 customizable inventory buttons that can be styled and run a command when clicked.", + "skyblocker.config.quickNav.quickNavConfigScreen": "Open Configuration Screen", "skyblocker.config.shortcutToKeybindsSettings": "Edit Keybind", "skyblocker.config.shortcutToKeybindsSettings.@Text": "Keybinds Menu", From f4eaa83f725b8d634974cc891dbb50253aeb0a9c Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Thu, 30 Jul 2026 14:45:20 +0200 Subject: [PATCH 06/15] fix a few interaction issues and other little things --- .../categories/QuickNavigationCategory.java | 5 +- .../screens/quicknav/ItemEditPopup.java | 11 +- .../injected/SkyblockerGraphicsExtractor.java | 9 + .../mixins/AbstractContainerWidgetMixin.java | 21 ++ .../mixins/GuiGraphicsExtractorMixin.java | 23 +- .../utils/command/CommandUtils.java | 3 +- .../suggestions/TextFieldSuggestions.java | 1 - .../utils/render/gui/ComponentEditWidget.java | 6 - .../utils/render/gui/SuggestionsEditBox.java | 78 +++++-- src/main/resources/skyblocker.classtweaker | 1 + src/main/resources/skyblocker.mixins.json | 209 +++++++++--------- 11 files changed, 233 insertions(+), 134 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/injected/SkyblockerGraphicsExtractor.java create mode 100644 src/main/java/de/hysky/skyblocker/mixins/AbstractContainerWidgetMixin.java diff --git a/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java index 38464befd4f..e15e91da533 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java @@ -4,6 +4,7 @@ import de.hysky.skyblocker.config.ConfigUtils; import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.configs.QuickNavigationConfig; +import de.hysky.skyblocker.config.screens.quicknav.QuickNavConfigScreen; import de.hysky.skyblocker.utils.render.gui.ItemSelectionPopup; import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; import net.azureaaron.dandelion.api.ButtonOption; @@ -32,11 +33,11 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.quickNav.enableQuickNav = newValue) .controller(ConfigUtils.createBooleanController()) .build()) - /*.option(ButtonOption.createBuilder() + .optionIf(Minecraft.getInstance().player != null, ButtonOption.createBuilder() .name(Component.translatable("skyblocker.config.quickNav.quickNavConfigScreen")) .prompt(Component.translatable("text.skyblocker.open")) .action(screen -> Minecraft.getInstance().gui.setScreen(new QuickNavConfigScreen(screen))) - .build())*/ + .build()) //Buttons .group(quickNavButton(defaults.quickNav.button1, config.quickNav.button1, 1)) diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index e628ea115f4..22497e3c63d 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -23,6 +23,8 @@ import net.minecraft.client.gui.components.ScrollableLayout; import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.components.events.ContainerEventHandler; +import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.layouts.GridLayout; import net.minecraft.client.gui.layouts.LayoutElement; import net.minecraft.client.gui.layouts.LayoutSettings; @@ -44,7 +46,7 @@ class ItemEditPopup extends AbstractPopupScreen { private static final Logger LOGGER = LogUtils.getLogger(); - private static final int SCROLLABLE_CONTENT_HEIGHT_DIFF = BACKGROUND_MARGIN * 2 + 20 + 20; + private static final int SCROLLABLE_CONTENT_HEIGHT_DIFF = BACKGROUND_MARGIN * 2 + 20 + 40; private final Runnable onClose; private final QuickNavigationConfig.QuickNavItem item; @@ -194,6 +196,13 @@ public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mou @Override public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { super.extractRenderState(graphics, mouseX, mouseY, a); + int y = 5; + GuiEventListener thing = getFocused(); + while (thing != null) { + graphics.text(font, String.valueOf(thing), 5, y, -1); + thing = thing instanceof ContainerEventHandler container ? container.getFocused() : null; + y += font.lineHeight; + } } @Override diff --git a/src/main/java/de/hysky/skyblocker/injected/SkyblockerGraphicsExtractor.java b/src/main/java/de/hysky/skyblocker/injected/SkyblockerGraphicsExtractor.java new file mode 100644 index 00000000000..4df8814d97c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/injected/SkyblockerGraphicsExtractor.java @@ -0,0 +1,9 @@ +package de.hysky.skyblocker.injected; + +import net.minecraft.client.gui.components.Renderable; + +public interface SkyblockerGraphicsExtractor { + default void skb$addDeferredElement(Renderable renderable) { + throw new AssertionError("Implemented in Mixin"); + }; +} diff --git a/src/main/java/de/hysky/skyblocker/mixins/AbstractContainerWidgetMixin.java b/src/main/java/de/hysky/skyblocker/mixins/AbstractContainerWidgetMixin.java new file mode 100644 index 00000000000..b48212e6edf --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixins/AbstractContainerWidgetMixin.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.mixins; + +import net.minecraft.client.gui.components.AbstractContainerWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import org.jspecify.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AbstractContainerWidget.class) +public class AbstractContainerWidgetMixin { + @Shadow + private @Nullable GuiEventListener focused; + + @Inject(method = "setFocused(Lnet/minecraft/client/gui/components/events/GuiEventListener;)V", at = @At("HEAD"), cancellable = true) + private void onSetFocused(GuiEventListener focused, CallbackInfo ci) { + if (focused == this.focused) ci.cancel(); // AbstractContainerEventHandler does this check but not this. + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixins/GuiGraphicsExtractorMixin.java b/src/main/java/de/hysky/skyblocker/mixins/GuiGraphicsExtractorMixin.java index f530ac2a879..4d57391a638 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/GuiGraphicsExtractorMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/GuiGraphicsExtractorMixin.java @@ -2,26 +2,33 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; +import de.hysky.skyblocker.injected.SkyblockerGraphicsExtractor; import de.hysky.skyblocker.skyblock.item.ItemCooldowns; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.render.text.GridComponentManager; import de.hysky.skyblocker.utils.render.text.GridTooltipComponent; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.Renderable; import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner; import net.minecraft.resources.Identifier; import net.minecraft.world.item.ItemStack; import org.jspecify.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.ArrayList; import java.util.List; @Mixin(GuiGraphicsExtractor.class) -public abstract class GuiGraphicsExtractorMixin { +public abstract class GuiGraphicsExtractorMixin implements SkyblockerGraphicsExtractor { + @Unique + private final List deferredElements = new ArrayList<>(); + @ModifyExpressionValue(method = "itemCooldown", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemCooldowns;getCooldownPercent(Lnet/minecraft/world/item/ItemStack;F)F")) private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local(name = "itemStack") ItemStack stack) { return Utils.isOnSkyblock() && ItemCooldowns.isOnCooldown(stack) ? ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent() : cooldownProgress; @@ -38,4 +45,18 @@ private void initializeGrids(Font font, List lines, int } } } + + @Inject(method = "extractDeferredElements", at = @At("TAIL")) + private void extractDeferredElements(int mouseX, int mouseY, float a, CallbackInfo ci) { + if (deferredElements.isEmpty()) return; + for (Renderable renderable : deferredElements) { + renderable.extractRenderState((GuiGraphicsExtractor) (Object) this, mouseX, mouseY, a); + } + deferredElements.clear(); + } + + @Override + public void skb$addDeferredElement(Renderable renderable) { + deferredElements.add(renderable); + } } diff --git a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java index f24d23d240d..34eaa2c38a9 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java @@ -8,6 +8,7 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.tree.RootCommandNode; import de.hysky.skyblocker.config.configs.QuickNavigationConfig; +import de.hysky.skyblocker.utils.RegistryUtils; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -39,7 +40,7 @@ public final class CommandUtils { public static CommandBuildContext newContext() { LocalPlayer player = Minecraft.getInstance().player; - if (player == null) return CommandBuildContext.simple(VanillaRegistries.createLookup(), FeatureFlagSet.of()); + if (player == null) return CommandBuildContext.simple(RegistryUtils.getRegistryWrapperLookup(), FeatureFlagSet.of()); return CommandBuildContext.simple(player.connection.registryAccess(), player.connection.enabledFeatures()); } diff --git a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java index 5c9945ba332..1d367787958 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java @@ -39,7 +39,6 @@ protected TextFieldSuggestions(Minecraft minecraft, Screen screen, EditBox input this.dispatcher.getRoot().addChild(node); this.mode = Mode.CUSTOM_NODE; } else { - Commands.validate(); this.dispatcher = minecraft.player == null ? CommandUtils.getOfflineCommandDispatcher() : minecraft.player.connection.getCommands(); this.mode = commandOnly ? Mode.VANILLA_COMMANDS_ONLY : Mode.VANILLA; } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java index 4480c5de578..3791367e833 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java @@ -319,11 +319,6 @@ private int getWordSkipPosition(boolean left) { return i; } - @Override - public void setFocused(boolean focused) { - if (!focused) setFocused(null); - } - private class FormattingButton extends AbstractButton { private boolean enabled; private final ChatFormatting format; @@ -466,7 +461,6 @@ public boolean keyPressed(KeyEvent input) { default -> captured = false; } if (captured) return true; - assert client != null; if (input.isSelectAll()) { selectionStart = 0; selectionEnd = textString.length(); diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java index a21be213357..1f4e9195e94 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java @@ -7,10 +7,15 @@ import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.CommandNode; import de.hysky.skyblocker.utils.command.suggestions.TextFieldSuggestions; +import net.fabricmc.fabric.api.client.screen.v1.Screens; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; @@ -18,6 +23,7 @@ import net.minecraft.network.chat.Component; import org.jspecify.annotations.Nullable; +import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -38,6 +44,7 @@ public SuggestionsEditBox(Minecraft minecraft, Screen screen, Font font, int wid super.setResponder(this::onUpdate); if (node instanceof ArgumentCommandNode argumentCommandNode) argument = argumentCommandNode.getName(); else argument = null; + Screens.getWidgets(screen).addFirst(new SuggestionsWrapper()); } @Override @@ -70,23 +77,6 @@ public void setFocused(boolean focused) { else suggestions.updateCommandInfo(); } - @Override - public boolean isMouseOver(double mouseX, double mouseY) { - return super.isMouseOver(mouseX, mouseY) || (suggestions.suggestions != null && suggestions.suggestions.rect.contains((int) mouseX, (int) mouseY)); - } - - @Override - public boolean mouseScrolled(double x, double y, double scrollX, double scrollY) { - if (suggestions.mouseScrolled(scrollY)) return true; - return super.mouseScrolled(x, y, scrollX, scrollY); - } - - @Override - public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { - if (suggestions.mouseClicked(event)) return true; - return super.mouseClicked(event, doubleClick); - } - @Override public void onClick(MouseButtonEvent event, boolean doubleClick) { super.onClick(event, doubleClick); @@ -96,7 +86,11 @@ public void onClick(MouseButtonEvent event, boolean doubleClick) { @Override public void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { super.extractWidgetRenderState(graphics, mouseX, mouseY, a); - if (visible) suggestions.extractRenderState(graphics, mouseX, mouseY); + if (visible) { + ScreenRectangle peeked = graphics.scissorStack.peek(); + if (peeked == null || peeked.intersects(getRectangle())) + graphics.skb$addDeferredElement((graphics1, mouseX1, mouseY1, _) -> suggestions.extractRenderState(graphics1, mouseX1, mouseY1)); + } } @Override @@ -209,4 +203,52 @@ public void setOptionalValueResponder(Consumer<@Nullable T> optionalValueRespond setResponder(_ -> optionalValueResponder.accept(getParsedValue().orElse(null))); } } + + private class SuggestionsWrapper extends AbstractWidget { + + public SuggestionsWrapper() { + super(0, 0, 0, 0, Component.empty()); + } + + @Override + protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + + } + + @Override + public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { + return suggestions.mouseClicked(event); + } + + @Override + public boolean mouseScrolled(double x, double y, double scrollX, double scrollY) { + return suggestions.mouseScrolled(scrollY); + } + + @Override + public boolean isMouseOver(double mouseX, double mouseY) { + return (suggestions.suggestions != null && suggestions.suggestions.rect.contains((int) mouseX, (int) mouseY)); + } + + @Override + public void setFocused(boolean focused) { + + } + + @Override + protected void updateWidgetNarration(NarrationElementOutput output) { + + } + + @Override + public boolean isFocused() { + return false; + } + + @Override + public boolean shouldTakeFocusAfterInteraction() { + return false; + } + + } } diff --git a/src/main/resources/skyblocker.classtweaker b/src/main/resources/skyblocker.classtweaker index dce13ba614e..78a710a3856 100644 --- a/src/main/resources/skyblocker.classtweaker +++ b/src/main/resources/skyblocker.classtweaker @@ -76,6 +76,7 @@ inject-interface net/minecraft/client/gui/screens/inventory/InventoryScreen de/h inject-interface net/minecraft/client/renderer/LevelRenderer de/hysky/skyblocker/injected/EntityRenderMarker inject-interface net/minecraft/client/renderer/feature/ItemFeatureRenderer$Submit de/hysky/skyblocker/injected/CustomGlowState inject-interface net/minecraft/client/renderer/rendertype/RenderType de/hysky/skyblocker/injected/GlowRenderTypeHolder +inject-interface net/minecraft/client/gui/GuiGraphicsExtractor de/hysky/skyblocker/injected/SkyblockerGraphicsExtractor # Suggestions accessible field net/minecraft/client/gui/components/CommandSuggestions input Lnet/minecraft/client/gui/components/EditBox; diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index e924fd2a860..5b14ca2a508 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -1,107 +1,108 @@ { - "required": true, - "package": "de.hysky.skyblocker.mixins", - "plugin": "de.hysky.skyblocker.compatibility.MixinPlugin", - "compatibilityLevel": "JAVA_25", - "client": [ - "AbstractContainerMenuMixin", - "AbstractContainerScreenMixin", - "AbstractSignEditScreenMixin", - "BatMixin", - "BossHealthOverlayMixin", - "CameraMixin", - "ChestBlockEntityMixin", - "ChestMenuMixin", - "ClientboundCommandsPacketMixin", - "ClientCommonPacketListenerImplMixin", - "ClientLevelMixin", - "ClientPacketListenerMixin", - "ClientTooltipComponentMixin", - "ContainerScreenMixin", - "DataComponentHolderMixin", - "DyedItemColorMixin", - "EntityMixin", - "EntityRenderDispatcherMixin", - "EntityRendererMixin", - "EquipmentLayerRendererMixin", - "FishingHookMixin", - "FogRendererMixin", - "GameRendererMixin", - "GlCommandEncoderMixin", - "GuiGraphicsExtractorMixin", - "HudMixin", - "InventoryMixin", - "InventoryScreenMixin", - "ItemFeatureRendererMixin", - "ItemInHandRendererMixin", - "ItemStackMixin", - "KeyMappingMixin", - "LevelExtractorMixin", - "LevelLoadingScreenMixin", - "LevelRendererMixin", - "LocalPlayerMixin", - "MapRendererMixin", - "MenuScreensConstructorMixin", - "MinecraftMixin", - "MouseHandlerMixin", - "MultiPlayerGameModeMixin", - "MutableComponentMixin", - "OptionsMixin", - "PingDebugMonitorMixin", - "PlayerTabOverlayMixin", - "PreparedRenderTypeMixin", - "QuickNavMixin", - "QuickNavScreenMixin", - "RenderFishMixin", - "RenderSubmitMixin", - "RenderTypeMixin", - "ScoreboardMixin", - "ScreenEffectRendererMixin", - "ScreenMixin", - "SkinManagerMixin", - "SkinTextureDownloaderMixin", - "SocialInteractionsPlayerListMixin", - "SubmitNodeCollectionMixin", - "SynchedEntityDataMixin", - "WindowMixin", - "YggdrasilMinecraftSessionServiceMixin", - "YggdrasilServicesKeyInfoMixin", - "accessors.AbstractContainerScreenAccessor", - "accessors.BlockEntityRenderStateAccessor", - "accessors.ChatListenerAccessor", - "accessors.CheckboxAccessor", - "accessors.CustomDataAccessor", - "accessors.DataComponentPatchAccessor", - "accessors.DirectStateAccessInvoker", - "accessors.EditBoxAccessor", - "accessors.EnderManAccessor", - "accessors.EntityRenderDispatcherAccessor", - "accessors.FontAccessor", - "accessors.FrustumInvoker", - "accessors.GpuDeviceAccessor", - "accessors.GuiGraphicsExtractorInvoker", - "accessors.HudAccessor", - "accessors.InventoryScreenInvoker", - "accessors.MapItemSavedDataAccessor", - "accessors.MapRendererInvoker", - "accessors.MinecraftAccessor", - "accessors.ModelManagerAccessor", - "accessors.PlayerTabOverlayAccessor", - "accessors.PopupScreenAccessor", - "accessors.RecipeBookComponentAccessor", - "accessors.RenderSetupAccessor", - "accessors.ScreenAccessor", - "accessors.SlotAccessor", - "accessors.SpriteContentsAccessor", - "adaptivetooltips.ScrollTrackerMixin", - "discordipc.ConnectionMixin", - "jgit.SystemReaderMixin", - "jgit.UrlConfigMixin" - ], - "injectors": { - "defaultRequire": 1 - }, - "mixinextras": { - "minVersion": "0.5.0" + "required": true, + "package": "de.hysky.skyblocker.mixins", + "plugin": "de.hysky.skyblocker.compatibility.MixinPlugin", + "compatibilityLevel": "JAVA_25", + "client": [ + "AbstractContainerMenuMixin", + "AbstractContainerScreenMixin", + "AbstractContainerWidgetMixin", + "AbstractSignEditScreenMixin", + "BatMixin", + "BossHealthOverlayMixin", + "CameraMixin", + "ChestBlockEntityMixin", + "ChestMenuMixin", + "ClientboundCommandsPacketMixin", + "ClientCommonPacketListenerImplMixin", + "ClientLevelMixin", + "ClientPacketListenerMixin", + "ClientTooltipComponentMixin", + "ContainerScreenMixin", + "DataComponentHolderMixin", + "DyedItemColorMixin", + "EntityMixin", + "EntityRenderDispatcherMixin", + "EntityRendererMixin", + "EquipmentLayerRendererMixin", + "FishingHookMixin", + "FogRendererMixin", + "GameRendererMixin", + "GlCommandEncoderMixin", + "GuiGraphicsExtractorMixin", + "HudMixin", + "InventoryMixin", + "InventoryScreenMixin", + "ItemFeatureRendererMixin", + "ItemInHandRendererMixin", + "ItemStackMixin", + "KeyMappingMixin", + "LevelExtractorMixin", + "LevelLoadingScreenMixin", + "LevelRendererMixin", + "LocalPlayerMixin", + "MapRendererMixin", + "MenuScreensConstructorMixin", + "MinecraftMixin", + "MouseHandlerMixin", + "MultiPlayerGameModeMixin", + "MutableComponentMixin", + "OptionsMixin", + "PingDebugMonitorMixin", + "PlayerTabOverlayMixin", + "PreparedRenderTypeMixin", + "QuickNavMixin", + "QuickNavScreenMixin", + "RenderFishMixin", + "RenderSubmitMixin", + "RenderTypeMixin", + "ScoreboardMixin", + "ScreenEffectRendererMixin", + "ScreenMixin", + "SkinManagerMixin", + "SkinTextureDownloaderMixin", + "SocialInteractionsPlayerListMixin", + "SubmitNodeCollectionMixin", + "SynchedEntityDataMixin", + "WindowMixin", + "YggdrasilMinecraftSessionServiceMixin", + "YggdrasilServicesKeyInfoMixin", + "accessors.AbstractContainerScreenAccessor", + "accessors.BlockEntityRenderStateAccessor", + "accessors.ChatListenerAccessor", + "accessors.CheckboxAccessor", + "accessors.CustomDataAccessor", + "accessors.DataComponentPatchAccessor", + "accessors.DirectStateAccessInvoker", + "accessors.EditBoxAccessor", + "accessors.EnderManAccessor", + "accessors.EntityRenderDispatcherAccessor", + "accessors.FontAccessor", + "accessors.FrustumInvoker", + "accessors.GpuDeviceAccessor", + "accessors.GuiGraphicsExtractorInvoker", + "accessors.HudAccessor", + "accessors.InventoryScreenInvoker", + "accessors.MapItemSavedDataAccessor", + "accessors.MapRendererInvoker", + "accessors.MinecraftAccessor", + "accessors.ModelManagerAccessor", + "accessors.PlayerTabOverlayAccessor", + "accessors.PopupScreenAccessor", + "accessors.RecipeBookComponentAccessor", + "accessors.RenderSetupAccessor", + "accessors.ScreenAccessor", + "accessors.SlotAccessor", + "accessors.SpriteContentsAccessor", + "adaptivetooltips.ScrollTrackerMixin", + "discordipc.ConnectionMixin", + "jgit.SystemReaderMixin", + "jgit.UrlConfigMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "mixinextras": { + "minVersion": "0.5.0" } } From 7129a61401f41623125deddf4aa1fb849e7d1534 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Thu, 30 Jul 2026 16:31:49 +0200 Subject: [PATCH 07/15] remove debugging stuff --- .../config/screens/quicknav/ItemEditPopup.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index 22497e3c63d..8ab93e0a206 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -23,8 +23,6 @@ import net.minecraft.client.gui.components.ScrollableLayout; import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.components.Tooltip; -import net.minecraft.client.gui.components.events.ContainerEventHandler; -import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.layouts.GridLayout; import net.minecraft.client.gui.layouts.LayoutElement; import net.minecraft.client.gui.layouts.LayoutSettings; @@ -196,13 +194,6 @@ public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mou @Override public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { super.extractRenderState(graphics, mouseX, mouseY, a); - int y = 5; - GuiEventListener thing = getFocused(); - while (thing != null) { - graphics.text(font, String.valueOf(thing), 5, y, -1); - thing = thing instanceof ContainerEventHandler container ? container.getFocused() : null; - y += font.lineHeight; - } } @Override From a10747f2e69ff2bcb31b07f9a3080623bc5e363e Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Thu, 30 Jul 2026 16:36:25 +0200 Subject: [PATCH 08/15] dumb --- .../skyblocker/config/screens/quicknav/ItemEditPopup.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index 8ab93e0a206..0297ed545b7 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -145,10 +145,9 @@ protected void init() { LinearLayout doubleClickLayout = content.addChild(createSectionLayout()); doubleClickLayout.addChild(Checkbox.builder(Component.literal("Require Double Click"), font) .onValueChange((_, value) -> item.doubleClick = value) - .tooltip(Tooltip.create(Component.literal("Useful to limit missclicks on warp buttons."))) .selected(item.doubleClick) .build() - ); + ).setTooltip(Tooltip.create(Component.literal("Useful to limit missclicks on warp buttons."))); content.addChild(SpacerElement.height(0)); scrollableContent = layout.addChild(new ScrollableLayout(minecraft, content, height - SCROLLABLE_CONTENT_HEIGHT_DIFF)); From 1420c94899c81898eedfbcdf05c140a4aef06f97 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Thu, 30 Jul 2026 16:58:09 +0200 Subject: [PATCH 09/15] translatable --- .../screens/quicknav/ItemEditPopup.java | 54 +++++++++---------- .../assets/skyblocker/lang/en_us.json | 4 ++ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index 0297ed545b7..4feb34851cc 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -1,5 +1,6 @@ package de.hysky.skyblocker.config.screens.quicknav; +import com.demonwav.mcdev.annotations.Translatable; import com.google.gson.JsonElement; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.logging.LogUtils; @@ -44,7 +45,7 @@ class ItemEditPopup extends AbstractPopupScreen { private static final Logger LOGGER = LogUtils.getLogger(); - private static final int SCROLLABLE_CONTENT_HEIGHT_DIFF = BACKGROUND_MARGIN * 2 + 20 + 40; + private static final int SCROLLABLE_CONTENT_HEIGHT_DIFF = BACKGROUND_MARGIN * 2 + 20 + 50; // 20: height of buttons, 50: some hardcoded constant private final Runnable onClose; private final QuickNavigationConfig.QuickNavItem item; @@ -55,7 +56,7 @@ class ItemEditPopup extends AbstractPopupScreen { private Component currentTooltip; ItemEditPopup(Screen backgroundScreen, Runnable onClose, QuickNavigationConfig.QuickNavItem item, QuickNavConfigScreen.ConfigItemSetter setter, int index) { - super(Component.literal("Editing Button " + index).withStyle(ChatFormatting.BOLD, ChatFormatting.UNDERLINE), backgroundScreen); + super(Component.translatable("skyblocker.config.quickNav.screen.title", index + 1).withStyle(ChatFormatting.BOLD, ChatFormatting.UNDERLINE), backgroundScreen); this.onClose = onClose; this.item = new QuickNavigationConfig.QuickNavItem(item); this.setter = setter; @@ -74,25 +75,24 @@ protected void init() { CommandBuildContext context = CommandUtils.newContext(); LinearLayout commandLayout = content.addChild(createSectionLayout()); // click event - addTitle(commandLayout, "Click Event"); + addTitle(commandLayout, "skyblocker.config.quickNav.button.clickEvent"); EditBox commandBox = SuggestionsEditBox.builder().width(250).buildVanillaDispatcher( minecraft, font, this, Component.empty(), false); commandBox.setValue(item.clickEvent); - commandBox.setTooltip(Tooltip.create(Component.literal("Command to run."))); commandBox.setResponder(s -> item.clickEvent = s); commandLayout.addChild(commandBox); // tooltip LinearLayout tooltipLayout = content.addChild(createSectionLayout()); - addTitle(tooltipLayout, "Tooltip"); + addTitle(tooltipLayout, "skyblocker.config.quickNav.button.tooltip"); ComponentEditWidget editWidget = new ComponentEditWidget(this, Component.literal("Customize Tooltip"), component -> currentTooltip = component.copy()); tooltipLayout.addChild(editWidget); editWidget.setText(currentTooltip.copy(), false); // menu regex LinearLayout regexLayout = content.addChild(createSectionLayout()); - addTitle(regexLayout, "Menu Title"); + addTitle(regexLayout, "skyblocker.config.quickNav.button.uiTitle"); SuggestionsEditBox.Argument patternBox = SuggestionsEditBox.builder().width(250).onlyShowIfCursorPastError(false).buildArg( minecraft, font, this, Component.empty(), new RegexArgumentType() @@ -103,16 +103,15 @@ protected void init() { patternBox.setValueResponder(p -> item.uiTitle = p.pattern().isBlank() ? "lorem ipsum" : p.pattern()); regexLayout.addChild(patternBox); - // item selection LinearLayout iconLayout = content.addChild(createSectionLayout()); - addTitle(iconLayout, "Icon"); + addTitle(iconLayout, "skyblocker.config.quickNav.button.icon"); GridLayout itemLayout = iconLayout.addChild(new GridLayout()).columnSpacing(4).rowSpacing(2); ItemStack stack = ItemStackComponentizationFixer.fromComponentsString(item.itemData.item.toString(), item.itemData.count, item.itemData.components); ItemWidget itemWidget = itemLayout.addChild(new ItemWidget(stack), 1, 0, l -> l.alignVerticallyMiddle().alignHorizontallyCenter()); int itemWidth = 250; - itemLayout.addChild(new StringWidget(Component.literal("Item"), font), 0, 1).setMaxWidth(itemWidth, StringWidget.TextOverflow.SCROLLING); + itemLayout.addChild(new StringWidget(Component.translatable("skyblocker.config.quickNav.button.item.itemName"), font), 0, 1).setMaxWidth(itemWidth, StringWidget.TextOverflow.SCROLLING); SuggestionsEditBox.Argument itemBox = SuggestionsEditBox.builder().width(250).buildArg( minecraft, font, this, Component.empty(), new ItemArgument(context) @@ -120,9 +119,15 @@ protected void init() { itemLayout.addChild(itemBox, 1, 1); itemBox.setMaxLength(4096); itemBox.setValue(item.itemData.item + item.itemData.components); + itemBox.setValueResponder(itemInput -> { + ItemStack itemStack = new ItemStack(itemInput.item(), item.itemData.count, itemInput.components()); + itemWidget.stack = itemStack; + item.itemData.item = itemStack.getItem(); + item.itemData.components = ItemStackComponentizationFixer.componentsAsString(itemStack); + }); int countWidth = 30; - itemLayout.addChild(new StringWidget(Component.literal("Count"), font), 0, 2).setMaxWidth(countWidth, StringWidget.TextOverflow.SCROLLING); + itemLayout.addChild(new StringWidget(Component.translatable("skyblocker.config.quickNav.screen.count"), font), 0, 2).setMaxWidth(countWidth, StringWidget.TextOverflow.SCROLLING); SuggestionsEditBox.Argument countBox = SuggestionsEditBox.builder().width(20).buildArg( minecraft, font, this, Component.empty(), IntegerArgumentType.integer(1) @@ -130,8 +135,12 @@ protected void init() { itemLayout.addChild(countBox, 1, 2); countBox.setMaxLength(2); countBox.setValue(String.valueOf(item.itemData.count)); + countBox.setValueResponder(count -> { + item.itemData.count = Math.max(count, 1); + itemWidget.stack = itemWidget.stack.copyWithCount(item.itemData.count); + }); - iconLayout.addChild(ButtonWidget.builder(Component.literal("Select Item"), _ -> minecraft.gui.setScreen( + iconLayout.addChild(ButtonWidget.builder(Component.translatable("skyblocker.config.quickNav.button.chooseSkyblockItem"), _ -> minecraft.gui.setScreen( new ItemSelectionPopup(this, itemStack -> { if (itemStack != null) { itemWidget.stack = itemStack; @@ -140,14 +149,15 @@ protected void init() { item.itemData.components = components; itemBox.setValue(itemStack.getItem() + components); } - }))).build()); + }))).tooltip(Tooltip.create(Component.translatable("skyblocker.config.quickNav.button.chooseSkyblockItem.@Tooltip"))).build()); + // require double click LinearLayout doubleClickLayout = content.addChild(createSectionLayout()); - doubleClickLayout.addChild(Checkbox.builder(Component.literal("Require Double Click"), font) + doubleClickLayout.addChild(Checkbox.builder(Component.translatable("skyblocker.config.quickNav.button.doubleClick"), font) .onValueChange((_, value) -> item.doubleClick = value) .selected(item.doubleClick) .build() - ).setTooltip(Tooltip.create(Component.literal("Useful to limit missclicks on warp buttons."))); + ).setTooltip(Tooltip.create(Component.translatable("skyblocker.config.quickNav.button.doubleClick.@Tooltip"))); content.addChild(SpacerElement.height(0)); scrollableContent = layout.addChild(new ScrollableLayout(minecraft, content, height - SCROLLABLE_CONTENT_HEIGHT_DIFF)); @@ -161,21 +171,11 @@ protected void init() { }).build()); layout.visitWidgets(this::addRenderableWidget); - itemBox.setValueResponder(itemInput -> { - ItemStack itemStack = new ItemStack(itemInput.item(), item.itemData.count, itemInput.components()); - itemWidget.stack = itemStack; - item.itemData.item = itemStack.getItem(); - item.itemData.components = ItemStackComponentizationFixer.componentsAsString(itemStack); - }); - countBox.setValueResponder(count -> { - item.itemData.count = Math.max(count, 1); - itemWidget.stack = itemWidget.stack.copyWithCount(item.itemData.count); - }); super.init(); } - private void addTitle(LinearLayout layout, String title) { - layout.addChild(new StringWidget(Component.literal(title).withStyle(ChatFormatting.BOLD), font), l -> l.paddingBottom(4)); + private void addTitle(LinearLayout layout, @Translatable String title) { + layout.addChild(new StringWidget(Component.translatable(title).withStyle(ChatFormatting.BOLD), font), l -> l.paddingBottom(4)); } private LinearLayout createSectionLayout() { @@ -213,7 +213,7 @@ public void onClose() { private void save() { item.tooltip = ComponentSerialization.CODEC.encodeStart(JsonOps.INSTANCE, currentTooltip) .ifError(error -> LOGGER.error("Failed to serialize component! {}", error.message())).result() - .map(SkyblockerMod.GSON_COMPACT::toJson).orElse(""); + .map(SkyblockerMod.GSON_COMPACT::toJson).orElse(currentTooltip.getString()); SkyblockerConfigManager.updateOnly(config -> setter.accept(config.quickNav, item)); } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 08e180a11c8..ffbe5f16207 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -1076,6 +1076,8 @@ "skyblocker.config.quickNav.button.chooseSkyblockItem.@Tooltip": "Select a SkyBlock icon or item to fill this button with.", "skyblocker.config.quickNav.button.clickEvent": "Click Event", "skyblocker.config.quickNav.button.doubleClick": "Require Double Click", + "skyblocker.config.quickNav.button.doubleClick.@Tooltip": "Useful to avoid miss-clicks with warp buttons.", + "skyblocker.config.quickNav.button.icon": "Icon", "skyblocker.config.quickNav.button.item.components": "Item Components", "skyblocker.config.quickNav.button.item.components.@Tooltip": "A string of item components enclosed in square brackets, which is the same format as the /give command.\n\nExample: [minecraft:enchantment_glint_override=true]", "skyblocker.config.quickNav.button.item.count": "Item Count", @@ -1087,6 +1089,8 @@ "skyblocker.config.quickNav.enableQuickNav": "Enable Quick Navigation", "skyblocker.config.quickNav.enableQuickNav.@Tooltip": "Adds 14 customizable inventory buttons that can be styled and run a command when clicked.", "skyblocker.config.quickNav.quickNavConfigScreen": "Open Configuration Screen", + "skyblocker.config.quickNav.screen.title": "Editing Button %s", + "skyblocker.config.quickNav.screen.count": "Editing Button %s", "skyblocker.config.shortcutToKeybindsSettings": "Edit Keybind", "skyblocker.config.shortcutToKeybindsSettings.@Text": "Keybinds Menu", From 034f54fd02c44731badd6e20848f9cc1cf33b6db Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Thu, 30 Jul 2026 17:10:59 +0200 Subject: [PATCH 10/15] spotless and remove unused things (ComponentArgument etc.) --- .../de/hysky/skyblocker/SkyblockerScreen.java | 1 - .../utils/command/CommandUtils.java | 6 -- .../argumenttypes/item/ComponentArgument.java | 45 ---------- .../argumenttypes/item/ComponentParser.java | 86 ------------------- .../argumenttypes/item/package-info.java | 4 - .../suggestions/TextFieldSuggestions.java | 2 - .../utils/render/gui/SuggestionsEditBox.java | 2 - .../assets/skyblocker/lang/en_us.json | 38 ++++---- src/main/resources/skyblocker.classtweaker | 6 -- 9 files changed, 19 insertions(+), 171 deletions(-) delete mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentArgument.java delete mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentParser.java delete mode 100644 src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/package-info.java diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java b/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java index ea85c677844..2f62b1033c5 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java @@ -5,7 +5,6 @@ import de.hysky.skyblocker.skyblock.Tips; import de.hysky.skyblocker.utils.FunUtils; import de.hysky.skyblocker.utils.LogsFolderFinder; -import de.hysky.skyblocker.utils.command.CommandUtils; import de.hysky.skyblocker.utils.scheduler.Scheduler; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.ClientCommands; diff --git a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java index 34eaa2c38a9..ba5b3e84307 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java @@ -4,27 +4,21 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.ParseResults; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.tree.RootCommandNode; -import de.hysky.skyblocker.config.configs.QuickNavigationConfig; import de.hysky.skyblocker.utils.RegistryUtils; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientSuggestionProvider; import net.minecraft.client.player.LocalPlayer; import net.minecraft.commands.CommandBuildContext; -import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.data.registries.VanillaRegistries; import net.minecraft.resources.ResourceKey; import net.minecraft.server.permissions.PermissionSet; import net.minecraft.world.flag.FeatureFlagSet; -import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentArgument.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentArgument.java deleted file mode 100644 index f0c763414d5..00000000000 --- a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentArgument.java +++ /dev/null @@ -1,45 +0,0 @@ -package de.hysky.skyblocker.utils.command.argumenttypes.item; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.CommandBuildContext; -import net.minecraft.commands.arguments.item.ItemParser; -import net.minecraft.core.component.DataComponentPatch; - -import java.util.concurrent.CompletableFuture; - -public class ComponentArgument implements ArgumentType { - - private final ComponentParser parser; - - public ComponentArgument(CommandBuildContext context) { - parser = new ComponentParser(context); - } - - @Override - public DataComponentPatch parse(StringReader reader) throws CommandSyntaxException { - return parser.parse(reader).components(); - // insert a fake item so we directly go to the component part of the ItemParser - /*final String fakeItem = "minecraft:stone"; - StringReader fakeReader = new StringReader(new StringBuilder(reader.getString()).insert(reader.getCursor(), fakeItem).toString()); - fakeReader.setCursor(reader.getCursor()); - DataComponentPatch components = this.parser.parse(fakeReader).components(); - reader.setCursor(fakeReader.getCursor() - fakeItem.length()); - return components;*/ - } - - @Override - public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { - /*StringBuilder stringBuilder = new StringBuilder(builder.getInput()); - stringBuilder.insert(builder.getStart(), "minecraft:stone"); - SuggestionsBuilder fakeBuilder = new SuggestionsBuilder(stringBuilder.toString(), builder.getStart()); - System.out.println("Original: " + builder.getInput() + " " + builder.getStart() + " " + builder.getRemaining()); - System.out.println("Fake: " + fakeBuilder.getInput() + " " + fakeBuilder.getStart() + " " + fakeBuilder.getRemaining()); - return this.parser.fillSuggestions(fakeBuilder);*/ - return parser.fillSuggestions(builder); - } -} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentParser.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentParser.java deleted file mode 100644 index b319ba44fe0..00000000000 --- a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/ComponentParser.java +++ /dev/null @@ -1,86 +0,0 @@ -package de.hysky.skyblocker.utils.command.argumenttypes.item; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.arguments.item.ItemInput; -import net.minecraft.commands.arguments.item.ItemParser; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.component.DataComponentPatch; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; -import org.apache.commons.lang3.mutable.MutableObject; - -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; - -public class ComponentParser extends ItemParser { - public ComponentParser(HolderLookup.Provider registries) { - super(registries); - } - - @Override - public ItemInput parse(StringReader reader) throws CommandSyntaxException { - final DataComponentPatch.Builder componentsBuilder = DataComponentPatch.builder(); - this.parse(reader, new ItemParser.Visitor() /* ItemParser$1 */ { - @Override - public void visitComponent(final DataComponentType type, final T value) { - componentsBuilder.set(type, value); - } - - @Override - public void visitRemovedComponent(final DataComponentType type) { - componentsBuilder.remove(type); - } - }); - DataComponentPatch components = componentsBuilder.build(); - return new ItemInput(Holder.direct(Items.AIR), components); - } - - @Override - public void parse(StringReader reader, Visitor visitor) throws CommandSyntaxException { - int cursor = reader.getCursor(); - - try { - (new ComponentState(reader, visitor)).parse(); - } catch (CommandSyntaxException e) { - reader.setCursor(cursor); - throw e; - } - } - - @Override - public CompletableFuture fillSuggestions(SuggestionsBuilder builder) { - StringReader reader = new StringReader(builder.getInput()); - reader.setCursor(builder.getStart()); - SuggestionsVisitor handler = new ItemParser.SuggestionsVisitor(); - ItemParser.State state = new ComponentState(reader, handler); - - try { - state.parse(); - } catch (CommandSyntaxException _) { - } - - return handler.resolveSuggestions(builder, reader); - } - - - private class ComponentState extends State { - - protected ComponentState(StringReader reader, Visitor visitor) { - super(reader, visitor); - } - - @Override - protected void readItem() {} - - @Override - protected CompletableFuture suggestItem(SuggestionsBuilder builder) { - return Suggestions.empty(); - } - } -} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/package-info.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/package-info.java deleted file mode 100644 index 3d116338e94..00000000000 --- a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/item/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NullMarked -package de.hysky.skyblocker.utils.command.argumenttypes.item; - -import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java index 1d367787958..7d841cc0db9 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java @@ -1,6 +1,5 @@ package de.hysky.skyblocker.utils.command.suggestions; -import com.google.common.base.Suppliers; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContext; @@ -24,7 +23,6 @@ import java.util.List; import java.util.Optional; -import java.util.function.Supplier; public class TextFieldSuggestions extends CommandSuggestions { diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java index 1f4e9195e94..a155e548dea 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java @@ -13,7 +13,6 @@ import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.gui.screens.Screen; @@ -23,7 +22,6 @@ import net.minecraft.network.chat.Component; import org.jspecify.annotations.Nullable; -import java.util.List; import java.util.Optional; import java.util.function.Consumer; diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index ffbe5f16207..14e201dbf2a 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -1072,25 +1072,25 @@ "skyblocker.config.quickNav": "Quick Navigation", "skyblocker.config.quickNav.button": "Button %s", - "skyblocker.config.quickNav.button.chooseSkyblockItem": "Choose SkyBlock Item", - "skyblocker.config.quickNav.button.chooseSkyblockItem.@Tooltip": "Select a SkyBlock icon or item to fill this button with.", - "skyblocker.config.quickNav.button.clickEvent": "Click Event", - "skyblocker.config.quickNav.button.doubleClick": "Require Double Click", - "skyblocker.config.quickNav.button.doubleClick.@Tooltip": "Useful to avoid miss-clicks with warp buttons.", - "skyblocker.config.quickNav.button.icon": "Icon", - "skyblocker.config.quickNav.button.item.components": "Item Components", - "skyblocker.config.quickNav.button.item.components.@Tooltip": "A string of item components enclosed in square brackets, which is the same format as the /give command.\n\nExample: [minecraft:enchantment_glint_override=true]", - "skyblocker.config.quickNav.button.item.count": "Item Count", - "skyblocker.config.quickNav.button.item.itemName": "Item", - "skyblocker.config.quickNav.button.render": "Render", - "skyblocker.config.quickNav.button.tooltip": "Tooltip", - "skyblocker.config.quickNav.button.tooltip.@Tooltip": "Tooltip shown when hovering your mouse over the button.\n\nYou can use text json format.\nPutting nothing will make the tooltip not render.", - "skyblocker.config.quickNav.button.uiTitle": "UI Title", - "skyblocker.config.quickNav.enableQuickNav": "Enable Quick Navigation", - "skyblocker.config.quickNav.enableQuickNav.@Tooltip": "Adds 14 customizable inventory buttons that can be styled and run a command when clicked.", - "skyblocker.config.quickNav.quickNavConfigScreen": "Open Configuration Screen", - "skyblocker.config.quickNav.screen.title": "Editing Button %s", - "skyblocker.config.quickNav.screen.count": "Editing Button %s", + "skyblocker.config.quickNav.button.chooseSkyblockItem": "Choose SkyBlock Item", + "skyblocker.config.quickNav.button.chooseSkyblockItem.@Tooltip": "Select a SkyBlock icon or item to fill this button with.", + "skyblocker.config.quickNav.button.clickEvent": "Click Event", + "skyblocker.config.quickNav.button.doubleClick": "Require Double Click", + "skyblocker.config.quickNav.button.doubleClick.@Tooltip": "Useful to avoid miss-clicks with warp buttons.", + "skyblocker.config.quickNav.button.icon": "Icon", + "skyblocker.config.quickNav.button.item.components": "Item Components", + "skyblocker.config.quickNav.button.item.components.@Tooltip": "A string of item components enclosed in square brackets, which is the same format as the /give command.\n\nExample: [minecraft:enchantment_glint_override=true]", + "skyblocker.config.quickNav.button.item.count": "Item Count", + "skyblocker.config.quickNav.button.item.itemName": "Item", + "skyblocker.config.quickNav.button.render": "Render", + "skyblocker.config.quickNav.button.tooltip": "Tooltip", + "skyblocker.config.quickNav.button.tooltip.@Tooltip": "Tooltip shown when hovering your mouse over the button.\n\nYou can use text json format.\nPutting nothing will make the tooltip not render.", + "skyblocker.config.quickNav.button.uiTitle": "UI Title", + "skyblocker.config.quickNav.enableQuickNav": "Enable Quick Navigation", + "skyblocker.config.quickNav.enableQuickNav.@Tooltip": "Adds 14 customizable inventory buttons that can be styled and run a command when clicked.", + "skyblocker.config.quickNav.quickNavConfigScreen": "Open Configuration Screen", + "skyblocker.config.quickNav.screen.count": "Editing Button %s", + "skyblocker.config.quickNav.screen.title": "Editing Button %s", "skyblocker.config.shortcutToKeybindsSettings": "Edit Keybind", "skyblocker.config.shortcutToKeybindsSettings.@Text": "Keybinds Menu", diff --git a/src/main/resources/skyblocker.classtweaker b/src/main/resources/skyblocker.classtweaker index 78a710a3856..08301f74c15 100644 --- a/src/main/resources/skyblocker.classtweaker +++ b/src/main/resources/skyblocker.classtweaker @@ -95,9 +95,3 @@ mutable field net/minecraft/client/gui/components/CommandSuggestions$Suggestions accessible field net/minecraft/client/gui/components/CommandSuggestions$SuggestionsList rect Lnet/minecraft/client/renderer/Rect2i; extendable method net/minecraft/client/gui/components/CommandSuggestions updateUsageInfo (Lcom/mojang/brigadier/ParseResults;Lcom/mojang/brigadier/suggestion/Suggestions;)V extendable method net/minecraft/client/gui/components/CommandSuggestions fillNodeUsage (Lcom/mojang/brigadier/context/SuggestionContext;Lnet/minecraft/network/chat/Style;)Ljava/util/List; -extendable method net/minecraft/commands/arguments/item/ItemParser$State (Lnet/minecraft/commands/arguments/item/ItemParser;Lcom/mojang/brigadier/StringReader;Lnet/minecraft/commands/arguments/item/ItemParser$Visitor;)V -extendable method net/minecraft/commands/arguments/item/ItemParser$State suggestItem (Lcom/mojang/brigadier/suggestion/SuggestionsBuilder;)Ljava/util/concurrent/CompletableFuture; -extendable method net/minecraft/commands/arguments/item/ItemParser$State readItem ()V -extendable method net/minecraft/commands/arguments/item/ItemParser$State readComponents ()V -extendable method net/minecraft/commands/arguments/item/ItemParser$State suggestStartComponents (Lcom/mojang/brigadier/suggestion/SuggestionsBuilder;)Ljava/util/concurrent/CompletableFuture; -accessible method net/minecraft/commands/arguments/item/ItemParser$SuggestionsVisitor ()V From b5a994bef4049c4b560fbe16dace4d88c809df30 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Thu, 30 Jul 2026 17:16:13 +0200 Subject: [PATCH 11/15] not sure why this turned into spaces --- src/main/resources/skyblocker.mixins.json | 210 +++++++++++----------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index 5b14ca2a508..17b7cbee8b0 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -1,108 +1,108 @@ { - "required": true, - "package": "de.hysky.skyblocker.mixins", - "plugin": "de.hysky.skyblocker.compatibility.MixinPlugin", - "compatibilityLevel": "JAVA_25", - "client": [ - "AbstractContainerMenuMixin", - "AbstractContainerScreenMixin", - "AbstractContainerWidgetMixin", - "AbstractSignEditScreenMixin", - "BatMixin", - "BossHealthOverlayMixin", - "CameraMixin", - "ChestBlockEntityMixin", - "ChestMenuMixin", - "ClientboundCommandsPacketMixin", - "ClientCommonPacketListenerImplMixin", - "ClientLevelMixin", - "ClientPacketListenerMixin", - "ClientTooltipComponentMixin", - "ContainerScreenMixin", - "DataComponentHolderMixin", - "DyedItemColorMixin", - "EntityMixin", - "EntityRenderDispatcherMixin", - "EntityRendererMixin", - "EquipmentLayerRendererMixin", - "FishingHookMixin", - "FogRendererMixin", - "GameRendererMixin", - "GlCommandEncoderMixin", - "GuiGraphicsExtractorMixin", - "HudMixin", - "InventoryMixin", - "InventoryScreenMixin", - "ItemFeatureRendererMixin", - "ItemInHandRendererMixin", - "ItemStackMixin", - "KeyMappingMixin", - "LevelExtractorMixin", - "LevelLoadingScreenMixin", - "LevelRendererMixin", - "LocalPlayerMixin", - "MapRendererMixin", - "MenuScreensConstructorMixin", - "MinecraftMixin", - "MouseHandlerMixin", - "MultiPlayerGameModeMixin", - "MutableComponentMixin", - "OptionsMixin", - "PingDebugMonitorMixin", - "PlayerTabOverlayMixin", - "PreparedRenderTypeMixin", - "QuickNavMixin", - "QuickNavScreenMixin", - "RenderFishMixin", - "RenderSubmitMixin", - "RenderTypeMixin", - "ScoreboardMixin", - "ScreenEffectRendererMixin", - "ScreenMixin", - "SkinManagerMixin", - "SkinTextureDownloaderMixin", - "SocialInteractionsPlayerListMixin", - "SubmitNodeCollectionMixin", - "SynchedEntityDataMixin", - "WindowMixin", - "YggdrasilMinecraftSessionServiceMixin", - "YggdrasilServicesKeyInfoMixin", - "accessors.AbstractContainerScreenAccessor", - "accessors.BlockEntityRenderStateAccessor", - "accessors.ChatListenerAccessor", - "accessors.CheckboxAccessor", - "accessors.CustomDataAccessor", - "accessors.DataComponentPatchAccessor", - "accessors.DirectStateAccessInvoker", - "accessors.EditBoxAccessor", - "accessors.EnderManAccessor", - "accessors.EntityRenderDispatcherAccessor", - "accessors.FontAccessor", - "accessors.FrustumInvoker", - "accessors.GpuDeviceAccessor", - "accessors.GuiGraphicsExtractorInvoker", - "accessors.HudAccessor", - "accessors.InventoryScreenInvoker", - "accessors.MapItemSavedDataAccessor", - "accessors.MapRendererInvoker", - "accessors.MinecraftAccessor", - "accessors.ModelManagerAccessor", - "accessors.PlayerTabOverlayAccessor", - "accessors.PopupScreenAccessor", - "accessors.RecipeBookComponentAccessor", - "accessors.RenderSetupAccessor", - "accessors.ScreenAccessor", - "accessors.SlotAccessor", - "accessors.SpriteContentsAccessor", - "adaptivetooltips.ScrollTrackerMixin", - "discordipc.ConnectionMixin", - "jgit.SystemReaderMixin", - "jgit.UrlConfigMixin" - ], - "injectors": { - "defaultRequire": 1 - }, - "mixinextras": { - "minVersion": "0.5.0" + "required": true, + "package": "de.hysky.skyblocker.mixins", + "plugin": "de.hysky.skyblocker.compatibility.MixinPlugin", + "compatibilityLevel": "JAVA_25", + "client": [ + "AbstractContainerMenuMixin", + "AbstractContainerScreenMixin", + "AbstractContainerWidgetMixin", + "AbstractSignEditScreenMixin", + "BatMixin", + "BossHealthOverlayMixin", + "CameraMixin", + "ChestBlockEntityMixin", + "ChestMenuMixin", + "ClientboundCommandsPacketMixin", + "ClientCommonPacketListenerImplMixin", + "ClientLevelMixin", + "ClientPacketListenerMixin", + "ClientTooltipComponentMixin", + "ContainerScreenMixin", + "DataComponentHolderMixin", + "DyedItemColorMixin", + "EntityMixin", + "EntityRenderDispatcherMixin", + "EntityRendererMixin", + "EquipmentLayerRendererMixin", + "FishingHookMixin", + "FogRendererMixin", + "GameRendererMixin", + "GlCommandEncoderMixin", + "GuiGraphicsExtractorMixin", + "HudMixin", + "InventoryMixin", + "InventoryScreenMixin", + "ItemFeatureRendererMixin", + "ItemInHandRendererMixin", + "ItemStackMixin", + "KeyMappingMixin", + "LevelExtractorMixin", + "LevelLoadingScreenMixin", + "LevelRendererMixin", + "LocalPlayerMixin", + "MapRendererMixin", + "MenuScreensConstructorMixin", + "MinecraftMixin", + "MouseHandlerMixin", + "MultiPlayerGameModeMixin", + "MutableComponentMixin", + "OptionsMixin", + "PingDebugMonitorMixin", + "PlayerTabOverlayMixin", + "PreparedRenderTypeMixin", + "QuickNavMixin", + "QuickNavScreenMixin", + "RenderFishMixin", + "RenderSubmitMixin", + "RenderTypeMixin", + "ScoreboardMixin", + "ScreenEffectRendererMixin", + "ScreenMixin", + "SkinManagerMixin", + "SkinTextureDownloaderMixin", + "SocialInteractionsPlayerListMixin", + "SubmitNodeCollectionMixin", + "SynchedEntityDataMixin", + "WindowMixin", + "YggdrasilMinecraftSessionServiceMixin", + "YggdrasilServicesKeyInfoMixin", + "accessors.AbstractContainerScreenAccessor", + "accessors.BlockEntityRenderStateAccessor", + "accessors.ChatListenerAccessor", + "accessors.CheckboxAccessor", + "accessors.CustomDataAccessor", + "accessors.DataComponentPatchAccessor", + "accessors.DirectStateAccessInvoker", + "accessors.EditBoxAccessor", + "accessors.EnderManAccessor", + "accessors.EntityRenderDispatcherAccessor", + "accessors.FontAccessor", + "accessors.FrustumInvoker", + "accessors.GpuDeviceAccessor", + "accessors.GuiGraphicsExtractorInvoker", + "accessors.HudAccessor", + "accessors.InventoryScreenInvoker", + "accessors.MapItemSavedDataAccessor", + "accessors.MapRendererInvoker", + "accessors.MinecraftAccessor", + "accessors.ModelManagerAccessor", + "accessors.PlayerTabOverlayAccessor", + "accessors.PopupScreenAccessor", + "accessors.RecipeBookComponentAccessor", + "accessors.RenderSetupAccessor", + "accessors.ScreenAccessor", + "accessors.SlotAccessor", + "accessors.SpriteContentsAccessor", + "adaptivetooltips.ScrollTrackerMixin", + "discordipc.ConnectionMixin", + "jgit.SystemReaderMixin", + "jgit.UrlConfigMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "mixinextras": { + "minVersion": "0.5.0" } } From d6d9261a1aedbb63cdec7910965720bdc0600022 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Fri, 31 Jul 2026 12:29:26 +0200 Subject: [PATCH 12/15] stylecheck --- .../skyblocker/config/screens/quicknav/ItemEditPopup.java | 4 ++-- .../config/screens/quicknav/QuickNavConfigScreen.java | 6 +++--- .../de/hysky/skyblocker/utils/command/CommandUtils.java | 2 +- .../skyblocker/utils/render/gui/SuggestionsEditBox.java | 7 +++++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index 4feb34851cc..e1c4e9a88a4 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -62,7 +62,7 @@ class ItemEditPopup extends AbstractPopupScreen { this.setter = setter; try { currentTooltip = ComponentSerialization.CODEC.decode(JsonOps.INSTANCE, SkyblockerMod.GSON.fromJson(item.tooltip, JsonElement.class)).getOrThrow().getFirst(); - } catch (Exception e) { + } catch (Exception _) { currentTooltip = Component.literal(item.tooltip); } } @@ -238,7 +238,7 @@ private static class BackgroundRender extends AbstractWidget { private final LayoutElement heightLayout; private final LayoutElement widthLayout; - public BackgroundRender(LayoutElement heightLayout, LayoutElement widthLayout) { + private BackgroundRender(LayoutElement heightLayout, LayoutElement widthLayout) { super(0, 0, 0, 0, Component.empty()); active = false; this.heightLayout = heightLayout; diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java index f7fe629f417..bb39704095d 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/QuickNavConfigScreen.java @@ -135,7 +135,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo if (entityToRender != null) { int xo = (width - INVENTORY_WIDTH) / 2; int yo = (height - INVENTORY_HEIGHT) / 2; - InventoryScreen.extractEntityInInventoryFollowsMouse(graphics, xo + 26, yo + 8, xo + 75, yo + 78, 30, 0.0625F, mouseX, mouseY, entityToRender); + InventoryScreen.extractEntityInInventoryFollowsMouse(graphics, xo + 26, yo + 8, xo + 75, yo + 78, 30, 0.0625f, mouseX, mouseY, entityToRender); } } @@ -144,7 +144,7 @@ public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mou super.extractBackground(graphics, mouseX, mouseY, a); int backgroundX = (width - INVENTORY_WIDTH) / 2; int backgroundY = (height - INVENTORY_HEIGHT) / 2; - graphics.blit(RenderPipelines.GUI_TEXTURED, InventoryScreen.INVENTORY_LOCATION, backgroundX, backgroundY, 0.0F, 0.0F, INVENTORY_WIDTH, INVENTORY_HEIGHT, 256, 256); + graphics.blit(RenderPipelines.GUI_TEXTURED, InventoryScreen.INVENTORY_LOCATION, backgroundX, backgroundY, 0.0f, 0.0f, INVENTORY_WIDTH, INVENTORY_HEIGHT, 256, 256); } @Override @@ -170,7 +170,7 @@ private class QuickNavConfigButton extends QuickNavButton { * @param icon the icon to display on the button. * @param tooltip the tooltip to show when hovered */ - public QuickNavConfigButton(boolean enabled, int index, ItemStack icon, String tooltip) { + private QuickNavConfigButton(boolean enabled, int index, ItemStack icon, String tooltip) { super(index, true, "", icon, tooltip); this.enabled = enabled; setRenderInFront(true); diff --git a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java index ba5b3e84307..1aa714fc3e1 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java @@ -103,7 +103,7 @@ public CompletableFuture customSuggestion(CommandContext context } private static class OfflineCommandDispatcher extends CommandDispatcher { - public OfflineCommandDispatcher(RootCommandNode root) { + private OfflineCommandDispatcher(RootCommandNode root) { super(root); } @Override diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java index a155e548dea..47312a32edf 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java @@ -40,7 +40,7 @@ public SuggestionsEditBox(Minecraft minecraft, Screen screen, Font font, int wid } suggestions.setAllowSuggestions(true); super.setResponder(this::onUpdate); - if (node instanceof ArgumentCommandNode argumentCommandNode) argument = argumentCommandNode.getName(); + if (node instanceof ArgumentCommandNode argumentCommandNode) argument = argumentCommandNode.getName(); else argument = null; Screens.getWidgets(screen).addFirst(new SuggestionsWrapper()); } @@ -202,9 +202,12 @@ public void setOptionalValueResponder(Consumer<@Nullable T> optionalValueRespond } } + /** + * Small wrapper so suggestions box interactions take priority + */ private class SuggestionsWrapper extends AbstractWidget { - public SuggestionsWrapper() { + private SuggestionsWrapper() { super(0, 0, 0, 0, Component.empty()); } From 480588d6c2891e551e45b6dde5ce1b5961eaa0ab Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sat, 1 Aug 2026 22:05:24 -0700 Subject: [PATCH 13/15] Clean up code --- .../screens/quicknav/ItemEditPopup.java | 5 ++-- .../injected/SkyblockerGraphicsExtractor.java | 2 +- .../skyblock/item/custom/screen/ItemTab.java | 28 ++++++++++--------- .../skyblock/quicknav/QuickNav.java | 7 ++--- .../skyblock/quicknav/QuickNavButton.java | 6 ++-- .../utils/command/CommandUtils.java | 6 ++-- .../utils/render/gui/AbstractPopupScreen.java | 6 ++-- .../utils/render/gui/ComponentEditWidget.java | 6 +++- 8 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java index e1c4e9a88a4..c34c8a7c52d 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/quicknav/ItemEditPopup.java @@ -149,7 +149,8 @@ protected void init() { item.itemData.components = components; itemBox.setValue(itemStack.getItem() + components); } - }))).tooltip(Tooltip.create(Component.translatable("skyblocker.config.quickNav.button.chooseSkyblockItem.@Tooltip"))).build()); + }) + )).tooltip(Tooltip.create(Component.translatable("skyblocker.config.quickNav.button.chooseSkyblockItem.@Tooltip"))).build()); // require double click LinearLayout doubleClickLayout = content.addChild(createSectionLayout()); @@ -187,7 +188,7 @@ private LinearLayout createSectionLayout() { @Override public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { super.extractBackground(graphics, mouseX, mouseY, a); - extractLighterPopupBackground(graphics, layout.getX(), layout.getY(), layout.getWidth(), layout.getHeight()); + extractPopupBackground(graphics, layout.getX(), layout.getY(), layout.getWidth(), layout.getHeight(), true); } @Override diff --git a/src/main/java/de/hysky/skyblocker/injected/SkyblockerGraphicsExtractor.java b/src/main/java/de/hysky/skyblocker/injected/SkyblockerGraphicsExtractor.java index 4df8814d97c..1e4612f6d96 100644 --- a/src/main/java/de/hysky/skyblocker/injected/SkyblockerGraphicsExtractor.java +++ b/src/main/java/de/hysky/skyblocker/injected/SkyblockerGraphicsExtractor.java @@ -5,5 +5,5 @@ public interface SkyblockerGraphicsExtractor { default void skb$addDeferredElement(Renderable renderable) { throw new AssertionError("Implemented in Mixin"); - }; + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ItemTab.java b/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ItemTab.java index fb2b6d593d4..22494cafd4c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ItemTab.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ItemTab.java @@ -107,21 +107,23 @@ public ItemTab(CustomizeScreen parentScreen) { LocalPlayer player = Minecraft.getInstance().player; - ItemStack handStack = player.getMainHandItem(); - if (!handStack.getUuid().isEmpty()) { - setCurrentItem(handStack); - return; - } - for (ItemStack stack : (Utils.isInTheRift() ? SkyblockInventoryScreen.equipment_rift : SkyblockInventoryScreen.equipment)) { - if (!stack.getUuid().isEmpty()) { - setCurrentItem(stack); + if (player != null) { + ItemStack handStack = player.getMainHandItem(); + if (!handStack.getUuid().isEmpty()) { + setCurrentItem(handStack); return; } - } - for (ItemStack stack : player.getInventory()) { - if (!stack.getUuid().isEmpty()) { - setCurrentItem(stack); - return; + for (ItemStack stack : (Utils.isInTheRift() ? SkyblockInventoryScreen.equipment_rift : SkyblockInventoryScreen.equipment)) { + if (!stack.getUuid().isEmpty()) { + setCurrentItem(stack); + return; + } + } + for (ItemStack stack : player.getInventory()) { + if (!stack.getUuid().isEmpty()) { + setCurrentItem(stack); + return; + } } } visitChildren(clickableWidget -> clickableWidget.visible = false); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java index b94b73cc41a..fdcd4e8bd3f 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java @@ -29,10 +29,8 @@ public class QuickNav { @Init public static void commandInit() { - ClientCommandRegistrationCallback.EVENT.register((dispatcher, _) -> { - dispatcher.register(ClientCommands.literal(SkyblockerMod.NAMESPACE).then( - ClientCommands.literal("quickNav").executes(Scheduler.queueOpenScreenCommand(QuickNavConfigScreen::new)))); - }); + ClientCommandRegistrationCallback.EVENT.register((dispatcher, _) -> dispatcher.register(ClientCommands.literal(SkyblockerMod.NAMESPACE).then( + ClientCommands.literal("quickNav").executes(Scheduler.queueOpenScreenCommand(QuickNavConfigScreen::new))))); } public static List init(String screenTitle) { @@ -61,6 +59,7 @@ public static List init(String screenTitle) { private static QuickNavButton parseButton(QuickNavigationConfig.QuickNavItem buttonInfo, String screenTitle, int id) throws CommandSyntaxException { QuickNavigationConfig.ItemData itemData = buttonInfo.itemData; + @SuppressWarnings("ConstantValue") ItemStack stack = itemData != null && itemData.item != null && itemData.components != null ? ItemStackComponentizationFixer.fromComponentsString(itemData.item.toString(), Math.clamp(itemData.count, 1, 99), itemData.components) : new ItemStack(Items.BARRIER); boolean uiTitleMatches = false; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java index bcbc11b41bc..d4538e89182 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java @@ -46,7 +46,7 @@ public class QuickNavButton extends AbstractWidget { private final boolean toggled; private final String command; private final ItemStack icon; - protected final Tooltip tooltip; + protected final @Nullable Tooltip tooltip; private boolean temporaryToggled = false; private long toggleTime; @@ -92,7 +92,7 @@ public QuickNavButton(int index, boolean toggled, String command, ItemStack icon this.command = command; this.icon = icon; this.toggleTime = 0; - if (tooltip == null || tooltip.isEmpty()) { + if (tooltip.isEmpty()) { this.tooltip = null; return; } @@ -145,7 +145,7 @@ public void onClick(MouseButtonEvent click, boolean doubled) { if (!this.temporaryToggled) { this.temporaryToggled = true; this.toggleTime = System.currentTimeMillis(); - if (command == null || command.isEmpty()) { + if (command.isEmpty() && Minecraft.getInstance().player != null) { Minecraft.getInstance().player.sendSystemMessage(Constants.PREFIX.get().append(Component.literal("Quick Nav button index " + (index + 1) + " has no command!").withStyle(ChatFormatting.RED))); } else { MessageScheduler.INSTANCE.sendMessageAfterCooldown(command, true); diff --git a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java index 1aa714fc3e1..606b0f195ca 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/CommandUtils.java @@ -56,12 +56,12 @@ public static ClientSuggestionProvider getOfflineSuggestionProvider() { return offlineSuggestionProvider; } - private static A unsafeCast(B b) { - return (A) b; + @SuppressWarnings("unchecked") + private static T unsafeCast(Object o) { + return (T) o; } - private static class FakeClientSuggestionProvider extends ClientSuggestionProvider { private final RegistryAccess registryAccess; diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java index 28683e88477..10ed43dea43 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java @@ -43,11 +43,11 @@ public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mou * These are the inner positions and size of the popup, not outer */ public static void extractPopupBackground(GuiGraphicsExtractor graphics, int x, int y, int width, int height) { - graphics.blitSprite(RenderPipelines.GUI_TEXTURED, BACKGROUND_TEXTURE, x - BACKGROUND_MARGIN, y - BACKGROUND_MARGIN, width + BACKGROUND_MARGIN * 2, height + BACKGROUND_MARGIN * 2); + extractPopupBackground(graphics, x, y, width, height, false); } - public static void extractLighterPopupBackground(GuiGraphicsExtractor graphics, int x, int y, int width, int height) { - graphics.blitSprite(RenderPipelines.GUI_TEXTURED, LIGHTER_BACKGROUND_TEXTURE, x - BACKGROUND_MARGIN, y - BACKGROUND_MARGIN, width + BACKGROUND_MARGIN * 2, height + BACKGROUND_MARGIN * 2); + public static void extractPopupBackground(GuiGraphicsExtractor graphics, int x, int y, int width, int height, boolean light) { + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, light ? LIGHTER_BACKGROUND_TEXTURE : BACKGROUND_TEXTURE, x - BACKGROUND_MARGIN, y - BACKGROUND_MARGIN, width + BACKGROUND_MARGIN * 2, height + BACKGROUND_MARGIN * 2); } @Override diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java index 3791367e833..a3f35203ee5 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ComponentEditWidget.java @@ -48,6 +48,7 @@ import org.jspecify.annotations.Nullable; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; @@ -371,11 +372,14 @@ private class ColorButton extends AbstractButton { private final ChatFormatting color; private final int intColor; + /** + * @param format Callers should ensure that this is a color. + */ private ColorButton(ChatFormatting format) { super(0, 0, 16, 16, ConfigUtils.FORMATTING_FORMATTER.apply(format)); setTooltip(Tooltip.create(getMessage())); this.color = format; - this.intColor = ARGB.opaque(SkyBlockColors.fromVanilla(TextColor.fromLegacyFormat(color).getValue())); + this.intColor = ARGB.opaque(SkyBlockColors.fromVanilla(Objects.requireNonNull(TextColor.fromLegacyFormat(color)).getValue())); } @Override From 6482bda9bd68c780abd53d535f966b80d8c502a6 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Sun, 2 Aug 2026 13:03:46 +0200 Subject: [PATCH 14/15] oopsies and a few comments --- .../suggestions/TextFieldSuggestions.java | 10 ++++++ .../utils/render/gui/SuggestionsEditBox.java | 31 +++++++++++++++++-- .../assets/skyblocker/lang/en_us.json | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java index 7d841cc0db9..9c2b140ee00 100644 --- a/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java +++ b/src/main/java/de/hysky/skyblocker/utils/command/suggestions/TextFieldSuggestions.java @@ -24,6 +24,10 @@ import java.util.List; import java.util.Optional; +/** + * A modified version of {@link CommandSuggestions} that doesn't have hardcoded positions + * and that allows to use a specific command node instead of the vanilla dispatcher. + */ public class TextFieldSuggestions extends CommandSuggestions { private final CommandDispatcher dispatcher; @@ -42,10 +46,16 @@ protected TextFieldSuggestions(Minecraft minecraft, Screen screen, EditBox input } } + /** + * Creates a text field suggestions with a specific node and children as the parser + */ public static TextFieldSuggestions ofSpecificNode(Minecraft minecraft, Screen screen, EditBox input, Font font, boolean onlyShowIfCursorPastError, int suggestionLineLimit, CommandNode node) { return new TextFieldSuggestions(minecraft, screen, input, font, onlyShowIfCursorPastError, suggestionLineLimit, node, true); } + /** + * Creates a text field suggestions that suggests like chat or a command block. Basically the same as {@link CommandSuggestions} but without hard coded positions and chat restrictions. + */ public static TextFieldSuggestions ofVanillaDispatcher(Minecraft minecraft, Screen screen, EditBox input, Font font, boolean onlyShowIfCursorPastError, int suggestionLineLimit, boolean commandOnly) { return new TextFieldSuggestions(minecraft, screen, input, font, onlyShowIfCursorPastError, suggestionLineLimit, null, commandOnly); } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java index 47312a32edf..9fd45ddd018 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java @@ -25,13 +25,17 @@ import java.util.Optional; import java.util.function.Consumer; +/// An edit box that can display suggestions using brigadier. +/// +/// It can be used to suggest an entire command (like in chat or a command block) +/// or to suggest something more specific like an item stack or an identifier using an argument public class SuggestionsEditBox extends EditBox { private final TextFieldSuggestions suggestions; private final @Nullable String argument; private @Nullable Consumer responder; - public SuggestionsEditBox(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, @Nullable CommandNode node, boolean commandsOnly) { + protected SuggestionsEditBox(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, @Nullable CommandNode node, boolean commandsOnly) { super(font, width, height, narration); if (node != null) { suggestions = TextFieldSuggestions.ofSpecificNode(minecraft, screen, this, font, onlyShowIfCursorPastError, suggestionLineLimit, node); @@ -155,30 +159,53 @@ public Builder onlyShowIfCursorPastError(boolean onlyShowIfCursorPastError) { return this; } + /** + * @see Builder#build(Minecraft, Font, Screen, Component, CommandNode) + */ public SuggestionsEditBox build(Screen screen, Component narration, CommandNode node) { return build(Minecraft.getInstance(), Minecraft.getInstance().font, screen, narration, node); } + /** + * @see Builder#build(Minecraft, Font, Screen, Component, CommandNode) + */ public SuggestionsEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, ArgumentBuilder builder) { return build(minecraft, font, screen, narration, builder.build()); } + /** + * Builds a suggestion edit box with the specified node.
+ * Note: if it is an argument node with no children it is recommended to use {@link Builder#buildArg(Minecraft, Font, Screen, Component, ArgumentType)} + * that has methods to get the parsed value directly. + */ public SuggestionsEditBox build(Minecraft minecraft, Font font, Screen screen, Component narration, CommandNode node) { return new SuggestionsEditBox(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, node, true); } + /** + * Builds a suggestion edit box with a specific argument type as the parser and suggestions provider.
+ * Has methods to get the parsed value directly thanks to the generic type. + */ public Argument buildArg(Minecraft minecraft, Font font, Screen screen, Component narration, ArgumentType argumentType) { return new Argument<>(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, argumentType); } + /** + * Builds a suggestion edit box that pretty much acts as if it was chat or a command block and suggests all commands. + */ public SuggestionsEditBox buildVanillaDispatcher(Minecraft minecraft, Font font, Screen screen, Component narration, boolean commandsOnly) { return new SuggestionsEditBox(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, null, commandsOnly); } } + /** + * A suggestion edit box based on an argument type. Has methods to get the parsed value directly. + * @see Builder#buildArg(Minecraft, Font, Screen, Component, ArgumentType) + * @param The edit box's parsed type + */ public static class Argument extends SuggestionsEditBox { - public Argument(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, ArgumentType argumentType) { + protected Argument(Minecraft minecraft, Screen screen, Font font, int width, int height, Component narration, boolean onlyShowIfCursorPastError, int suggestionLineLimit, ArgumentType argumentType) { super(minecraft, screen, font, width, height, narration, onlyShowIfCursorPastError, suggestionLineLimit, RequiredArgumentBuilder.argument("argument", argumentType).build(), true); } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 14e201dbf2a..3b8fd99df35 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -1089,7 +1089,7 @@ "skyblocker.config.quickNav.enableQuickNav": "Enable Quick Navigation", "skyblocker.config.quickNav.enableQuickNav.@Tooltip": "Adds 14 customizable inventory buttons that can be styled and run a command when clicked.", "skyblocker.config.quickNav.quickNavConfigScreen": "Open Configuration Screen", - "skyblocker.config.quickNav.screen.count": "Editing Button %s", + "skyblocker.config.quickNav.screen.count": "Count", "skyblocker.config.quickNav.screen.title": "Editing Button %s", "skyblocker.config.shortcutToKeybindsSettings": "Edit Keybind", From b3cea321d3a50e4c080f8b677dfa6e2b63c76b63 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Sun, 2 Aug 2026 13:09:50 +0200 Subject: [PATCH 15/15] . --- .../hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java index 9fd45ddd018..2e2803cc74a 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/SuggestionsEditBox.java @@ -200,8 +200,8 @@ public SuggestionsEditBox buildVanillaDispatcher(Minecraft minecraft, Font font, /** * A suggestion edit box based on an argument type. Has methods to get the parsed value directly. - * @see Builder#buildArg(Minecraft, Font, Screen, Component, ArgumentType) * @param The edit box's parsed type + * @see Builder#buildArg(Minecraft, Font, Screen, Component, ArgumentType) */ public static class Argument extends SuggestionsEditBox {