Skip to content
Merged
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 COMMAND_NAME = "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(COMMAND_NAME)
.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,63 @@
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.command.TranslateCommand;
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.commands.Commands;
import net.minecraft.util.ARGB;
import net.minecraft.util.FormattedCharSequence;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
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 (!tryCopy(instance, textTop, text, line)) {
return original.call(instance, textTop, opacity, text);
}
// original.call() always returns false, so we return false here also
return false;
}

@Unique
private static boolean tryCopy(ChatComponent.ChatGraphicsAccess instance, int textTop, FormattedCharSequence text, GuiMessage.Line line) {
if (!(instance instanceof ChatComponent.ClickableTextOnlyGraphicsAccess clickableTextOnlyGraphicsAccess)) {
return false;
}
Minecraft minecraft = Minecraft.getInstance();
if (!(minecraft.gui.screen() instanceof ChatScreen chatScreen)) {
return false;
}
if (!isTranslating(chatScreen.input.getValue())) {
return false;
}
if (!(clickableTextOnlyGraphicsAccess.output instanceof ActiveTextCollector.ClickableStyleFinder clickableStyleFinder)) {
return false;
}
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];
}

@Unique
private static boolean isTranslating(String value) {
return value.startsWith(Commands.COMMAND_PREFIX + TranslateCommand.COMMAND_NAME);
}
}
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;
7 changes: 7 additions & 0 deletions src/main/resources/clientcommands.aw
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ 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
accessible field net/minecraft/client/gui/screens/ChatScreen input Lnet/minecraft/client/gui/components/EditBox;

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

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/mixins.clientcommands.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"commands.reply.ClientPacketListenerMixin",
"commands.snap.MinecraftMixin",
"commands.time.ClientClockManagerMixin",
"commands.translate.ChatComponent_1Mixin",
"dataqueryhandler.ClientPacketListenerMixin",
"events.ClientPacketListenerMixin",
"events.GuiMixin",
Expand Down
Loading