180 lines
7.7 KiB
Java
180 lines
7.7 KiB
Java
package io.gitlab.jfronny.chattransform.client.mixin;
|
|
|
|
import io.gitlab.jfronny.chattransform.Cfg;
|
|
import io.gitlab.jfronny.chattransform.ChatTransform;
|
|
import io.gitlab.jfronny.chattransform.client.*;
|
|
import net.minecraft.client.font.TextRenderer;
|
|
import net.minecraft.client.gui.DrawContext;
|
|
import net.minecraft.client.gui.screen.Screen;
|
|
import net.minecraft.client.gui.widget.ClickableWidget;
|
|
import net.minecraft.client.gui.widget.TextFieldWidget;
|
|
import net.minecraft.text.Text;
|
|
import org.objectweb.asm.Opcodes;
|
|
import org.spongepowered.asm.mixin.*;
|
|
import org.spongepowered.asm.mixin.injection.*;
|
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.function.Predicate;
|
|
|
|
@Mixin(TextFieldWidget.class)
|
|
public abstract class TextFieldWidgetMixin extends ClickableWidget implements ITextFieldWidget {
|
|
public TextFieldWidgetMixin(int x, int y, int width, int height, Text message) {
|
|
super(x, y, width, height, message);
|
|
}
|
|
|
|
@Shadow public abstract String getText();
|
|
@Shadow private String text;
|
|
@Shadow private int selectionStart;
|
|
@Shadow private int selectionEnd;
|
|
@Shadow private boolean drawsBackground;
|
|
@Shadow public abstract int getCharacterX(int index);
|
|
@Shadow public abstract boolean isVisible();
|
|
@Shadow public abstract void setCursor(int cursor, boolean shiftKeyPressed);
|
|
|
|
@Shadow private Predicate<String> textPredicate;
|
|
@Shadow @Final private TextRenderer textRenderer;
|
|
@Unique private boolean chattransform$active = false;
|
|
@Override
|
|
public void chattransform$activate() {
|
|
chattransform$active = true;
|
|
ChatTransform.LOG.info("Activated widget " + this);
|
|
}
|
|
|
|
@Unique private boolean chattransform$shouldTransform = false;
|
|
@Redirect(method = "write(Ljava/lang/String;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/widget/TextFieldWidget;text:Ljava/lang/String;", opcode = Opcodes.PUTFIELD))
|
|
void transformBeforeWrite(TextFieldWidget instance, String value, String tx) {
|
|
if (chattransform$active) {
|
|
if (selectionStart == selectionEnd && tx.length() == 1 && !Screen.hasAltDown() && !getText().startsWith("/")) {
|
|
chattransform$shouldTransform = true;
|
|
} else {
|
|
transformStart.clear();
|
|
}
|
|
}
|
|
this.text = value;
|
|
}
|
|
|
|
@Inject(method = "write(Ljava/lang/String;)V", at = @At("TAIL"))
|
|
void transformAfterWrite(String text, CallbackInfo ci) {
|
|
if (chattransform$shouldTransform) {
|
|
chattransform$shouldTransform = false;
|
|
transform();
|
|
}
|
|
}
|
|
|
|
@Inject(method = "setCursor(IZ)V", at = @At("TAIL"))
|
|
void updateStartOnSetCursor(int cursor, boolean shiftKeyPressed, CallbackInfo ci) {
|
|
transformStart.clear();
|
|
lastSubstitution = null;
|
|
}
|
|
|
|
@Unique private final TransformStart transformStart = new TransformStart();
|
|
@Unique private Substitution lastSubstitution = null;
|
|
void transform() {
|
|
if (!transformStart.isAvailable()) transformStart.set(selectionStart - 1);
|
|
if (transformStart.get() >= selectionStart) return;
|
|
String currentString = getText().substring(transformStart.get(), selectionStart);
|
|
Iterator<? extends Map.Entry<String, String>> complete = Cfg.substitutions.getKeyValuePairsForKeysStartingWith(currentString).iterator();
|
|
if (complete.hasNext()) {
|
|
Map.Entry<String, String> substitution = complete.next();
|
|
if (substitution.getKey().length() == currentString.length() // Match without "overshoot"
|
|
&& !complete.hasNext() // No other options
|
|
&& substitute(transformStart.get(), selectionStart, substitution.getValue())) {
|
|
transformStart.set(selectionStart);
|
|
}
|
|
return;
|
|
}
|
|
// No matches
|
|
if (transformStart.get() == selectionStart - 1) {
|
|
// Nothing starts with this char
|
|
transformStart.increment();
|
|
} else {
|
|
// Something previously started with this...
|
|
String previousString = getText().substring(transformStart.get(), selectionStart - 1);
|
|
String substitution = Cfg.substitutions.get(previousString);
|
|
if (substitution != null
|
|
// ...and matched -> replace
|
|
&& substitute(transformStart.get(), selectionStart - 1, substitution)) {
|
|
setCursor(selectionStart + 1, false);
|
|
} else {
|
|
// ...and didn't match -> move transform start and call transform again (substring might have matched)
|
|
transformStart.increment();
|
|
transform();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String chattransform$finalize() {
|
|
String str = getText();
|
|
if (!transformStart.isAvailable() || transformStart.get() >= selectionStart) return str;
|
|
String currentString = str.substring(transformStart.get(), selectionStart);
|
|
String substitution = Cfg.substitutions.get(currentString);
|
|
if (substitution == null || !substitute(transformStart.get(), selectionStart, substitution)) {
|
|
transformStart.increment();
|
|
return chattransform$finalize();
|
|
}
|
|
return str;
|
|
}
|
|
|
|
boolean substitute(int start, int end, String substitution) {
|
|
ChatTransform.LOG.info("Transforming " + getText().substring(start, end) + " to " + substitution);
|
|
String sub = text.substring(0, start) + substitution + text.substring(end);
|
|
if (textPredicate.test(sub)) {
|
|
this.text = sub;
|
|
int oldLen = end - start;
|
|
int newLen = substitution.length();
|
|
if (selectionStart > end) selectionStart += newLen - oldLen;
|
|
else if (selectionStart > start) selectionStart = start + newLen;
|
|
selectionEnd = selectionStart;
|
|
lastSubstitution = new Substitution(start, start + newLen);
|
|
return true;
|
|
} else return false;
|
|
}
|
|
|
|
@Inject(method = "renderWidget(Lnet/minecraft/client/gui/DrawContext;IIF)V", at = @At(value = "TAIL"))
|
|
void renderTransformStart(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
|
if (isVisible() && Cfg.Client.visualize) {
|
|
int y = this.drawsBackground ? this.getY() + (this.height - 8) / 2 : this.getY();
|
|
if (transformStart.isAvailable()) {
|
|
int x = getCharacterX(transformStart.get());
|
|
chattransform$fill(context, x, y - 1, x + 1, y + 1 + 9, 0x7f0000ff);
|
|
}
|
|
if (transformStart.showPrevious()) {
|
|
int x = getCharacterX(transformStart.getPrevious());
|
|
chattransform$fill(context, x, y - 1, x + 1, y + 1 + 9, 0x7fff0000);
|
|
}
|
|
if (lastSubstitution != null && lastSubstitution.shouldShow()) {
|
|
int start = getCharacterX(lastSubstitution.start());
|
|
int end = getCharacterX(Math.min(lastSubstitution.end() + 1, text.length()));
|
|
chattransform$fill(context, start, y - 1, end, y + 1 + 9, 0x7fffff00);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Unique
|
|
private void chattransform$fill(DrawContext context, int x1, int y1, int x2, int y2, int color) {
|
|
if (x1 < 0) x1 = 0;
|
|
if (x2 < 0) x2 = 0;
|
|
int maxX = getX() + getWidth();
|
|
if (x1 > maxX) x1 = maxX;
|
|
if (x2 > maxX) x2 = maxX;
|
|
|
|
if (x1 < x2) {
|
|
int i = x1;
|
|
x1 = x2;
|
|
x2 = i;
|
|
}
|
|
|
|
if (y1 < y2) {
|
|
int i = y1;
|
|
y1 = y2;
|
|
y2 = i;
|
|
}
|
|
|
|
context.drawText(textRenderer, "X={" + x1 + ";" + x2 + "} Y={" + y1 + ";" + y2 + "}", 0, 0, 0xFF000000, false);
|
|
context.fill(x1, y1, x2, y2, color);
|
|
}
|
|
}
|