diff --git a/gradle.properties b/gradle.properties
index 5c05bce0..5a806839 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -7,11 +7,11 @@ minecraft_version=1.20.1
yarn_mappings=1.20.1+build.2
loader_version=0.14.21
# Mod Properties
-mod_version=0.11.2-pre.1
+mod_version=0.11.2-pre.5
maven_group=io.wispforest
archives_base_name=owo-lib
# Dependencies
-fabric_version=0.83.1+1.20.1
+fabric_version=0.85.0+1.20.1
# https://maven.shedaniel.me/me/shedaniel/RoughlyEnoughItems-fabric/
rei_version=12.0.625
diff --git a/src/main/java/io/wispforest/owo/itemgroup/Icon.java b/src/main/java/io/wispforest/owo/itemgroup/Icon.java
index a20b1a09..0a06f30a 100644
--- a/src/main/java/io/wispforest/owo/itemgroup/Icon.java
+++ b/src/main/java/io/wispforest/owo/itemgroup/Icon.java
@@ -6,19 +6,21 @@
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
-import org.jetbrains.annotations.ApiStatus;
/**
* An icon used for rendering on buttons in {@link OwoItemGroup}s
*
- * Default implementations provided for textures and itemstacks
+ * Default implementations provided for textures and item stacks
*/
+@FunctionalInterface
public interface Icon {
void render(DrawContext context, int x, int y, int mouseX, int mouseY, float delta);
static Icon of(ItemStack stack) {
- return new ItemIcon(stack);
+ return (context, x, y, mouseX, mouseY, delta) -> {
+ context.drawItemWithoutEntity(stack, x, y);
+ };
}
static Icon of(ItemConvertible item) {
@@ -26,7 +28,9 @@ static Icon of(ItemConvertible item) {
}
static Icon of(Identifier texture, int u, int v, int textureWidth, int textureHeight) {
- return new TextureIcon(texture, u, v, textureWidth, textureHeight);
+ return (context, x, y, mouseX, mouseY, delta) -> {
+ context.drawTexture(texture, x, y, u, v, 16, 16, textureWidth, textureHeight);
+ };
}
/**
@@ -39,66 +43,9 @@ static Icon of(Identifier texture, int u, int v, int textureWidth, int textureHe
* @return The created icon instance
*/
static Icon of(Identifier texture, int textureSize, int frameDelay, boolean loop) {
- return new AnimatedTextureIcon(texture, new SpriteSheetMetadata(textureSize, 16), frameDelay, loop);
- }
-
- /**
- * Renders an {@link ItemStack}
- */
- @ApiStatus.Internal
- class ItemIcon implements Icon {
-
- private final ItemStack stack;
-
- private ItemIcon(ItemStack stack) {
- this.stack = stack;
- }
-
- @Override
- public void render(DrawContext context, int x, int y, int mouseX, int mouseY, float delta) {
- context.drawItemWithoutEntity(stack, x, y);
- }
- }
-
- /**
- * Renders a 16x16 region of the given texture, starting at (u, v)
- */
- @ApiStatus.Internal
- class TextureIcon implements Icon {
-
- private final Identifier texture;
- private final int u, v;
- private final int textureWidth, textureHeight;
-
- public TextureIcon(Identifier texture, int u, int v, int textureWidth, int textureHeight) {
- this.texture = texture;
- this.u = u;
- this.v = v;
- this.textureWidth = textureWidth;
- this.textureHeight = textureHeight;
- }
-
- @Override
- public void render(DrawContext context, int x, int y, int mouseX, int mouseY, float delta) {
- context.drawTexture(texture, x, y, u, v, 16, 16, textureWidth, textureHeight);
- }
- }
-
- /**
- * Similar to TextureIcon but allows 16x16 frame animated textures.
- */
- @ApiStatus.Internal
- class AnimatedTextureIcon implements Icon {
- private final AnimatedTextureDrawable widget;
-
- public AnimatedTextureIcon(Identifier texture, SpriteSheetMetadata spriteSheetMetadata, int frameDelay, boolean loop) {
- this.widget = new AnimatedTextureDrawable(0, 0, 16, 16, texture, spriteSheetMetadata, frameDelay, loop);
- }
-
- @Override
- public void render(DrawContext context, int x, int y, int mouseX, int mouseY, float delta) {
+ var widget = new AnimatedTextureDrawable(0, 0, 16, 16, texture, new SpriteSheetMetadata(textureSize, 16), frameDelay, loop);
+ return (context, x, y, mouseX, mouseY, delta) -> {
widget.render(x, y, context, mouseX, mouseY, delta);
- }
+ };
}
-
}
diff --git a/src/main/java/io/wispforest/owo/itemgroup/OwoItemGroup.java b/src/main/java/io/wispforest/owo/itemgroup/OwoItemGroup.java
index 721a820d..b3db6e59 100644
--- a/src/main/java/io/wispforest/owo/itemgroup/OwoItemGroup.java
+++ b/src/main/java/io/wispforest/owo/itemgroup/OwoItemGroup.java
@@ -5,6 +5,9 @@
import io.wispforest.owo.itemgroup.gui.ItemGroupTab;
import io.wispforest.owo.mixin.itemgroup.ItemGroupAccessor;
import io.wispforest.owo.util.pond.OwoItemExtensions;
+import it.unimi.dsi.fastutil.ints.IntArraySet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+import it.unimi.dsi.fastutil.ints.IntSets;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.item.*;
@@ -23,15 +26,15 @@
import java.util.function.Supplier;
/**
- * A custom implementation of {@link ItemGroup} that supports multiple sub-tabs
- * within itself, as well as arbitrary buttons with defaults provided for links
- * like GitHub, Modrinth, etc.
+ * Extensions for {@link ItemGroup} which support multiple sub-tabs
+ * within, as well as arbitrary buttons with defaults provided for links
+ * to places like GitHub, Modrinth, etc.
*
- * By default, Items are added via tags, however you can also use {@link OwoItemSettings}
- * and set the tab for a given item via {@link OwoItemSettings#tab(int)}
+ * Tabs can be populated by using {@link OwoItemSettings} and setting the
+ * {@link OwoItemSettings#tab(int)}. Furthermore, tags can be used for easily populating
+ * tabs from data
*
- * Credits to Lemonszz for originally writing this for Biome Makeover.
- * Adapted from Azagwens implementation
+ * This concept originated in Biome Makeover, where it was written by Lemonszz
*/
public abstract class OwoItemGroup extends ItemGroup {
@@ -42,13 +45,13 @@ public abstract class OwoItemGroup extends ItemGroup {
public final List tabs = new ArrayList<>();
public final List buttons = new ArrayList<>();
- private final Identifier id;
private final Consumer initializer;
private final Supplier iconSupplier;
private Icon icon;
- private int selectedTab = 0;
+ private final IntSet activeTabs = new IntArraySet();
+ private final IntSet activeTabsView = IntSets.unmodifiable(this.activeTabs);
private boolean initialized = false;
private final int tabStackHeight;
@@ -56,10 +59,10 @@ public abstract class OwoItemGroup extends ItemGroup {
private final Identifier customTexture;
private final boolean useDynamicTitle;
private final boolean displaySingleTab;
+ private final boolean allowMultiSelect;
- protected OwoItemGroup(Identifier id, Consumer initializer, Supplier iconSupplier, int tabStackHeight, int buttonStackHeight, @Nullable Identifier customTexture, boolean useDynamicTitle, boolean displaySingleTab) {
+ protected OwoItemGroup(Identifier id, Consumer initializer, Supplier iconSupplier, int tabStackHeight, int buttonStackHeight, @Nullable Identifier customTexture, boolean useDynamicTitle, boolean displaySingleTab, boolean allowMultiSelect) {
super(null, -1, Type.CATEGORY, Text.translatable("itemGroup.%s.%s".formatted(id.getNamespace(), id.getPath())), () -> ItemStack.EMPTY, (displayContext, entries) -> {});
- this.id = id;
this.initializer = initializer;
this.iconSupplier = iconSupplier;
this.tabStackHeight = tabStackHeight;
@@ -67,13 +70,17 @@ protected OwoItemGroup(Identifier id, Consumer initializer, Suppli
this.customTexture = customTexture;
this.useDynamicTitle = useDynamicTitle;
this.displaySingleTab = displaySingleTab;
+ this.allowMultiSelect = allowMultiSelect;
((ItemGroupAccessor) this).owo$setEntryCollector((context, entries) -> {
if (!this.initialized) {
throw new IllegalStateException("oωo item group not initialized, was 'initialize()' called?");
}
- this.getSelectedTab().contentSupplier().addItems(context, entries);
- this.collectItemsFromRegistry(entries, true);
+
+ this.activeTabs.forEach(tabIdx -> {
+ this.tabs.get(tabIdx).contentSupplier().addItems(context, entries);
+ this.collectItemsFromRegistry(entries, tabIdx);
+ });
});
}
@@ -95,6 +102,15 @@ public void initialize() {
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) this.initializer.accept(this);
if (tabs.size() == 0) this.tabs.add(PLACEHOLDER_TAB);
+ if (this.allowMultiSelect) {
+ for (int tabIdx = 0; tabIdx < this.tabs.size(); tabIdx++) {
+ if (!this.tabs.get(tabIdx).primary()) continue;
+ this.activeTabs.add(tabIdx);
+ }
+ } else {
+ this.activeTabs.add(0);
+ }
+
this.initialized = true;
}
@@ -179,31 +195,117 @@ public void updateEntries(DisplayContext context) {
var searchEntries = new SearchOnlyEntries(this, context.enabledFeatures());
- this.collectItemsFromRegistry(searchEntries, false);
+ this.collectItemsFromRegistry(searchEntries, -1);
this.tabs.forEach(tab -> tab.contentSupplier().addItems(context, searchEntries));
((ItemGroupAccessor) this).owo$setSearchTabStacks(searchEntries.searchTabStacks);
}
- protected void collectItemsFromRegistry(Entries entries, boolean matchTab) {
+ protected void collectItemsFromRegistry(Entries entries, int tab) {
Registries.ITEM.stream()
- .filter(item -> ((OwoItemExtensions) item).owo$group() == this && (!matchTab || ((OwoItemExtensions) item).owo$tab() == this.selectedTab))
+ .filter(item -> ((OwoItemExtensions) item).owo$group() == this && (tab < 0 || tab == ((OwoItemExtensions) item).owo$tab()))
.forEach(item -> ((OwoItemExtensions) item).owo$stackGenerator().accept(item, entries));
}
// Getters and setters
+ /**
+ * @deprecated Use {@link #selectSingleTab(int, DisplayContext)} instead
+ */
+ @Deprecated(forRemoval = true)
public void setSelectedTab(int selectedTab, DisplayContext context) {
- this.selectedTab = selectedTab;
- this.updateEntries(context);
+ this.selectSingleTab(selectedTab, context);
}
+ /**
+ * @deprecated On an item group which allows multiple selection, this
+ * returns the first selected tab only. Instead, call {@link #selectedTabs()}
+ */
+ @Deprecated(forRemoval = true)
public ItemGroupTab getSelectedTab() {
- return tabs.get(selectedTab);
+ return this.tabs.get(this.getSelectedTabIndex());
}
+ /**
+ * @deprecated On an item group which allows multiple selection, this
+ * returns the index of the first selected tab only. Instead, call {@link #selectedTabs()}
+ */
+ @Deprecated(forRemoval = true)
public int getSelectedTabIndex() {
- return selectedTab;
+ return this.activeTabs.iterator().nextInt();
+ }
+
+ /**
+ * Select only {@code tab}, deselecting all other tabs,
+ * using {@code context} for re-population
+ */
+ public void selectSingleTab(int tab, DisplayContext context) {
+ this.activeTabs.clear();
+ this.activeTabs.add(tab);
+
+ this.updateEntries(context);
+ }
+
+ /**
+ * Select {@code tab} in addition to other currently selected
+ * tabs, using {@code context} for re-population.
+ *
+ * If this group does not allow multiple selection, behaves
+ * like {@link #selectSingleTab(int, DisplayContext)}
+ */
+ public void selectTab(int tab, DisplayContext context) {
+ if (!this.allowMultiSelect) {
+ this.activeTabs.clear();
+ }
+
+ this.activeTabs.add(tab);
+ this.updateEntries(context);
+ }
+
+ /**
+ * Deselect {@code tab} if it is currently selected, using {@code context} for
+ * re-population. If this results in no tabs being selected, all tabs are
+ * automatically selected instead
+ */
+ public void deselectTab(int tab, DisplayContext context) {
+ if (!this.allowMultiSelect) return;
+
+ this.activeTabs.remove(tab);
+ if (this.activeTabs.isEmpty()) {
+ for (int tabIdx = 0; tabIdx < this.tabs.size(); tabIdx++) {
+ this.activeTabs.add(tabIdx);
+ }
+ }
+
+ this.updateEntries(context);
+ }
+
+ /**
+ * Shorthand for {@link #selectTab(int, DisplayContext)} or
+ * {@link #deselectTab(int, DisplayContext)}, depending on the tabs
+ * current state
+ */
+ public void toggleTab(int tab, DisplayContext context) {
+ if (this.isTabSelected(tab)) {
+ this.deselectTab(tab, context);
+ } else {
+ this.selectTab(tab, context);
+ }
+ }
+
+ /**
+ * @return A set containing the indices of all currently
+ * selected tabs
+ */
+ public IntSet selectedTabs() {
+ return this.activeTabsView;
+ }
+
+ /**
+ * @return {@code true} if {@code tab} is currently selected
+ */
+ public boolean isTabSelected(int tab) {
+ return this.activeTabs.contains(tab);
}
public Identifier getCustomTexture() {
@@ -260,6 +362,7 @@ public static class Builder {
private @Nullable Identifier customTexture = null;
private boolean useDynamicTitle = true;
private boolean displaySingleTab = false;
+ private boolean allowMultiSelect = true;
private Builder(Identifier id, Supplier iconSupplier) {
this.id = id;
@@ -296,8 +399,13 @@ public Builder displaySingleTab() {
return this;
}
+ public Builder withoutMultipleSelection() {
+ this.allowMultiSelect = false;
+ return this;
+ }
+
public OwoItemGroup build() {
- final var group = new OwoItemGroup(id, initializer, iconSupplier, tabStackHeight, buttonStackHeight, customTexture, useDynamicTitle, displaySingleTab) {};
+ final var group = new OwoItemGroup(id, initializer, iconSupplier, tabStackHeight, buttonStackHeight, customTexture, useDynamicTitle, displaySingleTab, allowMultiSelect) {};
Registry.register(Registries.ITEM_GROUP, this.id, group);
return group;
}
diff --git a/src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupButton.java b/src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupButton.java
index 8addeada..c89db51d 100644
--- a/src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupButton.java
+++ b/src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupButton.java
@@ -75,16 +75,16 @@ public Identifier texture() {
@Override
public Icon icon() {
- return icon;
+ return this.icon;
}
@Override
public Text tooltip() {
- return tooltip;
+ return this.tooltip;
}
public Runnable action() {
- return action;
+ return this.action;
}
}
diff --git a/src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupButtonWidget.java b/src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupButtonWidget.java
index e05c3486..c2b0595a 100644
--- a/src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupButtonWidget.java
+++ b/src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupButtonWidget.java
@@ -2,42 +2,40 @@
import com.mojang.blaze3d.systems.RenderSystem;
import io.wispforest.owo.itemgroup.OwoItemGroup;
-import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.widget.ButtonWidget;
import org.jetbrains.annotations.ApiStatus;
+import java.util.function.Consumer;
+
@ApiStatus.Internal
public class ItemGroupButtonWidget extends ButtonWidget {
public boolean isSelected = false;
private final OwoItemGroup.ButtonDefinition definition;
- private final boolean hoverReactive;
+ private final int baseU;
- public ItemGroupButtonWidget(int x, int y, boolean hoverReactive, OwoItemGroup.ButtonDefinition definition, PressAction onPress) {
- super(x, y, 24, 24, definition.tooltip(), onPress, ButtonWidget.DEFAULT_NARRATION_SUPPLIER);
+ public ItemGroupButtonWidget(int x, int y, int baseU, OwoItemGroup.ButtonDefinition definition, Consumer onPress) {
+ super(x, y, 24, 24, definition.tooltip(), button -> onPress.accept((ItemGroupButtonWidget) button), ButtonWidget.DEFAULT_NARRATION_SUPPLIER);
+ this.baseU = baseU;
this.definition = definition;
- this.hoverReactive = hoverReactive;
}
@Override
public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) {
- var client = MinecraftClient.getInstance();
-
- drawTexture(context, definition.texture(), this.getX(), this.getY(), 0, 0, height, this.width, this.height, 64, 64);
+ RenderSystem.enableDepthTest();
+ context.drawTexture(this.definition.texture(), this.getX(), this.getY(), this.baseU, this.isSelected() || this.isSelected ? this.height : 0, this.width, this.height, 64, 64);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
+ if (!(this.isSelected() || this.isSelected)) RenderSystem.setShaderColor(.75f, .75f, .75f, 1f);
this.definition.icon().render(context, this.getX() + 4, this.getY() + 4, mouseX, mouseY, delta);
+ RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
RenderSystem.disableBlend();
}
- protected boolean shouldShowHighlight(boolean hovered) {
- return (hoverReactive && hovered) || isSelected;
- }
-
public boolean trulyHovered() {
return this.hovered;
}
diff --git a/src/main/java/io/wispforest/owo/itemgroup/json/WrapperGroup.java b/src/main/java/io/wispforest/owo/itemgroup/json/WrapperGroup.java
index 523c4b29..f8dac27c 100644
--- a/src/main/java/io/wispforest/owo/itemgroup/json/WrapperGroup.java
+++ b/src/main/java/io/wispforest/owo/itemgroup/json/WrapperGroup.java
@@ -30,7 +30,7 @@ public class WrapperGroup extends OwoItemGroup {
@SuppressWarnings("unchecked")
public WrapperGroup(ItemGroup parent, Identifier parentId, List tabs, List buttons) {
- super(parentId, owoItemGroup -> {}, () -> Icon.of(parent.getIcon()), 4, 4, null, true, false);
+ super(parentId, owoItemGroup -> {}, () -> Icon.of(parent.getIcon()), 4, 4, null, true, false, false);
int parentRawId = Registries.ITEM_GROUP.getRawId(parent);
diff --git a/src/main/java/io/wispforest/owo/mixin/itemgroup/CreativeInventoryScreenMixin.java b/src/main/java/io/wispforest/owo/mixin/itemgroup/CreativeInventoryScreenMixin.java
index 6539b543..940289d7 100644
--- a/src/main/java/io/wispforest/owo/mixin/itemgroup/CreativeInventoryScreenMixin.java
+++ b/src/main/java/io/wispforest/owo/mixin/itemgroup/CreativeInventoryScreenMixin.java
@@ -10,25 +10,26 @@
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.AbstractInventoryScreen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
-import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemGroup;
import net.minecraft.resource.featuretoggle.FeatureSet;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
-import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
+import org.spongepowered.asm.mixin.injection.ModifyArgs;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
@Mixin(CreativeInventoryScreen.class)
public abstract class CreativeInventoryScreenMixin extends AbstractInventoryScreen implements OwoCreativeInventoryScreenExtensions {
@@ -36,10 +37,6 @@ public abstract class CreativeInventoryScreenMixin extends AbstractInventoryScre
@Shadow
private static ItemGroup selectedTab;
- @Shadow
- @Final
- private static Identifier TEXTURE;
-
@Shadow
protected abstract void init();
@@ -49,9 +46,6 @@ public abstract class CreativeInventoryScreenMixin extends AbstractInventoryScre
@Unique
private final List owo$buttons = new ArrayList<>();
- @Unique
- private OwoItemGroup owo$owoGroup = null;
-
@Unique
private FeatureSet owo$enabledFeatures = null;
@@ -77,50 +71,29 @@ private Identifier injectCustomGroupTexture(Identifier original) {
// Scrollbar slider
// ----------------
- @ModifyArg(method = "drawBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIIIII)V", ordinal = 1))
- private Identifier injectCustomScrollbarTexture(Identifier original) {
- if (!(selectedTab instanceof OwoItemGroup owoGroup) || owoGroup.getCustomTexture() == null) return original;
- this.owo$owoGroup = owoGroup;
- return owoGroup.getCustomTexture();
- }
-
- @ModifyArg(method = "drawBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIIIII)V", ordinal = 1), index = 3)
- private int injectCustomScrollbarTextureU(int original) {
- if (owo$owoGroup == null) return original;
- return original - 232;
- }
-
- @ModifyArg(method = "drawBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIIIII)V", ordinal = 1), index = 4)
- private int injectCustomScrollbarTextureV(int original) {
- if (owo$owoGroup == null) return original;
- return 136;
- }
+ @ModifyArgs(method = "drawBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIIIII)V", ordinal = 1))
+ private void injectCustomScrollbarTexture(Args args) {
+ if (!(selectedTab instanceof OwoItemGroup owoGroup) || owoGroup.getCustomTexture() == null) return;
- @Inject(method = "drawBackground", at = @At("RETURN"))
- private void releaseGroupInstance(DrawContext context, float delta, int mouseX, int mouseY, CallbackInfo ci) {
- this.owo$owoGroup = null;
+ args.set(0, owoGroup.getCustomTexture());
+ args.set(3, args.get(3) - 232);
+ args.set(4, 136);
}
// -------------
// Group headers
// -------------
- @Inject(method = "renderTabIcon", at = @At("HEAD"))
- private void captureTabGroup(DrawContext context, ItemGroup group, CallbackInfo ci) {
- if (!(group instanceof OwoItemGroup owoGroup)) return;
- this.owo$owoGroup = owoGroup;
- }
-
@ModifyArg(method = "renderTabIcon", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIIIII)V"))
private Identifier injectCustomTabTexture(Identifier texture) {
- if (this.owo$owoGroup == null || this.owo$owoGroup.getCustomTexture() == null) return texture;
- return owo$owoGroup.getCustomTexture();
+ if (!(selectedTab instanceof OwoItemGroup owoGroup) || owoGroup.getCustomTexture() == null) return texture;
+ return owoGroup.getCustomTexture();
}
@ModifyArg(method = "renderTabIcon", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIIIII)V"), index = 3)
private int injectCustomTabTextureLocation(int original) {
- if (this.owo$owoGroup == null || this.owo$owoGroup.getCustomTexture() == null) return original;
- return this.owo$owoGroup.getColumn() == 0 ? 195 : 221;
+ if (!(selectedTab instanceof OwoItemGroup owoGroup) || owoGroup.getCustomTexture() == null) return original;
+ return owoGroup.getColumn() == 0 ? 195 : 221;
}
@Inject(method = "renderTabIcon", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemGroup;getIcon()Lnet/minecraft/item/ItemStack;"), locals = LocalCapture.CAPTURE_FAILHARD)
@@ -133,27 +106,22 @@ private void renderOwoIcon(DrawContext context, ItemGroup group, CallbackInfo ci
RenderSystem.disableBlend();
}
- @Inject(method = "renderTabIcon", at = @At("RETURN"))
- private void restoreTabTexture(DrawContext context, ItemGroup group, CallbackInfo ci) {
- if (this.owo$owoGroup == null) return;
- this.owo$owoGroup = null;
- RenderSystem.setShaderTexture(0, TEXTURE);
- }
-
// -------------
// oωo tab title
// -------------
@ModifyArg(method = "drawForeground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;IIIZ)I"))
private Text injectTabNameAsTitle(Text original) {
- if (!(selectedTab instanceof OwoItemGroup owoGroup) || !owoGroup.hasDynamicTitle()) return original;
- if (owoGroup.getSelectedTab().primary()) {
- return owoGroup.getSelectedTab().name();
+ if (!(selectedTab instanceof OwoItemGroup owoGroup) || !owoGroup.hasDynamicTitle() || owoGroup.selectedTabs().size() != 1) return original;
+
+ var singleActiveTab = owoGroup.getTab(owoGroup.selectedTabs().iterator().nextInt());
+ if (singleActiveTab.primary()) {
+ return singleActiveTab.name();
} else {
return Text.translatable(
"text.owo.itemGroup.tab_template",
owoGroup.getDisplayName(),
- owoGroup.getSelectedTab().name()
+ singleActiveTab.name()
);
}
}
@@ -164,28 +132,26 @@ private Text injectTabNameAsTitle(Text original) {
@Inject(at = @At("HEAD"), method = "setSelectedTab(Lnet/minecraft/item/ItemGroup;)V")
private void setSelectedTab(ItemGroup group, CallbackInfo ci) {
- owo$buttons.forEach(this::remove);
- owo$buttons.clear();
+ this.owo$buttons.forEach(this::remove);
+ this.owo$buttons.clear();
if (group instanceof OwoItemGroup owoGroup) {
-
int tabRootY = this.y;
final var tabStackHeight = owoGroup.getTabStackHeight();
tabRootY -= 13 * (tabStackHeight - 4);
if (owoGroup.shouldDisplaySingleTab() || owoGroup.tabs.size() > 1) {
- for (int i = 0; i < owoGroup.tabs.size(); i++) {
- var tab = owoGroup.tabs.get(i);
-
- int xOffset = this.x - 27 - (i / tabStackHeight) * 26;
- int yOffset = tabRootY + 10 + (i % tabStackHeight) * 30;
+ for (int tabIdx = 0; tabIdx < owoGroup.tabs.size(); tabIdx++) {
+ var tab = owoGroup.tabs.get(tabIdx);
- var tabButton = new ItemGroupButtonWidget(xOffset, yOffset, false, tab, owo$createSelectAction(this, owoGroup, i));
+ int xOffset = this.x - 27 - (tabIdx / tabStackHeight) * 26;
+ int yOffset = tabRootY + 10 + (tabIdx % tabStackHeight) * 30;
- if (i == owoGroup.getSelectedTabIndex()) tabButton.isSelected = true;
+ var tabButton = new ItemGroupButtonWidget(xOffset, yOffset, 32, tab, owo$createSelectAction(owoGroup, tabIdx));
+ if (owoGroup.isTabSelected(tabIdx)) tabButton.isSelected = true;
- owo$buttons.add(tabButton);
+ this.owo$buttons.add(tabButton);
this.addDrawableChild(tabButton);
}
}
@@ -195,26 +161,26 @@ private void setSelectedTab(ItemGroup group, CallbackInfo ci) {
var buttons = owoGroup.getButtons();
for (int i = 0; i < buttons.size(); i++) {
- var button = buttons.get(i);
+ var buttonDefinition = buttons.get(i);
int xOffset = this.x + 198 + (i / buttonStackHeight) * 26;
int yOffset = tabRootY + 10 + (i % buttonStackHeight) * 30;
- var tabButton = new ItemGroupButtonWidget(xOffset, yOffset, true, button, button1 -> button.action().run());
+ var tabButton = new ItemGroupButtonWidget(xOffset, yOffset, 0, buttonDefinition, __ -> buttonDefinition.action().run());
- owo$buttons.add(tabButton);
+ this.owo$buttons.add(tabButton);
this.addDrawableChild(tabButton);
}
}
}
@Inject(at = @At("TAIL"), method = "render")
- private void render(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo cbi) {
+ private void render(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
boolean anyButtonHovered = false;
- for (var button : owo$buttons) {
+ for (var button : this.owo$buttons) {
if (button.trulyHovered()) {
- context.drawTooltip(this.textRenderer, button.getMessage(), mouseX, mouseY);
+ context.drawTooltip(this.textRenderer, List.of(button.getMessage(), Text.translatable("text.owo.itemGroup.select_hint")), mouseX, mouseY);
anyButtonHovered = true;
}
}
@@ -243,11 +209,17 @@ private void disposeCursorAdapter(CallbackInfo ci) {
}
@Unique
- private ButtonWidget.PressAction owo$createSelectAction(Screen targetScreen, OwoItemGroup group, int targetTabIndex) {
+ private Consumer owo$createSelectAction(OwoItemGroup group, int tabIdx) {
return button -> {
- group.setSelectedTab(targetTabIndex, new ItemGroup.DisplayContext(this.owo$enabledFeatures, this.shouldShowOperatorTab(this.handler.player()), this.handler.player().getWorld().getRegistryManager()));
+ var context = new ItemGroup.DisplayContext(this.owo$enabledFeatures, this.shouldShowOperatorTab(this.handler.player()), this.handler.player().getWorld().getRegistryManager());
+ if (Screen.hasShiftDown()) {
+ group.toggleTab(tabIdx, context);
+ } else {
+ group.selectSingleTab(tabIdx, context);
+ }
+
this.clearAndInit();
- ((ItemGroupButtonWidget) button).isSelected = true;
+ button.isSelected = true;
};
}
diff --git a/src/main/resources/assets/owo/lang/en_us.json b/src/main/resources/assets/owo/lang/en_us.json
index 8e491dc1..8cb8c185 100644
--- a/src/main/resources/assets/owo/lang/en_us.json
+++ b/src/main/resources/assets/owo/lang/en_us.json
@@ -4,6 +4,7 @@
{ "text": " > ", "color": "gray" },
{ "index": 1, "color": "dark_gray" }
],
+ "text.owo.itemGroup.select_hint": {"text": "Shift-click to select multiple", "color": "gray"},
"text.owo.configure_hot_reload.title": "Configure Hot Reload",
"text.owo.configure_hot_reload.choose_file": "Choose file",
"text.owo.configure_hot_reload.choose_ui_model_source": "Choose UI Model source",
diff --git a/src/main/resources/assets/owo/textures/gui/tabs.png b/src/main/resources/assets/owo/textures/gui/tabs.png
index 791d6570..c4fd916e 100644
Binary files a/src/main/resources/assets/owo/textures/gui/tabs.png and b/src/main/resources/assets/owo/textures/gui/tabs.png differ