1
0
forked from Clones/Controlify

Proper layouts for guides

This commit is contained in:
isXander
2023-05-11 20:53:37 +01:00
parent 71c7e26587
commit 4c9cb11830
9 changed files with 568 additions and 77 deletions

View File

@ -0,0 +1,40 @@
package dev.isxander.controlify.gui.layout;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractLayoutComponent<T extends RenderComponent> implements RenderComponent {
private final List<T> components = new ArrayList<>();
public List<T> getChildComponents() {
return components;
}
public <U extends T> U insertTop(U area) {
components.add(area);
return area;
}
public <U extends T> U insertBottom(U area) {
components.add(0, area);
return area;
}
public <U extends T> U insertAbove(U area, T above) {
int index = components.indexOf(above);
if (index == -1)
throw new IllegalArgumentException("InteractionArea " + above + " is not registered!");
components.add(index + 1, area);
return area;
}
public <U extends T> U insertBelow(U area, T below) {
int index = components.indexOf(below);
if (index == -1)
throw new IllegalArgumentException("InteractionArea " + below + " is not registered!");
components.add(index, area);
return area;
}
}

View File

@ -0,0 +1,29 @@
package dev.isxander.controlify.gui.layout;
import org.joml.Vector2f;
import org.joml.Vector2fc;
import org.joml.Vector2i;
import org.joml.Vector2ic;
public enum AnchorPoint {
TOP_LEFT(0, 0),
TOP_CENTER(0.5f, 0),
TOP_RIGHT(1, 0),
CENTER_LEFT(0, 0.5f),
CENTER(0.5f, 0.5f),
CENTER_RIGHT(0.5f, 1),
BOTTOM_LEFT(0f, 1f),
BOTTOM_CENTER(0.5f, 1f),
BOTTOM_RIGHT(1f, 1f);
public final float anchorX, anchorY;
AnchorPoint(float anchorX, float anchorY) {
this.anchorX = anchorX;
this.anchorY = anchorY;
}
public Vector2i getAnchorPosition(Vector2ic windowSize) {
return new Vector2i((int) (windowSize.x() * anchorX), (int) (windowSize.y() * anchorY));
}
}

View File

