forked from Clones/Controlify
➕ Proper layouts for guides
This commit is contained in:
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,13 +3,13 @@ package dev.isxander.controlify.ingame.guide;
|
|||||||
import dev.isxander.controlify.api.bind.ControllerBinding;
|
import dev.isxander.controlify.api.bind.ControllerBinding;
|
||||||
import dev.isxander.controlify.api.ingameguide.ActionLocation;
|
import dev.isxander.controlify.api.ingameguide.ActionLocation;
|
||||||
import dev.isxander.controlify.api.ingameguide.ActionPriority;
|
import dev.isxander.controlify.api.ingameguide.ActionPriority;
|
||||||
|
import dev.isxander.controlify.api.ingameguide.GuideActionNameSupplier;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public record GuideAction(ControllerBinding binding, Component name, ActionLocation location,
|
public record GuideAction(ControllerBinding binding, GuideActionNameSupplier name, ActionPriority priority) implements Comparable<GuideAction> {
|
||||||
ActionPriority priority) implements Comparable<GuideAction> {
|
public GuideAction(ControllerBinding binding, GuideActionNameSupplier name) {
|
||||||
public GuideAction(ControllerBinding binding, Component name, ActionLocation location) {
|
this(binding, name, ActionPriority.NORMAL);
|
||||||
this(binding, name, location, ActionPriority.NORMAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
package dev.isxander.controlify.ingame.guide;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import dev.isxander.controlify.api.bind.BindRenderer;
|
||||||
|
import dev.isxander.controlify.api.ingameguide.IngameGuideContext;
|
||||||
|
import dev.isxander.controlify.gui.DrawSize;
|
||||||
|
import dev.isxander.controlify.gui.layout.RenderComponent;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.Font;
|
||||||
|
import net.minecraft.client.gui.GuiComponent;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import org.joml.Vector2i;
|
||||||
|
import org.joml.Vector2ic;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class GuideActionRenderer implements RenderComponent {
|
||||||
|
private final GuideAction guideAction;
|
||||||
|
private final boolean rtl;
|
||||||
|
|
||||||
|
private Optional<Component> name = Optional.empty();
|
||||||
|
|
||||||
|
public GuideActionRenderer(GuideAction action, boolean rtl) {
|
||||||
|
this.guideAction = action;
|
||||||
|
this.rtl = rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PoseStack stack, int x, int y, float deltaTime) {
|
||||||
|
if (name.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Font font = Minecraft.getInstance().font;
|
||||||
|
|
||||||
|
BindRenderer renderer = guideAction.binding().renderer();
|
||||||
|
DrawSize drawSize = renderer.size();
|
||||||
|
int textWidth = font.width(name.get());
|
||||||
|
|
||||||
|
renderer.render(stack, x + (!rtl ? 0 : textWidth + 2), y + drawSize.height() / 2);
|
||||||
|
|
||||||
|
int textX = x + (rtl ? 0 : drawSize.width() + 2);
|
||||||
|
int textY = y + drawSize.height() / 2 - font.lineHeight / 2;
|
||||||
|
|
||||||
|
GuiComponent.fill(stack, textX - 1, textY - 1, textX + textWidth + 1, textY + font.lineHeight + 1, 0x80000000);
|
||||||
|
font.draw(stack, name.get(), textX, textY, 0xFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector2ic size() {
|
||||||
|
DrawSize bindSize = guideAction.binding().renderer().size();
|
||||||
|
Font font = Minecraft.getInstance().font;
|
||||||
|
|
||||||
|
return new Vector2i(bindSize.width() + 2 + name.map(font::width).orElse(-2), Math.max(bindSize.height(), font.lineHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVisible() {
|
||||||
|
return name.isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateName(IngameGuideContext ctx) {
|
||||||
|
name = guideAction.name().supply(ctx);
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,9 @@ import dev.isxander.controlify.bindings.ControllerBindingImpl;
|
|||||||
import dev.isxander.controlify.compatibility.ControlifyCompat;
|
import dev.isxander.controlify.compatibility.ControlifyCompat;
|
||||||
import dev.isxander.controlify.controller.Controller;
|
import dev.isxander.controlify.controller.Controller;
|
||||||
import dev.isxander.controlify.api.event.ControlifyEvents;
|
import dev.isxander.controlify.api.event.ControlifyEvents;
|
||||||
|
import dev.isxander.controlify.gui.layout.AnchorPoint;
|
||||||
|
import dev.isxander.controlify.gui.layout.ColumnLayoutComponent;
|
||||||
|
import dev.isxander.controlify.gui.layout.PositionedComponent;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.GuiComponent;
|
import net.minecraft.client.gui.GuiComponent;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
@ -26,17 +29,45 @@ public class InGameButtonGuide implements IngameGuideRegistry {
|
|||||||
private final LocalPlayer player;
|
private final LocalPlayer player;
|
||||||
private final Minecraft minecraft = Minecraft.getInstance();
|
private final Minecraft minecraft = Minecraft.getInstance();
|
||||||
|
|
||||||
private final List<GuideActionSupplier> guidePredicates = new ArrayList<>();
|
|
||||||
|
|
||||||
private final List<GuideAction> leftGuides = new ArrayList<>();
|
private final List<GuideAction> leftGuides = new ArrayList<>();
|
||||||
private final List<GuideAction> rightGuides = new ArrayList<>();
|
private final List<GuideAction> rightGuides = new ArrayList<>();
|
||||||
|
|
||||||
|
private final PositionedComponent<ColumnLayoutComponent<GuideActionRenderer>> leftLayout;
|
||||||
|
private final PositionedComponent<ColumnLayoutComponent<GuideActionRenderer>> rightLayout;
|
||||||
|
|
||||||
public InGameButtonGuide(Controller<?, ?> controller, LocalPlayer localPlayer) {
|
public InGameButtonGuide(Controller<?, ?> controller, LocalPlayer localPlayer) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
this.player = localPlayer;
|
this.player = localPlayer;
|
||||||
|
|
||||||
registerDefaultActions();
|
registerDefaultActions();
|
||||||
ControlifyEvents.INGAME_GUIDE_REGISTRY.invoker().onRegisterIngameGuide(controller.bindings(), this);
|
ControlifyEvents.INGAME_GUIDE_REGISTRY.invoker().onRegisterIngameGuide(controller.bindings(), this);
|
||||||
|
|
||||||
|
Collections.sort(leftGuides);
|
||||||
|
Collections.sort(rightGuides);
|
||||||
|
|
||||||
|
leftLayout = new PositionedComponent<>(
|
||||||
|
ColumnLayoutComponent.<GuideActionRenderer>builder()
|
||||||
|
.elementPadding(2)
|
||||||
|
.colPadding(4, 4)
|
||||||
|
.elementPosition(ColumnLayoutComponent.ElementPosition.LEFT)
|
||||||
|
.elements(leftGuides.stream().map(guide -> new GuideActionRenderer(guide, false)).toList())
|
||||||
|
.build(),
|
||||||
|
AnchorPoint.TOP_LEFT,
|
||||||
|
0, 0,
|
||||||
|
AnchorPoint.TOP_LEFT
|
||||||
|
);
|
||||||
|
|
||||||
|
rightLayout = new PositionedComponent<>(
|
||||||
|
ColumnLayoutComponent.<GuideActionRenderer>builder()
|
||||||
|
.elementPadding(2)
|
||||||
|
.colPadding(4, 4)
|
||||||
|
.elementPosition(ColumnLayoutComponent.ElementPosition.RIGHT)
|
||||||
|
.elements(rightGuides.stream().map(guide -> new GuideActionRenderer(guide, true)).toList())
|
||||||
|
.build(),
|
||||||
|
AnchorPoint.TOP_RIGHT,
|
||||||
|
0, 0,
|
||||||
|
AnchorPoint.TOP_RIGHT
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderHud(PoseStack poseStack, float tickDelta, int width, int height) {
|
public void renderHud(PoseStack poseStack, float tickDelta, int width, int height) {
|
||||||
@ -45,76 +76,20 @@ public class InGameButtonGuide implements IngameGuideRegistry {
|
|||||||
|
|
||||||
ControlifyCompat.ifBeginHudBatching();
|
ControlifyCompat.ifBeginHudBatching();
|
||||||
|
|
||||||
{
|
leftLayout.render(poseStack, tickDelta);
|
||||||
var offset = 0;
|
rightLayout.render(poseStack, tickDelta);
|
||||||
for (var action : leftGuides) {
|
|
||||||
var renderer = action.binding().renderer();
|
|
||||||
|
|
||||||
var drawSize = renderer.size();
|
|
||||||
if (offset == 0) offset += drawSize.height() / 2;
|
|
||||||
|
|
||||||
int x = 4;
|
|
||||||
int y = 3 + offset;
|
|
||||||
|
|
||||||
renderer.render(poseStack, x, y);
|
|
||||||
|
|
||||||
int textX = x + drawSize.width() + 2;
|
|
||||||
int textY = y - minecraft.font.lineHeight / 2;
|
|
||||||
GuiComponent.fill(poseStack, textX - 1, textY - 1, textX + minecraft.font.width(action.name()) + 1, textY + minecraft.font.lineHeight + 1, 0x80000000);
|
|
||||||
minecraft.font.draw(poseStack, action.name(), textX, textY, 0xFFFFFF);
|
|
||||||
|
|
||||||
offset += drawSize.height() + 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var offset = 0;
|
|
||||||
for (var action : rightGuides) {
|
|
||||||
var renderer = action.binding().renderer();
|
|
||||||
|
|
||||||
var drawSize = renderer.size();
|
|
||||||
if (offset == 0) offset += drawSize.height() / 2;
|
|
||||||
|
|
||||||
int x = width - 4 - drawSize.width();
|
|
||||||
int y = 3 + offset;
|
|
||||||
|
|
||||||
renderer.render(poseStack, x, y);
|
|
||||||
|
|
||||||
int textX = x - minecraft.font.width(action.name()) - 2;
|
|
||||||
int textY = y - minecraft.font.lineHeight / 2;
|
|
||||||
GuiComponent.fill(poseStack, textX - 1, textY - 1, textX + minecraft.font.width(action.name()) + 1, textY + minecraft.font.lineHeight + 1, 0x80000000);
|
|
||||||
minecraft.font.draw(poseStack, action.name(), textX, textY, 0xFFFFFF);
|
|
||||||
|
|
||||||
offset += drawSize.height() + 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlifyCompat.ifEndHudBatching();
|
ControlifyCompat.ifEndHudBatching();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tick() {
|
public void tick() {
|
||||||
leftGuides.clear();
|
IngameGuideContext context = new IngameGuideContext(Minecraft.getInstance(), player, minecraft.level, calculateHitResult(), controller);
|
||||||
rightGuides.clear();
|
|
||||||
|
|
||||||
if (!controller.config().showIngameGuide || minecraft.screen != null)
|
leftLayout.getComponent().getChildComponents().forEach(renderer -> renderer.updateName(context));
|
||||||
return;
|
rightLayout.getComponent().getChildComponents().forEach(renderer -> renderer.updateName(context));
|
||||||
|
|
||||||
for (var actionPredicate : guidePredicates) {
|
leftLayout.updatePosition();
|
||||||
var action = actionPredicate.supply(Minecraft.getInstance(), player, minecraft.level, calculateHitResult(), controller);
|
rightLayout.updatePosition();
|
||||||
if (action.isEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
GuideAction guideAction = action.get();
|
|
||||||
if (!guideAction.binding().isUnbound()) {
|
|
||||||
if (action.get().location() == ActionLocation.LEFT)
|
|
||||||
leftGuides.add(action.get());
|
|
||||||
else
|
|
||||||
rightGuides.add(action.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(leftGuides);
|
|
||||||
Collections.sort(rightGuides);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -124,7 +99,10 @@ public class InGameButtonGuide implements IngameGuideRegistry {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerGuideAction(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier) {
|
public void registerGuideAction(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier) {
|
||||||
guidePredicates.add(new GuideActionSupplier(binding, location, priority, supplier));
|
if (location == ActionLocation.LEFT)
|
||||||
|
leftGuides.add(new GuideAction(binding, supplier, priority));
|
||||||
|
else
|
||||||
|
rightGuides.add(new GuideAction(binding, supplier, priority));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerDefaultActions() {
|
private void registerDefaultActions() {
|
||||||
@ -248,11 +226,4 @@ public class InGameButtonGuide implements IngameGuideRegistry {
|
|||||||
return pickResult;
|
return pickResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record GuideActionSupplier(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier nameSupplier) {
|
|
||||||
public Optional<GuideAction> supply(Minecraft client, LocalPlayer player, ClientLevel level, HitResult hitResult, Controller<?, ?> controller) {
|
|
||||||
return nameSupplier.supply(new IngameGuideContext(client, player, level, hitResult, controller))
|
|
||||||
.map(name -> new GuideAction(binding, name, location, priority));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user