Improvements to the usability of the number entry widgets (Priority, Level Emitter, Craft Amount): (#4737)
- Fixed tab order - Introduced focus state for corner buttons - Allowed the player to enter any text, but add validation that only persist it if it is a valid number - Enter will now confirm these dialogs and return to the previous dialog - The text field is automatically focused and its contents are selected
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package appeng.container.implementations;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
@@ -11,8 +13,6 @@ import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.network.NetworkHooks;
|
||||
|
||||
import appeng.api.config.SecurityPermissions;
|
||||
@@ -43,6 +43,8 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
|
||||
private final SecurityPermissions requiredPermission;
|
||||
|
||||
private Function<I, ITextComponent> containerTitleStrategy = this::getDefaultContainerTitle;
|
||||
|
||||
public ContainerHelper(ContainerFactory<C, I> factory, Class<I> interfaceClass) {
|
||||
this(factory, interfaceClass, null);
|
||||
}
|
||||
@@ -54,19 +56,47 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a custom strategy for obtaining a custom container name.
|
||||
*
|
||||
* The stratgy should return {@link StringTextComponent#EMPTY} if there's no
|
||||
* custom name.
|
||||
*/
|
||||
public ContainerHelper<C, I> withContainerTitle(Function<I, ITextComponent> containerTitleStrategy) {
|
||||
this.containerTitleStrategy = containerTitleStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a container that is based around a single tile entity. The tile
|
||||
* entity's position is encoded in the packet buffer.
|
||||
*/
|
||||
public C fromNetwork(int windowId, PlayerInventory inv, PacketBuffer packetBuf) {
|
||||
return fromNetwork(windowId, inv, packetBuf, (accessObj, container, buffer) -> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #open}, but allows or additional data to be read from the
|
||||
* packet, and passed onto the container.
|
||||
*/
|
||||
public C fromNetwork(int windowId, PlayerInventory inv, PacketBuffer packetBuf,
|
||||
InitialDataDeserializer<C, I> initialDataDeserializer) {
|
||||
I host = getHostFromLocator(inv.player, ContainerLocator.read(packetBuf));
|
||||
if (host != null) {
|
||||
return factory.create(windowId, inv, host);
|
||||
C container = factory.create(windowId, inv, host);
|
||||
initialDataDeserializer.deserializeInitialData(host, container, packetBuf);
|
||||
return container;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean open(PlayerEntity player, ContainerLocator locator) {
|
||||
return open(player, locator, (accessObj, buffer) -> {
|
||||
});
|
||||
}
|
||||
|
||||
public boolean open(PlayerEntity player, ContainerLocator locator, InitialDataSerializer<I> initialDataSerializer) {
|
||||
if (!(player instanceof ServerPlayerEntity)) {
|
||||
// Cannot open containers on the client or for non-players
|
||||
// FIXME logging?
|
||||
@@ -83,7 +113,7 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
return false;
|
||||
}
|
||||
|
||||
ITextComponent title = findContainerTitle(player.world, locator, accessInterface);
|
||||
ITextComponent title = containerTitleStrategy.apply(accessInterface);
|
||||
|
||||
INamedContainerProvider container = new SimpleNamedContainerProvider((wnd, p, pl) -> {
|
||||
C c = factory.create(wnd, p, accessInterface);
|
||||
@@ -92,34 +122,14 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
c.setLocator(locator);
|
||||
return c;
|
||||
}, title);
|
||||
NetworkHooks.openGui((ServerPlayerEntity) player, container, locator::write);
|
||||
NetworkHooks.openGui((ServerPlayerEntity) player, container, buffer -> {
|
||||
locator.write(buffer);
|
||||
initialDataSerializer.serializeInitialData(accessInterface, buffer);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ITextComponent findContainerTitle(World world, ContainerLocator locator, I accessInterface) {
|
||||
|
||||
if (accessInterface instanceof ICustomNameObject) {
|
||||
ICustomNameObject customNameObject = (ICustomNameObject) accessInterface;
|
||||
if (customNameObject.hasCustomInventoryName()) {
|
||||
return customNameObject.getCustomInventoryName();
|
||||
}
|
||||
}
|
||||
|
||||
// Use block name at position
|
||||
// FIXME: this is not right, we'd need to check the part's item stack, or custom
|
||||
// naming interface impl
|
||||
// FIXME: Should move this up, because at this point, it's hard to know where
|
||||
// the terminal host came from (part or tile)
|
||||
if (locator.hasBlockPos()) {
|
||||
return new TranslationTextComponent(
|
||||
world.getBlockState(locator.getBlockPos()).getBlock().getTranslationKey());
|
||||
}
|
||||
|
||||
return new StringTextComponent("Unknown");
|
||||
|
||||
}
|
||||
|
||||
private I getHostFromLocator(PlayerEntity player, ContainerLocator locator) {
|
||||
if (locator.hasItemIndex()) {
|
||||
return getHostFromPlayerInventory(player, locator);
|
||||
@@ -196,6 +206,24 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
C create(int windowId, PlayerInventory playerInv, I accessObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy used to serialize initial data for opening the container on the
|
||||
* client-side into the packet that is sent to the client.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface InitialDataSerializer<I> {
|
||||
void serializeInitialData(I host, PacketBuffer buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy used to deserialize initial data for opening the container on the
|
||||
* client-side from the packet received by the server.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface InitialDataDeserializer<C, I> {
|
||||
void deserializeInitialData(I host, C container, PacketBuffer buffer);
|
||||
}
|
||||
|
||||
private boolean checkPermission(PlayerEntity player, Object accessInterface) {
|
||||
|
||||
if (requiredPermission != null) {
|
||||
@@ -206,4 +234,15 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
|
||||
}
|
||||
|
||||
private ITextComponent getDefaultContainerTitle(I accessInterface) {
|
||||
if (accessInterface instanceof ICustomNameObject) {
|
||||
ICustomNameObject customNameObject = (ICustomNameObject) accessInterface;
|
||||
if (customNameObject.hasCustomInventoryName()) {
|
||||
return customNameObject.getCustomInventoryName();
|
||||
}
|
||||
}
|
||||
|
||||
return StringTextComponent.EMPTY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user