Changed namespace

This commit is contained in:
Frank 2022-10-29 13:07:44 +02:00
parent 47756e7ebf
commit 01c59be6ae
62 changed files with 107 additions and 4332 deletions

View file

@ -5,7 +5,6 @@ import org.betterx.bclib.util.ColorExtractor;
import org.betterx.bclib.util.MHelper;
import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
@ -18,6 +17,7 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import com.google.common.collect.Maps;
import org.wunder.lib.ui.ColorHelper;
import java.io.IOException;
import java.util.ArrayList;
@ -25,23 +25,23 @@ import java.util.List;
import java.util.Map;
public class ColorUtil {
public static final int BLACK = ChatFormatting.BLACK.getColor() | 0xFF000000;
public static final int DARK_BLUE = ChatFormatting.DARK_BLUE.getColor() | 0xFF000000;
public static final int DARK_GREEN = ChatFormatting.DARK_GREEN.getColor() | 0xFF000000;
public static final int DARK_AQUA = ChatFormatting.DARK_AQUA.getColor() | 0xFF000000;
public static final int DARK_RED = ChatFormatting.DARK_RED.getColor() | 0xFF000000;
public static final int DARK_PURPLE = ChatFormatting.DARK_PURPLE.getColor() | 0xFF000000;
public static final int GOLD = ChatFormatting.GOLD.getColor() | 0xFF000000;
public static final int GRAY = ChatFormatting.GRAY.getColor() | 0xFF000000;
public static final int DARK_GRAY = ChatFormatting.DARK_GRAY.getColor() | 0xFF000000;
public static final int BLUE = ChatFormatting.BLUE.getColor() | 0xFF000000;
public static final int GREEN = ChatFormatting.GREEN.getColor() | 0xFF000000;
public static final int AQUA = ChatFormatting.AQUA.getColor() | 0xFF000000;
public static final int RED = ChatFormatting.RED.getColor() | 0xFF000000;
public static final int LIGHT_PURPLE = ChatFormatting.LIGHT_PURPLE.getColor() | 0xFF000000;
public static final int YELLOW = ChatFormatting.YELLOW.getColor() | 0xFF000000;
public static final int WHITE = ChatFormatting.WHITE.getColor() | 0xFF000000;
public static final int DEFAULT_TEXT = WHITE;
public static final int BLACK = ColorHelper.BLACK;
public static final int DARK_BLUE = ColorHelper.DARK_BLUE;
public static final int DARK_GREEN = ColorHelper.DARK_GREEN;
public static final int DARK_AQUA = ColorHelper.DARK_AQUA;
public static final int DARK_RED = ColorHelper.DARK_RED;
public static final int DARK_PURPLE = ColorHelper.DARK_PURPLE;
public static final int GOLD = ColorHelper.GOLD;
public static final int GRAY = ColorHelper.GRAY;
public static final int DARK_GRAY = ColorHelper.DARK_GRAY;
public static final int BLUE = ColorHelper.BLUE;
public static final int GREEN = ColorHelper.GREEN;
public static final int AQUA = ColorHelper.AQUA;
public static final int RED = ColorHelper.RED;
public static final int LIGHT_PURPLE = ColorHelper.LIGHT_PURPLE;
public static final int YELLOW = ColorHelper.YELLOW;
public static final int WHITE = ColorHelper.WHITE;
public static final int DEFAULT_TEXT = ColorHelper.WHITE;
private static final float[] FLOAT_BUFFER = new float[4];
private static final int ALPHA = 255 << 24;

View file

@ -1,87 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.input.RelativeContainerEventHandler;
import org.betterx.ui.layout.components.render.NullRenderer;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.Value;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class AbstractHorizontalStack<S extends AbstractHorizontalStack<S>> extends AbstractStack<NullRenderer, S> implements RelativeContainerEventHandler {
public AbstractHorizontalStack(Value width, Value height) {
super(width, height);
}
@Override
public int updateContainerWidth(int containerWidth) {
int myWidth = width.calculateOrFill(containerWidth);
int fixedWidth = components.stream().map(c -> c.width.calculate(myWidth)).reduce(0, Integer::sum);
int freeWidth = Math.max(0, myWidth - fixedWidth);
fillWidth(myWidth, freeWidth);
for (LayoutComponent<?, ?> c : components) {
c.updateContainerWidth(c.width.calculatedSize());
}
return myWidth;
}
@Override
protected int updateContainerHeight(int containerHeight) {
int myHeight = height.calculateOrFill(containerHeight);
components.stream().forEach(c -> c.height.calculateOrFill(myHeight));
for (LayoutComponent<?, ?> c : components) {
c.updateContainerHeight(c.height.calculatedSize());
}
return myHeight;
}
@Override
void setRelativeBounds(int left, int top) {
super.setRelativeBounds(left, top);
int offset = 0;
for (LayoutComponent<?, ?> c : components) {
int delta = relativeBounds.height - c.height.calculatedSize();
if (c.vAlign == Alignment.MIN) delta = 0;
else if (c.vAlign == Alignment.CENTER) delta /= 2;
c.setRelativeBounds(offset, delta);
offset += c.relativeBounds.width;
}
}
@Override
public int getContentWidth() {
int fixedWidth = components.stream().map(c -> c.width.calculateFixed()).reduce(0, Integer::sum);
double percentage = components.stream().map(c -> c.width.calculateRelative()).reduce(0.0, Double::sum);
return (int) (fixedWidth / (1 - percentage));
}
@Override
public int getContentHeight() {
return components.stream().map(c -> c.height.calculateFixed()).reduce(0, Integer::max);
}
protected S addEmpty(Value size) {
this.components.add(new Empty(size, Value.fixed(0)));
return (S) this;
}
protected VerticalStack addColumn(Value width, Value height) {
VerticalStack stack = new VerticalStack(width, height);
add(stack);
return stack;
}
protected VerticalStack addColumn() {
return addColumn(Value.fit(), Value.fit());
}
}

View file

@ -1,287 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.input.RelativeContainerEventHandler;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Size;
import org.betterx.ui.layout.values.Value;
import org.betterx.ui.vanilla.VanillaScrollerRenderer;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import java.util.LinkedList;
import java.util.List;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public abstract class AbstractStack<R extends ComponentRenderer, T extends AbstractStack<R, T>> extends LayoutComponent<R, T> implements RelativeContainerEventHandler {
protected final List<LayoutComponent<?, ?>> components = new LinkedList<>();
public AbstractStack(Value width, Value height) {
this(width, height, null);
}
public AbstractStack(Value width, Value height, R renderer) {
super(width, height, renderer);
}
@Override
public int fillWidth(int parentSize, int fillSize) {
double totalFillWeight = components.stream().map(c -> c.width.fillWeight()).reduce(0.0, Double::sum);
return components.stream()
.map(c -> c.width.fill(fillSize, totalFillWeight))
.reduce(0, Integer::sum);
}
@Override
public int fillHeight(int parentSize, int fillSize) {
double totalFillHeight = components.stream().map(c -> c.height.fillWeight()).reduce(0.0, Double::sum);
return components.stream()
.map(c -> c.height.fill(fillSize, totalFillHeight))
.reduce(0, Integer::sum);
}
@Override
public void updateScreenBounds(int worldX, int worldY) {
super.updateScreenBounds(worldX, worldY);
for (LayoutComponent<?, ?> c : components) {
c.updateScreenBounds(screenBounds.left, screenBounds.top);
}
}
@Override
protected void renderInBounds(
PoseStack poseStack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle renderBounds,
Rectangle clipRect
) {
super.renderInBounds(poseStack, mouseX, mouseY, deltaTicks, renderBounds, clipRect);
for (LayoutComponent<?, ?> c : components) {
c.render(poseStack, mouseX, mouseY, deltaTicks, renderBounds, clipRect);
}
}
@Override
public List<? extends GuiEventListener> children() {
return components;
}
@Override
public Rectangle getInputBounds() {
return relativeBounds;
}
boolean dragging;
@Override
public boolean isDragging() {
return dragging;
}
@Override
public void setDragging(boolean bl) {
dragging = bl;
}
GuiEventListener focused;
@Nullable
@Override
public GuiEventListener getFocused() {
return focused;
}
@Override
public void setFocused(@Nullable GuiEventListener guiEventListener) {
focused = guiEventListener;
}
public boolean isEmpty() {
return components.isEmpty();
}
protected abstract T addEmpty(Value size);
protected T add(LayoutComponent<?, ?> c) {
this.components.add(c);
return (T) this;
}
protected T addSpacer(int size) {
return addEmpty(Value.fixed(size));
}
protected T addSpacer(float percentage) {
return addEmpty(Value.relative(percentage));
}
protected T addFiller() {
return addEmpty(Value.fill());
}
protected Image addIcon(ResourceLocation location, Size resourceSize) {
Image i = new Image(Value.fixed(24), Value.fixed(24), location, resourceSize);
add(i);
return i;
}
protected Image addImage(Value width, Value height, ResourceLocation location, Size resourceSize) {
Image i = new Image(width, height, location, resourceSize);
add(i);
return i;
}
protected Checkbox addCheckbox(
Value width, Value height, Component component,
boolean selected
) {
Checkbox c = new Checkbox(width, height, component, selected, true);
add(c);
return c;
}
protected Button addButton(
Value width, Value height,
Component component
) {
Button b = new Button(width, height, component);
add(b);
return b;
}
protected VerticalScroll<VanillaScrollerRenderer> addScrollable(LayoutComponent<?, ?> content) {
return addScrollable(Value.fill(), Value.fill(), content);
}
protected VerticalScroll<VanillaScrollerRenderer> addScrollable(
Value width,
Value height,
LayoutComponent<?, ?> content
) {
VerticalScroll<VanillaScrollerRenderer> s = VerticalScroll.create(width, height, content);
add(s);
return s;
}
protected Text addText(Value width, Value height, net.minecraft.network.chat.Component text) {
Text t = new Text(width, height, text);
add(t);
return t;
}
protected MultiLineText addMultilineText(Value width, Value height, net.minecraft.network.chat.Component text) {
MultiLineText t = new MultiLineText(width, height, text);
add(t);
return t;
}
protected Range<Integer> addRange(
Value width, Value height,
net.minecraft.network.chat.Component titleOrNull,
int minValue, int maxValue, int initialValue
) {
Range<Integer> r = new Range<>(width, height, titleOrNull, minValue, maxValue, initialValue);
add(r);
return r;
}
protected Range<Float> addRange(
Value width, Value height,
net.minecraft.network.chat.Component titleOrNull,
float minValue, float maxValue, float initialValue
) {
Range<Float> r = new Range<>(width, height, titleOrNull, minValue, maxValue, initialValue);
add(r);
return r;
}
protected Range<Double> addRange(
Value width, Value height,
net.minecraft.network.chat.Component titleOrNull,
double minValue, double maxValue, double initialValue
) {
Range<Double> r = new Range<>(width, height, titleOrNull, minValue, maxValue, initialValue);
add(r);
return r;
}
protected Input addInput(
Value width, Value height, net.minecraft.network.chat.Component titleOrNull, String initialValue
) {
Input i = new Input(width, height, titleOrNull, initialValue);
add(i);
return i;
}
protected ColorSwatch addColorSwatch(Value width, Value height, int color) {
ColorSwatch c = new ColorSwatch(width, height, color);
add(c);
return c;
}
protected ColorPicker addColorPicker(
Value width,
Value height,
net.minecraft.network.chat.Component titleOrNull,
int color
) {
ColorPicker c = new ColorPicker(width, height, titleOrNull, color);
add(c);
return c;
}
protected HLine addHorizontalSeparator(int height) {
return addHLine(Value.relative(1.0 / 1.618033988749894), Value.fixed(height));
}
protected HLine addHorizontalLine(int height) {
return addHLine(Value.fill(), Value.fixed(height));
}
protected HLine addHLine(Value width, Value height) {
HLine l = new HLine(width, height);
add(l);
return l;
}
protected VLine addVerticalSeparator(int width) {
return addVLine(Value.fixed(width), Value.relative(1.0 / 1.618033988749894));
}
protected VLine addVerticalLine(int width) {
return addVLine(Value.fixed(width), Value.fill());
}
protected VLine addVLine(Value width, Value height) {
VLine l = new VLine(width, height);
add(l);
return l;
}
protected Container addContainered(Value width, Value height, LayoutComponent<?, ?> content) {
Container c = new Container(width, height);
c.addChild(content);
add(c);
return c;
}
protected Item addItem(ItemStack stack) {
Item i = new Item(Value.fit(), Value.fit());
i.setItem(stack);
add(i);
return i;
}
}

View file

@ -1,142 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.values.Value;
import net.minecraft.client.gui.components.AbstractWidget;
public abstract class AbstractVanillaComponent<C extends AbstractWidget, V extends AbstractVanillaComponent<C, V>> extends LayoutComponent<AbstractVanillaComponentRenderer<C, V>, V> {
protected C vanillaComponent;
protected final net.minecraft.network.chat.Component component;
protected float alpha = 1.0f;
protected boolean enabled = true;
public AbstractVanillaComponent(
Value width,
Value height,
AbstractVanillaComponentRenderer<C, V> renderer,
net.minecraft.network.chat.Component component
) {
super(width, height, renderer);
this.component = component;
renderer.linkedComponent = (V) this;
}
protected abstract C createVanillaComponent();
@Override
protected void onBoundsChanged() {
vanillaComponent = createVanillaComponent();
vanillaComponent.setAlpha(this.alpha);
}
protected net.minecraft.network.chat.Component contentComponent() {
return component;
}
@Override
public int getContentWidth() {
return renderer.getWidth(contentComponent());
}
@Override
public int getContentHeight() {
return renderer.getHeight(contentComponent());
}
public float getAlpha() {
return alpha;
}
public V setAlpha(float alpha) {
this.alpha = alpha;
if (vanillaComponent != null) {
vanillaComponent.setAlpha(alpha);
}
return (V) this;
}
public boolean isEnabled() {
return enabled;
}
public V setEnabled(boolean enabled) {
this.enabled = enabled;
return (V) this;
}
@Override
public void mouseMoved(double x, double y) {
if (vanillaComponent != null && enabled)
vanillaComponent.mouseMoved(x - relativeBounds.left, y - relativeBounds.top);
}
@Override
public boolean mouseClicked(double x, double y, int button) {
if (vanillaComponent != null && enabled)
return vanillaComponent.mouseClicked(x - relativeBounds.left, y - relativeBounds.top, button);
return false;
}
@Override
public boolean mouseReleased(double x, double y, int button) {
if (vanillaComponent != null && enabled)
return vanillaComponent.mouseReleased(x - relativeBounds.left, y - relativeBounds.top, button);
return false;
}
@Override
public boolean mouseDragged(double x, double y, int button, double x2, double y2) {
if (vanillaComponent != null && enabled)
return vanillaComponent.mouseDragged(
x - relativeBounds.left,
y - relativeBounds.top,
button,
x2 - relativeBounds.left,
y2 - relativeBounds.top
);
return false;
}
@Override
public boolean mouseScrolled(double x, double y, double f) {
if (vanillaComponent != null && enabled)
return vanillaComponent.mouseScrolled(x - relativeBounds.left, y - relativeBounds.top, f);
return false;
}
@Override
public boolean keyPressed(int i, int j, int k) {
if (vanillaComponent != null && enabled)
return vanillaComponent.keyPressed(i, j, k);
return false;
}
@Override
public boolean keyReleased(int i, int j, int k) {
if (vanillaComponent != null && enabled)
return vanillaComponent.keyReleased(i, j, k);
return false;
}
@Override
public boolean charTyped(char c, int i) {
if (vanillaComponent != null && enabled)
return vanillaComponent.charTyped(c, i);
return false;
}
@Override
public boolean changeFocus(boolean bl) {
if (vanillaComponent != null && enabled)
return vanillaComponent.changeFocus(bl);
return false;
}
@Override
public boolean isMouseOver(double x, double y) {
if (vanillaComponent != null && enabled)
return vanillaComponent.isMouseOver(x - relativeBounds.left, y - relativeBounds.top);
return false;
}
}

View file

@ -1,43 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.components.render.TextProvider;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.AbstractWidget;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class AbstractVanillaComponentRenderer<C extends AbstractWidget, V extends AbstractVanillaComponent<C, V>> implements ComponentRenderer, TextProvider {
V linkedComponent;
protected V getLinkedComponent() {
return linkedComponent;
}
@Override
public void renderInBounds(
PoseStack poseStack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
) {
if (linkedComponent != null) {
if (linkedComponent.vanillaComponent != null) {
if (!linkedComponent.enabled) {
linkedComponent.vanillaComponent.setAlpha(linkedComponent.alpha / 2);
}
linkedComponent.vanillaComponent.render(poseStack, mouseX, mouseY, deltaTicks);
if (!linkedComponent.enabled) {
linkedComponent.vanillaComponent.setAlpha(linkedComponent.alpha);
}
}
}
}
}

View file

@ -1,83 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.input.RelativeContainerEventHandler;
import org.betterx.ui.layout.components.render.NullRenderer;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.Value;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class AbstractVerticalStack<S extends AbstractVerticalStack<S>> extends AbstractStack<NullRenderer, S> implements RelativeContainerEventHandler {
public AbstractVerticalStack(Value width, Value height) {
super(width, height);
}
@Override
protected int updateContainerWidth(int containerWidth) {
int myWidth = width.calculateOrFill(containerWidth);
components.stream().forEach(c -> c.width.calculateOrFill(myWidth));
for (LayoutComponent<?, ?> c : components) {
c.updateContainerWidth(c.width.calculatedSize());
}
return myWidth;
}
@Override
public int updateContainerHeight(int containerHeight) {
int myHeight = height.calculateOrFill(containerHeight);
int fixedHeight = components.stream().map(c -> c.height.calculate(myHeight)).reduce(0, Integer::sum);
int freeHeight = Math.max(0, myHeight - fixedHeight);
fillHeight(myHeight, freeHeight);
for (LayoutComponent<?, ?> c : components) {
c.updateContainerHeight(c.height.calculatedSize());
}
return myHeight;
}
@Override
void setRelativeBounds(int left, int top) {
super.setRelativeBounds(left, top);
int offset = 0;
for (LayoutComponent<?, ?> c : components) {
int delta = relativeBounds.width - c.width.calculatedSize();
if (c.hAlign == Alignment.MIN) delta = 0;
else if (c.hAlign == Alignment.CENTER) delta /= 2;
c.setRelativeBounds(delta, offset);
offset += c.relativeBounds.height;
}
}
@Override
public int getContentWidth() {
return components.stream().map(c -> c.width.calculateFixed()).reduce(0, Integer::max);
}
@Override
public int getContentHeight() {
int fixedHeight = components.stream().map(c -> c.height.calculateFixed()).reduce(0, Integer::sum);
double percentage = components.stream().map(c -> c.height.calculateRelative()).reduce(0.0, Double::sum);
return (int) (fixedHeight / (1 - percentage));
}
protected S addEmpty(Value size) {
this.components.add(new Empty(Value.fixed(0), size));
return (S) this;
}
protected HorizontalStack addRow(Value width, Value height) {
HorizontalStack stack = new HorizontalStack(width, height);
add(stack);
return stack;
}
protected HorizontalStack addRow() {
return addRow(Value.fit(), Value.fit());
}
}

View file

@ -1,67 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.ButtonRenderer;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class Button extends AbstractVanillaComponent<net.minecraft.client.gui.components.Button, Button> {
public static final OnTooltip NO_TOOLTIP = (button, poseStack, i, j) -> {
};
public static final OnPress NO_ACTION = (button) -> {
};
@Environment(EnvType.CLIENT)
public interface OnTooltip {
void onTooltip(Button button, PoseStack poseStack, int mouseX, int mouseY);
}
@Environment(EnvType.CLIENT)
public interface OnPress {
void onPress(Button button);
}
OnPress onPress;
OnTooltip onTooltip;
boolean glow = false;
public Button(
Value width,
Value height,
net.minecraft.network.chat.Component component
) {
super(width, height, new ButtonRenderer(), component);
this.onPress = NO_ACTION;
this.onTooltip = NO_TOOLTIP;
}
public Button onPress(OnPress onPress) {
this.onPress = onPress;
return this;
}
public Button onToolTip(OnTooltip onTooltip) {
this.onTooltip = onTooltip;
return this;
}
@Override
protected net.minecraft.client.gui.components.Button createVanillaComponent() {
Button self = this;
return net.minecraft.client.gui.components.Button
.builder(component, (bt) -> onPress.onPress(self))
.bounds(0, 0, relativeBounds.width, relativeBounds.height)
.tooltip((bt, stack, x, y) -> onTooltip.onTooltip(self, stack, x, y))
.build();
}
public boolean isGlowing() {
return glow;
}
}

View file

@ -1,68 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.CheckboxRenderer;
import org.betterx.ui.layout.values.Value;
import net.minecraft.network.chat.Component;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class Checkbox extends AbstractVanillaComponent<net.minecraft.client.gui.components.Checkbox, Checkbox> {
public static SelectionChanged IGNORE_CHANGE = (a, b) -> {
};
@FunctionalInterface
public interface SelectionChanged {
void now(Checkbox checkBox, boolean selected);
}
private final boolean selected;
private final boolean showLabel;
private SelectionChanged onSelectionChange;
public Checkbox(
Value width,
Value height,
Component component,
boolean selected, boolean showLabel
) {
super(width, height, new CheckboxRenderer(), component);
onSelectionChange = IGNORE_CHANGE;
this.selected = selected;
this.showLabel = showLabel;
}
public Checkbox onChange(SelectionChanged onSelectionChange) {
this.onSelectionChange = onSelectionChange;
return this;
}
public boolean isChecked() {
if (vanillaComponent != null) return vanillaComponent.selected();
return selected;
}
@Override
protected net.minecraft.client.gui.components.Checkbox createVanillaComponent() {
Checkbox self = this;
net.minecraft.client.gui.components.Checkbox cb = new net.minecraft.client.gui.components.Checkbox(
0, 0,
relativeBounds.width, relativeBounds.height,
component,
selected,
showLabel
) {
@Override
public void onPress() {
super.onPress();
onSelectionChange.now(self, this.selected());
}
};
onSelectionChange.now(this, cb.selected());
return cb;
}
}

View file

@ -1,36 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.values.Value;
import net.minecraft.network.chat.Component;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class ColorPicker extends AbstractHorizontalStack<ColorPicker> {
ColorSwatch swatch;
Input input;
public ColorPicker(Value width, Value height, Component title, int color) {
super(width, height);
swatch = addColorSwatch(Value.fixed(20), Value.fixed(20), color);
input = addInput(Value.fill(), Value.fit(), title, ColorUtil.toRGBHex(color));
//input.setFilter(ColorUtil::validHexColor);
input.setResponder(this::inputResponder);
}
private void inputResponder(String value) {
if (ColorUtil.validHexColor(value)) {
int color = ColorUtil.parseHex(value);
swatch.setColor(color);
swatch.setOffsetInner(false);
swatch.setBorderColor(ColorUtil.BLACK);
} else {
swatch.setOffsetInner(true);
swatch.setBorderColor(ColorUtil.RED);
}
}
}

View file

@ -1,68 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.components.render.RenderHelper;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class ColorSwatch extends CustomRenderComponent<ColorSwatch> {
private int color;
private int borderColor = ColorUtil.BLACK;
private boolean offsetInner = false;
public ColorSwatch(Value width, Value height, int color) {
super(width, height);
this.color = color;
}
@Override
protected void customRender(PoseStack stack, int x, int y, float deltaTicks, Rectangle bounds, Rectangle clipRect) {
int o = offsetInner ? 2 : 1;
RenderHelper.outline(stack, 0, 0, bounds.width, bounds.height, borderColor);
GuiComponent.fill(stack, o, o, bounds.width - o, bounds.height - o, color);
}
public int getColor() {
return color;
}
public ColorSwatch setColor(int color) {
this.color = color;
return this;
}
public int getBorderColor() {
return borderColor;
}
public ColorSwatch setBorderColor(int color) {
this.borderColor = color;
return this;
}
public boolean getOffsetInner() {
return offsetInner;
}
public ColorSwatch setOffsetInner(boolean val) {
this.offsetInner = val;
return this;
}
@Override
public int getContentWidth() {
return 20;
}
@Override
public int getContentHeight() {
return 20;
}
}

View file

@ -1,11 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.values.Rectangle;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public interface ComponentWithBounds {
Rectangle getRelativeBounds();
}

View file

@ -1,291 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.input.RelativeContainerEventHandler;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.components.render.RenderHelper;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import java.util.LinkedList;
import java.util.List;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class Container extends LayoutComponent<Container.ContainerRenderer, Container> implements RelativeContainerEventHandler {
public static class ContainerRenderer implements ComponentRenderer {
Container linkedContainer;
@Override
public void renderInBounds(
PoseStack stack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
) {
if (linkedContainer != null) {
if ((linkedContainer.backgroundColor & 0xFF000000) != 0)
GuiComponent.fill(stack, 0, 0, bounds.width, bounds.height, linkedContainer.backgroundColor);
if ((linkedContainer.outlineColor & 0xFF000000) != 0)
RenderHelper.outline(stack, 0, 0, bounds.width, bounds.height, linkedContainer.outlineColor);
}
}
}
record Positional(int left, int top, LayoutComponent<?, ?> component) {
public int getMaxX() {
return left + component().getContentWidth();
}
public int getMaxY() {
return top + component().getContentHeight();
}
}
private final List<Positional> children = new LinkedList<>();
boolean dragging = false;
GuiEventListener focused = null;
boolean visible = true;
int paddingLeft, paddingTop, paddingRight, paddingBottom;
int backgroundColor = 0;
int outlineColor = 0;
public Container(
Value width,
Value height
) {
super(width, height, new ContainerRenderer());
renderer.linkedContainer = this;
}
public Container addChild(LayoutComponent<?, ?> child) {
children.add(new Positional(0, 0, child));
return this;
}
public Container addChild(int left, int top, LayoutComponent<?, ?> child) {
children.add(new Positional(left, top, child));
return this;
}
public Container setVisible(boolean v) {
this.visible = v;
return this;
}
public boolean getVisible() {
return this.visible;
}
public Container setBackgroundColor(int color) {
this.backgroundColor = color;
return this;
}
public int getBackgroundColor() {
return backgroundColor;
}
public Container setOutlineColor(int color) {
this.outlineColor = color;
return this;
}
public int getOutlineColor() {
return outlineColor;
}
public Container setPadding(int padding) {
return setPadding(padding, padding, padding, padding);
}
public Container setPadding(int left, int top, int right, int bottom) {
this.paddingLeft = left;
this.paddingTop = top;
this.paddingRight = right;
this.paddingBottom = bottom;
return this;
}
@Override
public int getContentWidth() {
return children.stream()
.map(Positional::getMaxX)
.reduce(0, Math::max) + paddingLeft + paddingRight;
}
@Override
public int getContentHeight() {
return children.stream()
.map(Positional::getMaxY)
.reduce(0, Math::max) + paddingTop + paddingBottom;
}
@Override
public Rectangle getInputBounds() {
return relativeBounds;
}
@Override
public List<? extends GuiEventListener> children() {
return children.stream().map(p -> p.component).toList();
}
@Override
public boolean isDragging() {
return dragging;
}
@Override
public void setDragging(boolean bl) {
dragging = bl;
}
@Nullable
@Override
public GuiEventListener getFocused() {
return focused;
}
@Override
public void setFocused(@Nullable GuiEventListener guiEventListener) {
focused = guiEventListener;
}
@Override
protected int updateContainerWidth(int containerWidth) {
int myWidth = width.calculateOrFill(containerWidth);
for (var child : children) {
child.component.width.calculateOrFill(myWidth - (paddingLeft + paddingRight));
child.component.updateContainerWidth(child.component.width.calculatedSize());
}
return myWidth;
}
@Override
protected int updateContainerHeight(int containerHeight) {
int myHeight = height.calculateOrFill(containerHeight);
for (var child : children) {
child.component.height.calculateOrFill(myHeight - (paddingTop + paddingBottom));
child.component.updateContainerHeight(child.component.height.calculatedSize());
}
return myHeight;
}
@Override
protected void renderInBounds(
PoseStack poseStack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle renderBounds,
Rectangle clipRect
) {
if (visible) {
super.renderInBounds(poseStack, mouseX, mouseY, deltaTicks, renderBounds, clipRect);
setClippingRect(clipRect);
for (var child : children) {
child.component.render(
poseStack, mouseX, mouseY, deltaTicks,
renderBounds, clipRect
);
}
setClippingRect(null);
}
}
@Override
void setRelativeBounds(int left, int top) {
super.setRelativeBounds(left, top);
for (var child : children) {
int childLeft = (relativeBounds.width - (paddingLeft + paddingRight)) - child.component.width.calculatedSize();
if (child.component.hAlign == Alignment.MIN) childLeft = 0;
else if (child.component.hAlign == Alignment.CENTER) childLeft /= 2;
int childTop = (relativeBounds.height - (paddingTop + paddingBottom)) - child.component.height.calculatedSize();
if (child.component.vAlign == Alignment.MIN) childTop = 0;
else if (child.component.vAlign == Alignment.CENTER) childTop /= 2;
child.component.setRelativeBounds(child.left + paddingLeft + childLeft, child.top + paddingTop + childTop);
}
}
@Override
public void updateScreenBounds(int worldX, int worldY) {
super.updateScreenBounds(worldX, worldY);
for (Positional p : children) {
p.component.updateScreenBounds(p.left + screenBounds.left, p.top + screenBounds.top);
}
}
@Override
public void mouseMoved(double d, double e) {
if (visible)
RelativeContainerEventHandler.super.mouseMoved(d, e);
}
@Override
public boolean mouseClicked(double d, double e, int i) {
if (visible)
return RelativeContainerEventHandler.super.mouseClicked(d, e, i);
return false;
}
@Override
public boolean mouseReleased(double d, double e, int i) {
if (visible)
return RelativeContainerEventHandler.super.mouseReleased(d, e, i);
return false;
}
@Override
public boolean mouseScrolled(double d, double e, double f) {
if (visible)
return RelativeContainerEventHandler.super.mouseScrolled(d, e, f);
return false;
}
@Override
public boolean mouseDragged(double d, double e, int i, double f, double g) {
if (visible)
return RelativeContainerEventHandler.super.mouseDragged(d, e, i, f, g);
return false;
}
@Override
public boolean isMouseOver(double x, double y) {
if (visible) {
boolean res = false;
for (var child : children) {
res |= child.component.isMouseOver(x, y);
}
return res || relativeBounds.contains(x, y);
}
return false;
}
public static Container create(LayoutComponent<?, ?> content) {
return create(Value.fit(), Value.fit(), content);
}
public static Container create(Value width, Value height, LayoutComponent<?, ?> content) {
Container c = new Container(width, height);
c.addChild(content);
return c;
}
}

View file

@ -1,48 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public abstract class CustomRenderComponent<C extends CustomRenderComponent<C>> extends LayoutComponent<CustomRenderComponent.CustomRenderRenderer<C>, C> {
public CustomRenderComponent(
Value width,
Value height
) {
super(width, height, new CustomRenderRenderer<>());
renderer.linkedComponent = (C) this;
}
protected abstract void customRender(
PoseStack stack,
int x,
int y,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
);
protected static class CustomRenderRenderer<C extends CustomRenderComponent<C>> implements ComponentRenderer {
C linkedComponent;
@Override
public void renderInBounds(
PoseStack stack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
) {
if (linkedComponent != null) {
linkedComponent.customRender(stack, mouseX, mouseY, deltaTicks, bounds, clipRect);
}
}
}
}

View file

@ -1,32 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.NullRenderer;
import org.betterx.ui.layout.values.Value;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class Empty extends LayoutComponent<NullRenderer, Empty> {
public Empty(
Value width,
Value height
) {
super(width, height, null);
}
@Override
public int getContentWidth() {
return 0;
}
@Override
public int getContentHeight() {
return 0;
}
@Override
public boolean isMouseOver(double d, double e) {
return false;
}
}

View file

@ -1,46 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.components.render.RenderHelper;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class HLine extends CustomRenderComponent {
private int color = ColorUtil.DEFAULT_TEXT;
public HLine(Value width, Value height) {
super(width, height);
this.vAlign = Alignment.CENTER;
this.hAlign = Alignment.CENTER;
}
public HLine setColor(int color) {
this.color = color;
return this;
}
@Override
protected void customRender(PoseStack stack, int x, int y, float deltaTicks, Rectangle bounds, Rectangle clipRect) {
int top = bounds.height - getContentHeight();
if (vAlign == Alignment.CENTER) top /= 2;
else if (vAlign == Alignment.MIN) top = 0;
RenderHelper.hLine(stack, 0, bounds.width, top, color);
}
@Override
public int getContentWidth() {
return 0;
}
@Override
public int getContentHeight() {
return 1;
}
}

View file

@ -1,205 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.values.Size;
import org.betterx.ui.layout.values.Value;
import org.betterx.ui.vanilla.VanillaScrollerRenderer;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class HorizontalStack extends AbstractHorizontalStack<HorizontalStack> {
public HorizontalStack(Value width, Value height) {
super(width, height);
}
public static HorizontalStack centered(LayoutComponent<?, ?> c) {
return new HorizontalStack(Value.fill(), Value.fill()).addFiller().add(c).addFiller();
}
public static HorizontalStack bottom(LayoutComponent<?, ?> c) {
return new HorizontalStack(Value.fill(), Value.fill()).add(c).addFiller();
}
protected HorizontalStack addEmpty(Value size) {
this.components.add(new Empty(size, Value.fixed(0)));
return this;
}
@Override
public HorizontalStack add(LayoutComponent<?, ?> c) {
return super.add(c);
}
public VerticalStack addColumn(Value width, Value height) {
return super.addColumn(width, height);
}
public VerticalStack addColumn() {
return super.addColumn();
}
@Override
public HorizontalStack addSpacer(int size) {
return super.addSpacer(size);
}
@Override
public HorizontalStack addSpacer(float percentage) {
return super.addSpacer(percentage);
}
@Override
public HorizontalStack addFiller() {
return super.addFiller();
}
@Override
public Button addButton(
Value width,
Value height,
Component component
) {
return super.addButton(width, height, component);
}
@Override
public Checkbox addCheckbox(
Value width,
Value height,
Component component,
boolean selected
) {
return super.addCheckbox(width, height, component, selected);
}
@Override
public ColorPicker addColorPicker(Value width, Value height, Component titleOrNull, int color) {
return super.addColorPicker(width, height, titleOrNull, color);
}
@Override
public ColorSwatch addColorSwatch(Value width, Value height, int color) {
return super.addColorSwatch(width, height, color);
}
@Override
public Container addContainered(Value width, Value height, LayoutComponent<?, ?> content) {
return super.addContainered(width, height, content);
}
@Override
public HLine addHLine(Value width, Value height) {
return super.addHLine(width, height);
}
@Override
public HLine addHorizontalLine(int height) {
return super.addHorizontalLine(height);
}
@Override
public HLine addHorizontalSeparator(int height) {
return super.addHorizontalSeparator(height);
}
@Override
public Image addIcon(ResourceLocation location, Size resourceSize) {
return super.addIcon(location, resourceSize);
}
@Override
public Image addImage(Value width, Value height, ResourceLocation location, Size resourceSize) {
return super.addImage(width, height, location, resourceSize);
}
@Override
public Input addInput(Value width, Value height, Component titleOrNull, String initialValue) {
return super.addInput(width, height, titleOrNull, initialValue);
}
@Override
public MultiLineText addMultilineText(Value width, Value height, Component text) {
return super.addMultilineText(width, height, text);
}
@Override
public Range<Double> addRange(
Value width,
Value height,
Component titleOrNull,
double minValue,
double maxValue,
double initialValue
) {
return super.addRange(width, height, titleOrNull, minValue, maxValue, initialValue);
}
@Override
public Range<Float> addRange(
Value width,
Value height,
Component titleOrNull,
float minValue,
float maxValue,
float initialValue
) {
return super.addRange(width, height, titleOrNull, minValue, maxValue, initialValue);
}
@Override
public Range<Integer> addRange(
Value width,
Value height,
Component titleOrNull,
int minValue,
int maxValue,
int initialValue
) {
return super.addRange(width, height, titleOrNull, minValue, maxValue, initialValue);
}
@Override
public Text addText(Value width, Value height, Component text) {
return super.addText(width, height, text);
}
@Override
public VerticalScroll<VanillaScrollerRenderer> addScrollable(LayoutComponent<?, ?> content) {
return super.addScrollable(content);
}
@Override
public VLine addVerticalLine(int width) {
return super.addVerticalLine(width);
}
@Override
public VLine addVerticalSeparator(int width) {
return super.addVerticalSeparator(width);
}
@Override
public VLine addVLine(Value width, Value height) {
return super.addVLine(width, height);
}
@Override
public VerticalScroll<VanillaScrollerRenderer> addScrollable(
Value width,
Value height,
LayoutComponent<?, ?> content
) {
return super.addScrollable(width, height, content);
}
@Override
public Item addItem(ItemStack stack) {
return super.addItem(stack);
}
}

View file

@ -1,98 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.RenderHelper;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Size;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.resources.ResourceLocation;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class Image extends CustomRenderComponent {
protected Rectangle uvRect;
public final ResourceLocation location;
protected float alpha;
protected Size resourceSize;
public Image(Value width, Value height, ResourceLocation location) {
this(width, height, location, new Size(16, 16));
}
public Image(Value width, Value height, ResourceLocation location, Size resourceSize) {
super(width, height);
this.location = location;
this.uvRect = new Rectangle(0, 0, resourceSize.width(), resourceSize.height());
this.resourceSize = resourceSize;
this.alpha = 1f;
}
public Image setAlpha(float a) {
alpha = a;
return this;
}
public float getAlpha() {
return alpha;
}
public Image setUvRect(int left, int top, int width, int height) {
setUvRect(new Rectangle(left, top, width, height));
return this;
}
public Image setUvRect(Rectangle rect) {
uvRect = rect;
return this;
}
public Rectangle getUvRect() {
return uvRect;
}
public Image setResourceSize(int width, int height) {
return setResourceSize(new Size(width, height));
}
public Image setResourceSize(Size sz) {
resourceSize = sz;
return this;
}
public Size getResourceSize() {
return resourceSize;
}
@Override
public int getContentWidth() {
return uvRect.width;
}
@Override
public int getContentHeight() {
return uvRect.height;
}
@Override
protected void customRender(
PoseStack stack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
) {
RenderHelper.renderImage(stack, 0, 0, bounds.width, bounds.height, location, resourceSize, uvRect, alpha);
}
@Override
public boolean isMouseOver(double d, double e) {
return false;
}
}

View file

@ -1,96 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.EditBoxRenderer;
import org.betterx.ui.layout.values.Value;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.network.chat.Component;
import net.minecraft.util.FormattedCharSequence;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Predicate;
@Environment(EnvType.CLIENT)
public class Input extends AbstractVanillaComponent<EditBox, Input> {
private Consumer<String> responder;
private BiFunction<String, Integer, FormattedCharSequence> formatter;
private Predicate<String> filter;
private String initialValue = "";
public Input(
Value width,
Value height,
Component component,
String initialValue
) {
super(width, height, new EditBoxRenderer(), component);
this.initialValue = initialValue;
}
@Override
protected EditBox createVanillaComponent() {
EditBox eb = new EditBox(renderer.getFont(),
0, 0,
relativeBounds.width, relativeBounds.height,
null,
component
);
if (responder != null) eb.setResponder(responder);
if (filter != null) eb.setFilter(filter);
if (formatter != null) eb.setFormatter(formatter);
eb.setValue(initialValue);
eb.setBordered(true);
eb.setEditable(true);
return eb;
}
public Input setResponder(Consumer<String> consumer) {
this.responder = consumer;
if (vanillaComponent != null) vanillaComponent.setResponder(responder);
return this;
}
public Input setFormatter(BiFunction<String, Integer, FormattedCharSequence> formatter) {
this.formatter = formatter;
if (vanillaComponent != null) vanillaComponent.setFormatter(formatter);
return this;
}
public Input setFilter(Predicate<String> filter) {
this.filter = filter;
if (vanillaComponent != null) vanillaComponent.setFilter(filter);
return this;
}
public String getValue() {
if (vanillaComponent != null) return vanillaComponent.getValue();
return "";
}
public Input setValue(String value) {
if (vanillaComponent != null) vanillaComponent.setValue(value);
else initialValue = value;
return this;
}
@Override
protected Component contentComponent() {
return Component.literal(initialValue + "..");
}
@Override
public boolean changeFocus(boolean bl) {
return super.changeFocus(bl);
}
@Override
public boolean mouseClicked(double x, double y, int button) {
return super.mouseClicked(x, y, button);
}
}

View file

@ -1,46 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.world.item.ItemStack;
public class Item extends CustomRenderComponent {
private ItemStack itemStack;
public Item(Value width, Value height) {
super(width, height);
}
public Item setItem(ItemStack item) {
this.itemStack = item;
return this;
}
@Override
protected void customRender(PoseStack stack, int x, int y, float deltaTicks, Rectangle bounds, Rectangle clipRect) {
final ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
itemRenderer.renderAndDecorateItem(Minecraft.getInstance().player, itemStack, bounds.left, bounds.top, 0);
itemRenderer.renderGuiItemDecorations(
Minecraft.getInstance().font,
itemStack,
bounds.left,
bounds.top,
"" + itemStack.getCount()
);
}
@Override
public int getContentWidth() {
return 16;
}
@Override
public int getContentHeight() {
return 16;
}
}

View file

@ -1,184 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public abstract class LayoutComponent<R extends ComponentRenderer, L extends LayoutComponent<R, L>> implements ComponentWithBounds, GuiEventListener {
protected final R renderer;
protected final Value width;
protected final Value height;
protected String debugName;
protected Rectangle relativeBounds;
protected Rectangle screenBounds;
protected Alignment vAlign = Alignment.MIN;
protected Alignment hAlign = Alignment.MIN;
public LayoutComponent(Value width, Value height, R renderer) {
this.width = width.attachComponent(this::getContentWidth);
this.height = height.attachComponent(this::getContentHeight);
this.renderer = renderer;
}
public void reCalculateLayout() {
updateContainerWidth(relativeBounds.width);
updateContainerHeight(relativeBounds.height);
setRelativeBounds(relativeBounds.left, relativeBounds.top);
updateScreenBounds(screenBounds.left, screenBounds.top);
}
protected int updateContainerWidth(int containerWidth) {
return width.setCalculatedSize(containerWidth);
}
protected int updateContainerHeight(int containerHeight) {
return height.setCalculatedSize(containerHeight);
}
void setRelativeBounds(int left, int top) {
relativeBounds = new Rectangle(left, top, width.calculatedSize(), height.calculatedSize());
onBoundsChanged();
}
public void updateScreenBounds(int worldX, int worldY) {
screenBounds = relativeBounds.movedBy(worldX, worldY);
}
protected void onBoundsChanged() {
}
public Rectangle getRelativeBounds() {
return relativeBounds;
}
public Rectangle getScreenBounds() {
return screenBounds;
}
public abstract int getContentWidth();
public abstract int getContentHeight();
public int fillWidth(int parentSize, int fillSize) {
return width.fill(fillSize);
}
public int fillHeight(int parentSize, int fillSize) {
return height.fill(fillSize);
}
public int getWidth() {
return width.calculatedSize();
}
public int getHeight() {
return height.calculatedSize();
}
protected final void setClippingRect(Rectangle clippingRect) {
if (clippingRect == null) {
RenderSystem.disableScissor();
return;
}
final double uiScale = Minecraft.getInstance().getWindow().getGuiScale();
final int windowHeight = Minecraft.getInstance().getWindow().getHeight();
RenderSystem.enableScissor(
(int) (clippingRect.left * uiScale),
(int) (windowHeight - (clippingRect.bottom()) * uiScale),
(int) (clippingRect.width * uiScale),
(int) ((clippingRect.height) * uiScale)
);
}
public void render(
PoseStack poseStack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle parentBounds,
Rectangle clipRect
) {
Rectangle r = relativeBounds.movedBy(parentBounds.left, parentBounds.top);
Rectangle clip = r.intersect(clipRect);
poseStack.pushPose();
poseStack.translate(relativeBounds.left, relativeBounds.top, 0);
//if (r.overlaps(clip))
{
renderInBounds(poseStack, mouseX - relativeBounds.left, mouseY - relativeBounds.top, deltaTicks, r, clip);
}
poseStack.popPose();
}
protected void renderInBounds(
PoseStack poseStack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle renderBounds,
Rectangle clipRect
) {
if (renderer != null) {
setClippingRect(clipRect);
renderer.renderInBounds(poseStack, mouseX, mouseY, deltaTicks, renderBounds, clipRect);
setClippingRect(null);
}
}
@Override
public String toString() {
return super.toString() + "(" +
(debugName == null ? "" : debugName + " - ") +
relativeBounds + ", " +
width.calculatedSize() + "x" + height.calculatedSize() +
")";
}
public L alignTop() {
vAlign = Alignment.MIN;
return (L) this;
}
public L alignBottom() {
vAlign = Alignment.MAX;
return (L) this;
}
public L centerVertical() {
vAlign = Alignment.CENTER;
return (L) this;
}
public L alignLeft() {
hAlign = Alignment.MIN;
return (L) this;
}
public L alignRight() {
hAlign = Alignment.MAX;
return (L) this;
}
public L centerHorizontal() {
hAlign = Alignment.CENTER;
return (L) this;
}
public L setDebugName(String d) {
debugName = d;
return (L) this;
}
@Override
public boolean isMouseOver(double d, double e) {
return relativeBounds.contains(d, e);
}
}

View file

@ -1,180 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.components.render.TextProvider;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.MultiLineLabel;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.util.FormattedCharSequence;
import com.google.common.collect.ImmutableList;
import java.util.Iterator;
import java.util.List;
record LineWithWidth(FormattedCharSequence text, int width) {
}
public class MultiLineText extends LayoutComponent<MultiLineText.MultiLineTextRenderer, MultiLineText> {
net.minecraft.network.chat.Component text;
int color = ColorUtil.DEFAULT_TEXT;
protected MultiLineLabel multiLineLabel;
int bufferedContentWidth = 0;
List<LineWithWidth> lines = List.of();
public MultiLineText(
Value width,
Value height,
final net.minecraft.network.chat.Component text
) {
super(width, height, new MultiLineTextRenderer());
renderer.linkedComponent = this;
this.text = text;
updatedContentWidth();
}
public MultiLineText setColor(int cl) {
this.color = cl;
return this;
}
public static Component parse(Component text) {
String[] parts = text.getString().split("\\*\\*");
if (parts.length > 0) {
boolean bold = false;
MutableComponent c = Component.literal(parts[0]);
for (int i = 1; i < parts.length; i++) {
bold = !bold;
c.append(Component.literal(parts[i]).setStyle(Style.EMPTY.withBold(bold)));
}
return c;
}
return text;
}
public MultiLineText setText(Component text) {
this.text = text;
this.updatedContentWidth();
if (multiLineLabel != null) {
multiLineLabel = createVanillaComponent();
}
return this;
}
protected MultiLineLabel createVanillaComponent() {
final int wd = relativeBounds == null ? width.calculatedSize() : relativeBounds.width;
lines = renderer.getFont()
.split(text, wd)
.stream()
.map((component) -> new LineWithWidth(component, renderer.getFont().width(component)))
.collect(ImmutableList.toImmutableList());
return MultiLineLabel.create(renderer.getFont(), text, wd);
}
protected void updatedContentWidth() {
String[] lines = text.getString().split("\n");
if (lines.length == 0) bufferedContentWidth = 0;
else {
String line = lines[0];
for (int i = 1; i < lines.length; i++)
if (lines[i].length() > line.length())
line = lines[i];
bufferedContentWidth = renderer.getWidth(Component.literal(line));
}
}
@Override
protected void onBoundsChanged() {
super.onBoundsChanged();
multiLineLabel = createVanillaComponent();
}
@Override
public int getContentWidth() {
return bufferedContentWidth;
}
@Override
public int getContentHeight() {
return renderer.getHeight(text);
}
protected static class MultiLineTextRenderer implements ComponentRenderer, TextProvider {
MultiLineText linkedComponent;
@Override
public int getWidth(Component c) {
return getFont().width(c.getVisualOrderText());
}
@Override
public int getHeight(net.minecraft.network.chat.Component c) {
if (linkedComponent == null) return 20;
MultiLineLabel ml;
if (linkedComponent.multiLineLabel != null) ml = linkedComponent.multiLineLabel;
else ml = linkedComponent.createVanillaComponent();
return ml.getLineCount() * getLineHeight(c);
}
@Override
public void renderInBounds(
PoseStack stack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
) {
if (linkedComponent != null && linkedComponent.multiLineLabel != null) {
int top = bounds.height - getHeight(linkedComponent.text);
if (linkedComponent.vAlign == Alignment.MIN) top = 0;
if (linkedComponent.vAlign == Alignment.CENTER) top /= 2;
if (linkedComponent.hAlign == Alignment.CENTER) {
linkedComponent.multiLineLabel.renderCentered(
stack, bounds.width / 2, top,
getLineHeight(linkedComponent.text),
linkedComponent.color
);
} else if (linkedComponent.hAlign == Alignment.MAX) {
int lineY = 0;
int lineHeight = getLineHeight(linkedComponent.text);
for (Iterator<LineWithWidth> iter = linkedComponent.lines.iterator(); iter.hasNext(); lineY += lineHeight) {
LineWithWidth textWithWidth = iter.next();
getFont().drawShadow(
stack,
textWithWidth.text(),
linkedComponent.width.calculatedSize() - textWithWidth.width(),
lineY,
linkedComponent.color
);
}
} else {
linkedComponent.multiLineLabel.renderLeftAligned(
stack, 0, top,
getLineHeight(linkedComponent.text),
linkedComponent.color
);
}
}
}
}
@Override
public boolean isMouseOver(double d, double e) {
return false;
}
}

View file

@ -1,108 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.input.RelativeContainerEventHandler;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import java.util.List;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class Panel implements ComponentWithBounds, RelativeContainerEventHandler, NarratableEntry, Renderable {
protected LayoutComponent<?, ?> child;
List<? extends GuiEventListener> listeners = List.of();
public final Rectangle bounds;
public Panel(int width, int height) {
this(0, 0, width, height);
}
public Panel(int left, int top, int width, int height) {
bounds = new Rectangle(left, top, width, height);
}
public void setChild(LayoutComponent<?, ?> c) {
this.child = c;
listeners = List.of(c);
}
public void calculateLayout() {
if (child != null) {
child.updateContainerWidth(bounds.width);
child.updateContainerHeight(bounds.height);
child.setRelativeBounds(0, 0);
child.updateScreenBounds(bounds.left, bounds.top);
}
}
@Override
public Rectangle getRelativeBounds() {
return bounds;
}
@Override
public List<? extends GuiEventListener> children() {
return listeners;
}
@Override
public Rectangle getInputBounds() {
return bounds;
}
boolean dragging = false;
@Override
public boolean isDragging() {
return dragging;
}
@Override
public void setDragging(boolean bl) {
dragging = bl;
}
GuiEventListener focused;
@Nullable
@Override
public GuiEventListener getFocused() {
return focused;
}
@Override
public void setFocused(@Nullable GuiEventListener guiEventListener) {
focused = guiEventListener;
}
@Override
public NarrationPriority narrationPriority() {
return NarrationPriority.NONE;
}
@Override
public void updateNarration(NarrationElementOutput narrationElementOutput) {
}
@Override
public void render(PoseStack poseStack, int mouseX, int mouseY, float deltaTicks) {
if (child != null) {
poseStack.pushPose();
poseStack.translate(bounds.left, bounds.top, 0);
child.render(poseStack, mouseX - bounds.left, mouseY - bounds.top, deltaTicks, bounds, bounds);
poseStack.popPose();
}
}
}

View file

@ -1,92 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.RangeRenderer;
import org.betterx.ui.layout.values.Value;
import org.betterx.ui.vanilla.Slider;
import net.minecraft.network.chat.Component;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class Range<N extends Number> extends AbstractVanillaComponent<Slider<N>, Range<N>> {
@FunctionalInterface
public interface ValueChanged<N extends Number> {
void now(Range<N> range, N newValue);
}
private ValueChanged<N> onChange;
private final N minValue;
private final N maxValue;
private final N initialValue;
public Range(
Value width,
Value height,
Component component,
N minValue,
N maxValue,
N initialValue
) {
super(width, height, new RangeRenderer<>(), component);
this.onChange = (a, b) -> {
};
this.minValue = minValue;
this.maxValue = maxValue;
this.initialValue = initialValue;
}
public Range(
Value width,
Value height,
N minValue,
N maxValue,
N initialValue
) {
this(width, height, null, minValue, maxValue, initialValue);
}
public Range<N> onChange(ValueChanged<N> onChange) {
this.onChange = onChange;
return this;
}
@Override
protected Slider<N> createVanillaComponent() {
Range<N> self = this;
return new Slider<>(
0,
0,
relativeBounds.width,
relativeBounds.height,
component,
minValue,
maxValue,
initialValue,
(s, v) -> onChange.now(self, v)
);
}
public N getValue() {
return vanillaComponent.currentValue();
}
@Override
protected Component contentComponent() {
Slider<N> dummy = new Slider<>(
0,
0,
100,
20,
component,
minValue,
maxValue,
initialValue,
(a, b) -> {
}
);
return dummy.getValueComponent(maxValue);
}
}

View file

@ -1,158 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.values.Value;
import net.minecraft.network.chat.Component;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import java.util.LinkedList;
import java.util.List;
@Environment(EnvType.CLIENT)
public class Tabs extends AbstractVerticalStack<Tabs> {
@FunctionalInterface
public interface OnPageChange {
void now(Tabs tabs, int pageIndex);
}
private final HorizontalStack buttons;
private final Container content;
private final List<Container> pageList = new LinkedList<>();
private final List<Button> buttonList = new LinkedList<>();
private int initialPage = 0;
private OnPageChange onPageChange;
public Tabs(Value width, Value height) {
super(width, height);
buttons = new HorizontalStack(Value.fill(), Value.fit());
content = new Container(Value.fill(), Value.fill());
this.add(buttons);
this.add(content);
setBackgroundColor(0x77000000);
setPadding(4);
}
public Tabs addPage(Component title, LayoutComponent<?, ?> content) {
final Container c = new Container(Value.fill(), Value.fill());
c.addChild(content);
this.content.addChild(c);
pageList.add(c);
if (!buttons.isEmpty()) buttons.addSpacer(4);
Button b = new Button(Value.fit(), Value.fit(), title).alignBottom();
b.onPress((bt) -> {
for (Container cc : pageList) {
cc.setVisible(cc == c);
}
for (Button bb : buttonList) {
bb.glow = bb == b;
}
if (onPageChange != null) {
for (int i = 0; i < buttonList.size(); i++) {
if (buttonList.get(i).glow) onPageChange.now(this, i);
}
}
});
buttons.add(b);
buttonList.add(b);
return this;
}
public Tabs onPageChange(OnPageChange e) {
this.onPageChange = e;
return this;
}
public Button getButton(int idx) {
return buttonList.get(idx);
}
public Tabs setBackgroundColor(int color) {
content.setBackgroundColor(color);
return this;
}
public int getBackgroundColor() {
return content.getBackgroundColor();
}
public Tabs setOutlineColor(int color) {
content.setOutlineColor(color);
return this;
}
public int getOutlineColor() {
return content.getOutlineColor();
}
public Tabs setPadding(int padding) {
content.setPadding(padding);
return this;
}
public Tabs setPadding(int left, int top, int right, int bottom) {
content.setPadding(left, top, right, bottom);
return this;
}
@Override
public int getContentWidth() {
return content.getContentWidth();
}
public Tabs selectPage(int idx) {
for (int i = 0; i < pageList.size(); i++) {
pageList.get(i).setVisible(i == idx);
buttonList.get(i).glow = i == idx;
}
initialPage = idx;
return this;
}
public int getSelectedPage() {
return initialPage;
}
public Tabs addComponent(LayoutComponent<?, ?> title) {
buttons.add(title);
return this;
}
@Override
protected void onBoundsChanged() {
super.onBoundsChanged();
selectPage(initialPage);
}
@Override
public Tabs addFiller() {
buttons.addFiller();
return this;
}
@Override
public Tabs addSpacer(int size) {
buttons.addSpacer(size);
return this;
}
@Override
public Tabs addSpacer(float percentage) {
buttons.addSpacer(percentage);
return this;
}
}

View file

@ -1,89 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.components.render.TextProvider;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.network.chat.Component;
public class Text extends LayoutComponent<Text.TextRenderer, Text> {
net.minecraft.network.chat.Component text;
int color = ColorUtil.DEFAULT_TEXT;
public Text(
Value width,
Value height,
net.minecraft.network.chat.Component text
) {
super(width, height, new TextRenderer());
vAlign = Alignment.CENTER;
renderer.linkedComponent = this;
this.text = text;
}
public Text setColor(int cl) {
this.color = cl;
return this;
}
public Text setText(Component text) {
this.text = text;
return this;
}
@Override
public int getContentWidth() {
return renderer.getWidth(text);
}
@Override
public int getContentHeight() {
return renderer.getHeight(text);
}
public static class TextRenderer implements ComponentRenderer, TextProvider {
Text linkedComponent;
@Override
public int getWidth(net.minecraft.network.chat.Component c) {
return getFont().width(c.getVisualOrderText());
}
@Override
public int getHeight(net.minecraft.network.chat.Component c) {
return TextProvider.super.getLineHeight(c);
}
@Override
public void renderInBounds(
PoseStack stack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
) {
if (linkedComponent != null) {
int left = bounds.width - getWidth(linkedComponent.text);
if (linkedComponent.hAlign == Alignment.MIN) left = 0;
if (linkedComponent.hAlign == Alignment.CENTER) left /= 2;
int top = bounds.height - getLineHeight(linkedComponent.text);
if (linkedComponent.vAlign == Alignment.MIN) top = 0;
if (linkedComponent.vAlign == Alignment.CENTER) top = top / 2 + 1;
GuiComponent.drawString(stack, getFont(), linkedComponent.text, left, top, linkedComponent.color);
}
}
}
@Override
public boolean isMouseOver(double d, double e) {
return false;
}
}

View file

@ -1,51 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.components.render.RenderHelper;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class VLine extends CustomRenderComponent {
private int color = ColorUtil.DEFAULT_TEXT;
public VLine(Value width, Value height) {
super(width, height);
this.vAlign = Alignment.CENTER;
this.hAlign = Alignment.CENTER;
}
public VLine setColor(int color) {
this.color = color;
return this;
}
@Override
protected void customRender(PoseStack stack, int x, int y, float deltaTicks, Rectangle bounds, Rectangle clipRect) {
int left = bounds.height - getContentHeight();
if (hAlign == Alignment.CENTER) left /= 2;
else if (hAlign == Alignment.MIN) left = 0;
RenderHelper.vLine(stack, left, 0, bounds.height, color);
}
@Override
public int getContentWidth() {
return 1;
}
@Override
public int getContentHeight() {
return 0;
}
@Override
public boolean isMouseOver(double d, double e) {
return false;
}
}

View file

@ -1,314 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.NullRenderer;
import org.betterx.ui.layout.components.render.ScrollerRenderer;
import org.betterx.ui.layout.values.Alignment;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import org.betterx.ui.vanilla.VanillaScrollerRenderer;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.events.ContainerEventHandler;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import java.util.List;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class VerticalScroll<RS extends ScrollerRenderer> extends LayoutComponent<NullRenderer, VerticalScroll<RS>> implements ContainerEventHandler {
protected LayoutComponent<?, ?> child;
protected final RS scrollerRenderer;
protected int dist;
protected double scrollerY;
protected int scrollerHeight;
protected int travel;
protected int topOffset;
protected int scrollerPadding;
protected boolean keepSpaceForScrollbar = true;
public VerticalScroll(Value width, Value height, RS scrollerRenderer) {
super(width, height, new NullRenderer());
this.scrollerRenderer = scrollerRenderer;
this.scrollerPadding = scrollerRenderer.scrollerPadding();
}
public static VerticalScroll<VanillaScrollerRenderer> create(LayoutComponent<?, ?> c) {
return create(Value.relative(1), Value.relative(1), c);
}
public static VerticalScroll<VanillaScrollerRenderer> create(
Value width,
Value height,
LayoutComponent<?, ?> c
) {
VerticalScroll<VanillaScrollerRenderer> res = new VerticalScroll<>(
width,
height,
VanillaScrollerRenderer.DEFAULT
);
res.setChild(c);
return res;
}
List<LayoutComponent<?, ?>> children = List.of();
public VerticalScroll<RS> setChild(LayoutComponent<?, ?> c) {
this.child = c;
children = List.of(child);
return this;
}
public VerticalScroll<RS> setScrollerPadding(int pad) {
this.scrollerPadding = pad;
return this;
}
public VerticalScroll<RS> setKeepSpaceForScrollbar(boolean value) {
keepSpaceForScrollbar = value;
return this;
}
@Override
protected int updateContainerWidth(int containerWidth) {
int myWidth = width.calculateOrFill(containerWidth);
if (child != null) {
child.width.calculateOrFill(myWidth - scrollerWidth());
child.updateContainerWidth(child.width.calculatedSize());
}
return myWidth;
}
@Override
protected int updateContainerHeight(int containerHeight) {
int myHeight = height.calculateOrFill(containerHeight);
if (child != null) {
child.height.calculateOrFill(myHeight);
child.updateContainerHeight(child.height.calculatedSize());
}
return myHeight;
}
protected int scrollerWidth() {
return scrollerRenderer.scrollerWidth() + scrollerPadding;
}
@Override
public int getContentWidth() {
return scrollerWidth() + (child != null
? child.getContentWidth()
: 0);
}
@Override
public int getContentHeight() {
return child != null ? child.getContentHeight() : 0;
}
@Override
void setRelativeBounds(int left, int top) {
super.setRelativeBounds(left, top);
if (child != null) {
int width = relativeBounds.width;
boolean willNeedScrollBar = keepSpaceForScrollbar || child.height.calculatedSize() > relativeBounds.height;
if (willNeedScrollBar) width -= scrollerWidth();
int childLeft = width - child.width.calculatedSize();
if (child.hAlign == Alignment.MIN) childLeft = 0;
else if (child.hAlign == Alignment.CENTER) childLeft /= 2;
int childTop = relativeBounds.height - child.height.calculatedSize();
if (child.vAlign == Alignment.MIN) childTop = 0;
else if (child.vAlign == Alignment.CENTER) childTop /= 2;
child.setRelativeBounds(childLeft, childTop);
}
updateScrollViewMetrics();
}
@Override
public void updateScreenBounds(int worldX, int worldY) {
super.updateScreenBounds(worldX, worldY);
child.updateScreenBounds(screenBounds.left, screenBounds.top);
}
@Override
protected void renderInBounds(
PoseStack poseStack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle renderBounds,
Rectangle clipRect
) {
super.renderInBounds(poseStack, mouseX, mouseY, deltaTicks, renderBounds, clipRect);
if (showScrollBar()) {
if (child != null) {
poseStack.pushPose();
poseStack.translate(0, scrollerOffset(), 0);
setClippingRect(clipRect);
child.render(
poseStack, mouseX, mouseY - scrollerOffset(), deltaTicks,
renderBounds.movedBy(0, scrollerOffset(), scrollerWidth(), 0),
clipRect
);
setClippingRect(null);
poseStack.popPose();
}
scrollerRenderer.renderScrollBar(renderBounds, saveScrollerY(), scrollerHeight);
} else {
if (child != null) {
child.render(poseStack, mouseX, mouseY, deltaTicks, renderBounds, clipRect);
}
}
}
private boolean mouseDown = false;
private int mouseDownY = 0;
private int scrollerDownY = 0;
protected void updateScrollViewMetrics() {
final int view = relativeBounds.height;
final int content = child.relativeBounds.height;
this.dist = content - view;
this.scrollerHeight = Math.max(scrollerRenderer.scrollerHeight(), (view * view) / content);
this.travel = view - this.scrollerHeight;
this.topOffset = 0;
this.scrollerY = 0;
}
protected int saveScrollerY() {
return (int) Math.max(0, Math.min(travel, scrollerY));
}
protected int scrollerOffset() {
return -((int) (((float) saveScrollerY() / travel) * this.dist));
}
public boolean showScrollBar() {
return child.relativeBounds.height > relativeBounds.height;
}
@Override
public void mouseMoved(double x, double y) {
if (child != null && relativeBounds.contains(x, y))
ContainerEventHandler.super.mouseMoved(x - relativeBounds.left, y - relativeBounds.top - scrollerOffset());
}
@Override
public List<? extends GuiEventListener> children() {
return children;
}
@Override
public boolean mouseClicked(double x, double y, int button) {
Rectangle scroller = scrollerRenderer.getScrollerBounds(relativeBounds);
Rectangle picker = scrollerRenderer.getPickerBounds(scroller, saveScrollerY(), scrollerHeight);
if (picker.contains((int) x, (int) y)) {
mouseDown = true;
mouseDownY = (int) y;
scrollerDownY = saveScrollerY();
return true;
}
if (child != null && relativeBounds.contains(x, y))
return ContainerEventHandler.super.mouseClicked(
x - relativeBounds.left,
y - relativeBounds.top - scrollerOffset(),
button
);
return false;
}
@Override
public boolean mouseReleased(double x, double y, int button) {
mouseDown = false;
if (child != null && relativeBounds.contains(x, y))
return ContainerEventHandler.super.mouseReleased(
x - relativeBounds.left,
y - relativeBounds.top - scrollerOffset(),
button
);
return false;
}
@Override
public boolean mouseDragged(double x, double y, int button, double x2, double y2) {
if (mouseDown) {
int delta = (int) y - mouseDownY;
scrollerY = scrollerDownY + delta;
return true;
}
if (child != null && relativeBounds.contains(x, y))
return ContainerEventHandler.super.mouseDragged(
x - relativeBounds.left,
y - relativeBounds.top - scrollerOffset(),
button,
x2,
y2
);
return false;
}
@Override
public boolean mouseScrolled(double x, double y, double delta) {
boolean didCapture = false;
if (child != null && relativeBounds.contains(x, y)) {
didCapture = ContainerEventHandler.super.mouseScrolled(
x - relativeBounds.left,
y - relativeBounds.top - scrollerOffset(),
delta
);
}
if (!didCapture) {
scrollerY = Math.max(0, Math.min(travel, scrollerY)) + delta;
return true;
} else {
System.out.println("Child did capture scroll");
}
return false;
}
GuiEventListener focused;
@Override
@Nullable
public GuiEventListener getFocused() {
return focused;
}
@Override
public void setFocused(@Nullable GuiEventListener guiEventListener) {
focused = guiEventListener;
}
boolean dragging;
@Override
public boolean isDragging() {
return this.dragging;
}
@Override
public void setDragging(boolean bl) {
dragging = bl;
}
@Override
public boolean isMouseOver(double x, double y) {
if (child != null) {
if (child.isMouseOver(x - relativeBounds.left, y - relativeBounds.top - scrollerOffset()))
return true;
}
return relativeBounds.contains(x, y);
}
}

View file

@ -1,217 +0,0 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.values.Size;
import org.betterx.ui.layout.values.Value;
import org.betterx.ui.vanilla.VanillaScrollerRenderer;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class VerticalStack extends AbstractVerticalStack<VerticalStack> {
public VerticalStack(Value width, Value height) {
super(width, height);
}
public static VerticalStack centered(LayoutComponent<?, ?> c) {
return new VerticalStack(Value.relative(1), Value.relative(1)).addFiller().add(c).addFiller();
}
public static VerticalStack bottom(LayoutComponent<?, ?> c) {
return new VerticalStack(Value.relative(1), Value.relative(1)).add(c).addFiller();
}
@Override
protected VerticalStack addEmpty(Value size) {
this.components.add(new Empty(Value.fixed(0), size));
return this;
}
public HorizontalStack indent(int width) {
var h = new HorizontalStack(Value.fitOrFill(), Value.fit());
h.addSpacer(width);
add(h);
return h;
}
@Override
public HorizontalStack addRow(Value width, Value height) {
return super.addRow(width, height);
}
@Override
public HorizontalStack addRow() {
return super.addRow();
}
@Override
public VerticalStack add(LayoutComponent<?, ?> c) {
return super.add(c);
}
@Override
public VerticalStack addSpacer(int size) {
return super.addSpacer(size);
}
@Override
public VerticalStack addSpacer(float percentage) {
return super.addSpacer(percentage);
}
@Override
public VerticalStack addFiller() {
return super.addFiller();
}
@Override
public Button addButton(
Value width,
Value height,
Component component
) {
return super.addButton(width, height, component);
}
@Override
public Checkbox addCheckbox(
Value width,
Value height,
Component component,
boolean selected
) {
return super.addCheckbox(width, height, component, selected);
}
@Override
public ColorPicker addColorPicker(Value width, Value height, Component titleOrNull, int color) {
return super.addColorPicker(width, height, titleOrNull, color);
}
@Override
public ColorSwatch addColorSwatch(Value width, Value height, int color) {
return super.addColorSwatch(width, height, color);
}
@Override
public Container addContainered(Value width, Value height, LayoutComponent<?, ?> content) {
return super.addContainered(width, height, content);
}
@Override
public HLine addHLine(Value width, Value height) {
return super.addHLine(width, height);
}
@Override
public HLine addHorizontalLine(int height) {
return super.addHorizontalLine(height);
}
@Override
public HLine addHorizontalSeparator(int height) {
return super.addHorizontalSeparator(height);
}
@Override
public Image addIcon(ResourceLocation location, Size resourceSize) {
return super.addIcon(location, resourceSize);
}
@Override
public Image addImage(Value width, Value height, ResourceLocation location, Size resourceSize) {
return super.addImage(width, height, location, resourceSize);
}
@Override
public Input addInput(Value width, Value height, Component titleOrNull, String initialValue) {
return super.addInput(width, height, titleOrNull, initialValue);
}
@Override
public MultiLineText addMultilineText(Value width, Value height, Component text) {
return super.addMultilineText(width, height, text);
}
@Override
public Range<Double> addRange(
Value width,
Value height,
Component titleOrNull,
double minValue,
double maxValue,
double initialValue
) {
return super.addRange(width, height, titleOrNull, minValue, maxValue, initialValue);
}
@Override
public Range<Float> addRange(
Value width,
Value height,
Component titleOrNull,
float minValue,
float maxValue,
float initialValue
) {
return super.addRange(width, height, titleOrNull, minValue, maxValue, initialValue);
}
@Override
public Range<Integer> addRange(
Value width,
Value height,
Component titleOrNull,
int minValue,
int maxValue,
int initialValue
) {
return super.addRange(width, height, titleOrNull, minValue, maxValue, initialValue);
}
@Override
public Text addText(Value width, Value height, Component text) {
return super.addText(width, height, text);
}
@Override
public VerticalScroll<VanillaScrollerRenderer> addScrollable(LayoutComponent<?, ?> content) {
return super.addScrollable(content);
}
@Override
public VLine addVerticalLine(int width) {
return super.addVerticalLine(width);
}
@Override
public VLine addVerticalSeparator(int width) {
return super.addVerticalSeparator(width);
}
@Override
public VLine addVLine(Value width, Value height) {
return super.addVLine(width, height);
}
@Override
public VerticalScroll<VanillaScrollerRenderer> addScrollable(
Value width,
Value height,
LayoutComponent<?, ?> content
) {
return super.addScrollable(width, height, content);
}
@Override
public Item addItem(ItemStack stack) {
return super.addItem(stack);
}
}

View file

@ -1,55 +0,0 @@
package org.betterx.ui.layout.components.input;
import org.betterx.ui.layout.values.Rectangle;
import net.minecraft.client.gui.components.events.ContainerEventHandler;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import java.util.Optional;
@Environment(EnvType.CLIENT)
public interface RelativeContainerEventHandler extends ContainerEventHandler {
Rectangle getInputBounds();
default Optional<GuiEventListener> getChildAt(double d, double e) {
Rectangle r = getInputBounds();
return ContainerEventHandler.super.getChildAt(d, e);
}
default boolean mouseClicked(double d, double e, int i) {
if (getFocused() != null) {
//getFocused().mouseClicked(d, e, i);
}
Rectangle r = getInputBounds();
return ContainerEventHandler.super.mouseClicked(d - r.left, e - r.top, i);
}
default boolean mouseReleased(double d, double e, int i) {
Rectangle r = getInputBounds();
return ContainerEventHandler.super.mouseReleased(d - r.left, e - r.top, i);
}
default boolean mouseDragged(double d, double e, int i, double f, double g) {
Rectangle r = getInputBounds();
return ContainerEventHandler.super.mouseDragged(d - r.left, e - r.top, i, f - r.left, g - r.top);
}
default boolean mouseScrolled(double d, double e, double f) {
Rectangle r = getInputBounds();
return ContainerEventHandler.super.mouseScrolled(d - r.left, e - r.top, f);
}
default boolean isMouseOver(double x, double y) {
Rectangle r = getInputBounds();
boolean res = false;
for (GuiEventListener c : children()) {
res |= c.isMouseOver(x - r.left, y - r.top);
}
return res || r.contains(x, y);
}
}

View file

@ -1,96 +0,0 @@
package org.betterx.ui.layout.components.render;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.components.AbstractVanillaComponentRenderer;
import org.betterx.ui.layout.components.Button;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class ButtonRenderer extends AbstractVanillaComponentRenderer<net.minecraft.client.gui.components.Button, Button> {
double deltaSum = 0;
double deltaSum2 = .34;
double deltaSum3 = .12;
@Override
public void renderInBounds(
PoseStack poseStack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
) {
super.renderInBounds(poseStack, mouseX, mouseY, deltaTicks, bounds, clipRect);
deltaSum += deltaTicks * 0.03;
deltaSum2 += deltaTicks * 0.032;
deltaSum3 += deltaTicks * 0.028;
if (getLinkedComponent() != null && getLinkedComponent().isGlowing()) {
RenderHelper.outline(poseStack, 0, 0, bounds.width, bounds.height, ColorUtil.YELLOW);
int len = 2 * bounds.width + 2 * bounds.height;
deltaSum = deltaSum - (int) deltaSum;
int pos = (int) (len * deltaSum);
drawMoving(poseStack, bounds, pos);
drawMoving(poseStack, bounds, pos + 2);
drawMoving(poseStack, bounds, pos + 3);
drawMoving(poseStack, bounds, pos + 4);
drawMoving(poseStack, bounds, pos + 5);
drawMoving(poseStack, bounds, pos + 7);
deltaSum2 = deltaSum2 - (int) deltaSum2;
pos = (int) (len * deltaSum2);
drawMoving(poseStack, bounds, pos + len / 3);
drawMoving(poseStack, bounds, pos + 2 + len / 3);
drawMoving(poseStack, bounds, pos + 3 + len / 3);
drawMoving(poseStack, bounds, pos + 4 + len / 3);
drawMoving(poseStack, bounds, pos + 5 + len / 3);
drawMoving(poseStack, bounds, pos + 7 + len / 3);
deltaSum3 = deltaSum3 - (int) deltaSum3;
pos = (int) (len * deltaSum3);
drawMoving(poseStack, bounds, pos + 2 * len / 3);
drawMoving(poseStack, bounds, pos + 2 + 2 * len / 3);
drawMoving(poseStack, bounds, pos + 3 + 2 * len / 3);
drawMoving(poseStack, bounds, pos + 4 + 2 * len / 3);
drawMoving(poseStack, bounds, pos + 5 + 2 * len / 3);
drawMoving(poseStack, bounds, pos + 7 + 2 * len / 3);
}
}
private void drawMoving(PoseStack poseStack, Rectangle bounds, int pos) {
int bh = bounds.width + bounds.height;
pos = pos % (2 * bh);
int x, y;
/**
* pos <= w : x=pos, y=0
* pos > w && pos<=w+h : x=w, y=pos-w
* pos >w+h && pos<=2w+h : x=2w +h - pos, y=h
* pos>2w+h : x=0, y=2w+2h-pos
*/
if (pos <= bounds.width) {
x = pos;
y = 0;
} else if (pos <= bh) {
x = bounds.width - 1;
y = pos - bounds.width;
} else if (pos <= bh + bounds.width) {
x = bh + bounds.width - pos;
y = bounds.height - 1;
} else {
x = 0;
y = 2 * bh - pos;
}
GuiComponent.fill(poseStack, x, y, x + 1, y + 1, ColorUtil.BLACK);
}
}

View file

@ -1,11 +0,0 @@
package org.betterx.ui.layout.components.render;
import org.betterx.ui.layout.components.AbstractVanillaComponentRenderer;
import org.betterx.ui.layout.components.Checkbox;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class CheckboxRenderer extends AbstractVanillaComponentRenderer<net.minecraft.client.gui.components.Checkbox, Checkbox> {
}

View file

@ -1,20 +0,0 @@
package org.betterx.ui.layout.components.render;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public interface ComponentRenderer {
void renderInBounds(
PoseStack stack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
);
}

View file

@ -1,13 +0,0 @@
package org.betterx.ui.layout.components.render;
import org.betterx.ui.layout.components.AbstractVanillaComponentRenderer;
import org.betterx.ui.layout.components.Input;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class EditBoxRenderer extends AbstractVanillaComponentRenderer<net.minecraft.client.gui.components.EditBox, Input> {
}

View file

@ -1,23 +0,0 @@
package org.betterx.ui.layout.components.render;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class NullRenderer implements ComponentRenderer {
@Override
public void renderInBounds(
PoseStack stack,
int mouseX,
int mouseY,
float deltaTicks,
Rectangle bounds,
Rectangle clipRect
) {
}
}

View file

@ -1,13 +0,0 @@
package org.betterx.ui.layout.components.render;
import org.betterx.ui.layout.components.AbstractVanillaComponentRenderer;
import org.betterx.ui.layout.components.Range;
import org.betterx.ui.vanilla.Slider;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class RangeRenderer<N extends Number> extends AbstractVanillaComponentRenderer<Slider<N>, Range<N>> {
}

View file

@ -1,125 +0,0 @@
package org.betterx.ui.layout.components.render;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Size;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.resources.ResourceLocation;
import org.joml.Matrix4f;
public class RenderHelper {
public static void outline(PoseStack poseStack, int x0, int y0, int x1, int y1, int color) {
outline(poseStack, x0, y0, x1, y1, color, color);
}
public static void outline(PoseStack poseStack, int x0, int y0, int x1, int y1, int color1, int color2) {
int n;
if (x1 < x0) {
n = x0;
x0 = x1;
x1 = n;
}
if (y1 < y0) {
n = y0;
y0 = y1;
y1 = n;
}
y1--;
x1--;
Matrix4f transform = poseStack.last().pose();
innerHLine(transform, x0, x1, y0, color1);
innerVLine(transform, x0, y0 + 1, y1, color1);
innerHLine(transform, x0 + 1, x1, y1, color2);
innerVLine(transform, x1, y0 + 1, y1 - 1, color2);
}
public static void hLine(PoseStack poseStack, int x0, int x1, int y, int color) {
if (x1 < x0) {
int m = x0;
x0 = x1;
x1 = m;
}
innerHLine(poseStack.last().pose(), x0, x1, y, color);
}
protected static void innerHLine(Matrix4f transform, int x0, int x1, int y, int color) {
innerFill(transform, x0, y, x1 + 1, y + 1, color);
}
public static void vLine(PoseStack poseStack, int x, int y0, int y1, int color) {
if (y1 < y0) {
int m = y0;
y0 = y1;
y1 = m;
}
innerVLine(poseStack.last().pose(), x, y0, y1, color);
}
protected static void innerVLine(Matrix4f transform, int x, int y0, int y1, int color) {
innerFill(transform, x, y0, x + 1, y1 + 1, color);
}
private static void innerFill(Matrix4f transform, int x0, int y0, int x1, int y1, int color) {
float[] cl = ColorUtil.toFloatArray(color);
BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder();
RenderSystem.enableBlend();
RenderSystem.disableTexture();
RenderSystem.defaultBlendFunc();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
bufferBuilder.vertex(transform, (float) x0, (float) y1, 0.0F).color(cl[0], cl[1], cl[2], cl[3]).endVertex();
bufferBuilder.vertex(transform, (float) x1, (float) y1, 0.0F).color(cl[0], cl[1], cl[2], cl[3]).endVertex();
bufferBuilder.vertex(transform, (float) x1, (float) y0, 0.0F).color(cl[0], cl[1], cl[2], cl[3]).endVertex();
bufferBuilder.vertex(transform, (float) x0, (float) y0, 0.0F).color(cl[0], cl[1], cl[2], cl[3]).endVertex();
BufferUploader.drawWithShader(bufferBuilder.end());
RenderSystem.enableTexture();
RenderSystem.disableBlend();
}
public static void renderImage(
PoseStack stack,
int left, int top,
ResourceLocation location,
Size resourceSize, Rectangle uvRect,
float alpha
) {
renderImage(stack, left, top, uvRect.width, uvRect.height, location, resourceSize, uvRect, alpha);
}
public static void renderImage(
PoseStack stack,
int left, int top, int width, int height,
ResourceLocation location,
Size resourceSize, Rectangle uvRect,
float alpha
) {
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, location);
RenderSystem.enableBlend();
RenderSystem.blendFunc(
GlStateManager.SourceFactor.SRC_ALPHA,
GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA
);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, alpha);
GuiComponent.blit(
stack,
left, top, width, height,
uvRect.left,
uvRect.top,
uvRect.width,
uvRect.height,
resourceSize.width(),
resourceSize.height()
);
}
}

