Skip to content

Commit c8357cd

Browse files
committed
add an IPlaceable interface for GUI elements that can have their position set
1 parent cbe395a commit c8357cd

29 files changed

+497
-130
lines changed

Common/src/main/java/mezz/jei/common/gui/elements/OffsetDrawable.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package mezz.jei.common.gui.elements;
22

3-
import net.minecraft.client.gui.GuiGraphics;
43
import mezz.jei.api.gui.drawable.IDrawable;
4+
import mezz.jei.api.gui.placement.IPlaceable;
5+
import net.minecraft.client.gui.GuiGraphics;
56

67
/**
78
* Draws with a built-in offset.
89
*/
9-
public class OffsetDrawable implements IDrawable {
10+
public class OffsetDrawable implements IDrawable, IPlaceable<OffsetDrawable> {
1011
public static IDrawable create(IDrawable drawable, int xOffset, int yOffset) {
1112
if (xOffset == 0 && yOffset == 0) {
1213
return drawable;
@@ -15,10 +16,10 @@ public static IDrawable create(IDrawable drawable, int xOffset, int yOffset) {
1516
}
1617

1718
private final IDrawable drawable;
18-
private final int xOffset;
19-
private final int yOffset;
19+
private int xOffset;
20+
private int yOffset;
2021

21-
private OffsetDrawable(IDrawable drawable, int xOffset, int yOffset) {
22+
public OffsetDrawable(IDrawable drawable, int xOffset, int yOffset) {
2223
this.drawable = drawable;
2324
this.xOffset = xOffset;
2425
this.yOffset = yOffset;
@@ -47,4 +48,11 @@ public void draw(GuiGraphics guiGraphics, int xOffset, int yOffset) {
4748
public void draw(GuiGraphics guiGraphics) {
4849
this.drawable.draw(guiGraphics, this.xOffset, this.yOffset);
4950
}
51+
52+
@Override
53+
public OffsetDrawable setPosition(int xPos, int yPos) {
54+
this.xOffset = xPos;
55+
this.yOffset = yPos;
56+
return this;
57+
}
5058
}
Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package mezz.jei.common.gui.elements;
22

33
import mezz.jei.api.gui.builder.ITooltipBuilder;
4+
import mezz.jei.api.gui.placement.HorizontalAlignment;
5+
import mezz.jei.api.gui.placement.VerticalAlignment;
46
import mezz.jei.api.gui.widgets.IRecipeWidget;
57
import mezz.jei.api.gui.widgets.ITextWidget;
68
import mezz.jei.common.config.DebugConfig;
7-
import mezz.jei.common.util.HorizontalAlignment;
89
import mezz.jei.common.util.ImmutableRect2i;
910
import mezz.jei.common.util.StringUtil;
10-
import mezz.jei.common.util.VerticalAlignment;
1111
import mezz.jei.core.util.Pair;
1212
import net.minecraft.client.Minecraft;
1313
import net.minecraft.client.gui.Font;
@@ -22,19 +22,20 @@
2222

2323
public class TextWidget implements ITextWidget, IRecipeWidget {
2424
private final List<FormattedText> text;
25-
private final ImmutableRect2i area;
25+
private ImmutableRect2i availableArea;
2626

2727
private HorizontalAlignment horizontalAlignment;
2828
private VerticalAlignment verticalAlignment;
2929
private Font font;
3030
private int color;
3131
private boolean shadow;
3232
private int lineSpacing;
33-
private List<FormattedText> tooltipText = List.of();
33+
3434
private @Nullable List<FormattedText> wrappedText;
35+
private boolean truncated = false;
3536

3637
public TextWidget(List<FormattedText> text, int xPos, int yPos, int maxWidth, int maxHeight) {
37-
this.area = new ImmutableRect2i(xPos, yPos, maxWidth, maxHeight);
38+
this.availableArea = new ImmutableRect2i(xPos, yPos, maxWidth, maxHeight);
3839
Minecraft minecraft = Minecraft.getInstance();
3940
this.font = minecraft.font;
4041
this.color = 0xFF000000;
@@ -44,88 +45,93 @@ public TextWidget(List<FormattedText> text, int xPos, int yPos, int maxWidth, in
4445
this.verticalAlignment = VerticalAlignment.TOP;
4546
}
4647

47-
@Override
48-
public ITextWidget alignHorizontalLeft() {
49-
this.horizontalAlignment = HorizontalAlignment.LEFT;
50-
return this;
48+
private void invalidateCachedValues() {
49+
wrappedText = null;
50+
truncated = false;
5151
}
5252

5353
@Override
54-
public ITextWidget alignHorizontalRight() {
55-
this.horizontalAlignment = HorizontalAlignment.RIGHT;
56-
return this;
54+
public int getWidth() {
55+
return availableArea.width();
5756
}
5857

5958
@Override
60-
public ITextWidget alignHorizontalCenter() {
61-
this.horizontalAlignment = HorizontalAlignment.CENTER;
62-
return this;
59+
public int getHeight() {
60+
return availableArea.height();
6361
}
6462

6563
@Override
66-
public ITextWidget alignVerticalTop() {
67-
this.verticalAlignment = VerticalAlignment.TOP;
64+
public TextWidget setPosition(int xPos, int yPos) {
65+
this.availableArea = this.availableArea.setPosition(xPos, yPos);
66+
invalidateCachedValues();
6867
return this;
6968
}
7069

7170
@Override
72-
public ITextWidget alignVerticalCenter() {
73-
this.verticalAlignment = VerticalAlignment.CENTER;
71+
public TextWidget setTextAlignment(HorizontalAlignment horizontalAlignment) {
72+
if (this.horizontalAlignment.equals(horizontalAlignment)) {
73+
return this;
74+
}
75+
this.horizontalAlignment = horizontalAlignment;
76+
invalidateCachedValues();
7477
return this;
7578
}
7679

7780
@Override
78-
public ITextWidget alignVerticalBottom() {
79-
this.verticalAlignment = VerticalAlignment.BOTTOM;
81+
public TextWidget setTextAlignment(VerticalAlignment verticalAlignment) {
82+
if (this.verticalAlignment.equals(verticalAlignment)) {
83+
return this;
84+
}
85+
this.verticalAlignment = verticalAlignment;
86+
invalidateCachedValues();
8087
return this;
8188
}
8289

8390
@Override
8491
public ITextWidget setFont(Font font) {
8592
this.font = font;
93+
invalidateCachedValues();
8694
return this;
8795
}
8896

8997
@Override
9098
public ITextWidget setColor(int color) {
9199
this.color = color;
100+
invalidateCachedValues();
92101
return this;
93102
}
94103

95104
@Override
96105
public ITextWidget setLineSpacing(int lineSpacing) {
97106
this.lineSpacing = lineSpacing;
107+
invalidateCachedValues();
98108
return this;
99109
}
100110

101111
@Override
102112
public ITextWidget setShadow(boolean shadow) {
103113
this.shadow = shadow;
114+
invalidateCachedValues();
104115
return this;
105116
}
106117

107118
@Override
108119
public ScreenPosition getPosition() {
109-
return area.getScreenPosition();
120+
return availableArea.getScreenPosition();
110121
}
111122

112123
private List<FormattedText> calculateWrappedText() {
113124
if (wrappedText != null) {
114125
return wrappedText;
115126
}
116127
int lineHeight = getLineHeight();
117-
int maxLines = area.height() / lineHeight;
118-
if (maxLines * lineHeight + font.lineHeight <= area.height()) {
128+
int maxLines = availableArea.height() / lineHeight;
129+
if (maxLines * lineHeight + font.lineHeight <= availableArea.height()) {
119130
maxLines++;
120131
}
121-
Pair<List<FormattedText>, Boolean> result = StringUtil.splitLines(font, text, area.width(), maxLines);
132+
Pair<List<FormattedText>, Boolean> result = StringUtil.splitLines(font, text, availableArea.width(), maxLines);
122133
this.wrappedText = result.first();
123-
boolean truncated = result.second();
124-
if (truncated) {
125-
this.tooltipText = text;
126-
} else {
127-
this.tooltipText = List.of();
128-
}
134+
this.truncated = result.second();
129135
return wrappedText;
130136
}
131137

@@ -148,38 +154,30 @@ public void drawWidget(GuiGraphics guiGraphics, double mouseX, double mouseY) {
148154
}
149155

150156
if (DebugConfig.isDebugGuisEnabled()) {
151-
guiGraphics.fill(0,0, area.width(), area.height(), 0xAAAAAA00);
157+
guiGraphics.fill(0,0, availableArea.width(), availableArea.height(), 0xAAAAAA00);
152158
}
153159
}
154160

155161
@Override
156162
public void getTooltip(ITooltipBuilder tooltip, double mouseX, double mouseY) {
157-
if (mouseX >= 0 && mouseX < area.width() && mouseY >= 0 && mouseY < area.height()) {
163+
if (mouseX >= 0 && mouseX < availableArea.width() && mouseY >= 0 && mouseY < availableArea.height()) {
158164
calculateWrappedText();
159-
tooltip.addAll(tooltipText);
165+
if (truncated) {
166+
tooltip.addAll(text);
167+
}
160168
}
161169
}
162170

163171
private int getXPos(FormattedCharSequence text) {
164-
return switch (horizontalAlignment) {
165-
case LEFT -> 0;
166-
case RIGHT -> this.area.width() - font.width(text);
167-
case CENTER -> Math.round((this.area.width() - font.width(text)) / 2f);
168-
};
172+
return getXPos(font.width(text));
169173
}
170174

171-
private int getYPosStart(int lineHeight, List<FormattedText> text) {
172-
if (verticalAlignment == VerticalAlignment.TOP) {
173-
return 0;
174-
}
175+
private int getXPos(int lineWidth) {
176+
return horizontalAlignment.getXPos(this.availableArea.width(), lineWidth);
177+
}
175178

179+
private int getYPosStart(int lineHeight, List<FormattedText> text) {
176180
int linesHeight = (lineHeight * text.size()) - lineSpacing - 1;
177-
if (verticalAlignment == VerticalAlignment.BOTTOM) {
178-
return area.height() - linesHeight;
179-
} else if (verticalAlignment == VerticalAlignment.CENTER) {
180-
return Math.round((area.height() - linesHeight) / 2f);
181-
} else {
182-
throw new IllegalArgumentException("Unknown verticalAlignment " + verticalAlignment);
183-
}
181+
return verticalAlignment.getYPos(this.availableArea.height(), linesHeight);
184182
}
185183
}

CommonApi/src/main/java/mezz/jei/api/gui/builder/IRecipeLayoutBuilder.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package mezz.jei.api.gui.builder;
22

3+
import mezz.jei.api.gui.placement.IPlaceable;
34
import mezz.jei.api.gui.widgets.ISlottedWidgetFactory;
45
import mezz.jei.api.recipe.IFocusGroup;
56
import mezz.jei.api.recipe.RecipeIngredientRole;
@@ -24,7 +25,8 @@ public interface IRecipeLayoutBuilder {
2425
* @since 19.19.0
2526
*/
2627
default IRecipeSlotBuilder addInputSlot(int x, int y) {
27-
return addSlot(RecipeIngredientRole.INPUT, x, y);
28+
return addSlot(RecipeIngredientRole.INPUT)
29+
.setPosition(x, y);
2830
}
2931

3032
/**
@@ -37,20 +39,34 @@ default IRecipeSlotBuilder addInputSlot(int x, int y) {
3739
* @since 19.19.0
3840
*/
3941
default IRecipeSlotBuilder addOutputSlot(int x, int y) {
40-
return addSlot(RecipeIngredientRole.OUTPUT, x, y);
42+
return addSlot(RecipeIngredientRole.OUTPUT)
43+
.setPosition(x, y);
4144
}
4245

4346
/**
4447
* Add a slot that will be drawn at the given position relative to the recipe layout.
4548
*
46-
* @param recipeIngredientRole the {@link RecipeIngredientRole} of this slot (for lookups).
49+
* @param role the {@link RecipeIngredientRole} of this slot (for lookups).
4750
* @param x relative x position of the slot on the recipe layout.
4851
* @param y relative y position of the slot on the recipe layout.
4952
* @return a {@link IRecipeSlotBuilder} that has further methods for adding ingredients, etc.
5053
*
5154
* @since 9.3.0
5255
*/
53-
IRecipeSlotBuilder addSlot(RecipeIngredientRole recipeIngredientRole, int x, int y);
56+
default IRecipeSlotBuilder addSlot(RecipeIngredientRole role, int x, int y) {
57+
return addSlot(role)
58+
.setPosition(x, y);
59+
}
60+
61+
/**
62+
* Add a slot and set its position using {@link IPlaceable} methods.
63+
*
64+
* @param role the {@link RecipeIngredientRole} of this slot (for lookups).
65+
* @return a {@link IRecipeSlotBuilder} that has further methods for adding ingredients, etc.
66+
*
67+
* @since 19.19.1
68+
*/
69+
IRecipeSlotBuilder addSlot(RecipeIngredientRole role);
5470

5571
/**
5672
* Assign this slot to a {@link ISlottedWidgetFactory},

CommonApi/src/main/java/mezz/jei/api/gui/builder/IRecipeSlotBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import mezz.jei.api.gui.drawable.IDrawable;
44
import mezz.jei.api.gui.ingredient.IRecipeSlotRichTooltipCallback;
55
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
6+
import mezz.jei.api.gui.placement.IPlaceable;
67
import mezz.jei.api.helpers.IGuiHelper;
78
import mezz.jei.api.ingredients.IIngredientRenderer;
89
import mezz.jei.api.ingredients.IIngredientType;
@@ -21,7 +22,7 @@
2122
* @since 9.3.0
2223
*/
2324
@ApiStatus.NonExtendable
24-
public interface IRecipeSlotBuilder extends IIngredientAcceptor<IRecipeSlotBuilder> {
25+
public interface IRecipeSlotBuilder extends IIngredientAcceptor<IRecipeSlotBuilder>, IPlaceable<IRecipeSlotBuilder> {
2526
/**
2627
* Add a callback to alter the tooltip for these ingredients.
2728
*
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package mezz.jei.api.gui.placement;
2+
3+
/**
4+
* Represents a horizontal alignment of an element inside a larger area.
5+
* @since 19.19.1
6+
*/
7+
public enum HorizontalAlignment {
8+
LEFT {
9+
@Override
10+
public int getXPos(int availableWidth, int elementWidth) {
11+
return 0;
12+
}
13+
},
14+
CENTER {
15+
@Override
16+
public int getXPos(int availableWidth, int elementWidth) {
17+
return Math.round((availableWidth - elementWidth) / 2f);
18+
}
19+
},
20+
RIGHT {
21+
@Override
22+
public int getXPos(int availableWidth, int elementWidth) {
23+
return availableWidth - elementWidth;
24+
}
25+
};
26+
27+
/**
28+
* Calculate the x position needed to align an element with the given width inside the availableArea.
29+
* @since 19.19.1
30+
*/
31+
public abstract int getXPos(int availableWidth, int elementWidth);
32+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package mezz.jei.api.gui.placement;
2+
3+
/**
4+
* Interface for things that can have their position set, and be aligned vertically and horizontally in an area.
5+
*
6+
* @since 19.19.1
7+
*/
8+
public interface IPlaceable<THIS extends IPlaceable<THIS>> {
9+
/**
10+
* Place this element at the given position.
11+
* @since 19.19.1
12+
*/
13+
THIS setPosition(int xPos, int yPos);
14+
15+
/**
16+
* Place this element inside the given area, with the given alignment.
17+
*
18+
* @since 19.19.1
19+
*/
20+
default THIS setPosition(int areaX, int areaY, int areaWidth, int areaHeight, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) {
21+
int x = areaX + horizontalAlignment.getXPos(areaWidth, getWidth());
22+
int y = areaY + verticalAlignment.getYPos(areaHeight, getHeight());
23+
return setPosition(x, y);
24+
}
25+
26+
/**
27+
* Get the width of this element.
28+
* @since 19.19.1
29+
*/
30+
int getWidth();
31+
32+
/**
33+
* Get the height of this element.
34+
* @since 19.19.1
35+
*/
36+
int getHeight();
37+
}

0 commit comments

Comments
 (0)