@ -0,0 +1,160 @@
package dev.isxander.controlify.gui.layout;
import com.mojang.blaze3d.vertex.PoseStack;
import org.apache.commons.lang3.Validate;
import org.joml.Vector2i;
import org.joml.Vector2ic;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
public class ColumnLayoutComponent<T extends RenderComponent> extends AbstractLayoutComponent<T> {
private final int componentPaddingVertical;
private final int colPaddingLeft, colPaddingRight, colPaddingTop, colPaddingBottom;
private final ElementPosition elementPosition;
private ColumnLayoutComponent(Collection<? extends T> elements,
int componentPaddingVertical,
int colPaddingLeft, int colPaddingRight,
int colPaddingTop, int colPaddingBottom,
ElementPosition elementPosition
) {
for (var element : elements) {
insertTop(element);
}
this.componentPaddingVertical = componentPaddingVertical;
this.colPaddingLeft = colPaddingLeft;
this.colPaddingRight = colPaddingRight;
this.colPaddingTop = colPaddingTop;
this.colPaddingBottom = colPaddingBottom;
this.elementPosition = elementPosition;
}
@Override
public void render(PoseStack stack, int x, int y, float deltaTime) {
int width = getMaxChildWidth();
if (width == -1)
return;
int yOffset = 0;
for (var element : getChildComponents()) {
if (!element.isVisible())
continue;
element.render(
stack,
x + colPaddingLeft + elementPosition.positionFunction.apply(width, element.size().x()),
y + colPaddingTop + yOffset,
deltaTime
);
yOffset += element.size().y() + componentPaddingVertical;
}
}
@Override
public Vector2ic size() {
return new Vector2i(
getMaxChildWidth() + colPaddingLeft + colPaddingRight,
getSumHeight() + colPaddingTop + colPaddingBottom
);
}
private int getSumHeight() {
return this.getChildComponents().stream()
.filter(RenderComponent::isVisible)
.map(RenderComponent::size)
.mapToInt(size -> size.y() + componentPaddingVertical)
.sum() - componentPaddingVertical;
}
private int getMaxChildWidth() {
return this.getChildComponents().stream()
.filter(RenderComponent::isVisible)
.map(RenderComponent::size)
.mapToInt(Vector2ic::x)
.max().orElse(-1);
}
public static <T extends RenderComponent> Builder<T> builder() {
return new Builder<>();
}
public enum ElementPosition {
LEFT((rowWidth, elementWidth) -> 0),
RIGHT((rowWidth, elementWidth) -> rowWidth - elementWidth),
MIDDLE((rowWidth, elementWidth) -> rowWidth / 2 - elementWidth / 2);
public final BiFunction<Integer, Integer, Integer> positionFunction;
ElementPosition(BiFunction<Integer, Integer, Integer> positionFunction) {
this.positionFunction = positionFunction;
}
}
public static class Builder<T extends RenderComponent> {
private final List<T> elements = new ArrayList<>();
private int componentPaddingVertical;
private int colPaddingLeft, colPaddingRight, colPaddingTop, colPaddingBottom;
private ElementPosition elementPosition = null;
public Builder<T> element(T element) {
elements.add(element);
return this;
}
public Builder<T> elements(T... elements) {
this.elements.addAll(Arrays.asList(elements));
return this;
}
public Builder<T> elements(Collection<? extends T> elements) {
this.elements.addAll(elements);
return this;
}
public Builder<T> elementPadding(int padding) {
this.componentPaddingVertical = padding;
return this;
}
public Builder<T> colPadding(int left, int right, int top, int bottom) {
this.colPaddingLeft = left;
this.colPaddingRight = right;
this.colPaddingTop = top;
this.colPaddingBottom = bottom;
return this;
}
public Builder<T> colPadding(int horizontal, int vertical) {
return colPadding(horizontal, horizontal, vertical, vertical);
}
public Builder<T> colPadding(int padding) {
return colPadding(padding, padding, padding, padding);
}
public Builder<T> elementPosition(ElementPosition elementPosition) {
this.elementPosition = elementPosition;
return this;
}
public ColumnLayoutComponent<T> build() {
Validate.notEmpty(elements, "No elements were added to the column!");
Validate.notNull(elementPosition, "Element position cannot be null!");
return new ColumnLayoutComponent<>(
elements,
componentPaddingVertical,
colPaddingLeft, colPaddingRight,
colPaddingTop, colPaddingBottom,
elementPosition
);
}
}
}

View File

@ -0,0 +1,56 @@
package dev.isxander.controlify.gui.layout;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import org.joml.Vector2i;
import org.joml.Vector2ic;
public class PositionedComponent<T extends RenderComponent> {
private final T component;
private int x, y;
private final AnchorPoint windowAnchor;
private final int offsetX, offsetY;
private final AnchorPoint origin;
public PositionedComponent(T component, AnchorPoint windowAnchor, int offsetX, int offsetY, AnchorPoint origin) {
this.component = component;
this.offsetX = offsetX;
this.offsetY = offsetY;
this.windowAnchor = windowAnchor;
this.origin = origin;
updatePosition();
}
public void updatePosition() {
Vector2ic windowPosition = windowAnchor.getAnchorPosition(windowSize());
Vector2ic anchoredPosition = origin.getAnchorPosition(component.size());
this.x = windowPosition.x() + offsetX - anchoredPosition.x();
this.y = windowPosition.y() + offsetY - anchoredPosition.y();
}
public void render(PoseStack stack, float deltaTime) {
component.render(stack, x, y, deltaTime);
}
public int x() {
return x;
}
public int y() {
return y;
}
public T getComponent() {
return component;
}
private Vector2i windowSize() {
Window window = Minecraft.getInstance().getWindow();
return new Vector2i(window.getGuiScaledWidth(), window.getGuiScaledHeight());
}
}

View File

@ -0,0 +1,14 @@
package dev.isxander.controlify.gui.layout;
import com.mojang.blaze3d.vertex.PoseStack;
import org.joml.Vector2ic;
public interface RenderComponent {
void render(PoseStack stack, int x, int y, float deltaTime);
Vector2ic size();
default boolean isVisible() {
return true;
}
}

View File

