Better GridLayout for Screens

This commit is contained in:
Frank 2021-08-19 16:39:30 +02:00
parent 32e1b2edb4
commit 2d8c92d946
18 changed files with 880 additions and 242 deletions

View file

@ -1,154 +0,0 @@
package ru.bclib.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.Button.OnPress;
import net.minecraft.client.gui.components.MultiLineLabel;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
public class GridLayout {
class LablePos {
final MultiLineLabel label;
final int top;
LablePos(MultiLineLabel label, int top){
this.label = label;
this.top = top;
}
}
class ButtonPos {
final int top;
final int height;
final int width;
final float alpha;
final Component component;
final Button.OnPress onPress;
ButtonPos(float alpha, int top, int width, int height, Component component, OnPress onPress) {
this.height = height;
this.width = width;
this.top = top;
this.alpha = alpha;
this.component = component;
this.onPress = onPress;
}
}
public final int width;
public final int height;
@NotNull
private final Font font;
private final Consumer<Button> addButtonFunction;
private final int topStart;
private int topOffset;
private int top;
private int currentRowHeight = 0;
private int currentRowMargin = 6;
private int lastRowMargin = 0;
final private List<LablePos> labels;
final private List<List<ButtonPos>> buttons;
private List<ButtonPos> currentButtonRow;
public GridLayout(int topStart, int width, int height, Font font, Consumer<Button> addButtonFunction){
Objects.requireNonNull(font);
this.topStart = topStart;
top = topStart + 20;
this.topOffset = 0;
this.width = width;
this.height = height;
this.font = font;
this.addButtonFunction = addButtonFunction;
labels = new ArrayList<>(4);
buttons = new ArrayList<>(4);
}
public int getTopStart(){
return topStart + topOffset;
}
public void addMessageRow(Component text, int padding){
addMessageRow(MultiLineLabel.create(this.font, text, this.width - 2*padding));
}
public void addMessageRow(MultiLineLabel lb){
labels.add(new LablePos(lb, top));
int promptLines = lb.getLineCount() + 1;
int height = promptLines * 9;
currentRowMargin = 12;
currentRowHeight = height;
}
public void startRow(){
this.endRow();
this.currentButtonRow = new ArrayList<>(8);
this.buttons.add(this.currentButtonRow);
}
public void endRow(){
lastRowMargin = currentRowMargin;
top += currentRowHeight + currentRowMargin;
currentRowHeight = 0;
currentRowMargin = 0;
}
public void recenterVertically(){
int hg = (top - lastRowMargin) - topStart;
int targetTop = (height - hg)/2;
topOffset = targetTop - topStart;
}
void finalizeLayout(){
final int BUTTON_SPACING = 10;
for (List<ButtonPos> row : this.buttons) {
int count = row.size();
int rowWidth = row.stream()
.map(b -> b.width)
.reduce(0, (p, c) -> p + c) + (count - 1) * BUTTON_SPACING;
int left = (width - rowWidth) / 2;
for (ButtonPos bp : row) {
Button customButton = new Button(left, bp.top+topOffset, bp.width, bp.height, bp.component, bp.onPress);
customButton.setAlpha(bp.alpha);
addButtonFunction.accept(customButton);
left += BUTTON_SPACING + bp.width;
}
}
}
public void addButton(int width, int height, Component component, Button.OnPress onPress){
addButton(1.0f, width, height, component, onPress);
}
public void addButton(int height, Component component, Button.OnPress onPress){
addButton(1.0f, height, component, onPress);
}
public void addButton(float alpha, int height, Component component, Button.OnPress onPress){
final int BUTTON_PADDING = 12;
int width = font.width(component.getVisualOrderText()) + 2*BUTTON_PADDING;
addButton(alpha, width, height, component, onPress);
}
public void addButton(float alpha, int width, int height, Component component, Button.OnPress onPress){
currentRowHeight = Math.max(currentRowHeight, height);
currentRowMargin = 6;
currentButtonRow.add(new ButtonPos(alpha, top, width, height, component, onPress));
}
public void render(PoseStack poseStack){
labels.forEach(lp -> {
lp.label.renderCentered(poseStack, this.width / 2, lp.top + topOffset);
});
}
}

View file

@ -1,35 +0,0 @@
package ru.bclib.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
@Environment(EnvType.CLIENT)
public abstract class GridScreen extends Screen {
protected GridLayout grid = null;
public final int topStart;
public GridScreen(int topStart, Component title) {
super(title);
this.topStart = topStart;
}
final protected void init() {
super.init();
this.grid = new GridLayout(topStart, this.width, this.height, this.font, this::addRenderableWidget);
initLayout();
grid.finalizeLayout();
}
protected abstract void initLayout();
public void render(PoseStack poseStack, int i, int j, float f) {
//this.renderBackground(poseStack);
this.renderDirtBackground(i);
drawCenteredString(poseStack, this.font, this.title, grid.width / 2, grid.getTopStart(), 16777215);
if (grid!=null) grid.render(poseStack);
super.render(poseStack, i, j, f);
}
}

View file

@ -0,0 +1,27 @@
package ru.bclib.gui.gridlayout;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import ru.bclib.util.TriConsumer;
import java.util.List;
import java.util.function.Function;
@Environment(EnvType.CLIENT)
class GridCell extends GridCellDefinition {
public final float height;
Function<GridTransform, Object> componentPlacer;
final TriConsumer<PoseStack, GridTransform, Object> customRender;
GridCell(double width, double height, GridLayout.GridValueType widthType, Function<GridTransform, Object> componentPlacer, TriConsumer<PoseStack, GridTransform, Object> customRender) {
super(width, widthType);
this.height = (float) height;
this.componentPlacer = componentPlacer;
this.customRender = customRender;
}
protected GridElement buildElementAt(int left, int top, int width, final List<GridElement> collector) {
return new GridElement(left, top, width, (int) this.height, componentPlacer, customRender);
}
}

View file

@ -0,0 +1,50 @@
package ru.bclib.gui.gridlayout;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.components.Checkbox;
import net.minecraft.network.chat.Component;
import ru.bclib.gui.gridlayout.GridLayout.GridValueType;
import java.util.function.Consumer;
@Environment(EnvType.CLIENT)
class SignalingCheckBox extends Checkbox{
private Consumer<Boolean> onChange;
public SignalingCheckBox(int left, int top, int width, int height, Component component, boolean checked, Consumer<Boolean> onChange) {
super(left, top, width, height, component, checked);
this.onChange = onChange;
if (onChange!=null)
onChange.accept(checked);
}
@Override
public void onPress() {
super.onPress();
if (onChange!=null)
onChange.accept(this.selected());
}
}
@Environment(EnvType.CLIENT)
public class GridCheckboxCell extends GridCell{
private boolean checked;
GridCheckboxCell(Component text, boolean checked, float alpha, double width, GridValueType widthType, double height) {
super(width, height, widthType, null, null);
this.componentPlacer = (transform) -> {
Checkbox cb = new SignalingCheckBox(transform.left, transform.top, transform.width, transform.height,
text,
checked,
(state)-> this.checked = state
);
cb.setAlpha(alpha);
return cb;
};
}
public boolean isChecked(){
return checked;
}
}

View file

@ -0,0 +1,51 @@
package ru.bclib.gui.gridlayout;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import ru.bclib.gui.gridlayout.GridLayout.GridValueType;
import ru.bclib.gui.gridlayout.GridLayout.VerticalAlignment;
import java.util.List;
@Environment(EnvType.CLIENT)
public class GridColumn extends GridContainer {
GridColumn(double width) {
super(width);
}
GridColumn(double width, GridLayout.GridValueType widthType) {
super(width, widthType);
}
public GridRow addRow() {
return addRow(VerticalAlignment.TOP);
}
public GridRow addRow(VerticalAlignment align) {
GridRow row = new GridRow(1.0, GridLayout.GridValueType.PERCENTAGE, align);
this.cells.add(row);
return row;
}
public void addSpacerRow(){
this.addSpacerRow(4);
}
public void addSpacerRow(int height){
GridCell cell = new GridCell(1.0, height, GridValueType.PERCENTAGE, null, null);
this.cells.add(cell);
}
@Override
protected GridElement buildElementAt(int left, int inTop, int width, final List<GridElement> collector){
int height = 0;
int top = inTop;
for (GridCellDefinition row : cells) {
GridElement element = row.buildElement(width, 0, 1, left, top, collector);
top += element.height;
height += element.height;
}
return new GridElement(left, inTop, width, height);
}
}

View file

@ -0,0 +1,24 @@
package ru.bclib.gui.gridlayout;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.resources.ResourceLocation;
import ru.bclib.gui.gridlayout.GridLayout.GridValueType;
@Environment(EnvType.CLIENT)
public class GridImageCell extends GridCell{
GridImageCell(ResourceLocation location, double width, GridValueType widthType, double height, float alpha, int uvLeft, int uvTop, int uvWidth, int uvHeight, int resourceWidth, int resourceHeight) {
super(width, height, widthType, null, (poseStack, transform, context) -> {
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(poseStack, transform.left, transform.top, transform.width, transform.height, uvLeft, uvTop, uvWidth, uvHeight, resourceWidth, resourceHeight);
});
}
}

View file

@ -0,0 +1,175 @@
package ru.bclib.gui.gridlayout;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.components.AbstractWidget;
import ru.bclib.gui.gridlayout.GridLayout.GridValueType;
import ru.bclib.util.TriConsumer;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;
@Environment(EnvType.CLIENT)
abstract class GridCellDefinition {
public final float width;
public final GridLayout.GridValueType widthType;
public GridCellDefinition(double width, GridLayout.GridValueType widthType) {
this.width = (float)width;
this.widthType = widthType;
}
public final int calculateWidth(final int parentWidth){
if (widthType == GridLayout.GridValueType.CONSTANT) {
return (int) this.width;
} else if (widthType == GridValueType.PERCENTAGE) {
return (int) (this.width * parentWidth);
} else {
return 0;
}
}
final GridElement buildElement(final int parentWidth, final int autoWidth, final float autoWidthSum, int left, final int top, final List<GridElement> collector) {
final int width = widthType == GridValueType.AUTO?(int)((this.width/autoWidthSum)*autoWidth):calculateWidth(parentWidth);
final GridElement el = buildElementAt(left, top, width, collector);
if (collector!=null) {
collector.add(el);
}
return el;
}
abstract protected GridElement buildElementAt(int left, int top, int width, final List<GridElement> collector);
}
@Environment(EnvType.CLIENT)
class GridElement extends GridTransform{
final Function<GridTransform, Object> componentPlacer;
final TriConsumer<PoseStack, GridTransform, Object> customRender;
Object renderContext;
GridElement(int left, int top, int width, int height, Function<GridTransform, Object> componentPlacer, TriConsumer<PoseStack, GridTransform, Object> customRender) {
super(left, top, width, height);
this.componentPlacer = componentPlacer;
this.customRender = customRender;
}
GridElement(int left, int top, int width, int height) {
this(left, top, width, height, null, null);
}
GridTransform transformWithPadding(int leftPadding, int topPadding){
return new GridTransform(left + leftPadding, top +topPadding, width, height);
}
}
@Environment(EnvType.CLIENT)
abstract class GridContainer extends GridCellDefinition{
protected List<GridCellDefinition> cells;
public GridContainer(double width) {
this(width, GridLayout.GridValueType.CONSTANT);
}
GridContainer(double width, GridLayout.GridValueType widthType) {
super(width, widthType);
cells = new LinkedList<>();
}
}
@Environment(EnvType.CLIENT)
public class GridLayout extends GridColumn {
public static final int COLOR_WHITE = 0x00FFFFFF;
public static final int COLOR_RED = 0x00FF0000;
public static final int COLOR_GREEN = 0x0000FF00;
public static final int COLOR_BLUE = 0x000000FF;
public final GridScreen screen;
public final int screenHeight;
public final int sidePadding;
public final int initialTopPadding;
public final boolean centerVertically;
private int height;
private int topPadding;
private List<GridElement> elements;
public GridLayout(GridScreen screen) {
this(screen, 0, true);
}
public GridLayout(GridScreen screen, int topPadding, boolean centerVertically) {
this(screen, topPadding, 20, centerVertically);
}
public GridLayout(GridScreen screen, int topPadding, int sidePadding, boolean centerVertically) {
super(screen.width-2*sidePadding, GridValueType.CONSTANT);
this.screen = screen;
this.screenHeight = screen.height;
height = 0;
this.topPadding = topPadding;
this.sidePadding = sidePadding;
this.initialTopPadding = topPadding;
this.centerVertically = centerVertically;
}
public int getHeight(){
return height;
}
public int getTopPadding() {
return topPadding;
}
void buildLayout(){
elements = new LinkedList<>();
GridElement el = this.buildElement((int)this.width, 0, 1, 0,0, elements);
this.height = el.height;
if (centerVertically) {
topPadding = (screenHeight - el.height - initialTopPadding) >> 1;
} else {
topPadding = initialTopPadding;
}
}
public void finalizeLayout(){
buildLayout();
elements
.stream()
.filter(element -> element.componentPlacer!=null)
.forEach(element -> {
Object context = element.componentPlacer.apply(element.transformWithPadding(sidePadding, topPadding));
if (element.customRender != null){
element.renderContext = context;
} else if (context instanceof AbstractWidget) {
screen.addRenderableWidget((AbstractWidget) context);
}
});
}
public void render(PoseStack poseStack){
if (elements == null) return;
elements
.stream()
.filter(element -> element.customRender!=null)
.forEach(element -> element.customRender.accept(poseStack, element.transformWithPadding(sidePadding, topPadding), element.renderContext));
}
public static enum VerticalAlignment {
TOP, CENTER, BOTTOM
}
public static enum Alignment {
LEFT, CENTER, RIGHT
}
public static enum GridValueType {
CONSTANT, PERCENTAGE, AUTO;
}
}

View file

