UI to toggle off computer, toggle full input capturing.

This commit is contained in:
Florian Nücke
2020-12-28 17:53:45 +01:00
parent ff08e2fb9b
commit e2610e148f
15 changed files with 304 additions and 42 deletions

View File

@@ -29,6 +29,10 @@ public final class Constants {
///////////////////////////////////////////////////////////////////
public static final String COMPUTER_SCREEN_CAPTURE_INPUT_CAPTION = "gui.oc2.computer.capture_input.capt";
public static final String COMPUTER_SCREEN_CAPTURE_INPUT_DESCRIPTION = "gui.oc2.computer.capture_input.desc";
public static final String COMPUTER_SCREEN_POWER_CAPTION = "gui.oc2.computer.power.capt";
public static final String COMPUTER_SCREEN_POWER_DESCRIPTION = "gui.oc2.computer.power.desc";
public static final String COMPUTER_BOOT_ERROR_UNKNOWN = "gui.oc2.computer.boot_error.unknown";
public static final String COMPUTER_BOOT_ERROR_NO_MEMORY = "gui.oc2.computer.boot_error.no_memory";
public static final String COMPUTER_BUS_STATE_INCOMPLETE = "gui.oc2.computer.bus_state.incomplete";

View File

@@ -2,15 +2,21 @@ package li.cil.oc2.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import li.cil.oc2.Constants;
import li.cil.oc2.api.API;
import li.cil.oc2.client.gui.terminal.TerminalInput;
import li.cil.oc2.client.gui.widget.Sprite;
import li.cil.oc2.client.gui.widget.ToggleImageButton;
import li.cil.oc2.common.block.entity.ComputerTileEntity;
import li.cil.oc2.common.network.Network;
import li.cil.oc2.common.network.message.ComputerPowerMessage;
import li.cil.oc2.common.network.message.TerminalBlockInputMessage;
import li.cil.oc2.common.vm.Terminal;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import org.lwjgl.glfw.GLFW;
import java.nio.ByteBuffer;
@@ -21,13 +27,24 @@ public final class TerminalScreen extends Screen {
private static final ResourceLocation BACKGROUND = new ResourceLocation(API.MOD_ID, "textures/gui/screen/terminal.png");
private static final ResourceLocation BACKGROUND_TERMINAL_FOCUSED = new ResourceLocation(API.MOD_ID, "textures/gui/screen/terminal_focused.png");
private static final int TEXTURE_SIZE = 512;
private static final int SCREEN_WIDTH = 8 + 80 * 8 / 2 + 8;
private static final int SCREEN_HEIGHT = 8 + 24 * 16 / 2 + 8;
private static final Sprite CAPTURE_INPUT_BASE = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 15, 241);
private static final Sprite CAPTURE_INPUT_PRESSED = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 29, 241);
private static final Sprite CAPTURE_INPUT_ACTIVE = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 1, 241);
private static final Sprite POWER_BASE = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 15, 255);
private static final Sprite POWER_PRESSED = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 29, 255);
private static final Sprite POWER_ACTIVE = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 1, 255);
private static final int SCREEN_WIDTH = 336;
private static final int SCREEN_HEIGHT = 228;
private static final int TERMINAL_AREA_X = 8;
private static final int TERMINAL_AREA_Y = 8;
private static final int TERMINAL_AREA_WIDTH = 80 * 8 / 2;
private static final int TERMINAL_AREA_HEIGHT = 24 * 16 / 2;
private static boolean enableInputCapture;
///////////////////////////////////////////////////////////////////
private final ComputerTileEntity tileEntity;
@@ -55,7 +72,7 @@ public final class TerminalScreen extends Screen {
requireNonNull(minecraft).getTextureManager().bindTexture(BACKGROUND);
blit(matrixStack, windowLeft, windowTop, 0, 0, windowWidth, windowHeight, TEXTURE_SIZE, TEXTURE_SIZE);
if (isMouseOverTerminal && tileEntity.isRunning()) {
if (shouldCaptureInput()) {
requireNonNull(minecraft).getTextureManager().bindTexture(BACKGROUND_TERMINAL_FOCUSED);
blit(matrixStack, windowLeft, windowTop, 0, 0, windowWidth, windowHeight, TEXTURE_SIZE, TEXTURE_SIZE);
}
@@ -67,6 +84,18 @@ public final class TerminalScreen extends Screen {
stack.translate(windowLeft + TERMINAL_AREA_X, windowTop + TERMINAL_AREA_Y, this.itemRenderer.zLevel);
stack.scale(TERMINAL_AREA_WIDTH / (float) terminal.getWidth(), TERMINAL_AREA_HEIGHT / (float) terminal.getHeight(), 1f);
terminal.render(stack);
} else {
final ITextComponent bootError = tileEntity.getBootError();
if (bootError != null) {
final int textWidth = font.getStringPropertyWidth(bootError);
final int textOffsetX = (TERMINAL_AREA_WIDTH - textWidth) / 2;
final int textOffsetY = (TERMINAL_AREA_HEIGHT - font.FONT_HEIGHT) / 2;
font.func_243246_a(matrixStack,
bootError,
windowLeft + TERMINAL_AREA_X + textOffsetX,
windowTop + TERMINAL_AREA_Y + textOffsetY,
0xEE3322);
}
}
}
@@ -79,7 +108,10 @@ public final class TerminalScreen extends Screen {
Network.INSTANCE.sendToServer(new TerminalBlockInputMessage(tileEntity, input));
}
if (!tileEntity.isRunning()) {
assert minecraft != null;
final ClientPlayerEntity player = minecraft.player;
assert player != null;
if (!player.isAlive() || !tileEntity.getPos().withinDistance(player.getPositionVec(), 8)) {
closeScreen();
}
}
@@ -97,7 +129,7 @@ public final class TerminalScreen extends Screen {
@Override
public boolean keyPressed(final int keyCode, final int scanCode, final int modifiers) {
if ((!isMouseOverTerminal || !tileEntity.isRunning()) && keyCode == GLFW.GLFW_KEY_ESCAPE) {
if (!shouldCaptureInput() && keyCode == GLFW.GLFW_KEY_ESCAPE) {
return super.keyPressed(keyCode, scanCode, modifiers);
}
@@ -135,6 +167,53 @@ public final class TerminalScreen extends Screen {
this.windowTop = (this.height - this.windowHeight) / 2;
requireNonNull(minecraft).keyboardListener.enableRepeatEvents(true);
addButton(new ToggleImageButton(
this, windowLeft + 104, windowTop + 212, 12, 12,
new TranslationTextComponent(Constants.COMPUTER_SCREEN_CAPTURE_INPUT_CAPTION),
new TranslationTextComponent(Constants.COMPUTER_SCREEN_CAPTURE_INPUT_DESCRIPTION),
CAPTURE_INPUT_BASE,
CAPTURE_INPUT_PRESSED,
CAPTURE_INPUT_ACTIVE
) {
@Override
public void onPress() {
super.onPress();
enableInputCapture = !enableInputCapture;
}
@Override
public boolean isToggled() {
return enableInputCapture;
}
});
addButton(new ToggleImageButton(
this, windowLeft + 220, windowTop + 212, 12, 12,
new TranslationTextComponent(Constants.COMPUTER_SCREEN_POWER_CAPTION),
new TranslationTextComponent(Constants.COMPUTER_SCREEN_POWER_DESCRIPTION),
POWER_BASE,
POWER_PRESSED,
POWER_ACTIVE
) {
@Override
public void onPress() {
super.onPress();
final ComputerPowerMessage message = new ComputerPowerMessage(tileEntity, !tileEntity.isRunning());
Network.INSTANCE.sendToServer(message);
}
@Override
public boolean isToggled() {
return tileEntity.isRunning();
}
});
}
///////////////////////////////////////////////////////////////////
private boolean shouldCaptureInput() {
return isMouseOverTerminal && enableInputCapture && tileEntity.isRunning();
}
private boolean isPointInRegion(final int x, final int y, final int width, final int height, double mouseX, double mouseY) {

View File

@@ -0,0 +1,27 @@
package li.cil.oc2.client.gui.widget;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.AbstractGui;
import net.minecraft.util.ResourceLocation;
public final class Sprite extends AbstractGui {
public final ResourceLocation image;
public final int textureSize;
public final int width, height;
public final int u0, v0;
public Sprite(final ResourceLocation atlas, final int textureSize, final int width, final int height, final int u0, final int v0) {
this.image = atlas;
this.textureSize = textureSize;
this.width = width;
this.height = height;
this.u0 = u0;
this.v0 = v0;
}
public void draw(final MatrixStack stack, final int x, final int y) {
Minecraft.getInstance().getTextureManager().bindTexture(image);
blit(stack, x, y, u0, v0, width, height, textureSize, textureSize);
}
}

View File

@@ -0,0 +1,89 @@
package li.cil.oc2.client.gui.widget;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.button.AbstractButton;
import net.minecraft.util.text.Color;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.fml.client.gui.GuiUtils;
import java.util.Arrays;
import java.util.List;
public abstract class ToggleImageButton extends AbstractButton {
private static final long PRESS_DURATION = 200;
private static final long TOOLTIP_DELAY = 250;
private final Screen parent;
private final List<? extends ITextComponent> tooltip;
private final Sprite baseImage;
private final Sprite pressedImage;
private final Sprite activeImage;
private boolean isToggled;
private long lastPressedAt;
private long hoveringStartedAt;
public ToggleImageButton(
final Screen parent,
final int x, final int y,
final int width, final int height,
final ITextComponent caption,
final ITextComponent description,
final Sprite baseImage,
final Sprite pressedImage,
final Sprite activeImage) {
super(x, y, width, height, caption);
this.parent = parent;
this.tooltip = Arrays.asList(caption, new StringTextComponent("").modifyStyle(style -> style.setColor(Color.fromTextFormatting(TextFormatting.GRAY))).append(description));
this.baseImage = baseImage;
this.pressedImage = pressedImage;
this.activeImage = activeImage;
}
@Override
public void onPress() {
lastPressedAt = System.currentTimeMillis();
}
public boolean isToggled() {
return isToggled;
}
public void setToggled(final boolean value) {
isToggled = value;
}
@Override
public void renderButton(final MatrixStack stack, final int mouseX, final int mouseY, final float partialTicks) {
Sprite background = baseImage;
if ((System.currentTimeMillis() - lastPressedAt) < PRESS_DURATION) {
background = pressedImage;
}
background.draw(stack, x, y);
if (isToggled()) {
activeImage.draw(stack, x, y);
}
if (isHovered()) {
if (hoveringStartedAt == 0) {
hoveringStartedAt = System.currentTimeMillis();
}
if ((System.currentTimeMillis() - hoveringStartedAt) > TOOLTIP_DELAY) {
renderToolTip(stack, mouseX, mouseY);
}
} else {
hoveringStartedAt = 0;
}
}
@Override
public void renderToolTip(final MatrixStack stack, final int mouseX, final int mouseY) {
GuiUtils.drawHoveringText(stack, tooltip, mouseX, mouseY, parent.width, parent.height, 200, Minecraft.getInstance().fontRenderer);
}
}

View File

@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package li.cil.oc2.client.gui.widget;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -3,7 +3,6 @@ package li.cil.oc2.client.render.tile;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import li.cil.oc2.Constants;
import li.cil.oc2.api.API;
import li.cil.oc2.client.render.OpenComputersRenderType;
import li.cil.oc2.common.block.ComputerBlock;
@@ -24,7 +23,6 @@ import net.minecraft.util.math.vector.Vector3f;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.ITextProperties;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@@ -174,32 +172,15 @@ public final class ComputerTileEntityRenderer extends TileEntityRenderer<Compute
return;
}
final ITextComponent bootError = tileEntity.getBootError();
if (bootError == null) {
return;
}
stack.push();
stack.translate(3, 3, -0.9f);
switch (tileEntity.getBusState()) {
case SCAN_PENDING:
case INCOMPLETE:
drawText(stack, new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_INCOMPLETE));
break;
case TOO_COMPLEX:
drawText(stack, new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_TOO_COMPLEX));
break;
case MULTIPLE_CONTROLLERS:
drawText(stack, new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_MULTIPLE_CONTROLLERS));
break;
case READY:
switch (tileEntity.getRunState()) {
case STOPPED:
case LOADING_DEVICES:
final ITextComponent bootError = tileEntity.getBootError();
if (bootError != null) {
drawText(stack, bootError);
}
break;
}
break;
}
drawText(stack, bootError);
stack.pop();
}

