Fix NBT editor paste and delete shortcuts
This commit is contained in:
parent
30f4dc4933
commit
a45c574595
3 changed files with 120 additions and 11 deletions
|
|
@ -0,0 +1,52 @@
|
|||
package dev.quorate.client.nbt;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
|
||||
public final class ClipboardParser extends TagParser {
|
||||
private final StringReader input;
|
||||
|
||||
private ClipboardParser(StringReader input) {
|
||||
super(input);
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
public static CompoundTag parse(String text) throws CommandSyntaxException {
|
||||
StringReader input = new StringReader(text);
|
||||
CompoundTag result = new ClipboardParser(input).readStruct();
|
||||
input.skipWhitespace();
|
||||
if (input.canRead()) throw ERROR_TRAILING_DATA.createWithContext(input);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag readStruct() throws CommandSyntaxException {
|
||||
expect('{');
|
||||
CompoundTag result = new CompoundTag();
|
||||
input.skipWhitespace();
|
||||
while (input.canRead() && input.peek() != '}') {
|
||||
int start = input.getCursor();
|
||||
String key = readKey();
|
||||
if (key.isEmpty() && input.getCursor() == start) {
|
||||
throw ERROR_EXPECTED_KEY.createWithContext(input);
|
||||
}
|
||||
expect(':');
|
||||
result.put(key, readValue());
|
||||
input.skipWhitespace();
|
||||
if (!input.canRead() || input.peek() != ',') break;
|
||||
input.skip();
|
||||
input.skipWhitespace();
|
||||
if (!input.canRead()) throw ERROR_EXPECTED_KEY.createWithContext(input);
|
||||
}
|
||||
expect('}');
|
||||
return result;
|
||||
}
|
||||
|
||||
private void expect(char character) throws CommandSyntaxException {
|
||||
input.skipWhitespace();
|
||||
input.expect(character);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,11 +7,12 @@ import dev.ftb.mods.ftblibrary.nbtedit.NBTEditorScreen.ButtonNBT;
|
|||
import dev.ftb.mods.ftblibrary.nbtedit.NBTEditorScreen.ButtonNBTCollection;
|
||||
import dev.ftb.mods.ftblibrary.nbtedit.NBTEditorScreen.ButtonNBTMap;
|
||||
import dev.ftb.mods.ftblibrary.ui.input.Key;
|
||||
import dev.ftb.mods.ftblibrary.ui.misc.SimpleToast;
|
||||
import dev.quorate.client.nbt.ClipboardParser;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
|
@ -22,6 +23,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(value = NBTEditorScreen.class, remap = false)
|
||||
public abstract class EditorMixin {
|
||||
@Shadow private ButtonNBT selected;
|
||||
|
|
@ -33,14 +36,17 @@ public abstract class EditorMixin {
|
|||
|
||||
@Inject(method = "keyPressed", at = @At("RETURN"), cancellable = true)
|
||||
private void saltatory$keys(Key key, CallbackInfoReturnable<Boolean> result) {
|
||||
if (result.getReturnValue() || !key.modifiers.control()) return;
|
||||
if (result.getReturnValue() || (!key.modifiers.control() && !key.is(GLFW.GLFW_KEY_DELETE)))
|
||||
return;
|
||||
if (key.is(GLFW.GLFW_KEY_C)) {
|
||||
saltatory$copy();
|
||||
result.setReturnValue(true);
|
||||
} else if (key.is(GLFW.GLFW_KEY_V)) {
|
||||
saltatory$paste();
|
||||
result.setReturnValue(true);
|
||||
} else if (key.is(GLFW.GLFW_KEY_D) || key.is(GLFW.GLFW_KEY_X)) {
|
||||
} else if (key.is(GLFW.GLFW_KEY_DELETE)
|
||||
|| key.is(GLFW.GLFW_KEY_D)
|
||||
|| key.is(GLFW.GLFW_KEY_X)) {
|
||||
NodeAccess node = (NodeAccess) selected;
|
||||
ButtonNBTCollection parent = node.saltatory$parent();
|
||||
if (parent != null) {
|
||||
|
|
@ -74,11 +80,10 @@ public abstract class EditorMixin {
|
|||
if (!(selected instanceof ButtonNBTCollection target)) return;
|
||||
CompoundTag copied;
|
||||
try {
|
||||
copied = TagParser.parseTag(Minecraft.getInstance().keyboardHandler.getClipboard());
|
||||
copied = ClipboardParser.parse(Minecraft.getInstance().keyboardHandler.getClipboard());
|
||||
} catch (CommandSyntaxException e) {
|
||||
Minecraft.getInstance()
|
||||
.player
|
||||
.displayClientMessage(Component.literal("Invalid NBT clipboard"), true);
|
||||
SimpleToast.error(
|
||||
Component.literal("Invalid NBT clipboard"), Component.literal(e.getMessage()));
|
||||
return;
|
||||
}
|
||||
byte elementType = 0;
|
||||
|
|
@ -88,18 +93,33 @@ public abstract class EditorMixin {
|
|||
|| (!(target instanceof ButtonNBTMap)
|
||||
&& elementType != 0
|
||||
&& elementType != tag.getId())) {
|
||||
Minecraft.getInstance()
|
||||
.player
|
||||
.displayClientMessage(
|
||||
Component.literal("NBT type does not fit selection"), true);
|
||||
SimpleToast.error(
|
||||
Component.literal("NBT type does not fit selection"), Component.empty());
|
||||
return;
|
||||
}
|
||||
elementType = tag.getId();
|
||||
}
|
||||
Map<String, ButtonNBT> previous = Map.copyOf(target.children);
|
||||
for (String name : copied.getAllKeys()) {
|
||||
target.setTag(target instanceof ButtonNBTMap ? name : "-1", copied.get(name).copy());
|
||||
}
|
||||
target.updateChildren(false);
|
||||
saltatory$restore(previous, target);
|
||||
target.getGui().refreshWidgets();
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static void saltatory$restore(
|
||||
Map<String, ButtonNBT> previous, ButtonNBTCollection target) {
|
||||
for (var entry : target.children.entrySet()) {
|
||||
ButtonNBT old = previous.get(entry.getKey());
|
||||
ButtonNBT current = entry.getValue();
|
||||
if (old instanceof ButtonNBTCollection oldTree
|
||||
&& current instanceof ButtonNBTCollection newTree
|
||||
&& old.getClass() == current.getClass()) {
|
||||
newTree.setCollapsed(oldTree.collapsed);
|
||||
saltatory$restore(oldTree.children, newTree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package dev.quorate.client.nbt;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class ClipboardParserTest {
|
||||
private static final String EMPTY_NAMES = "{outer:{args:[{name:'value'},{'':''},{'':''}]}}";
|
||||
|
||||
@Test
|
||||
void readsEmptyNames() throws CommandSyntaxException {
|
||||
assertThrows(CommandSyntaxException.class, () -> TagParser.parseTag(EMPTY_NAMES));
|
||||
CompoundTag parsed = ClipboardParser.parse(EMPTY_NAMES);
|
||||
var args = parsed.getCompound("outer").getList("args", Tag.TAG_COMPOUND);
|
||||
assertEquals(3, args.size());
|
||||
assertTrue(args.getCompound(1).contains("", Tag.TAG_STRING));
|
||||
assertEquals("", args.getCompound(1).getString(""));
|
||||
assertEquals(parsed, ClipboardParser.parse(parsed.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsNormalSyntax() throws CommandSyntaxException {
|
||||
String text = "{a:[1,2,3],b:{nested:'value'},c:[B;1b,2b]}";
|
||||
assertEquals(TagParser.parseTag(text), ClipboardParser.parse(text));
|
||||
for (String invalid : new String[] {"{:1}", "{a:1} trailing", "{a:[1,2b]}", "{a:1,"}) {
|
||||
assertThrows(CommandSyntaxException.class, () -> ClipboardParser.parse(invalid));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue