diff options
Diffstat (limited to 'src/main/java')
32 files changed, 1181 insertions, 169 deletions
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/GuiDescription.java b/src/main/java/io/github/cottonmc/cotton/gui/GuiDescription.java index 0c31912..cf1226b 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/GuiDescription.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/GuiDescription.java @@ -5,6 +5,7 @@ import javax.annotation.Nullable; import io.github.cottonmc.cotton.gui.impl.FocusHandler; import io.github.cottonmc.cotton.gui.widget.WPanel; import io.github.cottonmc.cotton.gui.widget.WWidget; +import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.screen.PropertyDelegate; @@ -23,8 +24,30 @@ public interface GuiDescription { public int getTitleColor(); public GuiDescription setRootPanel(WPanel panel); + + /** + * Sets the title color of this GUI. + * + * <p>The dark-mode title color will also be set by this method. + * If the specified color is {@link io.github.cottonmc.cotton.gui.widget.WLabel#DEFAULT_TEXT_COLOR}, + * the dark-mode color will be {@link io.github.cottonmc.cotton.gui.widget.WLabel#DEFAULT_DARKMODE_TEXT_COLOR}; + * otherwise it will be the specified color. + * + * @param color the new title color + * @return this GUI + */ public GuiDescription setTitleColor(int color); - + + /** + * Sets the light and dark title colors of this GUI. + * + * @param lightColor the light-mode color + * @param darkColor the dark-mode color + * @return this GUI + * @since 2.1.0 + */ + GuiDescription setTitleColor(int lightColor, int darkColor); + /** Sets the object which manages the integer properties used by WBars */ public GuiDescription setPropertyDelegate(PropertyDelegate delegate); @@ -61,4 +84,57 @@ public interface GuiDescription { default void cycleFocus(boolean lookForwards) { FocusHandler.cycleFocus(this, lookForwards); } + + /** + * Gets whether this GUI is fullscreen. + * + * <p>Fullscreen GUIs have no default background painter and + * have the root panel stretched to fit the entire screen on the client. + * + * @return true if this GUI is fullscreen, false otherwise + * @since 2.0.0 + */ + boolean isFullscreen(); + + /** + * Sets whether this GUI is fullscreen. + * + * @param fullscreen true if this GUI is fullscreen, false otherwise + * @since 2.0.0 + */ + void setFullscreen(boolean fullscreen); + + /** + * Gets whether the title of this GUI should be rendered by the screen. + * + * <p>Modders can disable this to render the title themselves with a widget. + * + * @return true if the title is visible, false otherwise + * @since 2.0.0 + */ + boolean isTitleVisible(); + + /** + * Sets whether the title of this GUI should be rendered by the screen. + * + * @param titleVisible true if the title is visible, false otherwise + * @since 2.0.0 + */ + void setTitleVisible(boolean titleVisible); + + /** + * Gets the horizontal alignment of the GUI title. + * + * @return the alignment + * @since 2.1.0 + */ + HorizontalAlignment getTitleAlignment(); + + /** + * Sets the horizontal alignment of the GUI title. + * + * @param alignment the new alignment + * @since 2.1.0 + */ + void setTitleAlignment(HorizontalAlignment alignment); } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/SyncedGuiDescription.java b/src/main/java/io/github/cottonmc/cotton/gui/SyncedGuiDescription.java index 5f2bef0..cd47af4 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/SyncedGuiDescription.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/SyncedGuiDescription.java @@ -1,12 +1,14 @@ package io.github.cottonmc.cotton.gui; import java.util.ArrayList; +import java.util.function.Supplier; import javax.annotation.Nullable; import io.github.cottonmc.cotton.gui.client.BackgroundPainter; import io.github.cottonmc.cotton.gui.client.LibGuiClient; import io.github.cottonmc.cotton.gui.widget.*; +import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.block.Block; @@ -16,6 +18,7 @@ import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.SimpleInventory; import net.minecraft.item.ItemStack; import net.minecraft.screen.*; import net.minecraft.screen.slot.Slot; @@ -35,19 +38,22 @@ public class SyncedGuiDescription extends ScreenHandler implements GuiDescriptio protected WPanel rootPanel = new WGridPanel(); protected int titleColor = WLabel.DEFAULT_TEXT_COLOR; protected int darkTitleColor = WLabel.DEFAULT_DARKMODE_TEXT_COLOR; - + protected boolean fullscreen = false; + protected boolean titleVisible = true; + protected HorizontalAlignment titleAlignment = HorizontalAlignment.LEFT; + protected WWidget focus; - - public SyncedGuiDescription(int syncId, PlayerInventory playerInventory) { - super(null, syncId); + + public SyncedGuiDescription(ScreenHandlerType<?> type, int syncId, PlayerInventory playerInventory) { + super(type, syncId); this.blockInventory = null; this.playerInventory = playerInventory; this.world = playerInventory.player.world; this.propertyDelegate = null;//new ArrayPropertyDelegate(1); } - public SyncedGuiDescription(int syncId, PlayerInventory playerInventory, Inventory blockInventory, PropertyDelegate propertyDelegate) { - super(null, syncId); + public SyncedGuiDescription(ScreenHandlerType<?> type, int syncId, PlayerInventory playerInventory, Inventory blockInventory, PropertyDelegate propertyDelegate) { + super(type, syncId); this.blockInventory = blockInventory; this.playerInventory = playerInventory; this.world = playerInventory.player.world; @@ -60,22 +66,31 @@ public class SyncedGuiDescription extends ScreenHandler implements GuiDescriptio } public int getTitleColor() { - return LibGuiClient.config.darkMode ? darkTitleColor : titleColor; + return (world.isClient && LibGuiClient.config.darkMode) ? darkTitleColor : titleColor; } public SyncedGuiDescription setRootPanel(WPanel panel) { this.rootPanel = panel; return this; } - + + @Override public SyncedGuiDescription setTitleColor(int color) { this.titleColor = color; + this.darkTitleColor = (color == WLabel.DEFAULT_TEXT_COLOR) ? WLabel.DEFAULT_DARKMODE_TEXT_COLOR : color; + return this; + } + + @Override + public SyncedGuiDescription setTitleColor(int lightColor, int darkColor) { + this.titleColor = lightColor; + this.darkTitleColor = darkColor; return this; } @Environment(EnvType.CLIENT) public void addPainters() { - if (this.rootPanel!=null) { + if (this.rootPanel!=null && !fullscreen) { this.rootPanel.setBackgroundPainter(BackgroundPainter.VANILLA); } } @@ -373,17 +388,43 @@ public class SyncedGuiDescription extends ScreenHandler implements GuiDescriptio * @return the found inventory */ public static Inventory getBlockInventory(ScreenHandlerContext ctx) { + return getBlockInventory(ctx, () -> EmptyInventory.INSTANCE); + } + + /** + * Gets the block inventory at the context. + * + * <p>If no inventory is found, returns a simple mutable inventory + * with the specified number of slots. + * + * <p>Searches for these implementations in the following order: + * <ol> + * <li>Blocks implementing {@code InventoryProvider}</li> + * <li>Block entities implementing {@code InventoryProvider}</li> + * <li>Block entities implementing {@code Inventory}</li> + * </ol> + * + * @param ctx the context + * @param size the fallback inventory size + * @return the found inventory + * @since 2.0.0 + */ + public static Inventory getBlockInventory(ScreenHandlerContext ctx, int size) { + return getBlockInventory(ctx, () -> new SimpleInventory(size)); + } + + private static Inventory getBlockInventory(ScreenHandlerContext ctx, Supplier<Inventory> fallback) { return ctx.run((world, pos) -> { BlockState state = world.getBlockState(pos); Block b = state.getBlock(); - + if (b instanceof InventoryProvider) { Inventory inventory = ((InventoryProvider)b).getInventory(state, world, pos); if (inventory != null) { return inventory; } } - + BlockEntity be = world.getBlockEntity(pos); if (be!=null) { if (be instanceof InventoryProvider) { @@ -395,9 +436,9 @@ public class SyncedGuiDescription extends ScreenHandler implements GuiDescriptio return (Inventory)be; } } - - return EmptyInventory.INSTANCE; - }).orElse(EmptyInventory.INSTANCE); + + return fallback.get(); + }).orElseGet(fallback); } /** @@ -420,6 +461,30 @@ public class SyncedGuiDescription extends ScreenHandler implements GuiDescriptio return new ArrayPropertyDelegate(0); }).orElse(new ArrayPropertyDelegate(0)); } + + /** + * Gets the property delegate at the context. + * + * <p>If no property delegate is found, returns an array property delegate + * with the specified number of properties. + * + * <p>Searches for block entities implementing {@link PropertyDelegateHolder}. + * + * @param ctx the context + * @param size the number of properties + * @return the found property delegate + * @since 2.0.0 + */ + public static PropertyDelegate getBlockPropertyDelegate(ScreenHandlerContext ctx, int size) { + return ctx.run((world, pos) -> { + BlockEntity be = world.getBlockEntity(pos); + if (be!=null && be instanceof PropertyDelegateHolder) { + return ((PropertyDelegateHolder)be).getPropertyDelegate(); + } + + return new ArrayPropertyDelegate(size); + }).orElse(new ArrayPropertyDelegate(size)); + } //extends ScreenHandler { @Override @@ -455,4 +520,34 @@ public class SyncedGuiDescription extends ScreenHandler implements GuiDescriptio widget.onFocusLost(); } } + + @Override + public boolean isFullscreen() { + return fullscreen; + } + + @Override + public void setFullscreen(boolean fullscreen) { + this.fullscreen = fullscreen; + } + + @Override + public boolean isTitleVisible() { + return titleVisible; + } + + @Override + public void setTitleVisible(boolean titleVisible) { + this.titleVisible = titleVisible; + } + + @Override + public HorizontalAlignment getTitleAlignment() { + return titleAlignment; + } + + @Override + public void setTitleAlignment(HorizontalAlignment titleAlignment) { + this.titleAlignment = titleAlignment; + } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java b/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java index 1e9ee3c..6e7db9c 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/ValidatedSlot.java @@ -7,11 +7,14 @@ import net.minecraft.screen.slot.Slot; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.function.Predicate; + public class ValidatedSlot extends Slot { private static final Logger LOGGER = LogManager.getLogger(); private final int slotNumber; private boolean insertingAllowed = true; private boolean takingAllowed = true; + private Predicate<ItemStack> filter; public ValidatedSlot(Inventory inventory, int index, int x, int y) { super(inventory, index, x, y); @@ -21,7 +24,7 @@ public class ValidatedSlot extends Slot { @Override public boolean canInsert(ItemStack stack) { - return insertingAllowed && inventory.isValid(slotNumber, stack); + return insertingAllowed && inventory.isValid(slotNumber, stack) && filter.test(stack); } @Override @@ -88,4 +91,24 @@ public class ValidatedSlot extends Slot { public void setTakingAllowed(boolean takingAllowed) { this.takingAllowed = takingAllowed; } + + /** + * Gets the item stack filter of this slot. + * + * @return the item filter + * @since 2.0.0 + */ + public Predicate<ItemStack> getFilter() { + return filter; + } + + /** + * Sets the item stack filter of this slot. + * + * @param filter the new item filter + * @since 2.0.0 + */ + public void setFilter(Predicate<ItemStack> filter) { + this.filter = filter; + } }
\ No newline at end of file diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java index 2229424..95702a6 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java @@ -10,14 +10,27 @@ import net.minecraft.text.Text; import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.widget.WPanel; import io.github.cottonmc.cotton.gui.widget.WWidget; +import org.lwjgl.opengl.GL11; public class CottonClientScreen extends Screen implements TextHoverRendererScreen { protected GuiDescription description; protected int left = 0; protected int top = 0; - protected int containerWidth = 0; - protected int containerHeight = 0; - + + /** + * The X coordinate of the screen title. + * + * @since 2.0.0 + */ + protected int titleX; + + /** + * The Y coordinate of the screen title. + * + * @since 2.0.0 + */ + protected int titleY; + protected WWidget lastResponder = null; public CottonClientScreen(GuiDescription description) { @@ -33,7 +46,6 @@ public class CottonClientScreen extends Screen implements TextHoverRendererScree public GuiDescription getDescription() { return description; } - @Override public void init(MinecraftClient client, int screenWidth, int screenHeight) { @@ -43,32 +55,53 @@ public class CottonClientScreen extends Screen implements TextHoverRendererScree description.addPainters(); reposition(screenWidth, screenHeight); } - - public void reposition(int screenWidth, int screenHeight) { + + /** + * Repositions the root panel. + * + * @param screenWidth the width of the screen + * @param screenHeight the height of the screen + */ + protected void reposition(int screenWidth, int screenHeight) { if (description!=null) { WPanel root = description.getRootPanel(); if (root!=null) { - this.containerWidth = root.getWidth(); - this.containerHeight = root.getHeight(); - - this.left = (screenWidth - root.getWidth()) / 2; - this.top = (screenHeight - root.getHeight()) / 2; + if (!description.isFullscreen()) { + this.left = (screenWidth - root.getWidth()) / 2; + this.top = (screenHeight - root.getHeight()) / 2; + this.titleX = 0; + this.titleY = 0; + } else { + this.left = 0; + this.top = 0; + + // Offset the title coordinates a little from the edge + this.titleX = 10; + this.titleY = 10; + + root.setSize(screenWidth, screenHeight); + } } } } - public void paint(MatrixStack matrices, int mouseX, int mouseY) { + private void paint(MatrixStack matrices, int mouseX, int mouseY) { super.renderBackground(matrices); if (description!=null) { WPanel root = description.getRootPanel(); if (root!=null) { + GL11.glEnable(GL11.GL_SCISSOR_TEST); + Scissors.refreshScissors(); root.paint(matrices, left, top, mouseX-left, mouseY-top); + GL11.glDisable(GL11.GL_SCISSOR_TEST); + Scissors.checkStackIsEmpty(); + } + + if (getTitle() != null && description.isTitleVisible()) { + int width = description.getRootPanel().getWidth(); + ScreenDrawing.drawString(matrices, getTitle(), description.getTitleAlignment(), left + titleX, top + titleY, width, description.getTitleColor()); } - } - - if (getTitle() != null) { - textRenderer.draw(matrices, getTitle(), left, top, description.getTitleColor()); } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java index 10ff4f6..4b7a726 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java @@ -13,6 +13,7 @@ import org.lwjgl.glfw.GLFW; import io.github.cottonmc.cotton.gui.widget.WPanel; import io.github.cottonmc.cotton.gui.widget.WWidget; +import org.lwjgl.opengl.GL11; /** * A screen for a {@link SyncedGuiDescription}. @@ -65,12 +66,26 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl description.addPainters(); - reposition(); + reposition(screenWidth, screenHeight); } - - public void reposition() { + + /** + * Clears the heavyweight peers of this screen's GUI description. + */ + private void clearPeers() { + description.slots.clear(); + } + + /** + * Repositions the root panel. + * + * @param screenWidth the width of the screen + * @param screenHeight the height of the screen + */ + protected void reposition(int screenWidth, int screenHeight) { WPanel basePanel = description.getRootPanel(); if (basePanel!=null) { + clearPeers(); basePanel.validate(description); backgroundWidth = basePanel.getWidth(); @@ -80,13 +95,24 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl if (backgroundWidth<16) backgroundWidth=300; if (backgroundHeight<16) backgroundHeight=300; } - x = (width / 2) - (backgroundWidth / 2); - y = (height / 2) - (backgroundHeight / 2); - } - - @Override - public void onClose() { - super.onClose(); + + if (!description.isFullscreen()) { + x = (width / 2) - (backgroundWidth / 2); + y = (height / 2) - (backgroundHeight / 2); + titleX = 0; + titleY = 0; + } else { + x = 0; + y = 0; + + // Offset the title coordinates a little from the edge + titleX = 10; + titleY = 10; + + if (basePanel != null) { + basePanel.setSize(screenWidth, screenHeight); + } + } } @Override @@ -211,13 +237,17 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl @Override protected void drawBackground(MatrixStack matrices, float partialTicks, int mouseX, int mouseY) {} //This is just an AbstractContainerScreen thing; most Screens don't work this way. - public void paint(MatrixStack matrices, int mouseX, int mouseY) { + private void paint(MatrixStack matrices, int mouseX, int mouseY) { super.renderBackground(matrices); if (description!=null) { WPanel root = description.getRootPanel(); if (root!=null) { + GL11.glEnable(GL11.GL_SCISSOR_TEST); + Scissors.refreshScissors(); root.paint(matrices, x, y, mouseX-x, mouseY-y); + GL11.glDisable(GL11.GL_SCISSOR_TEST); + Scissors.checkStackIsEmpty(); } } } @@ -243,8 +273,9 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl @Override protected void drawForeground(MatrixStack matrices, int mouseX, int mouseY) { - if (description != null) { - this.textRenderer.draw(matrices, this.title, (float) this.field_25267, (float) this.field_25268, description.getTitleColor()); + if (description != null && description.isTitleVisible()) { + int width = description.getRootPanel().getWidth(); + ScreenDrawing.drawString(matrices, getTitle(), description.getTitleAlignment(), titleX, titleY, width, description.getTitleColor()); } // Don't draw the player inventory label as it's drawn by the widget itself diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/LightweightGuiDescription.java b/src/main/java/io/github/cottonmc/cotton/gui/client/LightweightGuiDescription.java index 3eb6578..8a57551 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/LightweightGuiDescription.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/LightweightGuiDescription.java @@ -1,5 +1,6 @@ package io.github.cottonmc.cotton.gui.client; +import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment; import net.minecraft.screen.PropertyDelegate; import io.github.cottonmc.cotton.gui.GuiDescription; @@ -16,10 +17,14 @@ import javax.annotation.Nullable; */ public class LightweightGuiDescription implements GuiDescription { protected WPanel rootPanel = new WGridPanel(); - protected int titleColor = WLabel.DEFAULT_TEXT_COLOR; - protected int darkmodeTitleColor = WLabel.DEFAULT_DARKMODE_TEXT_COLOR; protected PropertyDelegate propertyDelegate; protected WWidget focus; + + protected int titleColor = WLabel.DEFAULT_TEXT_COLOR; + protected int darkmodeTitleColor = WLabel.DEFAULT_DARKMODE_TEXT_COLOR; + protected boolean fullscreen = false; + protected boolean titleVisible = true; + protected HorizontalAlignment titleAlignment = HorizontalAlignment.LEFT; @Override public WPanel getRootPanel() { @@ -40,12 +45,20 @@ public class LightweightGuiDescription implements GuiDescription { @Override public GuiDescription setTitleColor(int color) { this.titleColor = color; + this.darkmodeTitleColor = (color == WLabel.DEFAULT_TEXT_COLOR) ? WLabel.DEFAULT_DARKMODE_TEXT_COLOR : color; + return this; + } + + @Override + public GuiDescription setTitleColor(int lightColor, int darkColor) { + this.titleColor = lightColor; + this.darkmodeTitleColor = darkColor; return this; } @Override public void addPainters() { - if (this.rootPanel!=null) { + if (this.rootPanel!=null && !fullscreen) { this.rootPanel.setBackgroundPainter(BackgroundPainter.VANILLA); } } @@ -94,4 +107,34 @@ public class LightweightGuiDescription implements GuiDescription { widget.onFocusLost(); } } + + @Override + public boolean isFullscreen() { + return fullscreen; + } + + @Override + public void setFullscreen(boolean fullscreen) { + this.fullscreen = fullscreen; + } + + @Override + public boolean isTitleVisible() { + return titleVisible; + } + + @Override + public void setTitleVisible(boolean titleVisible) { + this.titleVisible = titleVisible; + } + + @Override + public HorizontalAlignment getTitleAlignment() { + return titleAlignment; + } + + @Override + public void setTitleAlignment(HorizontalAlignment titleAlignment) { + this.titleAlignment = titleAlignment; + } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/Scissors.java b/src/main/java/io/github/cottonmc/cotton/gui/client/Scissors.java new file mode 100644 index 0000000..4998a05 --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/Scissors.java @@ -0,0 +1,147 @@ +package io.github.cottonmc.cotton.gui.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import org.lwjgl.opengl.GL11; + +import java.util.ArrayDeque; +import java.util.stream.Collectors; + +/** + * Contains a stack for GL scissors for restricting the drawn area of a widget. + * + * @since 2.0.0 + */ +@Environment(EnvType.CLIENT) +public final class Scissors { + private static final ArrayDeque<Frame> STACK = new ArrayDeque<>(); + + private Scissors() { + } + + /** + * Pushes a new scissor frame onto the stack and refreshes the scissored area. + * + * @param x the frame's X coordinate + * @param y the frame's Y coordinate + * @param width the frame's width in pixels + * @param height the frame's height in pixels + * @return the pushed frame + */ + public static Frame push(int x, int y, int width, int height) { + Frame frame = new Frame(x, y, width, height); + STACK.push(frame); + refreshScissors(); + + return frame; + } + + /** + * Pops the topmost scissor frame and refreshes the scissored area. + * + * @throws IllegalStateException if there are no scissor frames on the stack + */ + public static void pop() { + if (STACK.isEmpty()) { + throw new IllegalStateException("No scissors on the stack!"); + } + + STACK.pop(); + refreshScissors(); + } + + static void refreshScissors() { + MinecraftClient mc = Minecraf |
