From 8fadb2ba0967717d2fa8eb309f19c005df4e9cf4 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Mon, 11 May 2026 20:40:02 +0800 Subject: [PATCH 01/35] feat: initial commit of new GUI framework --- .../java/cam72cam/mod/gui_v2/GUIUtils.java | 18 ++ .../cam72cam/mod/gui_v2/core/ILayoutable.java | 23 ++ .../mod/gui_v2/core/actions/IClickable.java | 17 ++ .../mod/gui_v2/core/actions/ITooltipper.java | 9 + .../mod/gui_v2/core/actions/IUpdatable.java | 8 + .../mod/gui_v2/rendering/GUIRenderer.java | 199 ++++++++++++++++++ .../mod/gui_v2/widgets/AbstractButton.java | 69 ++++++ .../mod/gui_v2/widgets/AbstractPanel.java | 50 +++++ .../mod/gui_v2/widgets/AbstractWidget.java | 112 ++++++++++ .../mod/gui_v2/widgets/impl/Button.java | 64 ++++++ .../mod/gui_v2/widgets/impl/SimplePanel.java | 24 +++ .../mod/gui_v2/wrapper/ScreenBuilder.java | 38 ++++ 12 files changed, 631 insertions(+) create mode 100644 src/main/java/cam72cam/mod/gui_v2/GUIUtils.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/core/ILayoutable.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/core/actions/ITooltipper.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/core/actions/IUpdatable.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/widgets/AbstractButton.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/widgets/AbstractPanel.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/widgets/AbstractWidget.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/widgets/impl/Button.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/widgets/impl/SimplePanel.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java diff --git a/src/main/java/cam72cam/mod/gui_v2/GUIUtils.java b/src/main/java/cam72cam/mod/gui_v2/GUIUtils.java new file mode 100644 index 000000000..23ca888be --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/GUIUtils.java @@ -0,0 +1,18 @@ +package cam72cam.mod.gui_v2; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; +import org.lwjgl.input.Mouse; + +public class GUIUtils { + public static float mouseX; + public static float mouseY; + + public static float getMouseX() { + return mouseX; + } + + public static float getMouseY() { + return mouseY; + } +} diff --git a/src/main/java/cam72cam/mod/gui_v2/core/ILayoutable.java b/src/main/java/cam72cam/mod/gui_v2/core/ILayoutable.java new file mode 100644 index 000000000..75d82fa9d --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/core/ILayoutable.java @@ -0,0 +1,23 @@ +package cam72cam.mod.gui_v2.core; + +import cam72cam.mod.gui_v2.rendering.GUIRenderer; + +public interface ILayoutable { + //Position + int getX(); + int getY(); + int getWidth(); + int getHeight(); + void setX(int x); + void setY(int y); + void setWidth(int width); + void setHeight(int height); + + //Rendering + void renderBackground(GUIRenderer renderer); + void render(GUIRenderer renderer); + void renderForeground(GUIRenderer renderer); + + //Layout + void layout(int x, int y); +} diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java new file mode 100644 index 000000000..765af7071 --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java @@ -0,0 +1,17 @@ +package cam72cam.mod.gui_v2.core.actions; + +import cam72cam.mod.entity.Player; + +/** + * Represents + */ +public interface IClickable { + /** + * + * @param hand PRIMARY for left click, otherwise SECONDARY + * @param x + * @param y + * @return + */ + boolean consumeClick(Player.Hand hand, float x, float y); +} diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/ITooltipper.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/ITooltipper.java new file mode 100644 index 000000000..f6d021414 --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/ITooltipper.java @@ -0,0 +1,9 @@ +package cam72cam.mod.gui_v2.core.actions; + +import cam72cam.mod.text.PlayerMessage; + +import java.util.List; + +public interface ITooltipper { + List getTooltips(); +} diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/IUpdatable.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/IUpdatable.java new file mode 100644 index 000000000..7fbb22f2b --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/IUpdatable.java @@ -0,0 +1,8 @@ +package cam72cam.mod.gui_v2.core.actions; + +import com.sun.media.jfxmedia.events.PlayerStateEvent; + +public interface IUpdatable { + void postRender(); + void onStateChange(); +} diff --git a/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java b/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java new file mode 100644 index 000000000..0db6556db --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java @@ -0,0 +1,199 @@ +package cam72cam.mod.gui_v2.rendering; + +import cam72cam.mod.MinecraftClient; +import cam72cam.mod.ModCore; +import cam72cam.mod.fluid.Fluid; +import cam72cam.mod.gui.helpers.GUIHelpers; +import cam72cam.mod.item.ItemStack; +import cam72cam.mod.render.opengl.RenderContext; +import cam72cam.mod.render.opengl.RenderState; +import cam72cam.mod.render.opengl.Texture; +import cam72cam.mod.resource.Identifier; +import cam72cam.mod.text.PlayerMessage; +import cam72cam.mod.util.With; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.event.ClickEvent; +import util.Matrix4; + +import java.util.List; + +public class GUIRenderer { + /** Standard 54 slot chest UI */ + public static final Identifier CHEST_GUI_TEXTURE = new Identifier("textures/gui/container/generic_54.png"); + // Internal hack for using Gui functions + private final GuiScreen instance; + private final ScaledResolution resolution; + + public GUIRenderer(GuiScreen gui) { + this.instance = gui; + this.resolution = new ScaledResolution(Minecraft.getMinecraft()); + } + + /** Draw a solid color block */ + public void drawRect(int x, int y, int width, int height, int color) { + Gui.drawRect(x, y, x + width, y + height, color); + } + + /** Draw a full image (tex) at coords with given width/height */ + public void texturedRect(Identifier tex, int x, int y, int width, int height) { + this.texturedRect(tex, x, y, 0, 0, width, height); + } + + /** Draw a full image (tex) at coords with given width/height */ + public void texturedRect(Identifier tex, int x, int y, int startU, int startV, int width, int height) { + try (With ctx = RenderContext.apply(new RenderState().texture(Texture.wrap(tex)))) { + instance.drawTexturedModalRect(x, y, startU, startV, width, height); + } + } + + /** Draw fluid block at coords */ + public void drawFluid(Fluid fluid, int x, int y, int width, int height) { + TextureAtlasSprite sprite = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(fluid.internal.getStill().toString()); + drawSprite(sprite, fluid.internal.getColor(), x, y, width, height); + } + + /** This is kinda fun, we want to use the standard sprite drawer with a partial sprite! */ + private static class HackedSprite extends TextureAtlasSprite { + HackedSprite() { + super("unknown"); + } + + void setup(TextureAtlasSprite other, int width, int height) { + this.copyFrom(other); + this.width = width; + this.height = height; + int inX = (int) (originX / getMinU()); + int inY = (int) (originY / getMinV()); + this.initSprite(inX, inY, originX, originY, rotated); + } + } + private final HackedSprite sprite = new HackedSprite(); + + /** Draw a texture sprite at coords, tinted with col */ + private void drawSprite(TextureAtlasSprite sprite, int col, int x, int y, int width, int height) { + double zLevel = 0; + + try (With ctx = RenderContext.apply( + new RenderState() + .texture(Texture.wrap(new Identifier(TextureMap.LOCATION_BLOCKS_TEXTURE))) + .color((col >> 16 & 255) / 255.0f, (col >> 8 & 255) / 255.0f, (col & 255) / 255.0f, 1) + .stage(RenderContext.Stage.GUI) + )) { + int iW = sprite.getIconWidth(); + int iH = sprite.getIconHeight(); + + for (int offY = 0; offY < height; offY += iH) { + double curHeight = Math.min(iH, height - offY); + for (int offX = 0; offX < width; offX += iW) { + double curWidth = Math.min(iW, width - offX); + this.sprite.setup(sprite, (int)curWidth, (int)curHeight); + instance.drawTexturedModalRect(x + offX, y + offY, this.sprite, (int)curWidth, (int)curHeight); + } + } + } + } + + /** Draw the fluid in a tank with a black background at % full */ + public void drawTankBlock(int x, int y, int width, int height, Fluid fluid, float percentFull) { + drawTankBlock(x, y, width, height, fluid, percentFull, true, 0x00000000); + } + + /** Draw the fluid in a tank with a colored background at % full */ + public void drawTankBlock(int x, int y, int width, int height, Fluid fluid, float percentFull, boolean drawBackground, int color) { + if (drawBackground) { + drawRect(x, y, width, height, 0xFF000000); + } + + if (percentFull > 0 && fluid != null) { + int fullHeight = Math.max(1, (int) (height * percentFull)); + drawFluid(fluid, x, y + height - fullHeight, width, fullHeight); + drawRect(x, y + height - fullHeight, width, fullHeight, color); + } + } + + /** Draw a shadowed string offset from the center of coords */ + public void drawCenteredString(String text, int x, int y, int color) { + drawCenteredString(text, x, y, color, new Matrix4()); + } + public void drawCenteredString(String text, int x, int y, int color, Matrix4 matrix) { + drawString(text, (int) (x - Minecraft.getMinecraft().fontRenderer.getStringWidth(text) / 2f), y, color, matrix); + } + + /** Draw a left-aligned shadowed string */ + public void drawString(String text, int x, int y, int color) { + drawString(text, x, y, color, new Matrix4()); + } + public void drawString(String text, int x, int y, int color, Matrix4 matrix) { + RenderState state = new RenderState().color(1, 1, 1, 1).alpha_test(true); + state.model_view().multiply(matrix); + state.stage(RenderContext.Stage.GUI); + try (With ctx = RenderContext.apply(state)) { + GlStateManager.color(1, 1, 1, 0); + Minecraft.getMinecraft().fontRenderer.drawString(text, x, y, color); + } + } + + /** Gat a string's internal width for further use */ + public int getTextWidth(String text) { + return Minecraft.getMinecraft().fontRenderer.getStringWidth(text); + } + + /** Screen Width in pixels (std coords) */ + public int getScreenWidth() { + return resolution.getScaledWidth(); + } + + /** Screen Height in pixels (std coords) */ + public int getScreenHeight() { + return resolution.getScaledHeight(); + } + + /** Draw a Item at the given coords */ + public void drawItem(ItemStack stack, int x, int y) { + drawItem(stack, x, y, new Matrix4()); + } + + public void drawItem(ItemStack stack, int x, int y, Matrix4 matrix) { + RenderState state = new RenderState(); + state.model_view().multiply(matrix); + try (With ctx = RenderContext.apply(state)) { + Minecraft.getMinecraft().getRenderItem().renderItemIntoGUI(stack.internal, x, y); + } + } + + /** Try to open an external link in player's browser */ + public void openLink(String url){ + ITextComponent component = new TextComponentString(""); + component.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)); + if (Minecraft.getMinecraft().currentScreen != null) { + Minecraft.getMinecraft().currentScreen.handleComponentClick(component); + } else { + ModCore.error("Trying to open a link outside a screen: %s", url); + if (MinecraftClient.isReady() && MinecraftClient.getPlayer() != null) { + MinecraftClient.getPlayer().sendMessage(PlayerMessage.url(url)); + } + } + } + + /** Try to open an external link in player's browser */ + public static void openFile(String path){ + ITextComponent component = new TextComponentString(""); + component.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, path)); + if (Minecraft.getMinecraft().currentScreen != null) { + Minecraft.getMinecraft().currentScreen.handleComponentClick(component); + } else { + ModCore.error("Trying to open a file outside a screen: %s", path); + if (MinecraftClient.isReady() && MinecraftClient.getPlayer() != null) { + MinecraftClient.getPlayer().sendMessage(PlayerMessage.direct("Please check this location on your computer: " + path)); + } + } + } +} diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractButton.java b/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractButton.java new file mode 100644 index 000000000..042386a2d --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractButton.java @@ -0,0 +1,69 @@ +package cam72cam.mod.gui_v2.widgets; + +import cam72cam.mod.entity.Player; +import cam72cam.mod.gui_v2.core.actions.IClickable; +import cam72cam.mod.gui_v2.core.actions.ITooltipper; +import cam72cam.mod.gui_v2.core.actions.IUpdatable; +import cam72cam.mod.text.PlayerMessage; + +import java.util.List; +import java.util.function.BiConsumer; + +public abstract class AbstractButton> + extends AbstractWidget + implements IClickable, IUpdatable, ITooltipper { + /** + * Handler consumer, called upon clicked + * Hand -> PRIMARY is a left-click, SECONDARY is a right-click + * AbstractButton -> Reference of self, as it may not be fully constructed + */ + protected BiConsumer handler; + + protected List tooltip; + + /** Default width/height */ + public AbstractButton(int x, int y, PlayerMessage text, BiConsumer handler) { + this(x, y, 200, 20, text, handler); + } + + /** Custom width/height */ + public AbstractButton(int x, int y, int width, int height, PlayerMessage text, BiConsumer handler) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.name = text; + this.handler = handler; + } + + @Override + public void layout(int x, int y) { + this.setX(x); + this.setY(y); + } + + /** Internal click handler*/ + @Override + public boolean consumeClick(Player.Hand hand, float x, float y) { + if (isHovering(x, y)) { + this.handler.accept(hand, (T) this); + this.onStateChange(); + return true; + } + return false; + } + + /** + * Set current widget's tooltip + */ + public void setTooltip(List text) { + this.tooltip = text; + } + + @Override + public void onStateChange() {} + + /** Called every screen draw */ + @Override + public void postRender() {} +} diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractPanel.java b/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractPanel.java new file mode 100644 index 000000000..1dba5ab98 --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractPanel.java @@ -0,0 +1,50 @@ +package cam72cam.mod.gui_v2.widgets; + +import cam72cam.mod.entity.Player; +import cam72cam.mod.gui_v2.core.ILayoutable; +import cam72cam.mod.gui_v2.core.actions.IClickable; +import cam72cam.mod.gui_v2.rendering.GUIRenderer; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractPanel + extends AbstractWidget + implements IClickable { + protected List children; + + public AbstractPanel(int x, int y, int width, int height) { + this.setX(x); + this.setY(y); + this.setWidth(width); + this.setHeight(height); + this.children = new ArrayList<>(); + } + + public void addChildren(ILayoutable child) { + this.children.add(child); + layout(this.getX(), this.getY()); + } + + @Override + public void render(GUIRenderer renderer) { + this.children.forEach(child -> child.render(renderer)); + } + + @Override + public void renderBackground(GUIRenderer renderer) { + + } + + @Override + public void renderForeground(GUIRenderer renderer) { + + } + + @Override + public boolean consumeClick(Player.Hand hand, float x, float y) { + return children.stream() + .filter(c -> c instanceof IClickable) + .anyMatch(c -> ((IClickable) c).consumeClick(hand, x, y)); + } +} diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractWidget.java b/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractWidget.java new file mode 100644 index 000000000..b0f742653 --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractWidget.java @@ -0,0 +1,112 @@ +package cam72cam.mod.gui_v2.widgets; + +import cam72cam.mod.gui_v2.GUIUtils; +import cam72cam.mod.gui_v2.core.ILayoutable; +import cam72cam.mod.gui_v2.core.actions.ITooltipper; +import cam72cam.mod.text.PlayerMessage; +import org.lwjgl.input.Mouse; + +import java.util.List; + +/** + * Basic UMC widget + */ +public abstract class AbstractWidget implements ILayoutable { + protected PlayerMessage name; + protected int nameColor; + + protected AbstractPanel parent; + + protected int x, y, width, height; + protected boolean visible; + protected boolean enabled; + + /** + * Change current widget's visibility + */ + public void setVisible(boolean visible) { + this.visible = visible; + } + + public boolean isVisible() { + return visible; + } + + /** + * Enable or disable current widget + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isEnabled() { + return enabled; + } + + /** + * Get/set current widget's display name + */ + public PlayerMessage getName() { + return name; + } + + public void setName(PlayerMessage text) { + this.name = text; + } + + /** Override the text color */ + public void setNameColor(int argb) { + this.nameColor = argb; + } + + /** + * Is mouse over? + */ + public boolean isHovering() { + return isHovering(GUIUtils.getMouseX(), GUIUtils.getMouseY()); + } + + public boolean isHovering(float mouseX, float mouseY) { + return mouseX >= this.x && mouseX <= this.x + this.width && mouseY >= this.y && mouseY <= this.y + this.height; + } + + @Override + public int getX() { + return x; + } + + @Override + public void setX(int x) { + this.x = x; + } + + @Override + public int getY() { + return y; + } + + @Override + public void setY(int y) { + this.y = y; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public void setWidth(int width) { + this.width = width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public void setHeight(int height) { + this.height = height; + } +} diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/impl/Button.java b/src/main/java/cam72cam/mod/gui_v2/widgets/impl/Button.java new file mode 100644 index 000000000..3d8b30e46 --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/widgets/impl/Button.java @@ -0,0 +1,64 @@ +package cam72cam.mod.gui_v2.widgets.impl; + +import cam72cam.mod.entity.Player; +import cam72cam.mod.gui_v2.rendering.GUIRenderer; +import cam72cam.mod.gui_v2.widgets.AbstractButton; +import cam72cam.mod.resource.Identifier; +import cam72cam.mod.text.PlayerMessage; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; + +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; + +public class Button> extends AbstractButton> { + private static final Identifier VANILLA_BUTTON = new Identifier("textures/gui/widgets.png"); + + /** Custom width/height */ + public Button(int width, int height, PlayerMessage name, BiConsumer> handler) { + super(0, 0, width, height, name, handler); + setEnabled(true); + } + + @Override + public void render(GUIRenderer renderer) { + Minecraft mc = Minecraft.getMinecraft(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + boolean isHovering = isHovering(); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); + GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + int i = !isEnabled() + ? 0 + : isHovering ? 2 : 1; + renderer.texturedRect(VANILLA_BUTTON, this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height); + renderer.texturedRect(VANILLA_BUTTON, this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height); + int j = 14737632; + + if (nameColor != 0) { + j = nameColor; + } else if (!this.enabled) { + j = 10526880; + } else if (isHovering) { + j = 16777120; + } + + renderer.drawCenteredString(this.name.internal.getFormattedText(), this.x + this.width / 2, this.y + (this.height - 8) / 2, j); + } + + @Override + public void renderBackground(GUIRenderer renderer) { + + } + + @Override + public void renderForeground(GUIRenderer renderer) { + + } + + @Override + public List getTooltips() { + return Collections.singletonList(this.getName()); + } +} diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/impl/SimplePanel.java b/src/main/java/cam72cam/mod/gui_v2/widgets/impl/SimplePanel.java new file mode 100644 index 000000000..d0ccaf9a3 --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/widgets/impl/SimplePanel.java @@ -0,0 +1,24 @@ +package cam72cam.mod.gui_v2.widgets.impl; + +import cam72cam.mod.gui_v2.core.ILayoutable; +import cam72cam.mod.gui_v2.rendering.GUIRenderer; +import cam72cam.mod.gui_v2.widgets.AbstractPanel; +import org.jline.reader.Widget; + +public class SimplePanel extends AbstractPanel { + public SimplePanel(int x, int y, int width, int height) { + super(x, y, width, height); + } + + @Override + public void layout(int x, int y) { + this.setX(x); + this.setY(y); + for (ILayoutable widget : children) { + widget.setX(0); + widget.setY(y); + widget.layout(x, y); + y += widget.getHeight(); + } + } +} diff --git a/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java b/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java new file mode 100644 index 000000000..bb5687aae --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java @@ -0,0 +1,38 @@ +package cam72cam.mod.gui_v2.wrapper; + +import cam72cam.mod.entity.Player; +import cam72cam.mod.gui.helpers.GUIHelpers; +import cam72cam.mod.gui_v2.GUIUtils; +import cam72cam.mod.gui_v2.rendering.GUIRenderer; +import cam72cam.mod.gui_v2.widgets.AbstractPanel; +import cam72cam.mod.gui_v2.widgets.impl.Button; +import cam72cam.mod.gui_v2.widgets.impl.SimplePanel; +import cam72cam.mod.text.PlayerMessage; +import net.minecraft.client.gui.GuiScreen; + +import java.io.IOException; + +public class ScreenBuilder extends GuiScreen { + private final AbstractPanel root; + + public ScreenBuilder() { + this.root = new SimplePanel(0, 0, GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight()); + Button button = new Button(150, 20, PlayerMessage.direct("clicker"), (hand, btn) -> System.out.println(btn.hashCode())); + root.addChildren(button); + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + GUIUtils.mouseX = mouseX; + GUIUtils.mouseY = mouseY; + GUIRenderer renderer = new GUIRenderer(this); + root.renderBackground(renderer); + root.render(renderer); + root.renderForeground(renderer); + } + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { + root.consumeClick(Player.Hand.PRIMARY, mouseX, mouseY); + } +} From c26b59fb33db12a0d19fbf7cbacfd31e796f5c2b Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Mon, 11 May 2026 20:50:47 +0800 Subject: [PATCH 02/35] ref: renames --- .../{widgets => control}/AbstractButton.java | 2 +- .../{widgets => control}/AbstractPanel.java | 18 ++++++++++++++++- .../{widgets => control}/AbstractWidget.java | 6 +----- .../panel/VerticalPanel.java} | 10 ++++------ .../impl => control/widget}/Button.java | 6 +++--- .../mod/gui_v2/core/actions/IClickable.java | 6 +----- .../mod/gui_v2/core/actions/IDraggable.java | 5 +++++ .../mod/gui_v2/core/actions/IScrollable.java | 5 +++++ .../mod/gui_v2/wrapper/ScreenBuilder.java | 20 +++++++++++++------ 9 files changed, 51 insertions(+), 27 deletions(-) rename src/main/java/cam72cam/mod/gui_v2/{widgets => control}/AbstractButton.java (98%) rename src/main/java/cam72cam/mod/gui_v2/{widgets => control}/AbstractPanel.java (72%) rename src/main/java/cam72cam/mod/gui_v2/{widgets => control}/AbstractWidget.java (93%) rename src/main/java/cam72cam/mod/gui_v2/{widgets/impl/SimplePanel.java => control/panel/VerticalPanel.java} (56%) rename src/main/java/cam72cam/mod/gui_v2/{widgets/impl => control/widget}/Button.java (94%) create mode 100644 src/main/java/cam72cam/mod/gui_v2/core/actions/IDraggable.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/core/actions/IScrollable.java diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractButton.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java similarity index 98% rename from src/main/java/cam72cam/mod/gui_v2/widgets/AbstractButton.java rename to src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java index 042386a2d..be3c4acb5 100644 --- a/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractButton.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java @@ -1,4 +1,4 @@ -package cam72cam.mod.gui_v2.widgets; +package cam72cam.mod.gui_v2.control; import cam72cam.mod.entity.Player; import cam72cam.mod.gui_v2.core.actions.IClickable; diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractPanel.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java similarity index 72% rename from src/main/java/cam72cam/mod/gui_v2/widgets/AbstractPanel.java rename to src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java index 1dba5ab98..840f156bc 100644 --- a/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractPanel.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java @@ -1,4 +1,4 @@ -package cam72cam.mod.gui_v2.widgets; +package cam72cam.mod.gui_v2.control; import cam72cam.mod.entity.Player; import cam72cam.mod.gui_v2.core.ILayoutable; @@ -6,6 +6,7 @@ import cam72cam.mod.gui_v2.rendering.GUIRenderer; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public abstract class AbstractPanel @@ -26,6 +27,18 @@ public void addChildren(ILayoutable child) { layout(this.getX(), this.getY()); } + public void addChildren(ILayoutable... children) { + this.children.addAll(Arrays.asList(children)); + layout(this.getX(), this.getY()); + } + + public void addChildren(Iterable children) { + for (ILayoutable child : children) { + this.children.add(child); + } + layout(this.getX(), this.getY()); + } + @Override public void render(GUIRenderer renderer) { this.children.forEach(child -> child.render(renderer)); @@ -43,6 +56,9 @@ public void renderForeground(GUIRenderer renderer) { @Override public boolean consumeClick(Player.Hand hand, float x, float y) { + if (!isHovering(x, y)) { + return false; + } return children.stream() .filter(c -> c instanceof IClickable) .anyMatch(c -> ((IClickable) c).consumeClick(hand, x, y)); diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractWidget.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractWidget.java similarity index 93% rename from src/main/java/cam72cam/mod/gui_v2/widgets/AbstractWidget.java rename to src/main/java/cam72cam/mod/gui_v2/control/AbstractWidget.java index b0f742653..f92c642aa 100644 --- a/src/main/java/cam72cam/mod/gui_v2/widgets/AbstractWidget.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractWidget.java @@ -1,12 +1,8 @@ -package cam72cam.mod.gui_v2.widgets; +package cam72cam.mod.gui_v2.control; import cam72cam.mod.gui_v2.GUIUtils; import cam72cam.mod.gui_v2.core.ILayoutable; -import cam72cam.mod.gui_v2.core.actions.ITooltipper; import cam72cam.mod.text.PlayerMessage; -import org.lwjgl.input.Mouse; - -import java.util.List; /** * Basic UMC widget diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/impl/SimplePanel.java b/src/main/java/cam72cam/mod/gui_v2/control/panel/VerticalPanel.java similarity index 56% rename from src/main/java/cam72cam/mod/gui_v2/widgets/impl/SimplePanel.java rename to src/main/java/cam72cam/mod/gui_v2/control/panel/VerticalPanel.java index d0ccaf9a3..38146a28f 100644 --- a/src/main/java/cam72cam/mod/gui_v2/widgets/impl/SimplePanel.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/panel/VerticalPanel.java @@ -1,12 +1,10 @@ -package cam72cam.mod.gui_v2.widgets.impl; +package cam72cam.mod.gui_v2.control.panel; import cam72cam.mod.gui_v2.core.ILayoutable; -import cam72cam.mod.gui_v2.rendering.GUIRenderer; -import cam72cam.mod.gui_v2.widgets.AbstractPanel; -import org.jline.reader.Widget; +import cam72cam.mod.gui_v2.control.AbstractPanel; -public class SimplePanel extends AbstractPanel { - public SimplePanel(int x, int y, int width, int height) { +public class VerticalPanel extends AbstractPanel { + public VerticalPanel(int x, int y, int width, int height) { super(x, y, width, height); } diff --git a/src/main/java/cam72cam/mod/gui_v2/widgets/impl/Button.java b/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java similarity index 94% rename from src/main/java/cam72cam/mod/gui_v2/widgets/impl/Button.java rename to src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java index 3d8b30e46..83ea4fb21 100644 --- a/src/main/java/cam72cam/mod/gui_v2/widgets/impl/Button.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java @@ -1,8 +1,8 @@ -package cam72cam.mod.gui_v2.widgets.impl; +package cam72cam.mod.gui_v2.control.widget; import cam72cam.mod.entity.Player; import cam72cam.mod.gui_v2.rendering.GUIRenderer; -import cam72cam.mod.gui_v2.widgets.AbstractButton; +import cam72cam.mod.gui_v2.control.AbstractButton; import cam72cam.mod.resource.Identifier; import cam72cam.mod.text.PlayerMessage; import net.minecraft.client.Minecraft; @@ -13,7 +13,7 @@ import java.util.function.BiConsumer; public class Button> extends AbstractButton> { - private static final Identifier VANILLA_BUTTON = new Identifier("textures/gui/widgets.png"); + private static final Identifier VANILLA_BUTTON = new Identifier("textures/gui/control.png"); /** Custom width/height */ public Button(int width, int height, PlayerMessage name, BiConsumer> handler) { diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java index 765af7071..08a5f494f 100644 --- a/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java @@ -7,11 +7,7 @@ */ public interface IClickable { /** - * - * @param hand PRIMARY for left click, otherwise SECONDARY - * @param x - * @param y - * @return + * PRIMARY for left click, otherwise SECONDARY */ boolean consumeClick(Player.Hand hand, float x, float y); } diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/IDraggable.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/IDraggable.java new file mode 100644 index 000000000..e4a1ae573 --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/IDraggable.java @@ -0,0 +1,5 @@ +package cam72cam.mod.gui_v2.core.actions; + +public interface IDraggable { + boolean consumeDrag(int oldX, int oldY, int newX, int newY); +} diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/IScrollable.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/IScrollable.java new file mode 100644 index 000000000..7e9917545 --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/IScrollable.java @@ -0,0 +1,5 @@ +package cam72cam.mod.gui_v2.core.actions; + +public interface IScrollable { + boolean consumeScroll(int mouseX, int mouseY, int deltaScroll); +} diff --git a/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java b/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java index bb5687aae..174227870 100644 --- a/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java +++ b/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java @@ -4,9 +4,9 @@ import cam72cam.mod.gui.helpers.GUIHelpers; import cam72cam.mod.gui_v2.GUIUtils; import cam72cam.mod.gui_v2.rendering.GUIRenderer; -import cam72cam.mod.gui_v2.widgets.AbstractPanel; -import cam72cam.mod.gui_v2.widgets.impl.Button; -import cam72cam.mod.gui_v2.widgets.impl.SimplePanel; +import cam72cam.mod.gui_v2.control.AbstractPanel; +import cam72cam.mod.gui_v2.control.widget.Button; +import cam72cam.mod.gui_v2.control.panel.VerticalPanel; import cam72cam.mod.text.PlayerMessage; import net.minecraft.client.gui.GuiScreen; @@ -16,9 +16,12 @@ public class ScreenBuilder extends GuiScreen { private final AbstractPanel root; public ScreenBuilder() { - this.root = new SimplePanel(0, 0, GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight()); - Button button = new Button(150, 20, PlayerMessage.direct("clicker"), (hand, btn) -> System.out.println(btn.hashCode())); - root.addChildren(button); + //Test + this.root = new VerticalPanel(0, 0, GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight()); + Button button1 = new Button(150, 20, PlayerMessage.direct("clicker"), (hand, btn) -> System.out.println(btn.hashCode())); + Button button2 = new Button(150, 20, PlayerMessage.direct("clicker2"), (hand, btn) -> System.out.println(btn.hashCode())); + Button button3 = new Button(150, 20, PlayerMessage.direct("clicker3"), (hand, btn) -> System.out.println(btn.hashCode())); + root.addChildren(button1, button2, button3); } @Override @@ -35,4 +38,9 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { root.consumeClick(Player.Hand.PRIMARY, mouseX, mouseY); } + + @Override + public boolean doesGuiPauseGame() { + return false; + } } From f6ffb44fc826d7abf9ae51097f6798c100309656 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Mon, 11 May 2026 22:42:06 +0800 Subject: [PATCH 03/35] feat: add some function markers | implement horizontal slider --- .../java/cam72cam/mod/gui_v2/GUIUtils.java | 39 +++++- .../mod/gui_v2/control/AbstractButton.java | 17 +-- .../mod/gui_v2/control/AbstractPanel.java | 42 +++++- .../mod/gui_v2/control/AbstractSlider.java | 121 ++++++++++++++++++ .../mod/gui_v2/control/AbstractWidget.java | 10 +- .../mod/gui_v2/control/widget/Button.java | 20 +-- .../mod/gui_v2/control/widget/Slider.java | 61 +++++++++ .../cam72cam/mod/gui_v2/core/ILayoutable.java | 1 + .../mod/gui_v2/core/actions/IClickable.java | 2 +- .../mod/gui_v2/core/actions/IDraggable.java | 5 +- .../mod/gui_v2/core/actions/IScrollable.java | 2 +- .../mod/gui_v2/core/actions/ITooltipper.java | 1 + .../mod/gui_v2/rendering/GUIRenderer.java | 54 ++------ .../mod/gui_v2/wrapper/ScreenBuilder.java | 44 ++++++- 14 files changed, 336 insertions(+), 83 deletions(-) create mode 100644 src/main/java/cam72cam/mod/gui_v2/control/AbstractSlider.java create mode 100644 src/main/java/cam72cam/mod/gui_v2/control/widget/Slider.java diff --git a/src/main/java/cam72cam/mod/gui_v2/GUIUtils.java b/src/main/java/cam72cam/mod/gui_v2/GUIUtils.java index 23ca888be..c8cda5bb3 100644 --- a/src/main/java/cam72cam/mod/gui_v2/GUIUtils.java +++ b/src/main/java/cam72cam/mod/gui_v2/GUIUtils.java @@ -1,12 +1,17 @@ package cam72cam.mod.gui_v2; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.ScaledResolution; -import org.lwjgl.input.Mouse; +import cam72cam.mod.MinecraftClient; +import cam72cam.mod.ModCore; +import cam72cam.mod.gui_v2.wrapper.ScreenBuilder; +import cam72cam.mod.text.PlayerMessage; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.event.ClickEvent; public class GUIUtils { public static float mouseX; public static float mouseY; + public static ScreenBuilder currentBuilder; public static float getMouseX() { return mouseX; @@ -15,4 +20,32 @@ public static float getMouseX() { public static float getMouseY() { return mouseY; } + + /** Try to open an external link in player's browser */ + public void openLink(String url){ + ITextComponent component = new TextComponentString(""); + component.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)); + if (currentBuilder != null) { + currentBuilder.handleComponentClick(component); + } else { + ModCore.error("Trying to open a link outside a screen: %s", url); + if (MinecraftClient.isReady() && MinecraftClient.getPlayer() != null) { + MinecraftClient.getPlayer().sendMessage(PlayerMessage.url(url)); + } + } + } + + /** Try to open an external link in player's browser */ + public static void openFile(String path){ + ITextComponent component = new TextComponentString(""); + component.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, path)); + if (currentBuilder != null) { + currentBuilder.handleComponentClick(component); + } else { + ModCore.error("Trying to open a file outside a screen: %s", path); + if (MinecraftClient.isReady() && MinecraftClient.getPlayer() != null) { + MinecraftClient.getPlayer().sendMessage(PlayerMessage.direct("Please check this location on your computer: " + path)); + } + } + } } diff --git a/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java index be3c4acb5..16e10789e 100644 --- a/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java @@ -6,6 +6,7 @@ import cam72cam.mod.gui_v2.core.actions.IUpdatable; import cam72cam.mod.text.PlayerMessage; +import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; @@ -21,11 +22,6 @@ public abstract class AbstractButton> protected List tooltip; - /** Default width/height */ - public AbstractButton(int x, int y, PlayerMessage text, BiConsumer handler) { - this(x, y, 200, 20, text, handler); - } - /** Custom width/height */ public AbstractButton(int x, int y, int width, int height, PlayerMessage text, BiConsumer handler) { this.x = x; @@ -42,10 +38,9 @@ public void layout(int x, int y) { this.setY(y); } - /** Internal click handler*/ @Override - public boolean consumeClick(Player.Hand hand, float x, float y) { - if (isHovering(x, y)) { + public boolean onClick(Player.Hand hand, float x, float y) { + if (isHovering()) { this.handler.accept(hand, (T) this); this.onStateChange(); return true; @@ -56,10 +51,16 @@ public boolean consumeClick(Player.Hand hand, float x, float y) { /** * Set current widget's tooltip */ + @Override public void setTooltip(List text) { this.tooltip = text; } + @Override + public List getTooltips() { + return Collections.singletonList(this.getName()); + } + @Override public void onStateChange() {} diff --git a/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java index 840f156bc..92cb1e676 100644 --- a/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java @@ -3,6 +3,8 @@ import cam72cam.mod.entity.Player; import cam72cam.mod.gui_v2.core.ILayoutable; import cam72cam.mod.gui_v2.core.actions.IClickable; +import cam72cam.mod.gui_v2.core.actions.IDraggable; +import cam72cam.mod.gui_v2.core.actions.IScrollable; import cam72cam.mod.gui_v2.rendering.GUIRenderer; import java.util.ArrayList; @@ -11,7 +13,7 @@ public abstract class AbstractPanel extends AbstractWidget - implements IClickable { + implements IClickable, IDraggable, IScrollable { protected List children; public AbstractPanel(int x, int y, int width, int height) { @@ -55,12 +57,44 @@ public void renderForeground(GUIRenderer renderer) { } @Override - public boolean consumeClick(Player.Hand hand, float x, float y) { - if (!isHovering(x, y)) { + public boolean onClick(Player.Hand hand, float x, float y) { + if (!isHovering()) { return false; } return children.stream() .filter(c -> c instanceof IClickable) - .anyMatch(c -> ((IClickable) c).consumeClick(hand, x, y)); + .anyMatch(c -> ((IClickable) c).onClick(hand, x, y)); + } + + @Override + public boolean onDrag(Player.Hand hand, int mouseX, int mouseY) { + //TODO Tracking + if (!isHovering()) { + return false; + } + return children.stream() + .filter(c -> c instanceof IDraggable) + .anyMatch(c -> ((IDraggable) c).onDrag(hand, mouseX, mouseY)); + } + + @Override + public boolean onRelease(Player.Hand hand, int mouseX, int mouseY) { + if (!isHovering()) { + return false; + } + return children.stream() + .filter(c -> c instanceof IDraggable) + .anyMatch(c -> ((IDraggable) c).onRelease(hand, mouseX, mouseY)); + } + + @Override + public boolean onScroll(int mouseX, int mouseY, double deltaScroll) { + if (!isHovering()) { + return false; + } + + return children.stream() + .filter(c -> c instanceof IScrollable) + .anyMatch(c -> ((IScrollable) c).onScroll(mouseX, mouseY, deltaScroll)); } } diff --git a/src/main/java/cam72cam/mod/gui_v2/control/AbstractSlider.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractSlider.java new file mode 100644 index 000000000..8ab0a221b --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractSlider.java @@ -0,0 +1,121 @@ +package cam72cam.mod.gui_v2.control; + +import cam72cam.mod.entity.Player; +import cam72cam.mod.gui_v2.core.actions.IDraggable; +import cam72cam.mod.gui_v2.rendering.GUIRenderer; +import cam72cam.mod.text.PlayerMessage; +import net.minecraft.util.math.MathHelper; + +import java.util.function.Consumer; + +public abstract class AbstractSlider> + extends AbstractWidget + implements IDraggable { + protected final boolean isHorizontal; + + protected double min; + protected double max; + protected double value; + protected int displayPrecision; + + protected boolean isDragging; + + protected Consumer handler; + + //TODO + // 1.Int precision + // 2.Prefix/Suffix text + public AbstractSlider(PlayerMessage text, double min, double max, double start, boolean doublePrecision, Consumer handler) { + this(150, 20, text, min, max, start, doublePrecision, handler); + } + + public AbstractSlider(int width, int height, PlayerMessage text, double min, double max, double start, boolean doublePrecision, Consumer handler) { + this(width, height, text, min, max, start, doublePrecision, handler, true); + } + + public AbstractSlider(int width, int height, PlayerMessage text, double min, double max, double start, boolean doublePrecision, Consumer handler, boolean isHorizontal) { + this.setBound(0, 0, width, height); + this.name = text; + this.min = min; + this.max = max; + this.value = start; + this.displayPrecision = doublePrecision ? 4 : 0; + this.handler = handler; + this.isHorizontal = isHorizontal; + } + + public double getValue() { + return value; + } + + public void setValue(double value) { + this.value = value; + } + + public void setSliderBound(double min, double max) { + this.min = min; + this.max = max; + this.value = MathHelper.clamp(min, max, value); + } + + @Override + public boolean onDrag(Player.Hand hand, int mouseX, int mouseY) { + if (!isHovering()) { + if (!isDragging) { + return false; + } + } + isDragging = true; + + double oldValue = value; + double ratio; + + if (isHorizontal) { + double relX = mouseX - getX(); + ratio = relX / getWidth(); + } else { + double relY = getY() + getHeight() - mouseY; + ratio = relY / getHeight(); + } + + ratio = Math.max(0.0, Math.min(1.0, ratio)); + + double rawValue = min + ratio * (max - min); + value = Math.max(min, Math.min(max, rawValue)); + + if (value != oldValue && handler != null) { + handler.accept((T) this); + } + + return true; + } + + @Override + public boolean onRelease(Player.Hand hand, int mouseX, int mouseY) { + if (isDragging) { + isDragging = false; + if (handler != null) { + handler.accept((T) this); + } + return true; + } + return false; + } + + @Override + public void renderBackground(GUIRenderer renderer) { + + } + + @Override + public void renderForeground(GUIRenderer renderer) { + + } + + @Override + public void layout(int x, int y) { + this.setX(x); + this.setY(y); + isDragging = false; + } +} diff --git a/src/main/java/cam72cam/mod/gui_v2/control/AbstractWidget.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractWidget.java index f92c642aa..f0bd44fca 100644 --- a/src/main/java/cam72cam/mod/gui_v2/control/AbstractWidget.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractWidget.java @@ -62,7 +62,7 @@ public boolean isHovering() { return isHovering(GUIUtils.getMouseX(), GUIUtils.getMouseY()); } - public boolean isHovering(float mouseX, float mouseY) { + private boolean isHovering(float mouseX, float mouseY) { return mouseX >= this.x && mouseX <= this.x + this.width && mouseY >= this.y && mouseY <= this.y + this.height; } @@ -105,4 +105,12 @@ public int getHeight() { public void setHeight(int height) { this.height = height; } + + @Override + public void setBound(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } } diff --git a/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java b/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java index 83ea4fb21..50ba523fe 100644 --- a/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java @@ -3,17 +3,11 @@ import cam72cam.mod.entity.Player; import cam72cam.mod.gui_v2.rendering.GUIRenderer; import cam72cam.mod.gui_v2.control.AbstractButton; -import cam72cam.mod.resource.Identifier; import cam72cam.mod.text.PlayerMessage; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; -import java.util.Collections; -import java.util.List; import java.util.function.BiConsumer; public class Button> extends AbstractButton> { - private static final Identifier VANILLA_BUTTON = new Identifier("textures/gui/control.png"); /** Custom width/height */ public Button(int width, int height, PlayerMessage name, BiConsumer> handler) { @@ -23,17 +17,12 @@ public Button(int width, int height, PlayerMessage name, BiConsumer getTooltips() { - return Collections.singletonList(this.getName()); - } } diff --git a/src/main/java/cam72cam/mod/gui_v2/control/widget/Slider.java b/src/main/java/cam72cam/mod/gui_v2/control/widget/Slider.java new file mode 100644 index 000000000..0ba9af3ac --- /dev/null +++ b/src/main/java/cam72cam/mod/gui_v2/control/widget/Slider.java @@ -0,0 +1,61 @@ +package cam72cam.mod.gui_v2.control.widget; + +import cam72cam.mod.gui_v2.control.AbstractSlider; +import cam72cam.mod.gui_v2.rendering.GUIRenderer; +import cam72cam.mod.text.PlayerMessage; +import net.minecraft.client.renderer.GlStateManager; + +import java.util.function.Consumer; + +public class Slider> extends AbstractSlider { + public Slider(PlayerMessage text, double min, double max, double start, boolean doublePrecision, Consumer handler) { + super(text, min, max, start, doublePrecision, handler); + } + + public Slider(int width, int height, PlayerMessage text, double min, double max, double start, boolean doublePrecision, Consumer handler) { + super(width, height, text, min, max, start, doublePrecision, handler); + } + + public Slider(int width, int height, PlayerMessage text, double min, double max, double start, boolean doublePrecision, Consumer handler, boolean isHorizontal) { + super(width, height, text, min, max, start, doublePrecision, handler, isHorizontal); + } + + @Override + public void render(GUIRenderer renderer) { + renderer.drawVanillaButton(getX(), getY(), getWidth(), getHeight(), 0); + + double ratio = (value - min) / (max - min); + ratio = Math.max(0.0, Math.min(1.0, ratio)); + + if (isHorizontal) { + int trackWidth = getWidth() - 8; + int handleX = getX() + (int) (ratio * trackWidth); + int handleY = getY(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + renderer.texturedRect(GUIRenderer.VANILLA_BUTTON, handleX, handleY, 0, 66, 4, 20); + renderer.texturedRect(GUIRenderer.VANILLA_BUTTON, handleX + 4, handleY, 196, 66, 4, 20); + + + } else { + //TODO Vertical +// int trackHeight = getHeight() - 8; +// int handleX = getX(); +// int handleY = getY() + (int) ((1.0 - ratio) * trackHeight); +// +// renderer.setColor(1.0F, 1.0F, 1.0F, 1.0F); +// renderer.drawTexturedModalRect(handleX, handleY, 0, 66, 20, 4); +// renderer.drawTexturedModalRect(handleX, handleY + 4, 196, 66, 20, 4); + } + + + int j = 14737632; + + if (nameColor != 0) { + j = nameColor; + } else if (isHovering()) { + j = 16777120; + } + + renderer.drawCenteredString(this.name.internal.getFormattedText(), this.x + this.width / 2, this.y + (this.height - 8) / 2, j); + } +} diff --git a/src/main/java/cam72cam/mod/gui_v2/core/ILayoutable.java b/src/main/java/cam72cam/mod/gui_v2/core/ILayoutable.java index 75d82fa9d..eff42b71b 100644 --- a/src/main/java/cam72cam/mod/gui_v2/core/ILayoutable.java +++ b/src/main/java/cam72cam/mod/gui_v2/core/ILayoutable.java @@ -12,6 +12,7 @@ public interface ILayoutable { void setY(int y); void setWidth(int width); void setHeight(int height); + void setBound(int x, int y, int width, int height); //Rendering void renderBackground(GUIRenderer renderer); diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java index 08a5f494f..6ef5ece6f 100644 --- a/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java @@ -9,5 +9,5 @@ public interface IClickable { /** * PRIMARY for left click, otherwise SECONDARY */ - boolean consumeClick(Player.Hand hand, float x, float y); + boolean onClick(Player.Hand hand, float x, float y); } diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/IDraggable.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/IDraggable.java index e4a1ae573..1a00de73d 100644 --- a/src/main/java/cam72cam/mod/gui_v2/core/actions/IDraggable.java +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/IDraggable.java @@ -1,5 +1,8 @@ package cam72cam.mod.gui_v2.core.actions; +import cam72cam.mod.entity.Player; + public interface IDraggable { - boolean consumeDrag(int oldX, int oldY, int newX, int newY); + boolean onDrag(Player.Hand hand, int mouseX, int mouseY); + boolean onRelease(Player.Hand hand, int mouseX, int mouseY); } diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/IScrollable.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/IScrollable.java index 7e9917545..6bb14595a 100644 --- a/src/main/java/cam72cam/mod/gui_v2/core/actions/IScrollable.java +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/IScrollable.java @@ -1,5 +1,5 @@ package cam72cam.mod.gui_v2.core.actions; public interface IScrollable { - boolean consumeScroll(int mouseX, int mouseY, int deltaScroll); + boolean onScroll(int mouseX, int mouseY, double deltaScroll); } diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/ITooltipper.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/ITooltipper.java index f6d021414..1c676b682 100644 --- a/src/main/java/cam72cam/mod/gui_v2/core/actions/ITooltipper.java +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/ITooltipper.java @@ -6,4 +6,5 @@ public interface ITooltipper { List getTooltips(); + void setTooltip(List text); } diff --git a/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java b/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java index 0db6556db..49db67f07 100644 --- a/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java +++ b/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java @@ -1,15 +1,11 @@ package cam72cam.mod.gui_v2.rendering; -import cam72cam.mod.MinecraftClient; -import cam72cam.mod.ModCore; import cam72cam.mod.fluid.Fluid; -import cam72cam.mod.gui.helpers.GUIHelpers; import cam72cam.mod.item.ItemStack; import cam72cam.mod.render.opengl.RenderContext; import cam72cam.mod.render.opengl.RenderState; import cam72cam.mod.render.opengl.Texture; import cam72cam.mod.resource.Identifier; -import cam72cam.mod.text.PlayerMessage; import cam72cam.mod.util.With; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; @@ -18,17 +14,11 @@ import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureMap; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.event.ClickEvent; import util.Matrix4; -import java.util.List; - public class GUIRenderer { - /** Standard 54 slot chest UI */ - public static final Identifier CHEST_GUI_TEXTURE = new Identifier("textures/gui/container/generic_54.png"); - // Internal hack for using Gui functions + public static final Identifier VANILLA_BUTTON = new Identifier("textures/gui/widgets.png"); + private final GuiScreen instance; private final ScaledResolution resolution; @@ -54,6 +44,16 @@ public void texturedRect(Identifier tex, int x, int y, int startU, int startV, i } } + //0 - disabled, 1 - normal, 2 - hovering + public void drawVanillaButton(int x, int y, int width, int height, int state) { + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); + GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + this.texturedRect(VANILLA_BUTTON, x, y, 0, 46 + state * 20, width / 2, height); + this.texturedRect(VANILLA_BUTTON, x + width / 2, y, 200 - width / 2, 46 + state * 20, width / 2, height); + } + /** Draw fluid block at coords */ public void drawFluid(Fluid fluid, int x, int y, int width, int height) { TextureAtlasSprite sprite = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(fluid.internal.getStill().toString()); @@ -137,7 +137,7 @@ public void drawString(String text, int x, int y, int color, Matrix4 matrix) { state.stage(RenderContext.Stage.GUI); try (With ctx = RenderContext.apply(state)) { GlStateManager.color(1, 1, 1, 0); - Minecraft.getMinecraft().fontRenderer.drawString(text, x, y, color); + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow(text, x, y, color); } } @@ -168,32 +168,4 @@ public void drawItem(ItemStack stack, int x, int y, Matrix4 matrix) { Minecraft.getMinecraft().getRenderItem().renderItemIntoGUI(stack.internal, x, y); } } - - /** Try to open an external link in player's browser */ - public void openLink(String url){ - ITextComponent component = new TextComponentString(""); - component.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)); - if (Minecraft.getMinecraft().currentScreen != null) { - Minecraft.getMinecraft().currentScreen.handleComponentClick(component); - } else { - ModCore.error("Trying to open a link outside a screen: %s", url); - if (MinecraftClient.isReady() && MinecraftClient.getPlayer() != null) { - MinecraftClient.getPlayer().sendMessage(PlayerMessage.url(url)); - } - } - } - - /** Try to open an external link in player's browser */ - public static void openFile(String path){ - ITextComponent component = new TextComponentString(""); - component.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, path)); - if (Minecraft.getMinecraft().currentScreen != null) { - Minecraft.getMinecraft().currentScreen.handleComponentClick(component); - } else { - ModCore.error("Trying to open a file outside a screen: %s", path); - if (MinecraftClient.isReady() && MinecraftClient.getPlayer() != null) { - MinecraftClient.getPlayer().sendMessage(PlayerMessage.direct("Please check this location on your computer: " + path)); - } - } - } } diff --git a/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java b/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java index 174227870..f2d33f73c 100644 --- a/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java +++ b/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java @@ -3,14 +3,17 @@ import cam72cam.mod.entity.Player; import cam72cam.mod.gui.helpers.GUIHelpers; import cam72cam.mod.gui_v2.GUIUtils; +import cam72cam.mod.gui_v2.control.widget.Slider; import cam72cam.mod.gui_v2.rendering.GUIRenderer; import cam72cam.mod.gui_v2.control.AbstractPanel; import cam72cam.mod.gui_v2.control.widget.Button; import cam72cam.mod.gui_v2.control.panel.VerticalPanel; import cam72cam.mod.text.PlayerMessage; import net.minecraft.client.gui.GuiScreen; +import org.lwjgl.input.Mouse; import java.io.IOException; +import java.util.function.BiConsumer; public class ScreenBuilder extends GuiScreen { private final AbstractPanel root; @@ -18,10 +21,12 @@ public class ScreenBuilder extends GuiScreen { public ScreenBuilder() { //Test this.root = new VerticalPanel(0, 0, GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight()); - Button button1 = new Button(150, 20, PlayerMessage.direct("clicker"), (hand, btn) -> System.out.println(btn.hashCode())); - Button button2 = new Button(150, 20, PlayerMessage.direct("clicker2"), (hand, btn) -> System.out.println(btn.hashCode())); - Button button3 = new Button(150, 20, PlayerMessage.direct("clicker3"), (hand, btn) -> System.out.println(btn.hashCode())); - root.addChildren(button1, button2, button3); + BiConsumer> btnTest = (hand, btn) -> System.out.println(btn.hashCode()); + Button button1 = new Button(150, 20, PlayerMessage.direct("clicker"), btnTest); + Button button2 = new Button(150, 20, PlayerMessage.direct("clicker2"), btnTest); + Button button3 = new Button(150, 20, PlayerMessage.direct("clicker3"), btnTest); + Slider slider1 = new Slider(150, 20, PlayerMessage.direct("slider"), 0, 1, 0, false, slider -> System.out.println(((Slider)slider).getValue())); + root.addChildren(button1, button2, button3, slider1); } @Override @@ -36,7 +41,36 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { @Override protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { - root.consumeClick(Player.Hand.PRIMARY, mouseX, mouseY); + //TODO Mouse key detection + root.onClick(Player.Hand.PRIMARY, mouseX, mouseY); + } + + @Override + protected void mouseClickMove(int mouseX, int mouseY, int mouseButton, long deltaTicks) { + root.onDrag(Player.Hand.PRIMARY, mouseX, mouseY); + } + + @Override + protected void mouseReleased(int mouseX, int mouseY, int mouseButton) { + root.onRelease(Player.Hand.PRIMARY, mouseX, mouseY); + } + + protected void mouseScrolled(int mouseX, int mouseY, double delta) { + root.onScroll(mouseX, mouseY, delta); + } + + @Override + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + + //TODO Mixin? + int i = Mouse.getEventX() * this.width / this.mc.displayWidth; + int j = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1; + + double scroll = Math.signum(org.lwjgl.input.Mouse.getEventDWheel()); + if (scroll != 0) { + this.mouseScrolled(i, j, scroll); + } } @Override From 49c366d8dad188d0c684fe70dd26d8e18ed3f589 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Mon, 11 May 2026 23:31:03 +0800 Subject: [PATCH 04/35] feat: implement vertical slider | some other cleanup --- .../mod/gui_v2/control/AbstractButton.java | 2 +- .../mod/gui_v2/control/AbstractPanel.java | 2 +- .../mod/gui_v2/control/AbstractSlider.java | 26 ++++-- .../mod/gui_v2/control/widget/Slider.java | 21 +++-- .../mod/gui_v2/core/actions/IClickable.java | 2 +- .../mod/gui_v2/rendering/GUIRenderer.java | 81 ++++++++++++++++++- .../mod/gui_v2/wrapper/ScreenBuilder.java | 7 +- 7 files changed, 115 insertions(+), 26 deletions(-) diff --git a/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java index 16e10789e..43b16c21d 100644 --- a/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractButton.java @@ -39,7 +39,7 @@ public void layout(int x, int y) { } @Override - public boolean onClick(Player.Hand hand, float x, float y) { + public boolean onClick(Player.Hand hand, int x, int y) { if (isHovering()) { this.handler.accept(hand, (T) this); this.onStateChange(); diff --git a/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java index 92cb1e676..ada80ec58 100644 --- a/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java @@ -57,7 +57,7 @@ public void renderForeground(GUIRenderer renderer) { } @Override - public boolean onClick(Player.Hand hand, float x, float y) { + public boolean onClick(Player.Hand hand, int x, int y) { if (!isHovering()) { return false; } diff --git a/src/main/java/cam72cam/mod/gui_v2/control/AbstractSlider.java b/src/main/java/cam72cam/mod/gui_v2/control/AbstractSlider.java index 8ab0a221b..7b2a9a302 100644 --- a/src/main/java/cam72cam/mod/gui_v2/control/AbstractSlider.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/AbstractSlider.java @@ -1,6 +1,7 @@ package cam72cam.mod.gui_v2.control; import cam72cam.mod.entity.Player; +import cam72cam.mod.gui_v2.core.actions.IClickable; import cam72cam.mod.gui_v2.core.actions.IDraggable; import cam72cam.mod.gui_v2.rendering.GUIRenderer; import cam72cam.mod.text.PlayerMessage; @@ -10,7 +11,7 @@ public abstract class AbstractSlider> extends AbstractWidget - implements IDraggable { + implements IClickable, IDraggable { protected final boolean isHorizontal; protected double min; @@ -58,6 +59,15 @@ public void setSliderBound(double min, double max) { this.value = MathHelper.clamp(min, max, value); } + @Override + public boolean onClick(Player.Hand hand, int x, int y) { + if (!isHovering()) { + return false; + } + updateSlider(x, y); + return true; + } + @Override public boolean onDrag(Player.Hand hand, int mouseX, int mouseY) { if (!isHovering()) { @@ -66,16 +76,20 @@ public boolean onDrag(Player.Hand hand, int mouseX, int mouseY) { } } isDragging = true; + updateSlider(mouseX, mouseY); + return true; + } + protected void updateSlider(int mouseX, int mouseY) { double oldValue = value; double ratio; if (isHorizontal) { - double relX = mouseX - getX(); - ratio = relX / getWidth(); + double relX = mouseX - getX() - 4; //Slider bar size + ratio = relX / (getWidth() - 8); } else { - double relY = getY() + getHeight() - mouseY; - ratio = relY / getHeight(); + double relY = getY() + getHeight() - mouseY - 4; + ratio = relY / (getHeight() - 8); } ratio = Math.max(0.0, Math.min(1.0, ratio)); @@ -86,8 +100,6 @@ public boolean onDrag(Player.Hand hand, int mouseX, int mouseY) { if (value != oldValue && handler != null) { handler.accept((T) this); } - - return true; } @Override diff --git a/src/main/java/cam72cam/mod/gui_v2/control/widget/Slider.java b/src/main/java/cam72cam/mod/gui_v2/control/widget/Slider.java index 0ba9af3ac..e437d7803 100644 --- a/src/main/java/cam72cam/mod/gui_v2/control/widget/Slider.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/widget/Slider.java @@ -22,29 +22,26 @@ public Slider(int width, int height, PlayerMessage text, double min, double max, @Override public void render(GUIRenderer renderer) { + //Render track renderer.drawVanillaButton(getX(), getY(), getWidth(), getHeight(), 0); double ratio = (value - min) / (max - min); ratio = Math.max(0.0, Math.min(1.0, ratio)); + //Render slider bar if (isHorizontal) { int trackWidth = getWidth() - 8; int handleX = getX() + (int) (ratio * trackWidth); int handleY = getY(); GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - renderer.texturedRect(GUIRenderer.VANILLA_BUTTON, handleX, handleY, 0, 66, 4, 20); - renderer.texturedRect(GUIRenderer.VANILLA_BUTTON, handleX + 4, handleY, 196, 66, 4, 20); - - + renderer.drawVanillaButton(handleX, handleY, 8, height, 1); } else { - //TODO Vertical -// int trackHeight = getHeight() - 8; -// int handleX = getX(); -// int handleY = getY() + (int) ((1.0 - ratio) * trackHeight); -// -// renderer.setColor(1.0F, 1.0F, 1.0F, 1.0F); -// renderer.drawTexturedModalRect(handleX, handleY, 0, 66, 20, 4); -// renderer.drawTexturedModalRect(handleX, handleY + 4, 196, 66, 20, 4); + int trackHeight = getHeight() - 8; + int handleX = getX(); + int handleY = getY() + (int) ((1.0 - ratio) * trackHeight); + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + renderer.drawVanillaButton(handleX, handleY, width, 8, 1); } diff --git a/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java b/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java index 6ef5ece6f..22ad2c8c5 100644 --- a/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java +++ b/src/main/java/cam72cam/mod/gui_v2/core/actions/IClickable.java @@ -9,5 +9,5 @@ public interface IClickable { /** * PRIMARY for left click, otherwise SECONDARY */ - boolean onClick(Player.Hand hand, float x, float y); + boolean onClick(Player.Hand hand, int x, int y); } diff --git a/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java b/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java index 49db67f07..7abc7e311 100644 --- a/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java +++ b/src/main/java/cam72cam/mod/gui_v2/rendering/GUIRenderer.java @@ -46,12 +46,89 @@ public void texturedRect(Identifier tex, int x, int y, int startU, int startV, i //0 - disabled, 1 - normal, 2 - hovering public void drawVanillaButton(int x, int y, int width, int height, int state) { + if (width < 6 || height < 6) { + //Don't handle + return; + } + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); GlStateManager.enableBlend(); GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - this.texturedRect(VANILLA_BUTTON, x, y, 0, 46 + state * 20, width / 2, height); - this.texturedRect(VANILLA_BUTTON, x + width / 2, y, 200 - width / 2, 46 + state * 20, width / 2, height); + + int uBase = 0; + int vBase = 46 + state * 20; + int border = 3; + int texTotalW = 200; + int texTotalH = 20; + + int uLeft = uBase; + int uCenter = uBase + border; + int uRight = uBase + texTotalW - border; + int vTop = vBase; + int vMiddle = vBase + border; + int vBottom = vBase + texTotalH - border; + + int centerTexW = texTotalW - 2 * border; + int centerTexH = texTotalH - 2 * border; + + // Corners + texturedRect(VANILLA_BUTTON, x, y, uLeft, vTop, border, border); + texturedRect(VANILLA_BUTTON, x + width - border, y, uRight, vTop, border, border); + texturedRect(VANILLA_BUTTON, x, y + height - border, uLeft, vBottom, border, border); + texturedRect(VANILLA_BUTTON, x + width - border, y + height - border, uRight, vBottom, border, border); + + int innerW = width - 2 * border; + int innerH = height - 2 * border; + + // Upper/Lower edge + if (innerW > 0) { + int upperY = y; + int lowerY = y + height - border; + int startX = x + border; + int remaining = innerW; + while (remaining > 0) { + int w = Math.min(remaining, centerTexW); + texturedRect(VANILLA_BUTTON, startX, upperY, uCenter, vTop, w, border); + texturedRect(VANILLA_BUTTON, startX, lowerY, uCenter, vBottom, w, border); + startX += w; + remaining -= w; + } + } + + // Left/Right Edge + if (innerH > 0) { + int leftX = x; + int rightX = x + width - border; + int startY = y + border; + int remaining = innerH; + while (remaining > 0) { + int h = Math.min(remaining, centerTexH); + texturedRect(VANILLA_BUTTON, leftX, startY, uLeft, vMiddle, border, h); + texturedRect(VANILLA_BUTTON, rightX, startY, uRight, vMiddle, border, h); + startY += h; + remaining -= h; + } + } + + // Internal + if (innerW > 0 && innerH > 0) { + int startY = y + border; + int remainingH = innerH; + while (remainingH > 0) { + int h = Math.min(remainingH, centerTexH); + int startX = x + border; + int remainingW = innerW; + while (remainingW > 0) { + int w = Math.min(remainingW, centerTexW); + texturedRect(VANILLA_BUTTON, startX, startY, uCenter, vMiddle, w, h); + startX += w; + remainingW -= w; + } + startY += h; + remainingH -= h; + } + } } /** Draw fluid block at coords */ diff --git a/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java b/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java index f2d33f73c..10d8eac5e 100644 --- a/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java +++ b/src/main/java/cam72cam/mod/gui_v2/wrapper/ScreenBuilder.java @@ -25,8 +25,11 @@ public ScreenBuilder() { Button button1 = new Button(150, 20, PlayerMessage.direct("clicker"), btnTest); Button button2 = new Button(150, 20, PlayerMessage.direct("clicker2"), btnTest); Button button3 = new Button(150, 20, PlayerMessage.direct("clicker3"), btnTest); - Slider slider1 = new Slider(150, 20, PlayerMessage.direct("slider"), 0, 1, 0, false, slider -> System.out.println(((Slider)slider).getValue())); - root.addChildren(button1, button2, button3, slider1); + Slider hori = new Slider(150, 20, PlayerMessage.direct("slider"), 0, 1, 0, false, + slider -> System.out.println(((Slider)slider).getValue()), true); + Slider vert = new Slider(20, 150, PlayerMessage.direct("slider"), 0, 1, 0, false, + slider -> System.out.println(((Slider)slider).getValue()), false); + root.addChildren(button1, button2, button3, hori, vert); } @Override From af4c19494524145913f4f68a005b59a5976cdd03 Mon Sep 17 00:00:00 2001 From: Goldenfield192 <1437356849@qq.com> Date: Mon, 11 May 2026 23:37:48 +0800 Subject: [PATCH 05/35] fix: recursive generics --- .../mod/gui_v2/control/widget/Button.java | 4 ++-- .../mod/gui_v2/control/widget/Slider.java | 8 ++++---- .../mod/gui_v2/wrapper/ScreenBuilder.java | 20 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java b/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java index 50ba523fe..e0a5f8a9d 100644 --- a/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java +++ b/src/main/java/cam72cam/mod/gui_v2/control/widget/Button.java @@ -7,10 +7,10 @@ import java.util.function.BiConsumer; -public class Button> extends AbstractButton> { +public class Button extends AbstractButton