@ -0,0 +1,157 @@
package dev.isxander.controlify.gui.layout;
import com.mojang.blaze3d.vertex.PoseStack;
import org.apache.commons.lang3.Validate;
import org.joml.Vector2i;
import org.joml.Vector2ic;
import java.util.*;
import java.util.function.BiFunction;
public class RowLayoutComponent<T extends RenderComponent> extends AbstractLayoutComponent<T> {
private final int elementPaddingHorizontal;
private final int rowPaddingLeft, rowPaddingRight, rowPaddingTop, rowPaddingBottom;
private final ElementPosition elementPosition;
private RowLayoutComponent(Collection<? extends T> elements,
int elementPaddingHorizontal,
int rowPaddingLeft, int rowPaddingRight,
int rowPaddingTop, int rowPaddingBottom,
ElementPosition elementPosition
) {
for (var element : elements) {
insertTop(element);
}
this.elementPaddingHorizontal = elementPaddingHorizontal;
this.rowPaddingLeft = rowPaddingLeft;
this.rowPaddingRight = rowPaddingRight;
this.rowPaddingTop = rowPaddingTop;
this.rowPaddingBottom = rowPaddingBottom;
this.elementPosition = elementPosition;
}
@Override
public void render(PoseStack stack, int x, int y, float deltaTime) {
int height = getMaxChildHeight();
if (height == -1)
return;
int xOffset = 0;
for (var element : getChildComponents()) {
if (!element.isVisible())
continue;
element.render(
stack,
x + rowPaddingLeft + xOffset,
y + rowPaddingTop + elementPosition.positionFunction.apply(height, element.size().y()),
deltaTime
);
xOffset += element.size().x() + elementPaddingHorizontal;
}
}
@Override
public Vector2ic size() {
return new Vector2i(
getSumWidth() + rowPaddingLeft + rowPaddingRight,
getMaxChildHeight() + rowPaddingTop + rowPaddingBottom
);
}
private int getMaxChildHeight() {
return this.getChildComponents().stream()
.filter(RenderComponent::isVisible)
.map(RenderComponent::size)
.mapToInt(Vector2ic::y)
.max().orElse(-1);
}
private int getSumWidth() {
return this.getChildComponents().stream()
.filter(RenderComponent::isVisible)
.map(RenderComponent::size)
.mapToInt(size -> size.x() + elementPaddingHorizontal)
.sum() - elementPaddingHorizontal;
}
public static <T extends RenderComponent> Builder<T> builder() {
return new Builder<>();
}
public enum ElementPosition {
TOP((rowHeight, elementHeight) -> 0),
BOTTOM((rowHeight, elementHeight) -> rowHeight - elementHeight),
MIDDLE((rowHeight, elementHeight) -> rowHeight / 2 - elementHeight / 2);
public final BiFunction<Integer, Integer, Integer> positionFunction;
ElementPosition(BiFunction<Integer, Integer, Integer> positionFunction) {
this.positionFunction = positionFunction;
}
}
public static class Builder<T extends RenderComponent> {
private final List<T> elements = new ArrayList<>();
private int elementPaddingHorizontal;
private int rowPaddingLeft, rowPaddingRight, rowPaddingTop, rowPaddingBottom;
private ElementPosition elementPosition = null;
public Builder<T> element(T element) {
elements.add(element);
return this;
}
public Builder<T> elements(T... elements) {
this.elements.addAll(Arrays.asList(elements));
return this;
}
public Builder<T> elements(Collection<? extends T> elements) {
this.elements.addAll(elements);
return this;
}
public Builder<T> elementPadding(int padding) {
this.elementPaddingHorizontal = padding;
return this;
}
public Builder<T> rowPadding(int left, int right, int top, int bottom) {
this.rowPaddingLeft = left;
this.rowPaddingRight = right;
this.rowPaddingTop = top;
this.rowPaddingBottom = bottom;
return this;
}
public Builder<T> rowPadding(int horizontal, int vertical) {
return rowPadding(horizontal, horizontal, vertical, vertical);
}
public Builder<T> rowPadding(int padding) {
return rowPadding(padding, padding, padding, padding);
}
public Builder<T> elementPosition(ElementPosition elementPosition) {
this.elementPosition = elementPosition;
return this;
}
public RowLayoutComponent<T> build() {
Validate.notEmpty(elements, "No elements were added to the row!");
Validate.notNull(elementPosition, "Element position cannot be null!");
return new RowLayoutComponent<>(
elements,
elementPaddingHorizontal,
rowPaddingLeft, rowPaddingRight,
rowPaddingTop, rowPaddingBottom,
elementPosition
);
}
}
}