@ -0,0 +1,53 @@
package ru.bclib.gui.gridlayout;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.MultiLineLabel;
import net.minecraft.network.chat.Component;
import ru.bclib.gui.gridlayout.GridLayout.Alignment;
import ru.bclib.gui.gridlayout.GridLayout.GridValueType;
import java.util.List;
@Environment(EnvType.CLIENT)
class GridMessageCell extends GridCell {
private final Font font;
private final Component text;
private MultiLineLabel label;
GridMessageCell(double width, GridValueType widthType, Alignment contentAlignment, Font font, Component text) {
this(width, widthType, contentAlignment, font, text, GridLayout.COLOR_WHITE);
}
GridMessageCell(double width, GridValueType widthType, Alignment contentAlignment, Font font, Component text, int color) {
super(width, -1, widthType, null, (poseStack, transform, context) -> {
MultiLineLabel label = (MultiLineLabel) context;
if (contentAlignment == Alignment.CENTER) {
label.renderCentered(poseStack, transform.width / 2 + transform.left, transform.top, font.lineHeight, color);
}
else if (contentAlignment == Alignment.LEFT) {
label.renderLeftAligned(poseStack, transform.left, transform.top, font.lineHeight, color);
}
});
this.font = font;
this.text = text;
}
private MultiLineLabel getLabel(GridTransform transform) {
return label;
}
protected void create(GridTransform transform) {
this.label = MultiLineLabel.create(font, text, transform.width);
}
@Override
protected GridElement buildElementAt(int left, int top, int width, List<GridElement> collector) {
create(new GridTransform(left, top, width, 0));
int promptLines = this.label.getLineCount() + 1;
int height = promptLines * 9;
return new GridElement(left, top, width, height, this::getLabel, customRender);
}
}

View file

