[Change] Migrated ModMenu Screen to new Layout Engine

This commit is contained in:
Frank 2022-07-16 13:55:02 +02:00
parent 399a2c8a4e
commit bebd7e1c70
21 changed files with 289 additions and 152 deletions

View file

@ -1,60 +1,65 @@
package org.betterx.bclib.client.gui.modmenu;
import org.betterx.bclib.client.gui.gridlayout.*;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.config.ConfigKeeper;
import org.betterx.bclib.config.Configs;
import org.betterx.bclib.config.NamedPathConfig;
import org.betterx.bclib.config.NamedPathConfig.ConfigTokenDescription;
import org.betterx.bclib.config.NamedPathConfig.DependendConfigToken;
import org.betterx.ui.layout.components.*;
import org.betterx.ui.layout.values.Value;
import org.betterx.ui.vanilla.LayoutScreenWithIcon;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
public class MainScreen extends GridScreen {
public class MainScreen extends LayoutScreenWithIcon {
static final ResourceLocation BCLIB_LOGO_LOCATION = new ResourceLocation(BCLib.MOD_ID, "icon.png");
public MainScreen(@Nullable Screen parent) {
super(parent, Component.translatable("title.bclib.modmenu.main"), 10, false);
super(parent, BCLIB_LOGO_LOCATION, Component.translatable("title.bclib.modmenu.main"), 10, 10, 20);
}
protected <T> Component getComponent(NamedPathConfig config, ConfigTokenDescription<T> option, String type) {
return Component.translatable(type + ".config." + config.configID + option.getPath());
}
Map<GridWidgetWithEnabledState, Supplier<Boolean>> dependentWidgets = new HashMap<>();
Map<Checkbox, Supplier<Boolean>> dependentWidgets = new HashMap<>();
protected void updateEnabledState() {
dependentWidgets.forEach((cb, supl) -> cb.setEnabled(supl.get()));
}
@SuppressWarnings("unchecked")
protected <T> void addRow(GridColumn grid, NamedPathConfig config, ConfigTokenDescription<T> option) {
protected <T> void addRow(VerticalStack grid, NamedPathConfig config, ConfigTokenDescription<T> option) {
if (ConfigKeeper.BooleanEntry.class.isAssignableFrom(option.token.type)) {
addCheckbox(grid, config, (ConfigTokenDescription<Boolean>) option);
}
grid.addSpacerRow(2);
grid.addSpacer(2);
}
protected void addCheckbox(GridColumn grid, NamedPathConfig config, ConfigTokenDescription<Boolean> option) {
protected void addCheckbox(VerticalStack grid, NamedPathConfig config, ConfigTokenDescription<Boolean> option) {
if (option.topPadding > 0) {
grid.addSpacerRow(option.topPadding);
grid.addSpacer(option.topPadding);
}
GridRow row = grid.addRow();
HorizontalStack row = grid.addRow();
if (option.leftPadding > 0) {
row.addSpacer(option.leftPadding);
}
GridCheckboxCell cb = row.addCheckbox(
Checkbox cb = row.addCheckbox(
Value.fit(), Value.fit(),
getComponent(config, option, "title"),
config.getRaw(option.token),
font,
(state) -> {
(caller, state) -> {
config.set(option.token, state);
updateEnabledState();
}
@ -71,34 +76,39 @@ public class MainScreen extends GridScreen {
return false;
}
@Override
protected void initLayout() {
final int BUTTON_HEIGHT = 20;
protected LayoutComponent initContent() {
VerticalStack content = new VerticalStack(Value.fit(), Value.fit());
Configs.GENERATOR_CONFIG.getAllOptions()
.stream()
.filter(o -> !o.hidden)
.forEach(o -> addRow(grid, Configs.GENERATOR_CONFIG, o));
grid.addSpacerRow(12);
.forEach(o -> addRow(content, Configs.GENERATOR_CONFIG, o));
content.addSpacer(12);
Configs.MAIN_CONFIG.getAllOptions()
.stream()
.filter(o -> !o.hidden)
.forEach(o -> addRow(grid, Configs.MAIN_CONFIG, o));
grid.addSpacerRow(12);
.forEach(o -> addRow(content, Configs.MAIN_CONFIG, o));
content.addSpacer(12);
Configs.CLIENT_CONFIG.getAllOptions()
.stream()
.filter(o -> !o.hidden)
.forEach(o -> addRow(grid, Configs.CLIENT_CONFIG, o));
.forEach(o -> addRow(content, Configs.CLIENT_CONFIG, o));
grid.addSpacerRow(15);
GridRow row = grid.addRow();
VerticalStack grid = new VerticalStack(Value.fill(), Value.fill());
grid.add(VerticalScroll.create(Value.fill(), Value.fill(), content));
grid.addSpacer(8);
HorizontalStack row = grid.addRow();
row.addFiller();
row.addButton(CommonComponents.GUI_DONE, BUTTON_HEIGHT, font, (button) -> {
grid.addButton(Value.fit(), Value.fit(), CommonComponents.GUI_DONE, (button) -> {
Configs.CLIENT_CONFIG.saveChanges();
Configs.GENERATOR_CONFIG.saveChanges();
Configs.MAIN_CONFIG.saveChanges();
onClose();
});
grid.addSpacerRow(10);
}).alignRight();
return grid;
}
}

View file

@ -3,8 +3,8 @@ package org.betterx.bclib.client.gui.modmenu;
import org.betterx.bclib.BCLib;
import org.betterx.ui.ColorUtil;
import org.betterx.ui.layout.components.*;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Size;
import org.betterx.ui.layout.values.Value;
import org.betterx.ui.vanilla.LayoutScreen;
import net.minecraft.client.gui.screens.Screen;
@ -24,36 +24,36 @@ public class TestScreen extends LayoutScreen {
}
@Override
protected org.betterx.ui.layout.components.Component<?> initContent() {
VerticalStack rows = new VerticalStack(DynamicSize.fit(), DynamicSize.fitOrFill());
protected LayoutComponent<?> initContent() {
VerticalStack rows = new VerticalStack(Value.fit(), Value.fitOrFill());
rows.addFiller();
rows.add(new Text(
DynamicSize.fitOrFill(), DynamicSize.fixed(20),
Value.fitOrFill(), Value.fixed(20),
Component.literal("Some Text")
).alignRight()
);
rows.add(new Text(
DynamicSize.fitOrFill(), DynamicSize.fixed(20),
Value.fitOrFill(), Value.fixed(20),
Component.literal("Some other, longer Text")
).centerHorizontal()
);
rows.addSpacer(16);
rows.add(new Image(
DynamicSize.fixed(24), DynamicSize.fixed(24),
Value.fixed(24), Value.fixed(24),
BCLib.makeID("icon.png"),
new Size(512, 512)
).centerHorizontal()
);
rows.add(new MultiLineText(
DynamicSize.fill(), DynamicSize.fit(),
Value.fill(), Value.fit(),
Component.literal(
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.")
).setColor(ColorUtil.LIGHT_PURPLE).centerHorizontal()
);
rows.addSpacer(16);
rows.add(new Range<>(
DynamicSize.fill(), DynamicSize.fit(),
Value.fill(), Value.fit(),
Component.literal("Integer"),
10, 90, 20,
(slider, value) -> {
@ -62,7 +62,7 @@ public class TestScreen extends LayoutScreen {
));
rows.addSpacer(8);
rows.add(new Range<>(
DynamicSize.fill(), DynamicSize.fit(),
Value.fill(), Value.fit(),
Component.literal("Float"),
10f, 90f, 20f,
(slider, value) -> {
@ -71,7 +71,7 @@ public class TestScreen extends LayoutScreen {
));
rows.addSpacer(16);
Checkbox cb1 = new Checkbox(
DynamicSize.fit(), DynamicSize.fit(),
Value.fit(), Value.fit(),
Component.literal("Some Sub-State"),
false, true,
(checkbox, value) -> {
@ -79,7 +79,7 @@ public class TestScreen extends LayoutScreen {
}
);
rows.add(new Checkbox(
DynamicSize.fit(), DynamicSize.fit(),
Value.fit(), Value.fit(),
Component.literal("Some Selectable State"),
false, true,
(checkbox, value) -> {
@ -90,7 +90,7 @@ public class TestScreen extends LayoutScreen {
rows.add(cb1);
rows.addSpacer(16);
rows.add(new Button(
DynamicSize.fit(), DynamicSize.fit(),
Value.fit(), Value.fit(),
Component.literal("test"),
(bt) -> {
System.out.println("clicked test");
@ -101,7 +101,7 @@ public class TestScreen extends LayoutScreen {
);
rows.addSpacer(8);
rows.add(new Button(
DynamicSize.fit(), DynamicSize.fit(),
Value.fit(), Value.fit(),
Component.literal("Hello World"),
(bt) -> {
System.out.println("clicked hello");
@ -112,6 +112,6 @@ public class TestScreen extends LayoutScreen {
);
rows.addFiller();
return HorizontalStack.centered(VerticalScroll.create(DynamicSize.fit(), DynamicSize.relative(1), rows));
return HorizontalStack.centered(VerticalScroll.create(Value.fit(), Value.relative(1), rows));
}
}

View file

@ -2,11 +2,14 @@ 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.DynamicSize;
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.client.gui.components.events.GuiEventListener;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@ -16,14 +19,14 @@ 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 Component<R> implements RelativeContainerEventHandler {
protected final List<Component<?>> components = new LinkedList<>();
public abstract class AbstractStack<R extends ComponentRenderer, T extends AbstractStack<R, T>> extends LayoutComponent<R> implements RelativeContainerEventHandler {
protected final List<LayoutComponent<?>> components = new LinkedList<>();
public AbstractStack(DynamicSize width, DynamicSize height) {
public AbstractStack(Value width, Value height) {
this(width, height, null);
}
public AbstractStack(DynamicSize width, DynamicSize height, R renderer) {
public AbstractStack(Value width, Value height, R renderer) {
super(width, height, renderer);
}
@ -53,28 +56,28 @@ public abstract class AbstractStack<R extends ComponentRenderer, T extends Abstr
Rectangle clipRect
) {
super.renderInBounds(poseStack, mouseX, mouseY, deltaTicks, renderBounds, clipRect);
for (Component<?> c : components) {
for (LayoutComponent<?> c : components) {
c.render(poseStack, mouseX, mouseY, deltaTicks, renderBounds, clipRect);
}
}
public T add(Component<?> c) {
public T add(LayoutComponent<?> c) {
this.components.add(c);
return (T) this;
}
protected abstract T addEmpty(DynamicSize size);
protected abstract T addEmpty(Value size);
public T addSpacer(int size) {
return addEmpty(DynamicSize.fixed(size));
return addEmpty(Value.fixed(size));
}
public T addSpacer(float percentage) {
return addEmpty(DynamicSize.relative(percentage));
return addEmpty(Value.relative(percentage));
}
public T addFiller() {
return addEmpty(DynamicSize.fill());
return addEmpty(Value.fill());
}
@Override
@ -111,5 +114,46 @@ public abstract class AbstractStack<R extends ComponentRenderer, T extends Abstr
public void setFocused(@Nullable GuiEventListener guiEventListener) {
focused = guiEventListener;
}
public Image addIcon(ResourceLocation location, Size resourceSize) {
Image i = new Image(Value.fixed(24), Value.fixed(24), location, resourceSize);
add(i);
return i;
}
public Image addImage(Value width, Value height, ResourceLocation location, Size resourceSize) {
Image i = new Image(width, height, location, resourceSize);
add(i);
return i;
}
public Checkbox addCheckbox(
Value width, Value height, Component component,
boolean selected,
Checkbox.SelectionChanged onSelectionChange
) {
Checkbox c = new Checkbox(width, height, component, selected, true, onSelectionChange);
add(c);
return c;
}
public Button addButton(
Value width, Value height,
Component component,
net.minecraft.client.gui.components.Button.OnPress onPress
) {
return addButton(width, height, component, onPress, net.minecraft.client.gui.components.Button.NO_TOOLTIP);
}
public Button addButton(
Value width, Value height,
Component component,
net.minecraft.client.gui.components.Button.OnPress onPress,
net.minecraft.client.gui.components.Button.OnTooltip onTooltip
) {
Button b = new Button(width, height, component, onPress, onTooltip);
add(b);
return b;
}
}

View file

@ -1,18 +1,18 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.values.DynamicSize;
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 Component<AbstractVanillaComponentRenderer<C, V>> {
public abstract class AbstractVanillaComponent<C extends AbstractWidget, V extends AbstractVanillaComponent<C, V>> extends LayoutComponent<AbstractVanillaComponentRenderer<C, V>> {
protected C vanillaComponent;
protected final net.minecraft.network.chat.Component component;
protected float alpha = 1.0f;
protected boolean enabled = true;
public AbstractVanillaComponent(
DynamicSize width,
DynamicSize height,
Value width,
Value height,
AbstractVanillaComponentRenderer<C, V> renderer,
net.minecraft.network.chat.Component component
) {

View file

@ -1,7 +1,7 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.ButtonRenderer;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Value;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@ -12,8 +12,8 @@ public class Button extends AbstractVanillaComponent<net.minecraft.client.gui.co
final net.minecraft.client.gui.components.Button.OnTooltip onTooltip;
public Button(
DynamicSize width,
DynamicSize height,
Value width,
Value height,
net.minecraft.network.chat.Component component,
net.minecraft.client.gui.components.Button.OnPress onPress,
net.minecraft.client.gui.components.Button.OnTooltip onTooltip

View file

@ -1,7 +1,7 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.CheckboxRenderer;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Value;
import net.minecraft.network.chat.Component;
@ -21,8 +21,8 @@ public class Checkbox extends AbstractVanillaComponent<net.minecraft.client.gui.
private final SelectionChanged onSelectionChange;
public Checkbox(
DynamicSize width,
DynamicSize height,
Value width,
Value height,
Component component,
boolean selected, boolean showLabel,
SelectionChanged onSelectionChange

View file

@ -1,8 +1,8 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.ComponentRenderer;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Rectangle;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
@ -10,10 +10,10 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public abstract class CustomRenderComponent<C extends CustomRenderComponent<C>> extends Component<CustomRenderComponent.CustomRenderRenderer<C>> {
public abstract class CustomRenderComponent<C extends CustomRenderComponent<C>> extends LayoutComponent<CustomRenderComponent.CustomRenderRenderer<C>> {
public CustomRenderComponent(
DynamicSize width,
DynamicSize height
Value width,
Value height
) {
super(width, height, new CustomRenderRenderer<>());
renderer.linkedComponent = (C) this;

View file

@ -1,15 +1,15 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Value;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class Empty extends Component {
public class Empty extends LayoutComponent {
public Empty(
DynamicSize width,
DynamicSize height
Value width,
Value height
) {
super(width, height, null);
}

View file

@ -3,14 +3,14 @@ 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.DynamicSize;
import org.betterx.ui.layout.values.Value;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class HorizontalStack extends AbstractStack<NullRenderer, HorizontalStack> implements RelativeContainerEventHandler {
public HorizontalStack(DynamicSize width, DynamicSize height) {
public HorizontalStack(Value width, Value height) {
super(width, height);
}
@ -22,7 +22,7 @@ public class HorizontalStack extends AbstractStack<NullRenderer, HorizontalStack
int freeWidth = Math.max(0, myWidth - fixedWidth);
fillWidth(myWidth, freeWidth);
for (Component<?> c : components) {
for (LayoutComponent<?> c : components) {
c.updateContainerWidth(c.width.calculatedSize());
}
@ -33,7 +33,7 @@ public class HorizontalStack extends AbstractStack<NullRenderer, HorizontalStack
protected int updateContainerHeight(int containerHeight) {
int myHeight = height.calculateOrFill(containerHeight);
components.stream().forEach(c -> c.height.calculateOrFill(myHeight));
for (Component<?> c : components) {
for (LayoutComponent<?> c : components) {
c.updateContainerHeight(c.height.calculatedSize());
}
return myHeight;
@ -45,7 +45,7 @@ public class HorizontalStack extends AbstractStack<NullRenderer, HorizontalStack
super.setRelativeBounds(left, top);
int offset = 0;
for (Component<?> c : components) {
for (LayoutComponent<?> c : components) {
int delta = relativeBounds.height - c.height.calculatedSize();
if (c.hAlign == Alignment.MIN) delta = 0;
else if (c.hAlign == Alignment.CENTER) delta /= 2;
@ -67,16 +67,27 @@ public class HorizontalStack extends AbstractStack<NullRenderer, HorizontalStack
return components.stream().map(c -> c.height.calculateFixed()).reduce(0, Integer::max);
}
public static HorizontalStack centered(Component<?> c) {
return new HorizontalStack(DynamicSize.fill(), DynamicSize.fill()).addFiller().add(c).addFiller();
public static HorizontalStack centered(LayoutComponent<?> c) {
return new HorizontalStack(Value.fill(), Value.fill()).addFiller().add(c).addFiller();
}
public static HorizontalStack bottom(Component<?> c) {
return new HorizontalStack(DynamicSize.fill(), DynamicSize.fill()).add(c).addFiller();
public static HorizontalStack bottom(LayoutComponent<?> c) {
return new HorizontalStack(Value.fill(), Value.fill()).add(c).addFiller();
}
protected HorizontalStack addEmpty(DynamicSize size) {
this.components.add(new Empty(size, DynamicSize.fixed(0)));
protected HorizontalStack addEmpty(Value size) {
this.components.add(new Empty(size, Value.fixed(0)));
return this;
}
public VerticalStack addColumn(Value width, Value height) {
VerticalStack stack = new VerticalStack(width, height);
add(stack);
return stack;
}
public VerticalStack addColumn() {
return addColumn(Value.fit(), Value.fit());
}
}

View file

@ -1,8 +1,8 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.values.DynamicSize;
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.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
@ -21,11 +21,11 @@ public class Image extends CustomRenderComponent {
protected float alpha;
protected Size resourceSize;
public Image(DynamicSize width, DynamicSize height, ResourceLocation location) {
public Image(Value width, Value height, ResourceLocation location) {
this(width, height, location, new Size(16, 16));
}
public Image(DynamicSize width, DynamicSize height, ResourceLocation location, Size resourceSize) {
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());

View file

@ -2,8 +2,8 @@ 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.DynamicSize;
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;
@ -14,15 +14,15 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public abstract class Component<R extends ComponentRenderer> implements ComponentWithBounds, GuiEventListener {
public abstract class LayoutComponent<R extends ComponentRenderer> implements ComponentWithBounds, GuiEventListener {
protected final R renderer;
protected final DynamicSize width;
protected final DynamicSize height;
protected final Value width;
protected final Value height;
protected Rectangle relativeBounds;
protected Alignment vAlign = Alignment.MIN;
protected Alignment hAlign = Alignment.MIN;
public Component(DynamicSize width, DynamicSize height, R renderer) {
public LayoutComponent(Value width, Value height, R renderer) {
this.width = width.attachComponent(this::getContentWidth);
this.height = height.attachComponent(this::getContentHeight);
this.renderer = renderer;
@ -120,32 +120,32 @@ public abstract class Component<R extends ComponentRenderer> implements Componen
return super.toString() + "(" + relativeBounds + ", " + width.calculatedSize() + "x" + height.calculatedSize() + ")";
}
public Component<R> alignTop() {
public LayoutComponent<R> alignTop() {
vAlign = Alignment.MIN;
return this;
}
public Component<R> alignBottom() {
public LayoutComponent<R> alignBottom() {
vAlign = Alignment.MAX;
return this;
}
public Component<R> centerVertical() {
public LayoutComponent<R> centerVertical() {
vAlign = Alignment.CENTER;
return this;
}
public Component<R> alignLeft() {
public LayoutComponent<R> alignLeft() {
hAlign = Alignment.MIN;
return this;
}
public Component<R> alignRight() {
public LayoutComponent<R> alignRight() {
hAlign = Alignment.MAX;
return this;
}
public Component<R> centerHorizontal() {
public LayoutComponent<R> centerHorizontal() {
hAlign = Alignment.CENTER;
return this;
}

View file

@ -4,20 +4,20 @@ 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.DynamicSize;
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;
public class MultiLineText extends Component<MultiLineText.MultiLineTextRenderer> {
public class MultiLineText extends LayoutComponent<MultiLineText.MultiLineTextRenderer> {
final net.minecraft.network.chat.Component text;
int color = ColorUtil.DEFAULT_TEXT;
protected MultiLineLabel multiLineLabel;
public MultiLineText(
DynamicSize width,
DynamicSize height,
Value width,
Value height,
final net.minecraft.network.chat.Component text
) {
super(width, height, new MultiLineTextRenderer());

View file

@ -18,7 +18,7 @@ import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class Panel implements ComponentWithBounds, RelativeContainerEventHandler, NarratableEntry, Widget {
protected Component<?> child;
protected LayoutComponent<?> child;
List<? extends GuiEventListener> listeners = List.of();
public final Rectangle bounds;
@ -30,7 +30,7 @@ public class Panel implements ComponentWithBounds, RelativeContainerEventHandler
bounds = new Rectangle(left, top, width, height);
}
public void setChild(Component<?> c) {
public void setChild(LayoutComponent<?> c) {
this.child = c;
listeners = List.of(c);
}

View file

@ -1,7 +1,7 @@
package org.betterx.ui.layout.components;
import org.betterx.ui.layout.components.render.RangeRenderer;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.values.Value;
import org.betterx.ui.vanilla.Slider;
import net.minecraft.network.chat.Component;
@ -17,8 +17,8 @@ public class Range<N extends Number> extends AbstractVanillaComponent<Slider<N>,
private final N initialValue;
public Range(
DynamicSize width,
DynamicSize height,
Value width,
Value height,
Component component,
N minValue,
N maxValue,
@ -33,8 +33,8 @@ public class Range<N extends Number> extends AbstractVanillaComponent<Slider<N>,
}
public Range(
DynamicSize width,
DynamicSize height,
Value width,
Value height,
N minValue,
N maxValue,
N initialValue,

View file

@ -4,19 +4,19 @@ 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.DynamicSize;
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;
public class Text extends Component<Text.TextRenderer> {
public class Text extends LayoutComponent<Text.TextRenderer> {
final net.minecraft.network.chat.Component text;
int color = ColorUtil.DEFAULT_TEXT;
public Text(
DynamicSize width,
DynamicSize height,
Value width,
Value height,
net.minecraft.network.chat.Component text
) {

View file

@ -4,8 +4,8 @@ import org.betterx.ui.layout.components.render.ComponentRenderer;
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.DynamicSize;
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;
@ -14,8 +14,8 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRenderer> extends Component<R> {
protected Component<?> child;
public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRenderer> extends LayoutComponent<R> {
protected LayoutComponent<?> child;
protected final RS scrollerRenderer;
protected int dist;
@ -24,23 +24,23 @@ public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRend
protected int travel;
protected int topOffset;
public VerticalScroll(DynamicSize width, DynamicSize height, RS scrollerRenderer) {
public VerticalScroll(Value width, Value height, RS scrollerRenderer) {
this(width, height, scrollerRenderer, null);
}
public VerticalScroll(DynamicSize width, DynamicSize height, RS scrollerRenderer, R renderer) {
public VerticalScroll(Value width, Value height, RS scrollerRenderer, R renderer) {
super(width, height, renderer);
this.scrollerRenderer = scrollerRenderer;
}
public static VerticalScroll<NullRenderer, VanillaScrollerRenderer> create(Component<?> c) {
return create(DynamicSize.relative(1), DynamicSize.relative(1), c);
public static VerticalScroll<NullRenderer, VanillaScrollerRenderer> create(LayoutComponent<?> c) {
return create(Value.relative(1), Value.relative(1), c);
}
public static VerticalScroll<NullRenderer, VanillaScrollerRenderer> create(
DynamicSize width,
DynamicSize height,
Component<?> c
Value width,
Value height,
LayoutComponent<?> c
) {
VerticalScroll<NullRenderer, VanillaScrollerRenderer> res = new VerticalScroll<>(
width,
@ -52,7 +52,7 @@ public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRend
return res;
}
public void setChild(Component<?> c) {
public void setChild(LayoutComponent<?> c) {
this.child = c;
}

View file

@ -4,14 +4,14 @@ 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.DynamicSize;
import org.betterx.ui.layout.values.Value;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class VerticalStack extends AbstractStack<NullRenderer, VerticalStack> implements RelativeContainerEventHandler {
public VerticalStack(DynamicSize width, DynamicSize height) {
public VerticalStack(Value width, Value height) {
super(width, height);
}
@ -19,7 +19,7 @@ public class VerticalStack extends AbstractStack<NullRenderer, VerticalStack> im
protected int updateContainerWidth(int containerWidth) {
int myWidth = width.calculateOrFill(containerWidth);
components.stream().forEach(c -> c.width.calculateOrFill(myWidth));
for (Component<?> c : components) {
for (LayoutComponent<?> c : components) {
c.updateContainerWidth(c.width.calculatedSize());
}
return myWidth;
@ -33,7 +33,7 @@ public class VerticalStack extends AbstractStack<NullRenderer, VerticalStack> im
int freeHeight = Math.max(0, myHeight - fixedHeight);
fillHeight(myHeight, freeHeight);
for (Component<?> c : components) {
for (LayoutComponent<?> c : components) {
c.updateContainerHeight(c.height.calculatedSize());
}
@ -45,7 +45,7 @@ public class VerticalStack extends AbstractStack<NullRenderer, VerticalStack> im
super.setRelativeBounds(left, top);
int offset = 0;
for (Component<?> c : components) {
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;
@ -67,16 +67,26 @@ public class VerticalStack extends AbstractStack<NullRenderer, VerticalStack> im
return (int) (fixedHeight / (1 - percentage));
}
public static VerticalStack centered(Component<?> c) {
return new VerticalStack(DynamicSize.relative(1), DynamicSize.relative(1)).addFiller().add(c).addFiller();
public static VerticalStack centered(LayoutComponent<?> c) {
return new VerticalStack(Value.relative(1), Value.relative(1)).addFiller().add(c).addFiller();
}
public static VerticalStack bottom(Component<?> c) {
return new VerticalStack(DynamicSize.relative(1), DynamicSize.relative(1)).add(c).addFiller();
public static VerticalStack bottom(LayoutComponent<?> c) {
return new VerticalStack(Value.relative(1), Value.relative(1)).add(c).addFiller();
}
protected VerticalStack addEmpty(DynamicSize size) {
this.components.add(new Empty(DynamicSize.fixed(0), size));
protected VerticalStack addEmpty(Value size) {
this.components.add(new Empty(Value.fixed(0), size));
return this;
}
public HorizontalStack addRow(Value width, Value height) {
HorizontalStack stack = new HorizontalStack(width, height);
add(stack);
return stack;
}
public HorizontalStack addRow() {
return addRow(Value.fit(), Value.fit());
}
}

View file

@ -1,4 +1,11 @@
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

@ -4,33 +4,33 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class DynamicSize {
public class Value {
private SizeType sizeType;
private int calculatedSize;
public DynamicSize(SizeType sizeType) {
public Value(SizeType sizeType) {
this.sizeType = sizeType;
this.calculatedSize = 0;
}
public static DynamicSize fixed(int size) {
return new DynamicSize(new SizeType.Fixed(size));
public static Value fixed(int size) {
return new Value(new SizeType.Fixed(size));
}
public static DynamicSize relative(double percentage) {
return new DynamicSize(new SizeType.Relative(percentage));
public static Value relative(double percentage) {
return new Value(new SizeType.Relative(percentage));
}
public static DynamicSize fill() {
return new DynamicSize(SizeType.FILL);
public static Value fill() {
return new Value(SizeType.FILL);
}
public static DynamicSize fit() {
return new DynamicSize(SizeType.FIT_CONTENT);
public static Value fit() {
return new Value(SizeType.FIT_CONTENT);
}
public static DynamicSize fitOrFill() {
return new DynamicSize(SizeType.FIT_CONTENT_OR_FILL);
public static Value fitOrFill() {
return new Value(SizeType.FIT_CONTENT_OR_FILL);
}
public int calculatedSize() {
@ -42,7 +42,7 @@ public class DynamicSize {
return value;
}
public DynamicSize attachComponent(SizeType.FitContent.ContentSizeSupplier c) {
public Value attachComponent(SizeType.FitContent.ContentSizeSupplier c) {
if (sizeType instanceof SizeType.FitContent fit && fit.contentSize() == null) {
sizeType = fit.copyForSupplier(c);
}

View file

@ -1,10 +1,7 @@
package org.betterx.ui.vanilla;
import org.betterx.ui.layout.components.HorizontalStack;
import org.betterx.ui.layout.components.Panel;
import org.betterx.ui.layout.components.Text;
import org.betterx.ui.layout.components.VerticalStack;
import org.betterx.ui.layout.values.DynamicSize;
import org.betterx.ui.layout.components.*;
import org.betterx.ui.layout.values.Value;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.screens.Screen;
@ -49,7 +46,7 @@ public abstract class LayoutScreen extends Screen {
@Nullable
public final Screen parent;
protected abstract org.betterx.ui.layout.components.Component<?> initContent();
protected abstract LayoutComponent<?> initContent();
@Override
protected final void init() {
@ -61,18 +58,24 @@ public abstract class LayoutScreen extends Screen {
addRenderableWidget(main);
}
protected org.betterx.ui.layout.components.Component<?> addTitle(org.betterx.ui.layout.components.Component<?> content) {
VerticalStack rows = new VerticalStack(DynamicSize.fill(), DynamicSize.fill());
protected LayoutComponent<?> buildTitle() {
var text = new Text(Value.fill(), Value.fit(), title).centerHorizontal();
return text;
}
protected LayoutComponent<?> addTitle(LayoutComponent<?> content) {
VerticalStack rows = new VerticalStack(Value.fill(), Value.fill());
if (topPadding > 0) rows.addSpacer(topPadding);
rows.add(new Text(DynamicSize.fill(), DynamicSize.fit(), title).centerHorizontal());
rows.add(buildTitle());
rows.addSpacer(15);
rows.add(content);
if (bottomPadding > 0) rows.addSpacer(bottomPadding);
if (sidePadding <= 0) return rows;
HorizontalStack cols = new HorizontalStack(DynamicSize.fill(), DynamicSize.fill());
HorizontalStack cols = new HorizontalStack(Value.fill(), Value.fill());
cols.addSpacer(sidePadding);
cols.add(rows);
cols.addSpacer(sidePadding);

View file

@ -0,0 +1,52 @@
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 org.betterx.ui.layout.values.Value;
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(Value.fill(), Value.fit());
row.addFiller();
row.addIcon(icon, Size.of(512));
row.addSpacer(4);
row.add(title);
row.addFiller();
return row;
}
}