View File

@@ -91,14 +91,8 @@ public final class ComputerBlock extends HorizontalBlock {
} else {
if (player.isSneaking()) {
computer.start();
} else {
if (computer.isRunning()) {
if (world.isRemote()) {
openTerminalScreen(computer);
}
} else {
computer.start();
}
} else if (world.isRemote()) {
openTerminalScreen(computer);
}
}

View File

@@ -202,7 +202,23 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
@Nullable
public ITextComponent getBootError() {
return bootError;
switch (getBusState()) {
case SCAN_PENDING:
case INCOMPLETE:
return new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_INCOMPLETE);
case TOO_COMPLEX:
return new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_TOO_COMPLEX);
case MULTIPLE_CONTROLLERS:
return new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_MULTIPLE_CONTROLLERS);
case READY:
switch (getRunState()) {
case STOPPED:
case LOADING_DEVICES:
return bootError;
}
break;
}
return null;
}
public void handleNeighborChanged() {
@@ -225,6 +241,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
}
}
@OnlyIn(Dist.CLIENT)
public void setBootErrorClient(final ITextComponent value) {
final World world = getWorld();
if (world != null && world.isRemote()) {

View File

@@ -55,6 +55,12 @@ public final class Network {
.decoder(ComputerBootErrorMessage::new)
.consumer(ComputerBootErrorMessage::handleMessage)
.add();
INSTANCE.messageBuilder(ComputerPowerMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_SERVER)
.encoder(ComputerPowerMessage::toBytes)
.decoder(ComputerPowerMessage::new)
.consumer(ComputerPowerMessage::handleMessage)
.add();
}
public static <T> void sendToClientsTrackingChunk(final T message, final Chunk chunk) {

View File

@@ -9,7 +9,7 @@ import net.minecraftforge.fml.network.NetworkEvent;
import java.util.function.Supplier;
public class ComputerBootErrorMessage {
public final class ComputerBootErrorMessage {
private BlockPos pos;
private ITextComponent value;

View File

@@ -9,7 +9,7 @@ import net.minecraftforge.fml.network.NetworkEvent;
import java.util.function.Supplier;
public class ComputerBusStateMessage {
public final class ComputerBusStateMessage {
private BlockPos pos;
private AbstractDeviceBusController.BusState busState;

View File

@@ -0,0 +1,53 @@
package li.cil.oc2.common.network.message;
import li.cil.oc2.common.block.entity.ComputerTileEntity;
import li.cil.oc2.common.network.MessageUtils;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.fml.network.NetworkEvent;
import java.util.function.Supplier;
public final class ComputerPowerMessage {
private BlockPos pos;
private boolean power;
///////////////////////////////////////////////////////////////////
public ComputerPowerMessage(final ComputerTileEntity computer, final boolean power) {
this.pos = computer.getPos();
this.power = power;
}
public ComputerPowerMessage(final PacketBuffer buffer) {
fromBytes(buffer);
}
///////////////////////////////////////////////////////////////////
public static boolean handleMessage(final ComputerPowerMessage message, final Supplier<NetworkEvent.Context> context) {
context.get().enqueueWork(() -> MessageUtils.withServerTileEntityAt(context, message.pos, ComputerTileEntity.class,
(computer) -> {
final ServerPlayerEntity player = context.get().getSender();
if (player != null && computer.getPos().withinDistance(player.getPositionVec(), 8)) {
if (message.power) {
computer.start();
} else {
computer.stop();
}
}
}));
return true;
}
public void fromBytes(final PacketBuffer buffer) {
pos = buffer.readBlockPos();
power = buffer.readBoolean();
}
public static void toBytes(final ComputerPowerMessage message, final PacketBuffer buffer) {
buffer.writeBlockPos(message.pos);
buffer.writeBoolean(message.power);
}
}

View File

@@ -8,7 +8,7 @@ import net.minecraftforge.fml.network.NetworkEvent;
import java.util.function.Supplier;
public class ComputerRunStateMessage {
public final class ComputerRunStateMessage {
private BlockPos pos;
private ComputerTileEntity.RunState runState;

View File

@@ -19,6 +19,11 @@
"gui.oc2.computer.bus_state.too_complex": "Bus Too Complex",
"gui.oc2.computer.bus_state.multiple_controllers": "Multiple Bus Controllers",
"gui.oc2.computer.capture_input.capt": "Capture Input",
"gui.oc2.computer.capture_input.desc": "When enabled, as long as the mouse cursor is hovering the terminal contents, all input will be captured, including the escape key.",
"gui.oc2.computer.power.capt": "Toggle Power",
"gui.oc2.computer.power.desc": "Toggles the power state of the computer.",
"gui.oc2.device_type.eeprom": "EEPROM",
"gui.oc2.device_type.memory": "Memory",
"gui.oc2.device_type.hard_drive": "Hard Drive",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB