aboutsummaryrefslogtreecommitdiff
path: root/runtime/src
diff options
context:
space:
mode:
authorshedaniel <daniel@shedaniel.me>2023-07-03 18:30:58 +0800
committershedaniel <daniel@shedaniel.me>2024-04-16 00:38:18 +0900
commit5aaae0c437971008678afb44474f0d43bdad9783 (patch)
tree65bae7c154e1dc7bca102791426e3e9d3a720269 /runtime/src
parentee254e4a09aba59fca596f831a66530a7607925b (diff)
downloadRoughlyEnoughItems-5aaae0c437971008678afb44474f0d43bdad9783.tar.gz
RoughlyEnoughItems-5aaae0c437971008678afb44474f0d43bdad9783.tar.bz2
RoughlyEnoughItems-5aaae0c437971008678afb44474f0d43bdad9783.zip
Improve Widgets creation
Diffstat (limited to 'runtime/src')
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoriesListWidget.java11
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoryEntryWidget.java5
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/DelegateWidgetWithTranslate.java5
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java17
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java63
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/MergedWidgetWithBounds.java132
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScissoredWidget.java19
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScrollableViewWidget.java18
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/VStackWidget.java157
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/common/util/RectangleUtils.java12
10 files changed, 378 insertions, 61 deletions
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoriesListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoriesListWidget.java
index 46e48b290..237cd64f7 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoriesListWidget.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoriesListWidget.java
@@ -24,22 +24,25 @@
package me.shedaniel.rei.impl.client.gui.config.components;
import dev.architectury.utils.value.IntValue;
+import me.shedaniel.clothconfig2.api.scroll.ScrollingContainer;
import me.shedaniel.math.Rectangle;
import me.shedaniel.rei.api.client.gui.widgets.Widget;
+import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds;
import me.shedaniel.rei.impl.client.gui.config.options.OptionCategory;
import me.shedaniel.rei.impl.client.gui.widget.ListWidget;
+import me.shedaniel.rei.impl.client.gui.widget.ScrollableViewWidget;
+import me.shedaniel.rei.impl.common.util.RectangleUtils;
import java.util.List;
public class ConfigCategoriesListWidget {
public static Widget create(Rectangle bounds, List<OptionCategory> categories, IntValue selected) {
- return ListWidget.builderOf(bounds, categories,
- (index, entry) -> ConfigCategoryEntryWidget.create(entry))
- .paddingHorizontal(3)
- .paddingVertical(5)
+ WidgetWithBounds list = ListWidget.builderOf(RectangleUtils.inset(bounds, 3, 5), categories,
+ (index, entry) -> ConfigCategoryEntryWidget.create(entry))
.gap(3)
.isSelectable((index, entry) -> true)
.selected(selected)
.build();
+ return ScrollableViewWidget.create(bounds, list, true);
}
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoryEntryWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoryEntryWidget.java
index 1bc56d216..177c0104d 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoryEntryWidget.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoryEntryWidget.java
@@ -38,9 +38,10 @@ public class ConfigCategoryEntryWidget {
.leftAligned();
Font font = Minecraft.getInstance().font;
Rectangle bounds = new Rectangle(0, 0, label.getBounds().getMaxX(), 7 * 3);
- return Widgets.withBounds(Widgets.concat(
+ return Widgets.concatWithBounds(
+ bounds,
label,
Widgets.createTexturedWidget(category.getIcon(), new Rectangle(3, 3, 16, 16), 0, 0, 1, 1, 1, 1)
- ), bounds);
+ );
}
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/DelegateWidgetWithTranslate.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/DelegateWidgetWithTranslate.java
index a7b67f197..05026ab8b 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/DelegateWidgetWithTranslate.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/DelegateWidgetWithTranslate.java
@@ -42,6 +42,11 @@ public class DelegateWidgetWithTranslate extends DelegateWidget {
this.translate = translate;
}
+ public DelegateWidgetWithTranslate(Widget widget, Supplier<Matrix4f> translate) {
+ super(widget);
+ this.translate = translate;
+ }
+
protected Matrix4f translate() {
return translate.get();
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java
index 05575da4d..b0a6f67d9 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java
@@ -59,7 +59,8 @@ import java.util.function.Supplier;
@ApiStatus.Internal
@Environment(EnvType.CLIENT)
public final class InternalWidgets {
- private InternalWidgets() {}
+ private InternalWidgets() {
+ }
public static Widget createAutoCraftingButtonWidget(Rectangle displayBounds, Rectangle rectangle, Component text, Supplier<Display> displaySupplier, Supplier<Collection<ResourceLocation>> idsSupplier, List<Widget> setupDisplay, DisplayCategory<?> category) {
Button autoCraftingButton = Widgets.createButton(rectangle, text)
@@ -160,6 +161,10 @@ public final class InternalWidgets {
return new MergedWidget(widgets);
}
+ public static WidgetWithBounds concatWidgetsWithBounds(Supplier<Rectangle> bounds, List<Widget> widgets) {
+ return new MergedWidgetWithBounds(bounds, widgets);
+ }
+
private static class LateRenderableWidget extends DelegateWidget implements LateRenderable {
private LateRenderableWidget(Widget widget) {
super(widget);
@@ -271,6 +276,11 @@ public final class InternalWidgets {
}
@Override
+ public WidgetWithBounds concatWidgetsWithBounds(Supplier<Rectangle> bounds, List<Widget> widgets) {
+ return InternalWidgets.concatWidgetsWithBounds(bounds, widgets);
+ }
+
+ @Override
public WidgetWithBounds noOp() {
return NoOpWidget.INSTANCE;
}
@@ -286,6 +296,11 @@ public final class InternalWidgets {
}
@Override
+ public WidgetWithBounds wrapScissored(WidgetWithBounds widget) {
+ return new ScissoredWidget(widget);
+ }
+
+ @Override
public WidgetWithBounds wrapPadded(int padLeft, int padRight, int padTop, int padBottom, WidgetWithBounds widget) {
return new PaddedWidget(padLeft, padRight, padTop, padBottom, widget);
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java
index af1ddcd75..7f66ff7cb 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java
@@ -47,8 +47,6 @@ public class ListWidget {
public static abstract class AbstractBuilder<T extends WidgetWithBounds, SELF extends AbstractBuilder<T, SELF>> {
protected final Rectangle bounds;
- protected int paddingHorizontal = 0;
- protected int paddingVertical = 0;
protected IntValue selected = new IntValue() {
private int value;
@@ -64,22 +62,11 @@ public class ListWidget {
};
protected int gap = 4;
protected boolean calculateTotalHeightDynamically = false;
- protected boolean background = true;
protected AbstractBuilder(Rectangle bounds) {
this.bounds = bounds;
}
- public SELF paddingHorizontal(int paddingHorizontal) {
- this.paddingHorizontal = paddingHorizontal;
- return (SELF) this;
- }
-
- public SELF paddingVertical(int paddingVertical) {
- this.paddingVertical = paddingVertical;
- return (SELF) this;
- }
-
public SELF selected(IntValue selected) {
this.selected = selected;
return (SELF) this;
@@ -100,11 +87,6 @@ public class ListWidget {
return (SELF) this;
}
- public SELF background(boolean background) {
- this.background = background;
- return (SELF) this;
- }
-
public abstract WidgetWithBounds build();
}
@@ -126,8 +108,8 @@ public class ListWidget {
@Override
public WidgetWithBounds build() {
- return ListWidget.create(bounds, entries, paddingHorizontal, paddingVertical, selected, gap, calculateTotalHeightDynamically,
- cellRenderer, isSelectable, background);
+ return ListWidget.create(bounds, entries, selected, gap, calculateTotalHeightDynamically,
+ cellRenderer, isSelectable);
}
}
@@ -147,23 +129,22 @@ public class ListWidget {
@Override
public WidgetWithBounds build() {
- return ListWidget.create(bounds, entries, paddingHorizontal, paddingVertical, selected, gap, calculateTotalHeightDynamically,
- isSelectable, background);
+ return ListWidget.create(bounds, entries, selected, gap, calculateTotalHeightDynamically,
+ isSelectable);
}
}
- public static <T> WidgetWithBounds create(Rectangle bounds, List<T> entries, int paddingHorizontal, int paddingVertical,
- IntValue selected, int gap, boolean calculateTotalHeightDynamically, ListCellRenderer<T> cellRenderer,
- ListEntryPredicate<T> isSelectable, boolean background) {
+ public static <T> WidgetWithBounds create(Rectangle bounds, List<T> entries, IntValue selected, int gap,
+ boolean calculateTotalHeightDynamically, ListCellRenderer<T> cellRenderer,
+ ListEntryPredicate<T> isSelectable) {
int[] i = {0};
- return create(bounds, CollectionUtils.map(entries, entry -> cellRenderer.create(i[0]++, entry)), paddingHorizontal, paddingVertical,
- selected, gap, calculateTotalHeightDynamically, (index, entry) -> isSelectable.test(index, entries.get(index)), background);
+ return create(bounds, CollectionUtils.map(entries, entry -> cellRenderer.create(i[0]++, entry)),
+ selected, gap, calculateTotalHeightDynamically, (index, entry) -> isSelectable.test(index, entries.get(index)));
}
- public static <T extends WidgetWithBounds> WidgetWithBounds create(Rectangle bounds, List<T> entries, int paddingHorizontal, int paddingVertical,
- IntValue selected, int gap, boolean calculateTotalHeightDynamically,
- ListEntryPredicate<T> isSelectable, boolean background) {
- int[] height = {collectTotalHeight(entries, gap) + paddingVertical * 2};
+ public static <T extends WidgetWithBounds> WidgetWithBounds create(Rectangle bounds, List<T> entries, IntValue selected, int gap,
+ boolean calculateTotalHeightDynamically, ListEntryPredicate<T> isSelectable) {
+ int[] height = {collectTotalHeight(entries, gap)};
Rectangle innerBounds = bounds.clone();
if (height[0] > bounds.getHeight()) {
@@ -171,22 +152,22 @@ public class ListWidget {
}
int[] i = {0};
- List<CellWidget<T>> wrapped = CollectionUtils.map(entries, cell -> new CellWidget<>(innerBounds, paddingHorizontal, i[0]++, cell, selected, entries, isSelectable));
+ List<CellWidget<T>> wrapped = CollectionUtils.map(entries, cell -> new CellWidget<>(innerBounds, i[0]++, cell, selected, entries, isSelectable));
Widget update = Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> {
if (calculateTotalHeightDynamically) {
- height[0] = collectTotalHeight(entries, gap) + paddingVertical * 2;
+ height[0] = collectTotalHeight(entries, gap);
innerBounds.width = height[0] > bounds.getHeight() ? bounds.width - 6 : bounds.width;
}
- int y = bounds.y + paddingVertical;
+ int y = bounds.y;
for (CellWidget<T> cell : wrapped) {
- cell.position.move(bounds.x + paddingHorizontal, y);
+ cell.position.move(bounds.x, y);
y += (calculateTotalHeightDynamically ? cell.getBounds().getHeight() : cell.height) + gap;
}
if (selected.getAsInt() != -1) {
CellWidget<T> cellWidget = wrapped.get(selected.getAsInt());
- int x1 = innerBounds.x + paddingHorizontal, x2 = innerBounds.getMaxX() - paddingHorizontal;
+ int x1 = innerBounds.x, x2 = innerBounds.getMaxX();
boolean contains = new Rectangle(x1 - 1, cellWidget.position.y - 1, x2 - x1 + 2, cellWidget.getBounds().height + 2).contains(mouseX, mouseY);
GuiComponent.fill(matrices, x1 - 1, cellWidget.position.y - 1, x2 + 1,
cellWidget.position.y + cellWidget.getBounds().height + 1, contains ? 0xFFD0D0D0 : 0xFF8F8F8F);
@@ -197,13 +178,12 @@ public class ListWidget {
List<Widget> innerWidgets = new ArrayList<>();
innerWidgets.add(update);
innerWidgets.addAll(wrapped);
- return ScrollableViewWidget.create(bounds, Widgets.withBounds(Widgets.concat(innerWidgets),
- () -> new Rectangle(bounds.x, bounds.y, bounds.width, height[0])), background);
+ return Widgets.concatWithBounds(() -> new Rectangle(bounds.x, bounds.y, bounds.width, height[0]),
+ innerWidgets);
}
private static class CellWidget<T> extends DelegateWidgetWithTranslate {
private final Rectangle bounds;
- private final int paddingHorizontal;
private final int index;
private final Point position = new Point();
private final int height;
@@ -211,10 +191,9 @@ public class ListWidget {
private final List<T> list;
private final ListEntryPredicate<T> isSelectable;
- public CellWidget(Rectangle bounds, int paddingHorizontal, int index, WidgetWithBounds widget, IntValue selected, List<T> list, ListEntryPredicate<T> isSelectable) {
+ public CellWidget(Rectangle bounds, int index, WidgetWithBounds widget, IntValue selected, List<T> list, ListEntryPredicate<T> isSelectable) {
super(widget, Matrix4f::new);
this.bounds = bounds;
- this.paddingHorizontal = paddingHorizontal;
this.index = index;
this.height = widget.getBounds().getHeight();
this.selected = selected;
@@ -232,7 +211,7 @@ public class ListWidget {
boolean clicked = super.mouseClicked(mouseX, mouseY, button);
Rectangle bounds = delegate().getBounds();
- if (clicked || new Rectangle(position.x, position.y, this.bounds.width - paddingHorizontal * 2, bounds.height).contains(mouseX, mouseY)) {
+ if (clicked || new Rectangle(position.x, position.y, this.bounds.width, bounds.height).contains(mouseX, mouseY)) {
if (isSelectable.test(index, list.get(index))) {
selected.accept(index);
if (!clicked) {
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/MergedWidgetWithBounds.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/MergedWidgetWithBounds.java
new file mode 100644
index 000000000..99c7975c4
--- /dev/null
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/MergedWidgetWithBounds.java
@@ -0,0 +1,132 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.impl.client.gui.widget;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import me.shedaniel.math.Rectangle;
+import me.shedaniel.rei.api.client.gui.widgets.Widget;
+import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds;
+import me.shedaniel.rei.api.common.util.CollectionUtils;
+import net.minecraft.client.gui.components.events.GuiEventListener;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Supplier;
+
+public class MergedWidgetWithBounds extends WidgetWithBounds {
+ private final Supplier<Rectangle> bounds;
+ private final List<Widget> widgets;
+
+ public MergedWidgetWithBounds(Supplier<Rectangle> bounds, List<Widget> widgets) {
+ this.bounds = bounds;
+ this.widgets = widgets;
+ }
+
+ @Override
+ public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
+ for (Widget widget : widgets) {
+ widget.setZ(getZ());
+ widget.render(matrices, mouseX, mouseY, delta);
+ }
+ }
+
+ @Override
+ public List<? extends GuiEventListener> children() {
+ return widgets;
+ }
+
+ @Override
+ public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
+ for (Widget widget : this.widgets) {
+ if (widget.mouseScrolled(mouseX, mouseY, amount))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
+ for (Widget widget : this.widgets) {
+ if (widget.keyPressed(keyCode, scanCode, modifiers))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean keyReleased(int keyCode, int scanCode, int modifiers) {
+ for (Widget widget : this.widgets) {
+ if (widget.keyReleased(keyCode, scanCode, modifiers))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean charTyped(char character, int modifiers) {
+ for (Widget widget : this.widgets) {
+ if (widget.charTyped(character, modifiers))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
+ for (Widget widget : this.widgets) {
+ if (widget.mouseDragged(mouseX, mouseY, button, deltaX, deltaY))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean mouseReleased(double mouseX, double mouseY, int button) {
+ for (Widget widget : this.widgets) {
+ if (widget.mouseReleased(mouseX, mouseY, button))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public double getZRenderingPriority() {
+ return CollectionUtils.max(widgets, Comparator.comparingDouble(Widget::getZRenderingPriority))
+ .map(Widget::getZRenderingPriority).orElse(0.0);
+ }
+
+ @Override
+ public Rectangle getBounds() {
+ return bounds.get();
+ }
+
+ @Deprecated
+ @Override
+ public void render(PoseStack matrices, Rectangle bounds, int mouseX, int mouseY, float delta) {
+ Rectangle clone = getBounds().clone();
+ getBounds().setBounds(bounds);
+ render(matrices, mouseX, mouseY, delta);
+ getBounds().setBounds(clone);
+ }
+} \ No newline at end of file
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScissoredWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScissoredWidget.java
index c06dfe862..0e745f225 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScissoredWidget.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScissoredWidget.java
@@ -28,19 +28,28 @@ import me.shedaniel.math.Rectangle;
import me.shedaniel.rei.api.client.gui.widgets.CloseableScissors;
import me.shedaniel.rei.api.client.gui.widgets.DelegateWidget;
import me.shedaniel.rei.api.client.gui.widgets.Widget;
+import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds;
+
+import java.util.function.Supplier;
public class ScissoredWidget extends DelegateWidget {
- private final Rectangle bounds;
+ private final Supplier<Rectangle> bounds;
public ScissoredWidget(Rectangle bounds, Widget widget) {
super(widget);
- this.bounds = bounds;
+ this.bounds = () -> bounds;
+ }
+
+ public ScissoredWidget(WidgetWithBounds widget) {
+ super(widget);
+ this.bounds = widget::getBounds;
}
@Override
public void render(PoseStack poseStack, int mouseX, int mouseY, float delta) {
- try (CloseableScissors scissors = scissor(poseStack, this.bounds)) {
- boolean containsMouse = this.bounds.contains(mouseX, mouseY);
+ try (CloseableScissors scissors = scissor(poseStack, this.bounds.get())) {
+ boolean containsMouse = this.delegate() instanceof WidgetWithBounds withBounds ? withBounds.containsMouse(mouseX, mouseY)
+ : this.bounds.get().contains(mouseX, mouseY);
if (containsMouse) {
super.render(poseStack, mouseX, mouseY, delta);
@@ -52,7 +61,7 @@ public class ScissoredWidget extends DelegateWidget {
@Override
public Rectangle getBounds() {
- return bounds;
+ return bounds.get();
}
@Override
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScrollableViewWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScrollableViewWidget.java
index 8d4cf677d..b8f2552a5 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScrollableViewWidget.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScrollableViewWidget.java
@@ -39,7 +39,11 @@ import java.util.List;
public class ScrollableViewWidget {
public static WidgetWithBounds create(Rectangle bounds, WidgetWithBounds inner, boolean background) {
- ScrollingContainer scrolling = new ScrollingContainer() {
+ return create(bounds, inner, new ScrollingContainer[1], background);
+ }
+
+ public static WidgetWithBounds create(Rectangle bounds, WidgetWithBounds inner, ScrollingContainer[] scrollingRef, boolean background) {
+ scrollingRef[0] = new ScrollingContainer() {
@Override
public Rectangle getBounds() {
return bounds;
@@ -54,16 +58,16 @@ public class ScrollableViewWidget {
List<Widget> widgets = new ArrayList<>();
if (background) {
- widgets.add(HoleWidget.create(bounds, scrolling::scrollAmountInt, 32));
+ widgets.add(HoleWidget.create(bounds, scrollingRef[0]::scrollAmountInt, 32));
}
- widgets.add(Widgets.scissored(scrolling.getScissorBounds(), Widgets.withTranslate(inner,
- () -> Matrix4f.createTranslateMatrix(0, -scrolling.scrollAmountInt(), 0))));
+ widgets.add(Widgets.scissored(scrollingRef[0].getScissorBounds(), Widgets.withTranslate(inner,
+ () -> Matrix4f.createTranslateMatrix(0, -scrollingRef[0].scrollAmountInt(), 0))));
widgets.add(Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> {
- scrolling.updatePosition(delta);
- scrolling.renderScrollBar();
+ scrollingRef[0].updatePosition(delta);
+ scrollingRef[0].renderScrollBar();
}));
- widgets.add(createScrollerWidget(bounds, scrolling));
+ widgets.add(createScrollerWidget(bounds, scrollingRef[0]));
return Widgets.withBounds(Widgets.concat(widgets), bounds);
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/VStackWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/VStackWidget.java
new file mode 100644
index 000000000..d82c2ba3b
--- /dev/null
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/VStackWidget.java
@@ -0,0 +1,157 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.impl.client.gui.widget;
+
+import com.google.common.collect.ForwardingList;
+import com.mojang.math.Matrix4f;
+import me.shedaniel.math.Point;
+import me.shedaniel.math.Rectangle;
+import me.shedaniel.rei.api.client.gui.widgets.Widget;
+import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds;
+import me.shedaniel.rei.api.client.gui.widgets.Widgets;
+import me.shedaniel.rei.api.common.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class VStackWidget {
+ public static <T extends WidgetWithBounds> Builder<T> builder(int x, int y) {
+ return builder(new Point(x, y));
+ }
+
+ public static <T extends WidgetWithBounds> Builder<T> builder(Point point) {
+ return new Builder<>(point);
+ }
+
+ public static class Builder<T extends WidgetWithBounds> extends ForwardingList<T> {
+ private final Point point;
+ private final List<T> entries = new ArrayList<>();
+ private int gap;
+ private boolean calculateWidthDynamically = false;
+ private boolean calculateTotalHeightDynamically = false;
+
+ public Builder(Point point) {
+ this.point = point;
+ }
+
+ @Override
+ protected List<T> delegate() {
+ return entries;
+ }
+
+ public Builder<T> gap(int gap) {
+ this.gap = gap;
+ return this;
+ }
+
+ public Builder<T> calculateWidthDynamically() {
+ this.calculateWidthDynamically = true;
+ return this;
+ }
+
+ public Builder<T> calculateTotalHeightDynamically() {
+ this.calculateTotalHeightDynamically = true;
+ return this;
+ }
+
+ public WidgetWithBounds build() {
+ return VStackWidget.create(point, entries, gap, calculateWidthDynamically, calculateTotalHeightDynamically);
+ }
+ }
+
+ public static <T extends WidgetWithBounds> WidgetWithBounds create(Point point, List<T> entries,
+ int gap, boolean calculateWidthDynamically, boolean calculateTotalHeightDynamically) {
+ Rectangle bounds = new Rectangle(point.x, point.y, collectMaximumWidth(entries), collectTotalHeight(entries, gap));
+
+ List<CellWidget<T>> wrapped = CollectionUtils.map(entries, CellWidget::new);
+ Widget update = Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> {
+ if (calculateWidthDynamically) {
+ bounds.width = collectMaximumWidth(entries);
+ }
+
+ if (calculateTotalHeightDynamically) {
+ bounds.height = collectTotalHeight(entries, gap);
+ }
+
+ bounds.move(point.x, point.y);
+
+ int y = bounds.y;
+ for (CellWidget<T> cell : wrapped) {
+ cell.position.move(bounds.x, y);
+ y += (calculateTotalHeightDynamically ? cell.getBounds().getHeight() : cell.height) + gap;
+ }
+ });
+ List<Widget> innerWidgets = new ArrayList<>();
+ innerWidgets.add(update);
+ innerWidgets.addAll(wrapped);
+ return Widgets.concatWithBounds(bounds, innerWidgets);
+ }
+
+ private static class CellWidget<T> extends DelegateWidgetWithTranslate {
+ private final Point position = new Point();
+ private final int height;
+
+ public CellWidget(WidgetWithBounds widget) {
+ super(widget, Matrix4f::new);
+ this.height = widget.getBounds().getHeight();
+ }
+
+ @Override
+ public WidgetWithBounds delegate() {
+ return (WidgetWithBounds) super.delegate();
+ }
+
+ @Override
+ protected Matrix4f translate() {
+ Rectangle bounds = delegate().getBounds();
+ return Matrix4f.createTranslateMatrix(position.x - bounds.x, position.y - bounds.y, 0);
+ }
+ }
+
+ private static int collectMaximumWidth(List<? extends WidgetWithBounds> cells) {
+ int width = 0;
+ for (WidgetWithBounds cell : cells) {
+ width = Math.max(width, cell.getBounds().getWidth());
+ }
+ return width;
+ }
+
+ private static int collectTotalHeight(List<? extends WidgetWithBounds> cells, int gap) {
+ int height = Math.max(0, (cells.size() - 1) * gap);
+ for (WidgetWithBounds cell : cells) {
+ height += cell.getBounds().getHeight();
+ }
+ return height;
+ }
+
+ @FunctionalInterface
+ public interface ListCellRenderer<T> {
+ WidgetWithBounds create(int index, T entry);
+ }
+
+ @FunctionalInterface
+ public interface ListEntryPredicate<T> {
+ boolean test(int index, T entry);
+ }
+}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/common/util/RectangleUtils.java b/runtime/src/main/java/me/shedaniel/rei/impl/common/util/RectangleUtils.java
index 8918ad32d..1e54c1129 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/common/util/RectangleUtils.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/common/util/RectangleUtils.java
@@ -29,6 +29,18 @@ import java.util.Comparator;
import java.util.stream.Stream;
public class RectangleUtils {
+ public static Rectangle inset(Rectangle rectangle, int inset) {
+ return inset(rectangle, inset, inset);
+ }
+
+ public static Rectangle inset(Rectangle rectangle, int insetX, int insetY) {
+ return inset(rectangle, insetX, insetY, insetX, insetY);
+ }
+
+ public static Rectangle inset(Rectangle rectangle, int insetLeft, int insetTop, int insetRight, int insetBottom) {
+ return new Rectangle(rectangle.x + insetLeft, rectangle.y + insetTop, rectangle.width - insetLeft - insetRight, rectangle.height - insetTop - insetBottom);
+ }
+
public static Rectangle excludeZones(Rectangle rectangle, Stream<Rectangle> exclusionZones) {
return exclusionZones
.filter(rect -> rect.intersects(rectangle))