View file

@ -1,41 +0,0 @@
package org.betterx.ui.layout.components.render;
import org.betterx.ui.layout.values.Rectangle;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public interface ScrollerRenderer {
default int scrollerHeight() {
return 16;
}
default int scrollerWidth() {
return 8;
}
default int scrollerPadding() {
return 2;
}
default Rectangle getScrollerBounds(Rectangle renderBounds) {
return new Rectangle(
renderBounds.right() - this.scrollerWidth(),
renderBounds.top,
this.scrollerWidth(),
renderBounds.height
);
}
default Rectangle getPickerBounds(Rectangle renderBounds, int pickerOffset, int pickerSize) {
return new Rectangle(
renderBounds.left,
renderBounds.top + pickerOffset,
renderBounds.width,
pickerSize
);
}
void renderScrollBar(Rectangle renderBounds, int pickerOffset, int pickerSize);
}

View file

@ -1,26 +0,0 @@
package org.betterx.ui.layout.components.render;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public interface TextProvider {
default Font getFont() {
return Minecraft.getInstance().font;
}
default int getWidth(net.minecraft.network.chat.Component c) {
return getFont().width(c.getVisualOrderText()) + 24;
}
default int getLineHeight(net.minecraft.network.chat.Component c) {
return getFont().lineHeight;
}
default int getHeight(net.minecraft.network.chat.Component c) {
return getLineHeight(c) + 11;
}
}

