142 lines
6.2 KiB
Java
142 lines
6.2 KiB
Java
package io.gitlab.jfronny.chattransform.mixin;
|
|
|
|
import io.gitlab.jfronny.chattransform.*;
|
|
import net.minecraft.client.gui.DrawableHelper;
|
|
import net.minecraft.client.gui.screen.Screen;
|
|
import net.minecraft.client.gui.widget.ClickableWidget;
|
|
import net.minecraft.client.gui.widget.TextFieldWidget;
|
|
import net.minecraft.client.util.math.MatrixStack;
|
|
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.Map;
|
|
import java.util.Set;
|
|
import java.util.function.Predicate;
|
|
import java.util.stream.Collectors;
|
|
|
|
@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);
|
|
|
|
@Shadow private Predicate<String> textPredicate;
|
|
@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 {
|
|
chattransform$start = null;
|
|
}
|
|
}
|
|
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(I)V", at = @At("TAIL"))
|
|
void updateStartOnSetCursor(int cursor, CallbackInfo ci) {
|
|
chattransform$start = null;
|
|
}
|
|
|
|
@Unique private Integer chattransform$start = null;
|
|
void transform() {
|
|
if (chattransform$start == null) chattransform$start = selectionStart - 1;
|
|
if (chattransform$start < 0) chattransform$start = 0;
|
|
if (chattransform$start >= selectionStart) return;
|
|
String currentString = getText().substring(chattransform$start, selectionStart);
|
|
Set<Map.Entry<String, String>> complete = getStartingWith(currentString);
|
|
// Exact match
|
|
if (Cfg.substitutions.containsKey(currentString) && complete.size() == 1
|
|
&& substitute(chattransform$start, selectionStart, Cfg.substitutions.get(currentString))) {
|
|
chattransform$start = selectionStart;
|
|
return;
|
|
}
|
|
if (complete.isEmpty()) {
|
|
if (chattransform$start == selectionStart - 1) {
|
|
// Nothing starts with this char
|
|
chattransform$start++;
|
|
} else {
|
|
// Something previously started with this...
|
|
String previousString = getText().substring(chattransform$start, selectionStart - 1);
|
|
if (Cfg.substitutions.containsKey(previousString)
|
|
// ...and matched -> replace
|
|
&& substitute(chattransform$start, selectionStart - 1, Cfg.substitutions.get(previousString))) {
|
|
setCursor(selectionStart + 1);
|
|
} else {
|
|
// ...and didn't match -> move transform start and call transform again (substring might have matched)
|
|
chattransform$start++;
|
|
transform();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String chattransform$finalize() {
|
|
String str = getText();
|
|
if (chattransform$start == null || chattransform$start >= selectionStart) return str;
|
|
String currentString = str.substring(chattransform$start, selectionStart);
|
|
if (!Cfg.substitutions.containsKey(currentString)
|
|
|| !substitute(chattransform$start, selectionStart, Cfg.substitutions.get(currentString))) {
|
|
chattransform$start++;
|
|
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 -= oldLen + newLen;
|
|
else if (selectionStart > start) selectionStart = start + newLen;
|
|
selectionEnd = selectionStart;
|
|
return true;
|
|
} else return false;
|
|
}
|
|
|
|
Set<Map.Entry<String, String>> getStartingWith(String start) {
|
|
return Cfg.substitutions.entrySet().stream().filter(s -> s.getKey().startsWith(start)).collect(Collectors.toUnmodifiableSet());
|
|
}
|
|
|
|
@Inject(method = "renderButton(Lnet/minecraft/client/util/math/MatrixStack;IIF)V", at = @At(value = "TAIL"))
|
|
void renderTransformStart(MatrixStack matrices, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
|
if (isVisible() && chattransform$start != null) {
|
|
int x = getCharacterX(chattransform$start);
|
|
int y = this.drawsBackground ? this.getY() + (this.height - 8) / 2 : this.getY();
|
|
DrawableHelper.fill(matrices, x, y - 1, x + 1, y + 1 + 9, 0x7f0000ff);
|
|
}
|
|
}
|
|
}
|