aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/CottonScreenController.java397
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/EmptyInventory.java53
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/PropertyDelegateHolder.java7
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java46
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java37
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/client/CottonScreen.java237
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/client/ScreenDrawing.java235
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WBar.java175
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WGridPanel.java27
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WItemSlot.java147
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WLabel.java45
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WPanel.java141
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WPlainPanel.java27
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java210
-rw-r--r--src/main/java/net/fabricmc/example/ExampleMod.java14
-rw-r--r--src/main/java/net/fabricmc/example/mixin/ExampleMixin.java15
-rw-r--r--src/main/resources/assets/libgui/icon.png (renamed from src/main/resources/assets/modid/icon.png)bin453 -> 453 bytes
-rw-r--r--src/main/resources/fabric.mod.json24
-rw-r--r--src/main/resources/modid.mixins.json13
19 files changed, 1791 insertions, 59 deletions
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/CottonScreenController.java b/src/main/java/io/github/cottonmc/cotton/gui/CottonScreenController.java
new file mode 100644
index 0000000..47b56f6
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/CottonScreenController.java
@@ -0,0 +1,397 @@
+package io.github.cottonmc.cotton.gui;
+
+import java.util.ArrayList;
+
+import javax.annotation.Nullable;
+
+import io.github.cottonmc.cotton.gui.client.BackgroundPainter;
+import io.github.cottonmc.cotton.gui.widget.WGridPanel;
+import io.github.cottonmc.cotton.gui.widget.WItemSlot;
+import io.github.cottonmc.cotton.gui.widget.WPanel;
+import io.github.cottonmc.cotton.gui.widget.WPlainPanel;
+import io.github.cottonmc.cotton.gui.widget.WWidget;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.InventoryProvider;
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.container.ArrayPropertyDelegate;
+import net.minecraft.container.BlockContext;
+import net.minecraft.container.CraftingContainer;
+import net.minecraft.container.PropertyDelegate;
+import net.minecraft.container.Slot;
+import net.minecraft.container.SlotActionType;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.recipe.Recipe;
+import net.minecraft.recipe.RecipeFinder;
+import net.minecraft.recipe.RecipeInputProvider;
+import net.minecraft.recipe.RecipeType;
+import net.minecraft.world.World;
+
+public abstract class CottonScreenController extends CraftingContainer<Inventory> {
+
+ protected Inventory blockInventory;
+ protected PlayerInventory playerInventory;
+ protected RecipeType<?> recipeType;
+ protected World world;
+ protected PropertyDelegate propertyDelegate;
+
+ protected WPanel rootPanel = new WGridPanel();
+ protected int titleColor = 0xFF404040;
+
+ public CottonScreenController(RecipeType<?> recipeType, int syncId, PlayerInventory playerInventory) {
+ super(null, syncId);
+ this.blockInventory = null;
+ this.playerInventory = playerInventory;
+ this.recipeType = recipeType;
+ this.world = playerInventory.player.world;
+ this.propertyDelegate = null;//new ArrayPropertyDelegate(1);
+ }
+
+ public CottonScreenController(RecipeType<?> recipeType, int syncId, PlayerInventory playerInventory, Inventory blockInventory, PropertyDelegate propertyDelegate) {
+ super(null, syncId);
+ this.blockInventory = blockInventory;
+ this.playerInventory = playerInventory;
+ this.recipeType = recipeType;
+ this.world = playerInventory.player.world;
+ this.propertyDelegate = propertyDelegate;
+ if (propertyDelegate!=null && propertyDelegate.size()>0) this.addProperties(propertyDelegate);
+ }
+
+ public WPanel getRootPanel() {
+ return rootPanel;
+ }
+
+ public int getTitleColor() {
+ return titleColor;
+ }
+
+ public CottonScreenController setRootPanel(WPanel panel) {
+ this.rootPanel = panel;
+ return this;
+ }
+
+ public CottonScreenController setTitleColor(int color) {
+ this.titleColor = color;
+ return this;
+ }
+
+ @Environment(EnvType.CLIENT)
+ public void addPainters() {
+ if (this.rootPanel!=null) {
+ this.rootPanel.setBackgroundPainter(BackgroundPainter.VANILLA);
+ }
+ }
+
+ public void addSlotPeer(ValidatedSlot slot) {
+ this.addSlot(slot);
+ }
+
+ @Override
+ public ItemStack onSlotClick(int slotNumber, int button, SlotActionType action, PlayerEntity player) {
+ if (action==SlotActionType.QUICK_MOVE) {
+
+ if (slotNumber < 0) {
+ return ItemStack.EMPTY;
+ }
+
+ if (slotNumber>=this.slotList.size()) return ItemStack.EMPTY;
+ Slot slot = this.slotList.get(slotNumber);
+ if (slot == null || !slot.canTakeItems(player)) {
+ return ItemStack.EMPTY;
+ }
+
+ ItemStack remaining = ItemStack.EMPTY;
+ if (slot != null && slot.hasStack()) {
+ ItemStack toTransfer = slot.getStack();
+ remaining = toTransfer.copy();
+ //if (slot.inventory==blockInventory) {
+ if (blockInventory!=null) {
+ if (slot.inventory==blockInventory) {
+ //Try to transfer the item from the block into the player's inventory
+ if (!this.insertItem(toTransfer, this.playerInventory, true)) {
+ return ItemStack.EMPTY;
+ }
+ } else if (!this.insertItem(toTransfer, this.blockInventory, false)) { //Try to transfer the item from the player to the block
+ return ItemStack.EMPTY;
+ }
+ } else {
+ //There's no block, just swap between the player's storage and their hotbar
+ if (!swapHotbar(toTransfer, slotNumber, this.playerInventory)) {
+ return ItemStack.EMPTY;
+ }
+ }
+
+ if (toTransfer.isEmpty()) {
+ slot.setStack(ItemStack.EMPTY);
+ } else {
+ slot.markDirty();
+ }
+ }
+
+ return remaining;
+ } else {
+ return super.onSlotClick(slotNumber, button, action, player);
+ }
+ }
+
+ /** WILL MODIFY toInsert! Returns true if anything was inserted. */
+ private boolean insertIntoExisting(ItemStack toInsert, Slot slot) {
+ ItemStack curSlotStack = slot.getStack();
+ if (!curSlotStack.isEmpty() && canStacksCombine(toInsert, curSlotStack)) {
+ int combinedAmount = curSlotStack.getCount() + toInsert.getCount();
+ if (combinedAmount <= toInsert.getMaxCount()) {
+ toInsert.setCount(0);
+ curSlotStack.setCount(combinedAmount);
+ slot.markDirty();
+ return true;
+ } else if (curSlotStack.getCount() < toInsert.getMaxCount()) {
+ toInsert.decrement(toInsert.getMaxCount() - curSlotStack.getCount());
+ curSlotStack.setCount(toInsert.getMaxCount());
+ slot.markDirty();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** WILL MODIFY toInsert! Returns true if anything was inserted. */
+ private boolean insertIntoEmpty(ItemStack toInsert, Slot slot) {
+ ItemStack curSlotStack = slot.getStack();
+ if (curSlotStack.isEmpty() && slot.canInsert(toInsert)) {
+ if (toInsert.getCount() > slot.getMaxStackAmount()) {
+ slot.setStack(toInsert.split(slot.getMaxStackAmount()));
+ } else {
+ slot.setStack(toInsert.split(toInsert.getCount()));
+ }
+
+ slot.markDirty();
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean insertItem(ItemStack toInsert, Inventory inventory, boolean walkBackwards) {
+ //Make a unified list of slots *only from this inventory*
+ ArrayList<Slot> inventorySlots = new ArrayList<>();
+ for(Slot slot : slotList) {
+ if (slot.inventory==inventory) inventorySlots.add(slot);
+ }
+ if (inventorySlots.isEmpty()) return false;
+
+ //Try to insert it on top of existing stacks
+ boolean inserted = false;
+ if (walkBackwards) {
+ for(int i=inventorySlots.size()-1; i>=0; i--) {
+ Slot curSlot = inventorySlots.get(i);
+ if (insertIntoExisting(toInsert, curSlot)) inserted = true;
+ if (toInsert.isEmpty()) break;
+ }
+ } else {
+ for(int i=0; i<inventorySlots.size(); i++) {
+ Slot curSlot = inventorySlots.get(i);
+ if (insertIntoExisting(toInsert, curSlot)) inserted = true;
+ if (toInsert.isEmpty()) break;
+ }
+
+ }
+
+ //If we still have any, shove them into empty slots
+ if (!toInsert.isEmpty()) {
+ if (walkBackwards) {
+ for(int i=inventorySlots.size()-1; i>=0; i--) {
+ Slot curSlot = inventorySlots.get(i);
+ if (insertIntoEmpty(toInsert, curSlot)) inserted = true;
+ if (toInsert.isEmpty()) break;
+ }
+ } else {
+ for(int i=0; i<inventorySlots.size(); i++) {
+ Slot curSlot = inventorySlots.get(i);
+ if (insertIntoEmpty(toInsert, curSlot)) inserted = true;
+ if (toInsert.isEmpty()) break;
+ }
+
+ }
+ }
+
+ return inserted;
+ }
+
+ private boolean swapHotbar(ItemStack toInsert, int slotNumber, Inventory inventory) {
+ //Feel out the slots to see what's storage versus hotbar
+ ArrayList<Slot> storageSlots = new ArrayList<>();
+ ArrayList<Slot> hotbarSlots = new ArrayList<>();
+ boolean swapToStorage = true;
+ boolean inserted = false;
+
+ for(Slot slot : slotList) {
+ if (slot.inventory==inventory && slot instanceof ValidatedSlot) {
+ int index = ((ValidatedSlot)slot).getInventoryIndex();
+ if (PlayerInventory.isValidHotbarIndex(index)) {
+ hotbarSlots.add(slot);
+ } else {
+ storageSlots.add(slot);
+ if (index==slotNumber) swapToStorage = false;
+ }
+ }
+ }
+ if (storageSlots.isEmpty() || hotbarSlots.isEmpty()) return false;
+
+ if (swapToStorage) {
+ //swap from hotbar to storage
+ for(int i=0; i<storageSlots.size(); i++) {
+ Slot curSlot = storageSlots.get(i);
+ if (insertIntoExisting(toInsert, curSlot)) inserted = true;
+ if (toInsert.isEmpty()) break;
+ }
+ if (!toInsert.isEmpty()) {
+ for(int i=0; i<storageSlots.size(); i++) {
+ Slot curSlot = storageSlots.get(i);
+ if (insertIntoEmpty(toInsert, curSlot)) inserted = true;
+ if (toInsert.isEmpty()) break;
+ }
+ }
+ } else {
+ //swap from storage to hotbar
+ for(int i=0; i<hotbarSlots.size(); i++) {
+ Slot curSlot = hotbarSlots.get(i);
+ if (insertIntoExisting(toInsert, curSlot)) inserted = true;
+ if (toInsert.isEmpty()) break;
+ }
+ if (!toInsert.isEmpty()) {
+ for(int i=0; i<hotbarSlots.size(); i++) {
+ Slot curSlot = hotbarSlots.get(i);
+ if (insertIntoEmpty(toInsert, curSlot)) inserted = true;
+ if (toInsert.isEmpty()) break;
+ }
+ }
+ }
+
+ return inserted;
+ }
+
+ @Nullable
+ public WWidget doMouseUp(int x, int y, int state) {
+ if (rootPanel!=null) return rootPanel.onMouseUp(x, y, state);
+ return null;
+ }
+
+ @Nullable
+ public WWidget doMouseDown(int x, int y, int button) {
+ if (rootPanel!=null) return rootPanel.onMouseDown(x, y, button);
+ return null;
+ }
+
+ public void doMouseDrag(int x, int y, int button) {
+ if (rootPanel!=null) rootPanel.onMouseDrag(x, y, button);
+ }
+
+ public void doClick(int x, int y, int button) {
+ if (rootPanel!=null) rootPanel.onClick(x, y, button);
+ }
+
+ /**
+ * Gets the PropertyDelegate associated with this Controller.
+ */
+ @Nullable
+ public PropertyDelegate getPropertyDelegate() {
+ // TODO Auto-generated method stub
+ return propertyDelegate;
+ }
+
+ public WPanel createPlayerInventoryPanel() {
+ WPlainPanel inv = new WPlainPanel();
+ inv.add(WItemSlot.ofPlayerStorage(playerInventory), 0, 0);
+ inv.add(WItemSlot.of(playerInventory, 0, 9, 1), 0, 16*4 - 6);
+ return inv;
+ }
+
+ public static Inventory getBlockInventory(BlockContext ctx) {
+ return ctx.run((world, pos) -> {
+ BlockState state = world.getBlockState(pos);
+ Block b = state.getBlock();
+
+ if (b instanceof InventoryProvider) {
+ return ((InventoryProvider)b).getInventory(state, world, pos);
+ }
+
+ BlockEntity be = world.getBlockEntity(pos);
+ if (be!=null) {
+ if (be instanceof InventoryProvider) {
+ return ((InventoryProvider)be).getInventory(state, world, pos);
+ } else if (be instanceof Inventory) {
+ return (Inventory)be;
+ }
+ }
+
+ return EmptyInventory.INSTANCE;
+ }).orElse(EmptyInventory.INSTANCE);
+ }
+
+ public static PropertyDelegate getBlockPropertyDelegate(BlockContext ctx) {
+ return ctx.run((world, pos) -> {
+ BlockState state = world.getBlockState(pos);
+ Block block = state.getBlock();
+ if (block instanceof PropertyDelegateHolder) {
+ return ((PropertyDelegateHolder)block).getPropertyDelegate();
+ }
+ BlockEntity be = world.getBlockEntity(pos);
+ if (be!=null && be instanceof PropertyDelegateHolder) {
+ return ((PropertyDelegateHolder)be).getPropertyDelegate();
+ }
+
+ return new ArrayPropertyDelegate(0);
+ }).orElse(new ArrayPropertyDelegate(0));
+ }
+
+ //extends CraftingContainer<Inventory> {
+ @Override
+ public void populateRecipeFinder(RecipeFinder recipeFinder) {
+ if (this.blockInventory instanceof RecipeInputProvider) {
+ ((RecipeInputProvider)this.blockInventory).provideRecipeInputs(recipeFinder);
+ }
+ }
+
+ @Override
+ public void clearCraftingSlots() {
+ if (this.blockInventory!=null) this.blockInventory.clear();
+ }
+
+ @Override
+ public boolean matches(Recipe<? super Inventory> recipe) {
+ if (blockInventory==null || world==null) return false;
+ return false; //TODO recipe support
+ }
+
+ @Override
+ public abstract int getCraftingResultSlotIndex();
+
+ @Override
+ public int getCraftingWidth() {
+ return 1;
+ }
+
+ @Override
+ public int getCraftingHeight() {
+ return 1;
+ }
+
+ @Override
+ @Environment(EnvType.CLIENT)
+ public int getCraftingSlotCount() {
+ return 1;
+ }
+
+ //(implied) extends Container {
+ @Override
+ public boolean canUse(PlayerEntity entity) {
+ return (blockInventory!=null) ? blockInventory.canPlayerUseInv(entity) : true;
+ }
+ //}
+ //}
+}
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/EmptyInventory.java b/src/main/java/io/github/cottonmc/cotton/gui/EmptyInventory.java
new file mode 100644
index 0000000..a80b5da
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/EmptyInventory.java
@@ -0,0 +1,53 @@
+package io.github.cottonmc.cotton.gui;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.item.ItemStack;
+
+public class EmptyInventory implements Inventory {
+ public static final EmptyInventory INSTANCE = new EmptyInventory();
+
+ private EmptyInventory() {}
+
+ @Override
+ public void clear() {}
+
+ @Override
+ public int getInvSize() {
+ return 0;
+ }
+
+ @Override
+ public boolean isInvEmpty() {
+ return true;
+ }
+
+ @Override
+ public ItemStack getInvStack(int var1) {
+ return ItemStack.EMPTY;
+ }
+
+ @Override
+ public ItemStack takeInvStack(int var1, int var2) {
+ return ItemStack.EMPTY;
+ }
+
+ @Override
+ public ItemStack removeInvStack(int var1) {
+ return ItemStack.EMPTY;
+ }
+
+ @Override
+ public void setInvStack(int var1, ItemStack var2) {
+ }
+
+ @Override
+ public void markDirty() {
+ }
+
+ @Override
+ public boolean canPlayerUseInv(PlayerEntity var1) {
+ return false;
+ }
+
+}
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/PropertyDelegateHolder.java b/src/main/java/io/github/cottonmc/cotton/gui/PropertyDelegateHolder.java
new file mode 100644
index 0000000..6ec1476
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/PropertyDelegateHolder.java
@@ -0,0 +1,7 @@
+package io.github.cottonmc.cotton.gui;
+
+import net.minecraft.container.PropertyDelegate;
+
+public interface PropertyDelegateHolder {
+ public PropertyDelegate getPropertyDelegate();
+}
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java b/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java
new file mode 100644
index 0000000..9301761
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java
@@ -0,0 +1,46 @@
+package io.github.cottonmc.cotton.gui;
+
+import net.minecraft.container.Slot;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.item.ItemStack;
+
+public class ValidatedSlot extends Slot {
+ private final int slotNumber;
+
+ public ValidatedSlot(Inventory inventoryIn, int index, int xPosition, int yPosition) {
+ super(inventoryIn, index, xPosition, yPosition);
+ if (inventoryIn==null) throw new IllegalArgumentException("Can't make an itemslot from a null inventory!");
+ this.slotNumber = index;
+ }
+
+ @Override
+ public boolean canInsert(ItemStack stack) {
+ return inventory.isValidInvStack(slotNumber, stack);
+ }
+
+ @Override
+ public boolean canTakeItems(PlayerEntity player) {
+ return inventory.canPlayerUseInv(player);
+ }
+
+ @Override
+ public ItemStack getStack() {
+ if (inventory==null) {
+ System.out.println("Prevented null-inventory from WItemSlot with slot #: "+slotNumber);
+ return ItemStack.EMPTY;
+ }
+
+ ItemStack result = super.getStack();
+ if (result==null) {
+ System.out.println("Prevented null-itemstack crash from: "+inventory.getClass().getCanonicalName());
+ return ItemStack.EMPTY;
+ }
+
+ return result;
+ }
+
+ public int getInventoryIndex() {
+ return slotNumber;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java b/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java
new file mode 100644
index 0000000..38f9130
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java
@@ -0,0 +1,37 @@
+package io.github.cottonmc.cotton.gui.client;
+
+import io.github.cottonmc.cotton.gui.widget.WWidget;
+
+public interface BackgroundPainter {
+ /**
+ * Paint the specified panel to the screen.
+ * @param left The absolute position of the left of the panel, in gui-screen coordinates
+ * @param top The absolute position of the top of the panel, in gui-screen coordinates
+ * @param panel The panel being painted
+ */
+ public void paintBackground(int left, int top, WWidget panel);
+
+
+
+ public static BackgroundPainter VANILLA = (left, top, panel) -> {
+ ScreenDrawing.drawGuiPanel(left-8, top-8, panel.getWidth()+16, panel.getHeight()+16);
+
+ };
+
+
+
+ public static BackgroundPainter createColorful(int panelColor) {
+ return (left, top, panel) -> {
+ ScreenDrawing.drawGuiPanel(left-8, top-8, panel.getWidth()+16, panel.getHeight()+16, panelColor);
+ };
+ }
+
+ public static BackgroundPainter createColorful(int panelColor, float contrast) {
+ return (left, top, panel) -> {
+ int shadowColor = ScreenDrawing.multiplyColor(panelColor, 1.0f - contrast);
+ int hilightColor = ScreenDrawing.multiplyColor(panelColor, 1.0f + contrast);
+
+ ScreenDrawing.drawGuiPanel(left-8, top-8, panel.getWidth()+16, panel.getHeight()+16, shadowColor, panelColor, hilightColor, 0xFF000000);
+ };
+ }
+}
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonScreen.java b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonScreen.java
new file mode 100644
index 0000000..dc78a25
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonScreen.java
@@ -0,0 +1,237 @@
+package io.github.cottonmc.cotton.gui.client;
+
+import io.github.cottonmc.cotton.gui.CottonScreenController;
+import io.github.cottonmc.cotton.gui.widget.WPanel;
+import io.github.cottonmc.cotton.gui.widget.WWidget;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.text.LiteralText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Nameable;
+
+public class CottonScreen<T extends CottonScreenController> extends AbstractContainerScreen<T> {
+ protected CottonScreenController container;
+ public static final int PADDING = 8;
+ protected WWidget lastResponder = null;
+
+ public CottonScreen(T container, PlayerEntity player) {
+ super(container, player.inventory, new LiteralText(""));
+ this.container = container;
+ width = 18*9;
+ height = 18*9;
+ this.containerWidth = 18*9;
+ this.containerHeight = 18*9;
+ }
+
+ /*
+ * RENDERING NOTES:
+ *
+ * * "width" and "height" are the width and height of the overall screen
+ * * "containerWidth" and "containerHeight" are the width and height of the panel to render
+ * * "left" and "top" are *actually* self-explanatory
+ * * coordinates start at 0,0 at the topleft of the screen.
+ */
+
+
+ /*
+ * These methods are called frequently and empty, meaning they're probably *meant* for subclasses to override to
+ * provide core GUI functionality.
+ */
+
+ @Override
+ public void init(MinecraftClient minecraftClient_1, int screenWidth, int screenHeight) {
+ super.init(minecraftClient_1, screenWidth, screenHeight);
+
+ container.addPainters();
+
+ reposition();
+ }
+
+ public void reposition() {
+ WPanel basePanel = container.getRootPanel();
+ if (basePanel!=null) {
+ basePanel.validate(container);
+
+ containerWidth = basePanel.getWidth();
+ containerHeight = basePanel.getHeight();
+
+ //DEBUG
+ if (containerWidth<16) containerWidth=300;
+ if (containerHeight<16) containerHeight=300;
+ //if (left<0 || left>300) left = 10;
+ //if (top<0 || top>300) top = 10;
+ }
+ left = (width / 2) - (containerWidth / 2);
+ top = (height / 2) - (containerHeight / 2);
+ }
+
+ //Will probably re-activate for animation!
+ //@Override
+ //public void updateScreen() {
+ // System.out.println("updateScreen");
+ //}
+
+ @Override
+ public void onClose() {
+ super.onClose();
+ }
+
+ @Override
+ public boolean isPauseScreen() {
+ //...yeah, we're going to go ahead and override that.
+ return false;
+ }
+
+ /*
+ * While these methods are implemented in GuiScreen, chances are we'll be shadowing a lot of the GuiScreen methods
+ * in order to implement our own button protocol and more advanced features.
+ */
+
+
+ @Override
+ public boolean charTyped(char typedChar, int keyCode) {
+ if (MinecraftClient.getInstance().options.keyInventory.matchesKey(keyCode, keyCode));
+
+ return super.charTyped(typedChar, keyCode);
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) {
+ boolean result = super.mouseClicked(mouseX, mouseY, mouseButton);
+ int containerX = (int)mouseX-left;
+ int containerY = (int)mouseY-top;
+ if (containerX<0 || containerY<0 || containerX>=width || containerY>=height) return result;
+ lastResponder = container.doMouseDown(containerX, containerY, mouseButton);
+ return result;
+ }
+
+ @Override
+ public boolean mouseReleased(double mouseX, double mouseY, int mouseButton) { //Testing shows that STATE IS ACTUALLY BUTTON
+ boolean result = super.mouseReleased(mouseX, mouseY, mouseButton);
+ int containerX = (int)mouseX-left;
+ int containerY = (int)mouseY-top;
+ if (containerX<0 || containerY<0 || containerX>=width || containerY>=height) return result;
+
+ WWidget responder = container.doMouseUp(containerX, containerY, mouseButton);
+ if (responder!=null && responder==lastResponder) container.doClick(containerX, containerY, mouseButton);
+ lastResponder = null;
+ return result;
+ }
+
+ @Override
+ public boolean mouseDragged(double mouseX, double mouseY, int mouseButton, double unknown_1, double unknown_2) {
+ boolean result = super.mouseDragged(mouseX, mouseY, mouseButton, unknown_1, unknown_2);
+
+ int containerX = (int)mouseX-left;
+ int containerY = (int)mouseY-top;
+ if (containerX<0 || containerY<0 || containerX>=width || containerY>=height) return result;
+ container.doMouseDrag(containerX, containerY, mouseButton);
+ return result;
+ }
+
+ /*
+ @Override
+ protected void actionPerformed(GuiButton button) throws IOException {
+ super.actionPerformed(button);
+ }*/
+
+ /*
+ * We'll probably wind up calling some of this manually, but they do useful things for us so we may leave
+ * them unharmed.
+ */
+ /*
+ @Override
+ public void setWorldAndResolution(Minecraft mc, int width, int height) {
+ super.setWorldAndResolution(mc, width, height);
+
+ WPanel basePanel = container.getRootPanel();
+ if (basePanel!=null) {
+ xSize = basePanel.getWidth();
+ ySize = basePanel.getHeight();
+ }
+ left = (width / 2) - (xSize / 2);
+ top = (height / 2) - (ySize / 2);
+
+ }
+ */
+
+ @Override
+ public void resize(MinecraftClient minecraftClient_1, int int_1, int int_2) {
+ //super.onScaleChanged(minecraftClient_1, int_1, int_2);
+ this.width = int_1;
+ this.height = int_2;
+ reposition();
+ }
+
+ /*
+ * SPECIAL FUNCTIONS: Where possible, we want to draw everything based on *actual GUI state and composition* rather
+ * than relying on pre-baked textures that the programmer then needs to carefully match up their GUI to.
+ */
+
+ private int multiplyColor(int color, float amount) {
+ int a = color & 0xFF000000;
+ float r = (color >> 16 & 255) / 255.0F;
+ float g = (color >> 8 & 255) / 255.0F;
+ float b = (color & 255) / 255.0F;
+
+ r = Math.min(r*amount, 1.0f);
+ g = Math.min(g*amount, 1.0f);
+ b = Math.min(b*amount, 1.0f);
+
+ int ir = (int)(r*255);
+ int ig = (int)(g*255);
+ int ib = (int)(b*255);
+
+ return a |
+ (ir << 16) |
+ (ig << 8) |
+ ib;
+ }
+
+ @Override
+ protected void drawBackground(float partialTicks, int mouseX, int mouseY) {
+ if (this.container==null) {
+ System.out.println("CONTAINER IS NULL.");
+ return;
+ }
+ WPanel root = this.container.getRootPanel();
+ if (root==null) return;
+
+ root.paintBackground(left, top);
+
+ //TODO: Change this to a label that lives in the rootPanel instead?
+ if (container instanceof Nameable) {
+ Text name = ((Nameable)container).getDisplayName();
+ font.draw(name.asFormattedString(), left, top, container.getTitleColor());
+ } else if (getTitle() != null) {
+ font.draw(getTitle().asFormattedString(), left, top, container.getTitleColor());
+ }
+ }
+
+ @Override
+ protected void drawForeground(int mouseX, int mouseY) {
+ //if (cursorDragSlots != null && this.container.getRootPanel() != null) {
+ if (this.container==null) {
+ System.out.println("CONTAINER IS NULL.");
+ return;
+ }
+
+ if (this.container.getRootPanel()!=null) {
+ this.container.getRootPanel().paintForeground(left, top, mouseX, mouseY);
+ }
+ //}
+ }
+
+ @Override
+ public void render(int mouseX, int mouseY, float partialTicks) {
+ // Render the background shadow
+ this.renderBackground();
+
+ this.drawBackground(partialTicks, mouseX, mouseY);
+
+ super.render(mouseX, mouseY, partialTicks);
+ drawMouseoverTooltip(mouseX, mouseY);
+ }
+
+}
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/ScreenDrawing.java b/src/main/java/io/github/cottonmc/cotton/gui/client/ScreenDrawing.java
new file mode 100644
index 0000000..5592e5d
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/client/ScreenDrawing.java
@@ -0,0 +1,235 @@
+package io.github.cottonmc.cotton.gui.client;
+
+import org.lwjgl.opengl.GL11;
+
+import com.mojang.blaze3d.platform.GlStateManager;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.render.BufferBuilder;
+import net.minecraft.client.render.Tessellator;
+import net.minecraft.client.render.VertexFormats;
+import net.minecraft.util.Identifier;
+
+public class ScreenDrawing {
+ private ScreenDrawing() {}
+
+ public static void rect(Identifier texture, int left, int top, int width, int height, int color) {
+ rect(texture, left, top, width, height, 0, 0, 1, 1, color, 0);
+ }
+
+ public static void rect(Identifier texture, int left, int top, int width, int height, float u1, float v1, float u2, float v2, int color) {
+ rect(texture, left, top, width, height, u1, v1, u2, v2, 0xFFFFFFFF, 0);
+ }
+
+ public static void rect(Identifier texture, int left, int top, int width, int height, float u1, float v1, float u2, float v2, int color, int z) {
+ MinecraftClient.getInstance().getTextureManager().bindTexture(texture);
+
+ //float scale = 0.00390625F;
+
+ if (width <= 0) width = 1;
+ if (height <= 0) height = 1;
+
+ float r = (color >> 16 & 255) / 255.0F;
+ float g = (color >> 8 & 255) / 255.0F;
+ float b = (color & 255) / 255.0F;
+ Tessellator tessellator = Tessellator.getInstance();
+ BufferBuilder buffer = tessellator.getBufferBuilder();
+ GlStateManager.enableBlend();
+ //GlStateManager.disableTexture2D();
+ GlStateManager.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
+ GlStateManager.color4f(r, g, b, 1.0f);
+ buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_UV); //I thought GL_QUADS was deprecated but okay, sure.
+ buffer.vertex(left, top + height, z).texture(u1, v2).next();
+ buffer.vertex(left + width, top + height, z).texture(u2, v2).next();
+ buffer.vertex(left + width, top, z).texture(u2, v1).next();
+ buffer.vertex(left, top, z).texture(u1, v1).next();
+ tessellator.draw();
+ //GlStateManager.enableTexture2D();
+ GlStateManager.disableBlend();
+ }
+
+ /**
+ * Draws an untextured rectangle of the specified RGB color.
+ */
+ public static void rect(int left, int top, int width, int height, int color) {
+ if (width <= 0) width = 1;
+ if (height <= 0) height = 1;
+
+ float a = (color >> 24 & 255) / 255.0F;
+ float r = (color >> 16 & 255) / 255.0F;
+ float g = (color >> 8 & 255) / 255.0F;
+ float b = (color & 255) / 255.0F;
+ Tessellator tessellator = Tessellator.getInstance();
+ BufferBuilder buffer = tessellator.getBufferBuilder();
+ GlStateManager.enableBlend();
+ GlStateManager.disableTexture();
+ GlStateManager.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
+ GlStateManager.color4f(r, g, b, a);
+ buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION); //I thought GL_QUADS was deprecated but okay, sure.
+ buffer.vertex(left, top + height, 0.0D).next();
+ buffer.vertex(left + width, top + height, 0.0D).next();
+ buffer.vertex(left + width, top, 0.0D).next();
+ buffer.vertex(left, top, 0.0D).next();
+ tessellator.draw();
+ GlStateManager.enableTexture();
+ GlStateManager.disableBlend();
+ }
+
+ public static void maskedRect(Identifier mask, Identifier texture, int left, int top, int width, int height) {
+
+
+ rect(mask, left, top, width, height, 0, 0, 1, 1, 0xFFFFFFFF, 7);
+
+ GlStateManager.enableDepthTest();
+ GlStateManager.depthFunc(GL11.GL_EQUAL);
+
+ rect(texture, left, top, width, height, 0, 0, 1, 1, 0xFFFFFFFF, 7);
+
+ GlStateManager.depthFunc(GL11.GL_LESS);
+ GlStateManager.disableDepthTest();
+ }
+
+ /**
+ * Draws a rectangle for a Fluid, because fluids are tough.
+ */
+ /*
+ public static void rect(Fluid fluid, int left, int top, int width, int height, float u1, float v1, float u2, float v2, int color) {
+ Identifier fluidTexture = fluid.getStill();
+
+ TextureAtlasSprite tas = Minecraft.getMinecraft().getTextureMapBlocks().getAtl