diff options
6 files changed, 271 insertions, 16 deletions
diff --git a/GuiTest/src/main/java/io/github/cottonmc/test/GuiItem.java b/GuiTest/src/main/java/io/github/cottonmc/test/GuiItem.java index 4fbc6df..27c8d5f 100644 --- a/GuiTest/src/main/java/io/github/cottonmc/test/GuiItem.java +++ b/GuiTest/src/main/java/io/github/cottonmc/test/GuiItem.java @@ -1,20 +1,23 @@ package io.github.cottonmc.test; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; +import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.StackReference; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.screen.NamedScreenHandlerFactory; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.Rarity; import net.minecraft.util.TypedActionResult; import net.minecraft.world.World; -import io.github.cottonmc.cotton.gui.client.CottonClientScreen; -import io.github.cottonmc.test.client.TestClientGui; - public class GuiItem extends Item { public GuiItem() { super(new Item.Settings().rarity(Rarity.EPIC)); @@ -22,15 +25,31 @@ public class GuiItem extends Item { @Override public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand hand) { - if (world.isClient) { - openScreen(); // In its own method to prevent class loading issues - } - + player.openHandledScreen(createScreenHandlerFactory(player, hand)); return new TypedActionResult<>(ActionResult.SUCCESS, player.getStackInHand(hand)); } - @Environment(EnvType.CLIENT) - private void openScreen() { - MinecraftClient.getInstance().setScreen(new CottonClientScreen(new TestClientGui())); + private NamedScreenHandlerFactory createScreenHandlerFactory(PlayerEntity player, Hand hand) { + EquipmentSlot slot = switch (hand) { + case MAIN_HAND -> EquipmentSlot.MAINHAND; + case OFF_HAND -> EquipmentSlot.OFFHAND; + }; + ItemStack stack = player.getStackInHand(hand); + return new ExtendedScreenHandlerFactory() { + @Override + public Text getDisplayName() { + return stack.getName(); + } + + @Override + public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) { + return new TestItemDescription(syncId, playerInventory, StackReference.of(player, slot)); + } + + @Override + public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf) { + buf.writeEnumConstant(slot); + } + }; } } diff --git a/GuiTest/src/main/java/io/github/cottonmc/test/LibGuiTest.java b/GuiTest/src/main/java/io/github/cottonmc/test/LibGuiTest.java index 3c11004..1d48daa 100644 --- a/GuiTest/src/main/java/io/github/cottonmc/test/LibGuiTest.java +++ b/GuiTest/src/main/java/io/github/cottonmc/test/LibGuiTest.java @@ -2,13 +2,16 @@ package io.github.cottonmc.test; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; +import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.minecraft.block.AbstractBlock; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.StackReference; import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.registry.Registries; @@ -32,6 +35,7 @@ public class LibGuiTest implements ModInitializer { public static BlockItem GUI_BLOCK_ITEM; public static BlockEntityType<GuiBlockEntity> GUI_BLOCKENTITY_TYPE; public static ScreenHandlerType<TestDescription> GUI_SCREEN_HANDLER_TYPE; + public static ScreenHandlerType<TestItemDescription> ITEM_SCREEN_HANDLER_TYPE; public static ScreenHandlerType<ReallySimpleDescription> REALLY_SIMPLE_SCREEN_HANDLER_TYPE; @Override @@ -52,6 +56,12 @@ public class LibGuiTest implements ModInitializer { return new TestDescription(GUI_SCREEN_HANDLER_TYPE, syncId, inventory, ScreenHandlerContext.EMPTY); }, FeatureSet.of(FeatureFlags.VANILLA)); Registry.register(Registries.SCREEN_HANDLER, new Identifier(MODID, "gui"), GUI_SCREEN_HANDLER_TYPE); + ITEM_SCREEN_HANDLER_TYPE = new ExtendedScreenHandlerType<>((syncId, inventory, buf) -> { + var equipmentSlot = buf.readEnumConstant(EquipmentSlot.class); + StackReference handStack = StackReference.of(inventory.player, equipmentSlot); + return new TestItemDescription(syncId, inventory, handStack); + }); + Registry.register(Registries.SCREEN_HANDLER, new Identifier(MODID, "item_gui"), ITEM_SCREEN_HANDLER_TYPE); REALLY_SIMPLE_SCREEN_HANDLER_TYPE = new ScreenHandlerType<>(ReallySimpleDescription::new, FeatureSet.of(FeatureFlags.VANILLA)); Registry.register(Registries.SCREEN_HANDLER, new Identifier(MODID, "really_simple"), REALLY_SIMPLE_SCREEN_HANDLER_TYPE); diff --git a/GuiTest/src/main/java/io/github/cottonmc/test/TestItemDescription.java b/GuiTest/src/main/java/io/github/cottonmc/test/TestItemDescription.java new file mode 100644 index 0000000..4c9c1f0 --- /dev/null +++ b/GuiTest/src/main/java/io/github/cottonmc/test/TestItemDescription.java @@ -0,0 +1,156 @@ +package io.github.cottonmc.test; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.StackReference; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +import io.github.cottonmc.cotton.gui.ItemSyncedGuiDescription; +import io.github.cottonmc.cotton.gui.client.ScreenDrawing; +import io.github.cottonmc.cotton.gui.widget.TooltipBuilder; +import io.github.cottonmc.cotton.gui.widget.WGridPanel; +import io.github.cottonmc.cotton.gui.widget.WLabel; +import io.github.cottonmc.cotton.gui.widget.WSlider; +import io.github.cottonmc.cotton.gui.widget.WTextField; +import io.github.cottonmc.cotton.gui.widget.WTiledSprite; +import io.github.cottonmc.cotton.gui.widget.WWidget; +import io.github.cottonmc.cotton.gui.widget.data.Axis; +import io.github.cottonmc.cotton.gui.widget.data.Color; +import io.github.cottonmc.cotton.gui.widget.data.Insets; + +public class TestItemDescription extends ItemSyncedGuiDescription { + private int r = 0; + private int g = 0; + private int b = 0; + + public TestItemDescription(int syncId, PlayerInventory playerInventory, StackReference owner) { + super(LibGuiTest.ITEM_SCREEN_HANDLER_TYPE, syncId, playerInventory, owner); + WGridPanel root = new WGridPanel(22); + root.setInsets(Insets.ROOT_PANEL); + this.setRootPanel(root); + WLabel title = new WLabel(Text.literal("Client Test Gui"), WLabel.DEFAULT_TEXT_COLOR) { + @Environment(EnvType.CLIENT) + @Override + public void addTooltip(TooltipBuilder tooltip) { + tooltip.add(Text.literal("Radical!")); + } + }; + WTiledSprite wood = new WTiledSprite( + 8, 8, // tile width and height + 500, // animation speed + new Identifier("minecraft:textures/block/birch_planks.png"), + new Identifier("minecraft:textures/block/dark_oak_planks.png"), + new Identifier("minecraft:textures/block/jungle_planks.png") + ); + root.add(wood, 3, 3, 2, 2); + root.add(title, 0, 0); + + WTextField text = new WTextField(); + text.setSuggestion(Text.literal("Search")); + root.add(text, 0, 1, 8, 1); + text.setSize(7*18, 20); + /* + ArrayList<String> data = new ArrayList<>(); + data.add("Wolfram Alpha"); + data.add("Strange Home"); + data.add("Nether Base"); + data.add("Death"); + data.add("Cake"); + data.add("Mushroom Island"); + data.add("A List Item"); + data.add("Notes"); + data.add("Slime Island"); + + BiConsumer<String, PortalDestination> configurator = (String s, PortalDestination destination) -> { + destination.label.setText(new LiteralText(s)); + + int hash = s.hashCode(); + Identifier sprite = ((hash & 0x01) == 0) ? PORTAL1 : PORTAL2; + destination.sprite.setImage(sprite); + + int cost = (hash >> 1) & 0x2FF; + destination.cost.setText(new LiteralText(""+cost+" XP")); + }; + WListPanel<String, PortalDestination> list = new WListPanel<String, PortalDestination>(data, PortalDestination.class, PortalDestination::new, configurator); + list.setListItemHeight(2*18); + list.setBackgroundPainter(PANEL); + root.add(list, 0, 2, 7, 6); + + root.add(new WButton(new LiteralText("Teleport")), 3,8,4,1);*/ + WColorBox col = new WColorBox(); + root.add(col, 3,2,1,3); + + WSlider r = new WSlider(0, 100, Axis.VERTICAL); + root.add(r, 0, 2, 1, 3); + r.setValueChangeListener((i)->{ + this.r = i; + updateCol(col); + System.out.println("h: "+this.r+" s: "+this.g+ " l: "+this.b); + System.out.println("col is now "+Integer.toHexString(col.color)); + }); + WSlider g = new WSlider(0, 100, Axis.VERTICAL); + root.add(g, 1, 2, 1, 3); + g.setValueChangeListener((i)->{ + this.g = i; + updateCol(col); + }); + WSlider b = new WSlider(0, 100, Axis.VERTICAL); + root.add(b, 2, 2, 1, 3); + b.setValueChangeListener((i)->{ + this.b = i; + updateCol(col); + }); + + root.validate(this); + } + /* + public static class PortalDestination extends WPlainPanel { + WSprite sprite; + WLabel label; + WLabel cost; + + public PortalDestination() { + sprite = new WSprite(new Identifier("libgui-test:portal")); + this.add(sprite, 2, 2, 18, 18); + label = new WLabel("Foo"); + this.add(label, 18+ 4, 2, 5*18, 18); + cost = new WLabel("1000 Xp"); + this.add(cost, 2, 20, 6*18, 18); + + this.setSize(7*18, 2*18); + + this.setBackgroundPainter(PANEL); //Would fail on a serverside gui + } + }*/ + + private void updateCol(WColorBox col) { + Color.HSL hsl = new Color.HSL(r/100f, g/100f, b/100f); + col.setColor(hsl.toRgb()); + } + + public static class WColorBox extends WWidget { + protected int color = 0xFF_FFFFFF; + public WColorBox() {} + + public WColorBox(int col) { + this.color = col; + } + + @Override + public boolean canResize() { + return true; + } + + public void setColor(int col) { + this.color = col; + } + + @Override + public void paint(MatrixStack matrices, int x, int y, int mouseX, int mouseY) { + ScreenDrawing.coloredRect(matrices, x, y, this.getWidth(), this.getHeight(), color); + } + } +} diff --git a/GuiTest/src/main/java/io/github/cottonmc/test/client/LibGuiTestClient.java b/GuiTest/src/main/java/io/github/cottonmc/test/client/LibGuiTestClient.java index 794264c..32e8343 100644 --- a/GuiTest/src/main/java/io/github/cottonmc/test/client/LibGuiTestClient.java +++ b/GuiTest/src/main/java/io/github/cottonmc/test/client/LibGuiTestClient.java @@ -18,6 +18,7 @@ import io.github.cottonmc.cotton.gui.widget.WLabel; import io.github.cottonmc.test.LibGuiTest; import io.github.cottonmc.test.ReallySimpleDescription; import io.github.cottonmc.test.TestDescription; +import io.github.cottonmc.test.TestItemDescription; import java.util.function.Function; @@ -38,6 +39,11 @@ public class LibGuiTestClient implements ClientModInitializer { CottonInventoryScreen::new ); + HandledScreens.<TestItemDescription, CottonInventoryScreen<TestItemDescription>>register( + LibGuiTest.ITEM_SCREEN_HANDLER_TYPE, + CottonInventoryScreen::new + ); + CottonHud.add(new WHudTest(), 10, -20, 10, 10); CottonHud.add(new WLabel(Text.literal("Test label")), 10, -30, 10, 10); diff --git a/GuiTest/src/main/java/io/github/cottonmc/test/client/PaddingTestGui.java b/GuiTest/src/main/java/io/github/cottonmc/test/client/PaddingTestGui.java index 367cab0..9f5d790 100644 --- a/GuiTest/src/main/java/io/github/cottonmc/test/client/PaddingTestGui.java +++ b/GuiTest/src/main/java/io/github/cottonmc/test/client/PaddingTestGui.java @@ -1,13 +1,14 @@ package io.github.cottonmc.test.client; +import net.minecraft.text.Text; + import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription; import io.github.cottonmc.cotton.gui.widget.WGridPanel; import io.github.cottonmc.cotton.gui.widget.WLabel; import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment; import io.github.cottonmc.cotton.gui.widget.data.Insets; import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment; - -import net.minecraft.text.Text; +import io.github.cottonmc.test.TestItemDescription; public class PaddingTestGui extends LightweightGuiDescription { public PaddingTestGui(int hori, int vert) { @@ -25,7 +26,7 @@ public class PaddingTestGui extends LightweightGuiDescription { } void addBox(WGridPanel root, int x, int y, int w, int h) { - root.add(new TestClientGui.WColorBox(0xffff0000), x, y, w, h); + root.add(new TestItemDescription.WColorBox(0xffff0000), x, y, w, h); var l = new WLabel(Text.literal(w + "x" + h), 0xff00ffff); l.setVerticalAlignment(VerticalAlignment.CENTER); l.setHorizontalAlignment(HorizontalAlignment.CENTER); diff --git a/src/main/java/io/github/cottonmc/cotton/gui/ItemSyncedGuiDescription.java b/src/main/java/io/github/cottonmc/cotton/gui/ItemSyncedGuiDescription.java new file mode 100644 index 0000000..bdf3479 --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/ItemSyncedGuiDescription.java @@ -0,0 +1,63 @@ +package io.github.cottonmc.cotton.gui; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.StackReference; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.ScreenHandlerType; + +import java.util.Objects; + +/** + * A {@link SyncedGuiDescription} for an {@linkplain ItemStack item stack} + * in an {@linkplain net.minecraft.inventory.Inventory inventory}. + * + * <p>The owning item is represented with a {@link StackReference}, which can be + * an item in an entity's inventory or a block's container, or any other reference + * to an item stack. + * + * <p>If the owning item stack changes in any way, the screen closes by default (see {@link #canUse(PlayerEntity)}). + * + * @since 7.0.0 + */ +public class ItemSyncedGuiDescription extends SyncedGuiDescription { + /** + * A reference to the owning item stack of this GUI. + */ + protected final StackReference owner; + + /** + * The initial item stack of this GUI. This stack must <strong>not</strong> be mutated! + */ + protected final ItemStack ownerStack; + + /** + * Constructs an {@code ItemSyncedGuiDescription}. + * + * @param type the screen handler type + * @param syncId the sync ID + * @param playerInventory the inventory of the player viewing this GUI description + * @param owner a reference to the owning item stack of this GUI description + */ + public ItemSyncedGuiDescription(ScreenHandlerType<?> type, int syncId, PlayerInventory playerInventory, StackReference owner) { + super(type, syncId, playerInventory); + this.owner = Objects.requireNonNull(owner, "Owner cannot be null"); + this.ownerStack = owner.get().copy(); + } + + /** + * {@inheritDoc} + * + * <p>The default implementation for {@code ItemSyncedGuiDescription} returns {@code true} if and only if + * the {@linkplain #owner current owning item stack} is {@linkplain ItemStack#areEqual fully equal} + * to the {@linkplain #ownerStack original owner}. + * + * <p>If the item NBT is intended to change, subclasses should override this method to only check + * the item and the count. Those subclasses should also take care to respond properly + * to any NBT changes in the owning item stack. + */ + @Override + public boolean canUse(PlayerEntity entity) { + return ItemStack.areEqual(ownerStack, owner.get()); + } +} |