diff --git a/src/main/java/com/trunksbomb/trade/client/TradeScreen.java b/src/main/java/com/trunksbomb/trade/client/TradeScreen.java index da56a5e..b08eab5 100644 --- a/src/main/java/com/trunksbomb/trade/client/TradeScreen.java +++ b/src/main/java/com/trunksbomb/trade/client/TradeScreen.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.UUID; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; @@ -47,7 +48,11 @@ public class TradeScreen extends AbstractContainerScreen private static final int DEBUG_BUTTON_HEIGHT = 20; private static final int DEBUG_BUTTON_GAP = 4; private static final int DEBUG_SMALL_BUTTON_WIDTH = 41; + private static final int AMOUNT_PROMPT_WIDTH = 90; + private static final int AMOUNT_PROMPT_HEIGHT = 44; private ContextMenu contextMenu; + private EditBox amountInput; + private AmountPrompt amountPrompt; public TradeScreen(TradeMenu menu, Inventory inventory, Component title) { super(menu, inventory, title); @@ -70,6 +75,12 @@ public class TradeScreen extends AbstractContainerScreen super.init(); titleLabelX = BANNER_X; titleLabelY = BANNER_Y; + amountInput = new EditBox(font, leftPos + 0, topPos + 0, 54, 14, Component.literal("Trade Amount")); + amountInput.setVisible(false); + amountInput.setCanLoseFocus(false); + amountInput.setMaxLength(3); + amountInput.setFilter(value -> value.isEmpty() || value.chars().allMatch(Character::isDigit)); + addRenderableWidget(amountInput); addRenderableWidget(Button.builder(Component.literal("Accept"), button -> sendAction(TradeAction.ACCEPT, -1, 1)) .bounds(leftPos + CENTER_COLUMN_X + 3, topPos + ACCEPT_BUTTON_Y, ACTION_BUTTON_WIDTH, ACTION_BUTTON_HEIGHT) @@ -110,6 +121,19 @@ public class TradeScreen extends AbstractContainerScreen @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (amountPrompt != null) { + if (keyCode == 256) { + closeAmountPrompt(); + return true; + } + if (keyCode == 257 || keyCode == 335) { + submitAmountPrompt(); + return true; + } + if (amountInput != null && amountInput.keyPressed(keyCode, scanCode, modifiers)) { + return true; + } + } if (minecraft != null && minecraft.options.keyInventory.matches(keyCode, scanCode)) { onClose(); return true; @@ -130,6 +154,17 @@ public class TradeScreen extends AbstractContainerScreen @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (amountPrompt != null) { + if (isInsideAmountPrompt(mouseX, mouseY)) { + if (amountInput != null && amountInput.mouseClicked(mouseX, mouseY, button)) { + return true; + } + return true; + } + closeAmountPrompt(); + return true; + } + if (contextMenu != null) { if (handleContextMenuClick(mouseX, mouseY)) { return true; @@ -197,6 +232,7 @@ public class TradeScreen extends AbstractContainerScreen public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { super.render(guiGraphics, mouseX, mouseY, partialTick); renderContextMenu(guiGraphics, mouseX, mouseY); + renderAmountPrompt(guiGraphics); renderTooltip(guiGraphics, mouseX, mouseY); } @@ -274,9 +310,7 @@ public class TradeScreen extends AbstractContainerScreen ContextOption option = contextMenu.options().get(index); if (option.amount() < 0) { - if (minecraft != null && minecraft.player != null) { - minecraft.player.displayClientMessage(Component.literal(option.label() + " is not implemented yet."), true); - } + openAmountPrompt(contextMenu.action(), contextMenu.slot(), contextMenu.mouseX(), contextMenu.mouseY()); } else { sendAction(contextMenu.action(), contextMenu.slot(), option.amount()); } @@ -314,6 +348,84 @@ public class TradeScreen extends AbstractContainerScreen pose.popPose(); } + private void openAmountPrompt(TradeAction action, int slot, int mouseX, int mouseY) { + amountPrompt = new AmountPrompt(action, slot, mouseX, mouseY); + if (amountInput != null) { + amountInput.setValue(""); + amountInput.setFocused(true); + amountInput.setVisible(true); + setFocused(amountInput); + } + } + + private void submitAmountPrompt() { + if (amountPrompt == null || amountInput == null) { + return; + } + + String value = amountInput.getValue().trim(); + if (value.isEmpty()) { + return; + } + + try { + int amount = Integer.parseInt(value); + if (amount > 0) { + sendAction(amountPrompt.action(), amountPrompt.slot(), amount); + closeAmountPrompt(); + } + } catch (NumberFormatException ignored) { + // Filter should prevent this, but ignore invalid input safely. + } + } + + private void closeAmountPrompt() { + amountPrompt = null; + if (amountInput != null) { + amountInput.setValue(""); + amountInput.setFocused(false); + amountInput.setVisible(false); + } + setFocused(null); + } + + private void renderAmountPrompt(GuiGraphics guiGraphics) { + if (amountPrompt == null || amountInput == null) { + return; + } + + var pose = guiGraphics.pose(); + pose.pushPose(); + pose.translate(0.0F, 0.0F, 450.0F); + + int promptX = amountPromptX(); + int promptY = amountPromptY(); + guiGraphics.fill(promptX, promptY, promptX + AMOUNT_PROMPT_WIDTH, promptY + AMOUNT_PROMPT_HEIGHT, 0xF0101010); + guiGraphics.fill(promptX + 1, promptY + 1, promptX + AMOUNT_PROMPT_WIDTH - 1, promptY + AMOUNT_PROMPT_HEIGHT - 1, 0xFF2B2B2B); + guiGraphics.drawString(font, amountPrompt.action() == TradeAction.ADD_ITEM ? "Trade amount" : "Remove amount", promptX + 6, promptY + 6, 0xFFFFFF, false); + guiGraphics.drawString(font, "Enter to confirm", promptX + 6, promptY + 30, 0xAAAAAA, false); + + amountInput.setPosition(promptX + 6, promptY + 16); + amountInput.render(guiGraphics, 0, 0, 0.0F); + pose.popPose(); + } + + private boolean isInsideAmountPrompt(double mouseX, double mouseY) { + int promptX = amountPromptX(); + int promptY = amountPromptY(); + return mouseX >= promptX && mouseX < promptX + AMOUNT_PROMPT_WIDTH && mouseY >= promptY && mouseY < promptY + AMOUNT_PROMPT_HEIGHT; + } + + private int amountPromptX() { + int preferredX = amountPrompt == null ? leftPos + 8 : Math.min(amountPrompt.mouseX(), this.width - AMOUNT_PROMPT_WIDTH - 4); + return Math.max(4, preferredX); + } + + private int amountPromptY() { + int preferredY = amountPrompt == null ? topPos + 8 : Math.min(amountPrompt.mouseY(), this.height - AMOUNT_PROMPT_HEIGHT - 4); + return Math.max(4, preferredY); + } + private int contextMenuX() { int width = contextMenuWidth(); return Math.min(contextMenu.mouseX(), this.width - width - 4); @@ -349,6 +461,8 @@ public class TradeScreen extends AbstractContainerScreen private record ContextOption(String label, int amount) {} + private record AmountPrompt(TradeAction action, int slot, int mouseX, int mouseY) {} + public static class TradeMenu extends AbstractContainerMenu { private static final int OFFER_COLUMNS = 6; private static final int OFFER_ROWS = 6;