@ -0,0 +1,254 @@
package ru.bclib.gui.gridlayout;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.Button.OnPress;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import ru.bclib.gui.gridlayout.GridLayout.Alignment;
import ru.bclib.gui.gridlayout.GridLayout.GridValueType;
import ru.bclib.gui.gridlayout.GridLayout.VerticalAlignment;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@Environment(EnvType.CLIENT)
public class GridRow extends GridContainer {
public final GridLayout.VerticalAlignment alignment;
GridRow(double width) {
this(width, VerticalAlignment.TOP);
}
GridRow(double width, GridLayout.GridValueType widthType) {
this(width, widthType, VerticalAlignment.CENTER);
}
GridRow(double width, GridLayout.VerticalAlignment alignment) {
super(width);
this.alignment = alignment;
}
GridRow(double width, GridLayout.GridValueType widthType, GridLayout.VerticalAlignment alignment) {
super(width, widthType);
this.alignment = alignment;
}
public GridColumn addColumn(double width, GridLayout.GridValueType widthType) {
GridColumn cell = new GridColumn(width, widthType);
this.cells.add(cell);
return cell;
}
public GridCell addComponent(double height, Function<GridTransform, Object> componentPlacer) {
return addComponent(1.0, GridLayout.GridValueType.PERCENTAGE, height, componentPlacer);
}
public GridCell addComponent(double width, GridLayout.GridValueType widthType, double height, Function<GridTransform, Object> componentPlacer) {
GridCell cell = new GridCell(width, height, widthType, componentPlacer, null);
this.cells.add(cell);
return cell;
}
public GridCell addButton(Component text, double height, OnPress onPress) {
return addButton(text, 1.0, GridValueType.PERCENTAGE, height, onPress);
}
public GridCell addButton(Component text, float alpha, double height, OnPress onPress) {
return addButton(text, alpha, 1.0, GridValueType.PERCENTAGE, height, onPress);
}
public GridCell addButton(Component text, double height, Font font, OnPress onPress) {
return addButton(text, 1.0f, height, font, onPress);
}
public GridCell addButton(Component text, float alpha, double height, Font font, OnPress onPress) {
final int width = font.width(text.getVisualOrderText()) + 24;
return addButton(text, alpha, width, GridValueType.CONSTANT, height, onPress);
}
public GridCell addButton(Component text, double width, GridValueType widthType, double height, OnPress onPress) {
return addButton(text, 1.0f, width, widthType, height, onPress);
}
public GridCell addButton(Component text, float alpha, double width, GridValueType widthType, double height, OnPress onPress) {
GridCell cell = new GridCell(width, height, widthType, (transform) -> {
Button customButton = new Button(transform.left, transform.top, transform.width, transform.height, text, onPress);
customButton.setAlpha(alpha);
return customButton;
}, null);
this.cells.add(cell);
return cell;
}
public GridCheckboxCell addCheckbox(Component text, boolean checked, int height) {
return addCheckbox(text, checked, 1.0f, height);
}
public GridCheckboxCell addCheckbox(Component text, boolean checked, float alpha, int height) {
return addCheckbox(text, checked, alpha, 1.0, GridValueType.PERCENTAGE, height);
}
public GridCheckboxCell addCheckbox(Component text, boolean checked, int height, Font font) {
return addCheckbox(text, checked, 1.0f, height, font);
}
public GridCheckboxCell addCheckbox(Component text, boolean checked, float alpha, int height, Font font) {
final int width = font.width(text.getVisualOrderText()) + 24 + 2 * 12;
return addCheckbox(text, checked, alpha, width, GridValueType.CONSTANT, height);
}
public GridCheckboxCell addCheckbox(Component text, boolean checked, double width, GridValueType widthType, int height) {
return addCheckbox(text, checked, 1.0f, width, widthType, height);
}
public GridCheckboxCell addCheckbox(Component text, boolean checked, float alpha, double width, GridValueType widthType, int height) {
GridCheckboxCell cell = new GridCheckboxCell(text, checked, alpha, width, widthType, height);
this.cells.add(cell);
return cell;
}
public GridCell addImage(ResourceLocation location, int width, int height) {
return addImage(location, 1.0f, width, height);
}
public GridCell addImage(ResourceLocation location, float alpha, int width, int height) {
return addImage(location, alpha, width, GridValueType.CONSTANT, height, 0, 0, width, height, width, height);
}
public GridCell addImage(ResourceLocation location, double width, GridValueType widthType, int height, int resourceWidth, int resourceHeight) {
return addImage(location, 1.0f, width, widthType, height, resourceWidth, resourceHeight);
}
public GridCell addImage(ResourceLocation location, float alpha, double width, GridValueType widthType, int height, int resourceWidth, int resourceHeight) {
return addImage(location, alpha, width, widthType, height, 0, 0, resourceWidth, resourceWidth, resourceWidth, resourceHeight);
}
public GridCell addImage(ResourceLocation location, double width, GridValueType widthType, int height, int uvLeft, int uvTop, int uvWidth, int uvHeight, int resourceWidth, int resourceHeight) {
return addImage(location, 1.0f, width, widthType, height, uvLeft, uvTop, uvWidth, uvHeight, resourceWidth, resourceHeight);
}
public GridCell addImage(ResourceLocation location, float alpha, double width, GridValueType widthType, int height, int uvLeft, int uvTop, int uvWidth, int uvHeight, int resourceWidth, int resourceHeight) {
GridCell cell = new GridImageCell(location, width, widthType, height, alpha, uvLeft, uvTop, uvWidth, uvHeight, resourceWidth, resourceHeight);
this.cells.add(cell);
return cell;
}
public GridColumn addFiller() {
return addFiller(1);
}
public GridColumn addFiller(float portion) {
GridColumn cell = new GridColumn(portion, GridValueType.AUTO);
this.cells.add(cell);
return cell;
}
public void addSpacer() {
addSpacer(12);
}
public void addSpacer(int width) {
GridCell cell = new GridCell(width, 0, GridValueType.CONSTANT, null, null);
this.cells.add(cell);
}
public GridCell addMessage(Component text, Font font, Alignment contentAlignment) {
return addMessage(text, font, GridLayout.COLOR_WHITE, contentAlignment);
}
public GridCell addMessage(Component text, Font font, int color, Alignment contentAlignment) {
return addMessage(text, 1.0, GridLayout.GridValueType.PERCENTAGE, font, color, contentAlignment);
}
public GridCell addMessage(Component text, double width, GridValueType widthType, Font font, Alignment contentAlignment) {
return addMessage(text, width, widthType, font, GridLayout.COLOR_WHITE, contentAlignment);
}
public GridCell addMessage(Component text, double width, GridValueType widthType, Font font, int color, Alignment contentAlignment) {
GridCell cell = new GridMessageCell(width, widthType, Alignment.LEFT, font, text, color);
this.cells.add(cell);
return cell;
}
public GridCell addString(Component text, GridScreen parent) {
return this.addString(text, GridLayout.COLOR_WHITE, parent);
}
public GridCell addString(Component text, int color, GridScreen parent) {
final int width = parent.getFont()
.width(text.getVisualOrderText());
return this.addString(text, width, GridValueType.CONSTANT, GridLayout.COLOR_WHITE, Alignment.CENTER, parent);
}
public GridCell addString(Component text, Alignment contentAlignment, GridScreen parent) {
return this.addString(text, GridLayout.COLOR_WHITE, contentAlignment, parent);
}
public GridCell addString(Component text, int color, Alignment contentAlignment, GridScreen parent) {
return this.addString(text, 1.0, GridLayout.GridValueType.PERCENTAGE, color, contentAlignment, parent);
}
public GridCell addString(Component text, double width, GridValueType widthType, Alignment contentAlignment, GridScreen parent) {
return addString(text, width, widthType, GridLayout.COLOR_WHITE, contentAlignment, parent);
}
public GridCell addString(Component text, double width, GridValueType widthType, int color, Alignment contentAlignment, GridScreen parent) {
GridCell cell = new GridStringCell(width, widthType, parent.getFont().lineHeight, contentAlignment, parent, text, color);
this.cells.add(cell);
return cell;
}
@Override
protected GridElement buildElementAt(int inLeft, int top, int width, final List<GridElement> collector) {
int height = 0;
int left = inLeft;
final int fixedWidth = cells.stream()
.filter(col -> col.widthType != GridValueType.AUTO)
.map(col -> col.calculateWidth(width))
.reduce(0, (p, c) -> p + c);
final float autoWidthSum = cells.stream()
.filter(col -> col.widthType == GridValueType.AUTO)
.map(col -> col.width)
.reduce(0.0f, (p, c) -> p + c);
final int autoWidth = width - fixedWidth;
if (alignment == VerticalAlignment.TOP) {
for (GridCellDefinition col : cells) {
GridElement element = col.buildElement(width, autoWidth, autoWidthSum, left, top, collector);
left += element.width;
height = Math.max(height, element.height);
}
}
else {
//first iteration will collect heights, second one will transform top position for alignment
Map<GridCellDefinition, GridElement> cache = new HashMap<>();
for (GridCellDefinition col : cells) {
GridElement element = col.buildElement(width, autoWidth, autoWidthSum, left, top, null);
left += element.width;
height = Math.max(height, element.height);
cache.put(col, element);
}
left = inLeft;
for (GridCellDefinition col : cells) {
GridElement element = cache.get(col);
final int topOffset = (alignment == VerticalAlignment.BOTTOM) ? (height - element.height) : (height - element.height) >> 1;
element = col.buildElement(width, autoWidth, autoWidthSum, left, top + topOffset, collector);
left += element.width;
}
}
return new GridElement(inLeft, top, width, height);
}
}

