From 00ab0a636c38ca3f4b294d06ada4ea2d8d402f7b Mon Sep 17 00:00:00 2001
From: modmuss <modmuss50@gmail.com>
Date: Sat, 8 Jun 2024 14:06:14 +0100
Subject: [PATCH] Add API to control creative inventory screen (#3814)

* Add API to control creative inventory screen

* Rename methods

* Apply suggestions from code review

Co-authored-by: haykam821 <24855774+haykam821@users.noreply.github.com>

* Update fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/api/client/itemgroup/v1/FabricCreativeInventoryScreen.java

Co-authored-by: haykam821 <24855774+haykam821@users.noreply.github.com>

---------

Co-authored-by: haykam821 <24855774+haykam821@users.noreply.github.com>
---
 .../v1/FabricCreativeInventoryScreen.java     | 117 ++++++++++++
 .../itemgroup/CreativeGuiExtensions.java      |  29 ---
 .../FabricCreativeGuiComponents.java          |  36 ++--
 .../client/CreativeInventoryScreenMixin.java  | 175 +++++++++++-------
 ...temGroup.java => FabricItemGroupImpl.java} |   6 +-
 .../mixin/itemgroup/ItemGroupMixin.java       |  16 +-
 .../mixin/itemgroup/ItemGroupsMixin.java      |  14 +-
 .../src/main/resources/fabric.mod.json        |   5 +-
 8 files changed, 264 insertions(+), 134 deletions(-)
 create mode 100644 fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/api/client/itemgroup/v1/FabricCreativeInventoryScreen.java
 delete mode 100644 fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/impl/client/itemgroup/CreativeGuiExtensions.java
 rename fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/impl/itemgroup/{FabricItemGroup.java => FabricItemGroupImpl.java} (87%)

diff --git a/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/api/client/itemgroup/v1/FabricCreativeInventoryScreen.java b/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/api/client/itemgroup/v1/FabricCreativeInventoryScreen.java
new file mode 100644
index 000000000..5a59c99c0
--- /dev/null
+++ b/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/api/client/itemgroup/v1/FabricCreativeInventoryScreen.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.client.itemgroup.v1;
+
+import java.util.List;
+
+import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
+import net.minecraft.item.ItemGroup;
+
+/**
+ * Fabric provided extensions to {@link CreativeInventoryScreen}.
+ * This interface is automatically implemented on all creative inventory screens via Mixin and interface injection.
+ */
+public interface FabricCreativeInventoryScreen {
+	/**
+	 * Switches to the page with the given index if it exists.
+	 *
+	 * @param page the index of the page to switch to
+	 * @return Returns true when the page was changed
+	 */
+	default boolean switchToPage(int page) {
+		throw new AssertionError("Implemented by mixin");
+	}
+
+	/**
+	 * Switches to the next page if it exists.
+	 *
+	 * @return Returns true when the page was changed
+	 */
+	default boolean switchToNextPage() {
+		return switchToPage(getCurrentPage() + 1);
+	}
+
+	/**
+	 * Switches to the previous page if it exists.
+	 *
+	 * @return Returns true when the page was changed
+	 */
+	default boolean switchToPreviousPage() {
+		return switchToPage(getCurrentPage() - 1);
+	}
+
+	/**
+	 * Returns the index of the current page.
+	 */
+	default int getCurrentPage() {
+		throw new AssertionError("Implemented by mixin");
+	}
+
+	/**
+	 * Returns the total number of pages.
+	 */
+	default int getPageCount() {
+		throw new AssertionError("Implemented by mixin");
+	}
+
+	/**
+	 * Returns an ordered list containing the item groups on the requested page.
+	 */
+	default List<ItemGroup> getItemGroupsOnPage(int page) {
+		throw new AssertionError("Implemented by mixin");
+	}
+
+	/**
+	 * Returns the page index of the given item group.
+	 *
+	 * <p>Item groups appearing on every page always return the current page index.
+	 *
+	 * @param itemGroup the item group to get the page index for
+	 * @return the page index of the item group
+	 */
+	default int getPage(ItemGroup itemGroup) {
+		throw new AssertionError("Implemented by mixin");
+	}
+
+	/**
+	 * Returns whether there are additional pages to show on top of the default vanilla pages.
+	 *
+	 * @return true if there are additional pages
+	 */
+	default boolean hasAdditionalPages() {
+		throw new AssertionError("Implemented by mixin");
+	}
+
+	/**
+	 * Returns the {@link ItemGroup} that is associated with the currently selected tab.
+	 *
+	 * @return the currently selected {@link ItemGroup}
+	 */
+	default ItemGroup getSelectedItemGroup() {
+		throw new AssertionError("Implemented by mixin");
+	}
+
+	/**
+	 * Sets the currently selected tab to the given {@link ItemGroup}.
+	 *
+	 * @param itemGroup the {@link ItemGroup} to select
+	 * @return true if the tab was successfully selected
+	 */
+	default boolean setSelectedItemGroup(ItemGroup itemGroup) {
+		throw new AssertionError("Implemented by mixin");
+	}
+}
diff --git a/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/impl/client/itemgroup/CreativeGuiExtensions.java b/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/impl/client/itemgroup/CreativeGuiExtensions.java
deleted file mode 100644
index e2f2636f4..000000000
--- a/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/impl/client/itemgroup/CreativeGuiExtensions.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.fabricmc.fabric.impl.client.itemgroup;
-
-public interface CreativeGuiExtensions {
-	void fabric_nextPage();
-
-	void fabric_previousPage();
-
-	int fabric_currentPage();
-
-	boolean fabric_isButtonVisible(FabricCreativeGuiComponents.Type type);
-
-	boolean fabric_isButtonEnabled(FabricCreativeGuiComponents.Type type);
-}
diff --git a/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/impl/client/itemgroup/FabricCreativeGuiComponents.java b/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/impl/client/itemgroup/FabricCreativeGuiComponents.java
index 188def75d..e8fa8347a 100644
--- a/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/impl/client/itemgroup/FabricCreativeGuiComponents.java
+++ b/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/impl/client/itemgroup/FabricCreativeGuiComponents.java
@@ -18,6 +18,7 @@ package net.fabricmc.fabric.impl.client.itemgroup;
 
 import java.util.Set;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import net.minecraft.client.MinecraftClient;
