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() {

View file

@ -2,9 +2,9 @@
"message.bclib.anvil_damage": "§cDamage",
"bclib.datafixer.backupWarning.title": "Guardian detected an incompatible World",
"bclib.datafixer.backupWarning.message": "The Guardian detected, that the internals of the following Mods did change since this world was last played.\n\nWe can automatically change the world for you. If you continue without applying the changes the world may not load correct. Before you continue, you should create a Backup.",
"bclib.datafixer.backupWarning.backup": "Create Backup and Continue",
"bclib.datafixer.backupWarning.backup": "Create Backup before applying Fixes",
"bclib.datafixer.backupWarning.nofixes": "Continue Without Fixes",
"bclib.datafixer.backupWarning.continue": "Continue Without Backup",
"bclib.datafixer.backupWarning.fix": "Apply Fixes",
"bclib.datafixer.bclibmissmatch.title": "Version Mismatch",
"bclib.datafixer.bclibmissmatch.message": "The Version of BCLib on the server and this client do not match. This will cause problems when playing.\n\nDo you want to automatically download the BCLib-Version from the server. You will need to manually delete the old version from your Mods Directory.",
"bclib.datafixer.syncfiles.title": "Mismatching (Config-)Files",