diff options
Diffstat (limited to 'runtime/src/main/java/me')
7 files changed, 246 insertions, 171 deletions
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java index a18d7aeab..d59ece66c 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java @@ -684,7 +684,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { @Comment("Declares how the scrollbar in composite screen should act.") private boolean compositeScrollBarPermanent = false; private boolean toastDisplayedOnCopyIdentifier = true; @Comment("Declares whether REI should use compact tabs for categories.") private boolean useCompactTabs = true; - @Comment("Declares whether REI should use compact tab buttons for categories.") private boolean useCompactTabButtons = false; + @Comment("Declares whether REI should use compact tab buttons for categories.") @ConfigEntry.Gui.Excluded private boolean useCompactTabButtons = false; } public static class Search { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java index b2173b0c6..28050a6e3 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java @@ -29,6 +29,7 @@ import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.datafixers.util.Pair; import com.mojang.math.Matrix4f; import dev.architectury.fluid.FluidStack; +import dev.architectury.utils.value.IntValue; import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.REIRuntime; import me.shedaniel.rei.api.client.config.ConfigObject; @@ -50,6 +51,7 @@ import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.impl.client.REIRuntimeImpl; import me.shedaniel.rei.impl.client.gui.widget.EntryWidget; +import me.shedaniel.rei.impl.client.gui.widget.TabContainerWidget; import me.shedaniel.rei.impl.client.gui.widget.entrylist.EntryListWidget; import me.shedaniel.rei.impl.display.DisplaySpec; import net.minecraft.ChatFormatting; @@ -76,17 +78,17 @@ import java.util.stream.Stream; public abstract class AbstractDisplayViewingScreen extends Screen implements DisplayScreen { protected final Map<DisplayCategory<?>, List<DisplaySpec>> categoryMap; protected final List<DisplayCategory<?>> categories; + protected final TabContainerWidget tabs = new TabContainerWidget(); protected List<EntryStack<?>> ingredientStackToNotice = new ArrayList<>(); protected List<EntryStack<?>> resultStackToNotice = new ArrayList<>(); protected int selectedCategoryIndex = 0; - protected int tabsPerPage; + protected int categoryPages = -1; protected Rectangle bounds; - protected AbstractDisplayViewingScreen(Map<DisplayCategory<?>, List<DisplaySpec>> categoryMap, @Nullable CategoryIdentifier<?> category, int tabsPerPage) { + protected AbstractDisplayViewingScreen(Map<DisplayCategory<?>, List<DisplaySpec>> categoryMap, @Nullable CategoryIdentifier<?> category) { super(Component.empty()); this.categoryMap = categoryMap; this.categories = Lists.newArrayList(categoryMap.keySet()); - this.tabsPerPage = tabsPerPage; if (category != null) { selectCategory(category, false); } @@ -105,6 +107,7 @@ public abstract class AbstractDisplayViewingScreen extends Screen implements Dis } recalculateCategoryPage(); + this.tabs.updateScroll(categories, selectedCategoryIndex, !init ? 0 : 300); if (init) { init(); @@ -112,6 +115,35 @@ public abstract class AbstractDisplayViewingScreen extends Screen implements Dis } @Override + public void recalculateCategoryPage() { + this.categoryPages = -1; + } + + protected void initTabs() { + this.tabs.init(new Rectangle(bounds.x, bounds.y - 28, bounds.width, 28), categories, new IntValue() { + @Override + public void accept(int value) { + AbstractDisplayViewingScreen.this.categoryPages = value; + } + + @Override + public int getAsInt() { + return AbstractDisplayViewingScreen.this.categoryPages; + } + }, new IntValue() { + @Override + public void accept(int value) { + AbstractDisplayViewingScreen.this.selectCategory(categories.get(value).getCategoryIdentifier()); + } + + @Override + public int getAsInt() { + return selectedCategoryIndex; + } + }, AbstractDisplayViewingScreen.this::init); + } + + @Override public List<GuiEventListener> children() { List<? extends GuiEventListener> children = super.children(); children.sort(Comparator.comparingDouble(value -> value instanceof Widget widget ? widget.getZRenderingPriority() : 0).reversed()); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/CompositeDisplayViewingScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/CompositeDisplayViewingScreen.java index e6c9d9348..050b91e35 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/CompositeDisplayViewingScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/CompositeDisplayViewingScreen.java @@ -44,16 +44,12 @@ import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.display.Display; import me.shedaniel.rei.api.common.entry.EntryIngredient; import me.shedaniel.rei.impl.client.REIRuntimeImpl; -import me.shedaniel.rei.impl.client.gui.InternalTextures; import me.shedaniel.rei.impl.client.gui.widget.EntryWidget; import me.shedaniel.rei.impl.client.gui.widget.InternalWidgets; -import me.shedaniel.rei.impl.client.gui.widget.TabWidget; import me.shedaniel.rei.impl.display.DisplaySpec; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.network.chat.Component; -import net.minecraft.sounds.SoundEvents; import net.minecraft.util.Mth; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -68,7 +64,6 @@ public class CompositeDisplayViewingScreen extends AbstractDisplayViewingScreen private final List<Widget> widgets = Lists.newArrayList(); private final List<Button> buttonList = Lists.newArrayList(); private final List<DisplayRenderer> displayRenderers = Lists.newArrayList(); - private final List<TabWidget> tabs = Lists.newArrayList(); public Rectangle scrollListBounds; private int selectedRecipeIndex = 0; private final ScrollingContainer scrolling = new ScrollingContainer() { @@ -89,15 +84,14 @@ public class CompositeDisplayViewingScreen extends AbstractDisplayViewingScreen private float scrollBarAlpha = 0; private float scrollBarAlphaFuture = 0; private long scrollBarAlphaFutureTime = -1; - private int tabsPage = -1; public CompositeDisplayViewingScreen(Map<DisplayCategory<?>, List<DisplaySpec>> categoryMap, @Nullable CategoryIdentifier<?> category) { - super(categoryMap, category, 8); + super(categoryMap, category); } @Override public void recalculateCategoryPage() { - this.tabsPage = -1; + super.recalculateCategoryPage(); this.selectedRecipeIndex = 0; } @@ -112,19 +106,17 @@ public class CompositeDisplayViewingScreen extends AbstractDisplayViewingScreen this.widgets.clear(); this.buttonList.clear(); this.displayRenderers.clear(); - this.tabs.clear(); int largestWidth = width - 100; int largestHeight = height - 40; DisplayCategory<Display> category = getCurrentCategory(); DisplaySpec display = categoryMap.get(category).get(selectedRecipeIndex); int guiWidth = Mth.clamp(category.getDisplayWidth(display.provideInternalDisplay()) + 30, 0, largestWidth) + 100; int guiHeight = Mth.clamp(category.getDisplayHeight() + 40, 166, largestHeight); - this.tabsPerPage = Math.max(5, Mth.floor((guiWidth - tabButtonsSize * 2d) / tabSize)); - if (this.tabsPage == -1) { - this.tabsPage = selectedCategoryIndex / tabsPerPage; - } this.bounds = new Rectangle(width / 2 - guiWidth / 2, height / 2 - guiHeight / 2, guiWidth, guiHeight); + this.initTabs(); + this.widgets.addAll(this.tabs.widgets()); + List<EntryIngredient> workstations = CategoryRegistry.getInstance().get(category.getCategoryIdentifier()).getWorkstations(); if (!workstations.isEmpty()) { int ww = Mth.floor((bounds.width - 16) / 18f); @@ -190,66 +182,12 @@ public class CompositeDisplayViewingScreen extends AbstractDisplayViewingScreen .onRender((matrices, button) -> button.setEnabled(selectedRecipeIndex != finalIndex))); index++; } - int tabV = isCompactTabs ? 166 : 192; - for (int i = 0; i < tabsPerPage; i++) { - int j = i + tabsPage * tabsPerPage; - if (categories.size() > j) { - DisplayCategory<?> tabCategory = categories.get(j); - TabWidget tab; - tabs.add(tab = TabWidget.create(i, tabSize, bounds.x + bounds.width / 2 - Math.min(categories.size() - tabsPage * tabsPerPage, tabsPerPage) * tabSize / 2, bounds.y, 0, tabV, widget -> { - Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); - if (widget.selected) - return false; - selectCategory(tabCategory.getCategoryIdentifier()); - return true; - })); - tab.setRenderer(tabCategory, tabCategory.getIcon(), tabCategory.getTitle(), j == selectedCategoryIndex); - } - } - Button tabLeft, tabRight; - this.widgets.add(tabLeft = Widgets.createButton(new Rectangle(bounds.x + 2, bounds.y - (isCompactTabButtons ? 16 : 20), tabButtonsSize, tabButtonsSize), Component.literal("")) - .onClick(button -> { - tabsPage--; - if (tabsPage < 0) - tabsPage = Mth.ceil(categories.size() / (float) tabsPerPage) - 1; - CompositeDisplayViewingScreen.this.init(); - }) - .tooltipLine(Component.translatable("text.rei.previous_page")) - .enabled(categories.size() > tabsPerPage)); - this.widgets.add(tabRight = Widgets.createButton(new Rectangle(bounds.x + bounds.width - (isCompactTabButtons ? tabButtonsSize + 2 : tabButtonsSize + 3), bounds.y - (isCompactTabButtons ? 16 : 20), tabButtonsSize, tabButtonsSize), Component.literal("")) - .onClick(button -> { - tabsPage++; - if (tabsPage > Mth.ceil(categories.size() / (float) tabsPerPage) - 1) - tabsPage = 0; - CompositeDisplayViewingScreen.this.init(); - }) - .tooltipLine(Component.translatable("text.rei.next_page")) - .enabled(categories.size() > tabsPerPage)); - this.widgets.add(Widgets.withTranslate(Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> { - Rectangle tabLeftBounds = tabLeft.getBounds(); - Rectangle tabRightBounds = tabRight.getBounds(); - if (isCompactTabButtons) { - matrices.pushPose(); - matrices.translate(0, 0.5, 0); - RenderSystem.setShaderTexture(0, InternalTextures.ARROW_LEFT_SMALL_TEXTURE); - blit(matrices, tabLeftBounds.x + 2, tabLeftBounds.y + 2, 0, 0, 6, 6, 6, 6); - RenderSystem.setShaderTexture(0, InternalTextures.ARROW_RIGHT_SMALL_TEXTURE); - blit(matrices, tabRightBounds.x + 2, tabRightBounds.y + 2, 0, 0, 6, 6, 6, 6); - matrices.popPose(); - } else { - RenderSystem.setShaderTexture(0, InternalTextures.ARROW_LEFT_TEXTURE); - blit(matrices, tabLeftBounds.x + 4, tabLeftBounds.y + 4, 0, 0, 8, 8, 8, 8); - RenderSystem.setShaderTexture(0, InternalTextures.ARROW_RIGHT_TEXTURE); - blit(matrices, tabRightBounds.x + 4, tabRightBounds.y + 4, 0, 0, 8, 8, 8, 8); - } - }), 0, 0, 1)); this.widgets.add(Widgets.createClickableLabel(new Point(bounds.x + 4 + scrollListBounds.width / 2, bounds.y + 6), categories.get(selectedCategoryIndex).getTitle(), label -> { ViewSearchBuilder.builder().addAllCategories().open(); }).tooltip(Component.translatable("text.rei.view_all_categories")).noShadow().color(0xFF404040, 0xFFBBBBBB).hoveredColor(0xFF0041FF, 0xFFFFBD4D)); this.children().addAll(buttonList); - this.widgets.addAll(tabs); this.children().addAll(widgets); } @@ -316,15 +254,6 @@ public class CompositeDisplayViewingScreen extends AbstractDisplayViewingScreen } } REIRuntimeImpl.isWithinRecipeViewingScreen = false; - int tabSize = ConfigObject.getInstance().isUsingCompactTabs() ? 24 : 28; - if (mouseX >= bounds.x && mouseX <= bounds.getMaxX() && mouseY >= bounds.y - tabSize && mouseY < bounds.y) { - if (amount < 0) selectedCategoryIndex++; - else if (amount > 0) selectedCategoryIndex--; - if (selectedCategoryIndex < 0) selectedCategoryIndex = categories.size() - 1; - else if (selectedCategoryIndex >= categories.size()) selectedCategoryIndex = 0; - selectCategory(categories.get(selectedCategoryIndex).getCategoryIdentifier()); - return true; - } if (bounds.contains(PointHelper.ofMouse())) { if (amount < 0 && categoryMap.get(categories.get(selectedCategoryIndex)).size() > 1) { selectedRecipeIndex++; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/DefaultDisplayViewingScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/DefaultDisplayViewingScreen.java index 5817338c3..37bd9d0f0 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/DefaultDisplayViewingScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/DefaultDisplayViewingScreen.java @@ -66,9 +66,7 @@ import net.minecraft.client.gui.screens.ConfirmScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.resources.language.I18n; -import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.network.chat.Component; -import net.minecraft.sounds.SoundEvents; import net.minecraft.util.Mth; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -81,13 +79,12 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { private final Map<Rectangle, Pair<DisplaySpec, List<Widget>>> recipeBounds = Maps.newHashMap(); private List<Widget> widgets = Lists.newArrayList(); public int page; - public int categoryPages = -1; @Nullable private Panel workingStationsBaseWidget; private Button recipeBack, recipeNext, categoryBack, categoryNext; public DefaultDisplayViewingScreen(Map<DisplayCategory<?>, List<DisplaySpec>> categoriesMap, @Nullable CategoryIdentifier<?> category) { - super(categoriesMap, category, 5); + super(categoriesMap, category); this.bounds = new Rectangle(0, 0, 176, 150); } @@ -141,37 +138,12 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { int guiWidth = Math.max(maxWidthDisplay + 10 + 14 + 14, 190); this.bounds = new Rectangle(width / 2 - guiWidth / 2, height / 2 - maxHeight / 2, guiWidth, maxHeight); - boolean isCompactTabs = ConfigObject.getInstance().isUsingCompactTabs(); - boolean isCompactTabButtons = ConfigObject.getInstance().isUsingCompactTabButtons(); - int tabButtonsSize = isCompactTabButtons ? 10 : 16; - int tabSize = isCompactTabs ? 24 : 28; - this.tabsPerPage = Math.max(5, Mth.floor((guiWidth - tabButtonsSize * 2d) / tabSize)); - if (this.categoryPages == -1) { - this.categoryPages = Math.max(0, selectedCategoryIndex / tabsPerPage); - } + this.initTabs(); + this.widgets.addAll(this.tabs.widgets()); this.page = Mth.clamp(page, 0, getCurrentTotalPages() - 1); - Button tabLeft, tabRight; - this.widgets.add(tabLeft = Widgets.createButton(new Rectangle(bounds.x, bounds.y - (isCompactTabButtons ? 16 : 20), tabButtonsSize, tabButtonsSize), Component.literal("")) - .onClick(button -> { - categoryPages--; - if (categoryPages < 0) - categoryPages = Mth.ceil(categories.size() / (float) tabsPerPage) - 1; - DefaultDisplayViewingScreen.this.init(); - }) - .tooltipLine(Component.translatable("text.rei.previous_page")) - .enabled(categories.size() > tabsPerPage)); - this.widgets.add(tabRight = Widgets.createButton(new Rectangle(bounds.x + bounds.width - tabButtonsSize - (isCompactTabButtons ? 0 : 1), bounds.y - (isCompactTabButtons ? 16 : 20), tabButtonsSize, tabButtonsSize), Component.literal("")) - .onClick(button -> { - categoryPages++; - if (categoryPages > Mth.ceil(categories.size() / (float) tabsPerPage) - 1) - categoryPages = 0; - DefaultDisplayViewingScreen.this.init(); - }) - .tooltipLine(Component.translatable("text.rei.next_page")) - .enabled(categories.size() > tabsPerPage)); - this.widgets.add(categoryBack = Widgets.createButton(new Rectangle(bounds.getX() + 5, bounds.getY() + 5, 12, 12), Component.literal("")) - .onClick(button -> previousCategory()).tooltipLine(Component.translatable("text.rei.previous_category"))); + this.widgets.add(categoryBack = Widgets.createButton(new Rectangle(bounds.getX() + 5, bounds.getY() + 5, 12, 12), ImmutableTextComponent.EMPTY) + .onClick(button -> previousCategory()).tooltipLine(new TranslatableComponent("text.rei.previous_category"))); this.widgets.add(Widgets.createClickableLabel(new Point(bounds.getCenterX(), bounds.getY() + 7), getCurrentCategory().getTitle(), clickableLabelWidget -> { ViewSearchBuilder.builder().addAllCategories().open(); }).tooltip(Component.translatable("text.rei.view_all_categories"))); @@ -180,22 +152,6 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { this.categoryBack.setEnabled(categories.size() > 1); this.categoryNext.setEnabled(categories.size() > 1); this.widgets.add(Widgets.withTranslate(Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> { - Rectangle tabLeftBounds = tabLeft.getBounds(); - Rectangle tabRightBounds = tabRight.getBounds(); - if (isCompactTabButtons) { - matrices.pushPose(); - matrices.translate(0, 0.5, 0); - RenderSystem.setShaderTexture(0, InternalTextures.ARROW_LEFT_SMALL_TEXTURE); - blit(matrices, tabLeftBounds.x + 2, tabLeftBounds.y + 2, 0, 0, 6, 6, 6, 6); - RenderSystem.setShaderTexture(0, InternalTextures.ARROW_RIGHT_SMALL_TEXTURE); - blit(matrices, tabRightBounds.x + 2, tabRightBounds.y + 2, 0, 0, 6, 6, 6, 6); - matrices.popPose(); - } else { - RenderSystem.setShaderTexture(0, InternalTextures.ARROW_LEFT_TEXTURE); - blit(matrices, tabLeftBounds.x + 4, tabLeftBounds.y + 4, 0, 0, 8, 8, 8, 8); - RenderSystem.setShaderTexture(0, InternalTextures.ARROW_RIGHT_TEXTURE); - blit(matrices, tabRightBounds.x + 4, tabRightBounds.y + 4, 0, 0, 8, 8, 8, 8); - } Rectangle recipeBackBounds = recipeBack.getBounds(); Rectangle recipeNextBounds = recipeNext.getBounds(); Rectangle categoryBackBounds = categoryBack.getBounds(); @@ -242,20 +198,6 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { }).tooltipLine(Component.translatable("text.rei.next_page"))); this.recipeBack.setEnabled(getCurrentTotalPages() > 1); this.recipeNext.setEnabled(getCurrentTotalPages() > 1); - for (int id = 0; id < tabsPerPage; id++) { - int tabIndex = id + categoryPages * tabsPerPage; - if (categories.size() > tabIndex) { - TabWidget tab; - widgets.add(tab = TabWidget.create(id, tabSize, bounds.x + bounds.width / 2 - Math.min(categories.size() - categoryPages * tabsPerPage, tabsPerPage) * tabSize / 2, bounds.y, 0, isCompactTabs ? 166 : 192, widget -> { - Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); - if (widget.getId() + categoryPages * tabsPerPage == selectedCategoryIndex) - return false; - selectCategory(categories.get(widget.getId() + categoryPages * tabsPerPage).getCategoryIdentifier()); - return true; - })); - tab.setRenderer(categories.get(tabIndex), categories.get(tabIndex).getIcon(), categories.get(tabIndex).getTitle(), tab.getId() + categoryPages * tabsPerPage == selectedCategoryIndex); - } - } initDisplays(); widgets = CollectionUtils.map(widgets, widget -> Widgets.withTranslate(widget, 0, 0, 10)); widgets.add(Widgets.withTranslate(new PanelWidget(bounds), 0, 0, 5)); @@ -514,12 +456,6 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { else if (amount < 0 && recipeNext.isEnabled()) recipeNext.onClick(); } - if ((new Rectangle(bounds.x, bounds.y - 28, bounds.width, 28)).contains(PointHelper.ofMouse())) { - if (amount > 0 && categoryBack.isEnabled()) - categoryBack.onClick(); - else if (amount < 0 && categoryNext.isEnabled()) - categoryNext.onClick(); - } return super.mouseScrolled(mouseX, mouseY, amount); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/TabContainerWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/TabContainerWidget.java new file mode 100644 index 000000000..b204b73ec --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/TabContainerWidget.java @@ -0,0 +1,179 @@ +package me.shedaniel.rei.impl.client.gui.widget; + +import com.mojang.blaze3d.vertex.PoseStack; +import dev.architectury.utils.value.IntValue; +import me.shedaniel.clothconfig2.api.animator.NumberAnimator; +import me.shedaniel.clothconfig2.api.animator.ValueAnimator; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.config.ConfigObject; +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.Widgets; +import me.shedaniel.rei.api.client.registry.display.DisplayCategory; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.util.Mth; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Stream; + +public class TabContainerWidget extends GuiComponent { + private final Rectangle bounds = new Rectangle(); + private final List<Widget> widgets = new ArrayList<>(); + private final NumberAnimator<Double> scrollAnimator = ValueAnimator.ofDouble(); + private int tabsPerPage = 5; + + public TabContainerWidget() { + } + + public void setBounds(Rectangle bounds) { + this.bounds.setBounds(bounds); + } + + public void updateScroll(List<DisplayCategory<?>> categories, int selectedCategory, long duration) { + boolean isCompactTabs = ConfigObject.getInstance().isUsingCompactTabs(); + int tabSize = isCompactTabs ? 24 : 28; + + double curr = scrollAnimator.doubleValue() % (tabSize * categories.size()); + double newValue1 = selectedCategory * tabSize + Math.floor(scrollAnimator.doubleValue() / (tabSize * categories.size())) * (tabSize * categories.size()); + double newValue2 = newValue1 - (tabSize * categories.size()); + double newValue3 = newValue1 + (tabSize * categories.size()); + + Stream.of(newValue1, newValue2, newValue3).min(Comparator.comparingDouble(value -> Math.abs(value - scrollAnimator.doubleValue()))).ifPresent(newValue -> { + scrollAnimator.setTo(newValue, duration); + }); + } + + public void init(Rectangle bounds, List<DisplayCategory<?>> categories, IntValue categoryPages, IntValue selectedCategory, Runnable reInit) { + this.setBounds(bounds); + this.widgets.clear(); + + boolean isCompactTabs = ConfigObject.getInstance().isUsingCompactTabs(); + int tabSize = isCompactTabs ? 24 : 28; + this.tabsPerPage = Mth.floor((this.bounds.getWidth() - 10) / tabSize); + + if (categoryPages.getAsInt() == -1) { + categoryPages.accept(Math.max(0, selectedCategory.getAsInt() / tabsPerPage())); + } + + this.widgets.add(new Widget() { + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + scrollAnimator.update(delta); + int absLeft = bounds.x + bounds.width / 2 - tabsPerPage() * tabSize / 2; + int left; + if (categories.size() > tabsPerPage()) { + left = bounds.x + bounds.width / 2 - tabSize / 2 - (int) Math.round(scrollAnimator.doubleValue()); + updateScroll(categories, selectedCategory.getAsInt(), 300); + } else { + left = bounds.x + bounds.width / 2 - categories.size() * tabSize / 2; + } + int passed = 0; + for (TabWidget tab : Widgets.<TabWidget>walk(TabContainerWidget.this.widgets(), widget -> widget instanceof TabWidget)) { + if (categories.size() <= tabsPerPage()) { + tab.getBounds().x = left; + left += tabSize; + } else { + if (left > bounds.getMaxX()) { + while (left > bounds.getMaxX()) { + left -= tabSize * categories.size(); + } + tab.getBounds().x = left; + left += tabSize; + } else if (left + tabSize < bounds.x) { + while (left + tabSize < bounds.x) { + left += tabSize * categories.size(); + } + tab.getBounds().x = left; + left += tabSize; + } else { + tab.getBounds().x = left; + left += tabSize; + } + } + passed++; + + if (tab.getBounds().x > bounds.getMaxX() || tab.getBounds().getMaxX() < bounds.x) { + tab.getBounds().x = -1000; + } + + if (tab.getBounds().getCenterX() <= absLeft + 20) { + tab.opacity = 1 - (absLeft + 20 - tab.getBounds().getCenterX()) / 20f; + tab.opacity = (float) Math.pow(Mth.clamp(tab.opacity, 0, 1), 0.9); + } else if (tab.getBounds().getCenterX() >= absLeft + tabsPerPage() * tabSize - 20) { + tab.opacity = 1 - (tab.getBounds().getCenterX() - (absLeft + tabsPerPage() * tabSize - 20)) / 20f; + tab.opacity = (float) Math.pow(Mth.clamp(tab.opacity, 0, 1), 0.9); + } else { + tab.opacity = 1; + } + + if (tab.opacity < 0.1) { + tab.opacity = 0; + } + } + } + + @Override + public double getZRenderingPriority() { + return -1000; + } + + @Override + public List<? extends GuiEventListener> children() { + return List.of(); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (TabContainerWidget.this.bounds.contains(mouseX, mouseY) && categories.size() > 1) { + int currentCategoryIndex = selectedCategory.getAsInt(); + if (amount > 0) { + currentCategoryIndex--; + if (currentCategoryIndex < 0) + currentCategoryIndex = categories.size() - 1; + } else { + currentCategoryIndex++; + if (currentCategoryIndex >= categories.size()) + currentCategoryIndex = 0; + } + selectedCategory.accept(currentCategoryIndex); + + return true; + } + return false; + } + }); + + for (int id = 0; id < categories.size(); id++) { + int tabIndex = id; + DisplayCategory<?> category = categories.get(tabIndex); + TabWidget tab = TabWidget.create(id, tabSize, bounds.x + bounds.width / 2 - tabsPerPage * tabSize / 2, bounds.getMaxY(), 0, isCompactTabs ? 166 : 192, widget -> { + Widgets.produceClickSound(); + if (tabIndex == selectedCategory.getAsInt()) + return false; + selectedCategory.accept(tabIndex); + return true; + }); + tab.setRenderer(category, category.getIcon(), category.getTitle(), tabIndex == selectedCategory.getAsInt()); + this.widgets.add(new DelegateWidget(tab) { + @Override + public void render(PoseStack poseStack, int mouseX, int mouseY, float delta) { + try (CloseableScissors scissors = Widget.scissor(poseStack, new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height + 4))) { + super.render(poseStack, mouseX, mouseY, delta); + } + } + }); + } + } + + public List<Widget> widgets() { + return this.widgets; + } + + public int tabsPerPage() { + return tabsPerPage; + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/TabWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/TabWidget.java index a1e2c0604..071ad8e2e 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/TabWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/TabWidget.java @@ -36,6 +36,7 @@ import me.shedaniel.rei.api.client.gui.drag.DraggableStack; import me.shedaniel.rei.api.client.gui.drag.DraggableStackProviderWidget; import me.shedaniel.rei.api.client.gui.drag.DraggedAcceptorResult; import me.shedaniel.rei.api.client.gui.drag.DraggingContext; +import me.shedaniel.rei.api.client.gui.widgets.CloseableScissors; import me.shedaniel.rei.api.client.gui.widgets.Tooltip; import me.shedaniel.rei.api.client.gui.widgets.Widget; import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; @@ -56,19 +57,18 @@ import java.util.function.Predicate; public class TabWidget extends WidgetWithBounds implements DraggableStackProviderWidget { public boolean selected = false; public Renderer renderer; - public int id; public Component categoryName; public Rectangle bounds; public DisplayCategory<?> category; public int u, v; + public float opacity = 1.0F; @Nullable private Predicate<TabWidget> onClick; private final NumberAnimator<Float> darkBackgroundAlpha = ValueAnimator.ofFloat() .withConvention(() -> REIRuntime.getInstance().isDarkThemeEnabled() ? 1.0F : 0.0F, ValueAnimator.typicalTransitionTime()) .asFloat(); - private TabWidget(int id, Rectangle bounds, int u, int v, @Nullable Predicate<TabWidget> onClick) { - this.id = id; + private TabWidget(Rectangle bounds, int u, int v, @Nullable Predicate<TabWidget> onClick) { this.bounds = bounds; this.u = u; this.v = v; @@ -77,7 +77,7 @@ public class TabWidget extends WidgetWithBounds implements DraggableStackProvide @ApiStatus.Internal public static TabWidget create(int id, int tabSize, int leftX, int bottomY, int u, int v, @Nullable Predicate<TabWidget> onClick) { - return new TabWidget(id, new Rectangle(leftX + id * tabSize, bottomY - tabSize, tabSize, tabSize), u, v, onClick); + return new TabWidget(new Rectangle(leftX + id * tabSize, bottomY - tabSize, tabSize, tabSize), u, v, onClick); } @Override @@ -96,10 +96,6 @@ public class TabWidget extends WidgetWithBounds implements DraggableStackProvide return selected; } - public int getId() { - return id; - } - public boolean isShown() { return renderer != null; } @@ -111,20 +107,23 @@ public class TabWidget extends WidgetWithBounds implements DraggableStackProvide @Override public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + if (bounds.getMaxX() < 0 || opacity == 0) return; if (renderer != null) { - darkBackgroundAlpha.update(delta); - RenderSystem.enableBlend(); - RenderSystem.blendFuncSeparate(770, 771, 1, 0); - RenderSystem.blendFunc(770, 771); - RenderSystem.setShaderTexture(0, InternalTextures.CHEST_GUI_TEXTURE); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - this.blit(matrices, bounds.x, bounds.y + 2, u + (selected ? bounds.width : 0), v, bounds.width, (selected ? bounds.height + 2 : bounds.height - 1)); - RenderSystem.setShaderTexture(0, InternalTextures.CHEST_GUI_TEXTURE_DARK); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, darkBackgroundAlpha.value()); - this.blit(matrices, bounds.x, bounds.y + 2, u + (selected ? bounds.width : 0), v, bounds.width, (selected ? bounds.height + 2 : bounds.height - 1)); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - renderer.setZ(100); - renderer.render(matrices, new Rectangle(bounds.getCenterX() - 8, bounds.getCenterY() - 5, 16, 16), mouseX, mouseY, delta); + try (CloseableScissors scissors = Widget.scissor(matrices, new Rectangle(bounds.x, bounds.y + 2, bounds.width, (selected ? bounds.height + 2 : bounds.height - 2)))) { + darkBackgroundAlpha.update(delta); + RenderSystem.enableBlend(); + RenderSystem.blendFuncSeparate(770, 771, 1, 0); + RenderSystem.blendFunc(770, 771); + RenderSystem.setShaderTexture(0, InternalTextures.CHEST_GUI_TEXTURE); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, opacity); + this.blit(matrices, bounds.x, bounds.y + 2, u + (selected ? bounds.width : 0), v, bounds.width, (selected ? bounds.height + 2 : bounds.height - 2)); + RenderSystem.setShaderTexture(0, InternalTextures.CHEST_GUI_TEXTURE_DARK); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, darkBackgroundAlpha.value() * opacity); + this.blit(matrices, bounds.x, bounds.y + 2, u + (selected ? bounds.width : 0), v, bounds.width, (selected ? bounds.height + 2 : bounds.height - 2)); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, opacity); + renderer.setZ(100); + renderer.render(matrices, new Rectangle(bounds.getCenterX() - 8, bounds.getCenterY() - 5, 16, 16), mouseX, mouseY, delta); + } if (containsMouse(mouseX, mouseY)) { drawTooltip(); } |
