Item Group API (#44)

* Inital proof of concept on the item group api

* Some changes

* Show hotbar tab + fix bug with inv slots not clearing out

* Better button textures + docs + tweaks/auto format

* Add an api to provide the stack to display and expand the example mod

* Use COMMON_GROUPS in all places

* Add a tooltip showing the page numbers
This commit is contained in:
Modmuss50 2018-12-24 22:15:53 +00:00 committed by Adrian Siekierka
parent 655788b93d
commit 2b462b0c51
10 changed files with 548 additions and 0 deletions

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2016, 2017, 2018 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;
import net.fabricmc.fabric.client.itemgroup.ItemGroupExtensions;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.Identifier;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
public final class FabricItemGroupBuilder {
private Identifier identifier;
private Supplier<ItemStack> stackSupplier = () -> ItemStack.EMPTY;
private Consumer<List<ItemStack>> stacksForDisplay;
private FabricItemGroupBuilder(Identifier identifier) {
this.identifier = identifier;
}
/**
*
* Create a new Item Group Builder
*
* @param identifier the id will become the name of the ItemGroup and will be used for the translation key
* @return a FabricItemGroupBuilder
*/
public static FabricItemGroupBuilder create(Identifier identifier) {
return new FabricItemGroupBuilder(identifier);
}
/**
*
* This is used to add an icon to to the item group
*
* @param stackSupplier the supplier should return the item stack that you wish to show on the tab
* @return a reference to the FabricItemGroupBuilder
*/
public FabricItemGroupBuilder icon(Supplier<ItemStack> stackSupplier) {
this.stackSupplier = stackSupplier;
return this;
}
/**
*
* This allows for a custom list of items to be displayed in a tab, this enabled tabs to be created with a custom set of items
*
* @param stacksForDisplay Add ItemStack's to this list to show in the ItemGroup
* @return a reference to the FabricItemGroupBuilder
*/
public FabricItemGroupBuilder stacksForDisplay(Consumer<List<ItemStack>> stacksForDisplay){
this.stacksForDisplay = stacksForDisplay;
return this;
}
/**
*
* This is a single method that makes creating an ItemGroup with an icon one call
*
* @param identifier the id will become the name of the ItemGroup and will be used for the translation key
* @param stackSupplier the supplier should return the item stack that you wish to show on the tab
* @return An instance of the built ItemGroup
*/
public static ItemGroup build(Identifier identifier, Supplier<ItemStack> stackSupplier){
return new FabricItemGroupBuilder(identifier).icon(stackSupplier).build();
}
/**
*
* Create an instance of the ItemGroup
*
* @return An instance of the built ItemGroup
*/
public ItemGroup build() {
((ItemGroupExtensions) ItemGroup.BUILDING_BLOCKS).fabric_expandArray();
return new ItemGroup(ItemGroup.GROUPS.length - 1, identifier.toString()) {
@Override
public ItemStack getIconItem() {
return stackSupplier.get();
}
@Override
public void getStacksForDisplay(DefaultedList<ItemStack> stacks) {
if(stacksForDisplay != null){
stacksForDisplay.accept(stacks);
return;
}
super.getStacksForDisplay(stacks);
}
};
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2016, 2017, 2018 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.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);
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2016, 2017, 2018 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.client.itemgroup;
import com.mojang.blaze3d.platform.GlStateManager;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.ingame.CreativePlayerInventoryGui;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.item.ItemGroup;
import net.minecraft.util.Identifier;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
public class FabricCreativeGuiComponents {
private static final Identifier BUTTON_TEX = new Identifier("fabric", "textures/gui/creative_buttons.png");
public static final Set<ItemGroup> COMMON_GROUPS = new HashSet<>();
static {
COMMON_GROUPS.add(ItemGroup.SEARCH);
COMMON_GROUPS.add(ItemGroup.INVENTORY);
COMMON_GROUPS.add(ItemGroup.HOTBAR);
}
public static class ItemGroupButtonWidget extends ButtonWidget {
CreativeGuiExtensions extensions;
CreativePlayerInventoryGui gui;
Type type;
public ItemGroupButtonWidget(int id, int x, int y, Type type, CreativeGuiExtensions extensions) {
super(id, x, y, 10, 11, type.text);
this.extensions = extensions;
this.type = type;
this.gui = (CreativePlayerInventoryGui) extensions;
}
@Override
public void onPressed(double double_1, double double_2) {
super.onPressed(double_1, double_2);
type.clickConsumer.accept(extensions);
}
@Override
public void draw(int mouseX, int mouseY, float float_1) {
this.visible = extensions.fabric_isButtonVisible(type);
this.enabled = extensions.fabric_isButtonEnabled(type);
if (this.visible) {
MinecraftClient minecraftClient = MinecraftClient.getInstance();
minecraftClient.getTextureManager().bindTexture(BUTTON_TEX);
GlStateManager.color4f(1F, 1F, 1F, 1F);
this.drawTexturedRect(this.x, this.y, (type == Type.NEXT ? 12 : 0), (enabled ? 0 : 12), 12, 12);
if(mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height){
gui.drawTooltip(I18n.translate("fabric.gui.creativeTabPage", extensions.fabric_currentPage() + 1, ((ItemGroup.GROUPS.length - 12) / 9) + 2), mouseX, mouseY);
}
}
}
}
public enum Type {
NEXT(">", CreativeGuiExtensions::fabric_nextPage),
PREVIOUS("<", CreativeGuiExtensions::fabric_previousPage);
String text;
Consumer<CreativeGuiExtensions> clickConsumer;
Type(String text, Consumer<CreativeGuiExtensions> clickConsumer) {
this.text = text;
this.clickConsumer = clickConsumer;
}
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2016, 2017, 2018 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.client.itemgroup;
public interface ItemGroupExtensions {
void fabric_expandArray();
}

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2016, 2017, 2018 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.mixin.client.itemgroup;
import net.fabricmc.fabric.client.itemgroup.CreativeGuiExtensions;
import net.fabricmc.fabric.client.itemgroup.FabricCreativeGuiComponents;
import net.minecraft.client.gui.ingame.AbstractPlayerInventoryGui;
import net.minecraft.client.gui.ingame.CreativePlayerInventoryGui;
import net.minecraft.container.Container;
import net.minecraft.item.ItemGroup;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(CreativePlayerInventoryGui.class)
public abstract class MixinCreativePlayerInventoryGui extends AbstractPlayerInventoryGui implements CreativeGuiExtensions {
@Shadow
protected abstract void setSelectedTab(ItemGroup itemGroup_1);
private int currentPage = 0;
public MixinCreativePlayerInventoryGui(Container container_1) {
super(container_1);
}
@Override
public void fabric_nextPage() {
if ((currentPage + 1) * 12 > ItemGroup.GROUPS.length) {
return;
}
currentPage++;
updateSelection();
}
@Override
public void fabric_previousPage() {
if (currentPage == 0) {
return;
}
currentPage--;
updateSelection();
}
@Override
public boolean fabric_isButtonVisible(FabricCreativeGuiComponents.Type type) {
return ItemGroup.GROUPS.length != 12;
}
@Override
public boolean fabric_isButtonEnabled(FabricCreativeGuiComponents.Type type) {
if (type == FabricCreativeGuiComponents.Type.NEXT) {
return !((currentPage + 1) * 12 > ItemGroup.GROUPS.length);
}
if (type == FabricCreativeGuiComponents.Type.PREVIOUS) {
return currentPage != 0;
}
return false;
}
private void updateSelection() {
int nextTab;
if (currentPage == 0) {
nextTab = 0;
} else {
nextTab = 12 + ((12 - FabricCreativeGuiComponents.COMMON_GROUPS.size()) * (currentPage - 1));
}
setSelectedTab(ItemGroup.GROUPS[nextTab]);
}
@Inject(method = "onInitialized", at = @At("RETURN"))
private void onInitialized(CallbackInfo info) {
updateSelection();
int xpos = left + 170;
int ypos = top + 4;
addButton(new FabricCreativeGuiComponents.ItemGroupButtonWidget(1001, xpos + 10, ypos, FabricCreativeGuiComponents.Type.NEXT, this));
addButton(new FabricCreativeGuiComponents.ItemGroupButtonWidget(1002, xpos, ypos, FabricCreativeGuiComponents.Type.PREVIOUS, this));
}
@Inject(method = "setSelectedTab", at = @At("HEAD"), cancellable = true)
private void setSelectedTab(ItemGroup itemGroup, CallbackInfo info) {
if (!isGroupVisible(itemGroup)) {
info.cancel();
}
}
@Inject(method = "method_2471", at = @At("HEAD"), cancellable = true)
private void method_2471(ItemGroup itemGroup, int mx, int my, CallbackInfoReturnable<Boolean> info) {
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 (!isGroupVisible(itemGroup)) {
info.setReturnValue(false);
}
}
@Inject(method = "method_2468", at = @At("HEAD"), cancellable = true)
private void method_2468(ItemGroup itemGroup, CallbackInfo info) {
if (!isGroupVisible(itemGroup)) {
info.cancel();
}
}
private boolean isGroupVisible(ItemGroup itemGroup) {
if (FabricCreativeGuiComponents.COMMON_GROUPS.contains(itemGroup)) {
return true;
}
if (itemGroup.getId() < 12) {
return currentPage == 0;
}
int page = (int) Math.floor((itemGroup.getId() - 12) / (12 - FabricCreativeGuiComponents.COMMON_GROUPS.size()));
return currentPage == page + 1;
}
@Override
public int fabric_currentPage() {
return currentPage;
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2016, 2017, 2018 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.mixin.client.itemgroup;
import net.fabricmc.fabric.client.itemgroup.FabricCreativeGuiComponents;
import net.fabricmc.fabric.client.itemgroup.ItemGroupExtensions;
import net.minecraft.item.ItemGroup;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ItemGroup.class)
public abstract class MixinItemGroup implements ItemGroupExtensions {
@Shadow
@Final
@Mutable
public static ItemGroup[] GROUPS;
@Shadow
public abstract int getId();
@Shadow
public abstract boolean isTopRow();
@Shadow
@Final
private int id;
@Override
public void fabric_expandArray() {
ItemGroup[] tempGroups = GROUPS;
GROUPS = new ItemGroup[GROUPS.length + 1];
for (ItemGroup group : tempGroups) {
GROUPS[group.getId()] = group;
}
}
@Inject(method = "isTopRow", cancellable = true, at = @At("HEAD"))
private void isTopRow(CallbackInfoReturnable<Boolean> info) {
if (getId() > 11) {
info.setReturnValue((id - 12) % (12 - FabricCreativeGuiComponents.COMMON_GROUPS.size()) < 4);
}
}
@Inject(method = "getColumn", cancellable = true, at = @At("HEAD"))
private void getColumn(CallbackInfoReturnable<Integer> info) {
if (getId() > 11) {
if (isTopRow()) {
info.setReturnValue((id - 12) % (12 - FabricCreativeGuiComponents.COMMON_GROUPS.size()));
} else {
info.setReturnValue((id - 12) % (12 - FabricCreativeGuiComponents.COMMON_GROUPS.size()) - 4);
}
}
}
}

View file

@ -0,0 +1,3 @@
{
"fabric.gui.creativeTabPage": "Page %d/%d"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

View file

@ -5,6 +5,8 @@
"mixins": [
"block.entity.MixinClientPlayNetworkHandler",
"bugfix.MixinBiomeColors",
"client.itemgroup.MixinItemGroup",
"client.itemgroup.MixinCreativePlayerInventoryGui",
"client.render.MixinBlockColorMap",
"client.render.MixinBlockEntityRenderManager",
"client.render.MixinEntityRenderManager",

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2016, 2017, 2018 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.itemgroup;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.minecraft.block.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class ItemGroupMod implements ClientModInitializer {
@Override
public void onInitializeClient() {
//This creates your standard Item Group
ItemGroup group = FabricItemGroupBuilder.build(new Identifier("fabric", "fabric_test_tab"), () -> new ItemStack(Items.IRON_CHESTPLATE));
Item testItem = new Item(new Item.Settings().itemGroup(group));
Registry.ITEM.register(new Identifier("fabric_test", "itemgroup"), testItem);
//Creates a tab with all items (including ones that dont show in search such as the command block)
FabricItemGroupBuilder.create(new Identifier("fabric", "all")).stacksForDisplay(itemStacks -> Registry.ITEM.forEach(item -> itemStacks.add(new ItemStack(item)))).build();
//Creates a group with all modded items in
FabricItemGroupBuilder.create(new Identifier("fabric", "modded")).stacksForDisplay(itemStacks -> Registry.ITEM.forEach(item -> {
if(!Registry.ITEM.getId(item).getNamespace().equals("minecraft")){
itemStacks.add(new ItemStack(item));
}
})).icon(() -> new ItemStack(Blocks.TNT)).build();
//These are just padding to ensure more than one page works
FabricItemGroupBuilder.create(new Identifier("fabric", "test1")).icon(() -> new ItemStack(Items.APPLE)).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test2")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test3")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test4")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test5")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test6")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test7")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test8")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test9")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test10")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test11")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test12")).build();
FabricItemGroupBuilder.create(new Identifier("fabric", "test13")).build();
}
}