From 480d9599ab9284d65a6a0db4de0e312ee6bd86c7 Mon Sep 17 00:00:00 2001 From: Chai <7232280+Chailotl@users.noreply.github.com> Date: Thu, 29 Aug 2024 18:35:53 -0400 Subject: [PATCH 1/4] Added the popup block! --- .../dev/hephaestus/glowcase/Glowcase.java | 7 + .../glowcase/GlowcaseCommonProxy.java | 8 + .../glowcase/GlowcaseNetworking.java | 3 + .../hephaestus/glowcase/block/PopupBlock.java | 72 +++++ .../block/entity/PopupBlockEntity.java | 134 ++++++++ .../glowcase/client/GlowcaseClient.java | 2 + .../glowcase/client/GlowcaseClientProxy.java | 21 +- .../screen/ingame/PopupBlockEditScreen.java | 306 ++++++++++++++++++ .../screen/ingame/PopupBlockViewScreen.java | 52 +++ .../entity/PopupBlockEntityRenderer.java | 38 +++ .../glowcase/packet/C2SEditPopupBlock.java | 49 +++ .../glowcase/blockstates/popup_block.json | 7 + .../resources/assets/glowcase/lang/en_us.json | 1 + .../glowcase/models/block/popup_block.json | 5 + .../glowcase/models/item/popup_block.json | 49 +++ .../glowcase/textures/item/popup_block.png | Bin 0 -> 5954 bytes .../data/glowcase/tags/item/items.json | 3 +- 17 files changed, 753 insertions(+), 4 deletions(-) create mode 100644 src/main/java/dev/hephaestus/glowcase/block/PopupBlock.java create mode 100644 src/main/java/dev/hephaestus/glowcase/block/entity/PopupBlockEntity.java create mode 100644 src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java create mode 100644 src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockViewScreen.java create mode 100644 src/main/java/dev/hephaestus/glowcase/client/render/block/entity/PopupBlockEntityRenderer.java create mode 100644 src/main/java/dev/hephaestus/glowcase/packet/C2SEditPopupBlock.java create mode 100644 src/main/resources/assets/glowcase/blockstates/popup_block.json create mode 100644 src/main/resources/assets/glowcase/models/block/popup_block.json create mode 100644 src/main/resources/assets/glowcase/models/item/popup_block.json create mode 100644 src/main/resources/assets/glowcase/textures/item/popup_block.png diff --git a/src/main/java/dev/hephaestus/glowcase/Glowcase.java b/src/main/java/dev/hephaestus/glowcase/Glowcase.java index 84476b4..286d3c2 100644 --- a/src/main/java/dev/hephaestus/glowcase/Glowcase.java +++ b/src/main/java/dev/hephaestus/glowcase/Glowcase.java @@ -4,9 +4,11 @@ import com.google.common.base.Suppliers; import dev.hephaestus.glowcase.block.HyperlinkBlock; import dev.hephaestus.glowcase.block.ItemDisplayBlock; +import dev.hephaestus.glowcase.block.PopupBlock; import dev.hephaestus.glowcase.block.TextBlock; import dev.hephaestus.glowcase.block.entity.HyperlinkBlockEntity; import dev.hephaestus.glowcase.block.entity.ItemDisplayBlockEntity; +import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; import dev.hephaestus.glowcase.block.entity.TextBlockEntity; import dev.hephaestus.glowcase.compat.PolydexCompatibility; import net.fabricmc.api.ModInitializer; @@ -46,6 +48,10 @@ public class Glowcase implements ModInitializer { public static final Supplier TEXT_BLOCK_ITEM = registerItem("text_block", () -> new BlockItem(TEXT_BLOCK.get(), new Item.Settings())); public static final Supplier> TEXT_BLOCK_ENTITY = registerBlockEntity("text_block", () -> BlockEntityType.Builder.create(TextBlockEntity::new, TEXT_BLOCK.get()).build(null)); + public static final Supplier POPUP_BLOCK = registerBlock("popup_block", PopupBlock::new); + public static final Supplier POPUP_BLOCK_ITEM = registerItem("popup_block", () -> new BlockItem(POPUP_BLOCK.get(), new Item.Settings())); + public static final Supplier> POPUP_BLOCK_ENTITY = registerBlockEntity("popup_block", () -> BlockEntityType.Builder.create(PopupBlockEntity::new, POPUP_BLOCK.get()).build(null)); + public static final Supplier ITEM_GROUP = registerItemGroup("items", () -> FabricItemGroup.builder() .displayName(Text.translatable("itemGroup.glowcase.items")) .icon(() -> new ItemStack(Items.GLOWSTONE)) @@ -53,6 +59,7 @@ public class Glowcase implements ModInitializer { entries.add(HYPERLINK_BLOCK_ITEM.get()); entries.add(ITEM_DISPLAY_BLOCK_ITEM.get()); entries.add(TEXT_BLOCK_ITEM.get()); + entries.add(POPUP_BLOCK_ITEM.get()); }) .build() ); diff --git a/src/main/java/dev/hephaestus/glowcase/GlowcaseCommonProxy.java b/src/main/java/dev/hephaestus/glowcase/GlowcaseCommonProxy.java index 65ee6c0..bc436a6 100644 --- a/src/main/java/dev/hephaestus/glowcase/GlowcaseCommonProxy.java +++ b/src/main/java/dev/hephaestus/glowcase/GlowcaseCommonProxy.java @@ -18,4 +18,12 @@ public void openItemDisplayBlockEditScreen(BlockPos pos) { public void openTextBlockEditScreen(BlockPos pos) { //No-op } + + public void openPopupBlockEditScreen(BlockPos pos) { + //No-op + } + + public void openPopupBlockViewScreen(BlockPos pos) { + //No-op + } } diff --git a/src/main/java/dev/hephaestus/glowcase/GlowcaseNetworking.java b/src/main/java/dev/hephaestus/glowcase/GlowcaseNetworking.java index 8b932a4..6c7eab2 100644 --- a/src/main/java/dev/hephaestus/glowcase/GlowcaseNetworking.java +++ b/src/main/java/dev/hephaestus/glowcase/GlowcaseNetworking.java @@ -2,6 +2,7 @@ import dev.hephaestus.glowcase.packet.C2SEditHyperlinkBlock; import dev.hephaestus.glowcase.packet.C2SEditItemDisplayBlock; +import dev.hephaestus.glowcase.packet.C2SEditPopupBlock; import dev.hephaestus.glowcase.packet.C2SEditTextBlock; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; @@ -11,9 +12,11 @@ public static void init() { PayloadTypeRegistry.playC2S().register(C2SEditHyperlinkBlock.ID, C2SEditHyperlinkBlock.PACKET_CODEC); PayloadTypeRegistry.playC2S().register(C2SEditItemDisplayBlock.ID, C2SEditItemDisplayBlock.PACKET_CODEC); PayloadTypeRegistry.playC2S().register(C2SEditTextBlock.ID, C2SEditTextBlock.PACKET_CODEC); + PayloadTypeRegistry.playC2S().register(C2SEditPopupBlock.ID, C2SEditPopupBlock.PACKET_CODEC); ServerPlayNetworking.registerGlobalReceiver(C2SEditHyperlinkBlock.ID, C2SEditHyperlinkBlock::receive); ServerPlayNetworking.registerGlobalReceiver(C2SEditItemDisplayBlock.ID, C2SEditItemDisplayBlock::receive); ServerPlayNetworking.registerGlobalReceiver(C2SEditTextBlock.ID, C2SEditTextBlock::receive); + ServerPlayNetworking.registerGlobalReceiver(C2SEditPopupBlock.ID, C2SEditPopupBlock::receive); } } diff --git a/src/main/java/dev/hephaestus/glowcase/block/PopupBlock.java b/src/main/java/dev/hephaestus/glowcase/block/PopupBlock.java new file mode 100644 index 0000000..3a9812d --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/block/PopupBlock.java @@ -0,0 +1,72 @@ +package dev.hephaestus.glowcase.block; + +import dev.hephaestus.glowcase.Glowcase; +import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; +import net.minecraft.block.BlockEntityProvider; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.NbtComponent; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.ItemActionResult; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +public class PopupBlock extends GlowcaseBlock implements BlockEntityProvider { + private static final VoxelShape OUTLINE = VoxelShapes.cuboid(0.25, 0.25, 0.25, 0.75, 0.75, 0.75); + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return OUTLINE; + } + + @Nullable + @Override + public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return new PopupBlockEntity(pos, state); + } + + @Override + public void onPlaced(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + if (world.isClient && placer instanceof PlayerEntity player && canEditGlowcase(player, pos)) { + //load any ctrl-picked NBT clientside + NbtComponent blockEntityTag = stack.get(DataComponentTypes.BLOCK_ENTITY_DATA); + if (blockEntityTag != null && world.getBlockEntity(pos) instanceof PopupBlockEntity be) { + blockEntityTag.applyToBlockEntity(be, world.getRegistryManager()); + } + + Glowcase.proxy.openPopupBlockEditScreen(pos); + } + } + + @Override + protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) { + if (!(world.getBlockEntity(pos) instanceof PopupBlockEntity be)) return ActionResult.CONSUME; + if (world.isClient) { + Glowcase.proxy.openPopupBlockViewScreen(pos); + } + return ActionResult.SUCCESS; + } + + @Override + protected ItemActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + if (!(world.getBlockEntity(pos) instanceof PopupBlockEntity)) return ItemActionResult.CONSUME; + if (player.getStackInHand(hand).isIn(Glowcase.ITEM_TAG) && canEditGlowcase(player, pos)) { + if (world.isClient) { + Glowcase.proxy.openPopupBlockEditScreen(pos); + } + return ItemActionResult.SUCCESS; + } + return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/block/entity/PopupBlockEntity.java b/src/main/java/dev/hephaestus/glowcase/block/entity/PopupBlockEntity.java new file mode 100644 index 0000000..33ca336 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/block/entity/PopupBlockEntity.java @@ -0,0 +1,134 @@ +package dev.hephaestus.glowcase.block.entity; + +import dev.hephaestus.glowcase.Glowcase; +import dev.hephaestus.glowcase.client.render.block.entity.BakedBlockEntityRenderer; +import eu.pb4.placeholders.api.ParserContext; +import eu.pb4.placeholders.api.parsers.NodeParser; +import eu.pb4.placeholders.api.parsers.TagParser; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtString; +import net.minecraft.network.listener.ClientPlayPacketListener; +import net.minecraft.network.packet.Packet; +import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class PopupBlockEntity extends BlockEntity { + public static final NodeParser PARSER = TagParser.DEFAULT; + public List lines = new ArrayList<>(); + public TextBlockEntity.TextAlignment textAlignment = TextBlockEntity.TextAlignment.CENTER; + public int color = 0xFFFFFF; + public boolean renderDirty = true; + + public PopupBlockEntity(BlockPos pos, BlockState state) { + super(Glowcase.POPUP_BLOCK_ENTITY.get(), pos, state); + lines.add(Text.empty()); + } + + @Override + protected void writeNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) { + super.writeNbt(tag, registryLookup); + + tag.putInt("color", this.color); + + tag.putString("text_alignment", this.textAlignment.name()); + + NbtList lines = tag.getList("lines", 8); + for (var text : this.lines) { + lines.add(NbtString.of(Text.Serialization.toJsonString(text, registryLookup))); + } + + tag.put("lines", lines); + } + + @Override + protected void readNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) { + super.readNbt(tag, registryLookup); + + this.lines = new ArrayList<>(); + this.color = tag.getInt("color"); + + this.textAlignment = TextBlockEntity.TextAlignment.valueOf(tag.getString("text_alignment")); + + NbtList lines = tag.getList("lines", 8); + + for (NbtElement line : lines) { + if (line.getType() == NbtElement.END_TYPE) break; + this.lines.add(Text.Serialization.fromJson(line.asString(), registryLookup)); + } + + this.renderDirty = true; + } + + public String getRawLine(int i) { + var line = this.lines.get(i); + + if (line.getStyle() == null) { + return line.getString(); + } + + var insert = line.getStyle().getInsertion(); + + if (insert == null) { + return line.getString(); + } + return insert; + } + + public void addRawLine(int i, String string) { + var parsed = PARSER.parseText(string, ParserContext.of()); + + if (parsed.getString().equals(string)) { + this.lines.add(i, Text.literal(string)); + } else { + this.lines.add(i, Text.empty().append(parsed).setStyle(Style.EMPTY.withInsertion(string))); + } + } + + public void setRawLine(int i, String string) { + var parsed = PARSER.parseText(string, ParserContext.of()); + + if (parsed.getString().equals(string)) { + this.lines.set(i, Text.literal(string)); + } else { + this.lines.set(i, Text.empty().append(parsed).setStyle(Style.EMPTY.withInsertion(string))); + } + } + + @SuppressWarnings({"MethodCallSideOnly", "VariableUseSideOnly"}) + @Override + public void markRemoved() { + if (world != null && world.isClient) { + BakedBlockEntityRenderer.Manager.markForRebuild(getPos()); + } + super.markRemoved(); + } + + // standard blockentity boilerplate + + public void dispatch() { + if (world instanceof ServerWorld sworld) sworld.getChunkManager().markForUpdate(pos); + } + + @Override + public NbtCompound toInitialChunkDataNbt(RegistryWrapper.WrapperLookup registryLookup) { + return createNbt(registryLookup); + } + + @Nullable + @Override + public Packet toUpdatePacket() { + return BlockEntityUpdateS2CPacket.create(this); + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java index 02b4a8e..9332af1 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java +++ b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java @@ -5,6 +5,7 @@ import dev.hephaestus.glowcase.client.render.block.entity.HyperlinkBlockEntityRenderer; import dev.hephaestus.glowcase.client.render.block.entity.ItemDisplayBlockEntityRenderer; import dev.hephaestus.glowcase.client.render.block.entity.TextBlockEntityRenderer; +import dev.hephaestus.glowcase.client.render.block.entity.PopupBlockEntityRenderer; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.rendering.v1.InvalidateRenderStateCallback; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; @@ -18,6 +19,7 @@ public void onInitializeClient() { BlockEntityRendererFactories.register(Glowcase.TEXT_BLOCK_ENTITY.get(), TextBlockEntityRenderer::new); BlockEntityRendererFactories.register(Glowcase.HYPERLINK_BLOCK_ENTITY.get(), HyperlinkBlockEntityRenderer::new); BlockEntityRendererFactories.register(Glowcase.ITEM_DISPLAY_BLOCK_ENTITY.get(), ItemDisplayBlockEntityRenderer::new); + BlockEntityRendererFactories.register(Glowcase.POPUP_BLOCK_ENTITY.get(), PopupBlockEntityRenderer::new); WorldRenderEvents.AFTER_TRANSLUCENT.register(BakedBlockEntityRenderer.Manager::render); InvalidateRenderStateCallback.EVENT.register(BakedBlockEntityRenderer.Manager::reset); diff --git a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClientProxy.java b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClientProxy.java index 2cf548d..1157df2 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClientProxy.java +++ b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClientProxy.java @@ -3,10 +3,9 @@ import dev.hephaestus.glowcase.GlowcaseCommonProxy; import dev.hephaestus.glowcase.block.entity.HyperlinkBlockEntity; import dev.hephaestus.glowcase.block.entity.ItemDisplayBlockEntity; +import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; import dev.hephaestus.glowcase.block.entity.TextBlockEntity; -import dev.hephaestus.glowcase.client.gui.screen.ingame.HyperlinkBlockEditScreen; -import dev.hephaestus.glowcase.client.gui.screen.ingame.ItemDisplayBlockEditScreen; -import dev.hephaestus.glowcase.client.gui.screen.ingame.TextBlockEditScreen; +import dev.hephaestus.glowcase.client.gui.screen.ingame.*; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.ConfirmLinkScreen; import net.minecraft.util.math.BlockPos; @@ -40,4 +39,20 @@ public void openTextBlockEditScreen(BlockPos pos) { MinecraftClient.getInstance().setScreen(new TextBlockEditScreen(be)); } } + + @Override + public void openPopupBlockEditScreen(BlockPos pos) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.world != null && client.world.getBlockEntity(pos) instanceof PopupBlockEntity be) { + MinecraftClient.getInstance().setScreen(new PopupBlockEditScreen(be)); + } + } + + @Override + public void openPopupBlockViewScreen(BlockPos pos) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.world != null && client.world.getBlockEntity(pos) instanceof PopupBlockEntity be) { + MinecraftClient.getInstance().setScreen(new PopupBlockViewScreen(be)); + } + } } diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java new file mode 100644 index 0000000..928ffae --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java @@ -0,0 +1,306 @@ +package dev.hephaestus.glowcase.client.gui.screen.ingame; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; +import dev.hephaestus.glowcase.block.entity.TextBlockEntity; +import dev.hephaestus.glowcase.packet.C2SEditPopupBlock; +import dev.hephaestus.glowcase.packet.C2SEditTextBlock; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.client.render.*; +import net.minecraft.client.util.SelectionManager; +import net.minecraft.text.Text; +import net.minecraft.text.TextColor; +import net.minecraft.util.math.MathHelper; +import org.lwjgl.glfw.GLFW; + +//TODO: multi-character selection at some point? it may be a bit complex but it'd be nice +public class PopupBlockEditScreen extends GlowcaseScreen { + private final PopupBlockEntity popupBlockEntity; + + private SelectionManager selectionManager; + private int currentRow; + private long ticksSinceOpened = 0; + private ButtonWidget changeAlignment; + private TextFieldWidget colorEntryWidget; + + public PopupBlockEditScreen(PopupBlockEntity popupBlockEntity) { + this.popupBlockEntity = popupBlockEntity; + } + + @Override + public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) { + renderDarkening(context); + } + + @Override + public void init() { + super.init(); + + int innerPadding = width / 100; + + this.selectionManager = new SelectionManager( + () -> this.popupBlockEntity.getRawLine(this.currentRow), + (string) -> { + popupBlockEntity.setRawLine(this.currentRow, string); + this.popupBlockEntity.renderDirty = true; + }, + SelectionManager.makeClipboardGetter(this.client), + SelectionManager.makeClipboardSetter(this.client), + (string) -> true); + + this.changeAlignment = ButtonWidget.builder(Text.stringifiedTranslatable("gui.glowcase.alignment", this.popupBlockEntity.textAlignment), action -> { + switch (popupBlockEntity.textAlignment) { + case LEFT -> popupBlockEntity.textAlignment = TextBlockEntity.TextAlignment.CENTER; + case CENTER -> popupBlockEntity.textAlignment = TextBlockEntity.TextAlignment.RIGHT; + case RIGHT -> popupBlockEntity.textAlignment = TextBlockEntity.TextAlignment.LEFT; + } + this.popupBlockEntity.renderDirty = true; + + this.changeAlignment.setMessage(Text.stringifiedTranslatable("gui.glowcase.alignment", this.popupBlockEntity.textAlignment)); + }).dimensions(120 + innerPadding, 0, 160, 20).build(); + + this.colorEntryWidget = new TextFieldWidget(this.client.textRenderer, 280 + innerPadding * 2, 0, 50, 20, Text.empty()); + this.colorEntryWidget.setText("#" + Integer.toHexString(this.popupBlockEntity.color & 0x00FFFFFF)); + this.colorEntryWidget.setChangedListener(string -> { + TextColor.parse(this.colorEntryWidget.getText()).ifSuccess(color -> { + this.popupBlockEntity.color = color == null ? 0xFFFFFFFF : color.getRgb() | 0xFF000000; + this.popupBlockEntity.renderDirty = true; + }); + }); + + this.addDrawableChild(this.changeAlignment); + this.addDrawableChild(this.colorEntryWidget); + } + + @Override + public void tick() { + ++this.ticksSinceOpened; + } + + @Override + public void close() { + C2SEditPopupBlock.of(popupBlockEntity).send(); + super.close(); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + if (this.client != null) { + super.render(context, mouseX, mouseY, delta); + + context.getMatrices().push(); + context.getMatrices().translate(0, 40 + 2 * this.width / 100F, 0); + for (int i = 0; i < this.popupBlockEntity.lines.size(); ++i) { + var text = this.currentRow == i ? Text.literal(this.popupBlockEntity.getRawLine(i)) : this.popupBlockEntity.lines.get(i); + + int lineWidth = this.textRenderer.getWidth(text); + switch (this.popupBlockEntity.textAlignment) { + case LEFT -> context.drawTextWithShadow(client.textRenderer, text, this.width / 10, i * 12, this.popupBlockEntity.color); + case CENTER -> context.drawTextWithShadow(client.textRenderer, text, this.width / 2 - lineWidth / 2, i * 12, this.popupBlockEntity.color); + case RIGHT -> context.drawTextWithShadow(client.textRenderer, text, this.width - this.width / 10 - lineWidth, i * 12, this.popupBlockEntity.color); + } + } + + int caretStart = this.selectionManager.getSelectionStart(); + int caretEnd = this.selectionManager.getSelectionEnd(); + + if (caretStart >= 0) { + String line = this.popupBlockEntity.getRawLine(this.currentRow); + int selectionStart = MathHelper.clamp(Math.min(caretStart, caretEnd), 0, line.length()); + int selectionEnd = MathHelper.clamp(Math.max(caretStart, caretEnd), 0, line.length()); + + String preSelection = line.substring(0, MathHelper.clamp(line.length(), 0, selectionStart)); + int startX = this.client.textRenderer.getWidth(preSelection); + + float push = switch (this.popupBlockEntity.textAlignment) { + case LEFT -> this.width / 10F; + case CENTER -> this.width / 2F - this.textRenderer.getWidth(line) / 2F; + case RIGHT -> this.width - this.width / 10F - this.textRenderer.getWidth(line); + }; + + startX += (int) push; + + + int caretStartY = this.currentRow * 12; + int caretEndY = this.currentRow * 12 + 9; + if (this.ticksSinceOpened / 6 % 2 == 0 && !this.colorEntryWidget.isActive()) { + if (selectionStart < line.length()) { + context.fill(startX, caretStartY, startX + 1, caretEndY, 0xCCFFFFFF); + } else { + context.drawText(client.textRenderer, "_", startX, this.currentRow * 12, 0xFFFFFFFF, false); + } + } + + if (caretStart != caretEnd) { + int endX = startX + this.client.textRenderer.getWidth(line.substring(selectionStart, selectionEnd)); + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder bufferBuilder = tessellator.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR); + RenderSystem.enableColorLogicOp(); + RenderSystem.logicOp(GlStateManager.LogicOp.OR_REVERSE); + bufferBuilder.vertex(context.getMatrices().peek().getPositionMatrix(), startX, caretEndY, 0.0F).color(0, 0, 255, 255); + bufferBuilder.vertex(context.getMatrices().peek().getPositionMatrix(), endX, caretEndY, 0.0F).color(0, 0, 255, 255); + bufferBuilder.vertex(context.getMatrices().peek().getPositionMatrix(), endX, caretStartY, 0.0F).color(0, 0, 255, 255); + bufferBuilder.vertex(context.getMatrices().peek().getPositionMatrix(), startX, caretStartY, 0.0F).color(0, 0, 255, 255); + BufferRenderer.drawWithGlobalProgram(bufferBuilder.end()); + RenderSystem.disableColorLogicOp(); + } + } + + context.getMatrices().pop(); + } + } + + @Override + public boolean charTyped(char chr, int keyCode) { + if (this.colorEntryWidget.isActive()) { + return this.colorEntryWidget.charTyped(chr, keyCode); + } else { + this.selectionManager.insert(chr); + return true; + } + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (this.colorEntryWidget.isActive()) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + this.close(); + return true; + } else { + return this.colorEntryWidget.keyPressed(keyCode, scanCode, modifiers); + } + } else { + setFocused(null); + if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { + this.popupBlockEntity.addRawLine(this.currentRow + 1, + this.popupBlockEntity.getRawLine(this.currentRow).substring( + MathHelper.clamp(this.selectionManager.getSelectionStart(), 0, this.popupBlockEntity.getRawLine(this.currentRow).length()) + )); + this.popupBlockEntity.setRawLine(this.currentRow, + this.popupBlockEntity.getRawLine(this.currentRow).substring(0, MathHelper.clamp(this.selectionManager.getSelectionStart(), 0, this.popupBlockEntity.getRawLine(this.currentRow).length()) + )); + this.popupBlockEntity.renderDirty = true; + ++this.currentRow; + this.selectionManager.moveCursorToStart(); + return true; + } else if (keyCode == GLFW.GLFW_KEY_UP) { + this.currentRow = Math.max(this.currentRow - 1, 0); + this.selectionManager.putCursorAtEnd(); + return true; + } else if (keyCode == GLFW.GLFW_KEY_DOWN) { + this.currentRow = Math.min(this.currentRow + 1, (this.popupBlockEntity.lines.size() - 1)); + this.selectionManager.putCursorAtEnd(); + return true; + } else if (keyCode == GLFW.GLFW_KEY_BACKSPACE && this.currentRow > 0 && this.popupBlockEntity.lines.size() > 1 && this.selectionManager.getSelectionStart() == 0 && this.selectionManager.getSelectionEnd() == this.selectionManager.getSelectionStart()) { + --this.currentRow; + this.selectionManager.putCursorAtEnd(); + deleteLine(); + return true; + } else if (keyCode == GLFW.GLFW_KEY_DELETE && this.currentRow < this.popupBlockEntity.lines.size() - 1 && this.selectionManager.getSelectionEnd() == this.popupBlockEntity.getRawLine(this.currentRow).length()) { + deleteLine(); + return true; + } else { + try { + boolean val = this.selectionManager.handleSpecialKey(keyCode) || super.keyPressed(keyCode, scanCode, modifiers); + int selectionOffset = this.popupBlockEntity.getRawLine(this.currentRow).length() - this.selectionManager.getSelectionStart(); + + // Find line feed characters and create proper newlines + for (int i = 0; i < this.popupBlockEntity.lines.size(); ++i) { + int lineFeedIndex = this.popupBlockEntity.getRawLine(i).indexOf("\n"); + + if (lineFeedIndex >= 0) { + this.popupBlockEntity.addRawLine(i + 1, + this.popupBlockEntity.getRawLine(i).substring( + MathHelper.clamp(lineFeedIndex + 1, 0, this.popupBlockEntity.getRawLine(i).length()) + )); + this.popupBlockEntity.setRawLine(i, + this.popupBlockEntity.getRawLine(i).substring(0, MathHelper.clamp(lineFeedIndex, 0, this.popupBlockEntity.getRawLine(i).length()) + )); + this.popupBlockEntity.renderDirty = true; + ++this.currentRow; + this.selectionManager.putCursorAtEnd(); + this.selectionManager.moveCursor(-selectionOffset); + } + } + return val; + } catch (StringIndexOutOfBoundsException e) { + e.printStackTrace(); + MinecraftClient.getInstance().setScreen(null); + return false; + } + } + } + } + + private void deleteLine() { + this.popupBlockEntity.setRawLine(this.currentRow, + this.popupBlockEntity.getRawLine(this.currentRow) + this.popupBlockEntity.getRawLine(this.currentRow + 1) + ); + + this.popupBlockEntity.lines.remove(this.currentRow + 1); + this.popupBlockEntity.renderDirty = true; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + int topOffset = (int) (40 + 2 * this.width / 100F); + if (!this.colorEntryWidget.mouseClicked(mouseX, mouseY, button)) { + this.colorEntryWidget.setFocused(false); + } + if (mouseY > topOffset) { + this.currentRow = MathHelper.clamp((int) (mouseY - topOffset) / 12, 0, this.popupBlockEntity.lines.size() - 1); + this.setFocused(null); + String baseContents = this.popupBlockEntity.getRawLine(currentRow); + int baseContentsWidth = this.textRenderer.getWidth(baseContents); + int contentsStart; + int contentsEnd; + switch (this.popupBlockEntity.textAlignment) { + case LEFT -> { + contentsStart = this.width / 10; + contentsEnd = contentsStart + baseContentsWidth; + } + case CENTER -> { + int midpoint = this.width / 2; + int textMidpoint = baseContentsWidth / 2; + contentsStart = midpoint - textMidpoint; + contentsEnd = midpoint + textMidpoint; + } + case RIGHT -> { + contentsEnd = this.width - this.width / 10; + contentsStart = contentsEnd - baseContentsWidth; + } + //even though this is exhaustive, javac won't treat contentsStart and contentsEnd as initialized + //why? who knows! just throw bc this should be impossible + default -> throw new IllegalStateException(":HOW:"); + } + + if (mouseX <= contentsStart) { + this.selectionManager.moveCursorToStart(); + } else if (mouseX >= contentsEnd) { + this.selectionManager.putCursorAtEnd(); + } else { + int lastWidth = 0; + for (int i = 1; i < baseContents.length(); i++) { + String testContents = baseContents.substring(0, i); + int width = this.textRenderer.getWidth(testContents); + int midpointWidth = (width + lastWidth) / 2; + if (mouseX < contentsStart + midpointWidth) { + this.selectionManager.moveCursorTo(i - 1, false); + break; + } else if (mouseX <= contentsStart + width) { + this.selectionManager.moveCursorTo(i, false); + break; + } + lastWidth = width; + } + } + return true; + } else { + return super.mouseClicked(mouseX, mouseY, button); + } + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockViewScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockViewScreen.java new file mode 100644 index 0000000..9ad6fe5 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockViewScreen.java @@ -0,0 +1,52 @@ +package dev.hephaestus.glowcase.client.gui.screen.ingame; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; +import dev.hephaestus.glowcase.block.entity.TextBlockEntity; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.client.render.*; +import net.minecraft.client.util.SelectionManager; +import net.minecraft.text.Text; +import net.minecraft.text.TextColor; +import net.minecraft.util.math.MathHelper; +import org.lwjgl.glfw.GLFW; + +//TODO: multi-character selection at some point? it may be a bit complex but it'd be nice +public class PopupBlockViewScreen extends GlowcaseScreen { + private final PopupBlockEntity popupBlockEntity; + + public PopupBlockViewScreen(PopupBlockEntity popupBlockEntity) { + this.popupBlockEntity = popupBlockEntity; + } + + @Override + public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) { + renderDarkening(context); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + if (this.client != null) { + super.render(context, mouseX, mouseY, delta); + + context.getMatrices().push(); + context.getMatrices().translate(0, 40 + 2 * this.width / 100F, 0); + for (int i = 0; i < this.popupBlockEntity.lines.size(); ++i) { + var text = this.popupBlockEntity.lines.get(i); + + int lineWidth = this.textRenderer.getWidth(text); + switch (this.popupBlockEntity.textAlignment) { + case LEFT -> context.drawTextWithShadow(client.textRenderer, text, this.width / 10, i * 12, this.popupBlockEntity.color); + case CENTER -> context.drawTextWithShadow(client.textRenderer, text, this.width / 2 - lineWidth / 2, i * 12, this.popupBlockEntity.color); + case RIGHT -> context.drawTextWithShadow(client.textRenderer, text, this.width - this.width / 10 - lineWidth, i * 12, this.popupBlockEntity.color); + } + } + + context.getMatrices().pop(); + } + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/PopupBlockEntityRenderer.java b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/PopupBlockEntityRenderer.java new file mode 100644 index 0000000..9ef2d28 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/PopupBlockEntityRenderer.java @@ -0,0 +1,38 @@ +package dev.hephaestus.glowcase.client.render.block.entity; + +import dev.hephaestus.glowcase.Glowcase; +import dev.hephaestus.glowcase.block.entity.HyperlinkBlockEntity; +import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer.TextLayerType; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.block.entity.BlockEntityRenderer; +import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; +import net.minecraft.client.render.model.json.ModelTransformationMode; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.RotationAxis; + +public record PopupBlockEntityRenderer(BlockEntityRendererFactory.Context context) implements BlockEntityRenderer { + private static final MinecraftClient mc = MinecraftClient.getInstance(); + + public static final ItemStack STACK = new ItemStack(Glowcase.POPUP_BLOCK.get()); + + public void render(PopupBlockEntity entity, float f, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) { + Camera camera = context.getRenderDispatcher().camera; + matrices.push(); + matrices.translate(0.5D, 0.5D, 0.5D); + matrices.scale(0.5F, 0.5F, 0.5F); + float n = -camera.getYaw(); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(n)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(camera.getPitch())); + context.getItemRenderer().renderItem(STACK, ModelTransformationMode.FIXED, light, OverlayTexture.DEFAULT_UV, matrices, vertexConsumers, entity.getWorld(), 0); + + matrices.pop(); + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/packet/C2SEditPopupBlock.java b/src/main/java/dev/hephaestus/glowcase/packet/C2SEditPopupBlock.java new file mode 100644 index 0000000..0001e54 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/packet/C2SEditPopupBlock.java @@ -0,0 +1,49 @@ +package dev.hephaestus.glowcase.packet; + +import dev.hephaestus.glowcase.Glowcase; +import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; +import dev.hephaestus.glowcase.block.entity.TextBlockEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.codec.PacketCodecs; +import net.minecraft.network.packet.CustomPayload; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; +import net.minecraft.text.TextCodecs; +import net.minecraft.util.math.BlockPos; + +import java.util.ArrayList; +import java.util.List; + +public record C2SEditPopupBlock(BlockPos pos, List lines, TextBlockEntity.TextAlignment alignment, int color) implements C2SEditBlockEntity { + public static final Id ID = new Id<>(Glowcase.id("channel.popup_block")); + public static final PacketCodec PACKET_CODEC = PacketCodec.tuple( + BlockPos.PACKET_CODEC, C2SEditPopupBlock::pos, + PacketCodecs.collection(ArrayList::new, TextCodecs.REGISTRY_PACKET_CODEC), C2SEditPopupBlock::lines, + PacketCodecs.BYTE.xmap(index -> TextBlockEntity.TextAlignment.values()[index], textAlignment -> (byte) textAlignment.ordinal()), C2SEditPopupBlock::alignment, + PacketCodecs.INTEGER, C2SEditPopupBlock::color, + C2SEditPopupBlock::new + ); + + public static C2SEditPopupBlock of(PopupBlockEntity be) { + return new C2SEditPopupBlock(be.getPos(), be.lines, be.textAlignment, be.color); + } + + @Override + public Id getId() { + return ID; + } + + @Override + public void receive(ServerWorld world, BlockEntity blockEntity) { + if (!(blockEntity instanceof PopupBlockEntity be)) return; + + be.lines = this.lines(); + be.textAlignment = this.alignment(); + be.color = this.color(); + + be.markDirty(); + be.dispatch(); + } +} diff --git a/src/main/resources/assets/glowcase/blockstates/popup_block.json b/src/main/resources/assets/glowcase/blockstates/popup_block.json new file mode 100644 index 0000000..0858580 --- /dev/null +++ b/src/main/resources/assets/glowcase/blockstates/popup_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "glowcase:block/popup_block" + } + } +} diff --git a/src/main/resources/assets/glowcase/lang/en_us.json b/src/main/resources/assets/glowcase/lang/en_us.json index a32d0e3..0b222cb 100644 --- a/src/main/resources/assets/glowcase/lang/en_us.json +++ b/src/main/resources/assets/glowcase/lang/en_us.json @@ -3,6 +3,7 @@ "block.glowcase.text_block": "Text Block", "block.glowcase.hyperlink_block": "Hyperlink Block", "block.glowcase.item_display_block": "Item Display Block", + "block.glowcase.popup_block": "Popup Block", "gui.glowcase.scale": "Scale: %d", "gui.glowcase.alignment": "Alignment: %s", "gui.glowcase.gives_item": "Gives Item: %s", diff --git a/src/main/resources/assets/glowcase/models/block/popup_block.json b/src/main/resources/assets/glowcase/models/block/popup_block.json new file mode 100644 index 0000000..49f3a0b --- /dev/null +++ b/src/main/resources/assets/glowcase/models/block/popup_block.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "glowcase:item/popup_block" + } +} diff --git a/src/main/resources/assets/glowcase/models/item/popup_block.json b/src/main/resources/assets/glowcase/models/item/popup_block.json new file mode 100644 index 0000000..898e040 --- /dev/null +++ b/src/main/resources/assets/glowcase/models/item/popup_block.json @@ -0,0 +1,49 @@ +{ + "textures": { + "0": "glowcase:item/popup_block", + "particle": "glowcase:item/popup_block" + }, + "elements": [ + { + "from": [ + 2, + 2, + 8 + ], + "to": [ + 14, + 14, + 8 + ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ + 8, + 8, + 16 + ] + }, + "faces": { + "north": { + "uv": [ + 0, + 0, + 16, + 16 + ], + "texture": "#0" + }, + "south": { + "uv": [ + 0, + 0, + 16, + 16 + ], + "texture": "#0" + } + } + } + ] +} diff --git a/src/main/resources/assets/glowcase/textures/item/popup_block.png b/src/main/resources/assets/glowcase/textures/item/popup_block.png new file mode 100644 index 0000000000000000000000000000000000000000..9a17e05812c1989e17cf56f4ed0d33ec99faef38 GIT binary patch literal 5954 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/data/glowcase/tags/item/items.json b/src/main/resources/data/glowcase/tags/item/items.json index 26dc5c1..8d88222 100644 --- a/src/main/resources/data/glowcase/tags/item/items.json +++ b/src/main/resources/data/glowcase/tags/item/items.json @@ -3,6 +3,7 @@ "values": [ "glowcase:hyperlink_block", "glowcase:item_display_block", - "glowcase:text_block" + "glowcase:text_block", + "glowcase:popup_block" ] } From e81bc3c3f6b1bf59bb590252a1444fbadc805d22 Mon Sep 17 00:00:00 2001 From: Chai <7232280+Chailotl@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:12:23 -0400 Subject: [PATCH 2/4] Added tooltips for #36 --- .../dev/hephaestus/glowcase/block/PopupBlock.java | 13 +++++++++++++ src/main/resources/assets/glowcase/lang/en_us.json | 5 ++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/hephaestus/glowcase/block/PopupBlock.java b/src/main/java/dev/hephaestus/glowcase/block/PopupBlock.java index 3a9812d..122084e 100644 --- a/src/main/java/dev/hephaestus/glowcase/block/PopupBlock.java +++ b/src/main/java/dev/hephaestus/glowcase/block/PopupBlock.java @@ -10,8 +10,12 @@ import net.minecraft.component.type.NbtComponent; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.item.tooltip.TooltipType; +import net.minecraft.text.Text; import net.minecraft.util.ActionResult; +import net.minecraft.util.Formatting; import net.minecraft.util.Hand; import net.minecraft.util.ItemActionResult; import net.minecraft.util.hit.BlockHitResult; @@ -22,6 +26,8 @@ import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class PopupBlock extends GlowcaseBlock implements BlockEntityProvider { private static final VoxelShape OUTLINE = VoxelShapes.cuboid(0.25, 0.25, 0.25, 0.75, 0.75, 0.75); @@ -69,4 +75,11 @@ protected ItemActionResult onUseWithItem(ItemStack stack, BlockState state, Worl } return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; } + + @Override + public void appendTooltip(ItemStack itemStack, Item.TooltipContext context, List tooltip, TooltipType options) { + tooltip.add(Text.translatable("block.glowcase.popup_block.tooltip.0").formatted(Formatting.GRAY)); + tooltip.add(Text.translatable("block.glowcase.generic.tooltip").formatted(Formatting.DARK_GRAY)); + tooltip.add(Text.translatable("block.glowcase.popup_block.tooltip.1").formatted(Formatting.DARK_GRAY)); + } } diff --git a/src/main/resources/assets/glowcase/lang/en_us.json b/src/main/resources/assets/glowcase/lang/en_us.json index 0b222cb..4f4785c 100644 --- a/src/main/resources/assets/glowcase/lang/en_us.json +++ b/src/main/resources/assets/glowcase/lang/en_us.json @@ -15,5 +15,8 @@ "item.glowcase.text_block": "Text Block", "item.glowcase.hyperlink_block": "Hyperlink Block", "gui.glowcase.title": "Title", - "gui.glowcase.url": "URL" + "gui.glowcase.url": "URL", + "block.glowcase.generic.tooltip": "Interact with a glowcase item to edit", + "block.glowcase.popup_block.tooltip.0": "Displays formatted text when interacted", + "block.glowcase.popup_block.tooltip.1": "Supports Placeholder QuickText" } From cbbf76e55923b759113041f0e108e37a67f474ad Mon Sep 17 00:00:00 2001 From: Sisby folk <55819817+sisby-folk@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:33:42 +1000 Subject: [PATCH 3/4] Update en_us.json --- src/main/resources/assets/glowcase/lang/en_us.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/glowcase/lang/en_us.json b/src/main/resources/assets/glowcase/lang/en_us.json index db1187a..10fbdae 100644 --- a/src/main/resources/assets/glowcase/lang/en_us.json +++ b/src/main/resources/assets/glowcase/lang/en_us.json @@ -22,7 +22,7 @@ "block.glowcase.hyperlink_block.tooltip.0": "Opens a URL when interacted", "block.glowcase.item_display_block.tooltip.0": "Gives an item stack when interacted", "block.glowcase.item_display_block.tooltip.1": "Interact with an item stack to set it", - "block.glowcase.item_display_block.tooltip.2": "Interact with the same item to edit" - "block.glowcase.popup_block.tooltip.0": "Displays formatted text when interacted", + "block.glowcase.item_display_block.tooltip.2": "Interact with the same item to edit", + "block.glowcase.popup_block.tooltip.0": "Displays formatted text when interacted", "block.glowcase.popup_block.tooltip.1": "Supports Placeholder QuickText" } From dff5ca3bfd5e0236b2b8bc3b945e909de7c7bfa6 Mon Sep 17 00:00:00 2001 From: Chai <7232280+Chailotl@users.noreply.github.com> Date: Sat, 31 Aug 2024 00:27:49 -0400 Subject: [PATCH 4/4] Added titles to the popup block --- .../block/entity/PopupBlockEntity.java | 3 ++ .../screen/ingame/PopupBlockEditScreen.java | 35 +++++++++++++++---- .../entity/PopupBlockEntityRenderer.java | 11 +++++- .../glowcase/packet/C2SEditPopupBlock.java | 9 +++-- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/main/java/dev/hephaestus/glowcase/block/entity/PopupBlockEntity.java b/src/main/java/dev/hephaestus/glowcase/block/entity/PopupBlockEntity.java index 33ca336..c33d48c 100644 --- a/src/main/java/dev/hephaestus/glowcase/block/entity/PopupBlockEntity.java +++ b/src/main/java/dev/hephaestus/glowcase/block/entity/PopupBlockEntity.java @@ -26,6 +26,7 @@ public class PopupBlockEntity extends BlockEntity { public static final NodeParser PARSER = TagParser.DEFAULT; + public String title = ""; public List lines = new ArrayList<>(); public TextBlockEntity.TextAlignment textAlignment = TextBlockEntity.TextAlignment.CENTER; public int color = 0xFFFFFF; @@ -40,6 +41,7 @@ public PopupBlockEntity(BlockPos pos, BlockState state) { protected void writeNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) { super.writeNbt(tag, registryLookup); + tag.putString("title", this.title); tag.putInt("color", this.color); tag.putString("text_alignment", this.textAlignment.name()); @@ -56,6 +58,7 @@ protected void writeNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryL protected void readNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) { super.readNbt(tag, registryLookup); + this.title = tag.getString("title"); this.lines = new ArrayList<>(); this.color = tag.getInt("color"); diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java index 928ffae..ce8469f 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java @@ -2,10 +2,10 @@ import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; +import dev.hephaestus.glowcase.block.entity.HyperlinkBlockEntity; import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; import dev.hephaestus.glowcase.block.entity.TextBlockEntity; import dev.hephaestus.glowcase.packet.C2SEditPopupBlock; -import dev.hephaestus.glowcase.packet.C2SEditTextBlock; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.widget.ButtonWidget; @@ -24,6 +24,7 @@ public class PopupBlockEditScreen extends GlowcaseScreen { private SelectionManager selectionManager; private int currentRow; private long ticksSinceOpened = 0; + private TextFieldWidget titleEntryWidget; private ButtonWidget changeAlignment; private TextFieldWidget colorEntryWidget; @@ -52,6 +53,15 @@ public void init() { SelectionManager.makeClipboardSetter(this.client), (string) -> true); + this.titleEntryWidget = new TextFieldWidget(this.client.textRenderer, width / 10, 0, 8 * width / 10, 20, Text.empty()); + this.titleEntryWidget.setMaxLength(HyperlinkBlockEntity.TITLE_MAX_LENGTH); + this.titleEntryWidget.setText(this.popupBlockEntity.title); + this.titleEntryWidget.setPlaceholder(Text.translatable("gui.glowcase.title")); + this.titleEntryWidget.setChangedListener(string -> { + this.popupBlockEntity.title = this.titleEntryWidget.getText(); + this.popupBlockEntity.renderDirty = true; + }); + this.changeAlignment = ButtonWidget.builder(Text.stringifiedTranslatable("gui.glowcase.alignment", this.popupBlockEntity.textAlignment), action -> { switch (popupBlockEntity.textAlignment) { case LEFT -> popupBlockEntity.textAlignment = TextBlockEntity.TextAlignment.CENTER; @@ -61,9 +71,9 @@ public void init() { this.popupBlockEntity.renderDirty = true; this.changeAlignment.setMessage(Text.stringifiedTranslatable("gui.glowcase.alignment", this.popupBlockEntity.textAlignment)); - }).dimensions(120 + innerPadding, 0, 160, 20).build(); + }).dimensions(120 + innerPadding, 20 + innerPadding, 160, 20).build(); - this.colorEntryWidget = new TextFieldWidget(this.client.textRenderer, 280 + innerPadding * 2, 0, 50, 20, Text.empty()); + this.colorEntryWidget = new TextFieldWidget(this.client.textRenderer, 280 + innerPadding * 2, 20 + innerPadding, 50, 20, Text.empty()); this.colorEntryWidget.setText("#" + Integer.toHexString(this.popupBlockEntity.color & 0x00FFFFFF)); this.colorEntryWidget.setChangedListener(string -> { TextColor.parse(this.colorEntryWidget.getText()).ifSuccess(color -> { @@ -72,6 +82,7 @@ public void init() { }); }); + this.addDrawableChild(this.titleEntryWidget); this.addDrawableChild(this.changeAlignment); this.addDrawableChild(this.colorEntryWidget); } @@ -127,7 +138,7 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) { int caretStartY = this.currentRow * 12; int caretEndY = this.currentRow * 12 + 9; - if (this.ticksSinceOpened / 6 % 2 == 0 && !this.colorEntryWidget.isActive()) { + if (this.ticksSinceOpened / 6 % 2 == 0 && !this.titleEntryWidget.isActive() && !this.colorEntryWidget.isActive()) { if (selectionStart < line.length()) { context.fill(startX, caretStartY, startX + 1, caretEndY, 0xCCFFFFFF); } else { @@ -156,7 +167,9 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) { @Override public boolean charTyped(char chr, int keyCode) { - if (this.colorEntryWidget.isActive()) { + if (this.titleEntryWidget.isActive()) { + return this.titleEntryWidget.charTyped(chr, keyCode); + } else if (this.colorEntryWidget.isActive()) { return this.colorEntryWidget.charTyped(chr, keyCode); } else { this.selectionManager.insert(chr); @@ -166,7 +179,14 @@ public boolean charTyped(char chr, int keyCode) { @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (this.colorEntryWidget.isActive()) { + if (this.titleEntryWidget.isActive()) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + this.close(); + return true; + } else { + return this.titleEntryWidget.keyPressed(keyCode, scanCode, modifiers); + } + } else if (this.colorEntryWidget.isActive()) { if (keyCode == GLFW.GLFW_KEY_ESCAPE) { this.close(); return true; @@ -248,6 +268,9 @@ private void deleteLine() { @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { int topOffset = (int) (40 + 2 * this.width / 100F); + if (!this.titleEntryWidget.mouseClicked(mouseX, mouseY, button)) { + this.titleEntryWidget.setFocused(false); + } if (!this.colorEntryWidget.mouseClicked(mouseX, mouseY, button)) { this.colorEntryWidget.setFocused(false); } diff --git a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/PopupBlockEntityRenderer.java b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/PopupBlockEntityRenderer.java index 9ef2d28..1c0ffa3 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/PopupBlockEntityRenderer.java +++ b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/PopupBlockEntityRenderer.java @@ -1,7 +1,6 @@ package dev.hephaestus.glowcase.client.render.block.entity; import dev.hephaestus.glowcase.Glowcase; -import dev.hephaestus.glowcase.block.entity.HyperlinkBlockEntity; import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer.TextLayerType; @@ -33,6 +32,16 @@ public void render(PopupBlockEntity entity, float f, MatrixStack matrices, Verte matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(camera.getPitch())); context.getItemRenderer().renderItem(STACK, ModelTransformationMode.FIXED, light, OverlayTexture.DEFAULT_UV, matrices, vertexConsumers, entity.getWorld(), 0); + HitResult hitResult = mc.crosshairTarget; + if (hitResult instanceof BlockHitResult && ((BlockHitResult) hitResult).getBlockPos().equals(entity.getPos())) { + float scale = 0.025F; + matrices.scale(scale, scale, scale); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180)); + matrices.translate(-context.getTextRenderer().getWidth(entity.title) / 2F, -4, -scale); + // Fixes shadow being rendered in front of actual text + matrices.scale(1, 1, -1); + context.getTextRenderer().draw(entity.title, 0, 0, 0xFFFFFF, true, matrices.peek().getPositionMatrix(), vertexConsumers, TextLayerType.NORMAL, 0, LightmapTextureManager.MAX_LIGHT_COORDINATE); + } matrices.pop(); } } diff --git a/src/main/java/dev/hephaestus/glowcase/packet/C2SEditPopupBlock.java b/src/main/java/dev/hephaestus/glowcase/packet/C2SEditPopupBlock.java index 0001e54..7732cbc 100644 --- a/src/main/java/dev/hephaestus/glowcase/packet/C2SEditPopupBlock.java +++ b/src/main/java/dev/hephaestus/glowcase/packet/C2SEditPopupBlock.java @@ -1,6 +1,7 @@ package dev.hephaestus.glowcase.packet; import dev.hephaestus.glowcase.Glowcase; +import dev.hephaestus.glowcase.block.entity.HyperlinkBlockEntity; import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; import dev.hephaestus.glowcase.block.entity.TextBlockEntity; import net.minecraft.block.entity.BlockEntity; @@ -16,10 +17,11 @@ import java.util.ArrayList; import java.util.List; -public record C2SEditPopupBlock(BlockPos pos, List lines, TextBlockEntity.TextAlignment alignment, int color) implements C2SEditBlockEntity { +public record C2SEditPopupBlock(BlockPos pos, String title, List lines, TextBlockEntity.TextAlignment alignment, int color) implements C2SEditBlockEntity { public static final Id ID = new Id<>(Glowcase.id("channel.popup_block")); public static final PacketCodec PACKET_CODEC = PacketCodec.tuple( BlockPos.PACKET_CODEC, C2SEditPopupBlock::pos, + PacketCodecs.STRING, C2SEditPopupBlock::title, PacketCodecs.collection(ArrayList::new, TextCodecs.REGISTRY_PACKET_CODEC), C2SEditPopupBlock::lines, PacketCodecs.BYTE.xmap(index -> TextBlockEntity.TextAlignment.values()[index], textAlignment -> (byte) textAlignment.ordinal()), C2SEditPopupBlock::alignment, PacketCodecs.INTEGER, C2SEditPopupBlock::color, @@ -27,7 +29,7 @@ public record C2SEditPopupBlock(BlockPos pos, List lines, TextBlockEntity. ); public static C2SEditPopupBlock of(PopupBlockEntity be) { - return new C2SEditPopupBlock(be.getPos(), be.lines, be.textAlignment, be.color); + return new C2SEditPopupBlock(be.getPos(), be.title, be.lines, be.textAlignment, be.color); } @Override @@ -39,6 +41,9 @@ public Id getId() { public void receive(ServerWorld world, BlockEntity blockEntity) { if (!(blockEntity instanceof PopupBlockEntity be)) return; + if (this.title().length() <= HyperlinkBlockEntity.TITLE_MAX_LENGTH) { + be.title = this.title(); + } be.lines = this.lines(); be.textAlignment = this.alignment(); be.color = this.color();