Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

public class TranslateCommand {

public static final String LITERAL = "ctranslate";

private static final SimpleCommandExceptionType UNKNOWN_ERROR_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.ctranslate.unknownError"));

private static final String URL_FORMAT = "https://translate.googleapis.com/translate_a/single?client=gtx&dt=t&sl=%s&tl=%s&q=%s";
Expand All @@ -34,7 +36,7 @@ public class TranslateCommand {
private static final Duration DURATION = Duration.ofSeconds(5);

public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
dispatcher.register(literal("ctranslate")
dispatcher.register(literal(LITERAL)
.then(argument("query", translationQuery())
.executes(ctx -> translate(ctx.getSource(), getTranslationQuery(ctx, "query")))));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.earthcomputer.clientcommands.interfaces;

public interface IChatScreen {
boolean clientcommands_isTranslating();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package net.earthcomputer.clientcommands.mixin.commands.translate;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.earthcomputer.clientcommands.interfaces.IChatScreen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.ActiveTextCollector;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.TextAlignment;
import net.minecraft.client.gui.components.ChatComponent;
import net.minecraft.client.gui.screens.ChatScreen;
import net.minecraft.client.multiplayer.chat.GuiMessage;
import net.minecraft.client.renderer.state.gui.GuiTextRenderState;
import net.minecraft.util.ARGB;
import net.minecraft.util.FormattedCharSequence;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(targets = "net/minecraft/client/gui/components/ChatComponent$1")
public abstract class ChatComponent$1Mixin {
@WrapOperation(method = "accept", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/ChatComponent$ChatGraphicsAccess;handleMessage(IFLnet/minecraft/util/FormattedCharSequence;)Z"))
private boolean copyText(ChatComponent.ChatGraphicsAccess instance, int textTop, float opacity, FormattedCharSequence text, Operation<Boolean> original, GuiMessage.Line line) {
if (shouldCallOriginal(instance, textTop, text, line)) {
return original.call(instance, textTop, opacity, text);
}
// original.call() always returns false
return false;
}

private static boolean shouldCallOriginal(ChatComponent.ChatGraphicsAccess instance, int textTop, FormattedCharSequence text, GuiMessage.Line line) {
if (!(instance instanceof ChatComponent.ClickableTextOnlyGraphicsAccess clickableTextOnlyGraphicsAccess)) {
return true;
}
Minecraft minecraft = Minecraft.getInstance();
if (!(minecraft.gui.screen() instanceof ChatScreen chatScreen)) {
return true;
}
if (!((IChatScreen) chatScreen).clientcommands_isTranslating()) {
return true;
}
if (!(clickableTextOnlyGraphicsAccess.output instanceof ActiveTextCollector.ClickableStyleFinder clickableStyleFinder)) {
return true;
}
Font font = minecraft.font;
ActiveTextCollector.Parameters parameters = clickableStyleFinder.defaultParameters();
int leftX = TextAlignment.LEFT.calculateLeft(0, font, text);
GuiTextRenderState renderState = new GuiTextRenderState(font, text, parameters.pose(), leftX, textTop, ARGB.white(parameters.opacity()), 0, true, true, parameters.scissor());
boolean[] found = {false};
ActiveTextCollector.findElementUnderCursor(renderState, clickableStyleFinder.testX, clickableStyleFinder.testY, _ -> {
minecraft.keyboardHandler.setClipboard(line.parent().content().getString());
found[0] = true;
});
return !found[0];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package net.earthcomputer.clientcommands.mixin.commands.translate;

import net.earthcomputer.clientcommands.command.TranslateCommand;
import net.earthcomputer.clientcommands.interfaces.IChatScreen;
import net.minecraft.client.gui.screens.ChatScreen;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(ChatScreen.class)
public abstract class ChatScreenMixin extends Screen implements IChatScreen {

@Unique
private boolean isTranslating = false;

protected ChatScreenMixin(Component title) {
super(title);
}

@Inject(method = "onEdited", at = @At("TAIL"))
private void onEdited(String value, CallbackInfo ci) {
this.isTranslating = value.startsWith(Commands.COMMAND_PREFIX + TranslateCommand.LITERAL);
}

@Override
public boolean clientcommands_isTranslating() {
return this.isTranslating;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NullMarked
package net.earthcomputer.clientcommands.mixin.commands.translate;

import org.jspecify.annotations.NullMarked;
6 changes: 6 additions & 0 deletions src/main/resources/clientcommands.aw
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ accessible field net/minecraft/client/renderer/ShaderManager$CompilationCache co
# crelog
accessible field net/minecraft/client/gui/screens/DisconnectedScreen parent Lnet/minecraft/client/gui/screens/Screen;

# ctranslate
accessible class net/minecraft/client/gui/components/ChatComponent$ClickableTextOnlyGraphicsAccess
accessible field net/minecraft/client/gui/components/ChatComponent$ClickableTextOnlyGraphicsAccess output Lnet/minecraft/client/gui/ActiveTextCollector;
accessible field net/minecraft/client/gui/ActiveTextCollector$ClickableStyleFinder testX I
accessible field net/minecraft/client/gui/ActiveTextCollector$ClickableStyleFinder testY I

# Game Options
accessible field net/minecraft/client/OptionInstance value Ljava/lang/Object;

Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/mixins.clientcommands.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
"commands.reply.ClientPacketListenerMixin",
"commands.snap.MinecraftMixin",
"commands.time.ClientClockManagerMixin",
"commands.translate.ChatComponent$1Mixin",
"commands.translate.ChatScreenMixin",
"dataqueryhandler.ClientPacketListenerMixin",
"events.ClientPacketListenerMixin",
"events.GuiMixin",
Expand Down
Loading