@@ -30,31 +31,33 @@ import net.minecraft.registry.Registries;
 import net.minecraft.text.Text;
 import net.minecraft.util.Identifier;
 
-import net.fabricmc.fabric.impl.itemgroup.FabricItemGroup;
+import net.fabricmc.fabric.impl.itemgroup.FabricItemGroupImpl;
 
 public class FabricCreativeGuiComponents {
 	private static final Identifier BUTTON_TEX = Identifier.of("fabric", "textures/gui/creative_buttons.png");
-	private static final double TABS_PER_PAGE = FabricItemGroup.TABS_PER_PAGE;
+	private static final double TABS_PER_PAGE = FabricItemGroupImpl.TABS_PER_PAGE;
 	public static final Set<ItemGroup> COMMON_GROUPS = Set.of(ItemGroups.SEARCH, ItemGroups.INVENTORY, ItemGroups.HOTBAR).stream()
 			.map(Registries.ITEM_GROUP::getOrThrow)
 			.collect(Collectors.toSet());
 
+	public static int getPageCount() {
+		return (int) Math.ceil((ItemGroups.getGroupsToDisplay().size() - COMMON_GROUPS.size()) / TABS_PER_PAGE);
+	}
+
 	public static class ItemGroupButtonWidget extends ButtonWidget {
-		final CreativeGuiExtensions extensions;
-		final CreativeInventoryScreen gui;
+		final CreativeInventoryScreen screen;
 		final Type type;
 
-		public ItemGroupButtonWidget(int x, int y, Type type, CreativeGuiExtensions extensions) {
-			super(x, y, 11, 12, type.text, (bw) -> type.clickConsumer.accept(extensions), ButtonWidget.DEFAULT_NARRATION_SUPPLIER);
-			this.extensions = extensions;
+		public ItemGroupButtonWidget(int x, int y, Type type, CreativeInventoryScreen screen) {
+			super(x, y, 11, 12, type.text, (bw) -> type.clickConsumer.accept(screen), ButtonWidget.DEFAULT_NARRATION_SUPPLIER);
 			this.type = type;
-			this.gui = (CreativeInventoryScreen) extensions;
+			this.screen = screen;
 		}
 
 		@Override
 		protected void renderWidget(DrawContext drawContext, int mouseX, int mouseY, float delta) {
-			this.active = extensions.fabric_isButtonEnabled(type);
-			this.visible = extensions.fabric_isButtonVisible(type);
+			this.active = type.isEnabled.test(screen);
+			this.visible = screen.hasAdditionalPages();
 
 			if (!this.visible) {
 				return;
@@ -65,22 +68,23 @@ public class FabricCreativeGuiComponents {
 			drawContext.drawTexture(BUTTON_TEX, this.getX(), this.getY(), u + (type == Type.NEXT ? 11 : 0), v, 11, 12);
 
 			if (this.isHovered()) {
-				int pageCount = (int) Math.ceil((ItemGroups.getGroupsToDisplay().size() - COMMON_GROUPS.size()) / TABS_PER_PAGE);
-				drawContext.drawTooltip(MinecraftClient.getInstance().textRenderer, Text.translatable("fabric.gui.creativeTabPage", extensions.fabric_currentPage() + 1, pageCount), mouseX, mouseY);
+				drawContext.drawTooltip(MinecraftClient.getInstance().textRenderer, Text.translatable("fabric.gui.creativeTabPage", screen.getCurrentPage() + 1, getPageCount()), mouseX, mouseY);
 			}
 		}
 	}
 
 	public enum Type {
-		NEXT(Text.literal(">"), CreativeGuiExtensions::fabric_nextPage),
-		PREVIOUS(Text.literal("<"), CreativeGuiExtensions::fabric_previousPage);
+		NEXT(Text.literal(">"), CreativeInventoryScreen::switchToNextPage, screen -> screen.getCurrentPage() + 1 < screen.getPageCount()),
+		PREVIOUS(Text.literal("<"), CreativeInventoryScreen::switchToPreviousPage, screen -> screen.getCurrentPage() != 0);
 
 		final Text text;
-		final Consumer<CreativeGuiExtensions> clickConsumer;
+		final Consumer<CreativeInventoryScreen> clickConsumer;
+		final Predicate<CreativeInventoryScreen> isEnabled;
 
-		Type(Text text, Consumer<CreativeGuiExtensions> clickConsumer) {
+		Type(Text text, Consumer<CreativeInventoryScreen> clickConsumer, Predicate<CreativeInventoryScreen> isEnabled) {
 			this.text = text;
 			this.clickConsumer = clickConsumer;
+			this.isEnabled = isEnabled;
 		}
 	}
 }
diff --git a/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/mixin/itemgroup/client/CreativeInventoryScreenMixin.java b/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/mixin/itemgroup/client/CreativeInventoryScreenMixin.java
index 67c771f90..535587ee0 100644
--- a/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/mixin/itemgroup/client/CreativeInventoryScreenMixin.java
+++ b/fabric-item-group-api-v1/src/client/java/net/fabricmc/fabric/mixin/itemgroup/client/CreativeInventoryScreenMixin.java
@@ -16,10 +16,13 @@
 
 package net.fabricmc.fabric.mixin.itemgroup.client;
 
+import java.util.Comparator;
+import java.util.List;
 import java.util.Objects;
 
 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.callback.CallbackInfo;
@@ -34,12 +37,12 @@ import net.minecraft.item.ItemGroups;
 import net.minecraft.screen.ScreenHandler;
 import net.minecraft.text.Text;
 
-import net.fabricmc.fabric.impl.client.itemgroup.CreativeGuiExtensions;
+import net.fabricmc.fabric.api.client.itemgroup.v1.FabricCreativeInventoryScreen;
 import net.fabricmc.fabric.impl.client.itemgroup.FabricCreativeGuiComponents;
-import net.fabricmc.fabric.impl.itemgroup.FabricItemGroup;
+import net.fabricmc.fabric.impl.itemgroup.FabricItemGroupImpl;
 
 @Mixin(CreativeInventoryScreen.class)
-public abstract class CreativeInventoryScreenMixin<T extends ScreenHandler> extends AbstractInventoryScreen<T> implements CreativeGuiExtensions {
+public abstract class CreativeInventoryScreenMixin<T extends ScreenHandler> extends AbstractInventoryScreen<T> implements FabricCreativeInventoryScreen {
 	public CreativeInventoryScreenMixin(T screenHandler, PlayerInventory playerInventory, Text text) {
 		super(screenHandler, playerInventory, text);
 	}
@@ -51,51 +54,15 @@ public abstract class CreativeInventoryScreenMixin<T extends ScreenHandler> exte
 	private static ItemGroup selectedTab;
 
 	// "static" matches selectedTab
-	private static int fabric_currentPage = 0;
+	@Unique
+	private static int currentPage = 0;
 
-	@Override
-	public void fabric_nextPage() {
-		if (!fabric_hasGroupForPage(fabric_currentPage + 1)) {
-			return;
-		}
-
-		fabric_currentPage++;
-		fabric_updateSelection();
-	}
-
-	@Override
-	public void fabric_previousPage() {
-		if (fabric_currentPage == 0) {
-			return;
-		}
-
-		fabric_currentPage--;
-		fabric_updateSelection();
-	}
-
-	@Override
-	public boolean fabric_isButtonVisible(FabricCreativeGuiComponents.Type type) {
-		return ItemGroups.getGroupsToDisplay().size() > (Objects.requireNonNull(ItemGroups.displayContext).hasPermissions() ? 14 : 13);
-	}
-
-	@Override
-	public boolean fabric_isButtonEnabled(FabricCreativeGuiComponents.Type type) {
-		if (type == FabricCreativeGuiComponents.Type.NEXT) {
-			return fabric_hasGroupForPage(fabric_currentPage + 1);
-		}
-
-		if (type == FabricCreativeGuiComponents.Type.PREVIOUS) {
-			return fabric_currentPage != 0;
-		}
-
-		return false;
-	}
-
-	private void fabric_updateSelection() {
-		if (!fabric_isGroupVisible(selectedTab)) {
+	@Unique
+	private void updateSelection() {
+		if (!isGroupVisible(selectedTab)) {
 			ItemGroups.getGroups()
 					.stream()
-					.filter(this::fabric_isGroupVisible)
+					.filter(this::isGroupVisible)
 					.min((a, b) -> Boolean.compare(a.isSpecial(), b.isSpecial()))
 					.ifPresent(this::setSelectedTab);
 		}
@@ -103,63 +70,131 @@ public abstract class CreativeInventoryScreenMixin<T extends ScreenHandler> exte
 
 	@Inject(method = "init", at = @At("RETURN"))
 	private void init(CallbackInfo info) {
-		fabric_currentPage = fabric_getPage(selectedTab);
+		currentPage = getPage(selectedTab);
 
 		int xpos = x + 170;
 		int ypos = y + 4;
 
-		addDrawableChild(new FabricCreativeGuiComponents.ItemGroupButtonWidget(xpos + 11, ypos, FabricCreativeGuiComponents.Type.NEXT, this));
-		addDrawableChild(new FabricCreativeGuiComponents.ItemGroupButtonWidget(xpos, ypos, FabricCreativeGuiComponents.Type.PREVIOUS, this));
+		CreativeInventoryScreen self = (CreativeInventoryScreen) (Object) this;
+		addDrawableChild(new FabricCreativeGuiComponents.ItemGroupButtonWidget(xpos + 11, ypos, FabricCreativeGuiComponents.Type.NEXT, self));
+		addDrawableChild(new FabricCreativeGuiComponents.ItemGroupButtonWidget(xpos, ypos, FabricCreativeGuiComponents.Type.PREVIOUS, self));
 	}
 
 	@Inject(method = "setSelectedTab", at = @At("HEAD"), cancellable = true)
 	private void setSelectedTab(ItemGroup itemGroup, CallbackInfo info) {
-		if (!fabric_isGroupVisible(itemGroup)) {
+		if (!isGroupVisible(itemGroup)) {
 			info.cancel();
 		}
 	}
 
 	@Inject(method = "renderTabTooltipIfHovered", at = @At("HEAD"), cancellable = true)
 	private void renderTabTooltipIfHovered(DrawContext drawContext, ItemGroup itemGroup, int mx, int my, CallbackInfoReturnable<Boolean> info) {
-		if (!fabric_isGroupVisible(itemGroup)) {
+		if (!isGroupVisible(itemGroup)) {
 			info.setReturnValue(false);
 		}
 	}
 
 	@Inject(method = "isClickInTab", at = @At("HEAD"), cancellable = true)
 	private void isClickInTab(ItemGroup itemGroup, double mx, double my, CallbackInfoReturnable<Boolean> info) {
-		if (!fabric_isGroupVisible(itemGroup)) {
+		if (!isGroupVisible(itemGroup)) {
 			info.setReturnValue(false);
 		}
 	}
 
 	@Inject(method = "renderTabIcon", at = @At("HEAD"), cancellable = true)
 	private void renderTabIcon(DrawContext drawContext, ItemGroup itemGroup, CallbackInfo info) {
-		if (!fabric_isGroupVisible(itemGroup)) {
+		if (!isGroupVisible(itemGroup)) {
 			info.cancel();
 		}
 	}
 
-	private boolean fabric_isGroupVisible(ItemGroup itemGroup) {
-		return itemGroup.shouldDisplay() && fabric_currentPage == fabric_getPage(itemGroup);
-	}
-
-	private static int fabric_getPage(ItemGroup itemGroup) {
-		if (FabricCreativeGuiComponents.COMMON_GROUPS.contains(itemGroup)) {
-			return fabric_currentPage;
-		}
-
-		final FabricItemGroup fabricItemGroup = (FabricItemGroup) itemGroup;
-		return fabricItemGroup.getPage();
-	}
-
-	private static boolean fabric_hasGroupForPage(int page) {
-		return ItemGroups.getGroupsToDisplay().stream()
-				.anyMatch(itemGroup -> fabric_getPage(itemGroup) == page);
+	@Unique
+	private boolean isGroupVisible(ItemGroup itemGroup) {
+		return itemGroup.shouldDisplay() && currentPage == getPage(itemGroup);
 	}
 
 	@Override
-	public int fabric_currentPage() {
-		return fabric_currentPage;
+	public int getPage(ItemGroup itemGroup) {
+		if (FabricCreativeGuiComponents.COMMON_GROUPS.contains(itemGroup)) {
+			return currentPage;
+		}
+
+		final FabricItemGroupImpl fabricItemGroup = (FabricItemGroupImpl) itemGroup;
+		return fabricItemGroup.fabric_getPage();
+	}
+
+	@Unique
+	private boolean hasGroupForPage(int page) {
+		return ItemGroups.getGroupsToDisplay()
+				.stream()
+				.anyMatch(itemGroup -> getPage(itemGroup) == page);
+	}
+
+	@Override
+	public boolean switchToPage(int page) {
+		if (!hasGroupForPage(page)) {
+			return false;
+		}
+
+		if (currentPage == page) {
+			return false;
+		}
+
+		currentPage = page;
+		updateSelection();
+		return true;
+	}
+
+	@Override
+	public int getCurrentPage() {
+		return currentPage;
+	}
+
+	@Override
+	public int getPageCount() {
+		return FabricCreativeGuiComponents.getPageCount();
+	}
+
+	@Override
+	public List<ItemGroup> getItemGroupsOnPage(int page) {
+		return ItemGroups.getGroupsToDisplay()
+				.stream()
+				.filter(itemGroup -> getPage(itemGroup) == page)
+				// Thanks to isXander for the sorting
+				.sorted(Comparator.comparing(ItemGroup::getRow).thenComparingInt(ItemGroup::getColumn))
+				.sorted((a, b) -> {
+					if (a.isSpecial() && !b.isSpecial()) return 1;
+					if (!a.isSpecial() && b.isSpecial()) return -1;
+					return 0;
+				})
+				.toList();
+	}
+
+	@Override
+	public boolean hasAdditionalPages() {
+		return ItemGroups.getGroupsToDisplay().size() > (Objects.requireNonNull(ItemGroups.displayContext).hasPermissions() ? 14 : 13);
+	}
+
+	@Override
+	public ItemGroup getSelectedItemGroup() {
+		return selectedTab;
+	}
+
+	@Override
+	public boolean setSelectedItemGroup(ItemGroup itemGroup) {
+		Objects.requireNonNull(itemGroup, "itemGroup");
+
+		if (selectedTab == itemGroup) {
+			return false;
+		}
+
+		if (currentPage != getPage(itemGroup)) {
+			if (!switchToPage(getPage(itemGroup))) {
+				return false;
+			}
+		}
+
+		setSelectedTab(itemGroup);
+		return true;
 	}
 }
diff --git a/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/impl/itemgroup/FabricItemGroup.java b/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/impl/itemgroup/FabricItemGroupImpl.java
similarity index 87%
rename from fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/impl/itemgroup/FabricItemGroup.java
rename to fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/impl/itemgroup/FabricItemGroupImpl.java
index 01f58477c..9fdb942b5 100644
--- a/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/impl/itemgroup/FabricItemGroup.java
+++ b/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/impl/itemgroup/FabricItemGroupImpl.java
@@ -16,10 +16,10 @@
 
 package net.fabricmc.fabric.impl.itemgroup;
 
-public interface FabricItemGroup {
+public interface FabricItemGroupImpl {
 	int TABS_PER_PAGE = 10;
 
-	int getPage();
+	int fabric_getPage();
 
-	void setPage(int page);
+	void fabric_setPage(int page);
 }
diff --git a/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/mixin/itemgroup/ItemGroupMixin.java b/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/mixin/itemgroup/ItemGroupMixin.java
index a3fa72964..b8f8b61ee 100644
--- a/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/mixin/itemgroup/ItemGroupMixin.java
+++ b/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/mixin/itemgroup/ItemGroupMixin.java
@@ -37,11 +37,11 @@ import net.minecraft.registry.RegistryKey;
 import net.fabricmc.fabric.api.event.Event;
 import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroupEntries;
 import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
-import net.fabricmc.fabric.impl.itemgroup.FabricItemGroup;
+import net.fabricmc.fabric.impl.itemgroup.FabricItemGroupImpl;
 import net.fabricmc.fabric.impl.itemgroup.ItemGroupEventsImpl;
 
 @Mixin(ItemGroup.class)
-abstract class ItemGroupMixin implements FabricItemGroup {
+abstract class ItemGroupMixin implements FabricItemGroupImpl {
 	@Shadow
 	private Collection<ItemStack> displayStacks;
 
@@ -49,7 +49,7 @@ abstract class ItemGroupMixin implements FabricItemGroup {
 	private Set<ItemStack> searchTabStacks;
 
 	@Unique
-	private int fabric_page = -1;
+	private int page = -1;
 
 	@SuppressWarnings("ConstantConditions")
 	@Inject(method = "updateEntries", at = @At("TAIL"))
@@ -91,16 +91,16 @@ abstract class ItemGroupMixin implements FabricItemGroup {
 	}
 
 	@Override
-	public int getPage() {
-		if (fabric_page < 0) {
+	public int fabric_getPage() {
+		if (page < 0) {
 			throw new IllegalStateException("Item group has no page");
 		}
 
-		return fabric_page;
+		return page;
 	}
 
 	@Override
-	public void setPage(int page) {
-		this.fabric_page = page;
+	public void fabric_setPage(int page) {
+		this.page = page;
 	}
 }
diff --git a/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/mixin/itemgroup/ItemGroupsMixin.java b/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/mixin/itemgroup/ItemGroupsMixin.java
index d381d638e..09dbf3c18 100644
--- a/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/mixin/itemgroup/ItemGroupsMixin.java
+++ b/fabric-item-group-api-v1/src/main/java/net/fabricmc/fabric/mixin/itemgroup/ItemGroupsMixin.java
@@ -47,12 +47,12 @@ import net.minecraft.registry.Registries;
 import net.minecraft.registry.RegistryKey;
 import net.minecraft.registry.entry.RegistryEntry;
 
-import net.fabricmc.fabric.impl.itemgroup.FabricItemGroup;
+import net.fabricmc.fabric.impl.itemgroup.FabricItemGroupImpl;
 
 @Mixin(ItemGroups.class)
 public class ItemGroupsMixin {
 	@Unique
-	private static final int TABS_PER_PAGE = FabricItemGroup.TABS_PER_PAGE;
+	private static final int TABS_PER_PAGE = FabricItemGroupImpl.TABS_PER_PAGE;
 
 	@Inject(method = "collect", at = @At("HEAD"), cancellable = true)
 	private static void deferDuplicateCheck(CallbackInfo ci) {
@@ -86,16 +86,16 @@ public class ItemGroupsMixin {
 
 		for (RegistryEntry.Reference<ItemGroup> reference : sortedItemGroups) {
 			final ItemGroup itemGroup = reference.value();
-			final FabricItemGroup fabricItemGroup = (FabricItemGroup) itemGroup;
+			final FabricItemGroupImpl fabricItemGroup = (FabricItemGroupImpl) itemGroup;
 
 			if (vanillaGroups.contains(reference.registryKey())) {
 				// Vanilla group goes on the first page.
-				fabricItemGroup.setPage(0);
+				fabricItemGroup.fabric_setPage(0);
 				continue;
 			}
 
 			final ItemGroupAccessor itemGroupAccessor = (ItemGroupAccessor) itemGroup;
-			fabricItemGroup.setPage((count / TABS_PER_PAGE) + 1);
+			fabricItemGroup.fabric_setPage((count / TABS_PER_PAGE) + 1);
 			int pageIndex = count % TABS_PER_PAGE;
 			ItemGroup.Row row = pageIndex < (TABS_PER_PAGE / 2) ? ItemGroup.Row.TOP : ItemGroup.Row.BOTTOM;
 			itemGroupAccessor.setRow(row);
@@ -110,9 +110,9 @@ public class ItemGroupsMixin {
 
 		for (RegistryKey<ItemGroup> registryKey : Registries.ITEM_GROUP.getKeys()) {
 			final ItemGroup itemGroup = Registries.ITEM_GROUP.getOrThrow(registryKey);
-			final FabricItemGroup fabricItemGroup = (FabricItemGroup) itemGroup;
+			final FabricItemGroupImpl fabricItemGroup = (FabricItemGroupImpl) itemGroup;
 			final String displayName = itemGroup.getDisplayName().getString();
-			final var position = new ItemGroupPosition(itemGroup.getRow(), itemGroup.getColumn(), fabricItemGroup.getPage());
+			final var position = new ItemGroupPosition(itemGroup.getRow(), itemGroup.getColumn(), fabricItemGroup.fabric_getPage());
 			final String existingName = map.put(position, displayName);
 
 			if (existingName != null) {
diff --git a/fabric-item-group-api-v1/src/main/resources/fabric.mod.json b/fabric-item-group-api-v1/src/main/resources/fabric.mod.json
index 67a084a2d..d5d92a70c 100644
--- a/fabric-item-group-api-v1/src/main/resources/fabric.mod.json
+++ b/fabric-item-group-api-v1/src/main/resources/fabric.mod.json
@@ -30,6 +30,9 @@
   ],
   "accessWidener": "fabric-item-group-api-v1.accesswidener",
   "custom": {
-    "fabric-api:module-lifecycle": "stable"
+    "fabric-api:module-lifecycle": "stable",
+    "loom:injected_interfaces": {
+      "net/minecraft/class_481": ["net/fabricmc/fabric/api/client/itemgroup/v1/FabricCreativeInventoryScreen"]
+    }
   }
 }