View file

@ -0,0 +1,78 @@
package ru.bclib.gui.gridlayout;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.Widget;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import ru.bclib.gui.gridlayout.GridLayout.Alignment;
@Environment(EnvType.CLIENT)
public abstract class GridScreen extends Screen {
protected GridLayout grid = null;
public final int topPadding;
public final int sidePadding;
public final boolean centerVertically;
public GridScreen(Component title) {
this(title, 0, true);
}
public GridScreen(Component title, int topPadding, boolean centerVertically) {
this(title, topPadding, 20, centerVertically);
}
public GridScreen(Component title, int topPadding, int sidePadding, boolean centerVertically) {
super(title);
this.topPadding = topPadding;
this.sidePadding = sidePadding;
this.centerVertically = centerVertically;
}
public Font getFont(){
return this.font;
}
@Override
public <T extends GuiEventListener & Widget & NarratableEntry> T addRenderableWidget(T guiEventListener) {
return super.addRenderableWidget(guiEventListener);
}
protected void addTitle(){
grid.addRow().addString(this.title, Alignment.CENTER, this);
grid.addSpacerRow(15);
}
final protected void init() {
super.init();
this.grid = new GridLayout(this, this.topPadding, this.sidePadding, this.centerVertically);
addTitle();
initLayout();
grid.finalizeLayout();
}
protected abstract void initLayout();
protected void renderScreen(PoseStack poseStack, int i, int j, float f) {
super.render(poseStack, i, j, f);
}
public void render(PoseStack poseStack, int i, int j, float f) {
//this.renderBackground(poseStack);
this.renderDirtBackground(i);
//drawCenteredString(poseStack, this.font, this.title, grid.width / 2, grid.getTopStart(), 16777215);
if (grid!=null) grid.render(poseStack);
super.render(poseStack, i, j, f);
}
}

View file

@ -0,0 +1,25 @@
package ru.bclib.gui.gridlayout;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.network.chat.Component;
import ru.bclib.gui.gridlayout.GridLayout.Alignment;
import ru.bclib.gui.gridlayout.GridLayout.GridValueType;
@Environment(EnvType.CLIENT)
class GridStringCell extends GridCell {
GridStringCell(double width, GridValueType widthType, int height, Alignment contentAlignment, GridScreen parent, Component text) {
this(width, widthType, height, contentAlignment, parent, text, GridLayout.COLOR_WHITE);
}
GridStringCell(double width, GridValueType widthType, int height, Alignment contentAlignment, GridScreen parent, Component text, int color) {
super(width, height, widthType, null, (poseStack, transform, context) -> {
if (contentAlignment == Alignment.CENTER) {
parent.drawCenteredString(poseStack, parent.getFont(), text, transform.width / 2 + transform.left, transform.top, color);
}
else if (contentAlignment == Alignment.LEFT) {
parent.drawString(poseStack, parent.getFont(), text, transform.left, transform.top, color);
}
});
}
}

View file

@ -0,0 +1,25 @@
package ru.bclib.gui.gridlayout;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class GridTransform {
public final int left;
public final int top;
public final int width;
public final int height;
GridTransform(int left, int top, int width, int height) {
this.left = left;
this.top = top;
this.width = width;
this.height = height;
}
@Override
public String toString() {
return "{" + "left=" + left + ", top=" + top + ", width=" + width + ", height=" + height + '}';
}
}

View file

