From 844c1ee7b8755e16d60e52946ab75f69c4fd70c1 Mon Sep 17 00:00:00 2001 From: reoseah Date: Sun, 5 Apr 2026 02:41:55 +0200 Subject: [PATCH 1/8] render text, not baked --- .../glowcase/client/GlowcaseClient.java | 2 +- .../block/entity/TextBlockEntityRenderer.java | 109 +++++++++++++++--- 2 files changed, 94 insertions(+), 17 deletions(-) diff --git a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java index 2eeee6e1..2c00e6fe 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java +++ b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java @@ -37,7 +37,7 @@ public class GlowcaseClient implements ClientModInitializer { public void onInitializeClient() { Glowcase.proxy = new GlowcaseClientProxy(); - BlockEntityRenderers.register(Glowcase.TEXT_BLOCK_ENTITY.get(), (ctx) -> new TextBlockEntityRenderer()); + BlockEntityRenderers.register(Glowcase.TEXT_BLOCK_ENTITY.get(), (ctx) -> new TextBlockEntityRenderer(ctx)); BlockEntityRenderers.register(Glowcase.HYPERLINK_BLOCK_ENTITY.get(), HyperlinkBlockEntityRenderer::new); BlockEntityRenderers.register(Glowcase.CONFIG_LINK_BLOCK_ENTITY.get(), ConfigLinkBlockEntityRenderer::new); BlockEntityRenderers.register(Glowcase.ITEM_DISPLAY_BLOCK_ENTITY.get(), ItemDisplayBlockEntityRenderer::new); diff --git a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java index ee20dc0c..e3831200 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java +++ b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java @@ -7,28 +7,42 @@ import dev.hephaestus.glowcase.client.util.BlockEntityRenderUtil; import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.SubmitNodeCollector; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; import net.minecraft.client.renderer.feature.ModelFeatureRenderer; import net.minecraft.client.renderer.state.level.CameraRenderState; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; +import net.minecraft.util.FormattedCharSequence; import net.minecraft.util.LightCoordsUtil; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.phys.Vec3; -import org.joml.Matrix4f; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import java.util.List; + @NullMarked public class TextBlockEntityRenderer implements BakedBlockEntityRenderer { public static Identifier ITEM_TEXTURE = Glowcase.id("textures/item/text_block.png"); private boolean wasOutOfRange = false; + private final Font font; + + public TextBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) { + this.font = ctx.font(); + } + public static class TextRenderState extends BlockEntityRenderState { public boolean shouldRenderPlaceholder; - public TextBlockEntity.ZOffset zOffset; public int rotation16; + public List lines = List.of(); + public TextBlockEntity.TextAlignment textAlignment; + public TextBlockEntity.ZOffset zOffset; + public boolean shadow; + public float scale = 1; + public int color; + public int backgroundColor; } @Override @@ -36,7 +50,6 @@ public TextRenderState createRenderState() { return new TextRenderState(); } - @Override public TextRenderState createBakedRenderState() { return new TextRenderState(); @@ -46,8 +59,16 @@ public TextRenderState createBakedRenderState() { public void extractRenderState(TextBlockEntity blockEntity, TextRenderState state, float partialTicks, Vec3 cameraPosition, ModelFeatureRenderer.@Nullable CrumblingOverlay breakProgress) { BakedBlockEntityRenderer.super.extractRenderState(blockEntity, state, partialTicks, cameraPosition, breakProgress); state.shouldRenderPlaceholder = blockEntity.lines.stream().allMatch(t -> t.getString().isBlank()) || BlockEntityRenderUtil.shouldRenderPlaceholder(blockEntity.getBlockPos()); - state.zOffset = blockEntity.zOffset; + state.rotation16 = blockEntity.getBlockState().getValue(BlockStateProperties.ROTATION_16); + + state.lines = blockEntity.lines.stream().map(Component::getVisualOrderText).toList(); + state.textAlignment = blockEntity.textAlignment; + state.zOffset = blockEntity.zOffset; + state.shadow = blockEntity.shadow; + state.scale = blockEntity.scale; + state.color = blockEntity.color; + state.backgroundColor = blockEntity.backgroundColor; } @Override @@ -57,6 +78,15 @@ public void extractBakingRenderState(TextBlockEntity blockEntity, TextRenderStat state.shouldRenderPlaceholder = blockEntity.lines.stream().allMatch(t -> t.getString().isBlank()) || BlockEntityRenderUtil.shouldRenderPlaceholder(blockEntity.getBlockPos()); state.zOffset = blockEntity.zOffset; state.rotation16 = blockEntity.getBlockState().getValue(BlockStateProperties.ROTATION_16); + + state.lines = blockEntity.lines.stream().map(Component::getVisualOrderText).toList(); + state.scale = blockEntity.scale; + } + + @Override + public boolean shouldBake(TextBlockEntity entity) { +// return !entity.lines.isEmpty(); + return false; } @Override @@ -64,23 +94,70 @@ public void submitForRendering(TextRenderState state, PoseStack poseStack, Submi if (state.shouldRenderPlaceholder) { BlockEntityRenderUtil.renderPlaceholderWithBlockRotation(state, state.rotation16, ITEM_TEXTURE, 1.0F, poseStack, submitNodeCollector, state.zOffset == TextBlockEntity.ZOffset.CENTER ? 0.01F : state.zOffset == TextBlockEntity.ZOffset.FRONT ? 0.4F : -0.4F); } - } - @Override - public void submitForBaking(TextRenderState state, PoseStack poseStack, SubmitNodeCollector submitNodeCollector) { - // TODO: Port to 26.1 + // TODO: move this to a baked rendering method + // currently it errors "Rendersystem called from wrong thread" on `this.font.width(text)` + int maxWidth = 0; + for (var line : state.lines) { + maxWidth = Math.max(maxWidth, this.font.width(line)); + } + poseStack.pushPose(); - poseStack.mulPose(Axis.YP.rotationDegrees(-(state.rotation16 * 360) / 16.0F)); - float a = 1 / 9f; - poseStack.translate(-.5, 1.5, -.5); - poseStack.scale(a, -a, a); - submitNodeCollector.submitText(poseStack, 0, 0, Component.literal("waff :3").getVisualOrderText(), true, Font.DisplayMode.NORMAL, LightCoordsUtil.FULL_BRIGHT, 0xFFFFFFFF, 0, 0); + poseStack.translate(0.5D, 0.5D, 0.5D); + // 2D rendering of the font has Y axis going down, not up + poseStack.scale(1, -1, 1); + + switch (state.zOffset) { + case FRONT -> poseStack.translate(0D, 0D, 0.4D); + case BACK -> poseStack.translate(0D, 0D, -0.4D); + } + + float rotation = -(state.rotation16 * 360) / 16.0F; + poseStack.mulPose(Axis.YP.rotationDegrees(rotation)); + + poseStack.scale(0.1F * state.scale, 0.1F * state.scale, 0.1F * state.scale); + poseStack.translate(0, -(state.lines.size() * this.font.lineHeight) / 2D, 0D); + + for (int i = 0; i < state.lines.size(); ++i) { + var line = state.lines.get(i); + + int width = this.font.width(line); + if (width == 0) { + continue; + } + + float x = switch (state.textAlignment) { + case LEFT -> -maxWidth / 2F; + case CENTER -> (maxWidth - width) / 2F - maxWidth / 2F; + case CENTER_LEFT -> -(50F / state.scale) - (width / 2F); + case CENTER_RIGHT -> (50F / state.scale) - (width / 2F); + case RIGHT -> maxWidth - width - maxWidth / 2F; + }; + + submitNodeCollector.submitText(poseStack, + x, + i * this.font.lineHeight, + line, state.shadow, + Font.DisplayMode.NORMAL, + LightCoordsUtil.FULL_BRIGHT, + state.color, + state.backgroundColor, + 0); + } + poseStack.popPose(); } @Override - public boolean shouldBake(TextBlockEntity entity) { - return !entity.lines.isEmpty(); + public void submitForBaking(TextRenderState state, PoseStack poseStack, SubmitNodeCollector submitNodeCollector) { + // TODO: Port to 26.1 +// poseStack.pushPose(); +// poseStack.mulPose(Axis.YP.rotationDegrees(-(state.rotation16 * 360) / 16.0F)); +// float a = 1 / 9f; +// poseStack.translate(-.5, 1.5, -.5); +// poseStack.scale(a, -a, a); +// submitNodeCollector.submitText(poseStack, 0, 0, Component.literal("waff :3").getVisualOrderText(), true, Font.DisplayMode.NORMAL, LightCoordsUtil.FULL_BRIGHT, 0xFFFFFFFF, 0, 0); +// poseStack.popPose(); } // FIXME 26.1 From a817a9bfdd1f7c43609072539db4d83ed6be83f5 Mon Sep 17 00:00:00 2001 From: reoseah Date: Sun, 5 Apr 2026 04:39:31 +0200 Subject: [PATCH 2/8] replace large Alignment button with three icon buttons --- libs.versions.toml | 2 +- .../screen/ingame/TextBlockEditScreen.java | 68 ++++++++++++++---- .../gui/widget/ingame/IconButtonWidget.java | 9 ++- .../gui/sprites/text_alignment/center.png | Bin 0 -> 136 bytes .../gui/sprites/text_alignment/left.png | Bin 0 -> 136 bytes .../gui/sprites/text_alignment/right.png | Bin 0 -> 132 bytes 6 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 src/main/resources/assets/glowcase/textures/gui/sprites/text_alignment/center.png create mode 100644 src/main/resources/assets/glowcase/textures/gui/sprites/text_alignment/left.png create mode 100644 src/main/resources/assets/glowcase/textures/gui/sprites/text_alignment/right.png diff --git a/libs.versions.toml b/libs.versions.toml index 54508985..2e79ce04 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -12,7 +12,7 @@ placeholder = "3.0.0-beta.2+26.1" #polydex = "1.8.1+1.21.11" # emi = "1.1.19+1.21.1" modmenu = "18.0.0-alpha.6" -sodium = "0.8.7-SNAPSHOT+mc26.1-local" +sodium = "0.8.7-SNAPSHOT+mc26.1-rc-3-build.859" [plugins] loom = { id = "net.fabricmc.fabric-loom", version.ref = "loom" } diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java index fb5d1d6a..d5234c9d 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java @@ -1,8 +1,10 @@ package dev.hephaestus.glowcase.client.gui.screen.ingame; import com.google.common.primitives.Floats; +import dev.hephaestus.glowcase.Glowcase; import dev.hephaestus.glowcase.block.entity.TextBlockEntity; import dev.hephaestus.glowcase.client.gui.widget.ingame.ColorPickerWidget; +import dev.hephaestus.glowcase.client.gui.widget.ingame.IconButtonWidget; import dev.hephaestus.glowcase.client.util.ColorUtil; import dev.hephaestus.glowcase.packet.C2SEditTextBlock; import eu.pb4.placeholders.api.parsers.tag.TagRegistry; @@ -22,7 +24,10 @@ import org.lwjgl.glfw.GLFW; import java.awt.*; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.function.Consumer; //TODO: multi-character selection at some point? it may be a bit complex but it'd be nice public class TextBlockEditScreen extends TextEditorScreen { @@ -37,7 +42,7 @@ public class TextBlockEditScreen extends TextEditorScreen { private int currentRow; private long ticksSinceOpened = 0; private ColorPickerWidget colorPickerWidget; - private Button changeAlignment; + // private Button changeAlignment; private EditBox colorEntryWidget; private EditBox backgroundColorEntryWidget; private Color colorEntryPreColorPicker; //used for color picker cancel button @@ -77,18 +82,54 @@ public void init() { this.textBlockEntity.renderDirty = true; }).bounds(middle - 110, 0, 20, 20).build(); - this.changeAlignment = Button.builder(Component.translatableEscape("gui.glowcase.alignment", this.textBlockEntity.textAlignment), action -> { - switch (textBlockEntity.textAlignment) { - case LEFT -> textBlockEntity.textAlignment = TextBlockEntity.TextAlignment.CENTER; - case CENTER -> textBlockEntity.textAlignment = TextBlockEntity.TextAlignment.CENTER_LEFT; - case CENTER_LEFT -> textBlockEntity.textAlignment = TextBlockEntity.TextAlignment.CENTER_RIGHT; - case CENTER_RIGHT -> textBlockEntity.textAlignment = TextBlockEntity.TextAlignment.RIGHT; - case RIGHT -> textBlockEntity.textAlignment = TextBlockEntity.TextAlignment.LEFT; + + Map textAlignmentButtons = new HashMap<>(); + + Consumer setAlignment = (alignment) -> { + var previous = textBlockEntity.textAlignment; + var prevButton = textAlignmentButtons.get(previous); + if (prevButton != null) { + prevButton.active = true; // there are a few legacy values without a button } - this.textBlockEntity.renderDirty = true; - this.changeAlignment.setMessage(Component.translatableEscape("gui.glowcase.alignment", this.textBlockEntity.textAlignment)); - }).bounds(middle - 90 + innerPadding, 0, 160, 20).build(); + textBlockEntity.textAlignment = alignment; + textBlockEntity.renderDirty = true; + }; + + var textAlignLeft = IconButtonWidget.builder(Glowcase.id("text_alignment/left"), button -> { + setAlignment.accept(TextBlockEntity.TextAlignment.LEFT); + button.active = false; + }) + .position(middle - 90 + innerPadding, 0) + .size(20, 20, 16, 16) + .build(); + var textAlignCenter = IconButtonWidget.builder(Glowcase.id("text_alignment/center"), button -> { + setAlignment.accept(TextBlockEntity.TextAlignment.CENTER); + button.active = false; + }) + .position(middle - 90 + innerPadding + 20, 0) + .size(20, 20, 16, 16) + .build(); + var textAlignRight = IconButtonWidget.builder(Glowcase.id("text_alignment/right"), button -> { + setAlignment.accept(TextBlockEntity.TextAlignment.RIGHT); + button.active = false; + }) + .position(middle - 90 + innerPadding + 40, 0) + .size(20, 20, 16, 16) + .build(); + + textAlignmentButtons.put(TextBlockEntity.TextAlignment.LEFT, textAlignLeft); + textAlignmentButtons.put(TextBlockEntity.TextAlignment.CENTER, textAlignCenter); + textAlignmentButtons.put(TextBlockEntity.TextAlignment.RIGHT, textAlignRight); + + var initialTextAlignmentButton = textAlignmentButtons.get(textBlockEntity.textAlignment); + if (initialTextAlignmentButton != null) { // there are a few legacy values without a button + initialTextAlignmentButton.active = false; + } + + this.addRenderableWidget(textAlignCenter); + this.addRenderableWidget(textAlignLeft); + this.addRenderableWidget(textAlignRight); this.shadowToggle = Checkbox.builder(Component.translatable("gui.glowcase.shadow"), this.font) .selected(this.textBlockEntity.shadow) @@ -157,7 +198,7 @@ public void init() { this.addRenderableWidget(colorPickerWidget); this.addRenderableWidget(increaseSize); this.addRenderableWidget(decreaseSize); - this.addRenderableWidget(this.changeAlignment); +// this.addRenderableWidget(this.changeAlignment); this.addRenderableWidget(this.shadowToggle); this.addRenderableWidget(this.zOffsetToggle); this.addRenderableWidget(this.colorEntryWidget); @@ -210,8 +251,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo int lineWidth = this.font.width(text); switch (this.textBlockEntity.textAlignment) { - case LEFT -> - graphics.text(minecraft.font, text, this.width / 10, i * 12, this.textBlockEntity.color); + case LEFT -> graphics.text(minecraft.font, text, this.width / 10, i * 12, this.textBlockEntity.color); case CENTER, CENTER_LEFT, CENTER_RIGHT -> graphics.text(minecraft.font, text, this.width / 2 - lineWidth / 2, i * 12, this.textBlockEntity.color); case RIGHT -> diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/IconButtonWidget.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/IconButtonWidget.java index 246519c0..40d8fbb5 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/IconButtonWidget.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/IconButtonWidget.java @@ -31,11 +31,18 @@ public IconButtonWidget(int x, int y, int width, int height, int iconWidth, int @Override protected void extractContents(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + this.extractDefaultSprite(graphics); + Identifier drawnIcon = this.icon; if(this.hoverIcon != null && this.isMouseOver(mouseX, mouseY)) { drawnIcon = this.hoverIcon; } - graphics.blitSprite(RenderPipelines.GUI_TEXTURED, drawnIcon, this.getX(), this.getY(), this.iconWidth, this.iconHeight); + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, + drawnIcon, + this.getX() + (this.width - this.iconWidth) / 2, + this.getY() + (this.height - this.iconHeight) / 2, + this.iconWidth, + this.iconHeight); } public void setPosition(int x, int y, int z, int size, int iconSize) { diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/text_alignment/center.png b/src/main/resources/assets/glowcase/textures/gui/sprites/text_alignment/center.png new file mode 100644 index 0000000000000000000000000000000000000000..24eb87482e920617994f8261227deb6f9bea40ff GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|oIG6|Lo9le z6C_xB437N=0^zlt69w1x3q~F~cdjqPnNQa0h)0W&QcSa6!+Gr!e#bKzm>JgxoP8;w gC$Vdz0S^O1<}(TQmU*8QfTl5cy85}Sb4q9e06Rk~X8-^I literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/text_alignment/left.png b/src/main/resources/assets/glowcase/textures/gui/sprites/text_alignment/left.png new file mode 100644 index 0000000000000000000000000000000000000000..ae2e9c3f03813ab36397119b77091f314c887a81 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|oIG6|Lo9le z6C_xB437N=0^zlt69w1x3q~F~cdjqPnNQa0h)0W&QVetQgU?KxCKpN_a5%snQf%ZW f&T}_H0%-2aXAgTe~DWM4fHw7zF literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/text_alignment/right.png b/src/main/resources/assets/glowcase/textures/gui/sprites/text_alignment/right.png new file mode 100644 index 0000000000000000000000000000000000000000..91f73123e3de5e070553573b2db73e951c1ef5d1 GIT binary patch literal 132 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|>^xl@Lo9le z6C_xB437N=0^zlt69w1x3q~F~cdjqPnNQa0h)0W&QcSZq)BNcLRgAI^J_~)_YfzG$ bD8ay>`AmZSU+fK0ph*m#u6{1-oD!M Date: Sun, 5 Apr 2026 05:18:55 +0200 Subject: [PATCH 3/8] separate horizontal alignment buttons --- .../block/entity/TextBlockEntity.java | 32 ++++++++-- .../screen/ingame/TextBlockEditScreen.java | 57 +++++++++++++++--- .../block/entity/TextBlockEntityRenderer.java | 7 +++ .../glowcase/packet/C2SEditTextBlock.java | 9 ++- .../sprites/horizontal_alignment/center.png | Bin 0 -> 183 bytes .../gui/sprites/horizontal_alignment/left.png | Bin 0 -> 185 bytes .../sprites/horizontal_alignment/right.png | Bin 0 -> 173 bytes 7 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 src/main/resources/assets/glowcase/textures/gui/sprites/horizontal_alignment/center.png create mode 100644 src/main/resources/assets/glowcase/textures/gui/sprites/horizontal_alignment/left.png create mode 100644 src/main/resources/assets/glowcase/textures/gui/sprites/horizontal_alignment/right.png diff --git a/src/main/java/dev/hephaestus/glowcase/block/entity/TextBlockEntity.java b/src/main/java/dev/hephaestus/glowcase/block/entity/TextBlockEntity.java index 9e19fede..069a2a35 100644 --- a/src/main/java/dev/hephaestus/glowcase/block/entity/TextBlockEntity.java +++ b/src/main/java/dev/hephaestus/glowcase/block/entity/TextBlockEntity.java @@ -6,18 +6,21 @@ import eu.pb4.placeholders.api.ParserContext; import eu.pb4.placeholders.api.parsers.NodeParser; import eu.pb4.placeholders.api.parsers.TagParser; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.chat.Style; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; import net.minecraft.util.StringRepresentable; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; +import java.util.ArrayList; +import java.util.List; + public class TextBlockEntity extends GlowcaseBlockEntity { public static final NodeParser PARSER = TagParser.DEFAULT; @@ -25,6 +28,7 @@ public class TextBlockEntity extends GlowcaseBlockEntity { public List lines = new ArrayList<>(); public TextAlignment textAlignment = TextAlignment.CENTER; + public HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER; public ZOffset zOffset = ZOffset.CENTER; public boolean shadow = true; public float scale = 1F; @@ -47,6 +51,7 @@ protected void saveAdditional(ValueOutput view) { view.putInt("background_color", this.backgroundColor); view.store("text_alignment", TextAlignment.CODEC, this.textAlignment); + view.store("horizontal_alignment", HorizontalAlignment.CODEC, this.horizontalAlignment); view.store("z_offset", ZOffset.CODEC, this.zOffset); view.putBoolean("shadow", this.shadow); view.putFloat("viewDistance", this.viewDistance); @@ -69,6 +74,7 @@ protected void loadAdditional(ValueInput view) { this.backgroundColor = view.getIntOr("background_color", 0); this.shadow = view.getBooleanOr("shadow", true); this.textAlignment = view.read("text_alignment", TextAlignment.CODEC).orElse(TextAlignment.CENTER); + this.horizontalAlignment = view.read("horizontal_alignment", HorizontalAlignment.CODEC).orElse(HorizontalAlignment.CENTER); this.zOffset = view.read("z_offset", ZOffset.CODEC).orElse(ZOffset.CENTER); this.viewDistance = view.getFloatOr("viewDistance", -1); this.lines = new ArrayList<>(view.read("lines", ComponentSerialization.CODEC.listOf()).orElseGet(List::of)); @@ -111,7 +117,13 @@ public void setRawLine(int i, String string) { } public enum TextAlignment implements StringRepresentable { - LEFT, CENTER, CENTER_LEFT, CENTER_RIGHT, RIGHT; + LEFT, + CENTER, + @Deprecated + CENTER_LEFT, + @Deprecated + CENTER_RIGHT, + RIGHT; public static final Codec CODEC = StringRepresentable.fromEnum(TextAlignment::values); @@ -131,4 +143,16 @@ public String getSerializedName() { return name().toLowerCase(); } } + + public enum HorizontalAlignment implements StringRepresentable { + LEFT, CENTER, RIGHT; + + public static final Codec CODEC = StringRepresentable.fromEnum(HorizontalAlignment::values); + public static final StreamCodec STREAM_CODEC = ByteBufCodecs.BYTE.map(index -> TextBlockEntity.HorizontalAlignment.values()[index], textAlignment -> (byte) textAlignment.ordinal()); + + @Override + public String getSerializedName() { + return name().toLowerCase(); + } + } } diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java index d5234c9d..3e8bbf1b 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java @@ -82,14 +82,13 @@ public void init() { this.textBlockEntity.renderDirty = true; }).bounds(middle - 110, 0, 20, 20).build(); - Map textAlignmentButtons = new HashMap<>(); - - Consumer setAlignment = (alignment) -> { + Consumer changeTextAlignment = (alignment) -> { var previous = textBlockEntity.textAlignment; var prevButton = textAlignmentButtons.get(previous); if (prevButton != null) { - prevButton.active = true; // there are a few legacy values without a button + // there are a few deprecated values without a button + prevButton.active = true; } textBlockEntity.textAlignment = alignment; @@ -97,21 +96,21 @@ public void init() { }; var textAlignLeft = IconButtonWidget.builder(Glowcase.id("text_alignment/left"), button -> { - setAlignment.accept(TextBlockEntity.TextAlignment.LEFT); + changeTextAlignment.accept(TextBlockEntity.TextAlignment.LEFT); button.active = false; }) .position(middle - 90 + innerPadding, 0) .size(20, 20, 16, 16) .build(); var textAlignCenter = IconButtonWidget.builder(Glowcase.id("text_alignment/center"), button -> { - setAlignment.accept(TextBlockEntity.TextAlignment.CENTER); + changeTextAlignment.accept(TextBlockEntity.TextAlignment.CENTER); button.active = false; }) .position(middle - 90 + innerPadding + 20, 0) .size(20, 20, 16, 16) .build(); var textAlignRight = IconButtonWidget.builder(Glowcase.id("text_alignment/right"), button -> { - setAlignment.accept(TextBlockEntity.TextAlignment.RIGHT); + changeTextAlignment.accept(TextBlockEntity.TextAlignment.RIGHT); button.active = false; }) .position(middle - 90 + innerPadding + 40, 0) @@ -123,7 +122,8 @@ public void init() { textAlignmentButtons.put(TextBlockEntity.TextAlignment.RIGHT, textAlignRight); var initialTextAlignmentButton = textAlignmentButtons.get(textBlockEntity.textAlignment); - if (initialTextAlignmentButton != null) { // there are a few legacy values without a button + if (initialTextAlignmentButton != null) { + // there are a few deprecated values without a button initialTextAlignmentButton.active = false; } @@ -131,6 +131,47 @@ public void init() { this.addRenderableWidget(textAlignLeft); this.addRenderableWidget(textAlignRight); + + Map horizontalAlignmentButtons = new HashMap<>(); + Consumer changeHorizontalAlignment = (alignment) -> { + var previous = textBlockEntity.horizontalAlignment; + horizontalAlignmentButtons.get(previous).active = true; + + textBlockEntity.horizontalAlignment = alignment; + textBlockEntity.renderDirty = true; + }; + + var horizontalAlignLeft = IconButtonWidget.builder(Glowcase.id("horizontal_alignment/left"), button -> { + changeHorizontalAlignment.accept(TextBlockEntity.HorizontalAlignment.LEFT); + button.active = false; + }) + .position(middle - 90 + 2 * innerPadding + 60, 0) + .size(20, 20, 16, 16) + .build(); + var horizontalAlignCenter = IconButtonWidget.builder(Glowcase.id("horizontal_alignment/center"), button -> { + changeHorizontalAlignment.accept(TextBlockEntity.HorizontalAlignment.CENTER); + button.active = false; + }) + .position(middle - 90 + 2 * innerPadding + 80, 0) + .size(20, 20, 16, 16) + .build(); + var horizontalAlignRight = IconButtonWidget.builder(Glowcase.id("horizontal_alignment/right"), button -> { + changeHorizontalAlignment.accept(TextBlockEntity.HorizontalAlignment.RIGHT); + button.active = false; + }) + .position(middle - 90 + 2 * innerPadding + 100, 0) + .size(20, 20, 16, 16) + .build(); + horizontalAlignmentButtons.put(TextBlockEntity.HorizontalAlignment.LEFT, horizontalAlignLeft); + horizontalAlignmentButtons.put(TextBlockEntity.HorizontalAlignment.CENTER, horizontalAlignCenter); + horizontalAlignmentButtons.put(TextBlockEntity.HorizontalAlignment.RIGHT, horizontalAlignRight); + + horizontalAlignmentButtons.get(textBlockEntity.horizontalAlignment).active = false; + + this.addRenderableWidget(horizontalAlignCenter); + this.addRenderableWidget(horizontalAlignLeft); + this.addRenderableWidget(horizontalAlignRight); + this.shadowToggle = Checkbox.builder(Component.translatable("gui.glowcase.shadow"), this.font) .selected(this.textBlockEntity.shadow) .onValueChange((widget, checked) -> { diff --git a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java index e3831200..4a774259 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java +++ b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java @@ -38,6 +38,7 @@ public static class TextRenderState extends BlockEntityRenderState { public int rotation16; public List lines = List.of(); public TextBlockEntity.TextAlignment textAlignment; + public TextBlockEntity.HorizontalAlignment horizontalAlignment; public TextBlockEntity.ZOffset zOffset; public boolean shadow; public float scale = 1; @@ -64,6 +65,7 @@ public void extractRenderState(TextBlockEntity blockEntity, TextRenderState stat state.lines = blockEntity.lines.stream().map(Component::getVisualOrderText).toList(); state.textAlignment = blockEntity.textAlignment; + state.horizontalAlignment = blockEntity.horizontalAlignment; state.zOffset = blockEntity.zOffset; state.shadow = blockEntity.shadow; state.scale = blockEntity.scale; @@ -118,6 +120,11 @@ public void submitForRendering(TextRenderState state, PoseStack poseStack, Submi poseStack.scale(0.1F * state.scale, 0.1F * state.scale, 0.1F * state.scale); poseStack.translate(0, -(state.lines.size() * this.font.lineHeight) / 2D, 0D); + switch (state.horizontalAlignment) { + case LEFT -> poseStack.translate(-maxWidth / 2F, 0, 0); + case RIGHT -> poseStack.translate(maxWidth / 2F, 0, 0); + } + for (int i = 0; i < state.lines.size(); ++i) { var line = state.lines.get(i); diff --git a/src/main/java/dev/hephaestus/glowcase/packet/C2SEditTextBlock.java b/src/main/java/dev/hephaestus/glowcase/packet/C2SEditTextBlock.java index 79ea677b..c1c1f976 100644 --- a/src/main/java/dev/hephaestus/glowcase/packet/C2SEditTextBlock.java +++ b/src/main/java/dev/hephaestus/glowcase/packet/C2SEditTextBlock.java @@ -14,19 +14,23 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.entity.BlockEntity; -public record C2SEditTextBlock(BlockPos pos, TextBlockEntity.TextAlignment alignment, TextBlockEntity.ZOffset offset, +public record C2SEditTextBlock(BlockPos pos, + TextBlockEntity.TextAlignment alignment, + TextBlockEntity.HorizontalAlignment horizontalAlignment, + TextBlockEntity.ZOffset offset, TextBlockValues values) implements C2SEditBlockEntity { public static final Type ID = new Type<>(Glowcase.id("channel.text_block")); public static final StreamCodec PACKET_CODEC = StreamCodec.composite( BlockPos.STREAM_CODEC, C2SEditTextBlock::pos, ByteBufCodecs.BYTE.map(index -> TextBlockEntity.TextAlignment.values()[index], textAlignment -> (byte) textAlignment.ordinal()), C2SEditTextBlock::alignment, + TextBlockEntity.HorizontalAlignment.STREAM_CODEC, C2SEditTextBlock::horizontalAlignment, ByteBufCodecs.BYTE.map(index -> TextBlockEntity.ZOffset.values()[index], zOffset -> (byte) zOffset.ordinal()), C2SEditTextBlock::offset, TextBlockValues.PACKET_CODEC, C2SEditTextBlock::values, C2SEditTextBlock::new ); public static C2SEditTextBlock of(TextBlockEntity be) { - return new C2SEditTextBlock(be.getBlockPos(), be.textAlignment, be.zOffset, new TextBlockValues(be.shadow, be.scale, be.backgroundColor, be.color, be.lines, be.viewDistance)); + return new C2SEditTextBlock(be.getBlockPos(), be.textAlignment, be.horizontalAlignment, be.zOffset, new TextBlockValues(be.shadow, be.scale, be.backgroundColor, be.color, be.lines, be.viewDistance)); } @Override @@ -42,6 +46,7 @@ public void receive(ServerLevel world, BlockEntity blockEntity) { be.scale = this.values().scale(); be.lines = this.values().lines(); be.textAlignment = this.alignment(); + be.horizontalAlignment = this.horizontalAlignment(); be.backgroundColor = this.values().backgroundColor(); be.color = this.values().color(); be.zOffset = this.offset(); diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/horizontal_alignment/center.png b/src/main/resources/assets/glowcase/textures/gui/sprites/horizontal_alignment/center.png new file mode 100644 index 0000000000000000000000000000000000000000..fca3d3a23ff62eae2923879a241294af7cf1699c GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|3O!vMLo9mF zPCUqaK!JxjcV%(w&9g}_SFd1ndC zof#Qwnp+PoTj+2p;lvd#k4tBqB7&;IBFeje@hhb9gz0}{Sk4;JEMq3a<>%PeB=>~x fNXXywcb1E*Nf;#=U-&2uw4A}y)z4*}Q$iB}-(y1w literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/horizontal_alignment/left.png b/src/main/resources/assets/glowcase/textures/gui/sprites/horizontal_alignment/left.png new file mode 100644 index 0000000000000000000000000000000000000000..a8377a7507e701a1afacf7af9aa24b61af2eac16 GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|ialK%Lo9l) zPO#=Xpupq&cGdHy&FbRb)5Ti-Swda~_zN`U+CC^ZGXDJiu>F}YZDwKAY8law!0|Xz*@0-HO^bF(lG~J~o`6k95 zpQ5yPGcoL77usiTu-V{-GKWXdrILk?2ETYGJ&e4py6?0!*M#Xa^0w_i!hZVT+8FC< V-`{_?f+K+6CC literal 0 HcmV?d00001 From bab48dca3bc491094b3716fae4038a6413e54f08 Mon Sep 17 00:00:00 2001 From: reoseah Date: Sun, 5 Apr 2026 06:23:35 +0200 Subject: [PATCH 4/8] experiment with a slider for scale --- .../screen/ingame/TextBlockEditScreen.java | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java index 3e8bbf1b..7ce7b8b9 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java @@ -10,10 +10,9 @@ import eu.pb4.placeholders.api.parsers.tag.TagRegistry; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.*; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Checkbox; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.font.TextFieldHelper; import net.minecraft.client.input.CharacterEvent; @@ -42,7 +41,6 @@ public class TextBlockEditScreen extends TextEditorScreen { private int currentRow; private long ticksSinceOpened = 0; private ColorPickerWidget colorPickerWidget; - // private Button changeAlignment; private EditBox colorEntryWidget; private EditBox backgroundColorEntryWidget; private Color colorEntryPreColorPicker; //used for color picker cancel button @@ -72,15 +70,31 @@ public void init() { int middle = width / 2; - Button decreaseSize = Button.builder(Component.literal("-"), action -> { - this.textBlockEntity.scale = Math.max(0, this.textBlockEntity.scale - (minecraft.hasShiftDown() ? 1F : 0.125F)); - this.textBlockEntity.renderDirty = true; - }).bounds(middle - 130, 0, 20, 20).build(); - Button increaseSize = Button.builder(Component.literal("+"), action -> { - this.textBlockEntity.scale += minecraft.hasShiftDown() ? 1F : 0.125F; - this.textBlockEntity.renderDirty = true; - }).bounds(middle - 110, 0, 20, 20).build(); + final float minScale = 0.125F; + final float maxScale = 16; + double initialScale = (textBlockEntity.scale - minScale) / (maxScale - minScale); + + var scaleSlider = new AbstractSliderButton( + middle - 203, + 0, + 113, + 20, + Component.translatable("gui.glowcase.scale_value", textBlockEntity.scale), + initialScale + ) { + @Override + protected void updateMessage() { + this.setMessage(Component.translatable("gui.glowcase.scale_value", textBlockEntity.scale)); + } + + @Override + protected void applyValue() { + textBlockEntity.scale = (float) Math.round(Mth.lerp(this.value, minScale, maxScale) * 8F) / 8F; + textBlockEntity.renderDirty = true; + } + }; + this.addRenderableWidget(scaleSlider); Map textAlignmentButtons = new HashMap<>(); Consumer changeTextAlignment = (alignment) -> { @@ -131,7 +145,6 @@ public void init() { this.addRenderableWidget(textAlignLeft); this.addRenderableWidget(textAlignRight); - Map horizontalAlignmentButtons = new HashMap<>(); Consumer changeHorizontalAlignment = (alignment) -> { var previous = textBlockEntity.horizontalAlignment; @@ -237,9 +250,8 @@ public void init() { this.viewDistanceHelpButton.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.screen.text_edit.view_distance"))); this.addRenderableWidget(colorPickerWidget); - this.addRenderableWidget(increaseSize); - this.addRenderableWidget(decreaseSize); -// this.addRenderableWidget(this.changeAlignment); +// this.addRenderableWidget(increaseSize); +// this.addRenderableWidget(decreaseSize); this.addRenderableWidget(this.shadowToggle); this.addRenderableWidget(this.zOffsetToggle); this.addRenderableWidget(this.colorEntryWidget); @@ -336,7 +348,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo } graphics.pose().popMatrix(); - graphics.text(minecraft.font, Component.translatable("gui.glowcase.scale_value", this.textBlockEntity.scale), width / 2 - 203, 7, 0xFFFFFFFF); +// graphics.text(minecraft.font, Component.translatable("gui.glowcase.scale_value", this.textBlockEntity.scale), width / 2 - 203, 7, 0xFFFFFFFF); colorPickerWidget.extractRenderState(graphics, mouseX, mouseY, delta); } From 25a7ffe0737cc06a331cb39d111ebe737aa36007 Mon Sep 17 00:00:00 2001 From: reoseah Date: Sun, 5 Apr 2026 08:13:15 +0200 Subject: [PATCH 5/8] second screen for options --- .../screen/ingame/TextBlockEditScreen.java | 60 +++++++------- .../screen/ingame/TextBlockOptionsScreen.java | 73 ++++++++++++++++++ .../resources/assets/glowcase/lang/en_us.json | 4 +- .../textures/gui/sprites/three_dots.png | Bin 0 -> 117 bytes 4 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java create mode 100644 src/main/resources/assets/glowcase/textures/gui/sprites/three_dots.png diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java index 7ce7b8b9..29fe4761 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java @@ -70,30 +70,7 @@ public void init() { int middle = width / 2; - - final float minScale = 0.125F; - final float maxScale = 16; - double initialScale = (textBlockEntity.scale - minScale) / (maxScale - minScale); - - var scaleSlider = new AbstractSliderButton( - middle - 203, - 0, - 113, - 20, - Component.translatable("gui.glowcase.scale_value", textBlockEntity.scale), - initialScale - ) { - @Override - protected void updateMessage() { - this.setMessage(Component.translatable("gui.glowcase.scale_value", textBlockEntity.scale)); - } - - @Override - protected void applyValue() { - textBlockEntity.scale = (float) Math.round(Mth.lerp(this.value, minScale, maxScale) * 8F) / 8F; - textBlockEntity.renderDirty = true; - } - }; + var scaleSlider = new TextScaleSliderWidget(textBlockEntity, middle - 203, 0, 113, 20); this.addRenderableWidget(scaleSlider); Map textAlignmentButtons = new HashMap<>(); @@ -185,6 +162,15 @@ protected void applyValue() { this.addRenderableWidget(horizontalAlignLeft); this.addRenderableWidget(horizontalAlignRight); + var moreOptionsButton = IconButtonWidget.builder(Glowcase.id("three_dots"), button -> { + var optionsScreen = new TextBlockOptionsScreen(this, textBlockEntity); + Minecraft.getInstance().setScreen(optionsScreen); + }) + .position(middle - 90 + 3 * innerPadding + 120, 0) + .size(32, 20, 16, 16) + .build(); + this.addRenderableWidget(moreOptionsButton); + this.shadowToggle = Checkbox.builder(Component.translatable("gui.glowcase.shadow"), this.font) .selected(this.textBlockEntity.shadow) .onValueChange((widget, checked) -> { @@ -250,8 +236,6 @@ protected void applyValue() { this.viewDistanceHelpButton.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.screen.text_edit.view_distance"))); this.addRenderableWidget(colorPickerWidget); -// this.addRenderableWidget(increaseSize); -// this.addRenderableWidget(decreaseSize); this.addRenderableWidget(this.shadowToggle); this.addRenderableWidget(this.zOffsetToggle); this.addRenderableWidget(this.colorEntryWidget); @@ -602,4 +586,28 @@ public void toggleColorPicker(boolean active) { TextFieldHelper getSelectionManager() { return this.selectionManager; } + + public static class TextScaleSliderWidget extends AbstractSliderButton { + private static final float MIN_SCALE = 0.125F; + private static final float MAX_SCALE = 16; + + private final TextBlockEntity entity; + + public TextScaleSliderWidget(TextBlockEntity entity, int x, int y, int width, int height) { + var initialValue = (entity.scale - MIN_SCALE) / (MAX_SCALE - MIN_SCALE); + super(x, y, width, height, Component.translatable("gui.glowcase.scale_value", entity.scale), initialValue); + this.entity = entity; + } + + @Override + protected void updateMessage() { + this.setMessage(Component.translatable("gui.glowcase.scale_value", entity.scale)); + } + + @Override + protected void applyValue() { + entity.scale = (float) Math.round(Mth.lerp(this.value, MIN_SCALE, MAX_SCALE) * 8F) / 8F; + entity.renderDirty = true; + } + } } diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java new file mode 100644 index 00000000..288bb3d0 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java @@ -0,0 +1,73 @@ +package dev.hephaestus.glowcase.client.gui.screen.ingame; + +import dev.hephaestus.glowcase.block.entity.TextBlockEntity; +import dev.hephaestus.glowcase.packet.C2SEditTextBlock; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.AbstractSliderButton; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.Mth; + +public class TextBlockOptionsScreen extends GlowcaseScreen { + private final Screen returnScreen; + private final TextBlockEntity entity; + + public TextBlockOptionsScreen(Screen returnScreen, TextBlockEntity entity) { + this.returnScreen = returnScreen; + this.entity = entity; + } + + @Override + public void onClose() { + super.onClose(); + C2SEditTextBlock.of(this.entity).send(); + Minecraft.getInstance().setScreen(this.returnScreen); + } + + @Override + public void init() { + super.init(); + + int middle = this.width / 2; + + this.addRenderableWidget(new TextBlockEditScreen.TextScaleSliderWidget(this.entity, middle - 120 - 2, 0, 120, 20)); + this.addRenderableWidget(new TextRenderDistanceSliderWidget(this.entity, middle + 2, 0, 120, 20)); + } + + private static class TextRenderDistanceSliderWidget extends AbstractSliderButton { + private static final float INFINITE_VALUE = -1; + private static final float MIN_VALUE = 1; + private static final float MAX_VALUE = 256; + + private final TextBlockEntity entity; + + public TextRenderDistanceSliderWidget(TextBlockEntity entity, int x, int y, int width, int height) { + double initialValue = entity.viewDistance == INFINITE_VALUE ? 1 : (entity.viewDistance - MIN_VALUE) / (MAX_VALUE - MIN_VALUE); + super(x, y, width, height, createMessage(entity), initialValue); + + this.entity = entity; + } + + private static MutableComponent createMessage(TextBlockEntity entity) { + return entity.viewDistance == INFINITE_VALUE + ? Component.translatable("gui.glowcase.render_distance_value.infinite") + : Component.translatable("gui.glowcase.render_distance_value", entity.viewDistance); + } + + @Override + protected void updateMessage() { + this.setMessage(createMessage(this.entity)); + } + + @Override + protected void applyValue() { + if (this.value == 1) { + this.entity.viewDistance = INFINITE_VALUE; + } else { + this.entity.viewDistance = (float) Math.round(Mth.lerp(this.value, MIN_VALUE, MAX_VALUE)); + } + this.entity.renderDirty = true; + } + } +} diff --git a/src/main/resources/assets/glowcase/lang/en_us.json b/src/main/resources/assets/glowcase/lang/en_us.json index eb0c4f5c..d1ea0bb2 100644 --- a/src/main/resources/assets/glowcase/lang/en_us.json +++ b/src/main/resources/assets/glowcase/lang/en_us.json @@ -171,5 +171,7 @@ "gui.glowcase.config_link.missing.modmenu": "Mod Menu isn't installed.", "gui.glowcase.config_link.missing.mod_screen": "No such mod screen: %s", "gui.glowcase.config_link.missing.mod": "No such mod: %s", - "gui.glowcase.config_link.missing.link": "Not implemented: %s" + "gui.glowcase.config_link.missing.link": "Not implemented: %s", + "gui.glowcase.render_distance_value": "Render Distance: %d", + "gui.glowcase.render_distance_value.infinite": "Render Distance: Infinite" } diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/three_dots.png b/src/main/resources/assets/glowcase/textures/gui/sprites/three_dots.png new file mode 100644 index 0000000000000000000000000000000000000000..bc8a81aa83002a479683bcadb7407b236026a7c3 GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|^gUf1Lo9le z6C_v<^ZsdPO8UtI1U@%-Hf`Sg+h1osYh=^Ji4z4QCkfPx@Q5)mypvK2RQ_JH7O0iM M)78&qol`;+0Clw>djJ3c literal 0 HcmV?d00001 From e458caece41600f3d90971526ff073d553556ac4 Mon Sep 17 00:00:00 2001 From: reoseah Date: Sun, 5 Apr 2026 23:58:21 +0200 Subject: [PATCH 6/8] use vanilla `HeaderAndFooterLayout` and `ContainerObjectSelectionList` for text options --- .../gui/screen/ingame/GlowcaseScreen.java | 4 + .../screen/ingame/TextBlockOptionsScreen.java | 74 +++++++++++++++++-- .../resources/assets/glowcase/lang/en_us.json | 1 + 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/GlowcaseScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/GlowcaseScreen.java index c00f0d8c..0f541f4b 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/GlowcaseScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/GlowcaseScreen.java @@ -9,6 +9,10 @@ protected GlowcaseScreen() { super(Component.empty()); } + protected GlowcaseScreen(Component title) { + super(title); + } + @Override public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float deltaTicks) { this.extractTransparentBackground(graphics); diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java index 288bb3d0..6682b250 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java @@ -3,17 +3,31 @@ import dev.hephaestus.glowcase.block.entity.TextBlockEntity; import dev.hephaestus.glowcase.packet.C2SEditTextBlock; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractSliderButton; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ContainerObjectSelectionList; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.layouts.HeaderAndFooterLayout; +import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.util.Mth; -public class TextBlockOptionsScreen extends GlowcaseScreen { +import java.util.List; + +public class TextBlockOptionsScreen extends Screen { private final Screen returnScreen; private final TextBlockEntity entity; + public final HeaderAndFooterLayout layout = new HeaderAndFooterLayout(this); + public TextOptionList options; + public TextBlockOptionsScreen(Screen returnScreen, TextBlockEntity entity) { + super(Component.translatable("gui.glowcase.text_options")); this.returnScreen = returnScreen; this.entity = entity; } @@ -29,10 +43,58 @@ public void onClose() { public void init() { super.init(); - int middle = this.width / 2; + this.layout.addTitleHeader(this.title, this.font); + this.layout.addToFooter(Button.builder(CommonComponents.GUI_DONE, _ -> this.onClose()).width(200).build()); + + this.options = this.layout.addToContents(new TextOptionList(this.minecraft, this.width, this.layout.getContentHeight(), this.layout.getHeaderHeight())); + this.options.add(new TextBlockEditScreen.TextScaleSliderWidget(this.entity, -1, -1, Button.DEFAULT_WIDTH, Button.DEFAULT_HEIGHT)); + this.options.add(new TextRenderDistanceSliderWidget(this.entity, -1, -1)); + + this.layout.visitWidgets(this::addRenderableWidget); + this.layout.arrangeElements(); + } + + public static class TextOptionList extends ContainerObjectSelectionList { + public TextOptionList(Minecraft minecraft, int width, int height, int y) { + super(minecraft, width, height, y, Button.DEFAULT_HEIGHT + 5); + } + + public void add(AbstractWidget widget) { + this.addEntry(new WidgetEntry(widget)); + } + + @Override + public int getRowWidth() { + return 310; + } + + public static abstract class Entry extends ContainerObjectSelectionList.Entry { + + } + + public static class WidgetEntry extends Entry { + protected final AbstractWidget widget; + + public WidgetEntry(AbstractWidget widget) { + this.widget = widget; + } + + @Override + public void extractContent(GuiGraphicsExtractor graphics, int mouseX, int mouseY, boolean hovered, float a) { + this.widget.setPosition(this.getContentX(), this.getContentY()); + this.widget.extractRenderState(graphics, mouseX, mouseY, a); + } + + @Override + public List narratables() { + return List.of(this.widget); + } - this.addRenderableWidget(new TextBlockEditScreen.TextScaleSliderWidget(this.entity, middle - 120 - 2, 0, 120, 20)); - this.addRenderableWidget(new TextRenderDistanceSliderWidget(this.entity, middle + 2, 0, 120, 20)); + @Override + public List children() { + return List.of(this.widget); + } + } } private static class TextRenderDistanceSliderWidget extends AbstractSliderButton { @@ -42,9 +104,9 @@ private static class TextRenderDistanceSliderWidget extends AbstractSliderButton private final TextBlockEntity entity; - public TextRenderDistanceSliderWidget(TextBlockEntity entity, int x, int y, int width, int height) { + public TextRenderDistanceSliderWidget(TextBlockEntity entity, int x, int y) { double initialValue = entity.viewDistance == INFINITE_VALUE ? 1 : (entity.viewDistance - MIN_VALUE) / (MAX_VALUE - MIN_VALUE); - super(x, y, width, height, createMessage(entity), initialValue); + super(x, y, Button.DEFAULT_WIDTH, Button.DEFAULT_HEIGHT, createMessage(entity), initialValue); this.entity = entity; } diff --git a/src/main/resources/assets/glowcase/lang/en_us.json b/src/main/resources/assets/glowcase/lang/en_us.json index d1ea0bb2..454c4119 100644 --- a/src/main/resources/assets/glowcase/lang/en_us.json +++ b/src/main/resources/assets/glowcase/lang/en_us.json @@ -172,6 +172,7 @@ "gui.glowcase.config_link.missing.mod_screen": "No such mod screen: %s", "gui.glowcase.config_link.missing.mod": "No such mod: %s", "gui.glowcase.config_link.missing.link": "Not implemented: %s", + "gui.glowcase.text_options": "Text Options", "gui.glowcase.render_distance_value": "Render Distance: %d", "gui.glowcase.render_distance_value.infinite": "Render Distance: Infinite" } From 9e041a4c301578f6b67c2ed64f9c2f6ec77c761a Mon Sep 17 00:00:00 2001 From: reoseah Date: Mon, 6 Apr 2026 02:11:12 +0200 Subject: [PATCH 7/8] color and background color fields in text properties screen --- .../screen/ingame/TextBlockOptionsScreen.java | 158 +++++++++++++++++- .../resources/assets/glowcase/lang/en_us.json | 6 +- 2 files changed, 156 insertions(+), 8 deletions(-) diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java index 6682b250..28016505 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java @@ -1,13 +1,11 @@ package dev.hephaestus.glowcase.client.gui.screen.ingame; import dev.hephaestus.glowcase.block.entity.TextBlockEntity; +import dev.hephaestus.glowcase.client.util.ColorUtil; import dev.hephaestus.glowcase.packet.C2SEditTextBlock; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.components.AbstractSliderButton; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.ContainerObjectSelectionList; +import net.minecraft.client.gui.components.*; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.layouts.HeaderAndFooterLayout; import net.minecraft.client.gui.narration.NarratableEntry; @@ -47,8 +45,92 @@ public void init() { this.layout.addToFooter(Button.builder(CommonComponents.GUI_DONE, _ -> this.onClose()).width(200).build()); this.options = this.layout.addToContents(new TextOptionList(this.minecraft, this.width, this.layout.getContentHeight(), this.layout.getHeaderHeight())); - this.options.add(new TextBlockEditScreen.TextScaleSliderWidget(this.entity, -1, -1, Button.DEFAULT_WIDTH, Button.DEFAULT_HEIGHT)); - this.options.add(new TextRenderDistanceSliderWidget(this.entity, -1, -1)); + this.options.add( + new TextBlockEditScreen.TextScaleSliderWidget(this.entity, -1, -1, Button.DEFAULT_WIDTH, Button.DEFAULT_HEIGHT), + new TextRenderDistanceSliderWidget(this.entity, -1, -1) + ); + this.options.add( + CycleButton.builder( + alignment -> Component.literal(alignment.toString()), + entity.horizontalAlignment + ) + .withValues(TextBlockEntity.HorizontalAlignment.values()) + .create( + Component.translatable("gui.glowcase.x_offset_label"), + (_, alignment) -> { + entity.horizontalAlignment = alignment; + entity.renderDirty = true; + } + ), + CycleButton.builder( + offset -> Component.literal(offset.toString()), + entity.zOffset + ) + .withValues(TextBlockEntity.ZOffset.values()) + .create( + Component.translatable("gui.glowcase.z_offset_label"), + (_, offset) -> { + entity.zOffset = offset; + entity.renderDirty = true; + } + ) + ); + this.options.add( + CycleButton.builder( + alignment -> Component.literal(alignment.toString()), + entity.textAlignment + ) + .withValues( + // not `.values()` to not have CENTER_LEFT or CENTER_RIGHT, unless they get removed + TextBlockEntity.TextAlignment.CENTER, + TextBlockEntity.TextAlignment.LEFT, + TextBlockEntity.TextAlignment.RIGHT + ) + .create( + Component.translatable("gui.glowcase.text_alignment"), + (_, alignment) -> { + entity.textAlignment = alignment; + entity.renderDirty = true; + } + ), + CycleButton.onOffBuilder(entity.shadow).create( + Component.translatable("gui.glowcase.text_shadow"), + (_, shadow) -> { + entity.shadow = shadow; + entity.renderDirty = true; + } + ) + ); + + this.options.addHeader(Component.translatable("gui.glowcase.color")); + var colorEditBox = new EditBox( + this.font, + Button.DEFAULT_WIDTH, + Button.DEFAULT_HEIGHT, + Component.translatable("gui.glowcase.color") + ); + colorEditBox.setValue(ColorUtil.toAlphaHex(this.entity.color)); + colorEditBox.setResponder(string -> ColorUtil.parse(string, entity.color) + .ifSuccess(newColor -> { + entity.color = newColor; + entity.renderDirty = true; + })); + this.options.add(colorEditBox); + + this.options.addHeader(Component.translatable("gui.glowcase.background_color")); + var backgroundEditBox = new EditBox( + this.font, + Button.DEFAULT_WIDTH, + Button.DEFAULT_HEIGHT, + Component.translatable("gui.glowcase.background_color") + ); + backgroundEditBox.setValue(ColorUtil.toAlphaHex(this.entity.backgroundColor)); + backgroundEditBox.setResponder(string -> ColorUtil.parse(string, entity.backgroundColor) + .ifSuccess(newColor -> { + entity.backgroundColor = newColor; + entity.renderDirty = true; + })); + this.options.add(backgroundEditBox); this.layout.visitWidgets(this::addRenderableWidget); this.layout.arrangeElements(); @@ -59,10 +141,20 @@ public TextOptionList(Minecraft minecraft, int width, int height, int y) { super(minecraft, width, height, y, Button.DEFAULT_HEIGHT + 5); } + public void addHeader(Component text) { + int lineHeight = this.minecraft.font.lineHeight; + int paddingTop = this.children().isEmpty() ? 0 : lineHeight * 2; + this.addEntry(new HeaderEntry(new StringWidget(text, this.minecraft.font), paddingTop), paddingTop + lineHeight + 4); + } + public void add(AbstractWidget widget) { this.addEntry(new WidgetEntry(widget)); } + public void add(AbstractWidget leftWidget, AbstractWidget rightWidget) { + this.addEntry(new TwoWidgetsEntry(leftWidget, rightWidget)); + } + @Override public int getRowWidth() { return 310; @@ -72,6 +164,32 @@ public static abstract class Entry extends ContainerObjectSelectionList.Entry narratables() { + return List.of(this.widget); + } + + @Override + public List children() { + return List.of(this.widget); + } + } + public static class WidgetEntry extends Entry { protected final AbstractWidget widget; @@ -95,6 +213,34 @@ public List children() { return List.of(this.widget); } } + + public static class TwoWidgetsEntry extends Entry { + protected final AbstractWidget leftWidget; + protected final AbstractWidget rightWidget; + + public TwoWidgetsEntry(AbstractWidget leftWidget, AbstractWidget rightWidget) { + this.leftWidget = leftWidget; + this.rightWidget = rightWidget; + } + + @Override + public void extractContent(GuiGraphicsExtractor graphics, int mouseX, int mouseY, boolean hovered, float a) { + this.leftWidget.setPosition(this.getContentX(), this.getContentY()); + this.leftWidget.extractRenderState(graphics, mouseX, mouseY, a); + this.rightWidget.setPosition(this.getContentX() + 160, this.getContentY()); + this.rightWidget.extractRenderState(graphics, mouseX, mouseY, a); + } + + @Override + public List narratables() { + return List.of(this.leftWidget, this.rightWidget); + } + + @Override + public List children() { + return List.of(this.leftWidget, this.rightWidget); + } + } } private static class TextRenderDistanceSliderWidget extends AbstractSliderButton { diff --git a/src/main/resources/assets/glowcase/lang/en_us.json b/src/main/resources/assets/glowcase/lang/en_us.json index 454c4119..f02ca127 100644 --- a/src/main/resources/assets/glowcase/lang/en_us.json +++ b/src/main/resources/assets/glowcase/lang/en_us.json @@ -172,7 +172,9 @@ "gui.glowcase.config_link.missing.mod_screen": "No such mod screen: %s", "gui.glowcase.config_link.missing.mod": "No such mod: %s", "gui.glowcase.config_link.missing.link": "Not implemented: %s", - "gui.glowcase.text_options": "Text Options", + "gui.glowcase.text_options": "Text Block Properties", "gui.glowcase.render_distance_value": "Render Distance: %d", - "gui.glowcase.render_distance_value.infinite": "Render Distance: Infinite" + "gui.glowcase.render_distance_value.infinite": "Render Distance: Infinite", + "gui.glowcase.text_alignment": "Text Alignment", + "gui.glowcase.text_shadow": "Text Shadow" } From 10dc9169a2c743b0984e4afc5b19b39e4dff9bf2 Mon Sep 17 00:00:00 2001 From: reoseah Date: Mon, 6 Apr 2026 02:35:16 +0200 Subject: [PATCH 8/8] remove widgets from text edit screen and compact the rest into one row --- .../screen/ingame/TextBlockEditScreen.java | 190 ++---------------- .../resources/assets/glowcase/lang/en_us.json | 3 +- 2 files changed, 20 insertions(+), 173 deletions(-) diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java index 29fe4761..48e8f931 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java @@ -1,18 +1,16 @@ package dev.hephaestus.glowcase.client.gui.screen.ingame; -import com.google.common.primitives.Floats; -import dev.hephaestus.glowcase.Glowcase; import dev.hephaestus.glowcase.block.entity.TextBlockEntity; import dev.hephaestus.glowcase.client.gui.widget.ingame.ColorPickerWidget; -import dev.hephaestus.glowcase.client.gui.widget.ingame.IconButtonWidget; import dev.hephaestus.glowcase.client.util.ColorUtil; import dev.hephaestus.glowcase.packet.C2SEditTextBlock; import eu.pb4.placeholders.api.parsers.tag.TagRegistry; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.components.*; +import net.minecraft.client.gui.components.AbstractSliderButton; import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.Checkbox; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.font.TextFieldHelper; import net.minecraft.client.input.CharacterEvent; @@ -23,10 +21,7 @@ import org.lwjgl.glfw.GLFW; import java.awt.*; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.function.Consumer; //TODO: multi-character selection at some point? it may be a bit complex but it'd be nice public class TextBlockEditScreen extends TextEditorScreen { @@ -34,21 +29,14 @@ public class TextBlockEditScreen extends TextEditorScreen { private final TextBlockEntity textBlockEntity; private List textWidgets; - private List colorListeners; private TextFieldHelper selectionManager; + private EditBox colorEntryWidget; private int currentRow; private long ticksSinceOpened = 0; private ColorPickerWidget colorPickerWidget; - private EditBox colorEntryWidget; - private EditBox backgroundColorEntryWidget; private Color colorEntryPreColorPicker; //used for color picker cancel button - private Button zOffsetToggle; - private Checkbox shadowToggle; - - private EditBox viewDistanceField; - private Button viewDistanceHelpButton; public TextBlockEditScreen(TextBlockEntity textBlockEntity) { this.textBlockEntity = textBlockEntity; @@ -73,117 +61,21 @@ public void init() { var scaleSlider = new TextScaleSliderWidget(textBlockEntity, middle - 203, 0, 113, 20); this.addRenderableWidget(scaleSlider); - Map textAlignmentButtons = new HashMap<>(); - Consumer changeTextAlignment = (alignment) -> { - var previous = textBlockEntity.textAlignment; - var prevButton = textAlignmentButtons.get(previous); - if (prevButton != null) { - // there are a few deprecated values without a button - prevButton.active = true; - } - - textBlockEntity.textAlignment = alignment; - textBlockEntity.renderDirty = true; - }; - - var textAlignLeft = IconButtonWidget.builder(Glowcase.id("text_alignment/left"), button -> { - changeTextAlignment.accept(TextBlockEntity.TextAlignment.LEFT); - button.active = false; - }) - .position(middle - 90 + innerPadding, 0) - .size(20, 20, 16, 16) - .build(); - var textAlignCenter = IconButtonWidget.builder(Glowcase.id("text_alignment/center"), button -> { - changeTextAlignment.accept(TextBlockEntity.TextAlignment.CENTER); - button.active = false; - }) - .position(middle - 90 + innerPadding + 20, 0) - .size(20, 20, 16, 16) - .build(); - var textAlignRight = IconButtonWidget.builder(Glowcase.id("text_alignment/right"), button -> { - changeTextAlignment.accept(TextBlockEntity.TextAlignment.RIGHT); - button.active = false; - }) - .position(middle - 90 + innerPadding + 40, 0) - .size(20, 20, 16, 16) - .build(); - - textAlignmentButtons.put(TextBlockEntity.TextAlignment.LEFT, textAlignLeft); - textAlignmentButtons.put(TextBlockEntity.TextAlignment.CENTER, textAlignCenter); - textAlignmentButtons.put(TextBlockEntity.TextAlignment.RIGHT, textAlignRight); - - var initialTextAlignmentButton = textAlignmentButtons.get(textBlockEntity.textAlignment); - if (initialTextAlignmentButton != null) { - // there are a few deprecated values without a button - initialTextAlignmentButton.active = false; - } - - this.addRenderableWidget(textAlignCenter); - this.addRenderableWidget(textAlignLeft); - this.addRenderableWidget(textAlignRight); - - Map horizontalAlignmentButtons = new HashMap<>(); - Consumer changeHorizontalAlignment = (alignment) -> { - var previous = textBlockEntity.horizontalAlignment; - horizontalAlignmentButtons.get(previous).active = true; - - textBlockEntity.horizontalAlignment = alignment; - textBlockEntity.renderDirty = true; - }; - - var horizontalAlignLeft = IconButtonWidget.builder(Glowcase.id("horizontal_alignment/left"), button -> { - changeHorizontalAlignment.accept(TextBlockEntity.HorizontalAlignment.LEFT); - button.active = false; - }) - .position(middle - 90 + 2 * innerPadding + 60, 0) - .size(20, 20, 16, 16) - .build(); - var horizontalAlignCenter = IconButtonWidget.builder(Glowcase.id("horizontal_alignment/center"), button -> { - changeHorizontalAlignment.accept(TextBlockEntity.HorizontalAlignment.CENTER); - button.active = false; - }) - .position(middle - 90 + 2 * innerPadding + 80, 0) - .size(20, 20, 16, 16) - .build(); - var horizontalAlignRight = IconButtonWidget.builder(Glowcase.id("horizontal_alignment/right"), button -> { - changeHorizontalAlignment.accept(TextBlockEntity.HorizontalAlignment.RIGHT); - button.active = false; - }) - .position(middle - 90 + 2 * innerPadding + 100, 0) - .size(20, 20, 16, 16) - .build(); - horizontalAlignmentButtons.put(TextBlockEntity.HorizontalAlignment.LEFT, horizontalAlignLeft); - horizontalAlignmentButtons.put(TextBlockEntity.HorizontalAlignment.CENTER, horizontalAlignCenter); - horizontalAlignmentButtons.put(TextBlockEntity.HorizontalAlignment.RIGHT, horizontalAlignRight); - - horizontalAlignmentButtons.get(textBlockEntity.horizontalAlignment).active = false; - - this.addRenderableWidget(horizontalAlignCenter); - this.addRenderableWidget(horizontalAlignLeft); - this.addRenderableWidget(horizontalAlignRight); - - var moreOptionsButton = IconButtonWidget.builder(Glowcase.id("three_dots"), button -> { - var optionsScreen = new TextBlockOptionsScreen(this, textBlockEntity); - Minecraft.getInstance().setScreen(optionsScreen); - }) - .position(middle - 90 + 3 * innerPadding + 120, 0) - .size(32, 20, 16, 16) + var moreOptionsButton = Button.builder( + Component.translatable("gui.glowcase.more"), + button -> { + var optionsScreen = new TextBlockOptionsScreen(this, textBlockEntity); + Minecraft.getInstance().setScreen(optionsScreen); + }) + .bounds(middle + 124, 0, 80, 20) .build(); this.addRenderableWidget(moreOptionsButton); - this.shadowToggle = Checkbox.builder(Component.translatable("gui.glowcase.shadow"), this.font) - .selected(this.textBlockEntity.shadow) - .onValueChange((widget, checked) -> { - this.textBlockEntity.shadow = checked; - this.textBlockEntity.renderDirty = true; - }) - .pos(middle - 90 + innerPadding, 20 + innerPadding).build(); - - this.colorEntryWidget = new EditBox(this.minecraft.font, middle + 70 + innerPadding * 2, 0, 64, 20, Component.empty()); + this.colorEntryWidget = new EditBox(this.minecraft.font, middle + 54, 0, 64, 20, Component.empty()); this.colorEntryWidget.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.color"))); this.colorEntryWidget.setValue(ColorUtil.toAlphaHex(this.textBlockEntity.color)); this.colorEntryWidget.setResponder(string -> { - ColorUtil.parse(this.colorEntryWidget.getValue(), this.textBlockEntity.color).ifSuccess(newColor -> { + ColorUtil.parse(string, this.textBlockEntity.color).ifSuccess(newColor -> { final int color = (Math.max(newColor >>> 24, 0x1A) << 24) | (newColor & ColorUtil.COLOR_MASK); this.textBlockEntity.color = color; @@ -195,67 +87,21 @@ public void init() { }); }); - this.backgroundColorEntryWidget = new EditBox(this.minecraft.font, middle + 136 + innerPadding * 2, 0, 64, 20, Component.empty()); - this.backgroundColorEntryWidget.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.background_color"))); - this.backgroundColorEntryWidget.setValue(ColorUtil.toAlphaHex(this.textBlockEntity.backgroundColor)); - this.backgroundColorEntryWidget.setResponder(string -> { - ColorUtil.parse(string, this.textBlockEntity.backgroundColor).ifSuccess(newColor -> { - this.textBlockEntity.backgroundColor = newColor; - if (this.colorEntryWidget.isFocused()) { - this.colorPickerWidget.setColor(new Color(newColor)); - } - this.textBlockEntity.renderDirty = true; - }); - }); - - this.zOffsetToggle = Button.builder(Component.literal(this.textBlockEntity.zOffset.name()), action -> { - switch (textBlockEntity.zOffset) { - case FRONT -> textBlockEntity.zOffset = TextBlockEntity.ZOffset.CENTER; - case CENTER -> textBlockEntity.zOffset = TextBlockEntity.ZOffset.BACK; - case BACK -> textBlockEntity.zOffset = TextBlockEntity.ZOffset.FRONT; - } - this.textBlockEntity.renderDirty = true; - - this.zOffsetToggle.setMessage(Component.literal(this.textBlockEntity.zOffset.name())); - }).bounds(middle + 2, 20 + innerPadding, 72, 20).build(); - this.colorPickerWidget = ColorPickerWidget.builder(this, 216, 10).size(182, 104).build(); this.colorPickerWidget.toggle(false); //start deactivated - this.viewDistanceField = new EditBox(this.minecraft.font, middle - 203, 20 + innerPadding, 83 + innerPadding, 20, Component.empty()); - this.viewDistanceField.setValue(String.valueOf(this.textBlockEntity.viewDistance)); - this.viewDistanceField.setResponder(s -> { - if (Floats.tryParse(s) instanceof Float parsed) { - this.textBlockEntity.viewDistance = parsed; - } - }); - this.viewDistanceField.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.screen.text_edit.view_distance"))); - this.viewDistanceHelpButton = Button.builder(Component.literal("?"), action -> { - }) - .bounds(middle - 115 + innerPadding + 5, 20 + innerPadding, 20, 20).build(); - this.viewDistanceHelpButton.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.screen.text_edit.view_distance"))); - this.addRenderableWidget(colorPickerWidget); - this.addRenderableWidget(this.shadowToggle); - this.addRenderableWidget(this.zOffsetToggle); this.addRenderableWidget(this.colorEntryWidget); - this.addRenderableWidget(this.backgroundColorEntryWidget); - - this.addRenderableWidget(this.viewDistanceField); - this.addRenderableWidget(this.viewDistanceHelpButton); this.textWidgets = List.of( - this.colorEntryWidget, - this.backgroundColorEntryWidget, - this.viewDistanceField + this.colorEntryWidget ); this.colorListeners = List.of( - this.colorEntryWidget, - this.backgroundColorEntryWidget + this.colorEntryWidget ); - addFormattingButtons(middle + 70, 20, innerPadding, 20, 2); + addFormattingButtons(middle - 90 + 6, 0, 0, 20, 2); } @Override @@ -282,7 +128,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo super.extractRenderState(graphics, mouseX, mouseY, delta); graphics.pose().pushMatrix(); - graphics.pose().translate(0, 40 + 2 * this.width / 100F); + graphics.pose().translate(0, 20 + 2 * this.width / 100F); for (int i = 0; i < this.textBlockEntity.lines.size(); ++i) { var text = this.currentRow == i ? Component.literal(this.textBlockEntity.getRawLine(i)) : this.textBlockEntity.lines.get(i); @@ -332,7 +178,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo } graphics.pose().popMatrix(); -// graphics.text(minecraft.font, Component.translatable("gui.glowcase.scale_value", this.textBlockEntity.scale), width / 2 - 203, 7, 0xFFFFFFFF); + colorPickerWidget.extractRenderState(graphics, mouseX, mouseY, delta); } diff --git a/src/main/resources/assets/glowcase/lang/en_us.json b/src/main/resources/assets/glowcase/lang/en_us.json index f02ca127..02ed87cf 100644 --- a/src/main/resources/assets/glowcase/lang/en_us.json +++ b/src/main/resources/assets/glowcase/lang/en_us.json @@ -176,5 +176,6 @@ "gui.glowcase.render_distance_value": "Render Distance: %d", "gui.glowcase.render_distance_value.infinite": "Render Distance: Infinite", "gui.glowcase.text_alignment": "Text Alignment", - "gui.glowcase.text_shadow": "Text Shadow" + "gui.glowcase.text_shadow": "Text Shadow", + "gui.glowcase.more": "More..." }