From cf06c30b54428bfebd7aedf262fd47f35e34d521 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 25 Dec 2022 00:12:55 +0100 Subject: [PATCH 01/11] Add basic Script visualization and colorization --- .../advancedscripts/lexer/Command.java | 13 + .../advancedscripts/lexer/Commands.java | 13 + .../advancedscripts/lexer/Headers.java | 13 + .../advancedscripts/lexer/Operators.java | 13 + .../lexer/ScriptColorizer.java | 239 ++++++++++++++++++ .../lexer/ScriptSyntaxPacketParser.java | 78 ++++++ .../advancedscripts/lexer/Token.java | 13 + .../advancedscripts/lexer/TokenType.java | 13 + .../lexer/TokenTypeColors.java | 22 ++ .../lexer/VariablePrefixes.java | 13 + .../lexer/VariableSuffixes.java | 13 + .../mixin/ClientPlayNetworkHandlerMixin.java | 31 +++ .../mixin/ClientPlayerEntityMixin.java | 28 ++ .../screen/ScriptEditScreen.java | 111 ++++++++ .../resources/advancedscripts.mixins.json | 4 +- 15 files changed, 616 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/Command.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/Commands.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/Headers.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/Operators.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptSyntaxPacketParser.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/Token.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/TokenType.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/TokenTypeColors.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/VariablePrefixes.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/VariableSuffixes.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/mixin/ClientPlayNetworkHandlerMixin.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/mixin/ClientPlayerEntityMixin.java create mode 100644 src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/Command.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/Command.java new file mode 100644 index 0000000..5930bc9 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/Command.java @@ -0,0 +1,13 @@ +package de.zonlykroks.advancedscripts.lexer; + +import java.util.List; + +public class Command { + public final boolean repeatable; + public final List> arguments; + + public Command(boolean repeatable, List> arguments) { + this.repeatable = repeatable; + this.arguments = arguments; + } +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/Commands.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/Commands.java new file mode 100644 index 0000000..3c2debc --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/Commands.java @@ -0,0 +1,13 @@ +package de.zonlykroks.advancedscripts.lexer; + +import java.util.HashMap; +import java.util.Map; + +public class Commands { + + private Commands() { + throw new IllegalStateException("Utility class"); + } + + public static Map COMMANDS = new HashMap<>(); +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/Headers.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/Headers.java new file mode 100644 index 0000000..2e3c4e7 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/Headers.java @@ -0,0 +1,13 @@ +package de.zonlykroks.advancedscripts.lexer; + +import java.util.HashSet; +import java.util.Set; + +public class Headers { + + private Headers() { + throw new IllegalStateException("Utility class"); + } + + public static final Set HEADERS = new HashSet<>(); +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/Operators.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/Operators.java new file mode 100644 index 0000000..70586f4 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/Operators.java @@ -0,0 +1,13 @@ +package de.zonlykroks.advancedscripts.lexer; + +import java.util.HashSet; +import java.util.Set; + +public class Operators { + + private Operators() { + throw new IllegalStateException("Utility class"); + } + + public static final Set OPERATORS = new HashSet<>(); +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java new file mode 100644 index 0000000..e90fc9e --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java @@ -0,0 +1,239 @@ +package de.zonlykroks.advancedscripts.lexer; + +import java.util.ArrayList; +import java.util.List; + +public class ScriptColorizer { + + private ScriptColorizer() { + throw new IllegalStateException("Utility class"); + } + + public static List colorize(int lineNumber, String line) { + if (lineNumber == 0) { + List tokens = colorizeHeader(line); + if (tokens != null) return tokens; + } + + List tokens; + tokens = colorizeComment(line); + if (tokens != null) return tokens; + tokens = colorizeJumpPoint(line); + if (tokens != null) return tokens; + return colorizeLine(line); + } + + private static List colorizeHeader(String line) { + if (!line.startsWith("#!")) return null; + List tokens = new ArrayList<>(); + tokens.add(new Token("#!", TokenTypeColors.COMMENT)); + String s = line.substring(2); + + for (String pattern : Headers.HEADERS) { + if (s.matches(pattern)) { + tokens.add(new Token(s, TokenTypeColors.OTHER)); + return tokens; + } + } + tokens.add(new Token(s, TokenTypeColors.ERROR)); + return tokens; + } + + private static List colorizeComment(String line) { + if (!line.startsWith("#")) return null; + return List.of(new Token(line, TokenTypeColors.COMMENT)); + } + + private static List colorizeJumpPoint(String line) { + if (!line.startsWith(".")) return null; + return List.of(new Token(line, TokenTypeColors.JUMP_POINT)); + } + + private static List colorizeLine(String line) { + List tokens = new ArrayList<>(); + + String command = line; + if (line.indexOf(' ') != -1) { + command = line.substring(0, line.indexOf(' ')); + } + boolean repeatable = false; + List> argumentTypes = null; + if (Commands.COMMANDS.containsKey(command)) { + Command c = Commands.COMMANDS.get(command); + repeatable = c.repeatable; + argumentTypes = c.arguments; + tokens.add(new Token(command, TokenTypeColors.LITERAL)); + } else { + repeatable = true; + argumentTypes = new ArrayList<>(); + argumentTypes.add(List.of(TokenType.any)); + tokens.add(new Token(command, TokenTypeColors.OTHER)); + } + if (command.equals(line)) return tokens; + tokens.add(Token.SPACE); + + String args = line.substring(command.length() + 1).trim(); + tokens.addAll(colorizeArgs(args, repeatable, argumentTypes)); + return tokens; + } + + private static List colorizeArgs(String args, boolean repeatable, List> argumentTypes) { + List tokens = new ArrayList<>(); + + for (List tokenTypes : argumentTypes) { + List temp = new ArrayList<>(); + int index = 0; + int argIndex = 0; + try { + while (argIndex < args.length()) { + if (args.charAt(argIndex) == ' ') { + argIndex++; + temp.add(Token.SPACE); + continue; + } + List current = parse(tokenTypes.get(index), args.substring(argIndex)); + if (current.isEmpty()) { + break; + } + temp.addAll(current); + argIndex += current.stream().mapToInt(t -> t.text.length()).sum(); + index++; + if (repeatable && index == tokenTypes.size()) { + index--; + } + if (index == tokenTypes.size()) { + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + if (argIndex != args.length()) { + continue; + } + if (index != tokenTypes.size() - (repeatable ? 1 : 0)) { + continue; + } + + if (!temp.isEmpty()) { + tokens.addAll(temp); + break; + } + } + + if (tokens.isEmpty()) { + tokens.add(new Token(args, TokenTypeColors.OTHER)); + } + return tokens; + } + + private static List parse(TokenType type, String current) { + return switch (type) { + case any -> parseAny(current); + case expression -> parseExpression(current); + case jump_point -> parseJumpPoint(current); + case variable -> parseVariable(current); + case text_type -> parseText(current); + case number_type -> parseNumber(current); + case floating_number_type -> parseFloatingNumber(current); + case boolean_type -> parseBoolean(current); + }; + } + + private static List parseAny(String current) { + List tokens = parseExpression(current); + if (!tokens.isEmpty()) return tokens; + tokens = parseFloatingNumber(current); + if (!tokens.isEmpty()) return tokens; + tokens = parseNumber(current); + if (!tokens.isEmpty()) return tokens; + tokens = parseBoolean(current); + if (!tokens.isEmpty()) return tokens; + return parseText(current); + } + + private static List parseExpression(String current) { + if (!current.startsWith("{")) return new ArrayList<>(); + int depth = 0; + int index = 0; + do { + if (current.charAt(index) == '{') { + depth++; + } else if (current.charAt(index) == '}') { + depth--; + } + index++; + } while (depth != 0 && index <= current.length()); + if (depth != 0) return List.of(new Token(current, TokenTypeColors.ERROR)); + String expression = current.substring(0, index); // TODO: colorize expression + return List.of(new Token(expression, TokenTypeColors.OTHER)); + } + + private static List parseJumpPoint(String current) { + int index = current.indexOf(' '); + if (index == -1) { + return List.of(new Token(current, TokenTypeColors.JUMP_POINT)); + } else { + return List.of(new Token(current.substring(0, index), TokenTypeColors.JUMP_POINT)); + } + } + + private static List parseVariable(String current) { + int index = current.indexOf(' '); + if (index == -1) { + return List.of(new Token(current, TokenTypeColors.VARIABLE)); + } else { + return List.of(new Token(current.substring(0, index), TokenTypeColors.VARIABLE)); + } + } + + private static List parseText(String current) { + int index = current.indexOf(' '); + if (index == -1) { + return List.of(new Token(current, TokenTypeColors.TEXT)); + } else { + return List.of(new Token(current.substring(0, index), TokenTypeColors.TEXT)); + } + } + + private static List parseNumber(String current) { + int index = current.indexOf(' '); + String number = current; + if (index != -1) { + number = current.substring(0, index); + } + try { + Long.parseLong(number); + return List.of(new Token(number, TokenTypeColors.NUMBER)); + } catch (NumberFormatException e) { + return new ArrayList<>(); + } + } + + private static List parseFloatingNumber(String current) { + int index = current.indexOf(' '); + String number = current; + if (index != -1) { + number = current.substring(0, index); + } + try { + Double.parseDouble(number); + return List.of(new Token(number, TokenTypeColors.NUMBER)); + } catch (NumberFormatException e) { + return new ArrayList<>(); + } + } + + private static List parseBoolean(String current) { + int index = current.indexOf(' '); + String bool = current; + if (index != -1) { + bool = current.substring(0, index); + } + if ("true".equalsIgnoreCase(bool) || "false".equalsIgnoreCase(bool)) { + return List.of(new Token(bool, TokenTypeColors.BOOLEAN)); + } else { + return new ArrayList<>(); + } + } +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptSyntaxPacketParser.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptSyntaxPacketParser.java new file mode 100644 index 0000000..abf09f6 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptSyntaxPacketParser.java @@ -0,0 +1,78 @@ +package de.zonlykroks.advancedscripts.lexer; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class ScriptSyntaxPacketParser { + + private ScriptSyntaxPacketParser() { + throw new IllegalStateException("Utility class"); + } + + private static final TokenType[] TOKEN_TYPES = TokenType.values(); + + private static void reset() { + Operators.OPERATORS.clear(); + Headers.HEADERS.clear(); + VariablePrefixes.RPEFIXES.clear(); + VariableSuffixes.SUFFIXES.clear(); + Commands.COMMANDS.clear(); + } + + public static synchronized void parse(String scriptSyntax) { + reset(); + + JsonObject jsonObject = JsonParser.parseString(scriptSyntax).getAsJsonObject(); + for (String key : jsonObject.keySet()) { + JsonArray jsonElements = jsonObject.get(key).getAsJsonArray(); + if (key.startsWith("@")) { + parseSpecial(key, jsonElements); + } else { + parseCommand(key, jsonElements); + } + } + } + + private static void parseCommand(String key, JsonArray value) { + boolean repeating = value.get(0).getAsBoolean(); + List> validArgumentTypes = new ArrayList<>(); + for (int i = 1; i < value.size(); i++) { + JsonArray parameters = value.get(i).getAsJsonArray(); + List parameterTypes = new ArrayList<>(); + for (JsonElement parameter : parameters) { + parameterTypes.add(TOKEN_TYPES[parameter.getAsInt()]); + } + validArgumentTypes.add(parameterTypes); + } + Commands.COMMANDS.put(key, new Command(repeating, validArgumentTypes)); + } + + private static void parseSpecial(String key, JsonArray value) { + Set set; + switch (key) { + case "@operators": + set = Operators.OPERATORS; + break; + case "@headers": + set = Headers.HEADERS; + break; + case "@prefixes": + set = VariablePrefixes.RPEFIXES; + break; + case "@suffixes": + set = VariableSuffixes.SUFFIXES; + break; + default: + return; + } + for (JsonElement element : value) { + set.add(element.getAsString()); + } + } +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/Token.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/Token.java new file mode 100644 index 0000000..6e7d022 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/Token.java @@ -0,0 +1,13 @@ +package de.zonlykroks.advancedscripts.lexer; + +public class Token { + public static final Token SPACE = new Token(" ", 0xFFFFFFFF); + + public final String text; + public final int color; + + public Token(String text, int color) { + this.text = text; + this.color = color; + } +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenType.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenType.java new file mode 100644 index 0000000..075646c --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenType.java @@ -0,0 +1,13 @@ +package de.zonlykroks.advancedscripts.lexer; + +public enum TokenType { // This is copied from the BauSystem2.0 sources. + any, // This does not include jump_point and variable + expression, + jump_point, + variable, + + text_type, + number_type, + floating_number_type, + boolean_type, +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenTypeColors.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenTypeColors.java new file mode 100644 index 0000000..e01fc89 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenTypeColors.java @@ -0,0 +1,22 @@ +package de.zonlykroks.advancedscripts.lexer; + +public class TokenTypeColors { + + private TokenTypeColors() { + throw new IllegalStateException("Utility class"); + } + + public static final int BACKGROUND = 0xFF1E1F22; + public static final int OTHER = 0xFFFFFFFF; + + public static final int ERROR = 0xFFAA0000; + + public static final int VARIABLE = 0xFFFFFFFF; + public static final int LITERAL = 0xFF925F35; + public static final int COMMENT = 0xFF656565; + public static final int JUMP_POINT = 0xFFFFa500; + + public static final int NUMBER = 0xFF61839F; + public static final int BOOLEAN = 0xFF925F35; + public static final int TEXT = 0xFF6F855D; +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/VariablePrefixes.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/VariablePrefixes.java new file mode 100644 index 0000000..631fd15 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/VariablePrefixes.java @@ -0,0 +1,13 @@ +package de.zonlykroks.advancedscripts.lexer; + +import java.util.HashSet; +import java.util.Set; + +public class VariablePrefixes { + + private VariablePrefixes() { + throw new IllegalStateException("Utility class"); + } + + public static final Set RPEFIXES = new HashSet<>(); +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/VariableSuffixes.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/VariableSuffixes.java new file mode 100644 index 0000000..d4d67f1 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/VariableSuffixes.java @@ -0,0 +1,13 @@ +package de.zonlykroks.advancedscripts.lexer; + +import java.util.HashSet; +import java.util.Set; + +public class VariableSuffixes { + + private VariableSuffixes() { + throw new IllegalStateException("Utility class"); + } + + public static final Set SUFFIXES = new HashSet<>(); +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/zonlykroks/advancedscripts/mixin/ClientPlayNetworkHandlerMixin.java new file mode 100644 index 0000000..3b9c457 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/mixin/ClientPlayNetworkHandlerMixin.java @@ -0,0 +1,31 @@ +package de.zonlykroks.advancedscripts.mixin; + +import de.zonlykroks.advancedscripts.lexer.ScriptSyntaxPacketParser; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientPlayNetworkHandler.class) +public class ClientPlayNetworkHandlerMixin { + + private static final Identifier CHANNEL = new Identifier("sw:script_syntax"); + + @Inject(method = "onCustomPayload", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/CustomPayloadS2CPacket;getData()Lnet/minecraft/network/PacketByteBuf;"), cancellable = true) + public void onCustomPayload(CustomPayloadS2CPacket packet, CallbackInfo ci) { + if (CHANNEL.equals(packet.getChannel())) { + PacketByteBuf buf = packet.getData(); + int readableBytes = buf.readableBytes(); + StringBuilder st = new StringBuilder(); + for (int i = 0; i < readableBytes; i++) { + st.append((char) buf.readByte()); + } + ScriptSyntaxPacketParser.parse(st.toString()); + ci.cancel(); + } + } +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/mixin/ClientPlayerEntityMixin.java b/src/main/java/de/zonlykroks/advancedscripts/mixin/ClientPlayerEntityMixin.java new file mode 100644 index 0000000..4c00782 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/mixin/ClientPlayerEntityMixin.java @@ -0,0 +1,28 @@ +package de.zonlykroks.advancedscripts.mixin; + +import de.zonlykroks.advancedscripts.screen.ScriptEditScreen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.Hand; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientPlayerEntity.class) +public class ClientPlayerEntityMixin { + + @Shadow @Final protected MinecraftClient client; + + @Inject(method = "useBook", at = @At("HEAD"), cancellable = true) + public void useBookMixin(ItemStack book, Hand hand, CallbackInfo ci) { + if (book.isOf(Items.WRITABLE_BOOK)) { + this.client.setScreen(new ScriptEditScreen(((ClientPlayerEntity)(Object)this), book, hand)); + ci.cancel(); + } + } +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java new file mode 100644 index 0000000..26c9e76 --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -0,0 +1,111 @@ +package de.zonlykroks.advancedscripts.screen; + +import com.mojang.blaze3d.systems.RenderSystem; +import de.zonlykroks.advancedscripts.lexer.ScriptColorizer; +import de.zonlykroks.advancedscripts.lexer.Token; +import de.zonlykroks.advancedscripts.lexer.TokenTypeColors; +import net.minecraft.client.font.TextHandler; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.BookScreen; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.util.NarratorManager; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.Style; +import net.minecraft.util.Hand; +import org.apache.commons.lang3.mutable.MutableInt; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class ScriptEditScreen extends Screen { + + private PlayerEntity player; + private ItemStack itemStack; + private Hand hand; + + private List lines = new ArrayList<>(); + + public ScriptEditScreen(PlayerEntity player, ItemStack itemStack, Hand hand) { + super(NarratorManager.EMPTY); + this.player = player; + this.itemStack = itemStack; + this.hand = hand; + + NbtCompound nbtCompound = itemStack.getNbt(); + if (nbtCompound != null) { + BookScreen.filterPages(nbtCompound, s -> { + lines.addAll(Arrays.asList(s.split("\n"))); + }); + } + if (lines.isEmpty()) { + lines.add(""); + } + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + this.renderBackground(matrices); + RenderSystem.setShader(GameRenderer::getPositionTexProgram); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + fill(matrices, 23, 23, this.width - 23, this.height - 72, TokenTypeColors.BACKGROUND); + + int lineNumberLength = textRenderer.getWidth(lines.size() + ""); + + // TODO: Implement text rendering + int lineNumberText = 1; + MutableInt lineNumber = new MutableInt(); + TextHandler textHandler = this.textRenderer.getTextHandler(); + for (String s : lines) { + if (lineNumber.getValue() * 9 + 25 > this.height - 75) { + break; + } + + // Line number + this.textRenderer.draw(matrices, lineNumberText + "", 25 + lineNumberLength - textRenderer.getWidth(lineNumberText + ""), 25 + lineNumber.getValue() * 9, 0xFFFFFF); + lineNumberText++; + + // Line text + List tokens = ScriptColorizer.colorize(lineNumber.getValue(), s); + AtomicInteger x = new AtomicInteger(25 + lineNumberLength + 5); + for (Token token : tokens) { + textHandler.wrapLines(token.text, this.width - x.get() - 25, Style.EMPTY, true, (style, start, end) -> { + int y = lineNumber.getValue() * 9; + if (y + 25 > this.height - 75) { + return; + } + + String line = token.text.substring(start, end); + this.textRenderer.draw(matrices, line, x.get(), 25 + y, token.color); + x.addAndGet(textRenderer.getWidth(line)); + if (x.get() > this.width - 50 - lineNumberLength - 5) { + x.set(25 + lineNumberLength + 5); + lineNumber.increment(); + } + }); + } + lineNumber.increment(); + } + + super.render(matrices, mouseX, mouseY, delta); + } + + @Override + public boolean keyReleased(int keyCode, int scanCode, int modifiers) { + return super.keyReleased(keyCode, scanCode, modifiers); + } + + @Override + public boolean charTyped(char chr, int modifiers) { + return super.charTyped(chr, modifiers); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + return super.mouseClicked(mouseX, mouseY, button); + } +} diff --git a/src/main/resources/advancedscripts.mixins.json b/src/main/resources/advancedscripts.mixins.json index c4c8409..8adf065 100644 --- a/src/main/resources/advancedscripts.mixins.json +++ b/src/main/resources/advancedscripts.mixins.json @@ -7,7 +7,9 @@ ], "client": [ "ClientLoginNetworkHandlerMixin", - "KeyboardMixin" + "KeyboardMixin", + "ClientPlayerEntityMixin", + "ClientPlayNetworkHandlerMixin" ], "injectors": { "defaultRequire": 1 From 909bce40924a8afa17d0238ae3ebb9627a8d751a Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 25 Dec 2022 09:50:44 +0100 Subject: [PATCH 02/11] Finalize ExpressionColorizer --- .../lexer/ExpressionColorizer.java | 134 ++++++++++++++++++ .../lexer/ScriptColorizer.java | 3 +- 2 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 src/main/java/de/zonlykroks/advancedscripts/lexer/ExpressionColorizer.java diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/ExpressionColorizer.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/ExpressionColorizer.java new file mode 100644 index 0000000..a0e691a --- /dev/null +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/ExpressionColorizer.java @@ -0,0 +1,134 @@ +package de.zonlykroks.advancedscripts.lexer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ExpressionColorizer { + + private ExpressionColorizer() { + throw new IllegalStateException("Utility class"); + } + + public static List colorize(String expression) { + List parts = tokenize(expression); + List tokens = new ArrayList<>(); + for (int i = 0; i < parts.size(); i++) { + String part = parts.get(i); + if ("{".equals(part) || "}".equals(part)) { + tokens.add(new Token(part, TokenTypeColors.OTHER)); + continue; + } + if ("true".equalsIgnoreCase(part) || "false".equalsIgnoreCase(part)) { + tokens.add(new Token(part, TokenTypeColors.BOOLEAN)); + continue; + } + try { + Double.parseDouble(part); + tokens.add(new Token(part, TokenTypeColors.NUMBER)); + continue; + } catch (NumberFormatException ignored) { + } + try { + Long.parseLong(part); + tokens.add(new Token(part, TokenTypeColors.NUMBER)); + continue; + } catch (NumberFormatException ignored) { + } + if (part.contains(".")) { + String[] split = part.split("\\."); + if (split.length == 1) { + tokens.add(new Token(part, TokenTypeColors.VARIABLE)); + continue; + } + if (VariablePrefixes.RPEFIXES.contains(split[0])) { + tokens.add(new Token(split[0], TokenTypeColors.OTHER)); + tokens.add(new Token(".", TokenTypeColors.OTHER)); + split = Arrays.copyOfRange(split, 1, split.length); + } + tokens.add(new Token(split[0], TokenTypeColors.VARIABLE)); + for (int j = 1; j < split.length; j++) { + String s = split[j]; + tokens.add(new Token(".", TokenTypeColors.OTHER)); + if (VariableSuffixes.SUFFIXES.contains(s)) { + tokens.add(new Token(s, TokenTypeColors.OTHER)); + } else { + tokens.add(new Token(s, TokenTypeColors.ERROR)); + } + } + continue; + } + if (Operators.OPERATORS.contains(part)) { + String previous = get(parts, i, -1); + String next = get(parts, i, 1); + if (previous == null || next == null) { + tokens.add(new Token(part, TokenTypeColors.ERROR)); + continue; + } + if (Operators.OPERATORS.contains(previous) || Operators.OPERATORS.contains(next)) { + tokens.add(new Token(part, TokenTypeColors.ERROR)); + continue; + } + if ("{".equals(previous) || "}".equals(next)) { + tokens.add(new Token(part, TokenTypeColors.ERROR)); + continue; + } + tokens.add(new Token(part, TokenTypeColors.OTHER)); + continue; + } + if (part.matches("[+\\-*/%^&|<>=!]+")) { + tokens.add(new Token(part, TokenTypeColors.ERROR)); + continue; + } + tokens.add(new Token(part, TokenTypeColors.VARIABLE)); + } + return tokens; + } + + private static String get(List parts, int index, int direction) { + for (int i = index + direction; i >= 0 && i < parts.size(); i += direction) { + String part = parts.get(i); + if (!part.isEmpty()) return part; + } + return null; + } + + private static List tokenize(String s) { + List tokens = new ArrayList<>(); + StringBuilder token = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '{' || c == '}' || c == ' ') { + if (token.length() > 0) { + tokens.add(token.toString()); + token = new StringBuilder(); + } + tokens.add(c + ""); + continue; + } + StringBuilder op = new StringBuilder(); + for (int j = i; j < s.length(); j++) { + char k = s.charAt(j); + if (k == '+' || k == '-' || k == '*' || k == '/' || k == '%' || k == '^' || k == '&' || k == '|' || k == '>' || k == '<' || k == '=' || k == '!') { + op.append(k); + } else { + break; + } + } + if (op.length() > 0) { + if (token.length() > 0) { + tokens.add(token.toString()); + token = new StringBuilder(); + } + tokens.add(op.toString()); + i += op.length() - 1; + continue; + } + token.append(c); + } + if (token.length() > 0) { + tokens.add(token.toString()); + } + return tokens; + } +} diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java index e90fc9e..54322be 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java @@ -165,8 +165,7 @@ public class ScriptColorizer { index++; } while (depth != 0 && index <= current.length()); if (depth != 0) return List.of(new Token(current, TokenTypeColors.ERROR)); - String expression = current.substring(0, index); // TODO: colorize expression - return List.of(new Token(expression, TokenTypeColors.OTHER)); + return ExpressionColorizer.colorize(current.substring(0, index)); } private static List parseJumpPoint(String current) { From 22939d42e02ee61dc6a7f278dd21ae1a4071ef9a Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 25 Dec 2022 10:11:40 +0100 Subject: [PATCH 03/11] Add basic editor and move cursor --- .../screen/ScriptEditScreen.java | 171 +++++++++++++++++- 1 file changed, 168 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java index 26c9e76..b6e3b8f 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -5,6 +5,7 @@ import de.zonlykroks.advancedscripts.lexer.ScriptColorizer; import de.zonlykroks.advancedscripts.lexer.Token; import de.zonlykroks.advancedscripts.lexer.TokenTypeColors; import net.minecraft.client.font.TextHandler; +import net.minecraft.client.gui.DrawableHelper; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.BookScreen; import net.minecraft.client.render.GameRenderer; @@ -20,6 +21,7 @@ import org.apache.commons.lang3.mutable.MutableInt; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; public class ScriptEditScreen extends Screen { @@ -29,6 +31,10 @@ public class ScriptEditScreen extends Screen { private Hand hand; private List lines = new ArrayList<>(); + private int cursorY = 0; + private int cursorX = 0; + + private int tickCounter; public ScriptEditScreen(PlayerEntity player, ItemStack itemStack, Hand hand) { super(NarratorManager.EMPTY); @@ -47,6 +53,12 @@ public class ScriptEditScreen extends Screen { } } + @Override + public void tick() { + super.tick(); + ++this.tickCounter; + } + @Override public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { this.renderBackground(matrices); @@ -60,7 +72,9 @@ public class ScriptEditScreen extends Screen { int lineNumberText = 1; MutableInt lineNumber = new MutableInt(); TextHandler textHandler = this.textRenderer.getTextHandler(); - for (String s : lines) { + for (int i = 0; i < lines.size(); i++) { + String s = lines.get(i); + if (lineNumber.getValue() * 9 + 25 > this.height - 75) { break; } @@ -90,18 +104,169 @@ public class ScriptEditScreen extends Screen { } lineNumber.increment(); } + drawCursor(matrices, 25 + lineNumberLength + 5 + textRenderer.getWidth(lines.get(cursorY).substring(0, cursorX)), 25 + cursorY * 9, isAtEndOfLine()); super.render(matrices, mouseX, mouseY, delta); } + private void drawCursor(MatrixStack matrices, int x, int y, boolean atEnd) { + if (this.tickCounter / 6 % 2 == 0) { + if (!atEnd) { + int var10001 = x; + int var10002 = y - 1; + int var10003 = x + 1; + int var10004 = y; + Objects.requireNonNull(this.textRenderer); + DrawableHelper.fill(matrices, var10001, var10002, var10003, var10004 + 9, 0xFFFFFFFF); + } else { + this.textRenderer.draw(matrices, "_", (float)x, (float)y, 0xFFFFFFFF); + } + } + + } + @Override public boolean keyReleased(int keyCode, int scanCode, int modifiers) { - return super.keyReleased(keyCode, scanCode, modifiers); + if (super.keyReleased(keyCode, scanCode, modifiers)) { + return true; + } + switch (keyCode) { + case 257: + case 335: + newLine(); + break; + case 259: + backspace(); + break; + case 261: + delete(); + break; + case 262: + moveCursor(1); + break; + case 263: + moveCursor(-1); + break; + case 264: + moveDown(); + break; + case 265: + moveUp(); + break; + } + return true; } @Override public boolean charTyped(char chr, int modifiers) { - return super.charTyped(chr, modifiers); + if (super.charTyped(chr, modifiers)) { + return true; + } + if (insert(chr, modifiers)) { + return true; + } + return true; + } + + private boolean insert(char chr, int modifiers) { + String line = lines.get(cursorY); + if (cursorX == line.length()) { + line += chr; + } else { + line = line.substring(0, cursorX) + chr + line.substring(cursorX); + } + lines.set(cursorY, line); + cursorX++; + return true; + } + + private boolean newLine() { + String line = lines.get(cursorY); + String newLine = line.substring(cursorX); + line = line.substring(0, cursorX); + lines.set(cursorY, line); + lines.add(cursorY + 1, newLine); + cursorY++; + cursorX = 0; + return true; + } + + private boolean backspace() { + if (cursorX == 0) { + if (cursorY == 0) { + return true; + } + String line = lines.get(cursorY); + String prevLine = lines.get(cursorY - 1); + lines.set(cursorY - 1, prevLine + line); + lines.remove(cursorY); + cursorY--; + cursorX = prevLine.length(); + } else { + String line = lines.get(cursorY); + line = line.substring(0, cursorX - 1) + line.substring(cursorX); + lines.set(cursorY, line); + cursorX--; + } + return true; + } + + private boolean delete() { + String line = lines.get(cursorY); + if (cursorX == line.length()) { + if (cursorY == lines.size() - 1) { + return true; + } + String nextLine = lines.get(cursorY + 1); + lines.set(cursorY, line + nextLine); + lines.remove(cursorY + 1); + } else { + line = line.substring(0, cursorX) + line.substring(cursorX + 1); + lines.set(cursorY, line); + } + return true; + } + + private boolean moveCursor(int offset) { + String line = lines.get(cursorY); + if (cursorX + offset < 0) { + if (cursorY == 0) { + return true; + } + cursorY--; + cursorX = lines.get(cursorY).length(); + } else if (cursorX + offset > line.length()) { + if (cursorY == lines.size() - 1) { + return true; + } + cursorY++; + cursorX = 0; + } else { + cursorX += offset; + } + return true; + } + + private boolean moveDown() { + if (cursorY == lines.size() - 1) { + return true; + } + cursorY++; + cursorX = Math.min(cursorX, lines.get(cursorY).length()); + return true; + } + + private boolean moveUp() { + if (cursorY == 0) { + return true; + } + cursorY--; + cursorX = Math.min(cursorX, lines.get(cursorY).length()); + return true; + } + + private boolean isAtEndOfLine() { + return cursorX == lines.get(cursorY).length(); } @Override From 702865eba16502192a5490697efddffcd0c469c6 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 25 Dec 2022 14:38:05 +0100 Subject: [PATCH 04/11] Finalize basic editor --- .../lexer/ExpressionColorizer.java | 2 +- .../lexer/ScriptColorizer.java | 52 +++++- .../screen/ScriptEditScreen.java | 160 +++++++++++++++--- 3 files changed, 187 insertions(+), 27 deletions(-) diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/ExpressionColorizer.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/ExpressionColorizer.java index a0e691a..feaea4a 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/lexer/ExpressionColorizer.java +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/ExpressionColorizer.java @@ -88,7 +88,7 @@ public class ExpressionColorizer { private static String get(List parts, int index, int direction) { for (int i = index + direction; i >= 0 && i < parts.size(); i += direction) { String part = parts.get(i); - if (!part.isEmpty()) return part; + if (!part.isBlank()) return part; } return null; } diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java index 54322be..6086486 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/ScriptColorizer.java @@ -72,7 +72,7 @@ public class ScriptColorizer { if (command.equals(line)) return tokens; tokens.add(Token.SPACE); - String args = line.substring(command.length() + 1).trim(); + String args = line.substring(command.length() + 1); tokens.addAll(colorizeArgs(args, repeatable, argumentTypes)); return tokens; } @@ -163,7 +163,7 @@ public class ScriptColorizer { depth--; } index++; - } while (depth != 0 && index <= current.length()); + } while (depth != 0 && index < current.length()); if (depth != 0) return List.of(new Token(current, TokenTypeColors.ERROR)); return ExpressionColorizer.colorize(current.substring(0, index)); } @@ -188,11 +188,51 @@ public class ScriptColorizer { private static List parseText(String current) { int index = current.indexOf(' '); - if (index == -1) { - return List.of(new Token(current, TokenTypeColors.TEXT)); - } else { - return List.of(new Token(current.substring(0, index), TokenTypeColors.TEXT)); + if (index != -1) { + current = current.substring(0, index); } + List tokens = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < current.length(); i++) { + char c = current.charAt(i); + if (c == '&' && i + 1 < current.length()) { + char color = current.charAt(i + 1); + if (color >= '0' && color <= '9' || color >= 'a' && color <= 'f' || color >= 'A' && color <= 'F') { + if (sb.length() > 0) { + tokens.add(new Token(sb.toString(), TokenTypeColors.TEXT)); + sb = new StringBuilder(); + } + i++; + switch (color) { + case '0' -> tokens.add(new Token("&0", 0xFF000000)); + case '1' -> tokens.add(new Token("&1", 0xFF0000AA)); + case '2' -> tokens.add(new Token("&2", 0xFF00AA00)); + case '3' -> tokens.add(new Token("&3", 0xFF00AAAA)); + case '4' -> tokens.add(new Token("&4", 0xFFAA0000)); + case '5' -> tokens.add(new Token("&5", 0xFFAA00AA)); + case '6' -> tokens.add(new Token("&6", 0xFFFFAA00)); + case '7' -> tokens.add(new Token("&7", 0xFFAAAAAA)); + case '8' -> tokens.add(new Token("&8", 0xFF555555)); + case '9' -> tokens.add(new Token("&9", 0xFF5555FF)); + case 'a', 'A' -> tokens.add(new Token("&a", 0xFF55FF55)); + case 'b', 'B' -> tokens.add(new Token("&b", 0xFF55FFFF)); + case 'c', 'C' -> tokens.add(new Token("&c", 0xFFFF5555)); + case 'd', 'D' -> tokens.add(new Token("&d", 0xFFFF55FF)); + case 'e', 'E' -> tokens.add(new Token("&e", 0xFFFFFF55)); + case 'f', 'F' -> tokens.add(new Token("&f", 0xFFFFFFFF)); + default -> tokens.add(new Token("&" + color, TokenTypeColors.TEXT)); + } + } else { + sb.append(c); + } + } else { + sb.append(c); + } + } + if (sb.length() > 0) { + tokens.add(new Token(sb.toString(), TokenTypeColors.TEXT)); + } + return tokens; } private static List parseNumber(String current) { diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java index b6e3b8f..582f3b1 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -8,20 +8,23 @@ import net.minecraft.client.font.TextHandler; import net.minecraft.client.gui.DrawableHelper; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.BookScreen; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.PressableWidget; import net.minecraft.client.render.GameRenderer; import net.minecraft.client.util.NarratorManager; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtString; +import net.minecraft.network.packet.c2s.play.BookUpdateC2SPacket; import net.minecraft.text.Style; +import net.minecraft.text.Text; import net.minecraft.util.Hand; import org.apache.commons.lang3.mutable.MutableInt; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; public class ScriptEditScreen extends Screen { @@ -36,6 +39,9 @@ public class ScriptEditScreen extends Screen { private int tickCounter; + private int keyCode = -1; + private long time; + public ScriptEditScreen(PlayerEntity player, ItemStack itemStack, Hand hand) { super(NarratorManager.EMPTY); this.player = player; @@ -53,10 +59,49 @@ public class ScriptEditScreen extends Screen { } } + @Override + protected void init() { + this.addDrawableChild( + new DoneButton(this.width / 2 - 98 / 2, height - 40, () -> { + this.client.setScreen(null); + finalizeBook(); + }) + ); + } + + public static final Text DONE = Text.translatable("gui.done"); + + private class DoneButton extends PressableWidget { + private Runnable onPress; + + public DoneButton(int x, int y, Runnable onPress) { + super(x, y, 98, 20, DONE); + visible = true; + this.onPress = onPress; + } + + @Override + public void onPress() { + onPress.run(); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + } + + @Override + public boolean isNarratable() { + return false; + } + } + @Override public void tick() { super.tick(); ++this.tickCounter; + if (keyCode != -1 && System.currentTimeMillis() - time > 500) { + key(keyCode); + } } @Override @@ -64,36 +109,50 @@ public class ScriptEditScreen extends Screen { this.renderBackground(matrices); RenderSystem.setShader(GameRenderer::getPositionTexProgram); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - fill(matrices, 23, 23, this.width - 23, this.height - 72, TokenTypeColors.BACKGROUND); + fill(matrices, 23, 23, this.width - 23, this.height - 63, TokenTypeColors.BACKGROUND); int lineNumberLength = textRenderer.getWidth(lines.size() + ""); - // TODO: Implement text rendering int lineNumberText = 1; MutableInt lineNumber = new MutableInt(); TextHandler textHandler = this.textRenderer.getTextHandler(); for (int i = 0; i < lines.size(); i++) { String s = lines.get(i); - if (lineNumber.getValue() * 9 + 25 > this.height - 75) { + if (lineNumber.getValue() * 9 + 25 > this.height - 66) { break; } + if (s.isEmpty() && i == cursorY) { + drawCursor(matrices, 25 + lineNumberLength + 5, lineNumber.getValue() * 9 + 25, true); + } + // Line number + if (lineTooLong(s)) { + int height = this.textRenderer.getWrappedLinesHeight(s, this.width - 50 - lineNumberLength - 5); + fill(matrices, 25 + lineNumberLength + 2, 25 + lineNumber.getValue() * 9, 25 + lineNumberLength + 3, 25 + lineNumber.getValue() * 9 + height, TokenTypeColors.ERROR); + } this.textRenderer.draw(matrices, lineNumberText + "", 25 + lineNumberLength - textRenderer.getWidth(lineNumberText + ""), 25 + lineNumber.getValue() * 9, 0xFFFFFF); lineNumberText++; // Line text List tokens = ScriptColorizer.colorize(lineNumber.getValue(), s); AtomicInteger x = new AtomicInteger(25 + lineNumberLength + 5); + AtomicInteger currentXIndex = new AtomicInteger(0); for (Token token : tokens) { + int finalI = i; textHandler.wrapLines(token.text, this.width - x.get() - 25, Style.EMPTY, true, (style, start, end) -> { int y = lineNumber.getValue() * 9; - if (y + 25 > this.height - 75) { + if (y + 25 > this.height - 66) { return; } String line = token.text.substring(start, end); + int previousXIndex = currentXIndex.get(); + currentXIndex.addAndGet(line.length()); + if (finalI == cursorY && currentXIndex.get() >= cursorX && previousXIndex <= cursorX) { + drawCursor(matrices, x.get() + textRenderer.getWidth(line.substring(0, cursorX - previousXIndex)) - 1, 25 + y, isAtEndOfLine()); + } this.textRenderer.draw(matrices, line, x.get(), 25 + y, token.color); x.addAndGet(textRenderer.getWidth(line)); if (x.get() > this.width - 50 - lineNumberLength - 5) { @@ -104,32 +163,30 @@ public class ScriptEditScreen extends Screen { } lineNumber.increment(); } - drawCursor(matrices, 25 + lineNumberLength + 5 + textRenderer.getWidth(lines.get(cursorY).substring(0, cursorX)), 25 + cursorY * 9, isAtEndOfLine()); super.render(matrices, mouseX, mouseY, delta); } + private boolean lineTooLong(String s) { + if (s.length() >= 1024) { + return true; + } + return textRenderer.getWrappedLinesHeight(s, 114) > 128; + } + private void drawCursor(MatrixStack matrices, int x, int y, boolean atEnd) { if (this.tickCounter / 6 % 2 == 0) { if (!atEnd) { - int var10001 = x; - int var10002 = y - 1; - int var10003 = x + 1; - int var10004 = y; Objects.requireNonNull(this.textRenderer); - DrawableHelper.fill(matrices, var10001, var10002, var10003, var10004 + 9, 0xFFFFFFFF); + DrawableHelper.fill(matrices, x, y - 1, x + 1, y + 9, 0xFFFFFFFF); } else { - this.textRenderer.draw(matrices, "_", (float)x, (float)y, 0xFFFFFFFF); + this.textRenderer.draw(matrices, "_", (float) x, (float) y, 0xFFFFFFFF); } } } - @Override - public boolean keyReleased(int keyCode, int scanCode, int modifiers) { - if (super.keyReleased(keyCode, scanCode, modifiers)) { - return true; - } + private void key(int keyCode) { switch (keyCode) { case 257: case 335: @@ -153,10 +210,32 @@ public class ScriptEditScreen extends Screen { case 265: moveUp(); break; + case 268: + cursorX = 0; + break; + case 269: + cursorX = lines.get(cursorY).length(); + break; } + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (super.keyPressed(keyCode, scanCode, modifiers)) { + return true; + } + key(keyCode); + this.keyCode = keyCode; + this.time = System.currentTimeMillis(); return true; } + @Override + public boolean keyReleased(int keyCode, int scanCode, int modifiers) { + this.keyCode = -1; + return super.keyReleased(keyCode, scanCode, modifiers); + } + @Override public boolean charTyped(char chr, int modifiers) { if (super.charTyped(chr, modifiers)) { @@ -249,6 +328,7 @@ public class ScriptEditScreen extends Screen { private boolean moveDown() { if (cursorY == lines.size() - 1) { + cursorX = lines.get(cursorY).length(); return true; } cursorY++; @@ -258,6 +338,7 @@ public class ScriptEditScreen extends Screen { private boolean moveUp() { if (cursorY == 0) { + cursorX = 0; return true; } cursorY--; @@ -273,4 +354,43 @@ public class ScriptEditScreen extends Screen { public boolean mouseClicked(double mouseX, double mouseY, int button) { return super.mouseClicked(mouseX, mouseY, button); } + + private List toPages() { + List pages = new ArrayList<>(); + StringBuilder page = new StringBuilder(); + for (String line : lines) { + if (!page.isEmpty()) { + page.append("\n"); + } + if (page.length() + line.length() > 1024) { + pages.add(page.toString()); + page = new StringBuilder(); + } + String temp = page + line; + if (textRenderer.getWrappedLinesHeight(temp, 114) > 128) { + pages.add(page.toString()); + page = new StringBuilder(); + } + page.append(line); + } + if (!page.isEmpty()) { + pages.add(page.toString()); + } + return pages; + } + + private void finalizeBook() { + List pages = toPages(); + this.writeNbtData(pages); + int i = this.hand == Hand.MAIN_HAND ? this.player.getInventory().selectedSlot : 40; + this.client.getNetworkHandler().sendPacket(new BookUpdateC2SPacket(i, pages, Optional.empty())); + } + + private void writeNbtData(List pages) { + NbtList nbtList = new NbtList(); + pages.stream().map(NbtString::of).forEach(nbtList::add); + if (!pages.isEmpty()) { + this.itemStack.setSubNbt("pages", nbtList); + } + } } From 06b46966b8868e350c82092374cec43552d86ea3 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 25 Dec 2022 14:53:08 +0100 Subject: [PATCH 05/11] Add scrolling via mouse --- .../screen/ScriptEditScreen.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java index 582f3b1..d98db9f 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -34,6 +34,7 @@ public class ScriptEditScreen extends Screen { private Hand hand; private List lines = new ArrayList<>(); + private int scroll = 0; private int cursorY = 0; private int cursorX = 0; @@ -113,10 +114,10 @@ public class ScriptEditScreen extends Screen { int lineNumberLength = textRenderer.getWidth(lines.size() + ""); - int lineNumberText = 1; + int lineNumberText = scroll + 1; MutableInt lineNumber = new MutableInt(); TextHandler textHandler = this.textRenderer.getTextHandler(); - for (int i = 0; i < lines.size(); i++) { + for (int i = scroll; i < lines.size(); i++) { String s = lines.get(i); if (lineNumber.getValue() * 9 + 25 > this.height - 66) { @@ -350,6 +351,23 @@ public class ScriptEditScreen extends Screen { return cursorX == lines.get(cursorY).length(); } + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + scroll -= amount / 10; + if (scroll > lines.size() - 1) { + scroll = lines.size() - 1; + } + if (scroll < 0) { + scroll = 0; + } + return true; + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { return super.mouseClicked(mouseX, mouseY, button); From ec973e310443481848ac5a44b926fae0652b40e8 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 25 Dec 2022 15:24:55 +0100 Subject: [PATCH 06/11] Fix save book --- .../advancedscripts/screen/ScriptEditScreen.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java index d98db9f..82b734b 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -353,7 +353,7 @@ public class ScriptEditScreen extends Screen { @Override public boolean mouseScrolled(double mouseX, double mouseY, double amount) { - scroll -= amount / 10; + scroll -= Math.signum(amount); if (scroll > lines.size() - 1) { scroll = lines.size() - 1; } @@ -363,11 +363,6 @@ public class ScriptEditScreen extends Screen { return true; } - @Override - public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { - return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); - } - @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { return super.mouseClicked(mouseX, mouseY, button); @@ -389,7 +384,11 @@ public class ScriptEditScreen extends Screen { pages.add(page.toString()); page = new StringBuilder(); } - page.append(line); + if (line.isEmpty()) { + page.append("\n"); + } else { + page.append(line); + } } if (!page.isEmpty()) { pages.add(page.toString()); From fe037aa766a9cc8e4fcd42fa66099c7b8ec0aea2 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 25 Dec 2022 15:38:10 +0100 Subject: [PATCH 07/11] Fix save book --- .../advancedscripts/screen/ScriptEditScreen.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java index 82b734b..628d982 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -52,7 +52,14 @@ public class ScriptEditScreen extends Screen { NbtCompound nbtCompound = itemStack.getNbt(); if (nbtCompound != null) { BookScreen.filterPages(nbtCompound, s -> { - lines.addAll(Arrays.asList(s.split("\n"))); + String[] split = s.split("\n"); + for (String s1 : split) { + if (s1.equals(" ")) { + lines.add(""); + } else { + lines.add(s1); + } + } }); } if (lines.isEmpty()) { @@ -385,7 +392,7 @@ public class ScriptEditScreen extends Screen { page = new StringBuilder(); } if (line.isEmpty()) { - page.append("\n"); + page.append(" "); } else { page.append(line); } From a6e2fa7fc55ce2867dd03270c39d9edae383be65 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 25 Dec 2022 17:51:08 +0100 Subject: [PATCH 08/11] Add button to go to Book form --- .../screen/ScriptEditScreen.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java index 628d982..e4211d6 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -7,6 +7,7 @@ import de.zonlykroks.advancedscripts.lexer.TokenTypeColors; import net.minecraft.client.font.TextHandler; import net.minecraft.client.gui.DrawableHelper; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.BookEditScreen; import net.minecraft.client.gui.screen.ingame.BookScreen; import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; import net.minecraft.client.gui.widget.PressableWidget; @@ -24,7 +25,10 @@ import net.minecraft.text.Text; import net.minecraft.util.Hand; import org.apache.commons.lang3.mutable.MutableInt; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; public class ScriptEditScreen extends Screen { @@ -70,20 +74,27 @@ public class ScriptEditScreen extends Screen { @Override protected void init() { this.addDrawableChild( - new DoneButton(this.width / 2 - 98 / 2, height - 40, () -> { + new Button(DONE, this.width / 2 - 98 / 2, height - 55, () -> { this.client.setScreen(null); finalizeBook(); }) ); + this.addDrawableChild( + new Button(BOOK, this.width - 98 - 5, height - 20 - 5, () -> { + finalizeBook(); + this.client.setScreen(new BookEditScreen(player, itemStack, hand)); + }) + ); } public static final Text DONE = Text.translatable("gui.done"); + public static final Text BOOK = Text.translatable("item.minecraft.book"); - private class DoneButton extends PressableWidget { + private static class Button extends PressableWidget { private Runnable onPress; - public DoneButton(int x, int y, Runnable onPress) { - super(x, y, 98, 20, DONE); + public Button(Text text, int x, int y, Runnable onPress) { + super(x, y, 98, 20, text); visible = true; this.onPress = onPress; } From 36151e3a460608e4977356c3f1ee0f947c16d78c Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 28 Dec 2022 11:30:12 +0100 Subject: [PATCH 09/11] Add selection and copy, paste and cut --- .../lexer/TokenTypeColors.java | 1 + .../screen/ScriptEditScreen.java | 271 ++++++++++++++---- 2 files changed, 224 insertions(+), 48 deletions(-) diff --git a/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenTypeColors.java b/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenTypeColors.java index e01fc89..71a5643 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenTypeColors.java +++ b/src/main/java/de/zonlykroks/advancedscripts/lexer/TokenTypeColors.java @@ -7,6 +7,7 @@ public class TokenTypeColors { } public static final int BACKGROUND = 0xFF1E1F22; + public static final int SELECTION = 0xFF23437F; public static final int OTHER = 0xFFFFFFFF; public static final int ERROR = 0xFFAA0000; diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java index e4211d6..581bde6 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -13,6 +13,7 @@ import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; import net.minecraft.client.gui.widget.PressableWidget; import net.minecraft.client.render.GameRenderer; import net.minecraft.client.util.NarratorManager; +import net.minecraft.client.util.SelectionManager; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; @@ -25,10 +26,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Hand; import org.apache.commons.lang3.mutable.MutableInt; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; public class ScriptEditScreen extends Screen { @@ -42,6 +40,9 @@ public class ScriptEditScreen extends Screen { private int cursorY = 0; private int cursorX = 0; + private int savedCursorY = -1; + private int savedCursorX = -1; + private int tickCounter; private int keyCode = -1; @@ -114,6 +115,17 @@ public class ScriptEditScreen extends Screen { } } + private void setClipboard(String clipboard) { + if (this.client != null) { + SelectionManager.setClipboard(this.client, clipboard); + } + + } + + private String getClipboard() { + return this.client != null ? SelectionManager.getClipboard(this.client) : ""; + } + @Override public void tick() { super.tick(); @@ -132,6 +144,12 @@ public class ScriptEditScreen extends Screen { int lineNumberLength = textRenderer.getWidth(lines.size() + ""); + boolean hasSelection = savedCursorY != -1 && savedCursorX != -1; + int minSelectionY = Math.min(cursorY, savedCursorY); + int maxSelectionY = Math.max(cursorY, savedCursorY); + int minSelectionX = minSelectionY == cursorY ? cursorX : savedCursorX; + int maxSelectionX = maxSelectionY == cursorY ? cursorX : savedCursorX; + int lineNumberText = scroll + 1; MutableInt lineNumber = new MutableInt(); TextHandler textHandler = this.textRenderer.getTextHandler(); @@ -147,8 +165,8 @@ public class ScriptEditScreen extends Screen { } // Line number + int height = this.textRenderer.getWrappedLinesHeight(s, this.width - 50 - lineNumberLength - 5); if (lineTooLong(s)) { - int height = this.textRenderer.getWrappedLinesHeight(s, this.width - 50 - lineNumberLength - 5); fill(matrices, 25 + lineNumberLength + 2, 25 + lineNumber.getValue() * 9, 25 + lineNumberLength + 3, 25 + lineNumber.getValue() * 9 + height, TokenTypeColors.ERROR); } this.textRenderer.draw(matrices, lineNumberText + "", 25 + lineNumberLength - textRenderer.getWidth(lineNumberText + ""), 25 + lineNumber.getValue() * 9, 0xFFFFFF); @@ -169,9 +187,37 @@ public class ScriptEditScreen extends Screen { String line = token.text.substring(start, end); int previousXIndex = currentXIndex.get(); currentXIndex.addAndGet(line.length()); + + if (hasSelection) { + int x1 = x.get(); + int x2 = x.get() + textRenderer.getWidth(line); + + if (finalI == minSelectionY) { + if (minSelectionX > currentXIndex.get()) { + x2 = 0; + } else if (minSelectionX <= currentXIndex.get() && minSelectionX >= previousXIndex) { + int startInLine = minSelectionX - previousXIndex; + x1 += textRenderer.getWidth(line.substring(0, startInLine)); + } + } + if (finalI == maxSelectionY) { + if (maxSelectionX < previousXIndex) { + x2 = 0; + } else if (maxSelectionX <= currentXIndex.get()) { + int endInLine = maxSelectionX - previousXIndex; + x2 = x.get() + textRenderer.getWidth(line.substring(0, endInLine)); + } + } + + if (finalI >= minSelectionY && finalI <= maxSelectionY && x2 > x1) { + fill(matrices, x1, y + 25, x2, y + 25 + 9, TokenTypeColors.SELECTION); + } + } + if (finalI == cursorY && currentXIndex.get() >= cursorX && previousXIndex <= cursorX) { drawCursor(matrices, x.get() + textRenderer.getWidth(line.substring(0, cursorX - previousXIndex)) - 1, 25 + y, isAtEndOfLine()); } + this.textRenderer.draw(matrices, line, x.get(), 25 + y, token.color); x.addAndGet(textRenderer.getWidth(line)); if (x.get() > this.width - 50 - lineNumberLength - 5) { @@ -206,35 +252,85 @@ public class ScriptEditScreen extends Screen { } private void key(int keyCode) { + if (Screen.isSelectAll(keyCode)) { + this.cursorX = 0; + this.cursorY = 0; + this.savedCursorX = lines.get(lines.size() - 1).length(); + this.savedCursorY = lines.size() - 1; + return; + } else if (Screen.isCopy(keyCode)) { + String copied = selection(false); + if (copied != null) { + setClipboard(copied); + } + return; + } else if (Screen.isPaste(keyCode)) { + String copied = getClipboard(); + if (copied != null) { + insert(copied); + } + return; + } else if (Screen.isCut(keyCode)) { + String copied = selection(true); + if (copied != null) { + setClipboard(copied); + } + return; + } + SelectionManager.SelectionType selectionType = Screen.hasControlDown() ? SelectionManager.SelectionType.WORD : SelectionManager.SelectionType.CHARACTER; + boolean valid = true; + int previousCursorX = cursorX; + int previousCursorY = cursorY; switch (keyCode) { case 257: case 335: + selection(true); newLine(); + valid = false; break; case 259: - backspace(); + if (selection(true) == null) backspace(selectionType); + valid = false; break; case 261: - delete(); + if (selection(true) == null) delete(selectionType); + valid = false; break; case 262: - moveCursor(1); + moveCursor(1, selectionType); + valid = Screen.hasShiftDown(); break; case 263: - moveCursor(-1); + moveCursor(-1, selectionType); + valid = Screen.hasShiftDown(); break; case 264: moveDown(); + valid = Screen.hasShiftDown(); break; case 265: moveUp(); + valid = Screen.hasShiftDown(); break; case 268: cursorX = 0; + valid = Screen.hasShiftDown(); break; case 269: cursorX = lines.get(cursorY).length(); + valid = Screen.hasShiftDown(); break; + default: + break; + } + if (valid) { + if (Screen.hasShiftDown() && savedCursorX == -1 && savedCursorY == -1) { + savedCursorX = previousCursorX; + savedCursorY = previousCursorY; + } + } else { + savedCursorY = -1; + savedCursorX = -1; } } @@ -260,21 +356,78 @@ public class ScriptEditScreen extends Screen { if (super.charTyped(chr, modifiers)) { return true; } - if (insert(chr, modifiers)) { - return true; - } - return true; + selection(true); + boolean valid = insert(chr + ""); + savedCursorY = -1; + savedCursorX = -1; + return valid; } - private boolean insert(char chr, int modifiers) { - String line = lines.get(cursorY); - if (cursorX == line.length()) { - line += chr; - } else { - line = line.substring(0, cursorX) + chr + line.substring(cursorX); + private String selection(boolean remove) { + if (savedCursorX == -1 || savedCursorY == -1) { + return null; + } + int minSelectionY = Math.min(savedCursorY, cursorY); + int maxSelectionY = Math.max(savedCursorY, cursorY); + int minSelectionX = minSelectionY == cursorY ? cursorX : savedCursorX; + int maxSelectionX = maxSelectionY == cursorY ? cursorX : savedCursorX; + + StringBuilder builder = new StringBuilder(); + for (int i = minSelectionY; i <= maxSelectionY; i++) { + String line = lines.get(i); + if (i == minSelectionY && i == maxSelectionY) { + builder.append(line, minSelectionX, maxSelectionX); + } else if (i == minSelectionY) { + builder.append(line, minSelectionX, line.length()); + } else if (i == maxSelectionY) { + builder.append(line, 0, Math.min(maxSelectionX, line.length())); + } else { + builder.append(line); + } + if (i != maxSelectionY) { + builder.append("\n"); + } + } + if (remove) { + for (int i = maxSelectionY; i >= minSelectionY; i--) { + String line = lines.get(i); + if (i == minSelectionY && i == maxSelectionY) { + lines.set(i, line.substring(0, minSelectionX) + line.substring(maxSelectionX)); + } else if (i == minSelectionY) { + lines.set(i, line.substring(0, minSelectionX)); + } else if (i == maxSelectionY) { + lines.set(i, line.substring(Math.min(maxSelectionX, line.length()))); + } else { + lines.remove(i); + } + } + if (minSelectionY != maxSelectionY) { + lines.set(minSelectionY, lines.get(minSelectionY) + lines.get(minSelectionY + 1)); + lines.remove(minSelectionY + 1); + } + cursorX = minSelectionX; + cursorY = minSelectionY; + savedCursorX = -1; + savedCursorY = -1; + } + return builder.toString(); + } + + private boolean insert(String s) { + String[] split = s.split("\n"); + for (int i = 0; i < split.length; i++) { + String line = lines.get(cursorY); + if (cursorX == line.length()) { + line += split[i]; + } else { + line = line.substring(0, cursorX) + split[i] + line.substring(cursorX); + } + lines.set(cursorY, line); + cursorX += split[i].length(); + if (i != split.length - 1) { + newLine(); + } } - lines.set(cursorY, line); - cursorX++; return true; } @@ -289,58 +442,80 @@ public class ScriptEditScreen extends Screen { return true; } - private boolean backspace() { + private boolean backspace(SelectionManager.SelectionType selectionType) { if (cursorX == 0) { if (cursorY == 0) { return true; } - String line = lines.get(cursorY); - String prevLine = lines.get(cursorY - 1); - lines.set(cursorY - 1, prevLine + line); - lines.remove(cursorY); + String previousLine = lines.get(cursorY - 1); + lines.set(cursorY - 1, lines.get(cursorY - 1) + lines.remove(cursorY)); cursorY--; - cursorX = prevLine.length(); + cursorX = previousLine.length(); } else { String line = lines.get(cursorY); - line = line.substring(0, cursorX - 1) + line.substring(cursorX); + int remove = selectionType == SelectionManager.SelectionType.CHARACTER ? 1 : getWordLength(line, cursorX, -1); + line = line.substring(0, cursorX - remove) + line.substring(cursorX); lines.set(cursorY, line); - cursorX--; + cursorX -= remove; } return true; } - private boolean delete() { - String line = lines.get(cursorY); - if (cursorX == line.length()) { + private boolean delete(SelectionManager.SelectionType selectionType) { + if (cursorX == lines.get(cursorY).length()) { if (cursorY == lines.size() - 1) { return true; } String nextLine = lines.get(cursorY + 1); - lines.set(cursorY, line + nextLine); - lines.remove(cursorY + 1); + lines.remove(cursorY); + lines.set(cursorY, lines.get(cursorY) + nextLine); } else { - line = line.substring(0, cursorX) + line.substring(cursorX + 1); + String line = lines.get(cursorY); + int remove = selectionType == SelectionManager.SelectionType.CHARACTER ? 1 : getWordLength(line, cursorX, 1); + line = line.substring(0, cursorX) + line.substring(cursorX + remove); lines.set(cursorY, line); } return true; } - private boolean moveCursor(int offset) { + private int getWordLength(String line, int cursorX, int direction) { + int i = cursorX; + while (i >= 0 && i < line.length()) { + char c = line.charAt(i); + if (Character.isLetterOrDigit(c)) { + i += direction; + } else { + break; + } + } + return Math.abs(i - cursorX); + } + + private boolean moveCursor(int offset, SelectionManager.SelectionType selectionType) { + if (offset == 0) { + return true; + } String line = lines.get(cursorY); - if (cursorX + offset < 0) { - if (cursorY == 0) { - return true; + if (offset > 0) { + if (cursorX == line.length()) { + if (cursorY == lines.size() - 1) { + return true; + } + cursorY++; + cursorX = 0; + } else { + cursorX += selectionType == SelectionManager.SelectionType.CHARACTER ? 1 : getWordLength(line, cursorX, 1); } - cursorY--; - cursorX = lines.get(cursorY).length(); - } else if (cursorX + offset > line.length()) { - if (cursorY == lines.size() - 1) { - return true; - } - cursorY++; - cursorX = 0; } else { - cursorX += offset; + if (cursorX == 0) { + if (cursorY == 0) { + return true; + } + cursorY--; + cursorX = lines.get(cursorY).length(); + } else { + cursorX -= selectionType == SelectionManager.SelectionType.CHARACTER ? 1 : getWordLength(line, cursorX, -1); + } } return true; } From 118b258244575c49f9a3eb785e7dfcbb4c454d4b Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 28 Dec 2022 11:35:35 +0100 Subject: [PATCH 10/11] Fix selection errors --- .../advancedscripts/screen/ScriptEditScreen.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java index 581bde6..6f0ea59 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -147,8 +147,8 @@ public class ScriptEditScreen extends Screen { boolean hasSelection = savedCursorY != -1 && savedCursorX != -1; int minSelectionY = Math.min(cursorY, savedCursorY); int maxSelectionY = Math.max(cursorY, savedCursorY); - int minSelectionX = minSelectionY == cursorY ? cursorX : savedCursorX; - int maxSelectionX = maxSelectionY == cursorY ? cursorX : savedCursorX; + int minSelectionX = (minSelectionY == maxSelectionY ? Math.min(cursorX, savedCursorX) : (minSelectionY == cursorY ? cursorX : savedCursorX)); + int maxSelectionX = (minSelectionY == maxSelectionY ? Math.max(cursorX, savedCursorX) : (maxSelectionY == cursorY ? cursorX : savedCursorX)); int lineNumberText = scroll + 1; MutableInt lineNumber = new MutableInt(); @@ -369,8 +369,8 @@ public class ScriptEditScreen extends Screen { } int minSelectionY = Math.min(savedCursorY, cursorY); int maxSelectionY = Math.max(savedCursorY, cursorY); - int minSelectionX = minSelectionY == cursorY ? cursorX : savedCursorX; - int maxSelectionX = maxSelectionY == cursorY ? cursorX : savedCursorX; + int minSelectionX = (minSelectionY == maxSelectionY ? Math.min(cursorX, savedCursorX) : (minSelectionY == cursorY ? cursorX : savedCursorX)); + int maxSelectionX = (minSelectionY == maxSelectionY ? Math.max(cursorX, savedCursorX) : (maxSelectionY == cursorY ? cursorX : savedCursorX)); StringBuilder builder = new StringBuilder(); for (int i = minSelectionY; i <= maxSelectionY; i++) { From 2cbf0958260ce2329bd421ce0ef18843461652e6 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 28 Dec 2022 11:48:45 +0100 Subject: [PATCH 11/11] Add autoscroll --- .../advancedscripts/screen/ScriptEditScreen.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java index 6f0ea59..b4daf11 100644 --- a/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java +++ b/src/main/java/de/zonlykroks/advancedscripts/screen/ScriptEditScreen.java @@ -332,6 +332,7 @@ public class ScriptEditScreen extends Screen { savedCursorY = -1; savedCursorX = -1; } + autoScroll(); } @Override @@ -360,6 +361,7 @@ public class ScriptEditScreen extends Screen { boolean valid = insert(chr + ""); savedCursorY = -1; savedCursorX = -1; + autoScroll(); return valid; } @@ -540,6 +542,14 @@ public class ScriptEditScreen extends Screen { return true; } + private void autoScroll() { + if (cursorY < scroll) { + scroll = cursorY; + } else if (cursorY >= scroll + ((this.height - 25 - 66) / 9)) { + scroll = cursorY - ((this.height - 25 - 66) / 9); + } + } + private boolean isAtEndOfLine() { return cursorX == lines.get(cursorY).length(); }