@ -0,0 +1,54 @@
package ru.bclib.gui.screens;
import com.mojang.blaze3d.vertex.PoseStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import ru.bclib.BCLib;
import ru.bclib.gui.gridlayout.GridLayout.GridValueType;
import ru.bclib.gui.gridlayout.GridLayout.VerticalAlignment;
import ru.bclib.gui.gridlayout.GridRow;
import ru.bclib.gui.gridlayout.GridScreen;
@Environment(EnvType.CLIENT)
abstract class BCLibScreen extends GridScreen {
static final ResourceLocation BCLIB_LOGO_LOCATION = new ResourceLocation(BCLib.MOD_ID, "icon.png");
public BCLibScreen(Component title) {
super(title);
}
public BCLibScreen(Component title, int topPadding, boolean centerVertically) {
super(title, topPadding, centerVertically);
}
public BCLibScreen(Component title, int topPadding, int sidePadding, boolean centerVertically) {
super(title, topPadding, sidePadding, centerVertically);
}
protected void addTitle(){
GridRow row = grid.addRow(VerticalAlignment.BOTTOM);
row.addFiller();
row.addImage(BCLIB_LOGO_LOCATION, 24, GridValueType.CONSTANT, 24, 512, 512);
row.addSpacer(4);
row.addString(this.title, font.lineHeight,this);
row.addFiller();
grid.addSpacerRow(15);
}
@Override
public void render(PoseStack poseStack, int i, int j, float f) {
this.renderDirtBackground(i);
//
// RenderSystem.setShader(GameRenderer::getPositionTexShader);
// RenderSystem.setShaderTexture(0, BCLIB_LOGO_LOCATION);
// RenderSystem.enableBlend();
// RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
// RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f);
// blit(poseStack, 0, 0, 32, 32, 0, 0, 512, 512, 512, 512);
if (grid!=null) grid.render(poseStack);
super.renderScreen(poseStack, i, j, f);
}
}

View file

@ -7,15 +7,14 @@ import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
import ru.bclib.BCLib;
import ru.bclib.gui.GridScreen;
import ru.bclib.gui.gridlayout.GridCheckboxCell;
import ru.bclib.gui.gridlayout.GridLayout.Alignment;
import ru.bclib.gui.gridlayout.GridRow;
@Environment(EnvType.CLIENT)
public class ConfirmFixScreen extends GridScreen {
static final ResourceLocation BCLIB_LOGO_LOCATION = new ResourceLocation(BCLib.MOD_ID,
"icon.png");
public class ConfirmFixScreen extends BCLibScreen {
@Nullable
private final Screen lastScreen;
protected final ConfirmFixScreen.Listener listener;
@ -23,7 +22,7 @@ public class ConfirmFixScreen extends GridScreen {
protected int id;
public ConfirmFixScreen(@Nullable Screen screen, ConfirmFixScreen.Listener listener) {
super(30, new TranslatableComponent("bclib.datafixer.backupWarning.title"));
super(new TranslatableComponent("bclib.datafixer.backupWarning.title"));
this.lastScreen = screen;
this.listener = listener;
@ -33,27 +32,29 @@ public class ConfirmFixScreen extends GridScreen {
protected void initLayout() {
final int BUTTON_HEIGHT = 20;
grid.addMessageRow(this.description, 25);
grid.addRow().addMessage(this.description, this.font, Alignment.CENTER);
grid.addSpacerRow();
grid.startRow();
grid.addButton( BUTTON_HEIGHT, new TranslatableComponent("bclib.datafixer.backupWarning.backup"), (button) -> {
this.listener.proceed(true, true);
});
GridRow row = grid.addRow();
GridCheckboxCell backup = row.addCheckbox(new TranslatableComponent("bclib.datafixer.backupWarning.backup"), true, BUTTON_HEIGHT, this.font);
grid.startRow();
grid.addButton( BUTTON_HEIGHT, CommonComponents.GUI_CANCEL, (button) -> {
grid.addSpacerRow(10);
row = grid.addRow();
GridCheckboxCell fix = row.addCheckbox(new TranslatableComponent("bclib.datafixer.backupWarning.fix"), true, BUTTON_HEIGHT, this.font);
grid.addSpacerRow(20);
row = grid.addRow();
row.addFiller();
row.addButton(CommonComponents.GUI_CANCEL, BUTTON_HEIGHT, this.font, (button) -> {
this.minecraft.setScreen(this.lastScreen);
});
grid.addButton(0.5f, BUTTON_HEIGHT, new TranslatableComponent("bclib.datafixer.backupWarning.continue"), (button) -> {
this.listener.proceed(false, true);
row.addSpacer();
row.addButton(CommonComponents.GUI_PROCEED, BUTTON_HEIGHT, this.font, (button) -> {
this.listener.proceed(backup.isChecked(), fix.isChecked());
});
grid.startRow();
grid.addButton(0.5f, BUTTON_HEIGHT, new TranslatableComponent("bclib.datafixer.backupWarning.nofixes"), (button) -> {
this.listener.proceed(false, false);
});
grid.endRow();
row.addFiller();
}
public boolean shouldCloseOnEsc() {

View file

@ -5,7 +5,9 @@ import net.fabricmc.api.Environment;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import ru.bclib.gui.GridScreen;
import ru.bclib.gui.gridlayout.GridLayout.Alignment;
import ru.bclib.gui.gridlayout.GridRow;
import ru.bclib.gui.gridlayout.GridScreen;
@Environment(EnvType.CLIENT)
@ -18,7 +20,7 @@ public class ConfirmRestartScreen extends GridScreen {
}
public ConfirmRestartScreen(ConfirmRestartScreen.Listener listener, Component message) {
super(30, new TranslatableComponent("bclib.datafixer.confirmrestart.title"));
super(new TranslatableComponent("bclib.datafixer.confirmrestart.title"));
this.description = message==null?new TranslatableComponent("bclib.datafixer.confirmrestart.message"):message;
this.listener = listener;
@ -26,16 +28,17 @@ public class ConfirmRestartScreen extends GridScreen {
protected void initLayout() {
final int BUTTON_HEIGHT = 20;
grid.addMessageRow(this.description, 25);
grid.startRow();
grid.addButton( BUTTON_HEIGHT, CommonComponents.GUI_PROCEED, (button) -> {
grid.addRow().addMessage(this.description, this.font, Alignment.CENTER);
grid.addSpacerRow();
GridRow row = grid.addRow();
row.addFiller();
row.addButton(CommonComponents.GUI_PROCEED, BUTTON_HEIGHT, (button) -> {
listener.proceed();
});
grid.endRow();
grid.recenterVertically();
row.addFiller();
}
public boolean shouldCloseOnEsc() {

View file

@ -5,14 +5,16 @@ import net.fabricmc.api.Environment;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import ru.bclib.gui.GridScreen;
import ru.bclib.gui.gridlayout.GridLayout.Alignment;
import ru.bclib.gui.gridlayout.GridRow;
import ru.bclib.gui.gridlayout.GridScreen;
@Environment(EnvType.CLIENT)
public class SyncFilesScreen extends GridScreen {
private final Component description;
private final SyncFilesScreen.Listener listener;
public SyncFilesScreen(SyncFilesScreen.Listener listener) {
super(30, new TranslatableComponent("bclib.datafixer.syncfiles.title"));
super(new TranslatableComponent("bclib.datafixer.syncfiles.title"));
this.description = new TranslatableComponent("bclib.datafixer.syncfiles.message");
this.listener = listener;
@ -21,18 +23,21 @@ public class SyncFilesScreen extends GridScreen {
protected void initLayout() {
final int BUTTON_HEIGHT = 20;
grid.addMessageRow(this.description, 25);
grid.addRow().addMessage(this.description, this.font, Alignment.CENTER);
//grid.addMessageRow(this.description, 25);
grid.startRow();
grid.addButton( BUTTON_HEIGHT, CommonComponents.GUI_NO, (button) -> {
grid.addSpacerRow();
GridRow row = grid.addRow();
row.addFiller();
row.addButton(CommonComponents.GUI_NO, BUTTON_HEIGHT, this.font, (button) -> {
listener.proceed(false);
});
grid.addButton( BUTTON_HEIGHT, CommonComponents.GUI_YES, (button) -> {
row.addSpacer();
row.addButton(CommonComponents.GUI_YES, BUTTON_HEIGHT, this.font, (button) -> {
listener.proceed(true);
});
grid.endRow();
grid.recenterVertically();
row.addFiller();
}
public boolean shouldCloseOnEsc() {

View file

@ -5,14 +5,16 @@ import net.fabricmc.api.Environment;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import ru.bclib.gui.GridScreen;
import ru.bclib.gui.gridlayout.GridLayout.Alignment;
import ru.bclib.gui.gridlayout.GridRow;
import ru.bclib.gui.gridlayout.GridScreen;
@Environment(EnvType.CLIENT)
public class WarnBCLibVersionMismatch extends GridScreen {
private final Component description;
private final Listener listener;
public WarnBCLibVersionMismatch(Listener listener) {
super(30, new TranslatableComponent("bclib.datafixer.bclibmissmatch.title"));
super(new TranslatableComponent("bclib.datafixer.bclibmissmatch.title"));
this.description = new TranslatableComponent("bclib.datafixer.bclibmissmatch.message");
this.listener = listener;
@ -21,18 +23,18 @@ public class WarnBCLibVersionMismatch extends GridScreen {
protected void initLayout() {
final int BUTTON_HEIGHT = 20;
grid.addMessageRow(this.description, 25);
grid.addRow().addMessage(this.description, this.font, Alignment.CENTER);
grid.startRow();
grid.addButton( BUTTON_HEIGHT, CommonComponents.GUI_NO, (button) -> {
GridRow row = grid.addRow();
row.addFiller();
row.addButton(CommonComponents.GUI_NO, BUTTON_HEIGHT, this.font, (button) -> {
listener.proceed(false);
});
grid.addButton( BUTTON_HEIGHT, CommonComponents.GUI_YES, (button) -> {
row.addSpacer();
row.addButton(CommonComponents.GUI_YES, BUTTON_HEIGHT, this.font, (button) -> {
listener.proceed(true);
});
grid.endRow();
grid.recenterVertically();
row.addFiller();
}
public boolean shouldCloseOnEsc() {