View file

@ -1,9 +0,0 @@
package org.betterx.ui.layout.values;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public enum Alignment {
MIN, MAX, CENTER
}

View file

@ -1,91 +0,0 @@
package org.betterx.ui.layout.values;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class Rectangle {
public static final Rectangle ZERO = new Rectangle(0, 0, 0, 0);
public final int left;
public final int top;
public final int width;
public final int height;
public Rectangle(int left, int top, int width, int height) {
this.left = left;
this.top = top;
this.width = width;
this.height = height;
}
public float aspect() {
return (float) width / height;
}
public Size sizeFromWidth(int width) {
return new Size(width, (int) (width / aspect()));
}
public Size sizeFromHeight(int height) {
return new Size((int) (height * aspect()), height);
}
public Size size(float scale) {
return new Size((int) (width * scale), (int) (height * scale));
}
public Size size() {
return new Size(width, height);
}
public int right() {
return left + width;
}
public int bottom() {
return top + height;
}
public Rectangle movedBy(int left, int top) {
return new Rectangle(this.left + left, this.top + top, this.width, this.height);
}
public Rectangle movedBy(int left, int top, int deltaWidth, int deltaHeight) {
return new Rectangle(this.left + left, this.top + top, this.width + deltaWidth, this.height + deltaHeight);
}
public boolean overlaps(Rectangle r) {
return this.left < r.right() && this.right() > r.left &&
this.top < r.bottom() && this.bottom() > r.top;
}
public boolean contains(int x, int y) {
return x >= left && x <= right() && y >= top && y <= bottom();
}
public boolean contains(double x, double y) {
return x >= left && x <= right() && y >= top && y <= bottom();
}
public Rectangle intersect(Rectangle r) {
if (!overlaps(r)) return ZERO;
int left = Math.max(this.left, r.left);
int top = Math.max(this.top, r.top);
int right = Math.min(this.right(), r.right());
int bottom = Math.min(this.bottom(), r.bottom());
return new Rectangle(left, top, right - left, bottom - top);
}
@Override
public String toString() {
return "rectangle{" +
left +
"x" + top +
", " + right() +
"x" + bottom() +
" [" + width +
"x" + height +
"]}";
}
}

View file

@ -1,11 +0,0 @@
package org.betterx.ui.layout.values;
public record Size(int width, int height) {
public static Size of(int size) {
return new Size(size, size);
}
public static Size of(int width, int height) {
return new Size(width, height);
}
}

View file

@ -1,74 +0,0 @@
package org.betterx.ui.layout.values;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public interface SizeType {
FitContent FIT_CONTENT = new FitContent();
FitContentOrFill FIT_CONTENT_OR_FILL = new FitContentOrFill();
Fill FILL = new Fill();
record Fill() implements SizeType {
}
class FitContentOrFill extends FitContent {
public FitContentOrFill(ContentSizeSupplier contentSize) {
super(contentSize);
}
public FitContentOrFill() {
super();
}
@Override
public FitContentOrFill copyForSupplier(ContentSizeSupplier component) {
return new FitContentOrFill(component);
}
}
class FitContent implements SizeType {
private final ContentSizeSupplier contentSize;
public FitContent(ContentSizeSupplier contentSize) {
this.contentSize = contentSize;
}
@FunctionalInterface
public interface ContentSizeSupplier {
int get();
}
public FitContent() {
this(null);
}
public FitContent copyForSupplier(ContentSizeSupplier component) {
return new FitContent(component);
}
public ContentSizeSupplier contentSize() {
return contentSize;
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}
record Fixed(int size) implements SizeType {
@Override
public String toString() {
return getClass().getSimpleName() + "(" + size + ")";
}
}
record Relative(double percentage) implements SizeType {
@Override
public String toString() {
return getClass().getSimpleName() + "(" + percentage + ")";
}
}
}

View file

@ -1,110 +0,0 @@
package org.betterx.ui.layout.values;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class Value {
private SizeType sizeType;
private int calculatedSize;
public Value(SizeType sizeType) {
this.sizeType = sizeType;
this.calculatedSize = 0;
}
public static Value fixed(int size) {
return new Value(new SizeType.Fixed(size));
}
public static Value relative(double percentage) {
return new Value(new SizeType.Relative(percentage));
}
public static Value fill() {
return new Value(SizeType.FILL);
}
public static Value fit() {
return new Value(SizeType.FIT_CONTENT);
}
public static Value fitOrFill() {
return new Value(SizeType.FIT_CONTENT_OR_FILL);
}
public int calculatedSize() {
return calculatedSize;
}
public int setCalculatedSize(int value) {
calculatedSize = value;
return value;
}
public Value attachComponent(SizeType.FitContent.ContentSizeSupplier c) {
if (sizeType instanceof SizeType.FitContent fit && fit.contentSize() == null) {
sizeType = fit.copyForSupplier(c);
}
return this;
}
public int calculateFixed() {
return calculate(0);
}
public double calculateRelative() {
if (sizeType instanceof SizeType.Relative rel) {
return rel.percentage();
}
return 0;
}
public int calculate(int parentSize) {
calculatedSize = 0;
if (sizeType instanceof SizeType.Fixed fixed) {
calculatedSize = fixed.size();
} else if (sizeType instanceof SizeType.FitContent fit) {
calculatedSize = fit.contentSize().get();
} else if (sizeType instanceof SizeType.Relative rel) {
calculatedSize = (int) (parentSize * rel.percentage());
}
return calculatedSize;
}
public int calculateOrFill(int parentSize) {
calculatedSize = calculate(parentSize);
if (sizeType instanceof SizeType.Fill || sizeType instanceof SizeType.FitContentOrFill) {
calculatedSize = Math.max(parentSize, calculatedSize);
}
return calculatedSize;
}
public double fillWeight() {
if (sizeType instanceof SizeType.Fill fill || sizeType instanceof SizeType.FitContentOrFill) {
return 1;
}
return 0;
}
public int fill(int fillSize) {
return fill(fillSize, fillWeight());
}
public int fill(int fillSize, double totalFillWeight) {
if (sizeType instanceof SizeType.Fill) {
calculatedSize = (int) Math.round(fillSize * (fillWeight() / totalFillWeight));
}
return calculatedSize;
}
@Override
public String toString() {
return "DynamicSize{" +
"sizeType=" + sizeType.getClass().getSimpleName() +
", calculatedSize=" + calculatedSize +
'}';
}
}

View file

@ -1,150 +0,0 @@
package org.betterx.ui.vanilla;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.components.*;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.ConfirmLinkScreen;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public abstract class LayoutScreen extends Screen {
protected final int topPadding;
protected final int bottomPadding;
protected final int sidePadding;
public LayoutScreen(Component component) {
this(null, component, 20, 10, 20);
}
public LayoutScreen(@Nullable Screen parent, Component component) {
this(parent, component, 20, 10, 20);
}
public LayoutScreen(
@Nullable Screen parent,
Component component,
int topPadding,
int bottomPadding,
int sidePadding
) {
super(component);
this.parent = parent;
this.topPadding = topPadding;
this.bottomPadding = topPadding;
this.sidePadding = sidePadding;
}
@Nullable
protected Panel main;
@Nullable
public final Screen parent;
protected abstract LayoutComponent<?, ?> initContent();
protected void openLink(String uri) {
ConfirmLinkScreen cls = new ConfirmLinkScreen(bl -> {
if (bl) {
Util.getPlatform().openUri(uri);
}
this.minecraft.setScreen(this);
}, uri, true);
Minecraft.getInstance().setScreen(cls);
}
@Override
protected final void init() {
super.init();
main = new Panel(this.width, this.height);
main.setChild(addTitle(initContent()));
main.calculateLayout();
addRenderableWidget(main);
}
protected LayoutComponent<?, ?> buildTitle() {
var text = new Text(fit(), fit(), title).centerHorizontal()
.setColor(ColorUtil.WHITE)
.setDebugName("title");
return text;
}
protected LayoutComponent<?, ?> addTitle(LayoutComponent<?, ?> content) {
VerticalStack rows = new VerticalStack(fill(), fill()).setDebugName("title stack");
if (topPadding > 0) rows.addSpacer(topPadding);
rows.add(buildTitle());
rows.addSpacer(15);
rows.add(content);
if (bottomPadding > 0) rows.addSpacer(bottomPadding);
if (sidePadding <= 0) return rows;
HorizontalStack cols = new HorizontalStack(fill(), fill()).setDebugName("padded side");
cols.addSpacer(sidePadding);
cols.add(rows);
cols.addSpacer(sidePadding);
return cols;
}
protected void renderBackground(PoseStack poseStack, int i, int j, float f) {
renderDirtBackground(i);
}
@Override
public void render(PoseStack poseStack, int i, int j, float f) {
renderBackground(poseStack, i, j, f);
super.render(poseStack, i, j, f);
}
@Override
public void onClose() {
this.minecraft.setScreen(parent);
}
@Override
public boolean isPauseScreen() {
return true;
}
public static Value fit() {
return Value.fit();
}
public static Value fitOrFill() {
return Value.fitOrFill();
}
public static Value fill() {
return Value.fill();
}
public static Value fixed(int size) {
return Value.fixed(size);
}
public static Value relative(double percentage) {
return Value.relative(percentage);
}
public static MutableComponent translatable(String key) {
return Component.translatable(key);
}
public static MutableComponent literal(String content) {
return Component.literal(content);
}
}

View file

@ -1,51 +0,0 @@
package org.betterx.ui.vanilla;
import org.betterx.ui.layout.components.HorizontalStack;
import org.betterx.ui.layout.components.LayoutComponent;
import org.betterx.ui.layout.values.Size;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
public abstract class LayoutScreenWithIcon extends LayoutScreen {
protected final ResourceLocation icon;
public LayoutScreenWithIcon(ResourceLocation icon, Component component) {
this(null, icon, component);
}
public LayoutScreenWithIcon(
@Nullable Screen parent,
ResourceLocation icon,
Component component
) {
this(parent, icon, component, 20, 10, 20);
}
public LayoutScreenWithIcon(
@Nullable Screen parent,
ResourceLocation icon,
Component component,
int topPadding,
int bottomPadding,
int sidePadding
) {
super(parent, component, topPadding, bottomPadding, sidePadding);
this.icon = icon;
}
@Override
protected LayoutComponent<?, ?> buildTitle() {
LayoutComponent<?, ?> title = super.buildTitle();
HorizontalStack row = new HorizontalStack(fill(), fit()).setDebugName("title bar");
row.addFiller();
row.addIcon(icon, Size.of(512)).setDebugName("icon");
row.addSpacer(4);
row.add(title);
row.addFiller();
return row;
}
}

View file

@ -1,115 +0,0 @@
package org.betterx.ui.vanilla;
import net.minecraft.client.gui.components.AbstractSliderButton;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
public class Slider<N extends Number> extends AbstractSliderButton {
@FunctionalInterface
public interface SliderValueChanged<N extends Number> {
void now(Slider<N> slider, N newValue);
}
protected final Component title;
protected final N minValue;
protected final N maxValue;
protected final SliderValueChanged<N> onChange;
public Slider(
int x,
int y,
int width,
int height,
N minValue,
N maxValue,
N initialValue,
SliderValueChanged<N> onChange
) {
this(x, y, width, height, null, minValue, maxValue, initialValue, onChange);
}
public Slider(
int x,
int y,
int width,
int height,
Component title,
N minValue,
N maxValue,
N initialValue,
SliderValueChanged<N> onChange
) {
super(
x,
y,
width,
height,
CommonComponents.EMPTY,
(initialValue.doubleValue() - minValue.doubleValue()) / (maxValue.doubleValue() - minValue.doubleValue())
);
this.title = title;
this.minValue = minValue;
this.maxValue = maxValue;
this.onChange = onChange;
this.updateMessage();
}
public N currentValue() {
Double res = value * (maxValue.doubleValue() - minValue.doubleValue()) + minValue.doubleValue();
if (minValue instanceof Integer) {
return (N) (Integer) res.intValue();
}
if (minValue instanceof Byte) {
return (N) (Byte) res.byteValue();
}
if (minValue instanceof Short) {
return (N) (Short) res.shortValue();
}
if (minValue instanceof Long) {
return (N) (Long) res.longValue();
}
if (minValue instanceof Float) {
return (N) (Float) res.floatValue();
}
if (minValue instanceof Double) {
return (N) res;
}
throw new IllegalStateException("The Type " + minValue.getClass()
.getSimpleName() + " is not nativley supported. Please override currentValue with an implementation ofr that type");
}
protected String valueToString(N value) {
if (minValue instanceof Float || minValue instanceof Double) {
double v = value.doubleValue();
double m = maxValue.doubleValue();
if (m > 1000)
return "" + (int) v;
if (m > 100)
return String.format("%.1f", v);
if (m > 10)
return String.format("%.2f", v);
return String.format("%.4f", v);
}
return "" + value;
}
public Component getValueComponent(N value) {
return Component.literal("" + this.valueToString(value));
}
@Override
protected void updateMessage() {
final Component valueComponent = getValueComponent(this.currentValue());
this.setMessage(title == null ? valueComponent : title.copy()
.append(": ")
.append(valueComponent));
}
@Override
protected void applyValue() {
onChange.now(this, currentValue());
}
}

View file

@ -1,54 +0,0 @@
package org.betterx.ui.vanilla;
import org.betterx.ui.layout.components.render.ScrollerRenderer;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.GameRenderer;
public class VanillaScrollerRenderer implements ScrollerRenderer {
public static final VanillaScrollerRenderer DEFAULT = new VanillaScrollerRenderer();
@Override
public void renderScrollBar(Rectangle b, int pickerOffset, int pickerSize) {
b = this.getScrollerBounds(b);
Rectangle p = this.getPickerBounds(b, pickerOffset, pickerSize);
Tesselator tesselator = Tesselator.getInstance();
BufferBuilder bufferBuilder = tesselator.getBuilder();
RenderSystem.disableTexture();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
//scroller background
bufferBuilder.vertex(b.left, b.bottom(), 0.0D).color(0, 0, 0, 255).endVertex();
bufferBuilder.vertex(b.right(), b.bottom(), 0.0D).color(0, 0, 0, 255).endVertex();
bufferBuilder.vertex(b.right(), b.top, 0.0D).color(0, 0, 0, 255).endVertex();
bufferBuilder.vertex(b.left, b.top, 0.0D).color(0, 0, 0, 255).endVertex();
//scroll widget shadow
bufferBuilder.vertex(p.left, p.bottom(), 0.0D).color(128, 128, 128, 255).endVertex();
bufferBuilder.vertex(p.right(), p.bottom(), 0.0D).color(128, 128, 128, 255).endVertex();
bufferBuilder.vertex(p.right(), p.top, 0.0D).color(128, 128, 128, 255).endVertex();
bufferBuilder.vertex(p.left, p.top, 0.0D).color(128, 128, 128, 255).endVertex();
//scroll widget
bufferBuilder.vertex(p.left, p.bottom() - 1, 0.0D)
.color(192, 192, 192, 255)
.endVertex();
bufferBuilder.vertex(p.right() - 1, p.bottom() - 1, 0.0D)
.color(192, 192, 192, 255)
.endVertex();
bufferBuilder.vertex(p.right() - 1, p.top, 0.0D).color(192, 192, 192, 255).endVertex();
bufferBuilder.vertex(p.left, p.top, 0.0D).color(192, 192, 192, 255).endVertex();
tesselator.end();
}
}