Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8fadb2b
feat: initial commit of new GUI framework
Goldenfield192 May 11, 2026
c26b59f
ref: renames
Goldenfield192 May 11, 2026
f6ffb44
feat: add some function markers | implement horizontal slider
Goldenfield192 May 11, 2026
49c366d
feat: implement vertical slider | some other cleanup
Goldenfield192 May 11, 2026
af4c194
fix: recursive generics
Goldenfield192 May 11, 2026
dfbea53
flatten abstract
Goldenfield192 May 12, 2026
de4f71d
implement textured button
Goldenfield192 May 12, 2026
7a7cf4a
feat: implement ScissorStack and ScrollPane
Goldenfield192 May 12, 2026
da533c3
Merge remote-tracking branch 'origin/1.12.2-forge' into 1.12.2-forge-…
Goldenfield192 May 12, 2026
dbc960f
fix scroll pane
Goldenfield192 May 13, 2026
6781c70
shrink AbstractWidget field visibility
Goldenfield192 May 13, 2026
ef7cd68
remove position settings
Goldenfield192 May 14, 2026
ac642cb
fix some bugs
Goldenfield192 May 14, 2026
ab34474
move requestLayout to AbstractWidget
Goldenfield192 May 14, 2026
49cb59f
add global dragger state
Goldenfield192 May 14, 2026
9685294
add labels and refactor
Goldenfield192 May 14, 2026
16f795d
add align types
Goldenfield192 May 14, 2026
6a5bb27
add anchor pane
Goldenfield192 May 14, 2026
a7bf2c6
anchor pane fixes and slider update
Goldenfield192 May 15, 2026
54de606
expose standard API
Goldenfield192 May 15, 2026
9d4ac0f
add checkbox
Goldenfield192 May 15, 2026
c20550a
some refactor
Goldenfield192 May 15, 2026
adaa469
change setBound impl
Goldenfield192 May 15, 2026
f5b9574
renames
Goldenfield192 May 15, 2026
b3a9327
feat: extra inputting helpers
Goldenfield192 May 16, 2026
df3bdd0
add TextField widget
Goldenfield192 May 16, 2026
18bee4a
fixes
Goldenfield192 May 16, 2026
6396467
exclude invisible children
Goldenfield192 May 16, 2026
f3330a0
add HBox
Goldenfield192 May 16, 2026
9174a13
ref: add GuiRenderFunc
Goldenfield192 May 16, 2026
15393a0
add Image
Goldenfield192 May 17, 2026
4fa0980
fixes on visibility
Goldenfield192 May 17, 2026
03f6d54
add NumberField widget
Goldenfield192 May 17, 2026
a0ee835
refactors
Goldenfield192 May 17, 2026
a70ae20
move NumberField to new package
Goldenfield192 May 17, 2026
b1b0104
add ItemPicker widget
Goldenfield192 May 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions src/main/java/cam72cam/mod/gui_v2/GuiUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package cam72cam.mod.gui_v2;

import cam72cam.mod.MinecraftClient;
import cam72cam.mod.ModCore;
import cam72cam.mod.gui_v2.control.AbstractWidget;
import cam72cam.mod.gui_v2.control.PositionedPanel;
import cam72cam.mod.gui_v2.core.ScreenWrapper;
import cam72cam.mod.text.PlayerMessage;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.event.ClickEvent;

public class GuiUtils {
public static int mouseX;
public static int mouseY;

public static int getMouseX() {
return mouseX;
}

public static int getMouseY() {
return mouseY;
}

public static int getTextWidth(PlayerMessage text) {
return getTextWidth(text.internal.getFormattedText());
}

public static int getTextWidth(String text) {
return Minecraft.getMinecraft().fontRenderer.getStringWidth(text);
}

public static int getScreenWidth() {
return new ScaledResolution(Minecraft.getMinecraft()).getScaledWidth();
}

public static int getScreenHeight() {
return new ScaledResolution(Minecraft.getMinecraft()).getScaledHeight();
}

public static boolean isPrintable(char c) {
return !Character.isISOControl(c) && Character.isDefined(c);
}

public static boolean insidePositionedPanel(AbstractWidget<?> widget) {
AbstractWidget<?> parent = widget;
while ((parent = parent.getParent()) != null) {
if (parent instanceof PositionedPanel) {
return true;
}
}
return false;
}

/** Try to open an external link in player's browser */
public void openLink(String url){
if (ScreenWrapper.getInstance() != null) {
ITextComponent component = new TextComponentString("");
component.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url));
ScreenWrapper.getInstance().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){
if (ScreenWrapper.getInstance() != null) {
ITextComponent component = new TextComponentString("");
component.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, path));
ScreenWrapper.getInstance().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));
}
}
}
}
249 changes: 249 additions & 0 deletions src/main/java/cam72cam/mod/gui_v2/control/AbstractPanel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
package cam72cam.mod.gui_v2.control;

