diff --git a/src/main/java/io/wispforest/gadget/client/gui/SidebarBuilder.java b/src/main/java/io/wispforest/gadget/client/gui/SidebarBuilder.java index 6385e36..21ae7ab 100644 --- a/src/main/java/io/wispforest/gadget/client/gui/SidebarBuilder.java +++ b/src/main/java/io/wispforest/gadget/client/gui/SidebarBuilder.java @@ -1,6 +1,7 @@ package io.wispforest.gadget.client.gui; import io.wispforest.owo.ui.component.Components; +import io.wispforest.owo.ui.component.LabelComponent; import io.wispforest.owo.ui.container.Containers; import io.wispforest.owo.ui.container.FlowLayout; import io.wispforest.owo.ui.core.*; @@ -43,10 +44,12 @@ public void button(Text icon, @Nullable Text tooltip, OnPressHandler handler) { } public static class Button extends FlowLayout { + private final LabelComponent iconLabel; + public Button(Text icon, Text tooltip) { super(Sizing.fixed(16), Sizing.fixed(16), Algorithm.VERTICAL); - child(Components.label(icon) + child((iconLabel = Components.label(icon)) .verticalTextAlignment(VerticalAlignment.CENTER) .horizontalTextAlignment(HorizontalAlignment.CENTER) .positioning(Positioning.absolute(5, 4)) @@ -63,6 +66,12 @@ public Button(Text icon, Text tooltip) { mouseLeave().subscribe( () -> surface(Surface.BLANK)); } + + public Button icon(Text icon) { + iconLabel.text(icon); + + return this; + } } public interface OnPressHandler { diff --git a/src/main/java/io/wispforest/gadget/client/nbt/StackComponentDataScreen.java b/src/main/java/io/wispforest/gadget/client/nbt/StackComponentDataScreen.java index f8b1916..1f94eb8 100644 --- a/src/main/java/io/wispforest/gadget/client/nbt/StackComponentDataScreen.java +++ b/src/main/java/io/wispforest/gadget/client/nbt/StackComponentDataScreen.java @@ -1,29 +1,85 @@ package io.wispforest.gadget.client.nbt; +import com.mojang.serialization.DataResult; +import io.wispforest.gadget.client.ServerData; +import io.wispforest.gadget.client.gui.SidebarBuilder; +import io.wispforest.gadget.network.GadgetNetworking; +import io.wispforest.gadget.network.packet.c2s.ReplaceStackC2SPacket; import io.wispforest.owo.ui.base.BaseOwoScreen; import io.wispforest.owo.ui.component.Components; import io.wispforest.owo.ui.container.Containers; import io.wispforest.owo.ui.container.FlowLayout; import io.wispforest.owo.ui.container.ScrollContainer; import io.wispforest.owo.ui.core.*; +import io.wispforest.owo.ui.util.UISounds; +import io.wispforest.owo.util.Observable; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.item.ItemStack; +import net.minecraft.client.gui.tooltip.TooltipComponent; +import net.minecraft.component.ComponentChanges; +import net.minecraft.component.ComponentMapImpl; import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtOps; -import net.minecraft.registry.Registries; import net.minecraft.screen.slot.Slot; import net.minecraft.text.Text; -import net.minecraft.util.Formatting; +import org.apache.commons.lang3.mutable.MutableInt; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.glfw.GLFW; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; public class StackComponentDataScreen extends BaseOwoScreen { - private final ItemStack stack; + private final NbtDataIsland island; private final HandledScreen parent; + private final Observable<@Nullable String> currentEncodingError = Observable.of(null); public StackComponentDataScreen(HandledScreen parent, Slot slot) { - this.stack = slot.getStack(); + var stack = slot.getStack(); + Consumer reloader = null; + + var registries = MinecraftClient.getInstance().world.getRegistryManager(); + + if (ServerData.canReplaceStacks()) { + reloader = newNbt -> { + DataResult result = ComponentChanges.CODEC.parse( + registries.getOps(NbtOps.INSTANCE), + newNbt + ); + + result + .ifError(error -> { + currentEncodingError.set(error.message()); + }) + .ifSuccess(newChanges -> { + currentEncodingError.set(null); + + ((ComponentMapImpl) stack.getComponents()).setChanges(newChanges); + stack.getItem().postProcessComponents(stack); + + if (parent instanceof CreativeInventoryScreen) { + // Let it handle it. + return; + } + + GadgetNetworking.CHANNEL.clientHandle().send(new ReplaceStackC2SPacket(slot.id, stack)); + }); + }; + } + + NbtCompound tag = (NbtCompound) ComponentChanges.CODEC.encodeStart( + registries.getOps(NbtOps.INSTANCE), + stack.getComponentChanges() + ) + .getOrThrow(); + + if (tag == null) tag = new NbtCompound(); + this.parent = parent; + this.island = new NbtDataIsland(tag, reloader); } @Override @@ -49,33 +105,67 @@ protected void build(FlowLayout rootComponent) { main .padding(Insets.of(15)); - for (var component : stack.getComponents()) { - FlowLayout full = Containers.verticalFlow(Sizing.content(), Sizing.content()); - FlowLayout row = Containers.horizontalFlow(Sizing.content(), Sizing.content()); + main.child(island); + + FlowLayout sidebar = Containers.verticalFlow(Sizing.content(), Sizing.content()); + + if (island.reloader != null) { + var addButton = Containers.verticalFlow(Sizing.fixed(16), Sizing.fixed(16)) + .child(Components.label(Text.literal("+")) + .verticalTextAlignment(VerticalAlignment.CENTER) + .horizontalTextAlignment(HorizontalAlignment.CENTER) + .positioning(Positioning.absolute(5, 4)) + .cursorStyle(CursorStyle.HAND) + ); + + addButton + .cursorStyle(CursorStyle.HAND); + + addButton.mouseEnter().subscribe( + () -> addButton.surface(Surface.flat(0x80ffffff))); - full.child(row); - main.child(full); + addButton.mouseLeave().subscribe( + () -> addButton.surface(Surface.BLANK)); - row.child(Components.label(Text.literal(Registries.DATA_COMPONENT_TYPE.getId(component.type()).toString()) - .append(Text.literal(" = ") - .formatted(Formatting.GRAY)))); + addButton.mouseDown().subscribe((mouseX, mouseY, button) -> { + if (button != GLFW.GLFW_MOUSE_BUTTON_LEFT) return false; - NbtElement tag = component.encode(client.world.getRegistryManager().getOps(NbtOps.INSTANCE)).getOrThrow(); + UISounds.playInteractionSound(); - NbtCompound compound; - if (tag instanceof NbtCompound c) { - compound = c; + island.typeSelector( + (int) (addButton.x() + mouseX), + (int) (addButton.y() + mouseY), + type -> island.child(new KeyAdderWidget(island, NbtPath.EMPTY, type, unused -> true))); + + return true; + }); + + sidebar.child(addButton); + } + + var infoButton = new SidebarBuilder.Button(Text.translatable("text.gadget.encode_status.success"), Text.translatable("text.gadget.encode_status.success.tooltip")); + + currentEncodingError.observe(error -> { + if (error == null) { + infoButton.icon(Text.translatable("text.gadget.encode_status.success")); + infoButton.tooltip(Text.translatable("text.gadget.encode_status.success.tooltip")); } else { - compound = new NbtCompound(); - compound.put("", tag); + infoButton.icon(Text.translatable("text.gadget.encode_status.failure")); + infoButton.tooltip(Text.translatable("text.gadget.encode_status.failure.tooltip", error)); } + }); - full.child(new NbtDataIsland(compound, null)); - } + sidebar.child(infoButton); + + sidebar + .positioning(Positioning.absolute(0, 0)) + .padding(Insets.of(5)); + + rootComponent.child(sidebar); } @Override public void close() { client.setScreen(parent); } -} +} \ No newline at end of file diff --git a/src/main/resources/assets/gadget/lang/en_us.json b/src/main/resources/assets/gadget/lang/en_us.json index fb706c9..ef944ab 100644 --- a/src/main/resources/assets/gadget/lang/en_us.json +++ b/src/main/resources/assets/gadget/lang/en_us.json @@ -107,6 +107,23 @@ "font": "gadget:monocraft" }, "text.gadget.export_button.tooltip": "Export", + "text.gadget.encode_status.success": { + "text": "✔", + "color": "green" + }, + "text.gadget.encode_status.success.tooltip": { + "text": "Component NBT is fully valid", + "color": "green" + }, + "text.gadget.encode_status.failure": { + "text": "❌", + "color": "dark_red" + }, + "text.gadget.encode_status.failure.tooltip": [ + "", + {"text": "Component NBT is invalid and has not been applied.\n\n", "color": "dark_red"}, + {"index": 0} + ], "text.gadget.back": "←", "text.gadget.back.tooltip": "Back", "text.gadget.open": {