import cam72cam.mod.entity.Player;
import cam72cam.mod.gui_v2.GuiUtils;
import cam72cam.mod.gui_v2.core.actions.*;
import cam72cam.mod.gui_v2.core.layout.ILayoutable;
import cam72cam.mod.gui_v2.core.ScissorStack;
import cam72cam.mod.gui_v2.rendering.GuiRenderer;
import cam72cam.mod.input.Keyboard;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public abstract class AbstractPanel<T extends AbstractPanel<T>> extends AbstractWidget<T>
implements IClickable, IDraggable, IUpdatable, IScrollable, IKeyboardListener {
private final List<ILayoutable<?>> children;
private final List<ILayoutable<?>> controller;

private IFocusable active;

public AbstractPanel(int width, int height) {
super();
this.setBound(0, 0, width, height);
this.children = new ArrayList<>();
this.controller = new ArrayList<>();

// this.setForegroundRenderFunc((gui, panel) -> panel.renderBound(gui, 0xFFFFFFFF));
}

public void addChildren(ILayoutable<?> child) {
addChildren(Collections.singleton(child));
}

public void addChildren(ILayoutable<?>... children) {
addChildren(Arrays.asList(children));
}

public void addChildren(Iterable<ILayoutable<?>> children) {
for (ILayoutable<?> child : children) {
if (child == this) {
throw new IllegalArgumentException("Cannot add self as child panel!");
}
this.children.add(child);
if (child instanceof AbstractWidget<?>) {
((AbstractWidget<?>) child).parent = this;
}
}
layout(this.x(), this.y());
}

public List<ILayoutable<?>> getChildren() {
return children;
}

public List<ILayoutable<?>> getVisibleChildren() {
return children.stream().filter(ILayoutable::isVisible).collect(Collectors.toList());
}

public void clearChildren() {
this.children.clear();
}

protected void addController(ILayoutable<?> ctrl) {
this.controller.add(ctrl);
if (ctrl instanceof AbstractWidget<?>) {
((AbstractWidget<?>) ctrl).parent = this;
}
}

public void renderPanel(GuiRenderer renderer, ScissorStack stack) {
stack.pushPanel(this);
this.getVisibleChildren().forEach(child -> {
stack.push(child);
drawWidget(child, renderer, stack);
stack.pop();
});
stack.pop();
stack.push(this);
this.controller.forEach(ctrl -> {
stack.push(ctrl);
drawWidget(ctrl, renderer, stack);
stack.pop();
});
drawWidget(this, renderer, stack);
stack.pop();
}

private void drawWidget(ILayoutable<?> widget, GuiRenderer renderer, ScissorStack stack) {
if (widget instanceof IUpdatable) {
((IUpdatable) widget).preRender();
}
if (widget != this && widget instanceof AbstractPanel) {
((AbstractPanel<?>) widget).renderPanel(renderer, stack);
} else {
widget.renderBackground(renderer, stack);
widget.render(renderer, stack);
widget.renderForeground(renderer, stack);
}
if (widget instanceof IUpdatable) {
((IUpdatable) widget).postRender();
}
}

public void renderBound(GuiRenderer renderer, int argb) {
renderer.drawRect(x(), y(), 1, height(), argb);
renderer.drawRect(x(), y(), width()-1, 1, argb);
renderer.drawRect(x() + width()-1, y(), 1, height(), argb);
renderer.drawRect(x(), y() + height()-1, width(), 1, argb);
}

/* Indicate actual range excluded panel basics like ScrollPane's scroll bar */
public int panelX() {
return x();
}
public int panelY() {
return y();
}
public int panelWidth() {
return width();
}
public int panelHeight() {
return height();
}

protected boolean isHoveringPanel() {
return isHoveringPanel(GuiUtils.getMouseX(), GuiUtils.getMouseY());
}
protected boolean isHoveringPanel(int mouseX, int mouseY) {
boolean flag = true;
if (parent != null) {
flag = parent.isHovering(mouseX, mouseY);
}
return flag && mouseX >= this.panelX() && mouseX <= this.panelX() + this.width()
&& mouseY >= this.panelY() && mouseY <= this.panelY() + this.height();
}

@Override
public boolean onClick(Player.Hand hand, int x, int y) {
if (!isHovering()) {
return false;
}
if (castedStream(controller, IClickable.class).anyMatch(c -> c.onClick(hand, x, y))) {
return true;
}
return isHoveringPanel() && castedStream(getVisibleChildren(), IClickable.class)
.anyMatch(c -> c.onClick(hand, x, y));
}

@Override
public boolean onDrag(Player.Hand hand, int mouseX, int mouseY) {
if (active instanceof IDraggable) {
return ((IDraggable) active).onDrag(hand, mouseX, mouseY);
}
if (!isHovering()) {
return false;
}
if (castedStream(controller, IDraggable.class).anyMatch(c -> c.onDrag(hand, mouseX, mouseY))) {
return true;
}
return isHoveringPanel() && castedStream(getVisibleChildren(), IDraggable.class)
.anyMatch(c -> c.onDrag(hand, mouseX, mouseY));
}

@Override
public boolean onRelease(Player.Hand hand, int mouseX, int mouseY) {
if (active instanceof IDraggable) {
return ((IDraggable) active).onRelease(hand, mouseX, mouseY);
}
if (!isHovering()) {
return false;
}
if (castedStream(controller, IDraggable.class).anyMatch(c -> c.onRelease(hand, mouseX, mouseY))) {
return true;
}
return isHoveringPanel() && castedStream(getVisibleChildren(), IDraggable.class)
.anyMatch(c -> c.onRelease(hand, mouseX, mouseY));
}

@Override
public boolean onScroll(int mouseX, int mouseY, double deltaScroll) {
if (!isHovering()) {
return false;
}
if (castedStream(controller, IScrollable.class).anyMatch(c -> c.onScroll(mouseX, mouseY, deltaScroll))) {
return true;
}
return isHoveringPanel() && castedStream(getVisibleChildren(), IScrollable.class)
.anyMatch(c -> c.onScroll(mouseX, mouseY, deltaScroll));
}

@Override
public void onTick() {
castedStream(controller, IUpdatable.class).forEach(IUpdatable::onTick);
castedStream(getVisibleChildren(), IUpdatable.class).forEach(IUpdatable::onTick);
}

@Override
public boolean onKeyPressed(Keyboard.KeyCode key) {
if (active instanceof IKeyboardListener) {
return ((IKeyboardListener) active).onKeyPressed(key);
}
if (castedStream(controller, IKeyboardListener.class).anyMatch(c -> c.onKeyPressed(key))) {
return true;
}
return castedStream(getVisibleChildren(), IKeyboardListener.class)
.anyMatch(c -> c.onKeyPressed(key));
}

@Override
public boolean onCharTyped(char ch) {
if (active instanceof IKeyboardListener) {
if (((IKeyboardListener) active).onCharTyped(ch)) return true;
}
if (castedStream(controller, IKeyboardListener.class).anyMatch(c -> c.onCharTyped(ch))) {
return true;
}
return castedStream(getVisibleChildren(), IKeyboardListener.class)
.anyMatch(c -> c.onCharTyped(ch));
}

@Override
protected void requestFocus(IFocusable focusing) {
if (this.parent != null) {
super.requestFocus(focusing);
return;
}
if (this.active != null) {
this.active.onFocusLost();
}
focusing.onFocusGained();
this.active = focusing;
}
@Override
public void freeFocus() {
if (this.parent != null) {
super.freeFocus();
return;
}
if (this.active != null) {
this.active.onFocusLost();
}
this.active = null;
}

private static <E, I> Stream<I> castedStream(Collection<E> elements, Class<I> interface1) {
return elements.stream().filter(interface1::isInstance).map(interface1::cast);
}
}
Loading